@daisforge/ui 1.17.0 → 1.18.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 (370) hide show
  1. package/chunks/{AnalyticalWidget-BWcBjoNh.mjs → AnalyticalWidget-Cdo6_s1Y.mjs} +165 -163
  2. package/chunks/AnalyticalWidget-wke3Jm3X.cjs +43 -0
  3. package/chunks/{ApprovalCenter-CGs7n-2C.cjs → ApprovalCenter-CAcC43zn.cjs} +1 -1
  4. package/chunks/{ApprovalCenter-BqN8Zjol.mjs → ApprovalCenter-DxcvzR8q.mjs} +1 -1
  5. package/chunks/{BlockTitle-o2QTRA7D.cjs → BlockTitle-BEIxiECX.cjs} +1 -1
  6. package/chunks/{BlockTitle-BM2EE1b1.mjs → BlockTitle-BZxOEdTc.mjs} +1 -1
  7. package/chunks/{FiltersActions-CKimtYsU.cjs → FiltersActions-BKJJH6Mu.cjs} +1 -1
  8. package/chunks/{FiltersActions-C5OGUDDx.mjs → FiltersActions-CFStLRsU.mjs} +3 -3
  9. package/chunks/{GridDND-DkL6-TgS.cjs → GridDND-DV-1g1sI.cjs} +1 -1
  10. package/chunks/{GridDND-D9T2wqLt.mjs → GridDND-UMUi8M_Q.mjs} +1 -1
  11. package/chunks/{ListOfFilters-B8XF6KiI.cjs → ListOfFilters-DJvnT9kw.cjs} +1 -1
  12. package/chunks/{ListOfFilters-BEDW_v5t.mjs → ListOfFilters-DTAZ3wfQ.mjs} +2 -2
  13. package/chunks/{MassActions-C66ulAuq.mjs → MassActions-DSAaO1-u.mjs} +2 -2
  14. package/chunks/{MassActions-BHNmuh3s.cjs → MassActions-Z-48g81T.cjs} +1 -1
  15. package/chunks/{PageTitle-BEdQpyZh.cjs → PageTitle-Bh3g6FUT.cjs} +1 -1
  16. package/chunks/{PageTitle-BVbWaV52.mjs → PageTitle-CaqXfrrQ.mjs} +1 -1
  17. package/chunks/{Stories-DVh7iByM.cjs → Stories-CZXv_Ves.cjs} +1 -1
  18. package/chunks/{Stories-lNAuXVJN.mjs → Stories-Dlwle-SM.mjs} +1 -1
  19. package/chunks/{Table-C_1QGsh8.mjs → Table-B9OAyWAW.mjs} +4 -4
  20. package/chunks/{Table-C-lLZklk.cjs → Table-BwhzcFX1.cjs} +1 -1
  21. package/chunks/{TableCanvas--g37wjRi.mjs → TableCanvas-DcoUBrgC.mjs} +5315 -4827
  22. package/chunks/TableCanvas-MMWMrYiV.cjs +30 -0
  23. package/chunks/{TableGlide-Cjcw7Wur.mjs → TableGlide-Ch-gaROR.mjs} +1982 -1852
  24. package/chunks/TableGlide-TYzM2bEB.cjs +1 -0
  25. package/components/AiAgentPopover/AiAgentPopover.styled.d.ts +1 -0
  26. package/components/AnalyticalWidget/AnalyticalWidget.d.ts +5 -3
  27. package/components/AnalyticalWidget/components/AnalyticalWidgetButtons/AnalyticalWidgetIconButtonDots.d.ts +69 -11
  28. package/components/AnalyticalWidget/components/AnalyticalWidgetButtons/index.cjs +1 -1
  29. package/components/AnalyticalWidget/components/AnalyticalWidgetButtons/index.mjs +1 -1
  30. package/components/AnalyticalWidget/components/AnalyticalWidgetChips/index.cjs +1 -1
  31. package/components/AnalyticalWidget/components/AnalyticalWidgetChips/index.mjs +1 -1
  32. package/components/AnalyticalWidget/components/AnalyticalWidgetHeader/index.cjs +1 -1
  33. package/components/AnalyticalWidget/components/AnalyticalWidgetHeader/index.mjs +1 -1
  34. package/components/AnalyticalWidget/index.cjs +1 -1
  35. package/components/AnalyticalWidget/index.mjs +1 -1
  36. package/components/ApprovalCenter/components/index.cjs +1 -1
  37. package/components/ApprovalCenter/components/index.mjs +1 -1
  38. package/components/ApprovalCenter/index.cjs +1 -1
  39. package/components/ApprovalCenter/index.mjs +1 -1
  40. package/components/ApprovalCenter/styled.d.ts +1 -0
  41. package/components/AutocompleteSearch/index.cjs +1 -1
  42. package/components/AutocompleteSearch/index.mjs +1 -1
  43. package/components/BlockTitle/index.cjs +1 -1
  44. package/components/BlockTitle/index.mjs +1 -1
  45. package/components/BlockTitle/utils/index.cjs +1 -1
  46. package/components/BlockTitle/utils/index.mjs +1 -1
  47. package/components/Combobox/index.cjs +1 -1
  48. package/components/Combobox/index.mjs +1 -1
  49. package/components/DrawerDF/components/styled.d.ts +1 -0
  50. package/components/GridDND/index.cjs +1 -1
  51. package/components/GridDND/index.mjs +1 -1
  52. package/components/GridDND/utils/index.cjs +1 -1
  53. package/components/GridDND/utils/index.mjs +1 -1
  54. package/components/LeftPanel/styled.d.ts +1 -0
  55. package/components/ListOfFilters/index.cjs +1 -1
  56. package/components/ListOfFilters/index.mjs +1 -1
  57. package/components/MassActions/hooks/index.cjs +1 -1
  58. package/components/MassActions/hooks/index.mjs +1 -1
  59. package/components/MassActions/index.cjs +1 -1
  60. package/components/MassActions/index.mjs +1 -1
  61. package/components/MassActions/styled.d.ts +7 -2
  62. package/components/PageTitle/index.cjs +1 -1
  63. package/components/PageTitle/index.mjs +1 -1
  64. package/components/PageTitle/utils/index.cjs +1 -1
  65. package/components/PageTitle/utils/index.mjs +1 -1
  66. package/components/PopoverDF/index.cjs +1 -1
  67. package/components/PopoverDF/index.mjs +1 -1
  68. package/components/PopupDF/index.cjs +1 -1
  69. package/components/PopupDF/index.d.ts +1 -1
  70. package/components/PopupDF/index.mjs +108 -94
  71. package/components/PopupDF/types.d.ts +1 -1
  72. package/components/Stories/components/StoriesBanner/index.cjs +1 -1
  73. package/components/Stories/components/StoriesBanner/index.mjs +1 -1
  74. package/components/Stories/components/StoriesContent/index.cjs +1 -1
  75. package/components/Stories/components/StoriesContent/index.mjs +1 -1
  76. package/components/Stories/components/StoriesPreview/index.cjs +1 -1
  77. package/components/Stories/components/StoriesPreview/index.mjs +1 -1
  78. package/components/Stories/components/StoriesProgress/index.cjs +1 -1
  79. package/components/Stories/components/StoriesProgress/index.mjs +1 -1
  80. package/components/Stories/components/StoriesViewer/index.cjs +1 -1
  81. package/components/Stories/components/StoriesViewer/index.mjs +1 -1
  82. package/components/Stories/index.cjs +1 -1
  83. package/components/Stories/index.mjs +1 -1
  84. package/components/Table/components/TableDropdown/index.cjs +1 -1
  85. package/components/Table/components/TableDropdown/index.mjs +1 -1
  86. package/components/Table/feature-collapse-table/CollapseTableButton.d.ts +2 -0
  87. package/components/Table/feature-collapse-table/index.cjs +1 -1
  88. package/components/Table/feature-collapse-table/index.mjs +1 -1
  89. package/components/Table/feature-column-control/ColumnsList/index.cjs +1 -1
  90. package/components/Table/feature-column-control/ColumnsList/index.mjs +1 -1
  91. package/components/Table/feature-column-control/index.cjs +1 -1
  92. package/components/Table/feature-column-control/index.mjs +1 -1
  93. package/components/Table/feature-column-control/styled.d.ts +1 -0
  94. package/components/Table/feature-columns-grouping/index.cjs +1 -1
  95. package/components/Table/feature-columns-grouping/index.mjs +1 -1
  96. package/components/Table/feature-context-menu/index.cjs +1 -1
  97. package/components/Table/feature-context-menu/index.mjs +1 -1
  98. package/components/Table/feature-edit/index.cjs +1 -1
  99. package/components/Table/feature-edit/index.mjs +1 -1
  100. package/components/Table/feature-filtering/combobox/index.cjs +1 -1
  101. package/components/Table/feature-filtering/combobox/index.mjs +1 -1
  102. package/components/Table/feature-filtering/filter-list-block/index.cjs +1 -1
  103. package/components/Table/feature-filtering/filter-list-block/index.mjs +1 -1
  104. package/components/Table/feature-filtering/header-filter-button/index.cjs +1 -1
  105. package/components/Table/feature-filtering/header-filter-button/index.mjs +1 -1
  106. package/components/Table/feature-filtering/header-filter-button/styles.d.ts +1 -0
  107. package/components/Table/feature-filtering/index.cjs +1 -1
  108. package/components/Table/feature-filtering/index.mjs +1 -1
  109. package/components/Table/feature-filtering/sidebar-filter-block/index.cjs +1 -1
  110. package/components/Table/feature-filtering/sidebar-filter-block/index.mjs +1 -1
  111. package/components/Table/feature-infinity-scroll/index.cjs +1 -1
  112. package/components/Table/feature-infinity-scroll/index.mjs +1 -1
  113. package/components/Table/feature-key-text/index.cjs +1 -1
  114. package/components/Table/feature-key-text/index.mjs +1 -1
  115. package/components/Table/feature-pagination/index.cjs +1 -1
  116. package/components/Table/feature-pagination/index.mjs +1 -1
  117. package/components/Table/feature-right-sidebar/index.cjs +1 -1
  118. package/components/Table/feature-right-sidebar/index.mjs +1 -1
  119. package/components/Table/feature-row-detail/handlers.d.ts +1 -0
  120. package/components/Table/feature-row-detail/index.cjs +1 -1
  121. package/components/Table/feature-row-detail/index.mjs +1 -1
  122. package/components/Table/feature-row-instruments/index.cjs +1 -1
  123. package/components/Table/feature-row-instruments/index.mjs +1 -1
  124. package/components/Table/feature-row-size/index.cjs +1 -1
  125. package/components/Table/feature-row-size/index.mjs +1 -1
  126. package/components/Table/feature-rows-grouping/index.cjs +1 -1
  127. package/components/Table/feature-rows-grouping/index.mjs +1 -1
  128. package/components/Table/feature-searching/index.cjs +1 -1
  129. package/components/Table/feature-searching/index.mjs +1 -1
  130. package/components/Table/feature-searching/styled.d.ts +1 -0
  131. package/components/Table/feature-select-row/index.cjs +1 -1
  132. package/components/Table/feature-select-row/index.mjs +1 -1
  133. package/components/Table/feature-sorting/index.cjs +1 -1
  134. package/components/Table/feature-sorting/index.mjs +1 -1
  135. package/components/Table/feature-sorting/styles.d.ts +1 -0
  136. package/components/Table/feature-table-settings/index.cjs +1 -1
  137. package/components/Table/feature-table-settings/index.mjs +1 -1
  138. package/components/Table/feature-tree/index.cjs +1 -1
  139. package/components/Table/feature-tree/index.mjs +1 -1
  140. package/components/Table/index.cjs +1 -1
  141. package/components/Table/index.mjs +1 -1
  142. package/components/Table/renders/formatCell/index.cjs +1 -1
  143. package/components/Table/renders/formatCell/index.mjs +1 -1
  144. package/components/Table/styles/index.cjs +1 -1
  145. package/components/Table/styles/index.mjs +1 -1
  146. package/components/Table/table-hooks/index.cjs +1 -1
  147. package/components/Table/table-hooks/index.mjs +1 -1
  148. package/components/Table/types/index.cjs +1 -1
  149. package/components/Table/types/index.mjs +1 -1
  150. package/components/Table/utils/index.cjs +1 -1
  151. package/components/Table/utils/index.mjs +1 -1
  152. package/components/Table/widgets/control-block/control-block-button.d.ts +3 -2
  153. package/components/Table/widgets/control-block/index.cjs +1 -1
  154. package/components/Table/widgets/control-block/index.mjs +1 -1
  155. package/components/Table/widgets/control-block/right-side/index.cjs +1 -1
  156. package/components/Table/widgets/control-block/right-side/index.mjs +1 -1
  157. package/components/Table/widgets/index.cjs +1 -1
  158. package/components/Table/widgets/index.mjs +1 -1
  159. package/components/Table/widgets/mass-actions/index.cjs +1 -1
  160. package/components/Table/widgets/mass-actions/index.mjs +1 -1
  161. package/components/TableCanvas/TableGlideInstance/adapters/column/index.cjs +1 -1
  162. package/components/TableCanvas/TableGlideInstance/adapters/column/index.mjs +1 -1
  163. package/components/TableCanvas/TableGlideInstance/adapters/index.cjs +1 -1
  164. package/components/TableCanvas/TableGlideInstance/adapters/index.mjs +1 -1
  165. package/components/TableCanvas/TableGlideInstance/index.cjs +1 -1
  166. package/components/TableCanvas/TableGlideInstance/index.mjs +2 -2
  167. package/components/TableCanvas/TableGlideInstance/type.d.ts +3 -1
  168. package/components/TableCanvas/TableGlideInstance/utils/column/index.cjs +1 -1
  169. package/components/TableCanvas/TableGlideInstance/utils/column/index.mjs +1 -1
  170. package/components/TableCanvas/TableGlideInstance/utils/index.cjs +1 -1
  171. package/components/TableCanvas/TableGlideInstance/utils/index.mjs +1 -1
  172. package/components/TableCanvas/components/SplitIconButton/index.cjs +1 -1
  173. package/components/TableCanvas/components/SplitIconButton/index.mjs +1 -1
  174. package/components/TableCanvas/components/TableDropdown/index.cjs +1 -1
  175. package/components/TableCanvas/components/TableDropdown/index.mjs +1 -1
  176. package/components/TableCanvas/feature-cell-transfer/index.cjs +1 -1
  177. package/components/TableCanvas/feature-cell-transfer/index.mjs +1 -1
  178. package/components/TableCanvas/feature-cell-transfer/types.d.ts +1 -1
  179. package/components/TableCanvas/feature-cell-transfer/utils/applyBlockEdit.d.ts +12 -0
  180. package/components/TableCanvas/feature-cell-transfer/utils/applyValuesToRows.d.ts +2 -1
  181. package/components/TableCanvas/feature-cell-transfer/utils/collectMatrixFromTargets.d.ts +1 -0
  182. package/components/TableCanvas/feature-cell-transfer/utils/collectTextMatrix.d.ts +1 -0
  183. package/components/TableCanvas/feature-cell-transfer/utils/resolveBlockOrigin.d.ts +13 -0
  184. package/components/TableCanvas/feature-cell-transfer/utils/snapDestToRowBlocks.d.ts +4 -0
  185. package/components/TableCanvas/feature-collapse-table/CollapseTableButton.d.ts +3 -2
  186. package/components/TableCanvas/feature-collapse-table/index.cjs +1 -1
  187. package/components/TableCanvas/feature-collapse-table/index.mjs +1 -1
  188. package/components/TableCanvas/feature-column-control/ColumnsList/index.cjs +1 -1
  189. package/components/TableCanvas/feature-column-control/ColumnsList/index.mjs +1 -1
  190. package/components/TableCanvas/feature-column-control/index.cjs +1 -1
  191. package/components/TableCanvas/feature-column-control/index.mjs +1 -1
  192. package/components/TableCanvas/feature-column-control/styled.d.ts +1 -0
  193. package/components/TableCanvas/feature-column-control/types.d.ts +3 -0
  194. package/components/TableCanvas/feature-columns-grouping/index.cjs +1 -1
  195. package/components/TableCanvas/feature-columns-grouping/index.mjs +1 -1
  196. package/components/TableCanvas/feature-columns-grouping/useLastLvlColumnConfig.d.ts +5 -1
  197. package/components/TableCanvas/feature-content-state/index.cjs +1 -1
  198. package/components/TableCanvas/feature-content-state/index.mjs +1 -1
  199. package/components/TableCanvas/feature-context-menu/index.cjs +1 -1
  200. package/components/TableCanvas/feature-context-menu/index.mjs +1 -1
  201. package/components/TableCanvas/feature-edit/index.cjs +1 -1
  202. package/components/TableCanvas/feature-edit/index.mjs +1 -1
  203. package/components/TableCanvas/feature-filtering/combobox/index.cjs +1 -1
  204. package/components/TableCanvas/feature-filtering/combobox/index.mjs +1 -1
  205. package/components/TableCanvas/feature-filtering/filter-list-block/index.cjs +1 -1
  206. package/components/TableCanvas/feature-filtering/filter-list-block/index.mjs +1 -1
  207. package/components/TableCanvas/feature-filtering/header-filter-button/index.cjs +1 -1
  208. package/components/TableCanvas/feature-filtering/header-filter-button/index.mjs +1 -1
  209. package/components/TableCanvas/feature-filtering/header-filter-button/styles.d.ts +1 -0
  210. package/components/TableCanvas/feature-filtering/index.cjs +1 -1
  211. package/components/TableCanvas/feature-filtering/index.mjs +1 -1
  212. package/components/TableCanvas/feature-filtering/sidebar-filter-block/index.cjs +1 -1
  213. package/components/TableCanvas/feature-filtering/sidebar-filter-block/index.mjs +1 -1
  214. package/components/TableCanvas/feature-infinity-scroll/index.cjs +1 -1
  215. package/components/TableCanvas/feature-infinity-scroll/index.mjs +1 -1
  216. package/components/TableCanvas/feature-key-text/index.cjs +1 -1
  217. package/components/TableCanvas/feature-key-text/index.mjs +1 -1
  218. package/components/TableCanvas/feature-notifications/index.cjs +1 -1
  219. package/components/TableCanvas/feature-notifications/index.mjs +1 -1
  220. package/components/TableCanvas/feature-pagination/index.cjs +1 -1
  221. package/components/TableCanvas/feature-pagination/index.mjs +1 -1
  222. package/components/TableCanvas/feature-popover-table/context/index.cjs +1 -1
  223. package/components/TableCanvas/feature-popover-table/context/index.mjs +1 -1
  224. package/components/TableCanvas/feature-popover-table/hooks/index.cjs +1 -1
  225. package/components/TableCanvas/feature-popover-table/hooks/index.mjs +1 -1
  226. package/components/TableCanvas/feature-popover-table/index.cjs +1 -1
  227. package/components/TableCanvas/feature-popover-table/index.mjs +1 -1
  228. package/components/TableCanvas/feature-popover-table/utils/index.cjs +1 -1
  229. package/components/TableCanvas/feature-popover-table/utils/index.mjs +1 -1
  230. package/components/TableCanvas/feature-right-sidebar/index.cjs +1 -1
  231. package/components/TableCanvas/feature-right-sidebar/index.mjs +1 -1
  232. package/components/TableCanvas/feature-row-detail/handlers.d.ts +1 -0
  233. package/components/TableCanvas/feature-row-detail/index.cjs +1 -1
  234. package/components/TableCanvas/feature-row-detail/index.mjs +1 -1
  235. package/components/TableCanvas/feature-row-instruments/index.cjs +1 -1
  236. package/components/TableCanvas/feature-row-instruments/index.mjs +1 -1
  237. package/components/TableCanvas/feature-row-markers/index.cjs +1 -1
  238. package/components/TableCanvas/feature-row-markers/index.mjs +1 -1
  239. package/components/TableCanvas/feature-row-size/index.cjs +1 -1
  240. package/components/TableCanvas/feature-row-size/index.mjs +1 -1
  241. package/components/TableCanvas/feature-rows-grouping/getColsAfterRowsGrouping.d.ts +2 -1
  242. package/components/TableCanvas/feature-rows-grouping/index.cjs +1 -1
  243. package/components/TableCanvas/feature-rows-grouping/index.mjs +1 -1
  244. package/components/TableCanvas/feature-rows-grouping/mergedView.d.ts +14 -0
  245. package/components/TableCanvas/feature-rows-grouping/types.d.ts +4 -0
  246. package/components/TableCanvas/feature-searching/index.cjs +1 -1
  247. package/components/TableCanvas/feature-searching/index.mjs +1 -1
  248. package/components/TableCanvas/feature-searching/styled.d.ts +1 -0
  249. package/components/TableCanvas/feature-select-row/index.cjs +1 -1
  250. package/components/TableCanvas/feature-select-row/index.mjs +1 -1
  251. package/components/TableCanvas/feature-sorting/index.cjs +1 -1
  252. package/components/TableCanvas/feature-sorting/index.mjs +1 -1
  253. package/components/TableCanvas/feature-sorting/styles.d.ts +1 -0
  254. package/components/TableCanvas/feature-table-settings/index.cjs +1 -1
  255. package/components/TableCanvas/feature-table-settings/index.mjs +1 -1
  256. package/components/TableCanvas/feature-tooltip/index.cjs +1 -1
  257. package/components/TableCanvas/feature-tooltip/index.mjs +1 -1
  258. package/components/TableCanvas/feature-tooltip/utils/index.cjs +1 -1
  259. package/components/TableCanvas/feature-tooltip/utils/index.mjs +1 -1
  260. package/components/TableCanvas/feature-tree/index.cjs +1 -1
  261. package/components/TableCanvas/feature-tree/index.mjs +1 -1
  262. package/components/TableCanvas/feature-tree/mergedSubRows.d.ts +5 -0
  263. package/components/TableCanvas/feature-tree/types.d.ts +2 -0
  264. package/components/TableCanvas/index.cjs +1 -1
  265. package/components/TableCanvas/index.mjs +4 -4
  266. package/components/TableCanvas/renders/formatCell/index.cjs +1 -1
  267. package/components/TableCanvas/renders/formatCell/index.mjs +1 -1
  268. package/components/TableCanvas/renders/rowIconConfig.d.ts +0 -5
  269. package/components/TableCanvas/renders/withSelectIcon.d.ts +3 -1
  270. package/components/TableCanvas/styles/index.cjs +1 -1
  271. package/components/TableCanvas/styles/index.mjs +1 -1
  272. package/components/TableCanvas/table-hooks/createMergeByValueRowSpan.d.ts +7 -0
  273. package/components/TableCanvas/table-hooks/createMergedRegionsResolver.d.ts +21 -0
  274. package/components/TableCanvas/table-hooks/index.cjs +1 -1
  275. package/components/TableCanvas/table-hooks/index.mjs +1 -1
  276. package/components/TableCanvas/table-hooks/resolveMergedView.d.ts +7 -0
  277. package/components/TableCanvas/table-hooks/usePrepareTableConfig.d.ts +4 -1
  278. package/components/TableCanvas/types/column-config-internal.type.d.ts +4 -1
  279. package/components/TableCanvas/types/column-config.type.d.ts +2 -0
  280. package/components/TableCanvas/types/ctxs.type.d.ts +2 -0
  281. package/components/TableCanvas/types/glide-type.d.ts +1 -1
  282. package/components/TableCanvas/types/index.cjs +1 -1
  283. package/components/TableCanvas/types/index.d.ts +1 -0
  284. package/components/TableCanvas/types/index.mjs +1 -1
  285. package/components/TableCanvas/types/merged-cells.type.d.ts +4 -0
  286. package/components/TableCanvas/types/table-config.type.d.ts +15 -0
  287. package/components/TableCanvas/utils/index.cjs +1 -1
  288. package/components/TableCanvas/utils/index.mjs +1 -1
  289. package/components/TableCanvas/widgets/control-block/compression/index.cjs +1 -1
  290. package/components/TableCanvas/widgets/control-block/compression/index.mjs +1 -1
  291. package/components/TableCanvas/widgets/control-block/control-block-button.d.ts +3 -2
  292. package/components/TableCanvas/widgets/control-block/index.cjs +1 -1
  293. package/components/TableCanvas/widgets/control-block/index.mjs +1 -1
  294. package/components/TableCanvas/widgets/control-block/right-side/index.cjs +1 -1
  295. package/components/TableCanvas/widgets/control-block/right-side/index.mjs +1 -1
  296. package/components/TableCanvas/widgets/index.cjs +1 -1
  297. package/components/TableCanvas/widgets/index.mjs +1 -1
  298. package/components/TableCanvas/widgets/mass-actions/hooks/index.cjs +1 -1
  299. package/components/TableCanvas/widgets/mass-actions/hooks/index.mjs +1 -1
  300. package/components/TableCanvas/widgets/mass-actions/index.cjs +1 -1
  301. package/components/TableCanvas/widgets/mass-actions/index.mjs +1 -1
  302. package/components/TableCanvas/widgets/mass-actions/styled.d.ts +10 -4
  303. package/components/TableContract/index.cjs +1 -1
  304. package/components/TableContract/index.mjs +1 -1
  305. package/components/TableGlide/TableGlide.d.ts +1 -1
  306. package/components/TableGlide/hooks/selection/index.cjs +1 -1
  307. package/components/TableGlide/hooks/selection/index.mjs +1 -1
  308. package/components/TableGlide/hooks/useErrorCellRanges.d.ts +14 -0
  309. package/components/TableGlide/index.cjs +1 -1
  310. package/components/TableGlide/index.mjs +13 -13
  311. package/components/TableGlide/lib/canvas/cells/buttons/index.cjs +1 -1
  312. package/components/TableGlide/lib/canvas/cells/buttons/index.mjs +1 -1
  313. package/components/TableGlide/lib/canvas/cells/iconSprites/index.cjs +1 -1
  314. package/components/TableGlide/lib/canvas/cells/iconSprites/index.mjs +1 -1
  315. package/components/TableGlide/lib/canvas/cells/index.cjs +1 -1
  316. package/components/TableGlide/lib/canvas/cells/index.mjs +1 -1
  317. package/components/TableGlide/lib/canvas/cells/renderer/index.cjs +1 -1
  318. package/components/TableGlide/lib/canvas/cells/renderer/index.mjs +1 -1
  319. package/components/TableGlide/lib/canvas/core/helpers/index.cjs +1 -1
  320. package/components/TableGlide/lib/canvas/core/helpers/index.mjs +1 -1
  321. package/components/TableGlide/lib/canvas/core/index.cjs +1 -1
  322. package/components/TableGlide/lib/canvas/core/index.mjs +1 -1
  323. package/components/TableGlide/lib/canvas/hooks/index.cjs +1 -1
  324. package/components/TableGlide/lib/canvas/hooks/index.mjs +1 -1
  325. package/components/TableGlide/lib/canvas/index.cjs +1 -1
  326. package/components/TableGlide/lib/canvas/index.mjs +22 -22
  327. package/components/TableGlide/lib/canvas/miniflex/index.cjs +1 -1
  328. package/components/TableGlide/lib/canvas/miniflex/index.mjs +1 -1
  329. package/components/TableGlide/lib/canvas/primitives/index.cjs +1 -1
  330. package/components/TableGlide/lib/canvas/primitives/index.mjs +1 -1
  331. package/components/TableGlide/lib/canvas/utils/index.cjs +1 -1
  332. package/components/TableGlide/lib/canvas/utils/index.mjs +2 -2
  333. package/components/TableGlide/theming/index.cjs +1 -1
  334. package/components/TableGlide/theming/index.mjs +1 -1
  335. package/components/TableGlide/types.d.ts +10 -0
  336. package/components/TableGlide/utils/findBlockOrigin.d.ts +5 -0
  337. package/components/TableGlide/utils/getRowSpan.d.ts +6 -0
  338. package/components/Tooltip/index.cjs +1 -1
  339. package/components/Tooltip/index.mjs +1 -1
  340. package/components/TourWidget/index.cjs +1 -1
  341. package/components/TourWidget/index.mjs +1 -1
  342. package/components/Typography/index.cjs +1 -1
  343. package/components/Typography/index.mjs +1 -1
  344. package/components/Widget/Widget.d.ts +1 -0
  345. package/components/Widget/components/WidgetIconButtonDots.d.ts +1 -0
  346. package/components/Widget/index.cjs +1 -1
  347. package/components/Widget/index.mjs +1 -1
  348. package/formComponents/FormCombobox/index.cjs +1 -1
  349. package/formComponents/FormCombobox/index.mjs +1 -1
  350. package/formComponents/FormDatePickers/FormDatePickerRange/ui/FormDatePickerRangeStyled.d.ts +2 -0
  351. package/formComponents/FormSwitch/index.cjs +1 -1
  352. package/formComponents/FormSwitch/index.mjs +1 -1
  353. package/formComponents/FormTextArea/ui/ButtonStyled.d.ts +1 -0
  354. package/formComponents/FormTextField/ui/IconButtonStyled.d.ts +1 -0
  355. package/index.cjs +1 -1
  356. package/index.mjs +11 -11
  357. package/layouts/FiltersActions/FiltersActions.d.ts +15 -6
  358. package/layouts/FiltersActions/components/FiltersButton.d.ts +4 -0
  359. package/layouts/FiltersActions/components/ResetAllFiltersButton.d.ts +6 -4
  360. package/layouts/FiltersActions/components/Tabs.d.ts +4 -2
  361. package/layouts/FiltersActions/hooks/index.cjs +1 -1
  362. package/layouts/FiltersActions/hooks/index.mjs +1 -1
  363. package/layouts/FiltersActions/index.cjs +1 -1
  364. package/layouts/FiltersActions/index.mjs +1 -1
  365. package/mcp-data/component-index.json +250 -80
  366. package/package.json +7 -6
  367. package/shared/ui/Search/styled.d.ts +1 -0
  368. package/chunks/AnalyticalWidget-z2HzQ4aA.cjs +0 -43
  369. package/chunks/TableCanvas-uzBMXlx6.cjs +0 -30
  370. package/chunks/TableGlide-DTBZ1jNA.cjs +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-09-08T13:18:19.046Z",
3
- "libVersion": "1.17.0",
2
+ "generatedAt": "2026-09-08T15:47:33.375Z",
3
+ "libVersion": "1.18.0",
4
4
  "components": {
5
5
  "Accordion": {
6
6
  "name": "Accordion",
@@ -778,7 +778,7 @@
778
778
  },
779
779
  {
780
780
  "name": "view",
781
- "type": "\"clear\" | \"default\" | \"secondary\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | undefined",
781
+ "type": "\"clear\" | \"default\" | \"secondary\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
782
782
  "required": false,
783
783
  "description": ""
784
784
  }
@@ -860,7 +860,7 @@
860
860
  },
861
861
  {
862
862
  "name": "view",
863
- "type": "\"default\" | \"secondary\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"info\" | undefined",
863
+ "type": "\"default\" | \"secondary\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"gradient\" | \"info\" | undefined",
864
864
  "required": false,
865
865
  "description": "",
866
866
  "default": "'secondary'"
@@ -5518,10 +5518,10 @@
5518
5518
  },
5519
5519
  {
5520
5520
  "exportName": "Usage2",
5521
- "displayName": "Использование в TableCanvas.cellsSelection.stories.tsx",
5521
+ "displayName": "Использование в TableCanvas.bigData.stories.tsx",
5522
5522
  "type": "usage",
5523
- "code": "<Checkbox\n checked={enableRowSelection}\n onChange={(e) => setEnableRowSelection(e.target.checked)}\n label={<div>enableRowSelection</div>}\n />",
5524
- "sourceFile": "packages/storybook/src/stories/TableCanvas/TableCanvas.CellsSelection/TableCanvas.cellsSelection.stories.tsx"
5523
+ "code": "<Checkbox\n label=\"объединение (subRows merged)\"\n checked={withMerge}\n onChange={(e) => setWithMerge(e.target.checked)}\n />",
5524
+ "sourceFile": "packages/storybook/src/stories/TableCanvas/TableCanvas.CellsMerging/TableCanvas.bigData.stories.tsx"
5525
5525
  },
5526
5526
  {
5527
5527
  "exportName": "Vendor1",
@@ -9597,6 +9597,12 @@
9597
9597
  "required": false,
9598
9598
  "description": ""
9599
9599
  },
9600
+ {
9601
+ "name": "overlayProps",
9602
+ "type": "any",
9603
+ "required": false,
9604
+ "description": ""
9605
+ },
9600
9606
  {
9601
9607
  "name": "withBlur",
9602
9608
  "type": "boolean | undefined",
@@ -10357,7 +10363,7 @@
10357
10363
  },
10358
10364
  {
10359
10365
  "name": "view",
10360
- "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"secondary\" | undefined",
10366
+ "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"accent\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"secondary\" | \"gradient\" | undefined",
10361
10367
  "required": false,
10362
10368
  "description": ""
10363
10369
  }
@@ -12381,7 +12387,7 @@
12381
12387
  },
12382
12388
  {
12383
12389
  "name": "view",
12384
- "type": "\"default\" | \"accent\" | \"secondary\" | \"positive\" | \"warning\" | \"negative\" | \"info\" | undefined",
12390
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"positive\" | \"warning\" | \"negative\" | \"info\" | \"gradient\" | undefined",
12385
12391
  "required": false,
12386
12392
  "description": ""
12387
12393
  }
@@ -13676,7 +13682,7 @@
13676
13682
  },
13677
13683
  {
13678
13684
  "name": "view",
13679
- "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | undefined",
13685
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
13680
13686
  "required": false,
13681
13687
  "description": ""
13682
13688
  }
@@ -13850,7 +13856,7 @@
13850
13856
  },
13851
13857
  {
13852
13858
  "name": "view",
13853
- "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | undefined",
13859
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
13854
13860
  "required": false,
13855
13861
  "description": ""
13856
13862
  }
@@ -14520,7 +14526,7 @@
14520
14526
  },
14521
14527
  {
14522
14528
  "name": "view",
14523
- "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"dark\" | undefined",
14529
+ "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"dark\" | \"gradient\" | undefined",
14524
14530
  "required": false,
14525
14531
  "description": ""
14526
14532
  }
@@ -14751,7 +14757,7 @@
14751
14757
  },
14752
14758
  {
14753
14759
  "name": "view",
14754
- "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"dark\" | undefined",
14760
+ "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"dark\" | \"gradient\" | undefined",
14755
14761
  "required": false,
14756
14762
  "description": ""
14757
14763
  }
@@ -16092,7 +16098,7 @@
16092
16098
  },
16093
16099
  {
16094
16100
  "name": "view",
16095
- "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"clear\" | \"dark\" | \"black\" | \"white\" | undefined",
16101
+ "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"clear\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
16096
16102
  "required": false,
16097
16103
  "description": ""
16098
16104
  }
@@ -16611,7 +16617,7 @@
16611
16617
  },
16612
16618
  {
16613
16619
  "name": "view",
16614
- "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | undefined",
16620
+ "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"gradient\" | undefined",
16615
16621
  "required": false,
16616
16622
  "description": ""
16617
16623
  }
@@ -17015,7 +17021,7 @@
17015
17021
  },
17016
17022
  {
17017
17023
  "name": "view",
17018
- "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | undefined",
17024
+ "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"gradient\" | undefined",
17019
17025
  "required": false,
17020
17026
  "description": ""
17021
17027
  }
@@ -17714,7 +17720,7 @@
17714
17720
  },
17715
17721
  {
17716
17722
  "name": "view",
17717
- "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | undefined",
17723
+ "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"gradient\" | undefined",
17718
17724
  "required": false,
17719
17725
  "description": ""
17720
17726
  }
@@ -17858,7 +17864,7 @@
17858
17864
  },
17859
17865
  {
17860
17866
  "name": "view",
17861
- "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | undefined",
17867
+ "type": "\"default\" | \"dark\" | \"black\" | \"white\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"gradient\" | undefined",
17862
17868
  "required": false,
17863
17869
  "description": ""
17864
17870
  }
@@ -23530,7 +23536,7 @@
23530
23536
  "exportName": "Usage2",
23531
23537
  "displayName": "Использование в PopupDF.stories.tsx",
23532
23538
  "type": "usage",
23533
- "code": "<PopupDF.Footer>\n <FooterActions size={size} />\n </PopupDF.Footer>",
23539
+ "code": "<PopupDF.Footer>\n <FooterActions size=\"l\" />\n </PopupDF.Footer>",
23534
23540
  "sourceFile": "packages/storybook/src/stories/PopupDF/PopupDF.stories.tsx"
23535
23541
  }
23536
23542
  ]
@@ -23564,7 +23570,7 @@
23564
23570
  "name": "rightBlock",
23565
23571
  "type": "React.ReactNode",
23566
23572
  "required": false,
23567
- "description": "Кастомный слот в правой части header, слева от крестика закрытия.\nПрижимается к крестику; если контент широкий — «давит» на title/description\n(уходят в многоточие с тултипом). Максимальная высота — как у крестика\n(32px при `size='m'`, 24px при `size='s'`), ширина не ограничена."
23573
+ "description": "Кастомный слот в правой части header, слева от крестика закрытия.\nПрижимается к крестику; если контент широкий — «давит» на title/description\n(уходят в многоточие с тултипом). Максимальная высота — как у крестика\n(32px при `size='l'` и `size='m'`, 24px при `size='s'`), ширина не\nограничена."
23568
23574
  },
23569
23575
  {
23570
23576
  "name": "showCloseButton",
@@ -23617,7 +23623,7 @@
23617
23623
  "exportName": "Usage2",
23618
23624
  "displayName": "Использование в PopupDF.stories.tsx",
23619
23625
  "type": "usage",
23620
- "code": "<PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n rightBlock={<RightBlockSlot />}\n />",
23626
+ "code": "<PopupDF.Header\n title=\"Заголовок PopupDF размера L\"\n description=\"Body S. Двигай за шапку, тяни за угол, разворачивай кнопкой.\"\n subHeader={\n <div style={sizeLSubHeaderStyle}>\n <IconInfoCircleOutline size=\"xs\" />\n <span>Кастомный слот</span>\n </div>\n }\n rightBlock={\n <IconButton\n size=\"xs\"\n view=\"secondary\"\n pin=\"circle-circle\"\n title={fullscreen ? 'Свернуть' : 'Развернуть'}\n onClick={() => (fullscreen ? collapse() : expand())}\n >\n <IconFullS size=\"xs\" />\n </IconButton>\n }\n />",
23621
23627
  "sourceFile": "packages/storybook/src/stories/PopupDF/PopupDF.stories.tsx"
23622
23628
  }
23623
23629
  ]
@@ -23745,7 +23751,7 @@
23745
23751
  "name": "rightBlock",
23746
23752
  "type": "React.ReactNode",
23747
23753
  "required": false,
23748
- "description": "Кастомный слот в правой части header, слева от крестика закрытия.\nПрижимается к крестику; если контент широкий — «давит» на title/description\n(уходят в многоточие с тултипом). Максимальная высота — как у крестика\n(32px при `size='m'`, 24px при `size='s'`), ширина не ограничена."
23754
+ "description": "Кастомный слот в правой части header, слева от крестика закрытия.\nПрижимается к крестику; если контент широкий — «давит» на title/description\n(уходят в многоточие с тултипом). Максимальная высота — как у крестика\n(32px при `size='l'` и `size='m'`, 24px при `size='s'`), ширина не\nограничена."
23749
23755
  },
23750
23756
  {
23751
23757
  "name": "showCloseButton",
@@ -24318,7 +24324,7 @@
24318
24324
  "compound popup"
24319
24325
  ],
24320
24326
  "docs": "# PopupDF\n\n`PopupDF` — локальная DF-обёртка над [Popup](https://plasma.sberdevices.ru/sdds-finai/components/popup/).\n\n## Ключевые особенности\n\n- крестик закрытия живёт в `PopupDF.Header`\n- доступны `PopupDF.Header`, `PopupDF.Body`, `PopupDF.Footer`\n- доступны размеры `size=\"m\" | \"s\"`\n- `title` и `description` ограничены двумя строками\n- в `Header` доступна кнопка назад через `onBackButtonClick`\n- иконка ресайза использует DF-иконку по умолчанию, учитывает `resizable.iconSize` и может быть переопределена через `resizable.icons`\n\n## Compound-компоненты\n\n```ts\nPopupDF.Header; // Шапка\nPopupDF.Body; // Контент\nPopupDF.Footer; // Футер\n```\n\n## Особенности\n\n- `Header` и `Footer` остаются опциональными и подключаются через композицию\n- `subHeader` подходит для кастомного контента, смыслово связанного с шапкой\n- если контента много, скролл должен жить внутри `PopupDF.Body`\n- по умолчанию угол иконки ресайза вычисляется из `placement` так, чтобы ресайз тянул popup внутрь доступной области\n- для ручной смены угла ресайза используйте нативный `resizable.directions`, например `directions: ['top-left']`\n- если `hiddenIcons` не передан, `PopupDF` автоматически скрывает угловые иконки, которые не входят в `directions`; для нестандартной видимости передайте `hiddenIcons` явно\n- если пользовательские `icons` не переданы, `PopupDF` заполняет угловые иконки своей `IconResizeCorneredFill`; `iconSize` влияет на DF-иконку по умолчанию\n\nОписание типов и compound-пропсов — в разделе [API](?path=/docs/локальные-компоненты-popupdf-api--docs).",
24321
- "apiDocs": "# PopupDF API\n\n## PopupDFProps\n\n```typescript\ntype PopupDFProps = Omit<\n ComponentProps<typeof Popup>,\n 'children' | 'isOpen'\n> & {\n /**\n * Содержимое popup.\n * Обычно используется композиция из `PopupDF.Header`, `PopupDF.Body` и `PopupDF.Footer`.\n */\n children?: ReactNode;\n /**\n * Начальное состояние открытия в uncontrolled-режиме.\n * @default false\n */\n defaultOpened?: boolean;\n /**\n * Коллбэк смены состояния открытия.\n */\n onToggle?: (opened: boolean) => void;\n /**\n * Размер PopupDF.\n * Влияет на внутренние отступы и типографику header.\n * @default 'm'\n */\n size?: PopupDFSize;\n};\n```\n\n> `PopupDF` наследует базовые пропсы `Popup` из `sdds-finai`, но добавляет compound-структуру,\n`size`, `defaultOpened`, `onToggle` и DF-иконки для базовой настройки `resizable`.\n\n## PopupDFHeaderProps\n\n```typescript\ntype PopupDFHeaderProps = Omit<BoxProps, 'children'> & {\n /**\n * Заголовок в верхней левой части header.\n */\n title?: ReactNode;\n /**\n * Текст под заголовком в верхней левой части header.\n */\n description?: ReactNode;\n /**\n * Кастомный контент, относящийся по смыслу к header.\n */\n subHeader?: ReactNode;\n /**\n * Кастомный слот в правой части header, слева от крестика закрытия.\n * Прижимается к крестику; если контент широкий — «давит» на title/description\n * (уходят в многоточие с тултипом). Максимальная высота — как у крестика\n * (32px при `size='m'`, 24px при `size='s'`), ширина не ограничена.\n */\n rightBlock?: ReactNode;\n /**\n * Показывать ли крестик закрытия в правой верхней части header.\n * @default true\n */\n showCloseButton?: boolean;\n /**\n * Callback при клике на кнопку со стрелкой назад слева от заголовка.\n */\n onBackButtonClick?: () => void;\n /**\n * Кастомный обработчик закрытия.\n * Если не передан, будет использован обработчик закрытия из `PopupDF`.\n */\n onClose?: () => void;\n};\n```\n\n## PopupDFBodyProps\n\n```typescript\ntype PopupDFBodyProps = BoxProps;\n```\n\n## PopupDFFooterProps\n\n```typescript\ntype PopupDFFooterProps = BoxProps & {\n children?: ReactNode;\n};\n```\n\n## PopupDFResizableConfig\n\n```typescript\ntype PopupDFResizableConfig = Exclude<\n ComponentProps<typeof Popup>['resizable'],\n boolean | undefined\n>;\n```",
24327
+ "apiDocs": "# PopupDF API\n\n## PopupDFProps\n\n```typescript\ntype PopupDFProps = Omit<\n ComponentProps<typeof Popup>,\n 'children' | 'isOpen'\n> & {\n /**\n * Содержимое popup.\n * Обычно используется композиция из `PopupDF.Header`, `PopupDF.Body` и `PopupDF.Footer`.\n */\n children?: ReactNode;\n /**\n * Начальное состояние открытия в uncontrolled-режиме.\n * @default false\n */\n defaultOpened?: boolean;\n /**\n * Коллбэк смены состояния открытия.\n */\n onToggle?: (opened: boolean) => void;\n /**\n * Размер PopupDF.\n * Влияет на внутренние отступы и типографику header.\n * @default 'm'\n */\n size?: PopupDFSize;\n};\n```\n\n> `PopupDF` наследует базовые пропсы `Popup` из `sdds-finai`, но добавляет compound-структуру,\n`size`, `defaultOpened`, `onToggle` и DF-иконки для базовой настройки `resizable`.\n\n## PopupDFSize\n\n```typescript\ntype PopupDFSize = 's' | 'm' | 'l';\n```\n\n> Размер попапа. Влияет на типографику шапки и внутренние отступы: `l` — крупный\nзаголовок (H4) с описанием (BodyS), `m` — средний, `s` — компактный.\n\n## PopupDFHeaderProps\n\n```typescript\ntype PopupDFHeaderProps = Omit<BoxProps, 'children'> & {\n /**\n * Заголовок в верхней левой части header.\n */\n title?: ReactNode;\n /**\n * Текст под заголовком в верхней левой части header.\n */\n description?: ReactNode;\n /**\n * Кастомный контент, относящийся по смыслу к header.\n */\n subHeader?: ReactNode;\n /**\n * Кастомный слот в правой части header, слева от крестика закрытия.\n * Прижимается к крестику; если контент широкий — «давит» на title/description\n * (уходят в многоточие с тултипом). Максимальная высота — как у крестика\n * (32px при `size='l'` и `size='m'`, 24px при `size='s'`), ширина не\n * ограничена.\n */\n rightBlock?: ReactNode;\n /**\n * Показывать ли крестик закрытия в правой верхней части header.\n * @default true\n */\n showCloseButton?: boolean;\n /**\n * Callback при клике на кнопку со стрелкой назад слева от заголовка.\n */\n onBackButtonClick?: () => void;\n /**\n * Кастомный обработчик закрытия.\n * Если не передан, будет использован обработчик закрытия из `PopupDF`.\n */\n onClose?: () => void;\n};\n```\n\n## PopupDFBodyProps\n\n```typescript\ntype PopupDFBodyProps = BoxProps;\n```\n\n## PopupDFFooterProps\n\n```typescript\ntype PopupDFFooterProps = BoxProps & {\n children?: ReactNode;\n};\n```\n\n## PopupDFResizableConfig\n\n```typescript\ntype PopupDFResizableConfig = Exclude<\n ComponentProps<typeof Popup>['resizable'],\n boolean | undefined\n>;\n```",
24322
24328
  "curatedStories": [
24323
24329
  {
24324
24330
  "exportName": "Simple",
@@ -24331,6 +24337,12 @@
24331
24337
  "displayName": "Playground",
24332
24338
  "type": "full-code",
24333
24339
  "code": "import { useState } from 'react';\nimport {\n Button,\n Flow,\n LinkButton,\n PopupDF,\n PopupProvider,\n SSRProvider,\n} from '@daisforge/ui';\n\n// Контент футера — обычные кнопки, которые вы кладёте в PopupDF.Footer\nfunction FooterActions() {\n return (\n <Flow mainAxisGap={8}>\n <LinkButton href=\"/\" size=\"xs\">\n LinkButton\n </LinkButton>\n <Button size=\"xs\" view=\"secondary\">\n Отмена\n </Button>\n <Button size=\"xs\" view=\"accent\">\n Применить\n </Button>\n </Flow>\n );\n}\nfunction Example() {\n const [opened, setOpened] = useState(true);\n\n return (\n <SSRProvider>\n <PopupProvider>\n <Button onClick={() => setOpened(true)}>Открыть PopupDF</Button>\n <PopupDF\n opened={opened}\n onToggle={setOpened}\n size=\"m\"\n placement=\"center\"\n offset={[0, 0]}\n draggable\n resizable={{ minWidth: 240, minHeight: 120, iconSize: 's' }}\n style={{ width: '396px' }}\n >\n <PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n rightBlock={\n <div style={{ display: 'flex', alignItems: 'center' }}>\n {/* кастомный контент слева от крестика, ширина не ограничена */}\n </div>\n }\n showCloseButton\n />\n <PopupDF.Body>Контент попапа</PopupDF.Body>\n <PopupDF.Footer>\n <FooterActions />\n </PopupDF.Footer>\n </PopupDF>\n </PopupProvider>\n </SSRProvider>\n );\n}"
24340
+ },
24341
+ {
24342
+ "exportName": "SizeL",
24343
+ "displayName": "Size L",
24344
+ "type": "full-code",
24345
+ "code": "import { useRef, useState } from 'react';\nimport {\n Button,\n IconButton,\n LinkButton,\n PopupDF,\n PopupProvider,\n SSRProvider,\n} from '@daisforge/ui';\nimport { IconFullscreenOff, IconFullscreenOn } from '@daisforge/ui/icons';\n\n// Разворот на весь экран собираем сами из пропсов PopupDF:\n// сначала выключаем resizable и draggable, чтобы попап вернулся к обычному\n// размеру, затем следующим тиком плавно растим его до краёв frame.\nfunction Example() {\n const frameRef = useRef(null);\n const [opened, setOpened] = useState(true);\n const [fullscreen, setFullscreen] = useState(false);\n const [interactive, setInteractive] = useState(true);\n\n const expand = () => {\n setInteractive(false);\n window.setTimeout(() => setFullscreen(true), 30);\n };\n\n const collapse = () => {\n setFullscreen(false);\n window.setTimeout(() => setInteractive(true), 320);\n };\n\n const IconFullS = fullscreen ? IconFullscreenOff : IconFullscreenOn;\n\n const sizeStyle = {\n width: fullscreen ? 'calc(100% - 48px)' : '600px',\n height: fullscreen ? 'calc(100% - 48px)' : '600px',\n maxWidth: '100%',\n maxHeight: '100%',\n transition: 'width 280ms ease, height 280ms ease',\n // grid тянет карточку на всю заданную высоту, иначе внешний узел Popup\n // сжимается по контенту и height не применяется (600 даёт только ширину).\n display: 'grid',\n gridTemplateRows: '1fr',\n gridTemplateColumns: '1fr',\n };\n\n return (\n <SSRProvider>\n <PopupProvider>\n {/* frame — контейнер, внутри которого живёт и разворачивается попап */}\n <div ref={frameRef} style={{ position: 'relative', minHeight: 720 }}>\n <PopupDF\n opened={opened}\n onToggle={setOpened}\n frame={frameRef}\n size=\"l\"\n placement=\"center\"\n draggable={interactive}\n resizable={\n interactive\n ? {\n // стартовый размер контейнера при включённом resizable\n defaultSize: { width: 600, height: 600 },\n minWidth: 320,\n minHeight: 220,\n iconSize: 's',\n }\n : false\n }\n style={sizeStyle}\n >\n <PopupDF.Header\n title=\"Заголовок PopupDF размера L\"\n description=\"Body S. Двигай за шапку, тяни за угол, разворачивай кнопкой.\"\n subHeader={\n <div style={{ height: 40, display: 'flex', alignItems: 'center' }}>\n Кастомный слот\n </div>\n }\n rightBlock={\n <IconButton\n size=\"xs\"\n view=\"secondary\"\n pin=\"circle-circle\"\n title={fullscreen ? 'Свернуть' : 'Развернуть'}\n onClick={() => (fullscreen ? collapse() : expand())}\n >\n <IconFullS size=\"xs\" />\n </IconButton>\n }\n />\n {/* высота 100%, чтобы контент тела рос при ресайзе попапа */}\n <PopupDF.Body>\n <div style={{ height: '100%' }}>Контент попапа</div>\n </PopupDF.Body>\n <PopupDF.Footer>\n {/* LinkButton отделён от группы кнопок отступом 16px (L) */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'flex-end',\n alignItems: 'center',\n gap: 16,\n }}\n >\n <LinkButton href=\"/\" size=\"s\">\n LinkButton\n </LinkButton>\n <div style={{ display: 'flex', gap: 4 }}>\n <Button size=\"s\" view=\"secondary\">\n Отмена\n </Button>\n <Button size=\"s\" view=\"accent\">\n Применить\n </Button>\n </div>\n </div>\n </PopupDF.Footer>\n </PopupDF>\n </div>\n </PopupProvider>\n </SSRProvider>\n );\n}"
24334
24346
  }
24335
24347
  ],
24336
24348
  "hasCuratedMeta": true,
@@ -24357,17 +24369,16 @@
24357
24369
  "code": "import { useState } from 'react';\nimport {\n Button,\n Flow,\n LinkButton,\n PopupDF,\n PopupProvider,\n SSRProvider,\n} from '@daisforge/ui';\n\n// Контент футера — обычные кнопки, которые вы кладёте в PopupDF.Footer\nfunction FooterActions() {\n return (\n <Flow mainAxisGap={8}>\n <LinkButton href=\"/\" size=\"xs\">\n LinkButton\n </LinkButton>\n <Button size=\"xs\" view=\"secondary\">\n Отмена\n </Button>\n <Button size=\"xs\" view=\"accent\">\n Применить\n </Button>\n </Flow>\n );\n}\nfunction Example() {\n const [opened, setOpened] = useState(true);\n\n return (\n <SSRProvider>\n <PopupProvider>\n <Button onClick={() => setOpened(true)}>Открыть PopupDF</Button>\n <PopupDF\n opened={opened}\n onToggle={setOpened}\n size=\"m\"\n placement=\"center\"\n offset={[0, 0]}\n draggable\n resizable={{ minWidth: 240, minHeight: 120, iconSize: 's' }}\n style={{ width: '396px' }}\n >\n <PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n rightBlock={\n <div style={{ display: 'flex', alignItems: 'center' }}>\n {/* кастомный контент слева от крестика, ширина не ограничена */}\n </div>\n }\n showCloseButton\n />\n <PopupDF.Body>Контент попапа</PopupDF.Body>\n <PopupDF.Footer>\n <FooterActions />\n </PopupDF.Footer>\n </PopupDF>\n </PopupProvider>\n </SSRProvider>\n );\n}"
24358
24370
  },
24359
24371
  {
24360
- "exportName": "Usage1",
24361
- "displayName": "Использование в PopupDF.stories.tsx",
24362
- "type": "usage",
24363
- "code": "<PopupDF\n opened={opened}\n onToggle={setOpened}\n frame={frameRef}\n placement={placement}\n offset={[offsetX, offsetY]}\n size={size}\n draggable={draggable}\n resizable={resizableConfig}\n style={{ width: '396px' }}\n >\n <PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n subHeader={withSubHeader ? <HeaderSlot /> : undefined}\n rightBlock={\n withRightBlock ? <RightBlockSlot size={size} /> : undefined\n }\n showCloseButton={showCloseButton}\n onBackButtonClick={showBackButton ? () => {} : undefined}\n />\n {withBody && (\n <PopupDF.Body>\n <BodySlot />\n </PopupDF.Body>\n )}\n {withFooter && (\n <PopupDF.Footer>\n <FooterActions size={size} />\n </PopupDF.Footer>\n )}\n </PopupDF>",
24364
- "sourceFile": "packages/storybook/src/stories/PopupDF/PopupDF.stories.tsx"
24372
+ "exportName": "SizeL",
24373
+ "displayName": "Size L",
24374
+ "type": "full-code",
24375
+ "code": "import { useRef, useState } from 'react';\nimport {\n Button,\n IconButton,\n LinkButton,\n PopupDF,\n PopupProvider,\n SSRProvider,\n} from '@daisforge/ui';\nimport { IconFullscreenOff, IconFullscreenOn } from '@daisforge/ui/icons';\n\n// Разворот на весь экран собираем сами из пропсов PopupDF:\n// сначала выключаем resizable и draggable, чтобы попап вернулся к обычному\n// размеру, затем следующим тиком плавно растим его до краёв frame.\nfunction Example() {\n const frameRef = useRef(null);\n const [opened, setOpened] = useState(true);\n const [fullscreen, setFullscreen] = useState(false);\n const [interactive, setInteractive] = useState(true);\n\n const expand = () => {\n setInteractive(false);\n window.setTimeout(() => setFullscreen(true), 30);\n };\n\n const collapse = () => {\n setFullscreen(false);\n window.setTimeout(() => setInteractive(true), 320);\n };\n\n const IconFullS = fullscreen ? IconFullscreenOff : IconFullscreenOn;\n\n const sizeStyle = {\n width: fullscreen ? 'calc(100% - 48px)' : '600px',\n height: fullscreen ? 'calc(100% - 48px)' : '600px',\n maxWidth: '100%',\n maxHeight: '100%',\n transition: 'width 280ms ease, height 280ms ease',\n // grid тянет карточку на всю заданную высоту, иначе внешний узел Popup\n // сжимается по контенту и height не применяется (600 даёт только ширину).\n display: 'grid',\n gridTemplateRows: '1fr',\n gridTemplateColumns: '1fr',\n };\n\n return (\n <SSRProvider>\n <PopupProvider>\n {/* frame — контейнер, внутри которого живёт и разворачивается попап */}\n <div ref={frameRef} style={{ position: 'relative', minHeight: 720 }}>\n <PopupDF\n opened={opened}\n onToggle={setOpened}\n frame={frameRef}\n size=\"l\"\n placement=\"center\"\n draggable={interactive}\n resizable={\n interactive\n ? {\n // стартовый размер контейнера при включённом resizable\n defaultSize: { width: 600, height: 600 },\n minWidth: 320,\n minHeight: 220,\n iconSize: 's',\n }\n : false\n }\n style={sizeStyle}\n >\n <PopupDF.Header\n title=\"Заголовок PopupDF размера L\"\n description=\"Body S. Двигай за шапку, тяни за угол, разворачивай кнопкой.\"\n subHeader={\n <div style={{ height: 40, display: 'flex', alignItems: 'center' }}>\n Кастомный слот\n </div>\n }\n rightBlock={\n <IconButton\n size=\"xs\"\n view=\"secondary\"\n pin=\"circle-circle\"\n title={fullscreen ? 'Свернуть' : 'Развернуть'}\n onClick={() => (fullscreen ? collapse() : expand())}\n >\n <IconFullS size=\"xs\" />\n </IconButton>\n }\n />\n {/* высота 100%, чтобы контент тела рос при ресайзе попапа */}\n <PopupDF.Body>\n <div style={{ height: '100%' }}>Контент попапа</div>\n </PopupDF.Body>\n <PopupDF.Footer>\n {/* LinkButton отделён от группы кнопок отступом 16px (L) */}\n <div\n style={{\n display: 'flex',\n justifyContent: 'flex-end',\n alignItems: 'center',\n gap: 16,\n }}\n >\n <LinkButton href=\"/\" size=\"s\">\n LinkButton\n </LinkButton>\n <div style={{ display: 'flex', gap: 4 }}>\n <Button size=\"s\" view=\"secondary\">\n Отмена\n </Button>\n <Button size=\"s\" view=\"accent\">\n Применить\n </Button>\n </div>\n </div>\n </PopupDF.Footer>\n </PopupDF>\n </div>\n </PopupProvider>\n </SSRProvider>\n );\n}"
24365
24376
  },
24366
24377
  {
24367
- "exportName": "Usage2",
24378
+ "exportName": "Usage1",
24368
24379
  "displayName": "Использование в PopupDF.stories.tsx",
24369
24380
  "type": "usage",
24370
- "code": "<PopupDF\n opened={opened}\n onToggle={setOpened}\n frame={frameRef}\n size=\"m\"\n placement=\"center\"\n style={{ width: '396px' }}\n >\n <PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n rightBlock={<RightBlockSlot />}\n />\n <PopupDF.Body>\n <BodySlot />\n </PopupDF.Body>\n <PopupDF.Footer>\n <FooterActions />\n </PopupDF.Footer>\n </PopupDF>",
24381
+ "code": "<PopupDF\n opened={opened}\n onToggle={setOpened}\n frame={frameRef}\n placement={placement}\n offset={[offsetX, offsetY]}\n size={size}\n draggable={draggable}\n resizable={resizableConfig}\n style={{ width: '396px' }}\n >\n <PopupDF.Header\n title=\"Title\"\n description=\"Description\"\n subHeader={withSubHeader ? <HeaderSlot /> : undefined}\n rightBlock={\n withRightBlock ? <RightBlockSlot size={size} /> : undefined\n }\n showCloseButton={showCloseButton}\n onBackButtonClick={showBackButton ? () => {} : undefined}\n />\n {withBody && (\n <PopupDF.Body>\n <BodySlot />\n </PopupDF.Body>\n )}\n {withFooter && (\n <PopupDF.Footer>\n <FooterActions size={size} />\n </PopupDF.Footer>\n )}\n </PopupDF>",
24371
24382
  "sourceFile": "packages/storybook/src/stories/PopupDF/PopupDF.stories.tsx"
24372
24383
  }
24373
24384
  ]
@@ -26609,10 +26620,10 @@
26609
26620
  },
26610
26621
  {
26611
26622
  "exportName": "Usage2",
26612
- "displayName": "Использование в TableCanvas.multipleFeatures.stories.tsx",
26623
+ "displayName": "Использование в TableCanvas.cellsMerging.stories.tsx",
26613
26624
  "type": "usage",
26614
- "code": "<Select\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(value) => setSelectionMode(value as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />",
26615
- "sourceFile": "packages/storybook/src/stories/TableCanvas/TableCanvas.MultipleFeatures/TableCanvas.multipleFeatures.stories.tsx"
26625
+ "code": "<Select\n target=\"textfield-like\"\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(v) => setSelectionMode(v as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />",
26626
+ "sourceFile": "packages/storybook/src/stories/TableCanvas/TableCanvas.CellsMerging/TableCanvas.cellsMerging.stories.tsx"
26616
26627
  },
26617
26628
  {
26618
26629
  "exportName": "Vendor1",
@@ -27397,6 +27408,13 @@
27397
27408
  "importStatement": "import { Slider } from '@daisforge/ui';",
27398
27409
  "minimalUsage": "<Slider max={0} min={0} />",
27399
27410
  "examples": [
27411
+ {
27412
+ "exportName": "Usage",
27413
+ "displayName": "Использование в TableCanvas.bigData.stories.tsx",
27414
+ "type": "usage",
27415
+ "code": "<Slider\n value={cols}\n min={1}\n max={1500}\n onChangeCommitted={(value) => setCols(value)}\n />",
27416
+ "sourceFile": "packages/storybook/src/stories/TableCanvas/TableCanvas.CellsMerging/TableCanvas.bigData.stories.tsx"
27417
+ },
27400
27418
  {
27401
27419
  "exportName": "Vendor1",
27402
27420
  "displayName": "Состояния Состояния Компонент поддерживает состояние disabled",
@@ -31176,7 +31194,7 @@
31176
31194
  },
31177
31195
  {
31178
31196
  "name": "view",
31179
- "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"linkDefault\" | \"linkAccent\" | \"linkSecondary\" | \"linkPositive\" | \"linkWarning\" | \"linkNegative\" | \"linkInfo\" | undefined",
31197
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | \"linkDefault\" | \"linkAccent\" | \"linkSecondary\" | \"linkPositive\" | \"linkWarning\" | \"linkNegative\" | \"linkGradient\" | \"linkInfo\" | undefined",
31180
31198
  "required": false,
31181
31199
  "description": ""
31182
31200
  },
@@ -35361,6 +35379,12 @@
35361
35379
  "required": false,
35362
35380
  "description": ""
35363
35381
  },
35382
+ {
35383
+ "name": "pilled",
35384
+ "type": "boolean | undefined",
35385
+ "required": false,
35386
+ "description": ""
35387
+ },
35364
35388
  {
35365
35389
  "name": "size",
35366
35390
  "type": "\"xs\" | \"s\" | \"m\" | \"l\" | \"h6\" | \"h5\" | \"h4\" | \"h3\" | \"h2\" | \"h1\" | undefined",
@@ -42450,7 +42474,7 @@
42450
42474
  },
42451
42475
  {
42452
42476
  "name": "view",
42453
- "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | undefined",
42477
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
42454
42478
  "required": false,
42455
42479
  "description": ""
42456
42480
  }
@@ -43942,7 +43966,7 @@
43942
43966
  },
43943
43967
  {
43944
43968
  "name": "view",
43945
- "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | undefined",
43969
+ "type": "\"default\" | \"accent\" | \"secondary\" | \"clear\" | \"positive\" | \"warning\" | \"negative\" | \"dark\" | \"black\" | \"white\" | \"gradient\" | undefined",
43946
43970
  "required": false,
43947
43971
  "description": ""
43948
43972
  }
@@ -45935,6 +45959,12 @@
45935
45959
  "description": "",
45936
45960
  "default": "'Формат даты указан не верно'"
45937
45961
  },
45962
+ {
45963
+ "name": "isCalendarPositionedByInput",
45964
+ "type": "boolean | undefined",
45965
+ "required": false,
45966
+ "description": ""
45967
+ },
45938
45968
  {
45939
45969
  "name": "isDoubleCalendar",
45940
45970
  "type": "boolean | undefined",
@@ -48885,7 +48915,7 @@
48885
48915
  },
48886
48916
  {
48887
48917
  "name": "view",
48888
- "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"dark\" | undefined",
48918
+ "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"dark\" | \"gradient\" | undefined",
48889
48919
  "required": false,
48890
48920
  "description": ""
48891
48921
  }
@@ -49601,6 +49631,12 @@
49601
49631
  "required": false,
49602
49632
  "description": ""
49603
49633
  },
49634
+ {
49635
+ "name": "pilled",
49636
+ "type": "boolean | undefined",
49637
+ "required": false,
49638
+ "description": ""
49639
+ },
49604
49640
  {
49605
49641
  "name": "size",
49606
49642
  "type": "\"s\" | \"xs\" | \"m\" | \"l\" | \"h6\" | \"h5\" | \"h4\" | \"h3\" | \"h2\" | \"h1\" | undefined",
@@ -50115,7 +50151,7 @@
50115
50151
  },
50116
50152
  {
50117
50153
  "name": "view",
50118
- "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"dark\" | undefined",
50154
+ "type": "\"default\" | \"clear\" | \"black\" | \"white\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"dark\" | \"gradient\" | undefined",
50119
50155
  "required": false,
50120
50156
  "description": ""
50121
50157
  }
@@ -50603,6 +50639,12 @@
50603
50639
  "required": false,
50604
50640
  "description": ""
50605
50641
  },
50642
+ {
50643
+ "name": "pilled",
50644
+ "type": "boolean | undefined",
50645
+ "required": false,
50646
+ "description": ""
50647
+ },
50606
50648
  {
50607
50649
  "name": "size",
50608
50650
  "type": "\"s\" | \"xs\" | \"m\" | \"l\" | \"h6\" | \"h5\" | \"h4\" | \"h3\" | \"h2\" | \"h1\" | undefined",
@@ -51297,7 +51339,7 @@
51297
51339
  },
51298
51340
  {
51299
51341
  "name": "view",
51300
- "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"info\" | undefined",
51342
+ "type": "\"default\" | \"positive\" | \"warning\" | \"negative\" | \"secondary\" | \"accent\" | \"gradient\" | \"info\" | undefined",
51301
51343
  "required": false,
51302
51344
  "description": ""
51303
51345
  }
@@ -52085,6 +52127,106 @@
52085
52127
  }
52086
52128
  ]
52087
52129
  },
52130
+ {
52131
+ "component": "TableCanvas",
52132
+ "feature": "CellsMerging",
52133
+ "legacy": false,
52134
+ "summary": "Объединение ячеек тела таблицы: по строкам (rowSpan), по колонкам (colSpan) и",
52135
+ "docs": "# CellsMerging (TableCanvas)\n\nОбъединение ячеек тела таблицы: по строкам (rowSpan), по колонкам (colSpan) и\nпрямоугольными блоками. Несколько одинаковых ячеек рисуются одной большой, а всё\nвзаимодействие — клики, выделение, навигация, копирование, вставка, протяжка,\nредактирование — обращается с блоком как с одной ячейкой.\n\nЭта страница — обзор: как выбрать способ и с чего начать, плюс живые примеры.\nПодробности (совмещение способов, работа с деревом, выравнивание, сводные\nограничения) — в\n[Справочнике](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-справочник--docs),\nтипы — в\n[API](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-api--docs).\n\n## Как это устроено\n\nГлавная идея простая: объединение — это способ показа, а не способ хранения.\n\n- В данных значение повторяется в каждой строке блока. Если «Отдел А» тянется на\n три строки, то во всех трёх строках в поле отдела записано «Отдел А».\n- Таблица замечает повторы (или получает границы блока явно) и вместо трёх\n одинаковых ячеек рисует одну высокую. Содержимое берётся из верхней-левой\n ячейки блока.\n- Данные при этом не меняются. Уберите объединение — увидите те же строки с теми\n же значениями.\n\nОтсюда следует и поведение, знакомое по Excel: клик в любую точку блока выделяет\nблок целиком, правка блока меняет значение во всех его строках, копирование\nотдаёт значение один раз.\n\n## Три способа включить объединение\n\nМеханика под всеми способами одна. Отличие — кто вычисляет границы блоков:\nпотребитель или таблица. Способы можно совмещать в одной таблице — правила в\nСправочнике, глава «Если включено несколько способов сразу».\n\n### 1. mergeCells.mergedCellsRegions — блоки, заданные снаружи\n\n«Слей вот эти конкретные ячейки». Список блоков по стабильным ключам строк и\nколонок; списком владеет потребитель, как выделением. Таблица переводит ключи в\nтекущие видимые позиции. Если сортировка, перестановка или скрытие колонки\nразорвали блок — он не рисуется (как ограничение сортировки в Excel).\n\n```tsx\ntableConfig={{\n mergeCells: {\n mergedCellsRegions: [\n { rowKeys: ['r1', 'r2'], colKeys: ['dept'] },\n { rowKeys: ['r4', 'r5'], colKeys: ['plan', 'fact'] }, // прямоугольник 2×2\n ],\n rowKeyGetter: (row) => row.id,\n },\n}}\n```\n\nПримеры — стори с префиксом `mergedCellsRegions:` ниже.\n\n### 2. mergeCells.mergeByCellValues — по одинаковым значениям\n\n«В этих колонках сливай подряд идущие одинаковые значения». Границы блоков\nтаблица вычисляет сама из данных; после сортировки или фильтра блоки\nпересобираются.\n\n```tsx\ntableConfig={{\n mergeCells: {\n mergeByCellValues: [\n 'dept', // по значению колонки\n { colKey: 'role', value: (row) => row.roleGroup }, // по своей функции\n ],\n },\n}}\n```\n\nОдинаковые названия в разных ветках иерархии могут ложно слиться — лечится\nсоставным ключом, разбор в Справочнике, глава «Объединение по значению: где\nпроходит граница». Примеры — стори с префиксом `mergeByCellValues:` ниже.\n\n### 3. Merged-виды группировки и subRows — полный автомат\n\nПотребитель включает вид — всю работу с блоками делает таблица: строки\nупорядочиваются по группам, колонки уровней сливаются по границам групп, чекбокс\nи нумерация работают по верхней группе.\n\nЧерез группировку (структуру задают значения группирующих колонок):\n\n```tsx\ntableConfig={{\n rowsGrouping: {\n view: 'merged',\n groupByState: [groupBy, setGroupBy], // например ['dept', 'role']\n rowKeyGetter: (row) => row.id,\n },\n}}\n```\n\nЧерез subRows (структуру задаёт готовое дерево потребителя):\n\n```tsx\ntableConfig={{\n subRows: {\n view: 'merged',\n mergedColumns: ['dept', 'role'], // колонки уровней дерева\n getSubRows: (row) => row.children,\n rowKeyGetter: (row) => row.id,\n },\n}}\n```\n\nПримеры — «subRows: дерево с пагинацией» и «rowsGrouping: группировка\nпользователем» в конце страницы.\n\n## Какой способ выбрать\n\nКартинка у способов похожая, поэтому выбирать стоит не по внешнему виду, а по\nтому, кто владеет данными и что нужно пользователю.\n\n### Дерево приходит с сервера, нужна постраничная загрузка — subRows\n\nЕсли данные уже иерархические (дивизион, управление, команда, сотрудники) и\nприходят от сервера частями, берите `subRows` с `view: 'merged'`. Дерево остаётся\nу вас: вы отдаёте таблице страницу дерева, она разворачивает её в строки и сама\nсливает колонки-предки. Именно поэтому здесь работает пагинация по верхним\nблокам — таблице не нужно видеть все данные сразу.\n\nОбратная сторона: раз данными владеете вы, то поиск, сортировка и фильтрация\nдерева — тоже на вашей стороне. Формат дерева, пагинация и возврат правок —\nв Справочнике, глава «Дерево через subRows».\n\n### Плоские строки, пользователь сам выбирает группировку — rowsGrouping\n\nЕсли строки плоские, а пользователь должен на лету решать, по каким колонкам\nгруппировать, — берите `rowsGrouping` с `view: 'merged'`. Состав группировки живёт\nв `groupByState`, а с настройкой `groupButton` в блоке управления появляется\nкнопка: пользователь сам выбирает колонки, таблица пересобирает блоки. Встроенные\nпоиск, сортировка и фильтры работают — таблица применяет их до сборки групп.\n\nОбратная сторона: пагинации в этом виде нет — таблице нужны все строки сразу.\nНе действует и всё, что привязано к строкам-групп и шевронам дерева\n(`groupedColumnProps`, `renderGroupCell`, `expandAllBtn`) — полный список в\nСправочнике, глава «Что не работает и почему».\n\n### Плоские строки, группировка фиксированная, всё считает сервер — mergeByCellValues\n\nЕсли структура известна заранее и не меняется пользователем, а данные (включая\nстраницы, сортировку и фильтры) готовит сервер, — самый прямой путь это\n`mergeByCellValues`. Вы упорядочиваете строки как нужно, таблица сливает соседние\nодинаковые значения. Никакой лишней механики: ни кнопок, ни дерева.\n\n### Отдельные ручные блоки — mergedCellsRegions\n\nШапки секций внутри таблицы, нестандартные прямоугольники, объединение по кнопке\nили из контекстного меню — всё, что не выводится из значений, делается регионами.\nПотребитель добавляет блок в свой список — таблица его рисует.\n\n## Смежные разделы\n\n- [Справочник](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-справочник--docs) —\n совмещение способов, составные ключи, дерево, выравнивание, сводные ограничения.\n- [CellsMerging API](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-api--docs) —\n типы.\n- Объединение ячеек шапки — раздел ColumnsGrouping (`squashEmptyCells`).\n\n## Полные примеры\n\nТри больших примера ниже собраны как заготовки для продуктов: в каждом —\nобъединение ячеек в связке с поиском, сортировкой, фильтрами, пагинацией,\nредактированием и выделением. Различаются они тем, кто владеет данными, поэтому\nи код обвязки разный. Полный код каждого примера открывается кнопкой «Show code»\nи снабжён комментариями по ключевым местам.\n\n### mergeByCellValues: пример с серверными данными\n\nДанные плоские, всё считает «сервер» (в примере его заменяет функция\n`selectPage`), таблица только рисует полученную страницу.\n\nЧто здесь показано:\n\n- поиск, фильтры, сортировка и пагинация в `manual`-режиме — считает потребитель;\n- составные ключи пути (`divPath` / `unitPath` / `teamPath`) против ложного\n слияния одинаковых имён из разных веток;\n- сортировка внутри команды — блоки не рассыпаются;\n- закрепление колонок вместе с групповой шапкой;\n- редактирование с возвратом правок страницы в полный набор.\n\n<Canvas of={CellsMergingExamples.Showcase} />\n\n### subRows: дерево с пагинацией\n\nДанные — дерево, и оно остаётся у потребителя: таблице отдаётся страница целых\nподдеревьев.\n\nЧто здесь показано:\n\n- пагинация по верхним блокам (дивизионам) со своим списком «показывать по»;\n- `manual`-поиск, сортировка и фильтры, посчитанные по дереву\n (функция `selectTreePage`);\n- сортировка показателей внутри команды и перестановка веток по имени;\n- возврат правок листьев обратно в дерево.\n\n<Canvas of={CellsMergingExamples.SubRowsMerged} />\n\n### rowsGrouping: группировка пользователем\n\nДанные плоские и передаются целиком, остальное делает таблица. Самый короткий\nкод из трёх.\n\nЧто здесь показано:\n\n- встроенные поиск, сортировка и фильтры — применяются до сборки групп;\n- выбор состава группировки пользователем (кнопка `groupButton`);\n- полный стейт фильтров: ключ на каждый фильтр из колонок;\n- без пагинации — чтобы собрать группы, таблице нужны все строки сразу.\n\n<Canvas of={CellsMergingExamples.GroupingMerged} />\n\n## Как перенести пример к себе\n\n1. Замените данные и типы строк на свои. Сохраните стабильный `id` строки — на\n нём держатся выделение и возврат правок, между страницами он меняться не\n должен.\n2. Замените `selectPage` / `selectTreePage` реальным запросом к серверу. Контракт\n тот же: параметры выборки на входе, страница и общее количество на выходе.\n3. Проверьте, что в `filtering.state` есть ключ каждого фильтра из колонок (со\n значением «показать всё»), — селект-фильтр без своего ключа молча отсеет все\n строки.\n4. Для `mergeByCellValues` заведите составные ключи пути, если одинаковые\n названия возможны в разных ветках иерархии.\n5. Уберите фичи, которые вам не нужны, — настройки в примерах независимы друг от\n друга: пагинация, закрепление, чекбоксы, редактирование отключаются по\n отдельности.\n\n# CellsMerging: справочник\n\nПодробности объединения ячеек: совмещение способов, составные ключи, работа с\nдеревом, взаимодействие с остальными фичами таблицы, выравнивание и сводный\nсписок ограничений. Обзор способов и живые примеры — на странице\n[Docs](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging--docs), типы —\nна странице\n[API](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-api--docs).\n\n## Если включено несколько способов сразу\n\nСпособы можно совмещать в одной таблице — правила простые и считаются по колонке:\n\n- Если колонка входит хотя бы в один регион из `mergedCellsRegions`, её\n объединения описывают только регионы. `mergeByCellValues` на этой колонке не\n действует.\n- Колонка merged-вида (группировки или subRows) сливается по границам групп.\n Пользовательский `mergeByCellValues` на той же колонке не действует — иначе два\n механизма спорили бы за одни ячейки.\n- В остальных случаях работает `mergeByCellValues`.\n- Разные колонки живут независимо: регион на одной колонке и слияние по значению\n на другой — обычная рабочая комбинация.\n\nПример: таблица с группировкой по «Отделу» (merged-вид) может дополнительно\nсливать колонку «Статус» через `mergeByCellValues`, а строки-заголовки секций\nрисовать регионами на всю ширину — все три способа уживаются, потому что заняты\nразными колонками.\n\n## Объединение по значению: где проходит граница\n\nУ `mergeByCellValues` есть классическая ловушка. Допустим, регионы «Север» и «Юг»,\nи в каждом есть «Команда 1» и «Команда 2»:\n\n```\nСевер | Команда 1 | Иванов\nСевер | Команда 2 | Петров\nЮг | Команда 2 | Сидоров\nЮг | Команда 1 | Смирнов\n```\n\nЕсли сливать колонку команды по её значению, «Команда 2» из Севера и «Команда 2»\nиз Юга стоят рядом — и сольются в один блок через границу региона. Визуально это\nвыглядит как ошибка: одна команда, а на деле две разные.\n\nРешение — сливать не по тексту в ячейке, а по составному ключу, в котором зашит\nпуть от корня:\n\n```tsx\nmergeCells: {\n mergeByCellValues: [\n 'region',\n // Сравнивается результат функции, а не текст в ячейке.\n { colKey: 'team', value: (row) => `${row.region} / ${row.team}` },\n ],\n}\n```\n\nТеперь у «Команды 2» из Севера ключ «Север / Команда 2», а у южной —\n«Юг / Команда 2». Ключи разные, слияние останавливается на границе региона само,\nбез какой-либо отдельной логики. В ячейке при этом показывается обычное значение\nколонки — функция влияет только на сравнение.\n\nВ полном примере (стори «mergeByCellValues: пример с серверными данными») такие\nключи собраны заранее в полях строки: `divPath`, `unitPath`, `teamPath` — по\nодному на каждый уровень иерархии.\n\nMerged-виды группировки и subRows строят такие ключи сами — там об этой ловушке\nдумать не нужно.\n\n## Дерево через subRows: как передавать данные\n\nРазберём подробнее самый ёмкий сценарий — `subRows` с `view: 'merged'`.\n\n### Какое дерево ждёт таблица\n\nВ `rows` передаются корневые узлы, детей таблица достаёт через `getSubRows`.\nРоли узлов распределены так:\n\n- **Внутренние узлы** (дивизион, управление, команда) — ярлыки уровней. Каждый\n несёт поле с именем своей колонки из `mergedColumns`: узел дивизиона — поле\n `division`, узел управления — поле `unit`, и так далее.\n- **Листья** — строки данных. Именно они становятся строками таблицы со своими\n показателями; внутренние узлы отдельными строками не показываются.\n\n```tsx\nconst tree = [\n {\n id: 'd1',\n division: 'Розничный бизнес', // узел уровня 0\n subRows: [\n {\n id: 'u1',\n unit: 'Управление продаж', // узел уровня 1\n subRows: [\n {\n id: 't1',\n team: 'Команда Север', // узел уровня 2\n subRows: [\n { id: 'e1', employee: 'Иванов', plan: 100, fact: 90 }, // лист\n { id: 'e2', employee: 'Петров', plan: 80, fact: 85 },\n ],\n },\n ],\n },\n ],\n },\n];\n```\n\n```tsx\ntableConfig={{\n subRows: {\n view: 'merged',\n mergedColumns: ['division', 'unit', 'team'], // порядок = уровни дерева\n getSubRows: (row) => row.subRows,\n rowKeyGetter: (row) => row.id,\n },\n}}\n```\n\n`mergedColumns` — это соответствие уровней дерева и колонок: первый элемент\nпоказывает узлы верхнего уровня, второй — следующего, и так далее. Колонка\n`division` возьмёт значение из узла дивизиона и сольётся на все его листья,\n`unit` — на листья своего управления, вложенно.\n\nСворачивания в этом виде нет: таблица показывает всё переданное дерево целиком.\nХотите меньше строк на экране — передайте меньший кусок дерева (следующий\nраздел).\n\n### Пагинация по верхним блокам\n\nДерево у вас, поэтому страницы нарезаете вы — по верхним узлам. Таблице отдаётся\nсрез дерева, а в `pagination.count` — общее число верхних узлов:\n\n```tsx\nconst [page, setPage] = useState(1);\nconst [perPage, setPerPage] = useState(2); // дивизионов на страницу\nconst pageTree = tree.slice((page - 1) * perPage, page * perPage);\n\ntableConfig={{\n pagination: {\n count: tree.length, // всего верхних узлов, не листьев\n perPage,\n perPageList: [1, 2, 3], // варианты «показывать по» — в блоках\n value: page,\n onChange: (nextPage, nextPerPage) => {\n if (typeof nextPerPage === 'number' && nextPerPage !== perPage) {\n setPerPage(nextPerPage);\n setPage(1);\n } else if (typeof nextPage === 'number') {\n setPage(nextPage);\n }\n },\n },\n subRows: { /* как выше */ },\n}}\n// ...\nrows={pageTree}\n```\n\nСтраница «2 дивизиона» может содержать разное число строк-листьев — это нормально:\nединица пагинации здесь блок, а не строка. По той же причине настройте селектор\n«Показывать по»: его стандартные варианты (20/50/100) рассчитаны на строки и для\nблоков не имеют смысла — задайте свой список `perPageList` в блоках, как выше,\nлибо спрячьте селектор (`hasPerPage: false`). Если дерево приходит с сервера\nчастями, вместо `slice` будет запрос следующей страницы — контракт тот же.\n\n### Редактирование: правки возвращаются листьями\n\nРедактируются листья. В `editing.onRowsChange` приходят изменённые строки-листья —\nзаписать их обратно в своё дерево должен потребитель, например по `id`:\n\n```tsx\nediting: {\n rowKeyGetter: (r) => r.id,\n onRowsChange: (updatedLeaves) => {\n const byId = new Map(updatedLeaves.map((r) => [r.id, r]));\n // Обойдите дерево и замените листья с совпавшими id.\n setTree((prev) => writeLeavesBack(prev, byId));\n },\n},\n```\n\nЭто следствие владения данными: таблица не знает, как устроено ваше дерево,\nпоэтому отдаёт плоский результат, а раскладываете его вы.\n\n### Что остаётся на вашей стороне\n\n- **Поиск, сортировка, фильтрация** — встроенные механизмы к структурному дереву\n пока не применяются. Отфильтровали или отсортировали своё дерево — передали\n результат в `rows`, таблица показала.\n- **Выделение** при этом работает из коробки: один чекбокс на верхний блок, клик\n выделяет все его листья (подробнее — в разделе про выделение ниже).\n\nЖивой пример всего вместе — стори «subRows: дерево с пагинацией» на странице\n[Docs](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging--docs).\n\n## Объединение и остальные возможности таблицы\n\nБлоки — не отдельный мир: выделение, сортировка, буфер обмена и редактирование\nпродолжают работать. Здесь собрано, как именно.\n\n### Выделение, нумерация и навигация\n\nВыделение настраивается как обычно — `selecting` со стейтом и `rowKeyGetter`.\nА вот ведёт оно себя по-разному в зависимости от способа объединения:\n\n- **Плоский вид с mergeByCellValues** — чекбокс у каждой строки, номер у каждой\n строки. Объединение колонок на выделение не влияет: в стори\n «mergeByCellValues: пример с серверными данными» можно выделить одного\n сотрудника внутри слитого блока.\n- **Merged-виды группировки и subRows** — один чекбокс и один номер на группу\n верхнего уровня. Клик по чекбоксу выделяет все строки группы разом, повторный —\n снимает. «Выделить всё» в шапке тоже работает. Счётчик выбранного внизу\n считает строки, а не группы: выделили дивизион из 12 сотрудников — внизу\n «выбрано 12».\n\nСпециально настраивать групповое поведение не нужно: таблица включает его сама,\nкогда активен merged-вид.\n\nНавигация тоже понимает блоки: клик в любую точку блока выделяет его целиком,\nрамка фокуса рисуется вокруг всего блока, один шаг стрелкой перепрыгивает блок.\nПопасть «внутрь» блока с клавиатуры нельзя — для таблицы это одна ячейка.\n\n### Сортировка, фильтрация, поиск и пагинация\n\nВозможности зависят от того, кто владеет данными.\n\n**mergeByCellValues (плоский вид).** Встроенные сортировка, фильтр и поиск\nработают как обычно, а блоки пересобираются после каждого изменения порядка —\nобъединение всегда отражает текущий видимый список строк. Если данные приходят с\nсервера постранично, следите, чтобы сортировка на сервере не разрывала группы:\nполовина группы на одной странице и половина на другой будут выглядеть как два\nотдельных блока.\n\n**rowsGrouping (view: 'merged').** Встроенные поиск, сортировка и фильтры\nработают: таблица применяет их к строкам до сборки групп, поэтому группы всегда\nсобираются из уже отфильтрованного списка. Пагинации нет: группировке нужны все\nстроки сразу. Сортировку по группирующим колонкам можно снять настройкой\n`disableGroupColumnsSort` — как в дереве, где сгруппированные колонки не\nсортируются.\n\nЧастая ошибка при встроенных фильтрах (не только в этом виде): в стейте\n`filtering.state` должен быть ключ каждого фильтра, объявленного в колонках.\nSelect-фильтр без своего ключа получит пустое значение вместо «показать всё» и\nможет отсеять все строки — таблица окажется пустой без единой ошибки.\n\n**subRows (view: 'merged').** Дерево у потребителя, поэтому пагинация по верхним\nблокам работает: отдаёте страницу дерева — таблица её показывает. Встроенные\nпоиск, сортировка и фильтрация к структурному дереву пока не применяются — эти\nоперации делаются на стороне потребителя над его данными.\n\n### Копирование, вставка и протяжка\n\n- Копирование отдаёт значение в позиции верхней-левой ячейки блока, остальные\n ячейки блока в буфере пустые — как в Excel.\n- Вставка пишет во весь блок: значение попадает во все его строки.\n- Протяжка (fill) растягивается только до целых блоков — залить пол-блока нельзя;\n пунктирная рамка протяжки сама прилипает к границам блоков.\n\n### Редактирование\n\n- Правка ячейки блока записывает значение во все его строки — данные под блоком\n всегда совпадают с тем, что на экране.\n- При объединении по значению правка пересобирает блоки: изменили «Отдел» у\n блока — он разъедется или сольётся с соседним, потому что объединение выводится\n из данных.\n- У редактируемых Select-ячеек редактор открывается по выравниванию блока: текст\n прижат к низу — редактор откроется у нижнего края, а выпадающий список\n раскроется вверх.\n\n## Выравнивание контента в блоке\n\nПо умолчанию контент блока выравнивается по горизонтали из `contentAlign` колонки\n(иначе слева), по вертикали — по центру. Настраивается лестницей «точечное важнее\nобщего»:\n\n- `mergedCellsAlign` конкретного региона (только для `mergedCellsRegions`);\n- `mergedCellsAlign` на колонке — все блоки этой колонки, работает для всех\n способов включения;\n- дефолт фичи: `mergeCells.mergedCellsAlign` или `rowsGrouping.mergedCellsAlign`.\n\n```tsx\ncolumnConfig={[\n { key: 'dept', name: 'Отдел', mergedCellsAlign: { horizontal: 'center', vertical: 'top' } },\n]}\n```\n\nКолоночный `mergedCellsAlign` может быть и функцией `(row) => MergedCellsAlign`\nпо данным строки блока — так разные блоки одной колонки получают разное\nвыравнивание. Это единственный способ управлять выравниванием поблочно в\nmerged-видах группировки и subRows, где границы блоков решает таблица, а не\nпотребитель.\n\n```tsx\ncolumnConfig={[\n { key: 'dept', mergedCellsAlign: (row) =>\n row.critical ? { vertical: 'top' } : { vertical: 'center' } },\n]}\n```\n\n### Свой рендер в блоке\n\nВыравнивание действует на текстовый контент. Кастомный `renderCell` получает весь\nпрямоугольник блока и выравнивает содержимое сам — через свойства своих\nконтейнеров.\n\nУ колонок со своим рендером есть неочевидность: предпросмотр значения (по\nдвойному клику на ячейке без редактирования) у них по умолчанию выключен.\nСо стороны это выглядит как «у блоков не открывается предпросмотр», хотя дело в\nколонке, а не в объединении. Включается настройкой колонки:\n\n```tsx\n{ key: 'dept', renderCell: ..., renderCellPreview: 'cellEditorAsPreview' }\n```\n\n## Что не работает и почему\n\nСводный список ограничений. Если «что-то не рисуется» — сначала сюда.\n\n### Во всех способах\n\n- Все ячейки блока обязаны нести одинаковые данные — это контракт\n `mergeByCellValues` (значения реально повторяются в строках) и регионов.\n Причина проста: блок показывает верхнюю-левую **видимую** ячейку, а какая\n ячейка окажется верхней-левой — зависит от закрепления и порядка колонок.\n Если данные одинаковые, блок выглядит одинаково при любой перестановке.\n- Попасть «внутрь» блока с клавиатуры нельзя — для таблицы это одна ячейка.\n Это поведение, а не ошибка.\n- Выравнивание пока не поддерживает письмо справа налево (RTL).\n- Горизонтальная протяжка через colSpan-блок не растягивается до границ блока\n (в работе).\n- У колонок со своим `renderCell` по умолчанию выключен предпросмотр значения —\n включается `renderCellPreview: 'cellEditorAsPreview'` (см. «Свой рендер в\n блоке» выше).\n\n### mergedCellsRegions\n\n- Разорванный регион исчезает молча: если после сортировки, перестановки или\n скрытия колонок ключи региона перестали идти подряд, блок просто не рисуется.\n Ошибок и предупреждений нет — следите за порядком сами.\n\n### rowsGrouping (view: 'merged')\n\n- Пагинации нет: чтобы собрать группы, таблице нужны все строки сразу.\n- Отдельная колонка «Группировка» не создаётся, поэтому не действует всё, что к\n ней привязано: `groupedColumnProps`, свой контент ячейки группы\n (`renderGroupCell` у колонки) и кнопка «развернуть всё» (`expandAllBtn`) —\n строк-групп и шевронов в этом виде нет.\n\n### subRows (view: 'merged')\n\n- Встроенные поиск, сортировка и фильтрация к структурному дереву не\n применяются — эти операции делает потребитель над своими данными.\n- `column.subRow` целиком не действует: шеврон (`isColumnWithArrow`), свой\n контент и ключи вложенных уровней (`renderSubRowCell`, `keyOfColumnInSubRow`,\n `parentKeyAsDefault`, `contentFormat`), свой редактор уровня. Листья рисуются\n как обычные строки — работают их обычные `renderCell`, `contentFormat` и\n `editingCell`.\n- `expandedIdsState` и сворачивание не действуют: раскрывать нечего, дерево\n показано целиком.\n\nЭто осознанное поведение: один и тот же конфиг `subRows` или `rowsGrouping`\nможно переключать между `view: 'tree'` и `view: 'merged'` — лишние настройки\nдругого вида не мешают. Пометки «работает только в виде дерева» есть и в\nподсказках типов.\n\n> Подробнее о типах — [CellsMerging API](?path=/docs/локальные-компоненты-tablecanvas-cellsmerging-api--docs)",
52136
+ "apiDocs": "# CellsMerging API\n\n## tableConfig.mergeCells\n\nТочка входа для объединения ячеек тела. Все поля опциональны:\n\n```ts\nmergeCells?: {\n /** Колонки, объединяемые по подряд идущим одинаковым значениям. */\n mergeByCellValues?: Array<string | { colKey: string; value: (row) => unknown }>;\n /** Управляемый список объединений по ключам строк и колонок. */\n mergedCellsRegions?: MergedCellsRegion[];\n /** Нужен для mergedCellsRegions: сопоставляет rowKeys со строками. */\n rowKeyGetter?: (row) => string | number;\n /** Общий (табличный) дефолт выравнивания контента в блоках. */\n mergedCellsAlign?: MergedCellsAlign;\n};\n```\n\n## MergedCellsRegion\n\nОдин управляемый регион: ключи строк, ключи колонок и точечное выравнивание.\n\n```typescript\ntype MergedCellsRegion = {\n rowKeys: Array<string | number>;\n colKeys: string[];\n /** Выравнивание контента этого блока. Важнее колоночного и общего. */\n mergedCellsAlign?: MergedCellsAlign;\n};\n```\n\n## MergedCellsAlign\n\nВыравнивание контента в блоке. Задаётся на трёх уровнях: `mergedCellsAlign` региона,\n`columnConfig[].mergedCellsAlign` (колонка), `mergeCells.mergedCellsAlign` или\n`rowsGrouping.mergedCellsAlign` (дефолт). Точечное важнее общего.\n\nКолоночный `mergedCellsAlign` — это `MergedCellsAlign` (одно на все блоки колонки) или\nфункция `(row) => MergedCellsAlign | undefined` по данным верхней строки блока. Функция\nдаёт разное выравнивание разным блокам, включая merged-виды группировки/subRows, где\nграницы блоков решает таблица.\n\n```tsx\ncolumnConfig={[\n { key: 'dept', mergedCellsAlign: (row) =>\n row.critical ? { vertical: 'top' } : { vertical: 'center' } },\n]}\n```\n\n```typescript\ntype MergedCellsAlign = {\n /** По горизонтали. @default из `contentAlign` колонки, иначе 'left' */\n horizontal?: 'left' | 'center' | 'right';\n /** По вертикали. @default 'center' */\n vertical?: 'top' | 'center' | 'bottom';\n};\n```\n\n## rowsGrouping (merged-вид)\n\nПоля группировки, относящиеся к merged-виду: `view: 'merged'`,\n`mergedCellsAlign`, `disableGroupColumnsSort`.\n\n```typescript\ntype RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Как показывать группировку.\n * 'tree' (по умолчанию) — дерево с раскрытием (шевроны) и колонкой группировки.\n * 'merged' — плоский вид: строки идут по группам, а группирующие колонки\n * показаны объединёнными ячейками. Раскрытия нет; чекбокс и номер — на всю\n * группу верхнего уровня.\n * @default 'tree'\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Выравнивание по умолчанию в объединённых ячейках\n * группирующих колонок. Колоночный mergedCellsAlign важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n /**\n * Только для view: 'merged'. Запретить сортировку по группирующим колонкам\n * (как в дереве). По остальным колонкам сортировка остаётся.\n * @default false\n */\n disableGroupColumnsSort?: boolean;\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * Настройки колонки «Группировка» (имя, ширина, свои рендеры). Работает только\n * в виде дерева: при view: 'merged' эта колонка не создаётся, а сгруппированные\n * колонки остаются на своих местах.\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n /** Кнопка «развернуть всё». Работает только в виде дерева: при view: 'merged' шевронов нет. */\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n /**\n * Свой контент ячейки строки-группы. Работает только в виде дерева: при\n * view: 'merged' строк-групп нет (группы показаны объединёнными ячейками),\n * поэтому этот рендер не вызывается.\n */\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};\n```\n\n## subRows (merged-вид)\n\nПоля subRows, относящиеся к merged-виду: `view: 'merged'` и `mergedColumns`\n(упорядоченный список колонок-предков, индекс = глубина уровня дерева).\n\n```typescript\ntype SubRows<RowType, RowIdType> = {\n /**\n * Возвращает дочерние строки узла (или undefined, если их нет).\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Возвращает уникальный ключ строки.\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний useState.\n * При view: 'merged' не используется: сворачивания в этом виде нет.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n /**\n * Как показывать дерево.\n * 'tree' (по умолчанию) — узлы с раскрытием (шевроны) и отступом по уровню.\n * 'merged' — дерево разворачивается в листья, а колонки-предки показаны\n * объединёнными ячейками. Структуру задаёт потребитель через getSubRows;\n * данные (пагинация, сортировка, поиск) остаются на его стороне.\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Колонки-предки по порядку уровней:\n * mergedColumns[0] — верхний уровень, дальше глубже. Каждая такая колонка\n * показывает значение своего узла-предка (узел должен нести поле с этим именем)\n * и объединяется по его границам. Верхний уровень задаёт чекбокс и номер блока.\n */\n mergedColumns?: string[];\n};\n```",
52137
+ "api": [
52138
+ {
52139
+ "typeName": "MergedCellsRegion",
52140
+ "source": "type MergedCellsRegion = {\n rowKeys: Array<string | number>;\n colKeys: string[];\n /** Выравнивание контента этого блока. Важнее колоночного и общего. */\n mergedCellsAlign?: MergedCellsAlign;\n};"
52141
+ },
52142
+ {
52143
+ "typeName": "MergedCellsAlign",
52144
+ "source": "type MergedCellsAlign = {\n /** По горизонтали. @default из `contentAlign` колонки, иначе 'left' */\n horizontal?: 'left' | 'center' | 'right';\n /** По вертикали. @default 'center' */\n vertical?: 'top' | 'center' | 'bottom';\n};"
52145
+ },
52146
+ {
52147
+ "typeName": "RowsGrouping",
52148
+ "source": "type RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Как показывать группировку.\n * 'tree' (по умолчанию) — дерево с раскрытием (шевроны) и колонкой группировки.\n * 'merged' — плоский вид: строки идут по группам, а группирующие колонки\n * показаны объединёнными ячейками. Раскрытия нет; чекбокс и номер — на всю\n * группу верхнего уровня.\n * @default 'tree'\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Выравнивание по умолчанию в объединённых ячейках\n * группирующих колонок. Колоночный mergedCellsAlign важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n /**\n * Только для view: 'merged'. Запретить сортировку по группирующим колонкам\n * (как в дереве). По остальным колонкам сортировка остаётся.\n * @default false\n */\n disableGroupColumnsSort?: boolean;\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * Настройки колонки «Группировка» (имя, ширина, свои рендеры). Работает только\n * в виде дерева: при view: 'merged' эта колонка не создаётся, а сгруппированные\n * колонки остаются на своих местах.\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n /** Кнопка «развернуть всё». Работает только в виде дерева: при view: 'merged' шевронов нет. */\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n /**\n * Свой контент ячейки строки-группы. Работает только в виде дерева: при\n * view: 'merged' строк-групп нет (группы показаны объединёнными ячейками),\n * поэтому этот рендер не вызывается.\n */\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};"
52149
+ },
52150
+ {
52151
+ "typeName": "SubRows",
52152
+ "source": "type SubRows<RowType, RowIdType> = {\n /**\n * Возвращает дочерние строки узла (или undefined, если их нет).\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Возвращает уникальный ключ строки.\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний useState.\n * При view: 'merged' не используется: сворачивания в этом виде нет.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n /**\n * Как показывать дерево.\n * 'tree' (по умолчанию) — узлы с раскрытием (шевроны) и отступом по уровню.\n * 'merged' — дерево разворачивается в листья, а колонки-предки показаны\n * объединёнными ячейками. Структуру задаёт потребитель через getSubRows;\n * данные (пагинация, сортировка, поиск) остаются на его стороне.\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Колонки-предки по порядку уровней:\n * mergedColumns[0] — верхний уровень, дальше глубже. Каждая такая колонка\n * показывает значение своего узла-предка (узел должен нести поле с этим именем)\n * и объединяется по его границам. Верхний уровень задаёт чекбокс и номер блока.\n */\n mergedColumns?: string[];\n};"
52153
+ }
52154
+ ],
52155
+ "stories": [
52156
+ {
52157
+ "exportName": "BigDataExample",
52158
+ "displayName": "Большой набор данных (строки × колонки)",
52159
+ "type": "full-code",
52160
+ "code": "() => {\n const [cols, setCols] = useState(1000);\n const [rowInput, setRowInput] = useState('50000');\n const [rowTarget, setRowTarget] = useState(50000);\n const [withMerge, setWithMerge] = useState(true);\n const [heavyCells, setHeavyCells] = useState(true);\n const [withEditing, setWithEditing] = useState(false);\n // Правки пользователя поверх сгенерированных данных (режим редактирования).\n const [editedRows, setEditedRows] = useState<Node[] | null>(null);\n\n const commitsRef = useRef<number[]>([]);\n const onRender = useCallback(\n (_id: string, _phase: string, actualDuration: number) => {\n commitsRef.current.push(actualDuration);\n },\n [],\n );\n\n const { rows: builtRows, leaves } = useMemo(\n () => buildData(rowTarget, withMerge),\n [rowTarget, withMerge],\n );\n\n useEffect(() => {\n setEditedRows(null);\n }, [builtRows]);\n\n const dataRows = editedRows ?? builtRows;\n\n const columns = useMemo(\n () => buildColumns(cols, heavyCells, withEditing),\n [cols, heavyCells, withEditing],\n );\n\n const tableConfig = useMemo(\n () => ({\n containerStyle: { height: '70vh' },\n rowMarkers: { startIndex: 1 },\n resizableColumn: true,\n columnsControl: {\n enable: true,\n pinning: true,\n },\n ...(withMerge\n ? {\n subRows: {\n getSubRows: (row: Node) => row?.subRows,\n rowKeyGetter: (row: Node) => row.id,\n view: 'merged' as const,\n mergedColumns: ['division', 'unit', 'team'],\n },\n }\n : {}),\n ...(withEditing\n ? {\n editing: {\n onRowsChange: (next: Node[]) => setEditedRows(next),\n rowKeyGetter: (r: Node) => r.id,\n },\n }\n : {}),\n }),\n [withMerge, withEditing],\n );\n\n return (\n <div>\n <StoryHint>\n Пример на большом наборе данных: настройте число колонок и строк,\n включите нужные режимы и проверяйте выделение, скролл, сайдбар и\n редактирование. HUD справа показывает метрики производительности: FPS\n — кадров в секунду (ниже 40 подсвечивается красным); «коммитов/с» —\n сколько раз в секунду React перерендерил таблицу (при протяжке\n выделения каждый шаг мыши даёт один коммит); «коммит сред/макс» —\n среднее и максимальное время одного такого перерендера в\n миллисекундах, то есть цена реакции таблицы на одно действие.\n </StoryHint>\n\n <div\n style={{\n display: 'flex',\n gap: 24,\n alignItems: 'center',\n flexWrap: 'wrap',\n padding: '8px 4px 12px',\n fontSize: 13,\n }}\n >\n <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n Колонок: <b style={{ minWidth: 42 }}>{cols}</b>\n <div style={{ width: 220 }}>\n <Slider\n value={cols}\n min={1}\n max={1500}\n onChangeCommitted={(value) => setCols(value)}\n />\n </div>\n </span>\n\n <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n Строк:\n <div style={{ width: 130 }}>\n <TextField\n value={rowInput}\n type=\"number\"\n size=\"s\"\n onChange={(e) => setRowInput(e.target.value)}\n />\n </div>\n <Button\n size=\"s\"\n view=\"secondary\"\n onClick={() => setRowTarget(Math.max(1, Number(rowInput) || 1))}\n >\n Построить\n </Button>\n <span style={{ opacity: 0.75 }}>\n (листьев: <b>{fmt(leaves)}</b>)\n </span>\n </span>\n\n <Checkbox\n label=\"объединение (subRows merged)\"\n checked={withMerge}\n onChange={(e) => setWithMerge(e.target.checked)}\n />\n\n <Checkbox\n label=\"тяжёлые ячейки (Canvas.Badge)\"\n checked={heavyCells}\n onChange={(e) => setHeavyCells(e.target.checked)}\n />\n\n <Checkbox\n label=\"редактирование (error-ячейки)\"\n checked={withEditing}\n onChange={(e) => setWithEditing(e.target.checked)}\n />\n\n <PerfHudBadge commitsRef={commitsRef} />\n </div>\n\n <Profiler id=\"big-data-table\" onRender={onRender}>\n <TableCanvas\n tableConfig={tableConfig}\n columnConfig={columns}\n rows={dataRows as Node[]}\n />\n </Profiler>\n </div>\n );\n }"
52161
+ },
52162
+ {
52163
+ "exportName": "RowSpanBasic",
52164
+ "displayName": "mergeByCellValues: объединение по значению",
52165
+ "type": "full-code",
52166
+ "code": "import { useMemo, useRef, useState } from 'react';\nimport { Select } from '@daisforge/ui';\nimport {\n Canvas,\n type CellsSelectionMode,\n type ColumnConfig,\n type HighlightActiveType,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n role: string;\n dept: string;\n period: string;\n plan: number;\n fact: number;\n};\n\n// role/dept повторяются в данных блоками по 3 строки — объединение\n// выводится из повторяющихся значений, данные не мутируются.\nconst GROUPS = [\n { role: 'Аналитик', dept: 'Отдел A' },\n { role: 'Разработчик', dept: 'Отдел B' },\n { role: 'Тестировщик', dept: 'Отдел C' },\n { role: 'Менеджер', dept: 'Отдел D' },\n];\nconst PERIODS = ['Q1', 'Q2', 'Q3'];\nconst INITIAL_ROWS: Row[] = [];\nlet counter = 1;\nfor (const g of GROUPS) {\n for (const p of PERIODS) {\n INITIAL_ROWS.push({\n id: counter,\n role: g.role,\n dept: g.dept,\n period: p,\n plan: counter * 10,\n fact: counter * 8,\n });\n counter += 1;\n }\n}\n\nconst SELECTION_MODE_OPTIONS: Array<{\n label: string;\n value: CellsSelectionMode;\n}> = [\n { label: 'cell', value: 'cell' },\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst HIGHLIGHT_ACTIVE_TYPE_OPTIONS: Array<{\n label: string;\n value: HighlightActiveType;\n}> = [\n { label: 'row', value: 'row' },\n { label: 'disabled', value: 'disabled' },\n];\n\nexport const Example = () => {\n const [rows, setRows] = useState<Row[]>(INITIAL_ROWS);\n const beforeEditRef = useRef(rows);\n const [selectionMode, setSelectionMode] =\n useState<CellsSelectionMode>('range-cell');\n const [highlightActiveType, setHighlightActiveType] =\n useState<HighlightActiveType>('disabled');\n\n const columns = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n { key: 'role', name: 'Роль (merge ×3)', width: 220 },\n {\n key: 'dept',\n name: 'Отдел (custom + merge ×3)',\n width: 220,\n renderCell: ({ row, theme }) => (\n <Canvas.Container padding={8} alignItems=\"center\" justifyContent=\"center\">\n <Canvas.Text color={theme.textDark}>{row.dept}</Canvas.Text>\n </Canvas.Container>\n ),\n copyData: (row) => row.dept,\n // У canvas-колонок превью по умолчанию выключено — включаем явно.\n renderCellPreview: 'cellEditorAsPreview',\n },\n { key: 'period', name: 'Период', width: 120, editingCell: { component: 'inputString' } },\n { key: 'plan', name: 'План', width: 120, editingCell: { component: 'inputNumber' } },\n { key: 'fact', name: 'Факт', width: 120, editingCell: { component: 'inputNumber' } },\n ],\n [],\n );\n\n return (\n <div>\n <div style={{ display: 'flex', gap: 16, marginBottom: 12 }}>\n <Select\n target=\"textfield-like\"\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(v) => setSelectionMode(v as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />\n <Select\n target=\"textfield-like\"\n label=\"Highlight active\"\n value={highlightActiveType}\n onChange={(v) => setHighlightActiveType(v as HighlightActiveType)}\n items={HIGHLIGHT_ACTIVE_TYPE_OPTIONS}\n />\n </div>\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n highlightActiveType,\n rowMarkers: { startIndex: 1 },\n columnsControl: { enable: true },\n rowSize: { default: 'medium', showInControl: true },\n fullScreenEnabled: true,\n mergeCells: { mergeByCellValues: ['role', 'dept'] },\n cellsSelection: { mode: selectionMode, enableColumnSelection: true },\n cellTransfer: { fillHandle: true },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => \\"
52167
+ },
52168
+ {
52169
+ "exportName": "DerivedGroupingSortFilter",
52170
+ "displayName": "mergeByCellValues: сортировка и фильтр",
52171
+ "type": "full-code",
52172
+ "code": "import { useState } from 'react';\nimport {\n type ColumnConfig,\n type SortColumn,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n role: string;\n dept: string;\n person: string;\n plan: number;\n};\n\nconst GROUPS = [\n { role: 'Аналитик', dept: 'Отдел A' },\n { role: 'Разработчик', dept: 'Отдел B' },\n { role: 'Тестировщик', dept: 'Отдел C' },\n { role: 'Менеджер', dept: 'Отдел D' },\n];\nconst ALL: Row[] = [];\nlet counter = 1;\nfor (const g of GROUPS) {\n for (let p = 0; p < 3; p += 1) {\n ALL.push({\n id: counter,\n role: g.role,\n dept: g.dept,\n person: \\"
52173
+ },
52174
+ {
52175
+ "exportName": "SpanByWithEditing",
52176
+ "displayName": "mergeByCellValues: редактирование",
52177
+ "type": "full-code",
52178
+ "code": "import { useRef, useState } from 'react';\nimport {\n type ColumnConfig,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = { id: number; region: string; team: string; person: string };\n\n// 6 регионов × 4 строки: регион слит блоками по 4, команда — по 2 (вложенно).\nconst REGIONS = ['Север', 'Юг', 'Запад', 'Восток', 'Центр', 'Урал'];\nconst INITIAL_ROWS: Row[] = [];\nlet c = 1;\nfor (const region of REGIONS) {\n for (let t = 0; t < 4; t += 1) {\n INITIAL_ROWS.push({\n id: c,\n region,\n team: \\"
52179
+ },
52180
+ {
52181
+ "exportName": "MergedSelectEditing",
52182
+ "displayName": "Select в объединённой ячейке",
52183
+ "type": "full-code",
52184
+ "code": "import { useRef, useState } from 'react';\nimport {\n type ColumnConfig,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n status: string;\n owner: string;\n priority: string;\n stage: string;\n task: string;\n};\n\nconst STATUSES = ['В работе', 'Готово', 'Отменено', 'На паузе'];\nconst OWNERS = ['Иванов', 'Петров', 'Сидоров'];\nconst PRIORITIES = ['Высокий', 'Средний', 'Низкий'];\n// Неслитый Select (per-row) — для проверки обычных ячеек рядом со слитыми.\nconst STAGES = ['Анализ', 'Разработка', 'Тест', 'Релиз'];\n\n// Блоки разной высоты: 4 строки, 3 строки, 2 строки. Все три select-колонки\n// слиты синхронно (одинаковые границы).\nconst INITIAL_ROWS: Row[] = [\n { id: 1, status: 'В работе', owner: 'Иванов', priority: 'Высокий', stage: 'Анализ', task: 'Задача 1' },\n { id: 2, status: 'В работе', owner: 'Иванов', priority: 'Высокий', stage: 'Разработка', task: 'Задача 2' },\n { id: 3, status: 'В работе', owner: 'Иванов', priority: 'Высокий', stage: 'Тест', task: 'Задача 3' },\n { id: 4, status: 'В работе', owner: 'Иванов', priority: 'Высокий', stage: 'Релиз', task: 'Задача 4' },\n { id: 5, status: 'Готово', owner: 'Петров', priority: 'Средний', stage: 'Анализ', task: 'Задача 5' },\n { id: 6, status: 'Готово', owner: 'Петров', priority: 'Средний', stage: 'Разработка', task: 'Задача 6' },\n { id: 7, status: 'Готово', owner: 'Петров', priority: 'Средний', stage: 'Тест', task: 'Задача 7' },\n { id: 8, status: 'Отменено', owner: 'Сидоров', priority: 'Низкий', stage: 'Анализ', task: 'Задача 8' },\n { id: 9, status: 'Отменено', owner: 'Сидоров', priority: 'Низкий', stage: 'Релиз', task: 'Задача 9' },\n];\n\nconst columns: readonly ColumnConfig<Row>[] = [\n {\n key: 'status',\n name: 'Статус — низ/лево',\n width: 220,\n // Выравнивание контента блока: вертикаль низ, горизонталь лево.\n mergedCellsAlign: { vertical: 'bottom', horizontal: 'left' },\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: STATUSES.map((s) => ({ text: s, value: s })),\n },\n },\n },\n {\n key: 'owner',\n name: 'Ответственный — низ/право',\n width: 220,\n mergedCellsAlign: { vertical: 'bottom', horizontal: 'right' },\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: OWNERS.map((s) => ({ text: s, value: s })),\n },\n },\n },\n {\n key: 'priority',\n name: 'Приоритет — центр/центр',\n width: 200,\n mergedCellsAlign: { vertical: 'center', horizontal: 'center' },\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: PRIORITIES.map((s) => ({ text: s, value: s })),\n },\n },\n },\n {\n key: 'stage',\n name: 'Этап (Select, НЕ слит)',\n width: 200,\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: STAGES.map((s) => ({ text: s, value: s })),\n },\n },\n },\n {\n key: 'task',\n name: 'Задача (текст, не слит)',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n];\n\nexport const Example = () => {\n const [rows, setRows] = useState<Row[]>(INITIAL_ROWS);\n const beforeEditRef = useRef(rows);\n\n return (\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '560px' },\n rowMarkers: { startIndex: 1 },\n columnsControl: { enable: true },\n rowSize: { default: 'medium', showInControl: true },\n fullScreenEnabled: true,\n mergeCells: {\n mergeByCellValues: ['status', 'owner', 'priority'],\n },\n cellsSelection: { mode: 'range-cell' },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => \\"
52185
+ },
52186
+ {
52187
+ "exportName": "Colspan",
52188
+ "displayName": "mergedCellsRegions: заголовки секций",
52189
+ "type": "full-code",
52190
+ "code": "import { useMemo, useRef, useState } from 'react';\nimport {\n Canvas,\n type ColumnConfig,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n kind: 'header' | 'data';\n label: string;\n a: string;\n b: string;\n};\n\n// Правило блока: данные одинаковы во всех его ячейках. Блок показывает свою\n// левую верхнюю видимую ячейку, а после закрепления или перестановки колонок\n// ею может стать другая колонка — заголовок при этом останется видимым.\nconst buildRows = (): Row[] => {\n const out: Row[] = [];\n let id = 1;\n for (let s = 0; s < 3; s += 1) {\n out.push({\n id: id++,\n kind: 'header',\n label: \\"
52191
+ },
52192
+ {
52193
+ "exportName": "Rectangular",
52194
+ "displayName": "mergedCellsRegions: прямоугольный блок",
52195
+ "type": "full-code",
52196
+ "code": "import { useRef, useState } from 'react';\nimport {\n Canvas,\n type ColumnConfig,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = { id: number; a: string; b: string; c: string; d: string };\n\nconst INITIAL_ROWS: Row[] = Array.from({ length: 10 }, (_, i) => ({\n id: i + 1,\n a: \\"
52197
+ },
52198
+ {
52199
+ "exportName": "ControlledSpans",
52200
+ "displayName": "mergedCellsRegions: управление кнопками",
52201
+ "type": "full-code",
52202
+ "code": "import { useState } from 'react';\nimport { Button } from '@daisforge/ui';\nimport {\n type ColumnConfig,\n type MergedCellsRegion,\n type SortColumn,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n dept: string;\n role: string;\n person: string;\n plan: number;\n};\n\nconst GROUPS = [\n { dept: 'Отдел A', role: 'Аналитик', ids: [1, 2, 3] },\n { dept: 'Отдел B', role: 'Разработчик', ids: [4, 5, 6] },\n { dept: 'Отдел C', role: 'Тестировщик', ids: [7, 8, 9, 10] },\n];\nconst ROWS: Row[] = [];\nfor (const g of GROUPS) {\n for (const id of g.ids) {\n ROWS.push({\n id,\n dept: g.dept,\n role: g.role,\n person: \\"
52203
+ },
52204
+ {
52205
+ "exportName": "FormatInBlock",
52206
+ "displayName": "Форматирование и свой рендер в блоке",
52207
+ "type": "full-code",
52208
+ "code": "import { useState } from 'react';\nimport { Button } from '@daisforge/ui';\nimport {\n Canvas,\n type ColumnConfig,\n type MergedCellsRegion,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\ntype Row = {\n id: number;\n dept: string;\n role: string;\n person: string;\n plan: number;\n fact: number;\n};\n\n// dept/role повторяются в данных: 3 отдела, в каждом 2 роли по 2 сотрудника.\nconst DEPTS = [\n { dept: 'Отдел A', roles: ['Аналитик', 'Разработчик'] },\n { dept: 'Отдел B', roles: ['Разработчик', 'Тестировщик'] },\n { dept: 'Отдел C', roles: ['Менеджер', 'Аналитик'] },\n];\nconst ROWS: Row[] = [];\nlet id = 1;\nfor (const d of DEPTS) {\n for (const role of d.roles) {\n for (let i = 0; i < 2; i += 1) {\n ROWS.push({\n id,\n dept: d.dept,\n role,\n person: \\"
52209
+ },
52210
+ {
52211
+ "exportName": "Showcase",
52212
+ "displayName": "mergeByCellValues: пример с серверными данными",
52213
+ "type": "full-code",
52214
+ "code": "import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n type ColumnConfig,\n type ColumnOrColumnGroupConfig,\n type SortColumn,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\n// Строка-лист: сотрудник, показатели и путь предков, продублированный в строке.\n// Ключи пути (divPath/unitPath/teamPath) учитывают предков — по ним\n// объединяются колонки-предки, одинаковые имена в разных ветках не сливаются.\ntype Row = {\n id: string;\n division: string;\n unit: string;\n team: string;\n employee: string;\n plan: number;\n fact: number;\n divPath: string;\n unitPath: string;\n teamPath: string;\n};\n\n// Оргструктура «от бэка»: дивизион -> управление -> команда -> сотрудники.\nconst ORG = [\n {\n name: 'Розничный бизнес',\n units: [\n { name: 'Управление продаж', teams: ['Команда Север', 'Команда Юг'] },\n { name: 'Управление сервиса', teams: ['Команда Онлайн'] },\n ],\n },\n {\n name: 'Корпоративный бизнес',\n units: [\n { name: 'Управление КИБ', teams: ['Команда Крупные', 'Команда СМБ'] },\n ],\n },\n];\nconst SURNAMES = ['Иванов', 'Петрова', 'Сидоров', 'Кузнецова', 'Смирнов'];\n\nconst buildRows = (): Row[] => {\n const out: Row[] = [];\n let g = 0;\n for (const div of ORG) {\n for (const unit of div.units) {\n for (const team of unit.teams) {\n // По 12 сотрудников в команде — крупные блоки под merge и пагинацию.\n for (let i = 0; i < 12; i += 1) {\n const plan = 600000 + ((g * 37) % 18) * 100000;\n out.push({\n id: \\"
52215
+ },
52216
+ {
52217
+ "exportName": "SubRowsMerged",
52218
+ "displayName": "subRows: дерево с пагинацией",
52219
+ "type": "full-code",
52220
+ "code": "import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n type ColumnConfig,\n type SortColumn,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\n// Узел дерева: узлы-предки несут своё поле (division/unit/team), листья — строки с показателями.\ntype Node = {\n id: string;\n division?: string;\n unit?: string;\n team?: string;\n employee?: string;\n plan?: number;\n fact?: number;\n subRows?: Node[];\n};\n\nconst ORG = [\n {\n name: 'Розничный бизнес',\n units: [\n { name: 'Управление продаж', teams: ['Команда Север', 'Команда Юг'] },\n { name: 'Управление сервиса', teams: ['Команда Онлайн'] },\n ],\n },\n {\n name: 'Корпоративный бизнес',\n units: [\n { name: 'Управление КИБ', teams: ['Команда Крупные', 'Команда СМБ'] },\n ],\n },\n];\nconst SURNAMES = ['Иванов', 'Петрова', 'Сидоров', 'Кузнецова', 'Смирнов'];\n\n// Дерево «от бэка»: дивизион -> управление -> команда -> сотрудники (листья).\nlet g = 0;\nconst TREE: Node[] = ORG.map((div, di) => ({\n id: \\"
52221
+ },
52222
+ {
52223
+ "exportName": "GroupingMerged",
52224
+ "displayName": "rowsGrouping: группировка пользователем",
52225
+ "type": "full-code",
52226
+ "code": "import { useRef, useState } from 'react';\nimport {\n type ColumnConfig,\n type SortColumn,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\n// Плоские листья на входе, дерево строит сама таблица по groupByState.\ntype Row = {\n id: string;\n division: string;\n unit: string;\n team: string;\n employee: string;\n plan: number;\n fact: number;\n};\n\nconst ORG = [\n {\n name: 'Розничный бизнес',\n units: [\n { name: 'Управление продаж', teams: ['Команда Север', 'Команда Юг'] },\n { name: 'Управление сервиса', teams: ['Команда Онлайн'] },\n ],\n },\n {\n name: 'Корпоративный бизнес',\n units: [\n { name: 'Управление КИБ', teams: ['Команда Крупные', 'Команда СМБ'] },\n ],\n },\n];\nconst SURNAMES = ['Иванов', 'Петрова', 'Сидоров', 'Кузнецова', 'Смирнов'];\n\nconst buildRows = (): Row[] => {\n const out: Row[] = [];\n let g = 0;\n for (const div of ORG) {\n for (const unit of div.units) {\n for (const team of unit.teams) {\n for (let i = 0; i < 12; i += 1) {\n const plan = 600000 + ((g * 37) % 18) * 100000;\n out.push({\n id: \\"
52227
+ }
52228
+ ]
52229
+ },
52088
52230
  {
52089
52231
  "component": "TableCanvas",
52090
52232
  "feature": "ColumnResize",
@@ -52160,11 +52302,11 @@
52160
52302
  "legacy": false,
52161
52303
  "summary": "*tableConfig.columnsControl**",
52162
52304
  "docs": "# ColumnsControl (TableCanvas)\n\n**tableConfig.columnsControl**\n\nНастройка колонок таблицы: скрытие, закрепление, изменение порядка (через Aside-панель и drag в header).\n\n## Ключевые особенности\n\n- Активация через `enable: true` — включает все подфичи по умолчанию\n- Скрытие колонок через свитчи в Aside (`hiding`)\n- Закрепление колонок (`pinning`)\n- Перетаскивание колонок в Aside (`reorderingAside`) и через header (`reorderingHeader`)\n- Дефолтные состояния при первом рендере (`pinnedDefault`, `hiddenDefault`, `orderDefault`)\n- Переопределение названий колонок в Aside (`columnsLabel`)\n- Колбэк при применении настроек (`onConfirm`)\n\n## Подфичи\n\nКаждая подфича по умолчанию наследует значение `enable`. Можно отключить отдельно:\n\n- **`hiding`** — скрытие/показ колонок через свитчи. `disableHiding` исключает конкретные колонки\n- **`pinning`** — закрепление колонок слева. `disablePinning` исключает конкретные колонки\n- **`reorderingAside`** — drag-and-drop порядка колонок в Aside-панели\n- **`reorderingHeader`** — drag-and-drop заголовков колонок прямо в таблице. `onReorderingHeader` — callback с новым порядком\n\n## Дефолтные состояния\n\n- **`pinnedDefault`** — массив ключей колонок, закреплённых при первом рендере\n- **`hiddenDefault`** — массив ключей скрытых колонок при первом рендере\n- **`orderDefault`** — массив ключей, определяющий начальный порядок колонок\n\n## onConfirm\n\nCallback вызывается при нажатии «Применить» в Aside. Получает текущее состояние (`pinned`, `order`, `hidden`, `changed`) и сеттеры для программного управления состоянием.\n\n## Меню закрепления в контрл-блоке (pinningMenu)\n\nКогда включено `pinning`, в правой зоне иконок контрл-блока автоматически появляется нативная кнопка-иконка (пин + шеврон) — быстрый доступ к закреплению выбранных колонок, дополняющий Aside-панель. Задаётся через `controlBlock.pinningMenu`.\n\n- Фича **нативная**: появляется сама при активном `pinning`, отдельно включать не нужно. Отключается `controlBlock.pinningMenu.enable: false`.\n- **Размеры** завязаны на размер контрл-блока (`controlBlock.size`) — в компактном (`xs`) ужимается вместе с остальными иконками.\n- Участвует в **компрессии**: при нехватке места уезжает в overflow-меню `…` вложенным подменю.\n\nПункты меню:\n\n- **Стандартные (из коробки):** «Открепить всё» и «Закрепить столбцы».\n- **Свои пункты** — через `pinningMenu.items` (напр. «Закрепить строки», «Закрепить шапку»). Порядок задаётся `order` (у нативных: `Открепить всё` = 100, `Закрепить столбцы` = 200), разделитель после пункта — `dividerAfter`. Совпадение `value` с нативным = переопределение.\n- Состояние и иконку продуктовых пунктов **контролирует продукт** (иконка — `ReactNode` или функция от `rowSize`, может отражать состояние).\n\nЛевая иконка (кнопка-действие):\n\n- Синеет, когда среди **видимых** колонок есть хотя бы одна закреплённая (скрыли видимость закреплённой — снова обычный цвет).\n- Tooltip и действие зависят от выделения: нет выделения — подсказка выбрать колонки; выбраны незакреплённые — «Закрепить»; выбраны только закреплённые — «Открепить».\n- После закрепления выделение **переезжает** за колонками (закреплённые уезжают влево), соседние сливаются без разделителей.\n- Клик по действию закрепления **без выделенных колонок** шлёт событие в `tableConfig.notifications` (`type: 'pin'`, `code: 'no-selection'`) — продукт показывает свою подсказку. Подробнее — в разделе [Notifications](?path=/docs/локальные-компоненты-tablecanvas-notifications--docs).\n\nПример добавления своих пунктов — в стори «ColumnsControl: доп. пункты меню закрепления».\n\n> Подробнее о типах и пропсах — [ColumnsControl API](?path=/docs/локальные-компоненты-tablecanvas-columnscontrol-api--docs)",
52163
- "apiDocs": "# ColumnsControl API\n\n## ColumnsControlConfig\n\n```typescript\ntype ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};\n```\n\n## PinningMenuConfig\n\nМеню закрепления в контрл-блоке (`controlBlock.pinningMenu`). Появляется, когда включено `columnsControl.pinning`.\n\n```typescript\ntype PinningMenuConfig = {\n /**\n * Явное вкл/выкл фичи.\n * @default columnsControl.pinning\n */\n enable?: boolean;\n /**\n * Продуктовые пункты меню — мёржатся с нативными (override по value,\n * сортировка по order).\n */\n items?: PinningMenuItem[];\n /**\n * DomMetadata левой кнопки-иконки закрепления. Её `dataAttributes`\n * навешиваются на кнопку-иконку (data-атрибуты для e2e-тестов, аналитики и\n * онбординга — поиск элемента в DOM). Пример:\n * `{ dataAttributes: { 'data-testid': 'pinning-icon' } }`.\n */\n iconDomMetadata?: DomMetadata;\n};\n```\n\n## PinningMenuItem\n\nПункт меню закрепления — нативный или добавленный продуктом.\n\n```typescript\ntype PinningMenuItem = {\n /**\n * Уникальный ключ пункта. Совпадение с value нативного пункта = override\n * (продуктовые поля перекрывают нативные).\n */\n value: string;\n label: string;\n /**\n * Иконка слева в пункте. Узел рендерится как есть, либо функция (ctx),\n * чтобы подстроить размер под rowSize (в т.ч. внутри overflow-дропдауна\n * при компрессии). Стейт/иконку продуктовых пунктов контролирует продукт.\n */\n icon?: FeatureIconRender;\n /**\n * Действие пункта. У нативных пунктов задаётся таблицей; продукт может\n * переопределить.\n */\n onClick?: () => void;\n /**\n * Порядок в списке (по возрастанию). Нативные: reset=100, pin-columns=200.\n * Пункты без order идут в конец, сохраняя исходный порядок.\n */\n order?: number;\n /**\n * Нарисовать разделитель после пункта.\n * @default false\n */\n dividerAfter?: boolean;\n disabled?: boolean;\n};\n```",
52305
+ "apiDocs": "# ColumnsControl API\n\n## ColumnsControlConfig\n\n```typescript\ntype ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n /**\n * Виртуализация списка колонок в правом сайдбаре: в DOM держатся только\n * видимые строки списка. false — отключить полностью; { minCount } —\n * включать, когда строк в списке не меньше порога.\n * @default { minCount: 30 }\n */\n virtualization?: boolean | { minCount?: number };\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};\n```\n\n## PinningMenuConfig\n\nМеню закрепления в контрл-блоке (`controlBlock.pinningMenu`). Появляется, когда включено `columnsControl.pinning`.\n\n```typescript\ntype PinningMenuConfig = {\n /**\n * Явное вкл/выкл фичи.\n * @default columnsControl.pinning\n */\n enable?: boolean;\n /**\n * Продуктовые пункты меню — мёржатся с нативными (override по value,\n * сортировка по order).\n */\n items?: PinningMenuItem[];\n /**\n * DomMetadata левой кнопки-иконки закрепления. Её `dataAttributes`\n * навешиваются на кнопку-иконку (data-атрибуты для e2e-тестов, аналитики и\n * онбординга — поиск элемента в DOM). Пример:\n * `{ dataAttributes: { 'data-testid': 'pinning-icon' } }`.\n */\n iconDomMetadata?: DomMetadata;\n};\n```\n\n## PinningMenuItem\n\nПункт меню закрепления — нативный или добавленный продуктом.\n\n```typescript\ntype PinningMenuItem = {\n /**\n * Уникальный ключ пункта. Совпадение с value нативного пункта = override\n * (продуктовые поля перекрывают нативные).\n */\n value: string;\n label: string;\n /**\n * Иконка слева в пункте. Узел рендерится как есть, либо функция (ctx),\n * чтобы подстроить размер под rowSize (в т.ч. внутри overflow-дропдауна\n * при компрессии). Стейт/иконку продуктовых пунктов контролирует продукт.\n */\n icon?: FeatureIconRender;\n /**\n * Действие пункта. У нативных пунктов задаётся таблицей; продукт может\n * переопределить.\n */\n onClick?: () => void;\n /**\n * Порядок в списке (по возрастанию). Нативные: reset=100, pin-columns=200.\n * Пункты без order идут в конец, сохраняя исходный порядок.\n */\n order?: number;\n /**\n * Нарисовать разделитель после пункта.\n * @default false\n */\n dividerAfter?: boolean;\n disabled?: boolean;\n};\n```",
52164
52306
  "api": [
52165
52307
  {
52166
52308
  "typeName": "ColumnsControlConfig",
52167
- "source": "type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};"
52309
+ "source": "type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n /**\n * Виртуализация списка колонок в правом сайдбаре: в DOM держатся только\n * видимые строки списка. false — отключить полностью; { minCount } —\n * включать, когда строк в списке не меньше порога.\n * @default { minCount: 30 }\n */\n virtualization?: boolean | { minCount?: number };\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};"
52168
52310
  },
52169
52311
  {
52170
52312
  "typeName": "PinningMenuConfig",
@@ -52506,7 +52648,7 @@
52506
52648
  "feature": "CopyPasteFill",
52507
52649
  "legacy": false,
52508
52650
  "summary": "*tableConfig.cellTransfer**",
52509
- "docs": "# Clipboard — копирование, вставка, fill handle\n\n**tableConfig.cellTransfer**\n\nФича реализует Copy (Ctrl+C), Paste (Ctrl+V) и Fill Handle (drag-to-fill) для TableCanvas.\n\n---\n\n## Формат данных\n\nДанные копируются и вставляются в формате **TSV (Tab-Separated Values)** — тот же\nформат, который использует Excel и Google Sheets. Значения разделяются `\\t` (tab)\nмежду колонками и `\\n` между строками.\n\nБлагодаря этому:\n\n- Скопированное из TableCanvas можно вставить в Excel / Google Sheets\n- Скопированное из Excel можно вставить в TableCanvas\n\n---\n\n## Copy (Ctrl+C / Cmd+C)\n\nКопирование работает всегда — и в режиме редактирования, и без него.\nОтключить можно только явно через `cellTransfer: { enabled: false }`.\n\nКопируется ровно то, что сейчас выделено в таблице (`selection.current.range`).\nФича не привязана к `highlightActiveType` — она работает с фактическим\nвыделением, а не с тем, как оно визуально подсвечивается.\n\n### Что копируется из ячейки\n\nДля обычной ячейки: `row[column.key]?.toString()`.\n\nДля кастомной canvas-ячейки (Canvas.Badge, Canvas.Text и т.д.) текст из canvas\nавтоматически извлечь нельзя. Поэтому используется `copyData` в конфиге\nколонки:\n\n```tsx\n{\n key: 'status',\n renderCell: ({ row }) => (\n <Canvas.Badge text={row.status} view=\"positive\" size=\"s\" />\n ),\n // Указываем, что копировать — текстовое значение из row\n copyData: (row) => row.status ?? '',\n}\n```\n\nЕсли `copyData` не указан, используется fallback на `row[column.key]?.toString()`.\nДля canvas-ячеек это может дать пустую строку или `[object Object]`, поэтому\n`copyData` рекомендуется всегда указывать для кастомных рендеров.\n\n### SubRows при копировании\n\nДля колонок с `subRow.keyOfColumnInSubRow` данные subrow-ячеек автоматически\nизвлекаются из нужного поля:\n\n```tsx\n{\n key: 'block',\n subRow: {\n keyOfColumnInSubRow: (lvl) => {\n switch (lvl) {\n case 1: return 'tribe';\n case 2: return 'product';\n default: return 'block';\n }\n },\n },\n}\n// Для parent (lvl=0): копирует row.block\n// Для subrow lvl=1: копирует row.tribe\n// Для subrow lvl=2: копирует row.product\n```\n\n---\n\n## Paste (Ctrl+V / Cmd+V)\n\nВставка работает только в режиме редактирования (`editing` активен).\n\nПорядок действий при вставке:\n\n1. Чтение TSV из clipboard (`navigator.clipboard.readText`)\n2. Парсинг в 2D-массив строк\n3. Определение целевой ячейки (из текущего selection)\n4. Применение значений начиная от целевой ячейки вправо и вниз\n\n### Валидация типов при вставке (type-check)\n\nПо умолчанию включена проверка `paste.validation: 'type-check'`:\n\n- **`inputNumber` / `contentFormat: 'number'`** — значение должно парситься в число. Если нет, ячейка пропускается.\n- **`select` (ComboBox)** — значение должно совпадать с одной из `options` (по `value` или `text`). Если нет, ячейка пропускается.\n- **`inputString`** — всегда валидно, строка вставляется как есть.\n\nНевалидные ячейки пропускаются, остальные вставляются нормально.\n\nОтключить: `paste: { validation: 'none' }`.\n\nРазделители для парсинга чисел берутся из `column.contentFormat`\n(`decimalSeparator`, `thousandSeparator`), а не из локали.\n\n### Readonly-ячейки\n\nПо умолчанию (`readonlyBehavior: 'skip'`) readonly-ячейки пропускаются,\nостальные вставляются нормально.\n\nПри `readonlyBehavior: 'abort'` любая readonly-ячейка в зоне вставки отменяет\nвсю вставку целиком (поведение Excel).\n\n### Выход за границы\n\nПо умолчанию (`overflowBehavior: 'truncate'`) данные обрезаются по границе\nтаблицы. То, что не влезло, теряется.\n\nПри `overflowBehavior: 'abort'` вставка отменяется, если данные не влезают.\n\n### Вставка в кастомные ячейки (Canvas.Badge и т.д.)\n\n`writeCellValue` пишет строку в `row[column.key]`. Кастомный рендер на следующем\nцикле получает обновлённый `row` и отрисовывает новое значение.\n\nЕсли `renderCell` ожидает в `row[key]` не строку, а объект, вставка строки\nсломает рендер. В этом случае используйте `onBeforePaste` для трансформации:\n\n```tsx\ncellTransfer: {\n onBeforePaste: (data, meta) => {\n // Трансформировать строки в нужные объекты\n return data.map((row, y) =>\n row.map((value, x) => {\n const cell = meta.targetCells[y]?.[x];\n if (cell?.column.key === 'complexField') {\n return JSON.stringify({ label: value, color: 'default' });\n }\n return value;\n })\n );\n },\n}\n```\n\n### SubRows при вставке\n\nРаботает аналогично копированию — через `keyOfColumnInSubRow`. Если у subrow\nданные в другом поле, `writeCellValue` запишет в правильный ключ.\n\nОтключить вставку в subrows: `paste: { allowSubRows: false }`.\n\n### Поток данных при вставке (pipeline)\n\nПри Ctrl+V значения проходят через цепочку проверок. Каждый шаг может отменить\nоперацию или пропустить отдельную ячейку:\n\n```\nCtrl+V\n │\n ▼\n1. editModeEnabled?\n └─ нет → СТОП (вставка только в editing)\n │\n ▼\n2. navigator.clipboard.readText() → parseTsv() → string[][]\n └─ пустой clipboard → СТОП\n │\n ▼\n3. onBeforePaste(data, meta)?\n └─ вернул false -> СТОП (пользователь отменил)\n └─ вернул модифицированный data → используем его\n │\n ▼\n4. overflowBehavior: данные выходят за границы?\n └─ 'abort' → СТОП\n └─ 'truncate' → обрезаем (default)\n │\n ▼\n5. readonlyBehavior: есть readonly-ячейки в зоне вставки?\n └─ 'abort' → СТОП (первый проход, до записи)\n └─ 'skip' → пропускаем эти ячейки (default)\n │\n ▼\n6. Для каждой ячейки:\n │\n ├─ shouldSkipCell?\n │ ├─ no-row / нет колонки → skip\n │ ├─ service-column → skip\n │ ├─ subrow + !allowSubRows → skip\n │ └─ not-editable → skip\n │\n ├─ validation: 'type-check'?\n │ ├─ inputNumber: не парсится в число → skip\n │ ├─ select: значения нет в options → skip\n │ └─ inputString: OK\n │\n └─ writeCellValue(row, column, lvl, value) → запись\n │\n ▼\n7. onRowsChange(newRows, { column, indexes })\n tree update → setRows → re-render\n```\n\nFill handle проходит тот же pipeline начиная с шага 6 (без clipboard, без\noverflow — TableCanvas сам ограничивает destination range). Перед записью вызывается\n`onBeforeFill(data, meta)` — аналог `onBeforePaste` для fill handle.\n\n---\n\n## Нотификации об ошибках\n\nОшибки copy / paste / fill дополнительно сообщаются во внешний модуль\n[Notifications](?path=/docs/локальные-компоненты-tablecanvas-notifications--docs)\nчерез `tableConfig.notifications.onNotification` — таблица UI не рисует, тост\nпоказывает потребитель (`type` = операция):\n\n- `paste` / `readonly-abort` — при `readonlyBehavior: 'abort'` в зоне вставки\n есть readonly-ячейка;\n- `paste` / `overflow-abort` — при `overflowBehavior: 'abort'` данные не влезли;\n- `paste` / `validation-skipped` (`warning`) — часть значений пропущена\n type-check валидацией;\n- `fill` / `readonly-abort` и `fill` / `validation-skipped` — то же для\n автозаполнения (fill handle);\n- `copy` / `paste` с code `multi-range-scattered` — 2D-разброс в\n `multi-range-cell`.\n\n## Перехват данных — onBeforeCopy / onBeforePaste\n\nКолбэки вызываются перед операцией. Получают:\n\n- Первый аргумент: `data: string[][]` — 2D-массив значений (TSV-формат)\n- Второй аргумент: `meta` — информация о ячейках\n\n### onBeforeCopy\n\n```tsx\ncellTransfer: {\n onBeforeCopy: (data, meta) => {\n // data — string[][] (что будет в clipboard)\n // meta.range — Rectangle выделения\n // meta.cells — CellTransferCellInfo[][] (row, column, lvl, rawValue, formattedValue)\n\n console.log('Копируем:', data);\n console.log('Колонки:', meta.cells[0]?.map(c => c.column.key));\n\n // Модифицировать данные:\n return data.map(row => row.map(val => val.toUpperCase()));\n\n // Или отменить копирование:\n return false;\n },\n}\n```\n\n### onBeforePaste\n\n```tsx\ncellTransfer: {\n onBeforePaste: (data, meta) => {\n // data — string[][] (что из clipboard)\n // meta.target — { col, row } верхний-левый угол вставки\n // meta.targetCells — CellTransferCellInfo[][] (инфа о ЦЕЛЕВЫХ ячейках)\n // meta.selection — GridSelection\n\n // Пример: ограничить кол-во строк\n if (data.length > 100) {\n alert('Слишком много строк для вставки');\n return false;\n }\n\n // Пример: трансформировать данные\n return data;\n },\n}\n```\n\n---\n\n## Fill Handle (drag-to-fill)\n\nВ правом-нижнем углу выделения появляется квадратик. Перетаскивая его мышью,\nможно размножить значения (как в Excel / Google Sheets).\n\nКвадратик и preview-рамку таблица рисует нативно — фича только применяет\nзначения source range в destination range.\n\n### Поведение\n\n- **Одна ячейка** — значение дублируется во все target-ячейки\n- **Диапазон N×M** — значения повторяются циклически\n- **Type-check валидация** — та же, что при paste (числа, select-опции)\n- **Readonly-ячейки** в destination пропускаются\n- **Квадратик скрывается**, если source-ячейка non-editable\n\n### Направления\n\nПо умолчанию `allowedDirections: 'orthogonal'` — snap к ближайшей оси\n(только строки или только колонки, не диагональ).\n\nВарианты: `'horizontal'`, `'vertical'`, `'orthogonal'`, `'any'`.\n\n### Перехват — onBeforeFill\n\nКолбэк `onBeforeFill` вызывается перед применением значений при fill handle.\nПолучает `data: string[][]` и `meta: FillMeta` (см. API — структура meta\nотличается от `CopyMeta` / `PasteMeta`).\n\n```tsx\ncellTransfer: {\n fillHandle: true,\n onBeforeFill: (data, meta) => {\n // data — string[][] (значения, которые будут вставлены)\n // meta.sourceRange — Rectangle источника\n // meta.destinationRange — Rectangle назначения\n // meta.sourceCells — CellTransferCellInfo[][] (исходные ячейки, включая row)\n // meta.targetCells — CellTransferCellInfo[][] (целевые ячейки до заполнения)\n\n const sourceRow = meta.sourceCells[0]?.[0]?.row;\n console.log('Исходная строка:', sourceRow);\n\n // Модифицировать данные:\n return data;\n\n // Или отменить:\n return false;\n },\n}\n```\n\n**Типичный сценарий:** при перетаскивании одной колонки нужно скопировать\nи соседние колонки из исходной строки. Source row доступен через\n`meta.sourceCells[rowIndex][colIndex].row`.\n\n### type в onRowsChange\n\nВторой аргумент `onRowsChange` содержит поле `type` (`'edit'` | `'paste'` | `'fill'`),\nуказывающее источник изменения строк. При `type === 'fill'` дополнительно\nпередаются `fillMeta` (FillMeta) и `fillResult` (результат `onBeforeFill`,\nесли он задан).\n\n---\n\n## Кастомные хоткеи\n\nПо умолчанию: Ctrl+C (copy), Ctrl+V (paste). На Mac — Cmd вместо Ctrl.\n\nХоткеи работают независимо от раскладки (RU/EN) — используется `event.code`\n(`'KeyC'`, `'KeyV'`), а не `event.key` (`'c'`, `'с'`).\n\n```tsx\ncellTransfer: {\n hotkeys: {\n copy: { code: 'KeyC', ctrl: true, alt: true }, // Ctrl+Alt+C\n paste: [\n { code: 'KeyV', ctrl: true }, // Ctrl+V (дефолт сохраняем)\n { code: 'Insert', shift: true }, // Shift+Insert (альтернатива)\n ],\n },\n}\n```\n\nЕсли задать только кастомный хоткей (без массива), дефолтный Ctrl+C / Ctrl+V\nперестаёт работать (replace-семантика). Чтобы сохранить оба варианта,\nпередавайте массив, как в примере для `paste` выше.\n\n---\n\n## Конфигурация\n\n```tsx\ntableConfig={{\n cellTransfer: {\n // Явно включить/выключить (copy работает всегда по умолчанию)\n enabled: true,\n\n // Хоткеи\n hotkeys: { copy: ..., paste: ... },\n\n // Настройки paste\n paste: {\n readonlyBehavior: 'skip' | 'abort', // default: 'skip'\n allowSubRows: true | false, // default: true\n overflowBehavior: 'truncate' | 'abort', // default: 'truncate'\n broadcast: true | false, // default: false\n validation: 'none' | 'type-check', // default: 'type-check'\n },\n\n // Fill handle\n fillHandle: true | false | {\n shape: 'square' | 'circle',\n size: number,\n allowedDirections: 'horizontal' | 'vertical' | 'orthogonal' | 'any',\n readonlyBehavior: 'skip' | 'abort',\n allowSubRows: boolean,\n },\n\n // Перехваты\n onBeforeCopy: (data, meta) => data | false,\n onBeforePaste: (data, meta) => data | false,\n onBeforeFill: (data, meta) => data | false,\n },\n}}\n```\n\n---\n\n## Debug\n\n```js\nwindow.__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n```\n\nВключите уровень \"Verbose\" в DevTools. Префиксы логов:\n\n- `[TableCanvas/cellTransfer]` — перехват хоткеев\n- `[TableCanvas/copy]` — копирование\n- `[TableCanvas/paste]` — вставка (включая skipped by validation)\n- `[TableCanvas/fillHandle]` — fill handle (source, dest, affected rows)\n\nПолное описание API типов см. на странице **Clipboard / API**.",
52651
+ "docs": "# Clipboard — копирование, вставка, fill handle\n\n**tableConfig.cellTransfer**\n\nФича реализует Copy (Ctrl+C), Paste (Ctrl+V) и Fill Handle (drag-to-fill) для TableCanvas.\n\n---\n\n## Формат данных\n\nДанные копируются и вставляются в формате **TSV (Tab-Separated Values)** — тот же\nформат, который использует Excel и Google Sheets. Значения разделяются `\\t` (tab)\nмежду колонками и `\\n` между строками.\n\nБлагодаря этому:\n\n- Скопированное из TableCanvas можно вставить в Excel / Google Sheets\n- Скопированное из Excel можно вставить в TableCanvas\n\n---\n\n## Copy (Ctrl+C / Cmd+C)\n\nКопирование работает всегда — и в режиме редактирования, и без него.\nОтключить можно только явно через `cellTransfer: { enabled: false }`.\n\nКопируется ровно то, что сейчас выделено в таблице (`selection.current.range`).\nФича не привязана к `highlightActiveType` — она работает с фактическим\nвыделением, а не с тем, как оно визуально подсвечивается.\n\n### Что копируется из ячейки\n\nДля обычной ячейки: `row[column.key]?.toString()`.\n\nДля кастомной canvas-ячейки (Canvas.Badge, Canvas.Text и т.д.) текст из canvas\nавтоматически извлечь нельзя. Поэтому используется `copyData` в конфиге\nколонки:\n\n```tsx\n{\n key: 'status',\n renderCell: ({ row }) => (\n <Canvas.Badge text={row.status} view=\"positive\" size=\"s\" />\n ),\n // Указываем, что копировать — текстовое значение из row\n copyData: (row) => row.status ?? '',\n}\n```\n\nЕсли `copyData` не указан, используется fallback на `row[column.key]?.toString()`.\nДля canvas-ячеек это может дать пустую строку или `[object Object]`, поэтому\n`copyData` рекомендуется всегда указывать для кастомных рендеров.\n\n### SubRows при копировании\n\nДля колонок с `subRow.keyOfColumnInSubRow` данные subrow-ячеек автоматически\nизвлекаются из нужного поля:\n\n```tsx\n{\n key: 'block',\n subRow: {\n keyOfColumnInSubRow: (lvl) => {\n switch (lvl) {\n case 1: return 'tribe';\n case 2: return 'product';\n default: return 'block';\n }\n },\n },\n}\n// Для parent (lvl=0): копирует row.block\n// Для subrow lvl=1: копирует row.tribe\n// Для subrow lvl=2: копирует row.product\n```\n\n---\n\n## Paste (Ctrl+V / Cmd+V)\n\nВставка работает только в режиме редактирования (`editing` активен).\n\nПорядок действий при вставке:\n\n1. Чтение TSV из clipboard (`navigator.clipboard.readText`)\n2. Парсинг в 2D-массив строк\n3. Определение целевой ячейки (из текущего selection)\n4. Применение значений начиная от целевой ячейки вправо и вниз\n\n### Валидация типов при вставке (type-check)\n\nПо умолчанию включена проверка `paste.validation: 'type-check'`:\n\n- **`inputNumber` / `contentFormat: 'number'`** — значение должно парситься в число. Если нет, ячейка пропускается.\n- **`select` (ComboBox)** — значение должно совпадать с одной из `options` (по `value` или `text`). Если нет, ячейка пропускается.\n- **`inputString`** — всегда валидно, строка вставляется как есть.\n\nНевалидные ячейки пропускаются, остальные вставляются нормально.\n\nОтключить: `paste: { validation: 'none' }`.\n\nРазделители для парсинга чисел берутся из `column.contentFormat`\n(`decimalSeparator`, `thousandSeparator`), а не из локали.\n\n### Readonly-ячейки\n\nПо умолчанию (`readonlyBehavior: 'skip'`) readonly-ячейки пропускаются,\nостальные вставляются нормально.\n\nПри `readonlyBehavior: 'abort'` любая readonly-ячейка в зоне вставки отменяет\nвсю вставку целиком (поведение Excel).\n\n### Выход за границы\n\nПо умолчанию (`overflowBehavior: 'truncate'`) данные обрезаются по границе\nтаблицы. То, что не влезло, теряется.\n\nПри `overflowBehavior: 'abort'` вставка отменяется, если данные не влезают.\n\n### Вставка в кастомные ячейки (Canvas.Badge и т.д.)\n\n`writeCellValue` пишет строку в `row[column.key]`. Кастомный рендер на следующем\nцикле получает обновлённый `row` и отрисовывает новое значение.\n\nЕсли `renderCell` ожидает в `row[key]` не строку, а объект, вставка строки\nсломает рендер. В этом случае используйте `onBeforePaste` для трансформации:\n\n```tsx\ncellTransfer: {\n onBeforePaste: (data, meta) => {\n // Трансформировать строки в нужные объекты\n return data.map((row, y) =>\n row.map((value, x) => {\n const cell = meta.targetCells[y]?.[x];\n if (cell?.column.key === 'complexField') {\n return JSON.stringify({ label: value, color: 'default' });\n }\n return value;\n })\n );\n },\n}\n```\n\n### SubRows при вставке\n\nРаботает аналогично копированию — через `keyOfColumnInSubRow`. Если у subrow\nданные в другом поле, `writeCellValue` запишет в правильный ключ.\n\nОтключить вставку в subrows: `paste: { allowSubRows: false }`.\n\n### Поток данных при вставке (pipeline)\n\nПри Ctrl+V значения проходят через цепочку проверок. Каждый шаг может отменить\nоперацию или пропустить отдельную ячейку:\n\n```\nCtrl+V\n │\n ▼\n1. editModeEnabled?\n └─ нет → СТОП (вставка только в editing)\n │\n ▼\n2. navigator.clipboard.readText() → parseTsv() → string[][]\n └─ пустой clipboard → СТОП\n │\n ▼\n3. onBeforePaste(data, meta)?\n └─ вернул false -> СТОП (пользователь отменил)\n └─ вернул модифицированный data → используем его\n │\n ▼\n4. overflowBehavior: данные выходят за границы?\n └─ 'abort' → СТОП\n └─ 'truncate' → обрезаем (default)\n │\n ▼\n5. readonlyBehavior: есть readonly-ячейки в зоне вставки?\n └─ 'abort' → СТОП (первый проход, до записи)\n └─ 'skip' → пропускаем эти ячейки (default)\n │\n ▼\n6. Для каждой ячейки:\n │\n ├─ shouldSkipCell?\n │ ├─ no-row / нет колонки → skip\n │ ├─ service-column → skip\n │ ├─ subrow + !allowSubRows → skip\n │ └─ not-editable → skip\n │\n ├─ validation: 'type-check'?\n │ ├─ inputNumber: не парсится в число → skip\n │ ├─ select: значения нет в options → skip\n │ └─ inputString: OK\n │\n └─ writeCellValue(row, column, lvl, value) → запись\n │\n ▼\n7. onRowsChange(newRows, { column, indexes })\n tree update → setRows → re-render\n```\n\nFill handle проходит ту же цепочку начиная с шага 6 (без clipboard и без\noverflow — зону протяжки таблица ограничивает сама). Перед записью вызывается\n`onBeforeFill(data, meta)` — аналог `onBeforePaste` для fill handle.\n\n---\n\n## Нотификации об ошибках\n\nОшибки copy / paste / fill дополнительно сообщаются во внешний модуль\n[Notifications](?path=/docs/локальные-компоненты-tablecanvas-notifications--docs)\nчерез `tableConfig.notifications.onNotification` — таблица UI не рисует, тост\nпоказывает потребитель (`type` = операция):\n\n- `paste` / `readonly-abort` — при `readonlyBehavior: 'abort'` в зоне вставки\n есть readonly-ячейка;\n- `paste` / `overflow-abort` — при `overflowBehavior: 'abort'` данные не влезли;\n- `paste` / `validation-skipped` (`warning`) — часть значений пропущена\n type-check валидацией;\n- `fill` / `readonly-abort` и `fill` / `validation-skipped` — то же для\n автозаполнения (fill handle);\n- `copy` / `paste` с code `multi-range-scattered` — 2D-разброс в\n `multi-range-cell`.\n\n## Перехват данных — onBeforeCopy / onBeforePaste\n\nКолбэки вызываются перед операцией. Получают:\n\n- Первый аргумент: `data: string[][]` — 2D-массив значений (TSV-формат)\n- Второй аргумент: `meta` — информация о ячейках\n\n### onBeforeCopy\n\n```tsx\ncellTransfer: {\n onBeforeCopy: (data, meta) => {\n // data — string[][] (что будет в clipboard)\n // meta.range — Rectangle выделения\n // meta.cells — CellTransferCellInfo[][] (row, column, lvl, rawValue, formattedValue)\n\n console.log('Копируем:', data);\n console.log('Колонки:', meta.cells[0]?.map(c => c.column.key));\n\n // Модифицировать данные:\n return data.map(row => row.map(val => val.toUpperCase()));\n\n // Или отменить копирование:\n return false;\n },\n}\n```\n\n### onBeforePaste\n\n```tsx\ncellTransfer: {\n onBeforePaste: (data, meta) => {\n // data — string[][] (что из clipboard)\n // meta.target — { col, row } верхний-левый угол вставки\n // meta.targetCells — CellTransferCellInfo[][] (инфа о ЦЕЛЕВЫХ ячейках)\n // meta.selection — GridSelection\n\n // Пример: ограничить кол-во строк\n if (data.length > 100) {\n alert('Слишком много строк для вставки');\n return false;\n }\n\n // Пример: трансформировать данные\n return data;\n },\n}\n```\n\n---\n\n## Fill Handle (drag-to-fill)\n\nВ правом-нижнем углу выделения появляется квадратик. Перетаскивая его мышью,\nможно размножить значения (как в Excel / Google Sheets).\n\nКвадратик и preview-рамку таблица рисует нативно — фича только применяет\nзначения source range в destination range.\n\n### Поведение\n\n- **Одна ячейка** — значение дублируется во все target-ячейки\n- **Диапазон N×M** — значения повторяются циклически\n- **Type-check валидация** — та же, что при paste (числа, select-опции)\n- **Readonly-ячейки** в destination пропускаются\n- **Квадратик скрывается**, если source-ячейка non-editable\n\n### Направления\n\nПо умолчанию `allowedDirections: 'orthogonal'` — snap к ближайшей оси\n(только строки или только колонки, не диагональ).\n\nВарианты: `'horizontal'`, `'vertical'`, `'orthogonal'`, `'any'`.\n\n### Перехват — onBeforeFill\n\nКолбэк `onBeforeFill` вызывается перед применением значений при fill handle.\nПолучает `data: string[][]` и `meta: FillMeta` (см. API — структура meta\nотличается от `CopyMeta` / `PasteMeta`).\n\n```tsx\ncellTransfer: {\n fillHandle: true,\n onBeforeFill: (data, meta) => {\n // data — string[][] (значения, которые будут вставлены)\n // meta.sourceRange — Rectangle источника\n // meta.destinationRange — Rectangle назначения\n // meta.sourceCells — CellTransferCellInfo[][] (исходные ячейки, включая row)\n // meta.targetCells — CellTransferCellInfo[][] (целевые ячейки до заполнения)\n\n const sourceRow = meta.sourceCells[0]?.[0]?.row;\n console.log('Исходная строка:', sourceRow);\n\n // Модифицировать данные:\n return data;\n\n // Или отменить:\n return false;\n },\n}\n```\n\n**Типичный сценарий:** при перетаскивании одной колонки нужно скопировать\nи соседние колонки из исходной строки. Source row доступен через\n`meta.sourceCells[rowIndex][colIndex].row`.\n\n### type в onRowsChange\n\nВторой аргумент `onRowsChange` содержит поле `type` (`'edit'` | `'paste'` | `'fill'`),\nуказывающее источник изменения строк. При `type === 'fill'` дополнительно\nпередаются `fillMeta` (FillMeta) и `fillResult` (результат `onBeforeFill`,\nесли он задан).\n\n---\n\n## Кастомные хоткеи\n\nПо умолчанию: Ctrl+C (copy), Ctrl+V (paste). На Mac — Cmd вместо Ctrl.\n\nХоткеи работают независимо от раскладки (RU/EN) — используется `event.code`\n(`'KeyC'`, `'KeyV'`), а не `event.key` (`'c'`, `'с'`).\n\n```tsx\ncellTransfer: {\n hotkeys: {\n copy: { code: 'KeyC', ctrl: true, alt: true }, // Ctrl+Alt+C\n paste: [\n { code: 'KeyV', ctrl: true }, // Ctrl+V (дефолт сохраняем)\n { code: 'Insert', shift: true }, // Shift+Insert (альтернатива)\n ],\n },\n}\n```\n\nЕсли задать только кастомный хоткей (без массива), дефолтный Ctrl+C / Ctrl+V\nперестаёт работать (replace-семантика). Чтобы сохранить оба варианта,\nпередавайте массив, как в примере для `paste` выше.\n\n---\n\n## Конфигурация\n\n```tsx\ntableConfig={{\n cellTransfer: {\n // Явно включить/выключить (copy работает всегда по умолчанию)\n enabled: true,\n\n // Хоткеи\n hotkeys: { copy: ..., paste: ... },\n\n // Настройки paste\n paste: {\n readonlyBehavior: 'skip' | 'abort', // default: 'skip'\n allowSubRows: true | false, // default: true\n overflowBehavior: 'truncate' | 'abort', // default: 'truncate'\n broadcast: true | false, // default: false\n validation: 'none' | 'type-check', // default: 'type-check'\n },\n\n // Fill handle\n fillHandle: true | false | {\n shape: 'square' | 'circle',\n size: number,\n allowedDirections: 'horizontal' | 'vertical' | 'orthogonal' | 'any',\n readonlyBehavior: 'skip' | 'abort',\n allowSubRows: boolean,\n },\n\n // Перехваты\n onBeforeCopy: (data, meta) => data | false,\n onBeforePaste: (data, meta) => data | false,\n onBeforeFill: (data, meta) => data | false,\n },\n}}\n```\n\n---\n\n## Debug\n\n```js\nwindow.__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n```\n\nВключите уровень \"Verbose\" в DevTools. Префиксы логов:\n\n- `[TableCanvas/cellTransfer]` — перехват хоткеев\n- `[TableCanvas/copy]` — копирование\n- `[TableCanvas/paste]` — вставка (включая skipped by validation)\n- `[TableCanvas/fillHandle]` — fill handle (source, dest, affected rows)\n\nПолное описание API типов см. на странице **Clipboard / API**.",
52510
52652
  "apiDocs": "# Cell Transfer API (Copy / Paste / Fill)\n\n## CellTransferConfig (в TableConfig)\n\n```typescript\ntype CellTransferConfig = {\n /**\n * Явное включение/выключение всей фичи.\n * По умолчанию copy работает всегда, а paste и fill handle следуют\n * за `editModeEnabled`. При `false` отключается всё.\n */\n enabled?: boolean;\n /**\n * Переопределение хоткеев.\n * Можно передать один `HotkeyConfig` или массив альтернативных комбинаций.\n * По умолчанию: copy = Ctrl+C / Cmd+C, paste = Ctrl+V / Cmd+V.\n */\n hotkeys?: {\n /** Хоткей(и) для копирования */\n copy?: HotkeyConfig | HotkeyConfig[];\n /** Хоткей(и) для вставки */\n paste?: HotkeyConfig | HotkeyConfig[];\n };\n /** Настройки paste (readonly, overflow, broadcast, validation) */\n paste?: PasteConfig;\n /** Настройки fill handle. `true/false` — быстрое вкл/выкл без деталей */\n fillHandle?: boolean | FillHandleConfig;\n /**\n * Колбэк перед копированием.\n * Можно модифицировать данные (вернуть новый `string[][]`) или отменить\n * операцию (вернуть `false`).\n * Если onBeforeCopy нужен для side-эффектов, то функция должна вернуть data из первого аргумента без изменений.\n */\n onBeforeCopy?: (data: string[][], meta: CopyMeta) => string[][] | false;\n /**\n * Колбэк перед вставкой.\n * Можно модифицировать данные (вернуть новый `string[][]`) или отменить\n * операцию (вернуть `false`).\n * Если onBeforePaste нужен для side-эффектов, то функция должна вернуть data из первого аргумента без изменений.\n */\n onBeforePaste?: (data: string[][], meta: PasteMeta) => string[][] | false;\n /**\n * Колбэк перед fill handle (drag-to-fill).\n * Вызывается перед применением значений. Можно модифицировать данные\n * (вернуть новый `string[][]`) или отменить операцию (вернуть `false`).\n *\n * `meta.sourceCells` содержит полную информацию об исходных ячейках,\n * включая объекты строк (`row`) — позволяет получить source row\n * и использовать данные из соседних колонок.\n * Если onBeforeFill нужен для side-эффектов, то функция должна вернуть data из первого аргумента без изменений.\n */\n onBeforeFill?: (data: string[][], meta: FillMeta) => string[][] | false;\n};\n```\n\n## HotkeyConfig\n\n```typescript\ntype HotkeyConfig = {\n /** Физический код клавиши: `'KeyC'`, `'KeyV'`, `'Insert'` и т.д. */\n code: string;\n /** Нужно ли нажатие Ctrl (на Mac учитывается и Cmd). По умолчанию `true` */\n ctrl?: boolean;\n /** Нужно ли нажатие Shift. По умолчанию `false` */\n shift?: boolean;\n /** Нужно ли нажатие Alt. По умолчанию `false` */\n alt?: boolean;\n /** Нужно ли нажатие Meta (Cmd на Mac, Win на Windows). По умолчанию `false` */\n meta?: boolean;\n};\n```\n\nМожно передать массив — сработает любая из комбинаций:\n\n```tsx\nhotkeys: {\n copy: [\n { code: 'KeyC', ctrl: true },\n { code: 'Insert', ctrl: true }, // альтернативный хоткей\n ],\n}\n```\n\n## PasteConfig\n\n```typescript\ntype PasteConfig = {\n /**\n * Что делать с readonly-ячейками в зоне вставки.\n * `'skip'` пропускает их, `'abort'` отменяет всю вставку целиком.\n * По умолчанию `'skip'`.\n */\n readonlyBehavior?: ReadonlyBehavior;\n /** Разрешать ли вставку в subrow-ячейки. По умолчанию `true` */\n allowSubRows?: boolean;\n /**\n * Что делать при выходе данных за границы таблицы.\n * `'truncate'` обрезает лишнее, `'abort'` отменяет вставку.\n * По умолчанию `'truncate'`.\n */\n overflowBehavior?: OverflowBehavior;\n /**\n * Тиражирование источника на всё выделение (как в Excel).\n * Если `true` и в буфере одна строка/колонка, а выделение больше,\n * буфер повторится по соответствующей оси.\n * По умолчанию `false`.\n */\n broadcast?: boolean;\n /**\n * Режим валидации значений при вставке.\n * - `'none'` вставляет строку как есть без проверок\n * - `'type-check'` (по умолчанию) проверяет соответствие типу ячейки:\n * числовые поля должны парситься в число, select-поля должны содержать\n * значение из `options`. Невалидные ячейки пропускаются.\n */\n validation?: ValidationMode;\n};\n```\n\nПоле `validation: 'type-check'` (default) автоматически проверяет:\n\n- **inputNumber / contentFormat.type='number'** — значение должно парситься в число через разделители из `contentFormat`. Невалидные ячейки пропускаются.\n- **select** — значение должно совпадать с одной из options (value или text). Невалидные пропускаются.\n- **inputString / остальное** — всегда валидно.\n\nУстановите `validation: 'none'` чтобы вставлять сырые строки без проверок.\n\n## CellTransferCellInfo\n\nИнформация об одной ячейке. Используется как элемент двумерного массива `cells` внутри\nвсех meta-типов ниже (`CopyMeta`, `PasteMeta`, `FillMeta`).\n\n```typescript\ntype CellTransferCellInfo = {\n /** Объект строки, в которой находится ячейка */\n row: ObjectForExtending;\n /** Конфиг колонки (срез важных полей) */\n column: TransferColumnConfig;\n /** Индекс колонки в массиве columns */\n colIndex: number;\n /** Индекс строки в массиве flattenedRows */\n rowIndex: number;\n /** Уровень вложенности subrow: 0 — parent, 1+ — subrow */\n lvl: number;\n /** Сырое значение из `row[key]` или `row[subKey]` (без форматирования) */\n rawValue: unknown;\n /** Текстовое представление ячейки (то что попадает в TSV) */\n formattedValue: string;\n};\n```\n\n## Метаданные колбэков (Meta-типы)\n\nКаждый колбэк (`onBeforeCopy`, `onBeforePaste`, `onBeforeFill`) получает вторым\nаргументом свой meta-объект с контекстом операции.\n\n### CopyMeta (`onBeforeCopy`)\n\n```typescript\ntype CopyMeta = {\n /** Прямоугольный диапазон выделения в координатах таблицы */\n range: Rectangle;\n /** Двумерный массив информации о каждой ячейке, той же формы что data */\n cells: CellTransferCellInfo[][];\n};\n```\n\n### PasteMeta (`onBeforePaste`)\n\n```typescript\ntype PasteMeta = {\n /** Верхний-левый угол целевой области (куда начинается вставка) */\n target: { col: number; row: number };\n /** Двумерный массив информации о целевых ячейках, той же формы что data */\n targetCells: CellTransferCellInfo[][];\n /** Текущее выделение в таблице */\n selection: GridSelection;\n};\n```\n\n### FillMeta (`onBeforeFill`)\n\n```typescript\ntype FillMeta = {\n /** Исходный диапазон (откуда тянули) */\n sourceRange: Rectangle;\n /** Целевой диапазон (куда заполнили) */\n destinationRange: Rectangle;\n /** Двумерный массив информации об исходных ячейках */\n sourceCells: CellTransferCellInfo[][];\n /** Двумерный массив информации о целевых ячейках (до заполнения) */\n targetCells: CellTransferCellInfo[][];\n};\n```\n\n## RowsChangeType\n\nПоле `type` в `onRowsChange` указывает источник изменения строк.\n\n```typescript\ntype RowsChangeType = 'edit' | 'paste' | 'fill';\n```\n\n## FillHandleConfig\n\n```typescript\ntype FillHandleConfig = {\n /** Включён ли fill handle. По умолчанию включён если включён cellTransfer */\n enabled?: boolean;\n /** Форма квадратика. По умолчанию `'square'` */\n shape?: 'square' | 'circle';\n /** Размер квадратика в пикселях */\n size?: number;\n /** Толщина обводки квадратика в пикселях */\n outline?: number;\n /**\n * Разрешённые направления перетаскивания.\n * `'orthogonal'` (по умолчанию) разрешает только вдоль одной оси за раз.\n */\n allowedDirections?: AllowedFillDirections;\n /**\n * Поведение при попадании readonly-ячеек в destination.\n * См. `PasteConfig.readonlyBehavior`. По умолчанию `'skip'`.\n */\n readonlyBehavior?: ReadonlyBehavior;\n /** Разрешать ли fill в subrow-ячейки. По умолчанию `true` */\n allowSubRows?: boolean;\n};\n```\n\n## AllowedFillDirections / FillPatternEvent\n\n```typescript\ntype AllowedFillDirections =\n | 'horizontal'\n | 'vertical'\n | 'orthogonal'\n | 'any';\n```\n\n```typescript\ntype FillPatternEvent = {\n /** Исходный диапазон (откуда берутся значения) */\n patternSource: Rectangle;\n /** Целевой диапазон (куда тиражируются значения) */\n fillDestination: Rectangle;\n /** Вызов отменяет fill (данные не записываются) */\n preventDefault: () => void;\n};\n```\n\n## columnConfig.copyData\n\nТекстовое представление ячейки для копирования в буфер.\n\n```ts\ncopyData?: string | ((row: Row) => string);\n```\n\nЕсли `copyData` не задан, используется `row[column.key]?.toString()`.\n\n## Примеры\n\n### Базовое использование (работает из коробки)\n\n```tsx\n<TableCanvas\n tableConfig={{\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => r.id,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n/>\n```\n\nCopy (Ctrl+C) и Paste (Ctrl+V) работают автоматически в режиме редактирования.\n\n### Excel-like поведение paste\n\n```tsx\ntableConfig={{\n cellTransfer: {\n paste: {\n readonlyBehavior: 'abort', // как в Excel\n broadcast: true,\n },\n },\n}}\n```\n\n### Кастомные хоткеи + перехват\n\n```tsx\ntableConfig={{\n cellTransfer: {\n hotkeys: {\n copy: { code: 'KeyC', ctrl: true, alt: true },\n paste: { code: 'KeyV', ctrl: true, alt: true },\n },\n onBeforeCopy: (data) => {\n // логируем или модифицируем данные\n console.log('Copying:', data);\n return data;\n },\n onBeforePaste: (data) => {\n if (data.length > 100) {\n alert('Слишком много строк');\n return false;\n }\n return data;\n },\n },\n}}\n```\n\n### Кастомный copyData для canvas-ячейки\n\n```tsx\ncolumnConfig={[\n {\n key: 'status',\n name: 'Статус',\n renderCell: ({ row }) => (\n <Canvas.Badge\n text={row.status}\n view={row.status === 'Активный' ? 'positive' : 'warning'}\n />\n ),\n copyData: (row) => row.status ?? '', // копируем только текст\n },\n]}\n```",
52511
52653
  "api": [
52512
52654
  {
@@ -52559,37 +52701,37 @@
52559
52701
  "exportName": "ClipboardFullDemo",
52560
52702
  "displayName": "Полный пример (все возможности)",
52561
52703
  "type": "full-code",
52562
- "code": "() => {\n useEffect(() => {\n (\n window as unknown as Record<string, boolean>\n ).__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n }, []);\n\n const [rows, setRows] = useState(() => createRowsTree());\n const [highlightActiveType, setHighlightActiveType] =\n useState<HighlightActiveType>('row');\n const [selectionMode, setSelectionMode] =\n useState<CellsSelectionMode>('range-cell');\n\n const columns = useMemo(\n (): readonly ColumnConfig<TreeRow>[] => [\n {\n key: 'block',\n name: 'Блок',\n editingCell: {\n editable: (r) => r.block === BLOCKS[1],\n component: 'select',\n options: {\n type: 'constant',\n options: BLOCKS.map((i) => ({ text: i, value: i })),\n },\n },\n subRow: {\n keyOfColumnInSubRow: (lvl) => {\n switch (lvl) {\n case 0:\n return 'block';\n case 1:\n return 'tribe';\n case 2:\n return 'product';\n default:\n return 'block';\n }\n },\n editingCell: {\n component: 'inputString',\n inputProps: { placeholder: 'Введите значение' },\n },\n isColumnWithArrow: true,\n },\n },\n {\n key: 'blockActivity',\n name: 'Активность блока',\n editingCell: { component: 'inputString' },\n renderCell: ({ row }) => (\n <Canvas.Container direction=\"row\" alignItems=\"center\" padding={8}>\n <Canvas.Badge\n text={row.blockActivity}\n view={row.blockActivity === 'Активный' ? 'positive' : 'warning'}\n size=\"s\"\n />\n </Canvas.Container>\n ),\n copyData: (row: TreeRow) => row.blockActivity ?? '',\n subRow: {\n keyOfColumnInSubRow: 'blockActivity',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'tribe',\n name: 'Трайб',\n editingCell: {\n component: 'select',\n options: {\n type: 'stateInRowContext',\n optionsKeyInRowContext: 'tribeOptions',\n },\n },\n renderCell: ({ row, theme }) => (\n <Canvas.Container padding={8}>\n <Canvas.Text color={theme.textDark}>{row.tribe}</Canvas.Text>\n </Canvas.Container>\n ),\n copyData: (row: TreeRow) => row.tribe ?? '',\n subRow: {\n keyOfColumnInSubRow: 'tribe',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'product',\n name: 'Продукт',\n editingCell: {\n component: 'select',\n options: {\n type: 'stateInRowContext',\n optionsKeyInRowContext: 'productOptions',\n },\n },\n subRow: {\n keyOfColumnInSubRow: 'product',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'q1',\n name: 'Q1',\n editingCell: { component: 'inputNumber' },\n contentFormat: {\n type: 'number',\n decimalSeparator: ',',\n thousandSeparator: ' ',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n },\n subRow: {\n keyOfColumnInSubRow: 'q1',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q2',\n name: 'Q2',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q2',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q3',\n name: 'Q3',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q3',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q4',\n name: 'Q4',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q4',\n editingCell: { component: 'inputNumber' },\n },\n },\n ],\n [],\n );\n\n const rowContextValue = useMemo(\n () => ({\n tribeOptions: TRIBES.map((i) => ({ text: i, value: i })),\n productOptions: PRODUCTS.map((i) => ({ text: i, value: i })),\n }),\n [],\n );\n\n const savedRowsRef = useRef<null | typeof rows>(null);\n\n return (\n <div>\n <div\n style={{ display: 'flex', gap: 12, maxWidth: 760, marginBottom: 12 }}\n >\n <Select\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(value) => setSelectionMode(value as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />\n <Select\n label=\"highlightActiveType (подсветка)\"\n value={highlightActiveType}\n onChange={(value) =>\n setHighlightActiveType(value as HighlightActiveType)\n }\n items={HIGHLIGHT_ACTIVE_TYPE_OPTIONS}\n />\n </div>\n\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>Copy:</b> работает всегда (Ctrl+C). <b>Paste/Fill:</b> только в\n режиме редактирования.\n <br />\n <b>Выделение строк:</b> клик/драг по колонке нумерации — строка(и);{' '}\n <b>Ctrl/Cmd+клик</b> по номерам — набор отдельных строк (группа).\n Скопированное (Ctrl+C) можно вставить (Ctrl+V) на выделенные строки.\n <br />\n <b>Выделение колонок:</b> клик по шапке (Ctrl/Shift — мультивыбор),\n copy/paste по колонкам.\n <br />\n <b>Колонки:</b> Блок (select, readonly часть), Активность\n (Canvas.Badge + copyData), Трайб (Canvas.Text + copyData + select),\n Продукт (select), Q1 (число + contentFormat), Q2-Q4 (число).\n <br />\n <b>Debug:</b> логи включены (DevTools → Verbose).\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '700px' },\n // Подсветка строки — отдельная визуальная ось (Фаза 2+).\n highlightActiveType,\n // Нумерация строк: клик/драг по ней выделяет строку(и); Ctrl/Meta —\n // накопительный выбор отдельных строк. Copy/Paste работают по строкам.\n rowMarkers: { startIndex: 1 },\n // Режим выделения ячеек: по нему работают copy/paste и рамка.\n // Выделение колонок по клику на шапку (по умолчанию включено).\n cellsSelection: {\n mode: selectionMode,\n enableColumnSelection: true,\n },\n rowSize: { showInControl: true, default: 'big' },\n editing: {\n onEnableEditing(enableEditorMode) {\n savedRowsRef.current = JSON.parse(JSON.stringify(rows));\n enableEditorMode();\n },\n onCancel(disableEditorMode) {\n if (savedRowsRef.current) setRows(savedRowsRef.current);\n disableEditorMode();\n },\n onSave(disableEditorMode) {\n savedRowsRef.current = null;\n disableEditorMode();\n },\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n rowEditable: (r) => r.block !== BLOCKS[1],\n },\n subRows: {\n getSubRows: (row) => row?.subRows,\n rowKeyGetter: (row) => row.id,\n },\n resizableColumn: true,\n }}\n columnConfig={columns}\n rows={rows}\n rowContextValue={rowContextValue}\n />\n </div>\n );\n }"
52704
+ "code": "() => {\n useEffect(() => {\n (\n window as unknown as Record<string, boolean>\n ).__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n }, []);\n\n const [rows, setRows] = useState(() => createRowsTree());\n const [highlightActiveType, setHighlightActiveType] =\n useState<HighlightActiveType>('row');\n const [selectionMode, setSelectionMode] =\n useState<CellsSelectionMode>('range-cell');\n\n const columns = useMemo(\n (): readonly ColumnConfig<TreeRow>[] => [\n {\n key: 'block',\n name: 'Блок',\n editingCell: {\n editable: (r) => r.block === BLOCKS[1],\n component: 'select',\n options: {\n type: 'constant',\n options: BLOCKS.map((i) => ({ text: i, value: i })),\n },\n },\n subRow: {\n keyOfColumnInSubRow: (lvl) => {\n switch (lvl) {\n case 0:\n return 'block';\n case 1:\n return 'tribe';\n case 2:\n return 'product';\n default:\n return 'block';\n }\n },\n editingCell: {\n component: 'inputString',\n inputProps: { placeholder: 'Введите значение' },\n },\n isColumnWithArrow: true,\n },\n },\n {\n key: 'blockActivity',\n name: 'Активность блока',\n editingCell: { component: 'inputString' },\n renderCell: ({ row }) => (\n <Canvas.Container direction=\"row\" alignItems=\"center\" padding={8}>\n <Canvas.Badge\n text={row.blockActivity}\n view={row.blockActivity === 'Активный' ? 'positive' : 'warning'}\n size=\"s\"\n />\n </Canvas.Container>\n ),\n copyData: (row: TreeRow) => row.blockActivity ?? '',\n subRow: {\n keyOfColumnInSubRow: 'blockActivity',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'tribe',\n name: 'Трайб',\n editingCell: {\n component: 'select',\n options: {\n type: 'stateInRowContext',\n optionsKeyInRowContext: 'tribeOptions',\n },\n },\n renderCell: ({ row, theme }) => (\n <Canvas.Container padding={8}>\n <Canvas.Text color={theme.textDark}>{row.tribe}</Canvas.Text>\n </Canvas.Container>\n ),\n copyData: (row: TreeRow) => row.tribe ?? '',\n subRow: {\n keyOfColumnInSubRow: 'tribe',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'product',\n name: 'Продукт',\n editingCell: {\n component: 'select',\n options: {\n type: 'stateInRowContext',\n optionsKeyInRowContext: 'productOptions',\n },\n },\n subRow: {\n keyOfColumnInSubRow: 'product',\n editingCell: { component: 'inputString' },\n },\n },\n {\n key: 'q1',\n name: 'Q1',\n editingCell: { component: 'inputNumber' },\n contentFormat: {\n type: 'number',\n decimalSeparator: ',',\n thousandSeparator: ' ',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n },\n subRow: {\n keyOfColumnInSubRow: 'q1',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q2',\n name: 'Q2',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q2',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q3',\n name: 'Q3',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q3',\n editingCell: { component: 'inputNumber' },\n },\n },\n {\n key: 'q4',\n name: 'Q4',\n contentFormat: 'number',\n editingCell: { component: 'inputNumber' },\n subRow: {\n keyOfColumnInSubRow: 'q4',\n editingCell: { component: 'inputNumber' },\n },\n },\n ],\n [],\n );\n\n const rowContextValue = useMemo(\n () => ({\n tribeOptions: TRIBES.map((i) => ({ text: i, value: i })),\n productOptions: PRODUCTS.map((i) => ({ text: i, value: i })),\n }),\n [],\n );\n\n const savedRowsRef = useRef<null | typeof rows>(null);\n\n return (\n <div>\n <div\n style={{ display: 'flex', gap: 12, maxWidth: 760, marginBottom: 12 }}\n >\n <Select\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(value) => setSelectionMode(value as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />\n <Select\n label=\"highlightActiveType (подсветка)\"\n value={highlightActiveType}\n onChange={(value) =>\n setHighlightActiveType(value as HighlightActiveType)\n }\n items={HIGHLIGHT_ACTIVE_TYPE_OPTIONS}\n />\n </div>\n\n <StoryHint>\n <b>Copy:</b> работает всегда (Ctrl+C). <b>Paste/Fill:</b> только в\n режиме редактирования.\n <br />\n <b>Выделение строк:</b> клик/драг по колонке нумерации — строка(и);{' '}\n <b>Ctrl/Cmd+клик</b> по номерам — набор отдельных строк (группа).\n Скопированное (Ctrl+C) можно вставить (Ctrl+V) на выделенные строки.\n <br />\n <b>Выделение колонок:</b> клик по шапке (Ctrl/Shift — мультивыбор),\n copy/paste по колонкам.\n <br />\n <b>Колонки:</b> Блок (select, readonly часть), Активность\n (Canvas.Badge + copyData), Трайб (Canvas.Text + copyData + select),\n Продукт (select), Q1 (число + contentFormat), Q2-Q4 (число).\n <br />\n <b>Debug:</b> логи включены (DevTools → Verbose).\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '700px' },\n // Подсветка строки — отдельная визуальная ось (Фаза 2+).\n highlightActiveType,\n // Нумерация строк: клик/драг по ней выделяет строку(и); Ctrl/Meta —\n // накопительный выбор отдельных строк. Copy/Paste работают по строкам.\n rowMarkers: { startIndex: 1 },\n // Режим выделения ячеек: по нему работают copy/paste и рамка.\n // Выделение колонок по клику на шапку (по умолчанию включено).\n cellsSelection: {\n mode: selectionMode,\n enableColumnSelection: true,\n },\n rowSize: { showInControl: true, default: 'big' },\n editing: {\n onEnableEditing(enableEditorMode) {\n savedRowsRef.current = JSON.parse(JSON.stringify(rows));\n enableEditorMode();\n },\n onCancel(disableEditorMode) {\n if (savedRowsRef.current) setRows(savedRowsRef.current);\n disableEditorMode();\n },\n onSave(disableEditorMode) {\n savedRowsRef.current = null;\n disableEditorMode();\n },\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n rowEditable: (r) => r.block !== BLOCKS[1],\n },\n subRows: {\n getSubRows: (row) => row?.subRows,\n rowKeyGetter: (row) => row.id,\n },\n resizableColumn: true,\n }}\n columnConfig={columns}\n rows={rows}\n rowContextValue={rowContextValue}\n />\n </div>\n );\n }"
52563
52705
  },
52564
52706
  {
52565
52707
  "exportName": "InterceptCopyPaste",
52566
52708
  "displayName": "Перехват onBeforeCopy / onBeforePaste",
52567
52709
  "type": "full-code",
52568
- "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 90,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'issueType',\n name: 'Issue Type',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>onBeforeCopy:</b> при Ctrl+C в консоль выводятся скопированные\n данные, колонки и rawValue каждой ячейки.\n <br />\n <b>onBeforePaste:</b> при Ctrl+V выводятся вставляемые данные и info о\n target-ячейках. Если вставляется больше 5 строк, вставка блокируется\n (return false).\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '500px' },\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n onBeforeCopy: (data, meta) => {\n console.log('[onBeforeCopy] data:', data);\n console.log(\n '[onBeforeCopy] колонки:',\n meta.cells[0]?.map((c) => c.column.key),\n );\n console.log(\n '[onBeforeCopy] rawValues:',\n meta.cells.map((row) => row.map((c) => c.rawValue)),\n );\n return data;\n },\n onBeforePaste: (data, meta) => {\n console.log('[onBeforePaste] data:', data);\n console.log('[onBeforePaste] target:', meta.target);\n console.log(\n '[onBeforePaste] targetCells:',\n meta.targetCells.map((row) =>\n row.map(\n (c) =>\n `${c.column.key} (${\n c.column.editingCell ? 'editable' : 'readonly'\n })`,\n ),\n ),\n );\n if (data.length > 5) {\n console.warn('[onBeforePaste] Блокировка: больше 5 строк');\n return false;\n }\n return data;\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n resizableColumn: true,\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52710
+ "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 90,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'issueType',\n name: 'Issue Type',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <StoryHint>\n <b>onBeforeCopy:</b> при Ctrl+C в консоль выводятся скопированные\n данные, колонки и rawValue каждой ячейки.\n <br />\n <b>onBeforePaste:</b> при Ctrl+V выводятся вставляемые данные и info о\n target-ячейках. Если вставляется больше 5 строк, вставка блокируется\n (return false).\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '500px' },\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n onBeforeCopy: (data, meta) => {\n console.log('[onBeforeCopy] data:', data);\n console.log(\n '[onBeforeCopy] колонки:',\n meta.cells[0]?.map((c) => c.column.key),\n );\n console.log(\n '[onBeforeCopy] rawValues:',\n meta.cells.map((row) => row.map((c) => c.rawValue)),\n );\n return data;\n },\n onBeforePaste: (data, meta) => {\n console.log('[onBeforePaste] data:', data);\n console.log('[onBeforePaste] target:', meta.target);\n console.log(\n '[onBeforePaste] targetCells:',\n meta.targetCells.map((row) =>\n row.map(\n (c) =>\n `${c.column.key} (${\n c.column.editingCell ? 'editable' : 'readonly'\n })`,\n ),\n ),\n );\n if (data.length > 5) {\n console.warn('[onBeforePaste] Блокировка: больше 5 строк');\n return false;\n }\n return data;\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n resizableColumn: true,\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52569
52711
  },
52570
52712
  {
52571
52713
  "exportName": "CopyDataExample",
52572
52714
  "displayName": "copyData для кастомных ячеек",
52573
52715
  "type": "full-code",
52574
- "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 240,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority (Badge + copyData)',\n width: 220,\n editingCell: { component: 'inputString' },\n renderCell: ({ row }) => (\n <Canvas.Container direction=\"row\" alignItems=\"center\" padding={8}>\n <Canvas.Badge\n text={row.priority}\n view={row.priority === 'Critical' ? 'negative' : 'default'}\n size=\"s\"\n />\n </Canvas.Container>\n ),\n copyData: (row: Row) => row.priority ?? '',\n },\n {\n key: 'issueType',\n name: 'Type (Text + copyData)',\n width: 200,\n editingCell: { component: 'inputString' },\n renderCell: ({ row, theme }) => (\n <Canvas.Container padding={8}>\n <Canvas.Text color={theme.textDark}>{row.issueType}</Canvas.Text>\n </Canvas.Container>\n ),\n copyData: (row: Row) => row.issueType ?? '',\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 140,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>Priority</b> — Canvas.Badge с <code>copyData</code>. <b>Type</b> —\n Canvas.Text с <code>copyData</code>.\n <br />\n Выделите ячейку Priority → Ctrl+C → вставьте в блокнот: скопируется\n текст бейджа (например &quot;Critical&quot;), а не пустая строка.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52716
+ "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 240,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority (Badge + copyData)',\n width: 220,\n editingCell: { component: 'inputString' },\n renderCell: ({ row }) => (\n <Canvas.Container direction=\"row\" alignItems=\"center\" padding={8}>\n <Canvas.Badge\n text={row.priority}\n view={row.priority === 'Critical' ? 'negative' : 'default'}\n size=\"s\"\n />\n </Canvas.Container>\n ),\n copyData: (row: Row) => row.priority ?? '',\n },\n {\n key: 'issueType',\n name: 'Type (Text + copyData)',\n width: 200,\n editingCell: { component: 'inputString' },\n renderCell: ({ row, theme }) => (\n <Canvas.Container padding={8}>\n <Canvas.Text color={theme.textDark}>{row.issueType}</Canvas.Text>\n </Canvas.Container>\n ),\n copyData: (row: Row) => row.issueType ?? '',\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 140,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <StoryHint>\n <b>Priority</b> — Canvas.Badge с <code>copyData</code>. <b>Type</b> —\n Canvas.Text с <code>copyData</code>.\n <br />\n Выделите ячейку Priority → Ctrl+C → вставьте в блокнот: скопируется\n текст бейджа (например &quot;Critical&quot;), а не пустая строка.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52575
52717
  },
52576
52718
  {
52577
52719
  "exportName": "TypeCheckValidation",
52578
52720
  "displayName": "Валидация типов при вставке",
52579
52721
  "type": "full-code",
52580
- "code": "() => {\n useEffect(() => {\n (\n window as unknown as Record<string, boolean>\n ).__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n }, []);\n\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n { key: 'id', name: 'ID', width: 80 },\n {\n key: 'task',\n name: 'Title (строка)',\n width: 240,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority (select)',\n width: 180,\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: [\n { value: 'Critical', text: 'Critical' },\n { value: 'High', text: 'High' },\n { value: 'Medium', text: 'Medium' },\n { value: 'Low', text: 'Low' },\n ],\n },\n },\n },\n {\n key: 'complete',\n name: '% Complete (число)',\n width: 180,\n editingCell: { component: 'inputNumber' },\n contentFormat: {\n type: 'number',\n decimalSeparator: ',',\n thousandSeparator: ' ',\n },\n },\n ],\n [],\n );\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>Priority</b> — select (Critical / High / Medium / Low). Вставка\n &quot;Unknown&quot; пропустится.\n <br />\n <b>% Complete</b> — число. Вставка &quot;hello&quot; пропустится.\n <br />\n <b>Title</b> — строка, принимает всё.\n <br />\n Debug логи включены — смотрите{' '}\n <code>[TableCanvas/paste] skipped by validation</code> в консоли.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52722
+ "code": "() => {\n useEffect(() => {\n (\n window as unknown as Record<string, boolean>\n ).__TABLE_CANVAS_CLIPBOARD_DEBUG__ = true;\n }, []);\n\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n { key: 'id', name: 'ID', width: 80 },\n {\n key: 'task',\n name: 'Title (строка)',\n width: 240,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority (select)',\n width: 180,\n editingCell: {\n component: 'select',\n options: {\n type: 'constant',\n options: [\n { value: 'Critical', text: 'Critical' },\n { value: 'High', text: 'High' },\n { value: 'Medium', text: 'Medium' },\n { value: 'Low', text: 'Low' },\n ],\n },\n },\n },\n {\n key: 'complete',\n name: '% Complete (число)',\n width: 180,\n editingCell: { component: 'inputNumber' },\n contentFormat: {\n type: 'number',\n decimalSeparator: ',',\n thousandSeparator: ' ',\n },\n },\n ],\n [],\n );\n\n return (\n <div>\n <StoryHint>\n <b>Priority</b> — select (Critical / High / Medium / Low). Вставка\n &quot;Unknown&quot; пропустится.\n <br />\n <b>% Complete</b> — число. Вставка &quot;hello&quot; пропустится.\n <br />\n <b>Title</b> — строка, принимает всё.\n <br />\n Debug логи включены — смотрите{' '}\n <code>[TableCanvas/paste] skipped by validation</code> в консоли.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52581
52723
  },
52582
52724
  {
52583
52725
  "exportName": "FillHandleWithSourceRow",
52584
52726
  "displayName": "Fill Handle — доступ к source row (onBeforeFill)",
52585
52727
  "type": "full-code",
52586
- "code": "() => {\n const [rows, setRows] = useState(createRows);\n const savedRowsRef = useRef<Row[] | null>(null);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'issueType',\n name: 'Issue Type',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Выделите ячейку в колонке <b>% Complete</b> и перетащите fill handle\n вниз.\n <br />\n <b>onRowsChange</b> получает <code>type: &apos;fill&apos;</code> и{' '}\n <code>fillMeta</code> с source row, что позволяет дополнительно\n скопировать <b>priority</b> из исходной строки.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '500px' },\n highlightActiveType: 'range-cell',\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n fillHandle: true,\n onBeforeFill: (data, meta) => {\n console.log(\n '[onBeforeFill] source row:',\n meta.sourceCells[0]?.[0]?.row,\n );\n console.log('[onBeforeFill] data:', data);\n return data;\n },\n },\n editing: {\n onEnableEditing(enableEditorMode) {\n savedRowsRef.current = JSON.parse(JSON.stringify(rows));\n enableEditorMode();\n },\n onCancel(disableEditorMode) {\n if (savedRowsRef.current) {\n setRows(savedRowsRef.current);\n savedRowsRef.current = null;\n }\n disableEditorMode();\n },\n onRowsChange: (newRows, { indexes, column, type, fillMeta }) => {\n if (type === 'fill' && fillMeta && column.key === 'complete') {\n const sourceRow = fillMeta.sourceCells[0]?.[0]?.row as\n | Row\n | undefined;\n\n if (sourceRow) {\n const updated = newRows.map((row, i) =>\n indexes.includes(i)\n ? { ...row, priority: sourceRow.priority }\n : row,\n );\n setRows(updated);\n return;\n }\n }\n\n setRows(newRows);\n },\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: false,\n },\n resizableColumn: true,\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52728
+ "code": "() => {\n const [rows, setRows] = useState(createRows);\n const savedRowsRef = useRef<Row[] | null>(null);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'issueType',\n name: 'Issue Type',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <StoryHint>\n Выделите ячейку в колонке <b>% Complete</b> и перетащите fill handle\n вниз.\n <br />\n <b>onRowsChange</b> получает <code>type: &apos;fill&apos;</code> и{' '}\n <code>fillMeta</code> с source row, что позволяет дополнительно\n скопировать <b>priority</b> из исходной строки.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '500px' },\n highlightActiveType: 'range-cell',\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n fillHandle: true,\n onBeforeFill: (data, meta) => {\n console.log(\n '[onBeforeFill] source row:',\n meta.sourceCells[0]?.[0]?.row,\n );\n console.log('[onBeforeFill] data:', data);\n return data;\n },\n },\n editing: {\n onEnableEditing(enableEditorMode) {\n savedRowsRef.current = JSON.parse(JSON.stringify(rows));\n enableEditorMode();\n },\n onCancel(disableEditorMode) {\n if (savedRowsRef.current) {\n setRows(savedRowsRef.current);\n savedRowsRef.current = null;\n }\n disableEditorMode();\n },\n onRowsChange: (newRows, { indexes, column, type, fillMeta }) => {\n if (type === 'fill' && fillMeta && column.key === 'complete') {\n const sourceRow = fillMeta.sourceCells[0]?.[0]?.row as\n | Row\n | undefined;\n\n if (sourceRow) {\n const updated = newRows.map((row, i) =>\n indexes.includes(i)\n ? { ...row, priority: sourceRow.priority }\n : row,\n );\n setRows(updated);\n return;\n }\n }\n\n setRows(newRows);\n },\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: false,\n },\n resizableColumn: true,\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52587
52729
  },
52588
52730
  {
52589
52731
  "exportName": "CustomHotkeys",
52590
52732
  "displayName": "Кастомные хоткеи",
52591
52733
  "type": "full-code",
52592
- "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Copy: <b>Ctrl+Alt+C</b>. Paste: <b>Ctrl+Alt+V</b>.\n <br />\n Обычные Ctrl+C / Ctrl+V для этой таблицы не работают — хук блокирует\n дефолт, когда задан кастомный хоткей.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n hotkeys: {\n copy: { code: 'KeyC', ctrl: true, alt: true },\n paste: { code: 'KeyV', ctrl: true, alt: true },\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52734
+ "code": "() => {\n const [rows, setRows] = useState(createRows);\n\n const columnConfig = useMemo<readonly ColumnConfig<Row>[]>(\n () => [\n {\n key: 'id',\n name: 'ID',\n width: 80,\n editingCell: { component: 'inputNumber' },\n },\n {\n key: 'task',\n name: 'Title',\n width: 260,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'priority',\n name: 'Priority',\n width: 180,\n editingCell: { component: 'inputString' },\n },\n {\n key: 'complete',\n name: '% Complete',\n width: 160,\n editingCell: { component: 'inputNumber' },\n },\n ],\n [],\n );\n\n return (\n <div>\n <StoryHint>\n Copy: <b>Ctrl+Alt+C</b>. Paste: <b>Ctrl+Alt+V</b>.\n <br />\n Обычные Ctrl+C / Ctrl+V для этой таблицы не работают — хук блокирует\n дефолт, когда задан кастомный хоткей.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '400px' },\n rowSize: { default: 'medium', showInControl: true },\n cellTransfer: {\n hotkeys: {\n copy: { code: 'KeyC', ctrl: true, alt: true },\n paste: { code: 'KeyV', ctrl: true, alt: true },\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
52593
52735
  }
52594
52736
  ]
52595
52737
  },
@@ -52637,7 +52779,7 @@
52637
52779
  "legacy": false,
52638
52780
  "summary": "*tableConfig.\\*.domMetadata**",
52639
52781
  "docs": "# DomMetadata\n\n**tableConfig.\\*.domMetadata**\n\nМетки для автоматизированного тестирования, аналитики и онбординга.\n\n`DomMetadata` позволяет навешивать CSS-классы, data-атрибуты и обработчики кликов на интерактивные элементы таблицы.\nЭто нужно для:\n\n- **Аналитики** — трекинг кликов по кнопкам таблицы\n- **Автотестов** — поиск элементов по `data-testid`\n- **Онбординга** — привязка подсказок к конкретным элементам\n\n## Поддерживаемые элементы (className, dataAttributes)\n\nСледующие элементы поддерживают `className` и `dataAttributes` через `domMetadata`:\n\n**Control Block (панель управления):**\n\n- **Редактирование** — `tableConfig.editing.domMetadata`\n- **Полноэкранный режим** — `tableConfig.fullScreenEnabled.domMetadata`\n- **Размер строки** — `tableConfig.rowSize.domMetadata`\n- **Переключение вида** (строки/карточки) — `tableConfig.view.domMetadata`\n- **Группировка строк** — `tableConfig.rowsGrouping.domMetadata`\n- **Поиск** — `tableConfig.searching.domMetadata`\n- **Итоговые строки** — `tableConfig.summaryRows.domMetadata`\n- **KeyText** (controlBlock) — `tableConfig.keyText.controlBlock.domMetadata`\n- **KeyText** (sidebar) — `tableConfig.keyText.sidebar.domMetadata`\n\n**Selecting (выбор строк):**\n\n- **Чекбокс \"Выбрать все\"** (controlBlock) — `tableConfig.selecting.controlBlock.domMetadata`\n- **Свитч в сайдбаре** — `tableConfig.selecting.sidebar.domMetadata`\n\n**Sidebar (правая панель):**\n\n- **Табы сайдбара** — `tableConfig.sidebarConfig.defaultTabs[].domMetadata`\n- **Подтабы настроек** (Фильтры, Столбцы) — `tableConfig.sidebarConfig.defaultTabs[{id:'filtering'|'columns'}].domMetadata`\n\n**Другие:**\n\n- **Кнопка сворачивания таблицы** — `tableConfig.collapse.domMetadata`\n\n## Элементы с расширенным onClick (className, dataAttributes, onClick + detail)\n\nВ дополнение к `className` и `dataAttributes`, следующие элементы поддерживают `onClick` с контекстом `detail`:\n\n- **Группировать** (чекбоксы, сброс) — `tableConfig.rowsGrouping.domMetadata` — actions: `toggle-group`, `reset-groups`, `custom-item-select`\n- **Кнопка размера строки** — `tableConfig.rowSize.domMetadata` — action: `change-row-size`\n- **Шестеренка настроек** — `tableConfig.sidebarConfig.defaultTabs[{id:'tableSettings'}].domMetadata` — action: `toggle-sidebar-tab`\n- **Крестик закрыть сайдбар** — тот же `domMetadata` таба — action: `close-sidebar`\n- **Закрепить столбец** — `tableConfig.columnsControl.pinDomMetadata` — actions: `pin-column`, `unpin-column`\n- **Свитчер видимости столбца** — `tableConfig.columnsControl.switchDomMetadata` — actions: `show-column`, `hide-column`\n\n## onClick\n\nОбработчик `onClick` вызывается **после** основной логики элемента. Он не мешает работе кнопки и не вызывает `stopPropagation`.\n\n```tsx\nonClick?: (\n e?: React.MouseEvent<HTMLElement>, // MouseEvent (undefined для элементов внутри Dropdown)\n detail?: Record<string, unknown> // контекст: action, columnKey, size и др.\n) => void;\n```\n\n### Примеры detail\n\n```ts\n// Группировка\n{ action: 'toggle-group', columnKey: 'status' }\n{ action: 'reset-groups' }\n\n// Размер строки\n{ action: 'change-row-size', size: 'medium' }\n\n// Сайдбар\n{ action: 'toggle-sidebar-tab', tabId: 'tableSettings' }\n{ action: 'close-sidebar' }\n\n// Столбцы\n{ action: 'pin-column', columnKey: 'name' }\n{ action: 'unpin-column', columnKey: 'name' }\n{ action: 'hide-column', columnKey: 'name' }\n{ action: 'show-column', columnKey: 'name' }\n```\n\nПодробнее о типах — в разделе [API](?path=/docs/локальные-компоненты-tablecanvas-dommetadata-api--docs).",
52640
- "apiDocs": "# DomMetadata API\n\n## DomMetadata\n\n```typescript\ntype DomMetadata = {\n /**\n * CSS класс для стилизации и поиска элемента\n *\n * @example 'user-profile-card'\n * @example 'onboarding-step-1'\n */\n className?: string;\n /**\n * Data-атрибуты для тестирования, аналитики и онбординга\n *\n * @example { 'data-testid': 'submit-button', 'data-analytics': 'click-event' }\n */\n dataAttributes?: DataAttributes;\n /**\n * Обработчик клика для аналитики и трекинга\n *\n * Вызывается вместе с основным обработчиком элемента (не заменяет его).\n * Не вызывает stopPropagation — оригинальная логика кнопки сохраняется.\n *\n * @param e - MouseEvent (может быть undefined для элементов внутри Dropdown)\n * @param detail - контекст действия: action, columnKey, size и др.\n *\n * @example onClick: (e, detail) => analytics.track(detail?.action, detail)\n */\n onClick?: (\n e?: React.MouseEvent<HTMLElement>,\n detail?: Record<string, unknown>,\n ) => void;\n};\n```\n\n## DOM_METADATA_ACTIONS\n\n```typescript\nexport const DOM_METADATA_ACTIONS = {\n /** Включение/выключение группировки по столбцу. detail.columnKey — ключ столбца */\n TOGGLE_GROUP: 'toggle-group',\n /** Сброс всех группировок */\n RESET_GROUPS: 'reset-groups',\n /** Выбор пункта в кастомном onItemSelect группировки. detail.columnKey — ключ */\n CUSTOM_ITEM_SELECT: 'custom-item-select',\n /** Переключение размера строки. detail.size — новый размер ('small' | 'medium' | 'big') */\n CHANGE_ROW_SIZE: 'change-row-size',\n /** Клик по табу сайдбара (шестерёнка и др.). detail.tabId — id таба */\n TOGGLE_SIDEBAR_TAB: 'toggle-sidebar-tab',\n /** Закрытие сайдбара (крестик) */\n CLOSE_SIDEBAR: 'close-sidebar',\n /** Закрепление столбца. detail.columnKey — ключ столбца */\n PIN_COLUMN: 'pin-column',\n /** Открепление столбца. detail.columnKey — ключ столбца */\n UNPIN_COLUMN: 'unpin-column',\n /** Скрытие столбца. detail.columnKey — ключ столбца */\n HIDE_COLUMN: 'hide-column',\n /** Показ столбца. detail.columnKey — ключ столбца */\n SHOW_COLUMN: 'show-column',\n} as const;\n```\n\n## DomMetadataAction\n\n```typescript\ntype DomMetadataAction =\n (typeof DOM_METADATA_ACTIONS)[keyof typeof DOM_METADATA_ACTIONS];\n```\n\n## ColumnsControlConfig\n\n```typescript\ntype ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};\n```",
52782
+ "apiDocs": "# DomMetadata API\n\n## DomMetadata\n\n```typescript\ntype DomMetadata = {\n /**\n * CSS класс для стилизации и поиска элемента\n *\n * @example 'user-profile-card'\n * @example 'onboarding-step-1'\n */\n className?: string;\n /**\n * Data-атрибуты для тестирования, аналитики и онбординга\n *\n * @example { 'data-testid': 'submit-button', 'data-analytics': 'click-event' }\n */\n dataAttributes?: DataAttributes;\n /**\n * Обработчик клика для аналитики и трекинга\n *\n * Вызывается вместе с основным обработчиком элемента (не заменяет его).\n * Не вызывает stopPropagation — оригинальная логика кнопки сохраняется.\n *\n * @param e - MouseEvent (может быть undefined для элементов внутри Dropdown)\n * @param detail - контекст действия: action, columnKey, size и др.\n *\n * @example onClick: (e, detail) => analytics.track(detail?.action, detail)\n */\n onClick?: (\n e?: React.MouseEvent<HTMLElement>,\n detail?: Record<string, unknown>,\n ) => void;\n};\n```\n\n## DOM_METADATA_ACTIONS\n\n```typescript\nexport const DOM_METADATA_ACTIONS = {\n /** Включение/выключение группировки по столбцу. detail.columnKey — ключ столбца */\n TOGGLE_GROUP: 'toggle-group',\n /** Сброс всех группировок */\n RESET_GROUPS: 'reset-groups',\n /** Выбор пункта в кастомном onItemSelect группировки. detail.columnKey — ключ */\n CUSTOM_ITEM_SELECT: 'custom-item-select',\n /** Переключение размера строки. detail.size — новый размер ('small' | 'medium' | 'big') */\n CHANGE_ROW_SIZE: 'change-row-size',\n /** Клик по табу сайдбара (шестерёнка и др.). detail.tabId — id таба */\n TOGGLE_SIDEBAR_TAB: 'toggle-sidebar-tab',\n /** Закрытие сайдбара (крестик) */\n CLOSE_SIDEBAR: 'close-sidebar',\n /** Закрепление столбца. detail.columnKey — ключ столбца */\n PIN_COLUMN: 'pin-column',\n /** Открепление столбца. detail.columnKey — ключ столбца */\n UNPIN_COLUMN: 'unpin-column',\n /** Скрытие столбца. detail.columnKey — ключ столбца */\n HIDE_COLUMN: 'hide-column',\n /** Показ столбца. detail.columnKey — ключ столбца */\n SHOW_COLUMN: 'show-column',\n} as const;\n```\n\n## DomMetadataAction\n\n```typescript\ntype DomMetadataAction =\n (typeof DOM_METADATA_ACTIONS)[keyof typeof DOM_METADATA_ACTIONS];\n```\n\n## ColumnsControlConfig\n\n```typescript\ntype ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n /**\n * Виртуализация списка колонок в правом сайдбаре: в DOM держатся только\n * видимые строки списка. false — отключить полностью; { minCount } —\n * включать, когда строк в списке не меньше порога.\n * @default { minCount: 30 }\n */\n virtualization?: boolean | { minCount?: number };\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};\n```",
52641
52783
  "api": [
52642
52784
  {
52643
52785
  "typeName": "DomMetadata",
@@ -52653,7 +52795,7 @@
52653
52795
  },
52654
52796
  {
52655
52797
  "typeName": "ColumnsControlConfig",
52656
- "source": "type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};"
52798
+ "source": "type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n /**\n * Виртуализация списка колонок в правом сайдбаре: в DOM держатся только\n * видимые строки списка. false — отключить полностью; { minCount } —\n * включать, когда строк в списке не меньше порога.\n * @default { minCount: 30 }\n */\n virtualization?: boolean | { minCount?: number };\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};"
52657
52799
  }
52658
52800
  ],
52659
52801
  "stories": [
@@ -52833,11 +52975,11 @@
52833
52975
  "legacy": false,
52834
52976
  "summary": "*tableConfig.filtering**",
52835
52977
  "docs": "# Filtering\n\n**tableConfig.filtering**\n\nФильтрация данных таблицы с поддержкой автоматического и ручного режимов.\n\n## Ключевые особенности\n\n- Три типа фильтров: `input`, `select`, `custom`\n- Автоматическая фильтрация по умолчанию, ручная через `manualFiltering: true`\n- Отображение активных фильтров в виде chip-элементов\n- Поддержка фильтрации через sidebar с настройкой порядка и глобальных фильтров\n- Кастомизация chip-стилей и лейблов\n\n## Типы фильтров\n\n### Input\n\nТекстовый фильтр с режимами: `'includes'`, `'startWith'`, `'equal'` или кастомная функция.\n\n### Select\n\nФильтр выбором из списка. Поддержка одиночного (`'single'`) и множественного (`'multiple'`) выбора.\n\n### Custom\n\nПолностью кастомный компонент фильтра через `customRender`.\n\n## Конфигурация FilteringConfig\n\n- **`state`** `[T, setState]` — стейт фильтров (обязательный)\n- **`manualFiltering`** `boolean` — ручная фильтрация (данные не фильтруются таблицей)\n- **`filtersInfo`** `Record<keyof T, { label, clearedValue }>` — метки и дефолтные значения фильтров\n- **`sidebarConfig`** `{ order?, items }` — конфигурация sidebar с фильтрами\n- **`chipStyle`** `(itemOrGroup, item) => CSSObject` — кастомные стили для chip-элементов\n- **`renderGroupLabel`** `(group) => string` — кастомный заголовок группы чипсов\n- **`renderChipLabel`** `(group, item) => string` — кастомный текст чипса\n\n## Кастомизация чипсов\n\n### chipStyle\n\nПозволяет динамически задавать стили для каждого чипа фильтра в зависимости от его типа и содержимого.\n\n### renderGroupLabel\n\nФорматтер заголовка группы чипсов. Полезен для i18n или добавления счётчика выбранных элементов.\n\n### renderChipLabel\n\nФорматтер текста внутри чипса. Полезен при серверной пагинации, когда в стейте хранится только `value`, а `label` подставляется из кэша.\n\n## Особенности\n\n- При `manualFiltering: true` таблица не фильтрует данные — ожидается внешняя логика (серверная)\n- `sidebarConfig` позволяет добавлять глобальные фильтры, не привязанные к колонкам\n- `filtersInfo` задаёт лейблы и значения по умолчанию для каждого фильтра\n\n> Подробнее о типах — [Filtering API](?path=/docs/локальные-компоненты-tablecanvas-filtering-api--docs)",
52836
- "apiDocs": "# Filtering API\n\n## FilteringConfig\n\n```typescript\ntype FilteringConfig<T> = {\n state: [T, React.Dispatch<React.SetStateAction<T>>];\n /**\n * @deprecated Использовать filtersInfo\n *\n * clearedValue - состояние фильтров после очистки. Используется для сравнения:\n * Например, был ли фильтр конкретной колонки изменен рассчитывается посредством сравнения текущего значения фильтра и значения фильтра в clearedValue.\n * Если они равны, то фильтр считается не измененным.\n */\n clearedValue?: T;\n /**\n * Информация о фильтрах для каждой колонки.\n * Используется для:\n * 1. Хранения меток (label) фильтров для отображения в UI\n * 2. Хранения значений по умолчанию (clearedValue) для каждого фильтра\n * 3. Определения, был ли фильтр изменен (сравнение текущего значения с clearedValue)\n */\n filtersInfo?: Record<\n keyof T,\n {\n label: string;\n clearedValue: T[keyof T];\n }\n >;\n /**\n * manualFiltering - пропс, активирующий ручную-кастомную (или на стороне бэка) фильтрацию.\n * @remarks\n * Для автоматической фильтрации (когда не используется manualFiltering в tableConfig):\n * - Для всех типов фильтров обязательно должен быть указан `valueInRow`\n * - Для component: 'select' дополнительно должен быть указан `filteringType`\n * - Для component: 'input' с кастомным фильтром (когда filter - функция)\n * функция фильтрации должна быть передана явно\n *\n * Если используется manualFiltering, эти параметры не обязательны,\n * так как вся логика фильтрации реализуется извне.\n */\n manualFiltering?: boolean;\n /**\n * Конфигурация для отображения фильтров в правом сайдбаре.\n *\n * Позволяет:\n * 1. Добавлять глобальные фильтры (не привязанные к конкретной колонке)\n * 2. Управлять порядком отображения фильтров\n * 3. Переопределять рендер и заголовки колоночных фильтров\n *\n * Логика работы:\n * - По умолчанию в сайдбаре отображаются все колоночные фильтры (из columnConfig)\n * - Ключом фильтра считается `key` колонки, если его нет, то пытаемся взять `keyInFilterState` (если указан), иначе это не будет работать\n * - Заголовком фильтра по умолчанию является `name` колонки или ключ фильтра (name || key)\n * - Глобальные фильтры добавляются через `items` с уникальными ключами\n *\n * Особенности:\n * 1. Порядок отображения:\n * - Сначала фильтры из `order` (в указанной последовательности)\n * - Затем остальные колоночные фильтры (в порядке columnConfig)\n * - В конце - глобальные фильтры (в алфавитном порядке ключей)\n *\n * 2. Переопределение колоночных фильтров:\n * - Чтобы изменить заголовок: укажите ключ колонки в `items` с новым `label`\n * - Чтобы заменить рендер: укажите ключ колонки с `customRenderFn`\n * - Оригинальный фильтр из колонки будет проигнорирован\n *\n * 3. Глобальные фильтры:\n * - Ключ не должен совпадать с ключами колонок\n * - Обязательно указать `label` и `customRenderFn`\n * - Отображаются после всех колоночных фильтров, если не переопределены в order\n *\n * @example\n * // Добавление глобального фильтра и изменение порядка\n * sidebarConfig: {\n * order: ['status', 'tasks', 'type'], // Сначала статус, затем глобальный поиск\n * items: {\n * tasks: { // Уникальный ключ\n * label: 'Поиск по всем полям',\n * customRenderFn: () => <TaskComponent />\n * },\n * status: { // Переопределение колоночного фильтра\n * label: 'Статус заявки', // Новый заголовок\n * customRenderFn: () => <CustomStatusFilter /> // Кастомный рендер\n * }\n * type: { // Переопределение только Label колоночного фильтра\n * label: 'Тип', // Новый заголовок\n * }\n * }\n * }\n */\n sidebarConfig?: {\n /**\n * Определяет порядок отображения фильтров.\n * - Указываются ключи фильтров (колоночных или глобальных)\n * - Не указанные в order фильтры отображаются после\n * - Если order пуст, сохраняется порядок: глобальные фильтры + columnConfig фильтры в конце\n */\n order?: string[];\n /**\n * Конфигурация фильтров для сайдбара.\n * Ключом может быть:\n * - Ключ колонки (для переопределения колоночного фильтра)\n * - Уникальный ключ (для добавления глобального фильтра)\n */\n items: Partial<\n Record<\n keyof T,\n {\n /** Заголовок фильтра (обязателен для глобальных фильтров), можно использовать для переопределения для локальных фильтров */\n label: string;\n /**\n * Функция рендера компонента фильтра.\n * Для колоночных фильтров переопределяет оригинальный рендер.\n * Для глобальных фильтров обязательна.\n */\n customRenderFn?: (\n filters: T,\n setFilters: React.Dispatch<React.SetStateAction<T>>,\n ) => React.ReactNode;\n }\n >\n >;\n };\n /**\n * Callback для кастомизации стилей чипсов (фильтров) в списке\n *\n * @param {ItemOrGroup} itemOrGroup - Текущий элемент или группа фильтров\n * @param {Item | undefined} item - Конкретный элемент фильтра (если есть)\n * @returns {CSSObject} Объект стилей, который будет применен к чипсу\n *\n * @description\n * Позволяет динамически задавать стили для каждого чипса в зависимости от его типа и содержимого.\n * Вызывается для каждого отображаемого чипса (как для отдельных элементов, так и для элементов внутри групп).\n *\n * @example\n * // Пример 1: Стилизация чипсов по типу\n * chipStyle: (itemOrGroup, item) => {\n * if (isGroup(itemOrGroup)) {\n * return { backgroundColor: '#f0f0f0' };\n * }\n * return { backgroundColor: '#e0e0e0' };\n * }\n *\n * @example\n * // Пример 2: Стилизация по значению фильтра\n * chipStyle: (itemOrGroup, item) => {\n * if (item?.label === 'Высокий приоритет') {\n * return {\n * backgroundColor: '#ffebee',\n * color: '#d32f2f'\n * };\n * }\n * return {};\n * }\n *\n * @example\n * // Пример 3: Разные стили для элементов в группе и одиночных\n * chipStyle: (itemOrGroup, item) => {\n * if (item) {\n * // Стиль для элемента внутри группы\n * return { maxWidth: 180 };\n * }\n * // Стиль для одиночного чипа\n * return { maxWidth: 150 };\n * }\n */\n chipStyle?: (itemOrGroup: ItemOrGroup, item: Item | undefined) => CSSObject;\n /**\n * Пользовательский форматтер заголовка группы чипсов.\n *\n * Используется в блоке активных фильтров для подмены текста заголовка\n * группы (например, “Статус”, “Тип”, “Автор”). Полезно, когда нужно\n * динамически менять подпись (добавить счётчик, i18n и т.п.).\n *\n * @param {ItemOrGroup} itemOrGroup Объект группы (тип Group). Вызов происходит только для групп,\n * поэтому безопасно кастовать к Group.\n * @returns {string} Текст заголовка группы чипсов.\n *\n * @default Возвращается `group.groupLabel`.\n *\n * @remarks\n * - Функция должна быть чистой и быстрой (без сетевых запросов и побочных эффектов).\n * - Возвращайте ТОЛЬКО строку — чипы отображают текстовое значение.\n * - Для длинных строк применяется усечение (overflow с многоточием) на уровне компонента.\n *\n * @example\n * // Добавить двоеточие и количество выбранных элементов\n * renderGroupLabel: (group) => `${group.groupLabel}: ${group.items.length}`\n *\n * @example\n * // Локализация заголовка группы\n * const i18n = { status: 'Статус', type: 'Тип' };\n * renderGroupLabel: (group) => i18n[group.groupId] ?? group.groupLabel\n */\n renderGroupLabel?: (group: Group) => string;\n\n /**\n * Пользовательский форматтер текста чипса (элемента внутри группы или одиночного фильтра).\n *\n * Позволяет управлять тем, что именно отображается внутри каждого чипса. Особенно полезен\n * при серверной пагинации/ленивой загрузке, когда в состоянии фильтра хранится только value,\n * а label необходимо подставить из локального кэша.\n *\n * @param {Group | null } group Группа, внутри которой рендерится чип (или null).\n * @param {Item} item Сам элемент чипса. В группах передаётся конкретный `Item`,\n * для одиночного чипа может быть `undefined`.\n * @returns {string} Текст, который будет показан в чипсе.\n *\n * @default Используется `item?.label.toString()` (или строковое представление value).\n *\n * @remarks\n * - Функция должна быть чистой и быстрой;\n * - Возвращайте ТОЛЬКО строку.\n *\n * @example\n * // 1) Отображение label из кэша при серверной пагинации\n * const labelsCacheRef = useRef(new Map<string, string>());\n * renderChipLabel: (group, item) => {\n * if (item?.id != null) {\n * const key = String(item.id);\n * const fromCache = labelsCacheRef.current.get(key);\n * if (fromCache) return fromCache; // красивый label, если уже загрузили\n * return String(item.label ?? key); // фоллбэк: временно показываем value/label из item\n * }\n * return ''; // безопасный фоллбэк для одиночных случаев\n * }\n *\n * @example\n * // 2) Нормализация отображения числовых значений\n * renderChipLabel: (_group, item) => {\n * const v = Number(item?.label);\n * return Number.isFinite(v) ? v.toLocaleString('ru-RU') : String(item?.label ?? '');\n * }\n *\n * @example\n * // 3) Префикс по типу группы\n * renderChipLabel: (group, item) => {\n * const prefix = group.groupId === 'status' ? '' : '#';\n * return `${prefix}${item?.label ?? item?.id ?? ''}`;\n * }\n */\n renderChipLabel?: (group: Group | null, item: Item) => string;\n};\n\nexport type DefaultSidebarTabIds = 'columns' | 'tableSettings' | 'filtering';\n\n/**\n * Конфигурация sidebar\n */\nexport type SidebarConfig = {\n /**\n * Включен ли sidebar. Отвечает только за UI. Не влияет на перерасчёт фичей.\n * @default true\n */\n enabled?: boolean;\n /**\n * Открыт ли sidebar при первом рендере (uncontrolled-режим).\n * Игнорируется, если передан openState.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Внешний контроль открытия/закрытия sidebar.\n * Если передан — источник истины, defaultOpen игнорируется.\n */\n openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * id вкладки, выбранной по умолчанию (актуально при открытом сайдбаре).\n * Если id не найден или у вкладки showInSidebar=false — используется первая доступная.\n */\n defaultActiveTabId?: string;\n /**\n * Внешний контроль активной вкладки (controlled-режим).\n * Если передан — источник истины, defaultActiveTabId игнорируется.\n */\n activeTabState?: [\n string | null,\n React.Dispatch<React.SetStateAction<string | null>>,\n ];\n /**\n * Колбэк смены активной вкладки. Вызывается и в uncontrolled-режиме.\n * При закрытии сайдбара приходит null.\n */\n onActiveTabChange?: (tabId: string | null, tab?: SidebarTab) => void;\n /**\n * Кастомные табы\n */\n customTabs?: Array<\n Omit<SidebarTab, 'id'> & {\n id: Exclude<string, DefaultSidebarTabIds>;\n }\n >;\n /**\n * Порядок отображения кастомных вкладок\n */\n customTabsOrder?: Array<Exclude<string, DefaultSidebarTabIds>>;\n defaultTabs?: Array<\n | {\n id: 'filtering' | 'columns';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара (если данный таб окажется единственным табом в настройках) */\n title?: string;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки (для табов с id = filtering и columns - недоступен. Настраивается через таб с id='tableSettings') */\n iconTooltipText?: undefined;\n }\n | {\n id: 'tableSettings';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара */\n title?: string;\n\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный слот для вкладки tableSettings (рендерится после общих настроек)\n * Только дл id: 'tableSettings'\n */\n customGeneralSettingsSlot?: React.ReactNode;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки @default 'Настройки' */\n iconTooltipText?: string;\n }\n >;\n};\n\nexport function activeViewIs<T extends 'cards' | 'rows'>(\n checkType: T,\n viewState: 'cards' | 'rows',\n view: View,\n): view is ViewMods[T] {\n return checkType === viewState;\n}\n\n/** Размер controlBlock: 'm' и 's' — одинаковые стили, 'xs' — уменьшенный */\nexport type ControlBlockSize = 'm' | 's' | 'xs';\n\nexport type ControlBlockConfig = {\n /**\n * show - наличие controlBlock-а\n * @default true\n */\n show?: boolean;\n /**\n * Размер controlBlock и его элементов (кнопки, иконки, поиск, отступы).\n * 'm' и 's' — стандартные стили, 'xs' — компактный режим.\n * Не влияет на customFeatures и кастомные слоты — они контролируются разработчиком.\n * @default 'm'\n */\n size?: ControlBlockSize;\n /**\n * @deprecated Используйте кастомные кнопки через rightSideInner или customFeatures.\n * leftSideInner - пропс для добавления кнопок в левую часть контролблока.\n * Продолжает работать для обратной совместимости.\n */\n leftSideInner?: ControlBlockButtonProps[];\n /**\n * rightSideInner - пропс для добавления кнопок в правую часть контролблока\n */\n rightSideInner?: ControlBlockButtonProps[];\n /**\n * customFeatures - пропс для добавления кастомных фичей (кнопки-иконки)\n */\n customFeatures?: FeatureItem[];\n /**\n * pinningMenu - нативная фича закрепления столбцов (SplitIconButton с\n * дропдауном) в правой зоне иконок. Появляется, когда активировано\n * закрепление колонок (columnsControl.pinning). Продукт может донастроить\n * и дополнить пункты меню (см. PinningMenuConfig).\n */\n pinningMenu?: PinningMenuConfig;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты левые фичи при адаптивной компрессии\n */\n leftSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты правые фичи при адаптивной компрессии\n */\n rightSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Активна ли фича компрессии (сжатие ToolsMenu при адаптиве)\n * @default true\n */\n enableAdaptiveCompress?: boolean;\n /**\n * Панель массовых действий\n */\n massActionPanel?: {\n /**\n * Кнопки панели. Размеры кнопок/дропдаунов панель подбирает сама (по `size` панели и компактности) —\n * передавать `size` кнопкам не нужно. Если `size` на кнопке всё же задан — он используется как есть.\n */\n buttons: MassActionButtonProps[];\n /**\n * Пропсы для Dropdown, в который сжались кнопки\n */\n collapsedDropdownProps?: TableDropdownConfigProps;\n /**\n * Отступ панели снизу от контейнера (px)\n * @default 24\n */\n bottom?: number;\n /**\n * Управление отображением панели массовых действий.\n * - `undefined` (по умолчанию) — стандартное поведение (показывается при selecting + выбранных строках)\n * - `false` — панель не рендерится, разработчик управляет действиями самостоятельно\n * - `true` — панель показывается всегда, даже без активного selecting и выбранных строк\n */\n show?: boolean;\n /**\n * Размерная сетка панели внутри таблицы: `m`/`s` — обычный размер, `xs` — уменьшенный (кнопки `xxs`, «ещё» `xs`, счётчик Body XS).\n * Viewport-логика (1280) в таблице не используется.\n * @default 's'\n */\n size?: MassActionsSize;\n };\n};\n\n/** Пропсы кнопки редактирования. onClick исключён — используйте onSave/onCancel/onEnableEditing. */\nexport type EditingButtonProps = Omit<\n Partial<ControlBlockButtonProps>,\n | 'onClick'\n | 'size'\n | 'dropdown'\n | 'isTargetAction'\n | 'skipRightDivider'\n | 'tooltipText'\n>;\n\nexport type EditingConfig<\n RowType extends ObjectForExtending,\n RowIdType,\n SummaryRowType,\n> = {\n /**\n * Функция обработки изменений в редактируемой строке\n */\n onRowsChange: Maybe<\n (rows: RowType[], data: RowsChangeData<RowType, SummaryRowType>) => void\n >;\n /**\n * свойство, необходимое для корректного изменения дочерних строк.\n * Данное свойство обязательно, если ваши дочерние строки установлены не под ключом \"subRows\" внутри строки.\n *\n * Как дочерние строки можно указывать свойство, которые вложено в другое свойство.\n * В таком случае \".\" будет знаком для обозначения сл. уровня вложенности.\n *\n * @example \"child\" или \"uvh367.children\"\n */\n subRowsKey?: string;\n /**\n * Глубокое копирование строк перед применением правки в режиме редактирования.\n *\n * Включите, если строки, передаваемые в таблицу, иммутабельны/заморожены\n * (напр. стейт под управлением Redux Toolkit или Immer — они вызывают\n * Object.freeze в dev). При редактировании дочерней строки таблица записывает\n * изменение ВНУТРЬ объекта родителя; на замороженном объекте это падает с\n * `TypeError: Cannot assign to read only property`.\n *\n * Когда флаг включён, таблица сама делает structuredClone редактируемой строки\n * перед записью — ваш иммутабельный стейт не мутируется, и редактирование\n * дочерних строк работает без действий с вашей стороны.\n *\n * ⚠️ Данные строки должны быть сериализуемыми. Где доступен structuredClone —\n * Date/Map/Set и вложенные структуры копируются корректно; на старых средах\n * без structuredClone используется JSON-фоллбэк (теряет Date/Map/Set, undefined\n * и функции). Не включайте без необходимости — глубокое копирование добавляет\n * накладные расходы.\n *\n * @default false\n */\n deepCloneRows?: boolean;\n /**\n * Функция определения уникального id строки\n * @param row\n * @returns\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Функция определения возможности редактирования строки\n * @param row\n * @returns\n */\n rowEditable?: (row: RowType) => boolean;\n /**\n * defaultEnabled - пропс, отвечающий за то, будет ли активен режим редактирования или нет. По умолчанию - не активно.\n * @default false\n */\n defaultEnabled?: boolean;\n /**\n * enabled - необходим для управления режимом редактирования снаружи компонента Таблицы.\n * По умолчанию, управление происходит в компоненте.\n * Приоритетнее, чем defaultEnabled.\n *\n * @type boolean - если НЕ нужно переключение в самой таблице\n * @type [boolean, React.Dispatch<React.SetStateAction<boolean>>] - если нужно переключение в самой таблице\n *\n */\n enabled?: boolean | [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * Cостояние скрытия/раскрытия кнопки включения/выключения фичи\n * @default true\n */\n showButtons?: boolean;\n /**\n * Функция, вызывающаяся при включении фичи.\n * @param enableEditorMode - функция включающая режим редактирования\n */\n onEnableEditing?: (enableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при сохранении отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onSave?: (disableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при отмене отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onCancel?: (disableEditorMode: () => void) => void;\n /** Пропсы кнопок в controlBlock. */\n buttons?: {\n save?: EditingButtonProps;\n cancel?: EditingButtonProps;\n edit?: EditingButtonProps;\n };\n /**\n * Кастомный слот, отображаемый левее кнопок «Отменить» / «Сохранить».\n * Виден только когда режим редактирования активен (editMode = true).\n * Зона ответственности разработчика — размерная сетка controlBlock не влияет на этот слот.\n */\n editModeLeftSlot?: React.ReactNode;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: {\n enableButton?: DomMetadata;\n saveButton?: DomMetadata;\n cancelButton?: DomMetadata;\n };\n};\n\nexport type SummaryRowsConfig = {\n showDefault: boolean;\n showInControl: boolean;\n} & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\n/**\n * Конфиг тултипа при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\nexport type TableConfigTooltip = {\n /**\n * Включить/выключить колоночные и кастомные тултипы (cellTooltip, headerCellTooltip).\n * Внутренние тултипы (drag-иконка в шапке и другие по nodeId) работают всегда.\n * @default false\n */\n enabled?: boolean;\n /** Глобальная задержка перед показом тултипа (мс). Per-node переопределяет. @default 500 */\n mouseEnterDelay?: number;\n /** Глобальная задержка перед скрытием тултипа (мс). Per-node переопределяет. @default 0 */\n mouseLeaveDelay?: number;\n};\n\nexport type InfinityScrollConfig<RowType extends ObjectForExtending> = {\n /** Триггер достижения скроллом зоны подгрузки данных. */\n onTrigger: (rows: RowType[]) => Promise<void> | void;\n /** Состояние загрузки (скелетоны) новых строк. */\n isLoading: boolean;\n /**\n * Флаг, когда все данные загружены — false.\n * @default true\n */\n hasMore?: boolean;\n /**\n * Для mode='region': за сколько строк до конца триггерить загрузку.\n * @default 5\n */\n rowThreshold?: number;\n};\n\nexport type { TableCanvasEmptyStateConfig, TableCanvasErrorStateConfig };\n\n/**\n * Настройка выделения ячеек/колонок/строк в TableCanvas — единый объект.\n * Это отдельная ось взаимодействия — не зависит от `highlightActiveType`.\n */\nexport type CellsSelectionConfig = {\n /**\n * Режим фактического выделения ячеек (нативный glide selection). По нему\n * работают copy/paste, рамка, fill-handle и затемнение шапки/нумерации.\n * @default \"range-cell\"\n */\n mode?: CellsSelectionMode;\n /**\n * Выделение колонок по клику на шапку. Ctrl/Cmd — добавить/убрать колонку,\n * Shift — диапазон колонок. Клик по иконкам сортировки/фильтрации в шапке\n * выделение НЕ вызывает. Поддерживает copy/paste по выделенным колонкам.\n * @default true\n */\n enableColumnSelection?: boolean;\n /**\n * Выделение строк по клику/драгу на колонке нумерации (`rowMarkers`).\n * Ctrl/Cmd — несмежный выбор отдельных строк, драг — смежный диапазон.\n * Поддерживает copy/paste по выделенным строкам. Требует включённой колонки\n * нумерации (`rowMarkers`). При `false` клик по нумерации ничего не выделяет.\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Выделение всей таблицы кликом по «нулевой» ячейке — шапке колонки нумерации\n * (левый верхний угол). Выделяет все строки данных по всем колонкам одним\n * диапазоном; поддерживает copy/paste. Требует включённой колонки нумерации\n * (`rowMarkers`) и активного `mode` (≠ `disabled`).\n * @default true\n */\n enableSelectAll?: boolean;\n /**\n * Внешний (controlled) контроль НАТИВНОГО выделения (ячейки/диапазон/смежные\n * строки и колонки). `state` — кортеж `[value, setter]` как у `selecting.state`.\n * Сброс: `setter({ current: undefined, rows: empty, columns: empty })`.\n * Несмежный Ctrl-выбор колонок/строк (own-state) сюда не входит — он внутренний.\n */\n state?: [GridSelection, React.Dispatch<React.SetStateAction<GridSelection>>];\n};\n\nexport type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *```jsx\n * import { Table } from '@daisforge/ui';\n *\n * <Table\n * tableConfig={{selecting: {stateAndSetter, rowKeyGetter}}}\n * {...rest}\n * />\n * ```\n */\n selecting?: {\n state: [\n ReadonlySet<RowIdType>,\n React.Dispatch<React.SetStateAction<ReadonlySet<RowIdType>>>,\n ];\n rowKeyGetter: (row: RowType) => RowIdType;\n } & Prettify<SelectingRowConfig<RowType, RowIdType>> & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n controlBlock?: {\n domMetadata?: DomMetadata;\n };\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам RightSidebar\n */\n sidebar?: {\n domMetadata?: DomMetadata;\n };\n };\n /**\n * editing - активация редактирования. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={editing: {onRowsChange, rowKeyGetter}}\n *\n * columnConfig=[{editingCell: {...}}]\n */\n editing?: EditingConfig<RowType, RowIdType, SummaryRowType>;\n\n /**\n * cellTransfer — конфигурация копирования, вставки и fill handle.\n * По умолчанию копирование (Ctrl+C) работает в режиме редактирования.\n * Данные копируются в формате TSV (tab-separated values), совместимом с Excel.\n *\n * Пример:\n *\n * tableConfig={{ cellTransfer: { enabled: true, onBeforeCopy: (data) => data } }}\n */\n cellTransfer?: CellTransferConfig;\n\n /**\n * Модуль нотификаций: стандартизированный канал событий для внешних\n * потребителей. Таблица UI не рисует — вызывает `onNotification`, потребитель\n * показывает тост/модалку на своей стороне. Сейчас эмитятся ошибки copy/paste\n * (в т.ч. multi-range 2D-разброс, readonly/overflow-abort, пропуск валидацией).\n */\n notifications?: TableNotificationsConfig;\n\n /**\n * subRows - активация иерархии, отображения вложенных строк. Условие активации - наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={subRows: {getSubRows, rowKeyGetter}}\n *\n * columnConfig=[{subRow: {...}}]\n */\n subRows?: SubRows<RowType, RowIdType>;\n /**\n * Столбец с нумерацией строк.\n * Активируется наличием этого свойства. Поддерживает `subRows` любой глубины.\n *\n * @see {@link RowMarkersTableConfig} — полное описание конфигурации и примеры.\n */\n rowMarkers?: RowMarkersTableConfig<RowType>;\n rowsGrouping?: RowsGrouping<RowType, SummaryRowType>;\n\n /**\n * resizableColumn - активация изменения ширины колонки с помощью курсора. Условие активации - resizableColumn: true. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={resizableColumn: true}\n *\n * columnConfig=[{resizable: true}]\n */\n resizableColumn?: boolean;\n /**\n * style - inline-стили. Для стилей самой таблицы (без вложенности).\n *\n * Исключены свойства 'width' | 'height' | 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight'. Их следует стилизовать через свойства containerStyle.\n */\n style?: Omit<\n CSSProperties,\n 'width' | 'height' | 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight'\n >;\n /**\n * css - для стилей простых и сложных(например, для стилизации cell, headerCell, ...).\n *\n * Пример:\n *\n * ```css\n * & > .rdg-header-row > .rdgCell {\n * background-color: red;\n * }\n * ```\n *\n */\n css?: string | CSSObject;\n\n summaryRows?: SummaryRowsConfig;\n /**\n * controlBlock - пропс для активации, кастомизации контролблока.\n */\n controlBlock?: ControlBlockConfig;\n /**\n * containerStyle - инлайновые стили контейнера таблицы и контролблока. высоту и ширину для таблицы рекомендуется задавать в этом пропсе.\n * Пример:\n *\n * ```tsx\n tableConfig={{ containerStyle: { height: '70vh' } }}\n * ```\n */\n containerStyle?: CSSProperties | undefined;\n /**\n * containerCss - стили для контейнера таблицы и контролблока.\n * Через этот пропс можно дополнительно кастомизировать контейнер и все его дочерние элементы.\n */\n containerCss?: string | CSSObject | undefined;\n /**\n * containerId - html-атрибут id контейнера таблицы.\n */\n containerId?: string | undefined;\n\n /**\n * unstickyHeader - откреплённая шапка. Header уезжает вместе со скроллом контента.\n * @default false\n */\n unstickyHeader?: boolean;\n\n /** Тултип при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\n tooltip?: TableConfigTooltip;\n\n /**\n * infinityScroll - бесконечная подгрузка данных при скролле.\n *\n * Условие активации - наличие самого пропса infinityScroll.\n *\n * При активации infinityScroll отключается дефолтная сортировка и фильтрация.\n * (Предполагается, что если есть infinityScroll,\n * то в таблице представлен не весь объем данных,\n * по этой причине дефолтная сортировка и фильтрация не подходят и будут отображать некорректные данные)\n *\n */\n infinityScroll?: InfinityScrollConfig<RowType>;\n /**\n * @isLoading - состояние загрузки данных для таблицы.\n */\n isLoading?: boolean | { boolean: boolean; skeletonRowsCount: number };\n /**\n * loadingOverlay - конфигурация для отображения оверлея загрузки.\n * Показывает индикатор загрузки поверх таблицы с возможностью кастомизации.\n */\n loadingOverlay?: {\n /**\n * Активен ли оверлей загрузки\n * @default true\n */\n active?: boolean;\n /**\n * Кастомный компонент спиннера\n * @default <Spinner view=\"secondary\" size=\"36\" /> (внутренний компонент)\n */\n spinner?: ReactNode;\n /**\n * Основной заголовок оверлея\n * @default \"Загрузка таблицы\"\n */\n title?: string;\n /**\n * Подзаголовок, который появится после задержки, пустая строка - значит он не появится\n * @default \"\"\n */\n subtitle?: string;\n /**\n * Задержка перед показом подзаголовка (мс)\n * @default 10000 (10 секунд)\n */\n showSubtitleDelay?: number;\n };\n /**\n * Empty state внутри content-области canvas-таблицы.\n * Не заменяет control block, header или pagination.\n */\n emptyState?: TableCanvasEmptyStateConfig;\n /**\n * Error state внутри content-области canvas-таблицы.\n * Не заменяет control block, header или pagination.\n */\n errorState?: TableCanvasErrorStateConfig;\n // FIXME:\n // noRowsFallback?: boolean | { custom: ReactNode };\n columnsControl?: ColumnsControlConfig;\n // FIXME:\n // rowDetailPanel?: RowDetailConfig<RowType>;\n // FIXME: Временно комментирую использование, пока не адаптируем TableCanvas\n // rowInstruments?: {\n // getRowDropdownConfig: RowInstrumentsType<RowType>;\n // /**\n // * @default false\n // */\n // defaultOpened?: boolean;\n // /**\n // * @default true\n // */\n // showInControl?: boolean;\n // /**\n // * Внешний стейт видимости\n // */\n // openedState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n // };\n fullScreenEnabled?: FullScreen;\n /**\n * Переносит портал editor overlay внутрь контейнера таблицы вместо document.body.\n * Решает проблему focus trap и сдвига координат при рендере таблицы в модалке с CSS transform.\n * @default undefined (overlay рендерится в document.body — стандартное поведение)\n */\n editorOverlayPortal?: 'inside';\n keyText?: boolean | KeyTextConfig;\n /**\n * pagination - пропс для пагинации.\n *\n * Условие активации - наличие самого пропса pagination.\n *\n * При активации pagination отключается дефолтные поиск, сортировка и фильтрация.\n * (поиск работает на уровне с фильтрацией и сортировкой, сначала поиск, затем фильтрация, затем сортировка)\n *\n */\n pagination?: PaginationProps;\n /**\n * searching - пропс для поиска по таблицу.\n *\n * Условие активации - наличие searching.enabled: true\n *\n */\n /**\n * Конфигурация табов в сайдбаре\n */\n sidebarConfig?: SidebarConfig;\n searching?: SearchingProps;\n /**\n * onHeaderContextMenu - callback открытия контекстного меню.\n */\n onHeaderContextMenu?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onHeaderContextMenu'];\n onHeaderContextMenuDropdown?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onHeaderContextMenuDropdown'];\n onCellContextMenuDropdown?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onCellContextMenuDropdown'];\n onCellContextMenu?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onCellContextMenu'];\n /**\n * Обработчик клика по ячейке.\n *\n * Получает `cell` readonly [colIndex, rowIndex] от glide, и `info` обогащённый объект\n * с row, column, ctxs, refTable (аналогично onCellContextMenu).\n */\n onCellClicked?: (\n cell: readonly [number, number],\n info: TableInfoWithRow<RowType, SummaryRowType>,\n ) => void;\n} & GlideInstanceOmitted<RowType, SummaryRowType>;\n\ntype GlideInstanceOmitted<\n RowType extends ObjectForExtending,\n SummaryRowType,\n> = Pick<\n Omit<\n TableGlideInstanceProps<RowType, SummaryRowType>,\n | keyof TableGlideCustomProps<RowType, SummaryRowType>\n | 'CellReadOnlyEditor' /* CellReadOnlyEditor - свойство только для GlideInstance */\n >,\n | 'className'\n | 'columnSelect'\n | 'minColumnWidth'\n | 'maxColumnWidth'\n | 'maxColumnAutoWidth'\n | 'onVisibleRegionChanged'\n | 'onCellClicked'\n // | 'CellReadOnlyEditor'\n // | 'onGroupHeaderClicked'\n | 'onHeaderClicked'\n | 'onItemHovered'\n | 'trapFocus'\n | 'onColumnResize'\n>;\nexport type CellReadOnlyEditorProps<\n RowType extends ObjectForExtending,\n SummaryRowType,\n> = CellReadOnlyEditorGlideInstanceStaticProps &\n EditingCellInfo<RowType, SummaryRowType>;\n\n// & Pick<\n// DataGridPropsDefault<RowType, SummaryRowType, RowIdType>,\n// | 'summaryRowHeight'\n// | 'onFill'\n// | 'onCopy'\n// | 'onPaste'\n// | 'onCellClick'\n// | 'onCellDoubleClick'\n// | 'onCellContextMenu'\n// | 'onCellKeyDown'\n// | 'onSelectedCellChange'\n// | 'onScroll'\n// | 'onColumnResize'\n// | 'onColumnsReorder'\n// | 'enableVirtualization'\n// | 'rowClass'\n// | 'data-testid'\n// | 'headerRowHeight'\n// >;\n\n/**\n * Конфигурация фичи rowMarkers (столбец с нумерацией строк).\n *\n * Столбец frozen, вставляется первым, исключён из reorder/pin/hide/sort/grouping.\n * Поддерживает плоский и древовидный (`subRows`) режимы любой глубины вложенности.\n *\n * **Дефолтное поведение** (без `getRowMarker`): root-only нумерация —\n * корневые строки получают номер (`startIndex + rootIndex`), дочерние — пустую строку.\n *\n * @example\n * ```tsx\n * // Минимальная конфигурация\n * tableConfig={{ rowMarkers: { startIndex: 1 } }}\n *\n * // Сквозная стабильная нумерация\n * tableConfig={{ rowMarkers: { getRowMarker: ({ flatIndex }) => flatIndex + 1 } }}\n * ```\n */\nexport type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * ⚠️ **Нестабильный** — меняется при сворачивании/разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};\n```\n\n## FilterComponentInPopoverProps\n\nПропсы, доступные в кастомном компоненте фильтра (`component: 'custom'`).\n\n```typescript\ntype FilterComponentInPopoverProps<\n FilterStateType extends ObjectForExtending,\n ColumnConfig,\n> = {\n popoverIsOpen: boolean;\n setPopoverIsOpen: React.Dispatch<React.SetStateAction<boolean>>;\n columnConfig: ColumnConfig;\n tabIndex: number;\n headerContextState: HeaderContextValueTypeInstance<FilterStateType> &\n FiltersVariables &\n HeaderContextValueTypeInstance<ObjectForExtending>;\n /**\n * Источник открытия фильтра: из шапки таблицы или из сайдбара\n * Полезно для условной логики в customRender (например, alwaysOpened для combobox)\n */\n filterSource?: 'header' | 'sidebar';\n};\n```",
52978
+ "apiDocs": "# Filtering API\n\n## FilteringConfig\n\n```typescript\ntype FilteringConfig<T> = {\n state: [T, React.Dispatch<React.SetStateAction<T>>];\n /**\n * @deprecated Использовать filtersInfo\n *\n * clearedValue - состояние фильтров после очистки. Используется для сравнения:\n * Например, был ли фильтр конкретной колонки изменен рассчитывается посредством сравнения текущего значения фильтра и значения фильтра в clearedValue.\n * Если они равны, то фильтр считается не измененным.\n */\n clearedValue?: T;\n /**\n * Информация о фильтрах для каждой колонки.\n * Используется для:\n * 1. Хранения меток (label) фильтров для отображения в UI\n * 2. Хранения значений по умолчанию (clearedValue) для каждого фильтра\n * 3. Определения, был ли фильтр изменен (сравнение текущего значения с clearedValue)\n */\n filtersInfo?: Record<\n keyof T,\n {\n label: string;\n clearedValue: T[keyof T];\n }\n >;\n /**\n * manualFiltering - пропс, активирующий ручную-кастомную (или на стороне бэка) фильтрацию.\n * @remarks\n * Для автоматической фильтрации (когда не используется manualFiltering в tableConfig):\n * - Для всех типов фильтров обязательно должен быть указан `valueInRow`\n * - Для component: 'select' дополнительно должен быть указан `filteringType`\n * - Для component: 'input' с кастомным фильтром (когда filter - функция)\n * функция фильтрации должна быть передана явно\n *\n * Если используется manualFiltering, эти параметры не обязательны,\n * так как вся логика фильтрации реализуется извне.\n */\n manualFiltering?: boolean;\n /**\n * Конфигурация для отображения фильтров в правом сайдбаре.\n *\n * Позволяет:\n * 1. Добавлять глобальные фильтры (не привязанные к конкретной колонке)\n * 2. Управлять порядком отображения фильтров\n * 3. Переопределять рендер и заголовки колоночных фильтров\n *\n * Логика работы:\n * - По умолчанию в сайдбаре отображаются все колоночные фильтры (из columnConfig)\n * - Ключом фильтра считается `key` колонки, если его нет, то пытаемся взять `keyInFilterState` (если указан), иначе это не будет работать\n * - Заголовком фильтра по умолчанию является `name` колонки или ключ фильтра (name || key)\n * - Глобальные фильтры добавляются через `items` с уникальными ключами\n *\n * Особенности:\n * 1. Порядок отображения:\n * - Сначала фильтры из `order` (в указанной последовательности)\n * - Затем остальные колоночные фильтры (в порядке columnConfig)\n * - В конце - глобальные фильтры (в алфавитном порядке ключей)\n *\n * 2. Переопределение колоночных фильтров:\n * - Чтобы изменить заголовок: укажите ключ колонки в `items` с новым `label`\n * - Чтобы заменить рендер: укажите ключ колонки с `customRenderFn`\n * - Оригинальный фильтр из колонки будет проигнорирован\n *\n * 3. Глобальные фильтры:\n * - Ключ не должен совпадать с ключами колонок\n * - Обязательно указать `label` и `customRenderFn`\n * - Отображаются после всех колоночных фильтров, если не переопределены в order\n *\n * @example\n * // Добавление глобального фильтра и изменение порядка\n * sidebarConfig: {\n * order: ['status', 'tasks', 'type'], // Сначала статус, затем глобальный поиск\n * items: {\n * tasks: { // Уникальный ключ\n * label: 'Поиск по всем полям',\n * customRenderFn: () => <TaskComponent />\n * },\n * status: { // Переопределение колоночного фильтра\n * label: 'Статус заявки', // Новый заголовок\n * customRenderFn: () => <CustomStatusFilter /> // Кастомный рендер\n * }\n * type: { // Переопределение только Label колоночного фильтра\n * label: 'Тип', // Новый заголовок\n * }\n * }\n * }\n */\n sidebarConfig?: {\n /**\n * Определяет порядок отображения фильтров.\n * - Указываются ключи фильтров (колоночных или глобальных)\n * - Не указанные в order фильтры отображаются после\n * - Если order пуст, сохраняется порядок: глобальные фильтры + columnConfig фильтры в конце\n */\n order?: string[];\n /**\n * Конфигурация фильтров для сайдбара.\n * Ключом может быть:\n * - Ключ колонки (для переопределения колоночного фильтра)\n * - Уникальный ключ (для добавления глобального фильтра)\n */\n items: Partial<\n Record<\n keyof T,\n {\n /** Заголовок фильтра (обязателен для глобальных фильтров), можно использовать для переопределения для локальных фильтров */\n label: string;\n /**\n * Функция рендера компонента фильтра.\n * Для колоночных фильтров переопределяет оригинальный рендер.\n * Для глобальных фильтров обязательна.\n */\n customRenderFn?: (\n filters: T,\n setFilters: React.Dispatch<React.SetStateAction<T>>,\n ) => React.ReactNode;\n }\n >\n >;\n };\n /**\n * Callback для кастомизации стилей чипсов (фильтров) в списке\n *\n * @param {ItemOrGroup} itemOrGroup - Текущий элемент или группа фильтров\n * @param {Item | undefined} item - Конкретный элемент фильтра (если есть)\n * @returns {CSSObject} Объект стилей, который будет применен к чипсу\n *\n * @description\n * Позволяет динамически задавать стили для каждого чипса в зависимости от его типа и содержимого.\n * Вызывается для каждого отображаемого чипса (как для отдельных элементов, так и для элементов внутри групп).\n *\n * @example\n * // Пример 1: Стилизация чипсов по типу\n * chipStyle: (itemOrGroup, item) => {\n * if (isGroup(itemOrGroup)) {\n * return { backgroundColor: '#f0f0f0' };\n * }\n * return { backgroundColor: '#e0e0e0' };\n * }\n *\n * @example\n * // Пример 2: Стилизация по значению фильтра\n * chipStyle: (itemOrGroup, item) => {\n * if (item?.label === 'Высокий приоритет') {\n * return {\n * backgroundColor: '#ffebee',\n * color: '#d32f2f'\n * };\n * }\n * return {};\n * }\n *\n * @example\n * // Пример 3: Разные стили для элементов в группе и одиночных\n * chipStyle: (itemOrGroup, item) => {\n * if (item) {\n * // Стиль для элемента внутри группы\n * return { maxWidth: 180 };\n * }\n * // Стиль для одиночного чипа\n * return { maxWidth: 150 };\n * }\n */\n chipStyle?: (itemOrGroup: ItemOrGroup, item: Item | undefined) => CSSObject;\n /**\n * Пользовательский форматтер заголовка группы чипсов.\n *\n * Используется в блоке активных фильтров для подмены текста заголовка\n * группы (например, “Статус”, “Тип”, “Автор”). Полезно, когда нужно\n * динамически менять подпись (добавить счётчик, i18n и т.п.).\n *\n * @param {ItemOrGroup} itemOrGroup Объект группы (тип Group). Вызов происходит только для групп,\n * поэтому безопасно кастовать к Group.\n * @returns {string} Текст заголовка группы чипсов.\n *\n * @default Возвращается `group.groupLabel`.\n *\n * @remarks\n * - Функция должна быть чистой и быстрой (без сетевых запросов и побочных эффектов).\n * - Возвращайте ТОЛЬКО строку — чипы отображают текстовое значение.\n * - Для длинных строк применяется усечение (overflow с многоточием) на уровне компонента.\n *\n * @example\n * // Добавить двоеточие и количество выбранных элементов\n * renderGroupLabel: (group) => `${group.groupLabel}: ${group.items.length}`\n *\n * @example\n * // Локализация заголовка группы\n * const i18n = { status: 'Статус', type: 'Тип' };\n * renderGroupLabel: (group) => i18n[group.groupId] ?? group.groupLabel\n */\n renderGroupLabel?: (group: Group) => string;\n\n /**\n * Пользовательский форматтер текста чипса (элемента внутри группы или одиночного фильтра).\n *\n * Позволяет управлять тем, что именно отображается внутри каждого чипса. Особенно полезен\n * при серверной пагинации/ленивой загрузке, когда в состоянии фильтра хранится только value,\n * а label необходимо подставить из локального кэша.\n *\n * @param {Group | null } group Группа, внутри которой рендерится чип (или null).\n * @param {Item} item Сам элемент чипса. В группах передаётся конкретный `Item`,\n * для одиночного чипа может быть `undefined`.\n * @returns {string} Текст, который будет показан в чипсе.\n *\n * @default Используется `item?.label.toString()` (или строковое представление value).\n *\n * @remarks\n * - Функция должна быть чистой и быстрой;\n * - Возвращайте ТОЛЬКО строку.\n *\n * @example\n * // 1) Отображение label из кэша при серверной пагинации\n * const labelsCacheRef = useRef(new Map<string, string>());\n * renderChipLabel: (group, item) => {\n * if (item?.id != null) {\n * const key = String(item.id);\n * const fromCache = labelsCacheRef.current.get(key);\n * if (fromCache) return fromCache; // красивый label, если уже загрузили\n * return String(item.label ?? key); // фоллбэк: временно показываем value/label из item\n * }\n * return ''; // безопасный фоллбэк для одиночных случаев\n * }\n *\n * @example\n * // 2) Нормализация отображения числовых значений\n * renderChipLabel: (_group, item) => {\n * const v = Number(item?.label);\n * return Number.isFinite(v) ? v.toLocaleString('ru-RU') : String(item?.label ?? '');\n * }\n *\n * @example\n * // 3) Префикс по типу группы\n * renderChipLabel: (group, item) => {\n * const prefix = group.groupId === 'status' ? '' : '#';\n * return `${prefix}${item?.label ?? item?.id ?? ''}`;\n * }\n */\n renderChipLabel?: (group: Group | null, item: Item) => string;\n};\n\nexport type DefaultSidebarTabIds = 'columns' | 'tableSettings' | 'filtering';\n\n/**\n * Конфигурация sidebar\n */\nexport type SidebarConfig = {\n /**\n * Включен ли sidebar. Отвечает только за UI. Не влияет на перерасчёт фичей.\n * @default true\n */\n enabled?: boolean;\n /**\n * Открыт ли sidebar при первом рендере (uncontrolled-режим).\n * Игнорируется, если передан openState.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Внешний контроль открытия/закрытия sidebar.\n * Если передан — источник истины, defaultOpen игнорируется.\n */\n openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * id вкладки, выбранной по умолчанию (актуально при открытом сайдбаре).\n * Если id не найден или у вкладки showInSidebar=false — используется первая доступная.\n */\n defaultActiveTabId?: string;\n /**\n * Внешний контроль активной вкладки (controlled-режим).\n * Если передан — источник истины, defaultActiveTabId игнорируется.\n */\n activeTabState?: [\n string | null,\n React.Dispatch<React.SetStateAction<string | null>>,\n ];\n /**\n * Колбэк смены активной вкладки. Вызывается и в uncontrolled-режиме.\n * При закрытии сайдбара приходит null.\n */\n onActiveTabChange?: (tabId: string | null, tab?: SidebarTab) => void;\n /**\n * Кастомные табы\n */\n customTabs?: Array<\n Omit<SidebarTab, 'id'> & {\n id: Exclude<string, DefaultSidebarTabIds>;\n }\n >;\n /**\n * Порядок отображения кастомных вкладок\n */\n customTabsOrder?: Array<Exclude<string, DefaultSidebarTabIds>>;\n defaultTabs?: Array<\n | {\n id: 'filtering' | 'columns';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара (если данный таб окажется единственным табом в настройках) */\n title?: string;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки (для табов с id = filtering и columns - недоступен. Настраивается через таб с id='tableSettings') */\n iconTooltipText?: undefined;\n }\n | {\n id: 'tableSettings';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара */\n title?: string;\n\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный слот для вкладки tableSettings (рендерится после общих настроек)\n * Только дл id: 'tableSettings'\n */\n customGeneralSettingsSlot?: React.ReactNode;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки @default 'Настройки' */\n iconTooltipText?: string;\n }\n >;\n};\n\nexport function activeViewIs<T extends 'cards' | 'rows'>(\n checkType: T,\n viewState: 'cards' | 'rows',\n view: View,\n): view is ViewMods[T] {\n return checkType === viewState;\n}\n\n/** Размер controlBlock: 'm' и 's' — одинаковые стили, 'xs' — уменьшенный */\nexport type ControlBlockSize = 'm' | 's' | 'xs';\n\nexport type ControlBlockConfig = {\n /**\n * show - наличие controlBlock-а\n * @default true\n */\n show?: boolean;\n /**\n * Размер controlBlock и его элементов (кнопки, иконки, поиск, отступы).\n * 'm' и 's' — стандартные стили, 'xs' — компактный режим.\n * Не влияет на customFeatures и кастомные слоты — они контролируются разработчиком.\n * @default 'm'\n */\n size?: ControlBlockSize;\n /**\n * @deprecated Используйте кастомные кнопки через rightSideInner или customFeatures.\n * leftSideInner - пропс для добавления кнопок в левую часть контролблока.\n * Продолжает работать для обратной совместимости.\n */\n leftSideInner?: ControlBlockButtonProps[];\n /**\n * rightSideInner - пропс для добавления кнопок в правую часть контролблока\n */\n rightSideInner?: ControlBlockButtonProps[];\n /**\n * customFeatures - пропс для добавления кастомных фичей (кнопки-иконки)\n */\n customFeatures?: FeatureItem[];\n /**\n * pinningMenu - нативная фича закрепления столбцов (SplitIconButton с\n * дропдауном) в правой зоне иконок. Появляется, когда активировано\n * закрепление колонок (columnsControl.pinning). Продукт может донастроить\n * и дополнить пункты меню (см. PinningMenuConfig).\n */\n pinningMenu?: PinningMenuConfig;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты левые фичи при адаптивной компрессии\n */\n leftSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты правые фичи при адаптивной компрессии\n */\n rightSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Активна ли фича компрессии (сжатие ToolsMenu при адаптиве)\n * @default true\n */\n enableAdaptiveCompress?: boolean;\n /**\n * Панель массовых действий\n */\n massActionPanel?: {\n /**\n * Кнопки панели. Размеры кнопок/дропдаунов панель подбирает сама (по `size` панели и компактности) —\n * передавать `size` кнопкам не нужно. Если `size` на кнопке всё же задан — он используется как есть.\n */\n buttons: MassActionButtonProps[];\n /**\n * Пропсы для Dropdown, в который сжались кнопки\n */\n collapsedDropdownProps?: TableDropdownConfigProps;\n /**\n * Отступ панели снизу от контейнера (px)\n * @default 24\n */\n bottom?: number;\n /**\n * Управление отображением панели массовых действий.\n * - `undefined` (по умолчанию) — стандартное поведение (показывается при selecting + выбранных строках)\n * - `false` — панель не рендерится, разработчик управляет действиями самостоятельно\n * - `true` — панель показывается всегда, даже без активного selecting и выбранных строк\n */\n show?: boolean;\n /**\n * Размерная сетка панели внутри таблицы: `m`/`s` — обычный размер, `xs` — уменьшенный (кнопки `xxs`, «ещё» `xs`, счётчик Body XS).\n * Viewport-логика (1280) в таблице не используется.\n * @default 's'\n */\n size?: MassActionsSize;\n };\n};\n\n/** Пропсы кнопки редактирования. onClick исключён — используйте onSave/onCancel/onEnableEditing. */\nexport type EditingButtonProps = Omit<\n Partial<ControlBlockButtonProps>,\n | 'onClick'\n | 'size'\n | 'dropdown'\n | 'isTargetAction'\n | 'skipRightDivider'\n | 'tooltipText'\n>;\n\nexport type EditingConfig<\n RowType extends ObjectForExtending,\n RowIdType,\n SummaryRowType,\n> = {\n /**\n * Функция обработки изменений в редактируемой строке\n */\n onRowsChange: Maybe<\n (rows: RowType[], data: RowsChangeData<RowType, SummaryRowType>) => void\n >;\n /**\n * свойство, необходимое для корректного изменения дочерних строк.\n * Данное свойство обязательно, если ваши дочерние строки установлены не под ключом \"subRows\" внутри строки.\n *\n * Как дочерние строки можно указывать свойство, которые вложено в другое свойство.\n * В таком случае \".\" будет знаком для обозначения сл. уровня вложенности.\n *\n * @example \"child\" или \"uvh367.children\"\n */\n subRowsKey?: string;\n /**\n * Глубокое копирование строк перед применением правки в режиме редактирования.\n *\n * Включите, если строки, передаваемые в таблицу, иммутабельны/заморожены\n * (напр. стейт под управлением Redux Toolkit или Immer — они вызывают\n * Object.freeze в dev). При редактировании дочерней строки таблица записывает\n * изменение ВНУТРЬ объекта родителя; на замороженном объекте это падает с\n * `TypeError: Cannot assign to read only property`.\n *\n * Когда флаг включён, таблица сама делает structuredClone редактируемой строки\n * перед записью — ваш иммутабельный стейт не мутируется, и редактирование\n * дочерних строк работает без действий с вашей стороны.\n *\n * ⚠️ Данные строки должны быть сериализуемыми. Где доступен structuredClone —\n * Date/Map/Set и вложенные структуры копируются корректно; на старых средах\n * без structuredClone используется JSON-фоллбэк (теряет Date/Map/Set, undefined\n * и функции). Не включайте без необходимости — глубокое копирование добавляет\n * накладные расходы.\n *\n * @default false\n */\n deepCloneRows?: boolean;\n /**\n * Функция определения уникального id строки\n * @param row\n * @returns\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Функция определения возможности редактирования строки\n * @param row\n * @returns\n */\n rowEditable?: (row: RowType) => boolean;\n /**\n * defaultEnabled - пропс, отвечающий за то, будет ли активен режим редактирования или нет. По умолчанию - не активно.\n * @default false\n */\n defaultEnabled?: boolean;\n /**\n * enabled - необходим для управления режимом редактирования снаружи компонента Таблицы.\n * По умолчанию, управление происходит в компоненте.\n * Приоритетнее, чем defaultEnabled.\n *\n * @type boolean - если НЕ нужно переключение в самой таблице\n * @type [boolean, React.Dispatch<React.SetStateAction<boolean>>] - если нужно переключение в самой таблице\n *\n */\n enabled?: boolean | [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * Cостояние скрытия/раскрытия кнопки включения/выключения фичи\n * @default true\n */\n showButtons?: boolean;\n /**\n * Функция, вызывающаяся при включении фичи.\n * @param enableEditorMode - функция включающая режим редактирования\n */\n onEnableEditing?: (enableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при сохранении отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onSave?: (disableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при отмене отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onCancel?: (disableEditorMode: () => void) => void;\n /** Пропсы кнопок в controlBlock. */\n buttons?: {\n save?: EditingButtonProps;\n cancel?: EditingButtonProps;\n edit?: EditingButtonProps;\n };\n /**\n * Кастомный слот, отображаемый левее кнопок «Отменить» / «Сохранить».\n * Виден только когда режим редактирования активен (editMode = true).\n * Зона ответственности разработчика — размерная сетка controlBlock не влияет на этот слот.\n */\n editModeLeftSlot?: React.ReactNode;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: {\n enableButton?: DomMetadata;\n saveButton?: DomMetadata;\n cancelButton?: DomMetadata;\n };\n};\n\nexport type SummaryRowsConfig = {\n showDefault: boolean;\n showInControl: boolean;\n} & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\n/**\n * Конфиг тултипа при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\nexport type TableConfigTooltip = {\n /**\n * Включить/выключить колоночные и кастомные тултипы (cellTooltip, headerCellTooltip).\n * Внутренние тултипы (drag-иконка в шапке и другие по nodeId) работают всегда.\n * @default false\n */\n enabled?: boolean;\n /** Глобальная задержка перед показом тултипа (мс). Per-node переопределяет. @default 500 */\n mouseEnterDelay?: number;\n /** Глобальная задержка перед скрытием тултипа (мс). Per-node переопределяет. @default 0 */\n mouseLeaveDelay?: number;\n};\n\nexport type InfinityScrollConfig<RowType extends ObjectForExtending> = {\n /** Триггер достижения скроллом зоны подгрузки данных. */\n onTrigger: (rows: RowType[]) => Promise<void> | void;\n /** Состояние загрузки (скелетоны) новых строк. */\n isLoading: boolean;\n /**\n * Флаг, когда все данные загружены — false.\n * @default true\n */\n hasMore?: boolean;\n /**\n * Для mode='region': за сколько строк до конца триггерить загрузку.\n * @default 5\n */\n rowThreshold?: number;\n};\n\nexport type { TableCanvasEmptyStateConfig, TableCanvasErrorStateConfig };\n\n/**\n * Настройка выделения ячеек/колонок/строк в TableCanvas — единый объект.\n * Это отдельная ось взаимодействия — не зависит от `highlightActiveType`.\n */\nexport type CellsSelectionConfig = {\n /**\n * Режим фактического выделения ячеек (нативный glide selection). По нему\n * работают copy/paste, рамка, fill-handle и затемнение шапки/нумерации.\n * @default \"range-cell\"\n */\n mode?: CellsSelectionMode;\n /**\n * Выделение колонок по клику на шапку. Ctrl/Cmd — добавить/убрать колонку,\n * Shift — диапазон колонок. Клик по иконкам сортировки/фильтрации в шапке\n * выделение НЕ вызывает. Поддерживает copy/paste по выделенным колонкам.\n * @default true\n */\n enableColumnSelection?: boolean;\n /**\n * Выделение строк по клику/драгу на колонке нумерации (`rowMarkers`).\n * Ctrl/Cmd — несмежный выбор отдельных строк, драг — смежный диапазон.\n * Поддерживает copy/paste по выделенным строкам. Требует включённой колонки\n * нумерации (`rowMarkers`). При `false` клик по нумерации ничего не выделяет.\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Выделение всей таблицы кликом по «нулевой» ячейке — шапке колонки нумерации\n * (левый верхний угол). Выделяет все строки данных по всем колонкам одним\n * диапазоном; поддерживает copy/paste. Требует включённой колонки нумерации\n * (`rowMarkers`) и активного `mode` (≠ `disabled`).\n * @default true\n */\n enableSelectAll?: boolean;\n /**\n * Внешний (controlled) контроль НАТИВНОГО выделения (ячейки/диапазон/смежные\n * строки и колонки). `state` — кортеж `[value, setter]` как у `selecting.state`.\n * Сброс: `setter({ current: undefined, rows: empty, columns: empty })`.\n * Несмежный Ctrl-выбор колонок/строк (own-state) сюда не входит — он внутренний.\n */\n state?: [GridSelection, React.Dispatch<React.SetStateAction<GridSelection>>];\n};\n\n/** Один объединённый блок: какие строки и колонки слить. */\nexport type MergedCellsRegion = {\n rowKeys: Array<string | number>;\n colKeys: string[];\n /** Выравнивание контента этого блока. Важнее колоночного и общего. */\n mergedCellsAlign?: MergedCellsAlign;\n};\n\nexport type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *```jsx\n * import { Table } from '@daisforge/ui';\n *\n * <Table\n * tableConfig={{selecting: {stateAndSetter, rowKeyGetter}}}\n * {...rest}\n * />\n * ```\n */\n selecting?: {\n state: [\n ReadonlySet<RowIdType>,\n React.Dispatch<React.SetStateAction<ReadonlySet<RowIdType>>>,\n ];\n rowKeyGetter: (row: RowType) => RowIdType;\n } & Prettify<SelectingRowConfig<RowType, RowIdType>> & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n controlBlock?: {\n domMetadata?: DomMetadata;\n };\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам RightSidebar\n */\n sidebar?: {\n domMetadata?: DomMetadata;\n };\n };\n /**\n * editing - активация редактирования. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={editing: {onRowsChange, rowKeyGetter}}\n *\n * columnConfig=[{editingCell: {...}}]\n */\n editing?: EditingConfig<RowType, RowIdType, SummaryRowType>;\n\n /**\n * cellTransfer — конфигурация копирования, вставки и fill handle.\n * По умолчанию копирование (Ctrl+C) работает в режиме редактирования.\n * Данные копируются в формате TSV (tab-separated values), совместимом с Excel.\n *\n * Пример:\n *\n * tableConfig={{ cellTransfer: { enabled: true, onBeforeCopy: (data) => data } }}\n */\n cellTransfer?: CellTransferConfig;\n\n /**\n * Объединение ячеек тела таблицы.\n *\n * mergeByCellValues — колонки, в которых подряд идущие одинаковые значения\n * сливаются в один блок (по вертикали). Границы блоков таблица считает сама из\n * данных; сортировка и фильтр их пересобирают. Данные не меняются — значение и\n * так повторяется в строках, объединение лишь показывает его одной ячейкой.\n *\n * mergedCellsRegions — список блоков, заданных снаружи (как выделение): каждый\n * блок описан ключами строк (rowKeys) и колонок (colKeys). Нужен rowKeyGetter.\n * Если после сортировки или скрытия колонок строки/колонки блока перестали идти\n * подряд — блок не рисуется. Важнее mergeByCellValues.\n *\n * Примеры: mergeCells={{ mergeByCellValues: ['dept', 'role'] }},\n * mergeCells={{ mergedCellsRegions, rowKeyGetter: (r) => r.id }}.\n */\n mergeCells?: {\n mergeByCellValues?: Array<\n string | { colKey: string; value: (row: RowType) => unknown }\n >;\n /** Список блоков, заданных снаружи (по ключам строк и колонок). */\n mergedCellsRegions?: MergedCellsRegion[];\n /**\n * Нужен для mergedCellsRegions: находит строку по её ключу. Должна быть\n * стабильной функцией; менять её на лету не нужно — в пересборку таблицы она\n * не входит.\n */\n rowKeyGetter?: (row: RowType) => string | number;\n /**\n * Выравнивание по умолчанию для всех блоков. Колоночный mergedCellsAlign и\n * выравнивание конкретного региона важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n };\n\n /**\n * Модуль нотификаций: стандартизированный канал событий для внешних\n * потребителей. Таблица UI не рисует — вызывает `onNotification`, потребитель\n * показывает тост/модалку на своей стороне. Сейчас эмитятся ошибки copy/paste\n * (в т.ч. multi-range 2D-разброс, readonly/overflow-abort, пропуск валидацией).\n */\n notifications?: TableNotificationsConfig;\n\n /**\n * subRows - активация иерархии, отображения вложенных строк. Условие активации - наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={subRows: {getSubRows, rowKeyGetter}}\n *\n * columnConfig=[{subRow: {...}}]\n */\n subRows?: SubRows<RowType, RowIdType>;\n /**\n * Столбец с нумерацией строк.\n * Активируется наличием этого свойства. Поддерживает `subRows` любой глубины.\n *\n * @see {@link RowMarkersTableConfig} — полное описание конфигурации и примеры.\n */\n rowMarkers?: RowMarkersTableConfig<RowType>;\n rowsGrouping?: RowsGrouping<RowType, SummaryRowType>;\n\n /**\n * resizableColumn - активация изменения ширины колонки с помощью курсора. Условие активации - resizableColumn: true. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={resizableColumn: true}\n *\n * columnConfig=[{resizable: true}]\n */\n resizableColumn?: boolean;\n /**\n * style - inline-стили. Для стилей самой таблицы (без вложенности).\n *\n * Исключены свойства 'width' | 'height' | 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight'. Их следует стилизовать через свойства containerStyle.\n */\n style?: Omit<\n CSSProperties,\n 'width' | 'height' | 'minWidth' | 'minHeight' | 'maxWidth' | 'maxHeight'\n >;\n /**\n * css - для стилей простых и сложных(например, для стилизации cell, headerCell, ...).\n *\n * Пример:\n *\n * ```css\n * & > .rdg-header-row > .rdgCell {\n * background-color: red;\n * }\n * ```\n *\n */\n css?: string | CSSObject;\n\n summaryRows?: SummaryRowsConfig;\n /**\n * controlBlock - пропс для активации, кастомизации контролблока.\n */\n controlBlock?: ControlBlockConfig;\n /**\n * containerStyle - инлайновые стили контейнера таблицы и контролблока. высоту и ширину для таблицы рекомендуется задавать в этом пропсе.\n * Пример:\n *\n * ```tsx\n tableConfig={{ containerStyle: { height: '70vh' } }}\n * ```\n */\n containerStyle?: CSSProperties | undefined;\n /**\n * containerCss - стили для контейнера таблицы и контролблока.\n * Через этот пропс можно дополнительно кастомизировать контейнер и все его дочерние элементы.\n */\n containerCss?: string | CSSObject | undefined;\n /**\n * containerId - html-атрибут id контейнера таблицы.\n */\n containerId?: string | undefined;\n\n /**\n * unstickyHeader - откреплённая шапка. Header уезжает вместе со скроллом контента.\n * @default false\n */\n unstickyHeader?: boolean;\n\n /** Тултип при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\n tooltip?: TableConfigTooltip;\n\n /**\n * infinityScroll - бесконечная подгрузка данных при скролле.\n *\n * Условие активации - наличие самого пропса infinityScroll.\n *\n * При активации infinityScroll отключается дефолтная сортировка и фильтрация.\n * (Предполагается, что если есть infinityScroll,\n * то в таблице представлен не весь объем данных,\n * по этой причине дефолтная сортировка и фильтрация не подходят и будут отображать некорректные данные)\n *\n */\n infinityScroll?: InfinityScrollConfig<RowType>;\n /**\n * @isLoading - состояние загрузки данных для таблицы.\n */\n isLoading?: boolean | { boolean: boolean; skeletonRowsCount: number };\n /**\n * loadingOverlay - конфигурация для отображения оверлея загрузки.\n * Показывает индикатор загрузки поверх таблицы с возможностью кастомизации.\n */\n loadingOverlay?: {\n /**\n * Активен ли оверлей загрузки\n * @default true\n */\n active?: boolean;\n /**\n * Кастомный компонент спиннера\n * @default <Spinner view=\"secondary\" size=\"36\" /> (внутренний компонент)\n */\n spinner?: ReactNode;\n /**\n * Основной заголовок оверлея\n * @default \"Загрузка таблицы\"\n */\n title?: string;\n /**\n * Подзаголовок, который появится после задержки, пустая строка - значит он не появится\n * @default \"\"\n */\n subtitle?: string;\n /**\n * Задержка перед показом подзаголовка (мс)\n * @default 10000 (10 секунд)\n */\n showSubtitleDelay?: number;\n };\n /**\n * Empty state внутри content-области canvas-таблицы.\n * Не заменяет control block, header или pagination.\n */\n emptyState?: TableCanvasEmptyStateConfig;\n /**\n * Error state внутри content-области canvas-таблицы.\n * Не заменяет control block, header или pagination.\n */\n errorState?: TableCanvasErrorStateConfig;\n // FIXME:\n // noRowsFallback?: boolean | { custom: ReactNode };\n columnsControl?: ColumnsControlConfig;\n // FIXME:\n // rowDetailPanel?: RowDetailConfig<RowType>;\n // FIXME: Временно комментирую использование, пока не адаптируем TableCanvas\n // rowInstruments?: {\n // getRowDropdownConfig: RowInstrumentsType<RowType>;\n // /**\n // * @default false\n // */\n // defaultOpened?: boolean;\n // /**\n // * @default true\n // */\n // showInControl?: boolean;\n // /**\n // * Внешний стейт видимости\n // */\n // openedState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n // };\n fullScreenEnabled?: FullScreen;\n /**\n * Переносит портал editor overlay внутрь контейнера таблицы вместо document.body.\n * Решает проблему focus trap и сдвига координат при рендере таблицы в модалке с CSS transform.\n * @default undefined (overlay рендерится в document.body — стандартное поведение)\n */\n editorOverlayPortal?: 'inside';\n keyText?: boolean | KeyTextConfig;\n /**\n * pagination - пропс для пагинации.\n *\n * Условие активации - наличие самого пропса pagination.\n *\n * При активации pagination отключается дефолтные поиск, сортировка и фильтрация.\n * (поиск работает на уровне с фильтрацией и сортировкой, сначала поиск, затем фильтрация, затем сортировка)\n *\n */\n pagination?: PaginationProps;\n /**\n * searching - пропс для поиска по таблицу.\n *\n * Условие активации - наличие searching.enabled: true\n *\n */\n /**\n * Конфигурация табов в сайдбаре\n */\n sidebarConfig?: SidebarConfig;\n searching?: SearchingProps;\n /**\n * onHeaderContextMenu - callback открытия контекстного меню.\n */\n onHeaderContextMenu?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onHeaderContextMenu'];\n onHeaderContextMenuDropdown?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onHeaderContextMenuDropdown'];\n onCellContextMenuDropdown?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onCellContextMenuDropdown'];\n onCellContextMenu?: TableGlideInstanceProps<\n RowType,\n SummaryRowType\n >['onCellContextMenu'];\n /**\n * Обработчик клика по ячейке.\n *\n * Получает `cell` readonly [colIndex, rowIndex] от glide, и `info` обогащённый объект\n * с row, column, ctxs, refTable (аналогично onCellContextMenu).\n */\n onCellClicked?: (\n cell: readonly [number, number],\n info: TableInfoWithRow<RowType, SummaryRowType>,\n ) => void;\n} & GlideInstanceOmitted<RowType, SummaryRowType>;\n\ntype GlideInstanceOmitted<\n RowType extends ObjectForExtending,\n SummaryRowType,\n> = Pick<\n Omit<\n TableGlideInstanceProps<RowType, SummaryRowType>,\n | keyof TableGlideCustomProps<RowType, SummaryRowType>\n | 'CellReadOnlyEditor' /* CellReadOnlyEditor - свойство только для GlideInstance */\n >,\n | 'className'\n | 'columnSelect'\n | 'minColumnWidth'\n | 'maxColumnWidth'\n | 'maxColumnAutoWidth'\n | 'onVisibleRegionChanged'\n | 'onCellClicked'\n // | 'CellReadOnlyEditor'\n // | 'onGroupHeaderClicked'\n | 'onHeaderClicked'\n | 'onItemHovered'\n | 'trapFocus'\n | 'onColumnResize'\n>;\nexport type CellReadOnlyEditorProps<\n RowType extends ObjectForExtending,\n SummaryRowType,\n> = CellReadOnlyEditorGlideInstanceStaticProps &\n EditingCellInfo<RowType, SummaryRowType>;\n\n// & Pick<\n// DataGridPropsDefault<RowType, SummaryRowType, RowIdType>,\n// | 'summaryRowHeight'\n// | 'onFill'\n// | 'onCopy'\n// | 'onPaste'\n// | 'onCellClick'\n// | 'onCellDoubleClick'\n// | 'onCellContextMenu'\n// | 'onCellKeyDown'\n// | 'onSelectedCellChange'\n// | 'onScroll'\n// | 'onColumnResize'\n// | 'onColumnsReorder'\n// | 'enableVirtualization'\n// | 'rowClass'\n// | 'data-testid'\n// | 'headerRowHeight'\n// >;\n\n/**\n * Конфигурация фичи rowMarkers (столбец с нумерацией строк).\n *\n * Столбец frozen, вставляется первым, исключён из reorder/pin/hide/sort/grouping.\n * Поддерживает плоский и древовидный (`subRows`) режимы любой глубины вложенности.\n *\n * **Дефолтное поведение** (без `getRowMarker`): root-only нумерация —\n * корневые строки получают номер (`startIndex + rootIndex`), дочерние — пустую строку.\n *\n * @example\n * ```tsx\n * // Минимальная конфигурация\n * tableConfig={{ rowMarkers: { startIndex: 1 } }}\n *\n * // Сквозная стабильная нумерация\n * tableConfig={{ rowMarkers: { getRowMarker: ({ flatIndex }) => flatIndex + 1 } }}\n * ```\n */\nexport type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * **Нестабильный** — меняется при сворачивании и разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};\n```\n\n## FilterComponentInPopoverProps\n\nПропсы, доступные в кастомном компоненте фильтра (`component: 'custom'`).\n\n```typescript\ntype FilterComponentInPopoverProps<\n FilterStateType extends ObjectForExtending,\n ColumnConfig,\n> = {\n popoverIsOpen: boolean;\n setPopoverIsOpen: React.Dispatch<React.SetStateAction<boolean>>;\n columnConfig: ColumnConfig;\n tabIndex: number;\n headerContextState: HeaderContextValueTypeInstance<FilterStateType> &\n FiltersVariables &\n HeaderContextValueTypeInstance<ObjectForExtending>;\n /**\n * Источник открытия фильтра: из шапки таблицы или из сайдбара\n * Полезно для условной логики в customRender (например, alwaysOpened для combobox)\n */\n filterSource?: 'header' | 'sidebar';\n};\n```",
52837
52979
  "api": [
52838
52980
  {
52839
52981
  "typeName": "FilteringConfig",
52840
- "source": "type FilteringConfig<T> = {\n state: [T, React.Dispatch<React.SetStateAction<T>>];\n /**\n * @deprecated Использовать filtersInfo\n *\n * clearedValue - состояние фильтров после очистки. Используется для сравнения:\n * Например, был ли фильтр конкретной колонки изменен рассчитывается посредством сравнения текущего значения фильтра и значения фильтра в clearedValue.\n * Если они равны, то фильтр считается не измененным.\n */\n clearedValue?: T;\n /**\n * Информация о фильтрах для каждой колонки.\n * Используется для:\n * 1. Хранения меток (label) фильтров для отображения в UI\n * 2. Хранения значений по умолчанию (clearedValue) для каждого фильтра\n * 3. Определения, был ли фильтр изменен (сравнение текущего значения с clearedValue)\n */\n filtersInfo?: Record<\n keyof T,\n {\n label: string;\n clearedValue: T[keyof T];\n }\n >;\n /**\n * manualFiltering - пропс, активирующий ручную-кастомную (или на стороне бэка) фильтрацию.\n * @remarks\n * Для автоматической фильтрации (когда не используется manualFiltering в tableConfig):\n * - Для всех типов фильтров обязательно должен быть указан `valueInRow`\n * - Для component: 'select' дополнительно должен быть указан `filteringType`\n * - Для component: 'input' с кастомным фильтром (когда filter - функция)\n * функция фильтрации должна быть передана явно\n *\n * Если используется manualFiltering, эти параметры не обязательны,\n * так как вся логика фильтрации реализуется извне.\n */\n manualFiltering?: boolean;\n /**\n * Конфигурация для отображения фильтров в правом сайдбаре.\n *\n * Позволяет:\n * 1. Добавлять глобальные фильтры (не привязанные к конкретной колонке)\n * 2. Управлять порядком отображения фильтров\n * 3. Переопределять рендер и заголовки колоночных фильтров\n *\n * Логика работы:\n * - По умолчанию в сайдбаре отображаются все колоночные фильтры (из columnConfig)\n * - Ключом фильтра считается `key` колонки, если его нет, то пытаемся взять `keyInFilterState` (если указан), иначе это не будет работать\n * - Заголовком фильтра по умолчанию является `name` колонки или ключ фильтра (name || key)\n * - Глобальные фильтры добавляются через `items` с уникальными ключами\n *\n * Особенности:\n * 1. Порядок отображения:\n * - Сначала фильтры из `order` (в указанной последовательности)\n * - Затем остальные колоночные фильтры (в порядке columnConfig)\n * - В конце - глобальные фильтры (в алфавитном порядке ключей)\n *\n * 2. Переопределение колоночных фильтров:\n * - Чтобы изменить заголовок: укажите ключ колонки в `items` с новым `label`\n * - Чтобы заменить рендер: укажите ключ колонки с `customRenderFn`\n * - Оригинальный фильтр из колонки будет проигнорирован\n *\n * 3. Глобальные фильтры:\n * - Ключ не должен совпадать с ключами колонок\n * - Обязательно указать `label` и `customRenderFn`\n * - Отображаются после всех колоночных фильтров, если не переопределены в order\n *\n * @example\n * // Добавление глобального фильтра и изменение порядка\n * sidebarConfig: {\n * order: ['status', 'tasks', 'type'], // Сначала статус, затем глобальный поиск\n * items: {\n * tasks: { // Уникальный ключ\n * label: 'Поиск по всем полям',\n * customRenderFn: () => <TaskComponent />\n * },\n * status: { // Переопределение колоночного фильтра\n * label: 'Статус заявки', // Новый заголовок\n * customRenderFn: () => <CustomStatusFilter /> // Кастомный рендер\n * }\n * type: { // Переопределение только Label колоночного фильтра\n * label: 'Тип', // Новый заголовок\n * }\n * }\n * }\n */\n sidebarConfig?: {\n /**\n * Определяет порядок отображения фильтров.\n * - Указываются ключи фильтров (колоночных или глобальных)\n * - Не указанные в order фильтры отображаются после\n * - Если order пуст, сохраняется порядок: глобальные фильтры + columnConfig фильтры в конце\n */\n order?: string[];\n /**\n * Конфигурация фильтров для сайдбара.\n * Ключом может быть:\n * - Ключ колонки (для переопределения колоночного фильтра)\n * - Уникальный ключ (для добавления глобального фильтра)\n */\n items: Partial<\n Record<\n keyof T,\n {\n /** Заголовок фильтра (обязателен для глобальных фильтров), можно использовать для переопределения для локальных фильтров */\n label: string;\n /**\n * Функция рендера компонента фильтра.\n * Для колоночных фильтров переопределяет оригинальный рендер.\n * Для глобальных фильтров обязательна.\n */\n customRenderFn?: (\n filters: T,\n setFilters: React.Dispatch<React.SetStateAction<T>>,\n ) => React.ReactNode;\n }\n >\n >;\n };\n /**\n * Callback для кастомизации стилей чипсов (фильтров) в списке\n *\n * @param {ItemOrGroup} itemOrGroup - Текущий элемент или группа фильтров\n * @param {Item | undefined} item - Конкретный элемент фильтра (если есть)\n * @returns {CSSObject} Объект стилей, который будет применен к чипсу\n *\n * @description\n * Позволяет динамически задавать стили для каждого чипса в зависимости от его типа и содержимого.\n * Вызывается для каждого отображаемого чипса (как для отдельных элементов, так и для элементов внутри групп).\n *\n * @example\n * // Пример 1: Стилизация чипсов по типу\n * chipStyle: (itemOrGroup, item) => {\n * if (isGroup(itemOrGroup)) {\n * return { backgroundColor: '#f0f0f0' };\n * }\n * return { backgroundColor: '#e0e0e0' };\n * }\n *\n * @example\n * // Пример 2: Стилизация по значению фильтра\n * chipStyle: (itemOrGroup, item) => {\n * if (item?.label === 'Высокий приоритет') {\n * return {\n * backgroundColor: '#ffebee',\n * color: '#d32f2f'\n * };\n * }\n * return {};\n * }\n *\n * @example\n * // Пример 3: Разные стили для элементов в группе и одиночных\n * chipStyle: (itemOrGroup, item) => {\n * if (item) {\n * // Стиль для элемента внутри группы\n * return { maxWidth: 180 };\n * }\n * // Стиль для одиночного чипа\n * return { maxWidth: 150 };\n * }\n */\n chipStyle?: (itemOrGroup: ItemOrGroup, item: Item | undefined) => CSSObject;\n /**\n * Пользовательский форматтер заголовка группы чипсов.\n *\n * Используется в блоке активных фильтров для подмены текста заголовка\n * группы (например, “Статус”, “Тип”, “Автор”). Полезно, когда нужно\n * динамически менять подпись (добавить счётчик, i18n и т.п.).\n *\n * @param {ItemOrGroup} itemOrGroup Объект группы (тип Group). Вызов происходит только для групп,\n * поэтому безопасно кастовать к Group.\n * @returns {string} Текст заголовка группы чипсов.\n *\n * @default Возвращается `group.groupLabel`.\n *\n * @remarks\n * - Функция должна быть чистой и быстрой (без сетевых запросов и побочных эффектов).\n * - Возвращайте ТОЛЬКО строку — чипы отображают текстовое значение.\n * - Для длинных строк применяется усечение (overflow с многоточием) на уровне компонента.\n *\n * @example\n * // Добавить двоеточие и количество выбранных элементов\n * renderGroupLabel: (group) => `${group.groupLabel}: ${group.items.length}`\n *\n * @example\n * // Локализация заголовка группы\n * const i18n = { status: 'Статус', type: 'Тип' };\n * renderGroupLabel: (group) => i18n[group.groupId] ?? group.groupLabel\n */\n renderGroupLabel?: (group: Group) => string;\n\n /**\n * Пользовательский форматтер текста чипса (элемента внутри группы или одиночного фильтра).\n *\n * Позволяет управлять тем, что именно отображается внутри каждого чипса. Особенно полезен\n * при серверной пагинации/ленивой загрузке, когда в состоянии фильтра хранится только value,\n * а label необходимо подставить из локального кэша.\n *\n * @param {Group | null } group Группа, внутри которой рендерится чип (или null).\n * @param {Item} item Сам элемент чипса. В группах передаётся конкретный `Item`,\n * для одиночного чипа может быть `undefined`.\n * @returns {string} Текст, который будет показан в чипсе.\n *\n * @default Используется `item?.label.toString()` (или строковое представление value).\n *\n * @remarks\n * - Функция должна быть чистой и быстрой;\n * - Возвращайте ТОЛЬКО строку.\n *\n * @example\n * // 1) Отображение label из кэша при серверной пагинации\n * const labelsCacheRef = useRef(new Map<string, string>());\n * renderChipLabel: (group, item) => {\n * if (item?.id != null) {\n * const key = String(item.id);\n * const fromCache = labelsCacheRef.current.get(key);\n * if (fromCache) return fromCache; // красивый label, если уже загрузили\n * return String(item.label ?? key); // фоллбэк: временно показываем value/label из item\n * }\n * return ''; // безопасный фоллбэк для одиночных случаев\n * }\n *\n * @example\n * // 2) Нормализация отображения числовых значений\n * renderChipLabel: (_group, item) => {\n * const v = Number(item?.label);\n * return Number.isFinite(v) ? v.toLocaleString('ru-RU') : String(item?.label ?? '');\n * }\n *\n * @example\n * // 3) Префикс по типу группы\n * renderChipLabel: (group, item) => {\n * const prefix = group.groupId === 'status' ? '' : '#';\n * return `${prefix}${item?.label ?? item?.id ?? ''}`;\n * }\n */\n renderChipLabel?: (group: Group | null, item: Item) => string;\n};\n\nexport type DefaultSidebarTabIds = 'columns' | 'tableSettings' | 'filtering';\n\n/**\n * Конфигурация sidebar\n */\nexport type SidebarConfig = {\n /**\n * Включен ли sidebar. Отвечает только за UI. Не влияет на перерасчёт фичей.\n * @default true\n */\n enabled?: boolean;\n /**\n * Открыт ли sidebar при первом рендере (uncontrolled-режим).\n * Игнорируется, если передан openState.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Внешний контроль открытия/закрытия sidebar.\n * Если передан — источник истины, defaultOpen игнорируется.\n */\n openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * id вкладки, выбранной по умолчанию (актуально при открытом сайдбаре).\n * Если id не найден или у вкладки showInSidebar=false — используется первая доступная.\n */\n defaultActiveTabId?: string;\n /**\n * Внешний контроль активной вкладки (controlled-режим).\n * Если передан — источник истины, defaultActiveTabId игнорируется.\n */\n activeTabState?: [\n string | null,\n React.Dispatch<React.SetStateAction<string | null>>,\n ];\n /**\n * Колбэк смены активной вкладки. Вызывается и в uncontrolled-режиме.\n * При закрытии сайдбара приходит null.\n */\n onActiveTabChange?: (tabId: string | null, tab?: SidebarTab) => void;\n /**\n * Кастомные табы\n */\n customTabs?: Array<\n Omit<SidebarTab, 'id'> & {\n id: Exclude<string, DefaultSidebarTabIds>;\n }\n >;\n /**\n * Порядок отображения кастомных вкладок\n */\n customTabsOrder?: Array<Exclude<string, DefaultSidebarTabIds>>;\n defaultTabs?: Array<\n | {\n id: 'filtering' | 'columns';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара (если данный таб окажется единственным табом в настройках) */\n title?: string;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки (для табов с id = filtering и columns - недоступен. Настраивается через таб с id='tableSettings') */\n iconTooltipText?: undefined;\n }\n | {\n id: 'tableSettings';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара */\n title?: string;\n\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный слот для вкладки tableSettings (рендерится после общих настроек)\n * Только дл id: 'tableSettings'\n */\n customGeneralSettingsSlot?: React.ReactNode;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки @default 'Настройки' */\n iconTooltipText?: string;\n }\n >;\n};\n\nexport function activeViewIs<T extends 'cards' | 'rows'>(\n checkType: T,\n viewState: 'cards' | 'rows',\n view: View,\n): view is ViewMods[T] {\n return checkType === viewState;\n}\n\n/** Размер controlBlock: 'm' и 's' — одинаковые стили, 'xs' — уменьшенный */\nexport type ControlBlockSize = 'm' | 's' | 'xs';\n\nexport type ControlBlockConfig = {\n /**\n * show - наличие controlBlock-а\n * @default true\n */\n show?: boolean;\n /**\n * Размер controlBlock и его элементов (кнопки, иконки, поиск, отступы).\n * 'm' и 's' — стандартные стили, 'xs' — компактный режим.\n * Не влияет на customFeatures и кастомные слоты — они контролируются разработчиком.\n * @default 'm'\n */\n size?: ControlBlockSize;\n /**\n * @deprecated Используйте кастомные кнопки через rightSideInner или customFeatures.\n * leftSideInner - пропс для добавления кнопок в левую часть контролблока.\n * Продолжает работать для обратной совместимости.\n */\n leftSideInner?: ControlBlockButtonProps[];\n /**\n * rightSideInner - пропс для добавления кнопок в правую часть контролблока\n */\n rightSideInner?: ControlBlockButtonProps[];\n /**\n * customFeatures - пропс для добавления кастомных фичей (кнопки-иконки)\n */\n customFeatures?: FeatureItem[];\n /**\n * pinningMenu - нативная фича закрепления столбцов (SplitIconButton с\n * дропдауном) в правой зоне иконок. Появляется, когда активировано\n * закрепление колонок (columnsControl.pinning). Продукт может донастроить\n * и дополнить пункты меню (см. PinningMenuConfig).\n */\n pinningMenu?: PinningMenuConfig;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты левые фичи при адаптивной компрессии\n */\n leftSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты правые фичи при адаптивной компрессии\n */\n rightSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Активна ли фича компрессии (сжатие ToolsMenu при адаптиве)\n * @default true\n */\n enableAdaptiveCompress?: boolean;\n /**\n * Панель массовых действий\n */\n massActionPanel?: {\n /**\n * Кнопки панели. Размеры кнопок/дропдаунов панель подбирает сама (по `size` панели и компактности) —\n * передавать `size` кнопкам не нужно. Если `size` на кнопке всё же задан — он используется как есть.\n */\n buttons: MassActionButtonProps[];\n /**\n * Пропсы для Dropdown, в который сжались кнопки\n */\n collapsedDropdownProps?: TableDropdownConfigProps;\n /**\n * Отступ панели снизу от контейнера (px)\n * @default 24\n */\n bottom?: number;\n /**\n * Управление отображением панели массовых действий.\n * - `undefined` (по умолчанию) — стандартное поведение (показывается при selecting + выбранных строках)\n * - `false` — панель не рендерится, разработчик управляет действиями самостоятельно\n * - `true` — панель показывается всегда, даже без активного selecting и выбранных строк\n */\n show?: boolean;\n /**\n * Размерная сетка панели внутри таблицы: `m`/`s` — обычный размер, `xs` — уменьшенный (кнопки `xxs`, «ещё» `xs`, счётчик Body XS).\n * Viewport-логика (1280) в таблице не используется.\n * @default 's'\n */\n size?: MassActionsSize;\n };\n};\n\n/** Пропсы кнопки редактирования. onClick исключён — используйте onSave/onCancel/onEnableEditing. */\nexport type EditingButtonProps = Omit<\n Partial<ControlBlockButtonProps>,\n | 'onClick'\n | 'size'\n | 'dropdown'\n | 'isTargetAction'\n | 'skipRightDivider'\n | 'tooltipText'\n>;\n\nexport type EditingConfig<\n RowType extends ObjectForExtending,\n RowIdType,\n SummaryRowType,\n> = {\n /**\n * Функция обработки изменений в редактируемой строке\n */\n onRowsChange: Maybe<\n (rows: RowType[], data: RowsChangeData<RowType, SummaryRowType>) => void\n >;\n /**\n * свойство, необходимое для корректного изменения дочерних строк.\n * Данное свойство обязательно, если ваши дочерние строки установлены не под ключом \"subRows\" внутри строки.\n *\n * Как дочерние строки можно указывать свойство, которые вложено в другое свойство.\n * В таком случае \".\" будет знаком для обозначения сл. уровня вложенности.\n *\n * @example \"child\" или \"uvh367.children\"\n */\n subRowsKey?: string;\n /**\n * Глубокое копирование строк перед применением правки в режиме редактирования.\n *\n * Включите, если строки, передаваемые в таблицу, иммутабельны/заморожены\n * (напр. стейт под управлением Redux Toolkit или Immer — они вызывают\n * Object.freeze в dev). При редактировании дочерней строки таблица записывает\n * изменение ВНУТРЬ объекта родителя; на замороженном объекте это падает с\n * `TypeError: Cannot assign to read only property`.\n *\n * Когда флаг включён, таблица сама делает structuredClone редактируемой строки\n * перед записью — ваш иммутабельный стейт не мутируется, и редактирование\n * дочерних строк работает без действий с вашей стороны.\n *\n * ⚠️ Данные строки должны быть сериализуемыми. Где доступен structuredClone —\n * Date/Map/Set и вложенные структуры копируются корректно; на старых средах\n * без structuredClone используется JSON-фоллбэк (теряет Date/Map/Set, undefined\n * и функции). Не включайте без необходимости — глубокое копирование добавляет\n * накладные расходы.\n *\n * @default false\n */\n deepCloneRows?: boolean;\n /**\n * Функция определения уникального id строки\n * @param row\n * @returns\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Функция определения возможности редактирования строки\n * @param row\n * @returns\n */\n rowEditable?: (row: RowType) => boolean;\n /**\n * defaultEnabled - пропс, отвечающий за то, будет ли активен режим редактирования или нет. По умолчанию - не активно.\n * @default false\n */\n defaultEnabled?: boolean;\n /**\n * enabled - необходим для управления режимом редактирования снаружи компонента Таблицы.\n * По умолчанию, управление происходит в компоненте.\n * Приоритетнее, чем defaultEnabled.\n *\n * @type boolean - если НЕ нужно переключение в самой таблице\n * @type [boolean, React.Dispatch<React.SetStateAction<boolean>>] - если нужно переключение в самой таблице\n *\n */\n enabled?: boolean | [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * Cостояние скрытия/раскрытия кнопки включения/выключения фичи\n * @default true\n */\n showButtons?: boolean;\n /**\n * Функция, вызывающаяся при включении фичи.\n * @param enableEditorMode - функция включающая режим редактирования\n */\n onEnableEditing?: (enableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при сохранении отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onSave?: (disableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при отмене отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onCancel?: (disableEditorMode: () => void) => void;\n /** Пропсы кнопок в controlBlock. */\n buttons?: {\n save?: EditingButtonProps;\n cancel?: EditingButtonProps;\n edit?: EditingButtonProps;\n };\n /**\n * Кастомный слот, отображаемый левее кнопок «Отменить» / «Сохранить».\n * Виден только когда режим редактирования активен (editMode = true).\n * Зона ответственности разработчика — размерная сетка controlBlock не влияет на этот слот.\n */\n editModeLeftSlot?: React.ReactNode;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: {\n enableButton?: DomMetadata;\n saveButton?: DomMetadata;\n cancelButton?: DomMetadata;\n };\n};\n\nexport type SummaryRowsConfig = {\n showDefault: boolean;\n showInControl: boolean;\n} & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\n/**\n * Конфиг тултипа при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\nexport type TableConfigTooltip = {\n /**\n * Включить/выключить колоночные и кастомные тултипы (cellTooltip, headerCellTooltip).\n * Внутренние тултипы (drag-иконка в шапке и другие по nodeId) работают всегда.\n * @default false\n */\n enabled?: boolean;\n /** Глобальная задержка перед показом тултипа (мс). Per-node переопределяет. @default 500 */\n mouseEnterDelay?: number;\n /** Глобальная задержка перед скрытием тултипа (мс). Per-node переопределяет. @default 0 */\n mouseLeaveDelay?: number;\n};\n\nexport type InfinityScrollConfig<RowType extends ObjectForExtending> = {\n /** Триггер достижения скроллом зоны подгрузки данных. */\n onTrigger: (rows: RowType[]) => Promise<void> | void;\n /** Состояние загрузки (скелетоны) новых строк. */\n isLoading: boolean;\n /**\n * Флаг, когда все данные загружены — false.\n * @default true\n */\n hasMore?: boolean;\n /**\n * Для mode='region': за сколько строк до конца триггерить загрузку.\n * @default 5\n */\n rowThreshold?: number;\n};\n\nexport type { TableCanvasEmptyStateConfig, TableCanvasErrorStateConfig };\n\n/**\n * Настройка выделения ячеек/колонок/строк в TableCanvas — единый объект.\n * Это отдельная ось взаимодействия — не зависит от `highlightActiveType`.\n */\nexport type CellsSelectionConfig = {\n /**\n * Режим фактического выделения ячеек (нативный glide selection). По нему\n * работают copy/paste, рамка, fill-handle и затемнение шапки/нумерации.\n * @default \"range-cell\"\n */\n mode?: CellsSelectionMode;\n /**\n * Выделение колонок по клику на шапку. Ctrl/Cmd — добавить/убрать колонку,\n * Shift — диапазон колонок. Клик по иконкам сортировки/фильтрации в шапке\n * выделение НЕ вызывает. Поддерживает copy/paste по выделенным колонкам.\n * @default true\n */\n enableColumnSelection?: boolean;\n /**\n * Выделение строк по клику/драгу на колонке нумерации (`rowMarkers`).\n * Ctrl/Cmd — несмежный выбор отдельных строк, драг — смежный диапазон.\n * Поддерживает copy/paste по выделенным строкам. Требует включённой колонки\n * нумерации (`rowMarkers`). При `false` клик по нумерации ничего не выделяет.\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Выделение всей таблицы кликом по «нулевой» ячейке — шапке колонки нумерации\n * (левый верхний угол). Выделяет все строки данных по всем колонкам одним\n * диапазоном; поддерживает copy/paste. Требует включённой колонки нумерации\n * (`rowMarkers`) и активного `mode` (≠ `disabled`).\n * @default true\n */\n enableSelectAll?: boolean;\n /**\n * Внешний (controlled) контроль НАТИВНОГО выделения (ячейки/диапазон/смежные\n * строки и колонки). `state` — кортеж `[value, setter]` как у `selecting.state`.\n * Сброс: `setter({ current: undefined, rows: empty, columns: empty })`.\n * Несмежный Ctrl-выбор колонок/строк (own-state) сюда не входит — он внутренний.\n */\n state?: [GridSelection, React.Dispatch<React.SetStateAction<GridSelection>>];\n};\n\nexport type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *"
52982
+ "source": "type FilteringConfig<T> = {\n state: [T, React.Dispatch<React.SetStateAction<T>>];\n /**\n * @deprecated Использовать filtersInfo\n *\n * clearedValue - состояние фильтров после очистки. Используется для сравнения:\n * Например, был ли фильтр конкретной колонки изменен рассчитывается посредством сравнения текущего значения фильтра и значения фильтра в clearedValue.\n * Если они равны, то фильтр считается не измененным.\n */\n clearedValue?: T;\n /**\n * Информация о фильтрах для каждой колонки.\n * Используется для:\n * 1. Хранения меток (label) фильтров для отображения в UI\n * 2. Хранения значений по умолчанию (clearedValue) для каждого фильтра\n * 3. Определения, был ли фильтр изменен (сравнение текущего значения с clearedValue)\n */\n filtersInfo?: Record<\n keyof T,\n {\n label: string;\n clearedValue: T[keyof T];\n }\n >;\n /**\n * manualFiltering - пропс, активирующий ручную-кастомную (или на стороне бэка) фильтрацию.\n * @remarks\n * Для автоматической фильтрации (когда не используется manualFiltering в tableConfig):\n * - Для всех типов фильтров обязательно должен быть указан `valueInRow`\n * - Для component: 'select' дополнительно должен быть указан `filteringType`\n * - Для component: 'input' с кастомным фильтром (когда filter - функция)\n * функция фильтрации должна быть передана явно\n *\n * Если используется manualFiltering, эти параметры не обязательны,\n * так как вся логика фильтрации реализуется извне.\n */\n manualFiltering?: boolean;\n /**\n * Конфигурация для отображения фильтров в правом сайдбаре.\n *\n * Позволяет:\n * 1. Добавлять глобальные фильтры (не привязанные к конкретной колонке)\n * 2. Управлять порядком отображения фильтров\n * 3. Переопределять рендер и заголовки колоночных фильтров\n *\n * Логика работы:\n * - По умолчанию в сайдбаре отображаются все колоночные фильтры (из columnConfig)\n * - Ключом фильтра считается `key` колонки, если его нет, то пытаемся взять `keyInFilterState` (если указан), иначе это не будет работать\n * - Заголовком фильтра по умолчанию является `name` колонки или ключ фильтра (name || key)\n * - Глобальные фильтры добавляются через `items` с уникальными ключами\n *\n * Особенности:\n * 1. Порядок отображения:\n * - Сначала фильтры из `order` (в указанной последовательности)\n * - Затем остальные колоночные фильтры (в порядке columnConfig)\n * - В конце - глобальные фильтры (в алфавитном порядке ключей)\n *\n * 2. Переопределение колоночных фильтров:\n * - Чтобы изменить заголовок: укажите ключ колонки в `items` с новым `label`\n * - Чтобы заменить рендер: укажите ключ колонки с `customRenderFn`\n * - Оригинальный фильтр из колонки будет проигнорирован\n *\n * 3. Глобальные фильтры:\n * - Ключ не должен совпадать с ключами колонок\n * - Обязательно указать `label` и `customRenderFn`\n * - Отображаются после всех колоночных фильтров, если не переопределены в order\n *\n * @example\n * // Добавление глобального фильтра и изменение порядка\n * sidebarConfig: {\n * order: ['status', 'tasks', 'type'], // Сначала статус, затем глобальный поиск\n * items: {\n * tasks: { // Уникальный ключ\n * label: 'Поиск по всем полям',\n * customRenderFn: () => <TaskComponent />\n * },\n * status: { // Переопределение колоночного фильтра\n * label: 'Статус заявки', // Новый заголовок\n * customRenderFn: () => <CustomStatusFilter /> // Кастомный рендер\n * }\n * type: { // Переопределение только Label колоночного фильтра\n * label: 'Тип', // Новый заголовок\n * }\n * }\n * }\n */\n sidebarConfig?: {\n /**\n * Определяет порядок отображения фильтров.\n * - Указываются ключи фильтров (колоночных или глобальных)\n * - Не указанные в order фильтры отображаются после\n * - Если order пуст, сохраняется порядок: глобальные фильтры + columnConfig фильтры в конце\n */\n order?: string[];\n /**\n * Конфигурация фильтров для сайдбара.\n * Ключом может быть:\n * - Ключ колонки (для переопределения колоночного фильтра)\n * - Уникальный ключ (для добавления глобального фильтра)\n */\n items: Partial<\n Record<\n keyof T,\n {\n /** Заголовок фильтра (обязателен для глобальных фильтров), можно использовать для переопределения для локальных фильтров */\n label: string;\n /**\n * Функция рендера компонента фильтра.\n * Для колоночных фильтров переопределяет оригинальный рендер.\n * Для глобальных фильтров обязательна.\n */\n customRenderFn?: (\n filters: T,\n setFilters: React.Dispatch<React.SetStateAction<T>>,\n ) => React.ReactNode;\n }\n >\n >;\n };\n /**\n * Callback для кастомизации стилей чипсов (фильтров) в списке\n *\n * @param {ItemOrGroup} itemOrGroup - Текущий элемент или группа фильтров\n * @param {Item | undefined} item - Конкретный элемент фильтра (если есть)\n * @returns {CSSObject} Объект стилей, который будет применен к чипсу\n *\n * @description\n * Позволяет динамически задавать стили для каждого чипса в зависимости от его типа и содержимого.\n * Вызывается для каждого отображаемого чипса (как для отдельных элементов, так и для элементов внутри групп).\n *\n * @example\n * // Пример 1: Стилизация чипсов по типу\n * chipStyle: (itemOrGroup, item) => {\n * if (isGroup(itemOrGroup)) {\n * return { backgroundColor: '#f0f0f0' };\n * }\n * return { backgroundColor: '#e0e0e0' };\n * }\n *\n * @example\n * // Пример 2: Стилизация по значению фильтра\n * chipStyle: (itemOrGroup, item) => {\n * if (item?.label === 'Высокий приоритет') {\n * return {\n * backgroundColor: '#ffebee',\n * color: '#d32f2f'\n * };\n * }\n * return {};\n * }\n *\n * @example\n * // Пример 3: Разные стили для элементов в группе и одиночных\n * chipStyle: (itemOrGroup, item) => {\n * if (item) {\n * // Стиль для элемента внутри группы\n * return { maxWidth: 180 };\n * }\n * // Стиль для одиночного чипа\n * return { maxWidth: 150 };\n * }\n */\n chipStyle?: (itemOrGroup: ItemOrGroup, item: Item | undefined) => CSSObject;\n /**\n * Пользовательский форматтер заголовка группы чипсов.\n *\n * Используется в блоке активных фильтров для подмены текста заголовка\n * группы (например, “Статус”, “Тип”, “Автор”). Полезно, когда нужно\n * динамически менять подпись (добавить счётчик, i18n и т.п.).\n *\n * @param {ItemOrGroup} itemOrGroup Объект группы (тип Group). Вызов происходит только для групп,\n * поэтому безопасно кастовать к Group.\n * @returns {string} Текст заголовка группы чипсов.\n *\n * @default Возвращается `group.groupLabel`.\n *\n * @remarks\n * - Функция должна быть чистой и быстрой (без сетевых запросов и побочных эффектов).\n * - Возвращайте ТОЛЬКО строку — чипы отображают текстовое значение.\n * - Для длинных строк применяется усечение (overflow с многоточием) на уровне компонента.\n *\n * @example\n * // Добавить двоеточие и количество выбранных элементов\n * renderGroupLabel: (group) => `${group.groupLabel}: ${group.items.length}`\n *\n * @example\n * // Локализация заголовка группы\n * const i18n = { status: 'Статус', type: 'Тип' };\n * renderGroupLabel: (group) => i18n[group.groupId] ?? group.groupLabel\n */\n renderGroupLabel?: (group: Group) => string;\n\n /**\n * Пользовательский форматтер текста чипса (элемента внутри группы или одиночного фильтра).\n *\n * Позволяет управлять тем, что именно отображается внутри каждого чипса. Особенно полезен\n * при серверной пагинации/ленивой загрузке, когда в состоянии фильтра хранится только value,\n * а label необходимо подставить из локального кэша.\n *\n * @param {Group | null } group Группа, внутри которой рендерится чип (или null).\n * @param {Item} item Сам элемент чипса. В группах передаётся конкретный `Item`,\n * для одиночного чипа может быть `undefined`.\n * @returns {string} Текст, который будет показан в чипсе.\n *\n * @default Используется `item?.label.toString()` (или строковое представление value).\n *\n * @remarks\n * - Функция должна быть чистой и быстрой;\n * - Возвращайте ТОЛЬКО строку.\n *\n * @example\n * // 1) Отображение label из кэша при серверной пагинации\n * const labelsCacheRef = useRef(new Map<string, string>());\n * renderChipLabel: (group, item) => {\n * if (item?.id != null) {\n * const key = String(item.id);\n * const fromCache = labelsCacheRef.current.get(key);\n * if (fromCache) return fromCache; // красивый label, если уже загрузили\n * return String(item.label ?? key); // фоллбэк: временно показываем value/label из item\n * }\n * return ''; // безопасный фоллбэк для одиночных случаев\n * }\n *\n * @example\n * // 2) Нормализация отображения числовых значений\n * renderChipLabel: (_group, item) => {\n * const v = Number(item?.label);\n * return Number.isFinite(v) ? v.toLocaleString('ru-RU') : String(item?.label ?? '');\n * }\n *\n * @example\n * // 3) Префикс по типу группы\n * renderChipLabel: (group, item) => {\n * const prefix = group.groupId === 'status' ? '' : '#';\n * return `${prefix}${item?.label ?? item?.id ?? ''}`;\n * }\n */\n renderChipLabel?: (group: Group | null, item: Item) => string;\n};\n\nexport type DefaultSidebarTabIds = 'columns' | 'tableSettings' | 'filtering';\n\n/**\n * Конфигурация sidebar\n */\nexport type SidebarConfig = {\n /**\n * Включен ли sidebar. Отвечает только за UI. Не влияет на перерасчёт фичей.\n * @default true\n */\n enabled?: boolean;\n /**\n * Открыт ли sidebar при первом рендере (uncontrolled-режим).\n * Игнорируется, если передан openState.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Внешний контроль открытия/закрытия sidebar.\n * Если передан — источник истины, defaultOpen игнорируется.\n */\n openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * id вкладки, выбранной по умолчанию (актуально при открытом сайдбаре).\n * Если id не найден или у вкладки showInSidebar=false — используется первая доступная.\n */\n defaultActiveTabId?: string;\n /**\n * Внешний контроль активной вкладки (controlled-режим).\n * Если передан — источник истины, defaultActiveTabId игнорируется.\n */\n activeTabState?: [\n string | null,\n React.Dispatch<React.SetStateAction<string | null>>,\n ];\n /**\n * Колбэк смены активной вкладки. Вызывается и в uncontrolled-режиме.\n * При закрытии сайдбара приходит null.\n */\n onActiveTabChange?: (tabId: string | null, tab?: SidebarTab) => void;\n /**\n * Кастомные табы\n */\n customTabs?: Array<\n Omit<SidebarTab, 'id'> & {\n id: Exclude<string, DefaultSidebarTabIds>;\n }\n >;\n /**\n * Порядок отображения кастомных вкладок\n */\n customTabsOrder?: Array<Exclude<string, DefaultSidebarTabIds>>;\n defaultTabs?: Array<\n | {\n id: 'filtering' | 'columns';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара (если данный таб окажется единственным табом в настройках) */\n title?: string;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки (для табов с id = filtering и columns - недоступен. Настраивается через таб с id='tableSettings') */\n iconTooltipText?: undefined;\n }\n | {\n id: 'tableSettings';\n /** название таба в рамках горизонтального табов внутри таба настройки */\n label?: string;\n /** название таба в рамках вертикальных табов сайдбара */\n title?: string;\n\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n /**\n * Кастомный слот для вкладки tableSettings (рендерится после общих настроек)\n * Только дл id: 'tableSettings'\n */\n customGeneralSettingsSlot?: React.ReactNode;\n /**\n * Кастомный элемент справа от title в шапке сайдбара\n */\n titleRightSlot?: React.ReactNode;\n /** @default true */\n showInSidebar?: boolean;\n /** тултип иконки шестеренки @default 'Настройки' */\n iconTooltipText?: string;\n }\n >;\n};\n\nexport function activeViewIs<T extends 'cards' | 'rows'>(\n checkType: T,\n viewState: 'cards' | 'rows',\n view: View,\n): view is ViewMods[T] {\n return checkType === viewState;\n}\n\n/** Размер controlBlock: 'm' и 's' — одинаковые стили, 'xs' — уменьшенный */\nexport type ControlBlockSize = 'm' | 's' | 'xs';\n\nexport type ControlBlockConfig = {\n /**\n * show - наличие controlBlock-а\n * @default true\n */\n show?: boolean;\n /**\n * Размер controlBlock и его элементов (кнопки, иконки, поиск, отступы).\n * 'm' и 's' — стандартные стили, 'xs' — компактный режим.\n * Не влияет на customFeatures и кастомные слоты — они контролируются разработчиком.\n * @default 'm'\n */\n size?: ControlBlockSize;\n /**\n * @deprecated Используйте кастомные кнопки через rightSideInner или customFeatures.\n * leftSideInner - пропс для добавления кнопок в левую часть контролблока.\n * Продолжает работать для обратной совместимости.\n */\n leftSideInner?: ControlBlockButtonProps[];\n /**\n * rightSideInner - пропс для добавления кнопок в правую часть контролблока\n */\n rightSideInner?: ControlBlockButtonProps[];\n /**\n * customFeatures - пропс для добавления кастомных фичей (кнопки-иконки)\n */\n customFeatures?: FeatureItem[];\n /**\n * pinningMenu - нативная фича закрепления столбцов (SplitIconButton с\n * дропдауном) в правой зоне иконок. Появляется, когда активировано\n * закрепление колонок (columnsControl.pinning). Продукт может донастроить\n * и дополнить пункты меню (см. PinningMenuConfig).\n */\n pinningMenu?: PinningMenuConfig;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты левые фичи при адаптивной компрессии\n */\n leftSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Пропсы для компонента dropdown в котором будут скрыты правые фичи при адаптивной компрессии\n */\n rightSideDropdownProps?: TableDropdownConfigProps;\n /**\n * Активна ли фича компрессии (сжатие ToolsMenu при адаптиве)\n * @default true\n */\n enableAdaptiveCompress?: boolean;\n /**\n * Панель массовых действий\n */\n massActionPanel?: {\n /**\n * Кнопки панели. Размеры кнопок/дропдаунов панель подбирает сама (по `size` панели и компактности) —\n * передавать `size` кнопкам не нужно. Если `size` на кнопке всё же задан — он используется как есть.\n */\n buttons: MassActionButtonProps[];\n /**\n * Пропсы для Dropdown, в который сжались кнопки\n */\n collapsedDropdownProps?: TableDropdownConfigProps;\n /**\n * Отступ панели снизу от контейнера (px)\n * @default 24\n */\n bottom?: number;\n /**\n * Управление отображением панели массовых действий.\n * - `undefined` (по умолчанию) — стандартное поведение (показывается при selecting + выбранных строках)\n * - `false` — панель не рендерится, разработчик управляет действиями самостоятельно\n * - `true` — панель показывается всегда, даже без активного selecting и выбранных строк\n */\n show?: boolean;\n /**\n * Размерная сетка панели внутри таблицы: `m`/`s` — обычный размер, `xs` — уменьшенный (кнопки `xxs`, «ещё» `xs`, счётчик Body XS).\n * Viewport-логика (1280) в таблице не используется.\n * @default 's'\n */\n size?: MassActionsSize;\n };\n};\n\n/** Пропсы кнопки редактирования. onClick исключён — используйте onSave/onCancel/onEnableEditing. */\nexport type EditingButtonProps = Omit<\n Partial<ControlBlockButtonProps>,\n | 'onClick'\n | 'size'\n | 'dropdown'\n | 'isTargetAction'\n | 'skipRightDivider'\n | 'tooltipText'\n>;\n\nexport type EditingConfig<\n RowType extends ObjectForExtending,\n RowIdType,\n SummaryRowType,\n> = {\n /**\n * Функция обработки изменений в редактируемой строке\n */\n onRowsChange: Maybe<\n (rows: RowType[], data: RowsChangeData<RowType, SummaryRowType>) => void\n >;\n /**\n * свойство, необходимое для корректного изменения дочерних строк.\n * Данное свойство обязательно, если ваши дочерние строки установлены не под ключом \"subRows\" внутри строки.\n *\n * Как дочерние строки можно указывать свойство, которые вложено в другое свойство.\n * В таком случае \".\" будет знаком для обозначения сл. уровня вложенности.\n *\n * @example \"child\" или \"uvh367.children\"\n */\n subRowsKey?: string;\n /**\n * Глубокое копирование строк перед применением правки в режиме редактирования.\n *\n * Включите, если строки, передаваемые в таблицу, иммутабельны/заморожены\n * (напр. стейт под управлением Redux Toolkit или Immer — они вызывают\n * Object.freeze в dev). При редактировании дочерней строки таблица записывает\n * изменение ВНУТРЬ объекта родителя; на замороженном объекте это падает с\n * `TypeError: Cannot assign to read only property`.\n *\n * Когда флаг включён, таблица сама делает structuredClone редактируемой строки\n * перед записью — ваш иммутабельный стейт не мутируется, и редактирование\n * дочерних строк работает без действий с вашей стороны.\n *\n * ⚠️ Данные строки должны быть сериализуемыми. Где доступен structuredClone —\n * Date/Map/Set и вложенные структуры копируются корректно; на старых средах\n * без structuredClone используется JSON-фоллбэк (теряет Date/Map/Set, undefined\n * и функции). Не включайте без необходимости — глубокое копирование добавляет\n * накладные расходы.\n *\n * @default false\n */\n deepCloneRows?: boolean;\n /**\n * Функция определения уникального id строки\n * @param row\n * @returns\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Функция определения возможности редактирования строки\n * @param row\n * @returns\n */\n rowEditable?: (row: RowType) => boolean;\n /**\n * defaultEnabled - пропс, отвечающий за то, будет ли активен режим редактирования или нет. По умолчанию - не активно.\n * @default false\n */\n defaultEnabled?: boolean;\n /**\n * enabled - необходим для управления режимом редактирования снаружи компонента Таблицы.\n * По умолчанию, управление происходит в компоненте.\n * Приоритетнее, чем defaultEnabled.\n *\n * @type boolean - если НЕ нужно переключение в самой таблице\n * @type [boolean, React.Dispatch<React.SetStateAction<boolean>>] - если нужно переключение в самой таблице\n *\n */\n enabled?: boolean | [boolean, React.Dispatch<React.SetStateAction<boolean>>];\n /**\n * Cостояние скрытия/раскрытия кнопки включения/выключения фичи\n * @default true\n */\n showButtons?: boolean;\n /**\n * Функция, вызывающаяся при включении фичи.\n * @param enableEditorMode - функция включающая режим редактирования\n */\n onEnableEditing?: (enableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при сохранении отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onSave?: (disableEditorMode: () => void) => void;\n /**\n * Функция, вызывающаяся при отмене отредактированного.\n * @param disableEditorMode - функция выключающая режим редактирования\n */\n onCancel?: (disableEditorMode: () => void) => void;\n /** Пропсы кнопок в controlBlock. */\n buttons?: {\n save?: EditingButtonProps;\n cancel?: EditingButtonProps;\n edit?: EditingButtonProps;\n };\n /**\n * Кастомный слот, отображаемый левее кнопок «Отменить» / «Сохранить».\n * Виден только когда режим редактирования активен (editMode = true).\n * Зона ответственности разработчика — размерная сетка controlBlock не влияет на этот слот.\n */\n editModeLeftSlot?: React.ReactNode;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: {\n enableButton?: DomMetadata;\n saveButton?: DomMetadata;\n cancelButton?: DomMetadata;\n };\n};\n\nexport type SummaryRowsConfig = {\n showDefault: boolean;\n showInControl: boolean;\n} & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\n/**\n * Конфиг тултипа при hover по canvas-элементам.\n * Для активации фичи необходимо заполнить данный конфиг,\n * а также добавить нужные изменения в Canvas-элементах или в конфиге колонки.\n */\nexport type TableConfigTooltip = {\n /**\n * Включить/выключить колоночные и кастомные тултипы (cellTooltip, headerCellTooltip).\n * Внутренние тултипы (drag-иконка в шапке и другие по nodeId) работают всегда.\n * @default false\n */\n enabled?: boolean;\n /** Глобальная задержка перед показом тултипа (мс). Per-node переопределяет. @default 500 */\n mouseEnterDelay?: number;\n /** Глобальная задержка перед скрытием тултипа (мс). Per-node переопределяет. @default 0 */\n mouseLeaveDelay?: number;\n};\n\nexport type InfinityScrollConfig<RowType extends ObjectForExtending> = {\n /** Триггер достижения скроллом зоны подгрузки данных. */\n onTrigger: (rows: RowType[]) => Promise<void> | void;\n /** Состояние загрузки (скелетоны) новых строк. */\n isLoading: boolean;\n /**\n * Флаг, когда все данные загружены — false.\n * @default true\n */\n hasMore?: boolean;\n /**\n * Для mode='region': за сколько строк до конца триггерить загрузку.\n * @default 5\n */\n rowThreshold?: number;\n};\n\nexport type { TableCanvasEmptyStateConfig, TableCanvasErrorStateConfig };\n\n/**\n * Настройка выделения ячеек/колонок/строк в TableCanvas — единый объект.\n * Это отдельная ось взаимодействия — не зависит от `highlightActiveType`.\n */\nexport type CellsSelectionConfig = {\n /**\n * Режим фактического выделения ячеек (нативный glide selection). По нему\n * работают copy/paste, рамка, fill-handle и затемнение шапки/нумерации.\n * @default \"range-cell\"\n */\n mode?: CellsSelectionMode;\n /**\n * Выделение колонок по клику на шапку. Ctrl/Cmd — добавить/убрать колонку,\n * Shift — диапазон колонок. Клик по иконкам сортировки/фильтрации в шапке\n * выделение НЕ вызывает. Поддерживает copy/paste по выделенным колонкам.\n * @default true\n */\n enableColumnSelection?: boolean;\n /**\n * Выделение строк по клику/драгу на колонке нумерации (`rowMarkers`).\n * Ctrl/Cmd — несмежный выбор отдельных строк, драг — смежный диапазон.\n * Поддерживает copy/paste по выделенным строкам. Требует включённой колонки\n * нумерации (`rowMarkers`). При `false` клик по нумерации ничего не выделяет.\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Выделение всей таблицы кликом по «нулевой» ячейке — шапке колонки нумерации\n * (левый верхний угол). Выделяет все строки данных по всем колонкам одним\n * диапазоном; поддерживает copy/paste. Требует включённой колонки нумерации\n * (`rowMarkers`) и активного `mode` (≠ `disabled`).\n * @default true\n */\n enableSelectAll?: boolean;\n /**\n * Внешний (controlled) контроль НАТИВНОГО выделения (ячейки/диапазон/смежные\n * строки и колонки). `state` — кортеж `[value, setter]` как у `selecting.state`.\n * Сброс: `setter({ current: undefined, rows: empty, columns: empty })`.\n * Несмежный Ctrl-выбор колонок/строк (own-state) сюда не входит — он внутренний.\n */\n state?: [GridSelection, React.Dispatch<React.SetStateAction<GridSelection>>];\n};\n\n/** Один объединённый блок: какие строки и колонки слить. */\nexport type MergedCellsRegion = {\n rowKeys: Array<string | number>;\n colKeys: string[];\n /** Выравнивание контента этого блока. Важнее колоночного и общего. */\n mergedCellsAlign?: MergedCellsAlign;\n};\n\nexport type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *"
52841
52983
  },
52842
52984
  {
52843
52985
  "typeName": "FilterComponentInPopoverProps",
@@ -52938,7 +53080,7 @@
52938
53080
  "exportName": "HighlightActiveRowControlled",
52939
53081
  "displayName": "Контролируемая подсветка строки (controlled)",
52940
53082
  "type": "full-code",
52941
- "code": "import {\n type ColumnConfig,\n type HighlightActiveType,\n type CellsSelectionMode,\n type SIZE_GLIDE_INSTANCE as RowSize,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst HIGHLIGHT_ACTIVE_TYPE_OPTIONS: readonly HighlightActiveType[] = [\n 'row',\n 'disabled',\n];\nconst SELECTION_MODE_OPTIONS: readonly CellsSelectionMode[] = [\n 'range-cell',\n 'multi-range-cell',\n 'cell',\n 'disabled',\n];\nconst ROW_SIZE_OPTIONS: readonly RowSize[] = ['big', 'medium', 'small'];\nconst ROW_MARKER_WIDTH_BY_SIZE: Record<RowSize, number> = {\n big: 48,\n medium: 32,\n small: 24,\n};\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\nconst getRowSelectionKey = (row: Row) => row.id + row.issueType;\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo<readonly ColumnConfig<TableRow>[]>(\n () => COLUMN_CONFIG,\n [],\n );\n\n const highlightActiveRowState = useState<number | undefined>(undefined);\n const [highlightActiveRow, setHighlightActiveRow] = highlightActiveRowState;\n\n return (\n <>\n <div\n style={{\n display: 'flex',\n gap: 8,\n flexWrap: 'wrap',\n marginBottom: 12,\n }}\n >\n <Button onClick={() => setHighlightActiveRow(2)}>\n Подсветить строку №3\n </Button>\n <Button onClick={() => setHighlightActiveRow(undefined)}>\n Сбросить подсветку строки\n </Button>\n </div>\n\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Подсветка активной строки — <b>controlled</b> через{' '}\n <code>tableConfig.highlightActiveRow.state</code>. Кнопки\n задают/сбрасывают внешний стейт; клик по строке тоже обновляет его.\n Подсвеченная строка: <b>{highlightActiveRow ?? '—'}</b>.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n highlightActiveType: 'row',\n rowMarkers: { startIndex: 1 },\n highlightActiveRow: { state: highlightActiveRowState },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </>\n );\n }"
53083
+ "code": "import {\n type ColumnConfig,\n type HighlightActiveType,\n type CellsSelectionMode,\n type SIZE_GLIDE_INSTANCE as RowSize,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst HIGHLIGHT_ACTIVE_TYPE_OPTIONS: readonly HighlightActiveType[] = [\n 'row',\n 'disabled',\n];\nconst SELECTION_MODE_OPTIONS: readonly CellsSelectionMode[] = [\n 'range-cell',\n 'multi-range-cell',\n 'cell',\n 'disabled',\n];\nconst ROW_SIZE_OPTIONS: readonly RowSize[] = ['big', 'medium', 'small'];\nconst ROW_MARKER_WIDTH_BY_SIZE: Record<RowSize, number> = {\n big: 48,\n medium: 32,\n small: 24,\n};\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\nconst getRowSelectionKey = (row: Row) => row.id + row.issueType;\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo<readonly ColumnConfig<TableRow>[]>(\n () => COLUMN_CONFIG,\n [],\n );\n\n const highlightActiveRowState = useState<number | undefined>(undefined);\n const [highlightActiveRow, setHighlightActiveRow] = highlightActiveRowState;\n\n return (\n <>\n <div\n style={{\n display: 'flex',\n gap: 8,\n flexWrap: 'wrap',\n marginBottom: 12,\n }}\n >\n <Button onClick={() => setHighlightActiveRow(2)}>\n Подсветить строку №3\n </Button>\n <Button onClick={() => setHighlightActiveRow(undefined)}>\n Сбросить подсветку строки\n </Button>\n </div>\n\n <StoryHint>\n Подсветка активной строки — <b>controlled</b> через{' '}\n <code>tableConfig.highlightActiveRow.state</code>. Кнопки\n задают/сбрасывают внешний стейт; клик по строке тоже обновляет его.\n Подсвеченная строка: <b>{highlightActiveRow ?? '—'}</b>.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n highlightActiveType: 'row',\n rowMarkers: { startIndex: 1 },\n highlightActiveRow: { state: highlightActiveRowState },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </>\n );\n }"
52942
53084
  },
52943
53085
  {
52944
53086
  "exportName": "HighlightActiveTypeSplitView",
@@ -53047,7 +53189,7 @@
53047
53189
  "exportName": "Notifications",
53048
53190
  "displayName": "Подписка на события (onNotification)",
53049
53191
  "type": "full-code",
53050
- "code": "() => {\n const [rows, setRows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMNS, []);\n const [log, setLog] = useState<TableNotification[]>([]);\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Режим <b>multi-range-cell</b>. Как спровоцировать события:\n <br />• <b>copy / multi-range-scattered</b> — Ctrl-выдели ячейки в\n РАЗНЫХ строках и колонках → Ctrl+C.\n <br />• <b>paste / multi-range-scattered</b> — тот же 2D-выбор →\n Ctrl+V.\n <br />• <b>paste / readonly-abort</b> — вставь в диапазон, задевающий\n колонку ID.\n <br />• <b>paste / overflow-abort</b> — вставь большой блок у\n нижнего/правого края.\n <br />• <b>paste / validation-skipped</b> — вставь текст в колонку «%\n (число)».\n <br />• <b>fill / readonly-abort</b>, <b>fill / validation-skipped</b>\n — то же протаскиванием fill-handle.\n </p>\n\n <div style={{ display: 'flex', gap: 16 }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '520px' },\n cellsSelection: { mode: 'multi-range-cell' },\n rowMarkers: { startIndex: 1 },\n cellTransfer: {\n enabled: true,\n paste: {\n readonlyBehavior: 'abort',\n overflowBehavior: 'abort',\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n notifications: {\n onNotification: (event) =>\n setLog((prev) => [event, ...prev].slice(0, 20)),\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n\n <div style={{ width: 340, fontSize: 12 }}>\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: 8,\n }}\n >\n <b>Полученные события ({log.length})</b>\n <button\n type=\"button\"\n onClick={() => setLog([])}\n style={{\n padding: '4px 10px',\n borderRadius: 8,\n border: '1px solid #c7d2e0',\n background: '#fff',\n cursor: 'pointer',\n }}\n >\n Очистить\n </button>\n </div>\n {log.length === 0 && (\n <div style={{ color: '#8893a5' }}>Пока пусто</div>\n )}\n {log.map((event, i) => (\n <div\n key={i}\n style={{\n borderLeft: `3px solid ${LEVEL_COLOR[event.level]}`,\n padding: '6px 10px',\n marginBottom: 6,\n background: '#fafbfc',\n }}\n >\n <div\n style={{ color: LEVEL_COLOR[event.level], fontWeight: 600 }}\n >\n {event.type} · {event.code} · {event.level}\n </div>\n <div style={{ color: '#556' }}>{event.message}</div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n }"
53192
+ "code": "() => {\n const [rows, setRows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMNS, []);\n const [log, setLog] = useState<TableNotification[]>([]);\n\n return (\n <div>\n <StoryHint>\n Режим <b>multi-range-cell</b>. Как спровоцировать события:\n <br />• <b>copy / multi-range-scattered</b> — Ctrl-выдели ячейки в\n РАЗНЫХ строках и колонках → Ctrl+C.\n <br />• <b>paste / multi-range-scattered</b> — тот же 2D-выбор →\n Ctrl+V.\n <br />• <b>paste / readonly-abort</b> — вставь в диапазон, задевающий\n колонку ID.\n <br />• <b>paste / overflow-abort</b> — вставь большой блок у\n нижнего/правого края.\n <br />• <b>paste / validation-skipped</b> — вставь текст в колонку «%\n (число)».\n <br />• <b>fill / readonly-abort</b>, <b>fill / validation-skipped</b>\n — то же протаскиванием fill-handle.\n </StoryHint>\n\n <div style={{ display: 'flex', gap: 16 }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '520px' },\n cellsSelection: { mode: 'multi-range-cell' },\n rowMarkers: { startIndex: 1 },\n cellTransfer: {\n enabled: true,\n paste: {\n readonlyBehavior: 'abort',\n overflowBehavior: 'abort',\n },\n },\n editing: {\n onRowsChange: setRows,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n notifications: {\n onNotification: (event) =>\n setLog((prev) => [event, ...prev].slice(0, 20)),\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n\n <div style={{ width: 340, fontSize: 12 }}>\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: 8,\n }}\n >\n <b>Полученные события ({log.length})</b>\n <button\n type=\"button\"\n onClick={() => setLog([])}\n style={{\n padding: '4px 10px',\n borderRadius: 8,\n border: '1px solid #c7d2e0',\n background: '#fff',\n cursor: 'pointer',\n }}\n >\n Очистить\n </button>\n </div>\n {log.length === 0 && (\n <div style={{ color: '#8893a5' }}>Пока пусто</div>\n )}\n {log.map((event, i) => (\n <div\n key={i}\n style={{\n borderLeft: `3px solid ${LEVEL_COLOR[event.level]}`,\n padding: '6px 10px',\n marginBottom: 6,\n background: '#fafbfc',\n }}\n >\n <div\n style={{ color: LEVEL_COLOR[event.level], fontWeight: 600 }}\n >\n {event.type} · {event.code} · {event.level}\n </div>\n <div style={{ color: '#556' }}>{event.message}</div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n }"
53051
53193
  }
53052
53194
  ]
53053
53195
  },
@@ -53149,11 +53291,11 @@
53149
53291
  "legacy": false,
53150
53292
  "summary": "*tableConfig.rowMarkers**",
53151
53293
  "docs": "# RowMarkers (TableCanvas)\n\n**tableConfig.rowMarkers**\n\nСервисный столбец с нумерацией строк. Frozen, вставляется первым, исключён из reorder/pin/hide/sort/grouping. Поддерживает плоский и древовидный (`subRows`) режимы любой глубины вложенности.\n\n## Быстрый старт\n\n```tsx\n<TableCanvas\n tableConfig={{\n rowMarkers: {\n startIndex: 1,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n/>\n```\n\n## `flatIndex` vs `rowIndex`\n\nЕсли ветка свернута, но нужно сохранить «сквозные» номера — используйте `flatIndex`.\n\n- полностью раскрыто: `1, 2, 3, 4, 5`\n- после сворачивания середины: `1, 2, 5` (а не `1, 2, 3`)\n\n`rowIndex` зависит только от видимых строк — номера будут «прыгать».\n\n## Частичная перерисовка\n\n`getRowMarker` должна быть чистой функцией. Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле — для одной ячейки. Не используйте внешние переменные-счётчики — используйте аргументы (`siblingPath`, `flatIndex`, `siblingIndex`, `parentKey`), они всегда актуальны.\n\n> Подробнее о типах — [RowMarkers API](?path=/docs/локальные-компоненты-tablecanvas-rowmarkers-api--docs)",
53152
- "apiDocs": "# RowMarkers API\n\n## RowMarkersTableConfig\n\n```typescript\ntype RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * ⚠️ **Нестабильный** — меняется при сворачивании/разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};\n```",
53294
+ "apiDocs": "# RowMarkers API\n\n## RowMarkersTableConfig\n\n```typescript\ntype RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * **Нестабильный** — меняется при сворачивании и разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};\n```",
53153
53295
  "api": [
53154
53296
  {
53155
53297
  "typeName": "RowMarkersTableConfig",
53156
- "source": "type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * ⚠️ **Нестабильный** — меняется при сворачивании/разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};"
53298
+ "source": "type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * **Нестабильный** — меняется при сворачивании и разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};"
53157
53299
  }
53158
53300
  ],
53159
53301
  "stories": [
@@ -53219,11 +53361,11 @@
53219
53361
  "legacy": false,
53220
53362
  "summary": "*tableConfig.rowsGrouping + columnConfig.rowsGrouping**",
53221
53363
  "docs": "# RowsGrouping (TableCanvas)\n\n**tableConfig.rowsGrouping + columnConfig.rowsGrouping**\n\nГруппировка строк таблицы по значениям колонок с поддержкой вложенных группировок.\n\n## Ключевые особенности\n\n- Управляемое состояние группировки через `groupByState`\n- Кнопка с выпадающим списком для выбора группировок в controlBlock\n- Кастомный рендер сгруппированных строк через `renderGroupCell`\n- Кастомные элементы в выпадающем списке через `groupButton.defaultCustomItems`\n- Возможность замены данных группированных строк через `groupRowReplaceTo`\n\n## Особенности\n\n- `rowKeyGetter` должен возвращать уникальный идентификатор строки\n- `groupedColumnProps` позволяет настроить колонку с группировкой (ширину, рендер и т.д.)\n- В `columnConfig.rowsGrouping` можно указать `groupByColumn: false` для исключения колонки из группировки\n- `columnGroupLabel` переопределяет лейбл в выпадающем списке (default: `columnConfig.key`)\n\n> Подробнее о типах — [RowsGrouping API](?path=/docs/локальные-компоненты-tablecanvas-rowsgrouping-api--docs)",
53222
- "apiDocs": "# RowsGrouping API\n\n## RowsGrouping\n\n```typescript\ntype RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};\n```",
53364
+ "apiDocs": "# RowsGrouping API\n\n## RowsGrouping\n\n```typescript\ntype RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Как показывать группировку.\n * 'tree' (по умолчанию) — дерево с раскрытием (шевроны) и колонкой группировки.\n * 'merged' — плоский вид: строки идут по группам, а группирующие колонки\n * показаны объединёнными ячейками. Раскрытия нет; чекбокс и номер — на всю\n * группу верхнего уровня.\n * @default 'tree'\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Выравнивание по умолчанию в объединённых ячейках\n * группирующих колонок. Колоночный mergedCellsAlign важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n /**\n * Только для view: 'merged'. Запретить сортировку по группирующим колонкам\n * (как в дереве). По остальным колонкам сортировка остаётся.\n * @default false\n */\n disableGroupColumnsSort?: boolean;\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * Настройки колонки «Группировка» (имя, ширина, свои рендеры). Работает только\n * в виде дерева: при view: 'merged' эта колонка не создаётся, а сгруппированные\n * колонки остаются на своих местах.\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n /** Кнопка «развернуть всё». Работает только в виде дерева: при view: 'merged' шевронов нет. */\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n /**\n * Свой контент ячейки строки-группы. Работает только в виде дерева: при\n * view: 'merged' строк-групп нет (группы показаны объединёнными ячейками),\n * поэтому этот рендер не вызывается.\n */\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};\n```",
53223
53365
  "api": [
53224
53366
  {
53225
53367
  "typeName": "RowsGrouping",
53226
- "source": "type RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};"
53368
+ "source": "type RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Как показывать группировку.\n * 'tree' (по умолчанию) — дерево с раскрытием (шевроны) и колонкой группировки.\n * 'merged' — плоский вид: строки идут по группам, а группирующие колонки\n * показаны объединёнными ячейками. Раскрытия нет; чекбокс и номер — на всю\n * группу верхнего уровня.\n * @default 'tree'\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Выравнивание по умолчанию в объединённых ячейках\n * группирующих колонок. Колоночный mergedCellsAlign важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n /**\n * Только для view: 'merged'. Запретить сортировку по группирующим колонкам\n * (как в дереве). По остальным колонкам сортировка остаётся.\n * @default false\n */\n disableGroupColumnsSort?: boolean;\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * Настройки колонки «Группировка» (имя, ширина, свои рендеры). Работает только\n * в виде дерева: при view: 'merged' эта колонка не создаётся, а сгруппированные\n * колонки остаются на своих местах.\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n /** Кнопка «развернуть всё». Работает только в виде дерева: при view: 'merged' шевронов нет. */\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};\n\nexport type RenderGroupCellProps<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n column: ColumnConfig;\n tabIndex: number;\n theme?: Theme;\n // TODO isExpanded: boolean;\n // TODO toggleGroup: () => void;\n} & GroupRow<RowType>;\n\nexport type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n /**\n * Свой контент ячейки строки-группы. Работает только в виде дерева: при\n * view: 'merged' строк-групп нет (группы показаны объединёнными ячейками),\n * поэтому этот рендер не вызывается.\n */\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};"
53227
53369
  }
53228
53370
  ],
53229
53371
  "stories": [
@@ -53358,7 +53500,7 @@
53358
53500
  "feature": "CellsSelection",
53359
53501
  "legacy": false,
53360
53502
  "summary": "Выделение в таблице задаётся пропом **`tableConfig.cellsSelection.mode`** — это",
53361
- "docs": "# CellsSelection\n\nВыделение в таблице задаётся пропом **`tableConfig.cellsSelection.mode`** — это\nфактическое нативное выделение ячеек. По нему работают copy/paste, рамка\nвыделения, fill-handle и затемнение шапки/нумерации.\n\n> Это **отдельная ось** от `highlightActiveType` (визуальная подсветка строки) и\n> от `selecting` (выбор строк чекбоксами).\n\n## Значения cellsSelection.mode\n\n- `'range-cell'` — выделение диапазона ячеек (по умолчанию). Тянем мышкой.\n- `'multi-range-cell'` — как `range-cell`, но **Ctrl/Cmd** докидывает несколько\n диапазонов (см. ниже).\n- `'cell'` — выделение одной ячейки.\n- `'disabled'` — выделение ячеек отключено.\n\n## Мультивыбор диапазонов (multi-range-cell)\n\nПри `cellsSelection: { mode: 'multi-range-cell' }` можно выделить несколько прямоугольных\nдиапазонов: тянем мышкой первый, затем **Ctrl/Cmd + клик/драг** докидываем\nостальные. Заливка и рамка — у всех выделенных.\n\n**Copy (Ctrl+C)** работает для разрозненного выбора, если все ячейки лежат:\n\n- в пределах **одной колонки** — значения копируются подряд, пустые промежутки\n между ними схлопываются;\n- либо в пределах **одной строки** — аналогично по горизонтали.\n\nРазброс сразу **по строкам и колонкам** (2D) не переносится — copy/paste для него\nне срабатывают. Об этом таблица сообщает через модуль\n[Notifications](?path=/docs/локальные-компоненты-tablecanvas-notifications--docs)\n(`copy` / `paste`, code `multi-range-scattered`).\n\n**Paste (Ctrl+V)** зеркально: вставка идёт в выбранные ячейки подряд, схлопывая\nпромежутки. Полный интерактивный пример copy/paste — в разделе\n**Copy-Paste-Fill**.\n\n## Оси выделения\n\nВыделение работает по трём независимым осям. Любая из них поддерживает\ncopy/paste (**Ctrl+C / Ctrl+V**) по текущему выделению.\n\n- **Ячейки / диапазон** — мышью по области данных, зависит от `cellsSelection.mode`.\n- **Колонки** — клик по шапке колонки.\n- **Строки** — клик/драг по колонке нумерации (`rowMarkers`).\n\n### Выделение колонок\n\nКлик по шапке выделяет колонку целиком:\n\n- **Клик** — одна колонка.\n- **Shift + клик** — смежный диапазон колонок (рисуется нативно, с fill-handle).\n- **Ctrl/Cmd + клик** — несмежный выбор: добавить/убрать колонку.\n\nКлик по иконкам сортировки/фильтрации в шапке выделение **не** вызывает.\nУправляется флагом `cellsSelection.enableColumnSelection` (по умолчанию `true`).\n\n### Выделение строк\n\nКлик/драг по колонке нумерации выделяет строку(и):\n\n- **Клик / драг** — смежный диапазон строк (рисуется нативно, с fill-handle).\n- **Ctrl/Cmd + клик** — несмежный выбор отдельных строк.\n\nТребует включённой колонки нумерации (`rowMarkers`). Управляется флагом\n`cellsSelection.enableRowSelection` (по умолчанию `true`).\n\n## Включение / выключение осей\n\nВыделение колонок и строк можно выключать независимо через `tableConfig.cellsSelection`:\n\n```ts\n<TableCanvas\n tableConfig={{\n rowMarkers: { startIndex: 1 },\n cellsSelection: {\n enableColumnSelection: false, // клик по шапке не выделяет колонку\n enableRowSelection: false, // клик по нумерации не выделяет строку\n },\n }}\n/>;\n```\n\nОба флага по умолчанию `true`. Выделение ячеек/диапазона при этом продолжает\nработать — оно управляется отдельным `cellsSelection.mode`.\n\n## Controlled-режим\n\nВыделением можно управлять снаружи через `state`-кортеж:\n\n```ts\nconst gridSelectionState = useState<GridSelection>(EMPTY_SELECTION);\n\n<TableCanvas\n tableConfig={{\n cellsSelection: { mode: 'range-cell', state: gridSelectionState }, // [value, setter]\n }}\n/>;\n```\n\nСброс: `setter({ current: undefined, rows: CompactSelection.empty(), columns: CompactSelection.empty() })`.\nКонтролируется нативное выделение (ячейки/диапазон/смежные строки и колонки);\nнесмежный Ctrl-выбор колонок/строк — внутренний (own-state), снаружи не задаётся.\n\n`CompactSelection` и тип `GridSelection` реэкспортируются из\n`@daisforge/ui/components/TableCanvas`.\n\n> Подробнее о типах — [CellsSelection API](?path=/docs/локальные-компоненты-tablecanvas-cellsselection-api--docs)",
53503
+ "docs": "# CellsSelection\n\nВыделение в таблице задаётся пропом **`tableConfig.cellsSelection.mode`** — это\nфактическое нативное выделение ячеек. По нему работают copy/paste, рамка\nвыделения, fill-handle и затемнение шапки/нумерации.\n\n> Это **отдельная ось** от `highlightActiveType` (визуальная подсветка строки) и\n> от `selecting` (выбор строк чекбоксами).\n\n## Значения cellsSelection.mode\n\n- `'range-cell'` — выделение диапазона ячеек (по умолчанию). Тянем мышкой.\n- `'multi-range-cell'` — как `range-cell`, но **Ctrl/Cmd** докидывает несколько\n диапазонов (см. ниже).\n- `'cell'` — выделение одной ячейки.\n- `'disabled'` — выделение ячеек отключено.\n\n## Мультивыбор диапазонов (multi-range-cell)\n\nПри `cellsSelection: { mode: 'multi-range-cell' }` можно выделить несколько прямоугольных\nдиапазонов: тянем мышкой первый, затем **Ctrl/Cmd + клик/драг** докидываем\nостальные. Заливка и рамка — у всех выделенных.\n\n**Copy (Ctrl+C)** работает для разрозненного выбора, если все ячейки лежат:\n\n- в пределах **одной колонки** — значения копируются подряд, пустые промежутки\n между ними схлопываются;\n- либо в пределах **одной строки** — аналогично по горизонтали;\n- либо куски складываются в **сплошной прямоугольник** без дыр — тогда он\n копируется как обычный диапазон.\n\nРазброс сразу **по строкам и колонкам** (2D) не переносится — copy/paste для него\nне срабатывают. Об этом таблица сообщает через модуль\n[Notifications](?path=/docs/локальные-компоненты-tablecanvas-notifications--docs)\n(`copy` / `paste`, code `multi-range-scattered`). Сама таблица тост не рисует:\nподключите `notifications.onNotification` и покажите ошибку пользователю — иначе\nотказ копирования выглядит как молчаливая поломка.\n\n**Paste (Ctrl+V)** зеркально: вставка идёт в выбранные ячейки подряд, схлопывая\nпромежутки. Полный интерактивный пример copy/paste — в разделе\n**Copy-Paste-Fill**.\n\n## Оси выделения\n\nВыделение работает по трём независимым осям. Любая из них поддерживает\ncopy/paste (**Ctrl+C / Ctrl+V**) по текущему выделению.\n\n- **Ячейки / диапазон** — мышью по области данных, зависит от `cellsSelection.mode`.\n- **Колонки** — клик по шапке колонки.\n- **Строки** — клик/драг по колонке нумерации (`rowMarkers`).\n\n### Выделение колонок\n\nКлик по шапке выделяет колонку целиком:\n\n- **Клик** — одна колонка.\n- **Shift + клик** — смежный диапазон колонок (рисуется нативно, с fill-handle).\n- **Ctrl/Cmd + клик** — несмежный выбор: добавить/убрать колонку.\n\nКлик по иконкам сортировки/фильтрации в шапке выделение **не** вызывает.\nУправляется флагом `cellsSelection.enableColumnSelection` (по умолчанию `true`).\n\n### Выделение строк\n\nКлик/драг по колонке нумерации выделяет строку(и):\n\n- **Клик / драг** — смежный диапазон строк (рисуется нативно, с fill-handle).\n- **Ctrl/Cmd + клик** — несмежный выбор отдельных строк.\n\nТребует включённой колонки нумерации (`rowMarkers`). Управляется флагом\n`cellsSelection.enableRowSelection` (по умолчанию `true`).\n\n## Включение / выключение осей\n\nВыделение колонок и строк можно выключать независимо через `tableConfig.cellsSelection`:\n\n```ts\n<TableCanvas\n tableConfig={{\n rowMarkers: { startIndex: 1 },\n cellsSelection: {\n enableColumnSelection: false, // клик по шапке не выделяет колонку\n enableRowSelection: false, // клик по нумерации не выделяет строку\n },\n }}\n/>;\n```\n\nОба флага по умолчанию `true`. Выделение ячеек/диапазона при этом продолжает\nработать — оно управляется отдельным `cellsSelection.mode`.\n\n## Controlled-режим\n\nВыделением можно управлять снаружи через `state`-кортеж:\n\n```ts\nconst gridSelectionState = useState<GridSelection>(EMPTY_SELECTION);\n\n<TableCanvas\n tableConfig={{\n cellsSelection: { mode: 'range-cell', state: gridSelectionState }, // [value, setter]\n }}\n/>;\n```\n\nСброс: `setter({ current: undefined, rows: CompactSelection.empty(), columns: CompactSelection.empty() })`.\nКонтролируется нативное выделение (ячейки/диапазон/смежные строки и колонки);\nнесмежный Ctrl-выбор колонок/строк — внутренний (own-state), снаружи не задаётся.\n\n`CompactSelection` и тип `GridSelection` реэкспортируются из\n`@daisforge/ui/components/TableCanvas`.\n\n> Подробнее о типах — [CellsSelection API](?path=/docs/локальные-компоненты-tablecanvas-cellsselection-api--docs)",
53362
53504
  "apiDocs": "# CellsSelection API\n\n## CellsSelectionMode\n\nРежим фактического (нативного) выделения ячеек — `tableConfig.cellsSelection.mode`.\n\n```typescript\ntype CellsSelectionMode =\n | 'cell'\n | 'range-cell'\n | 'multi-range-cell'\n | 'disabled';\n```\n\n## Controlled-режим\n\n`tableConfig.cellsSelection.state` — внешний (controlled) контроль нативного выделения,\n`state`-кортеж `[GridSelection, setter]`:\n\n```ts\ncellsSelection?: {\n state?: [\n GridSelection,\n React.Dispatch<React.SetStateAction<GridSelection>>\n ];\n};\n```\n\n- Сброс: `setter({ current: undefined, rows: CompactSelection.empty(), columns: CompactSelection.empty() })`.\n- Контролирует нативное выделение (ячейки/диапазон/смежные строки и колонки).\n Несмежный Ctrl-выбор колонок/строк — внутренний.\n- `CompactSelection` (значение) и `GridSelection` (тип) реэкспортируются из\n `@daisforge/ui/components/TableCanvas`.\n\n## CellsSelectionConfig\n\nКонфиг выделения колонок (`tableConfig.cellsSelection`).\n\n```typescript\ntype CellsSelectionConfig = {\n /**\n * Режим фактического выделения ячеек (нативный glide selection). По нему\n * работают copy/paste, рамка, fill-handle и затемнение шапки/нумерации.\n * @default \"range-cell\"\n */\n mode?: CellsSelectionMode;\n /**\n * Выделение колонок по клику на шапку. Ctrl/Cmd — добавить/убрать колонку,\n * Shift — диапазон колонок. Клик по иконкам сортировки/фильтрации в шапке\n * выделение НЕ вызывает. Поддерживает copy/paste по выделенным колонкам.\n * @default true\n */\n enableColumnSelection?: boolean;\n /**\n * Выделение строк по клику/драгу на колонке нумерации (`rowMarkers`).\n * Ctrl/Cmd — несмежный выбор отдельных строк, драг — смежный диапазон.\n * Поддерживает copy/paste по выделенным строкам. Требует включённой колонки\n * нумерации (`rowMarkers`). При `false` клик по нумерации ничего не выделяет.\n * @default true\n */\n enableRowSelection?: boolean;\n /**\n * Выделение всей таблицы кликом по «нулевой» ячейке — шапке колонки нумерации\n * (левый верхний угол). Выделяет все строки данных по всем колонкам одним\n * диапазоном; поддерживает copy/paste. Требует включённой колонки нумерации\n * (`rowMarkers`) и активного `mode` (≠ `disabled`).\n * @default true\n */\n enableSelectAll?: boolean;\n /**\n * Внешний (controlled) контроль НАТИВНОГО выделения (ячейки/диапазон/смежные\n * строки и колонки). `state` — кортеж `[value, setter]` как у `selecting.state`.\n * Сброс: `setter({ current: undefined, rows: empty, columns: empty })`.\n * Несмежный Ctrl-выбор колонок/строк (own-state) сюда не входит — он внутренний.\n */\n state?: [GridSelection, React.Dispatch<React.SetStateAction<GridSelection>>];\n};\n```",
53363
53505
  "api": [
53364
53506
  {
@@ -53375,25 +53517,25 @@
53375
53517
  "exportName": "SelectionModes",
53376
53518
  "displayName": "Режимы выделения (cellsSelection.mode)",
53377
53519
  "type": "full-code",
53378
- "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const [selectionMode, setSelectionMode] =\n useState<CellsSelectionMode>('range-cell');\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n\n return (\n <div>\n <div style={{ maxWidth: 320, marginBottom: 12 }}>\n <Select\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(value) => setSelectionMode(value as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />\n </div>\n\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>range-cell</b> — тянем мышкой диапазон ячеек.{' '}\n <b>multi-range-cell</b> — то же + Ctrl/Cmd копит несколько диапазонов.{' '}\n <b>cell</b> — одиночная ячейка. <b>disabled</b> — выделение ячеек\n отключено.\n <br />\n Клик/драг по колонке нумерации → выделение строки (Ctrl — группа).\n Клик по шапке → выделение колонки (Shift — диапазон, Ctrl — несмежно).\n Copy/Paste (Ctrl+C/V) работают по выделению.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: selectionMode },\n rowMarkers: { startIndex: 1 },\n editing: {\n onRowsChange: () => undefined,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53520
+ "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const [selectionMode, setSelectionMode] =\n useState<CellsSelectionMode>('range-cell');\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n\n return (\n <div>\n <div style={{ maxWidth: 320, marginBottom: 12 }}>\n <Select\n label=\"Режим выделения\"\n value={selectionMode}\n onChange={(value) => setSelectionMode(value as CellsSelectionMode)}\n items={SELECTION_MODE_OPTIONS}\n />\n </div>\n\n <StoryHint>\n <b>range-cell</b> — тянем мышкой диапазон ячеек.{' '}\n <b>multi-range-cell</b> — то же + Ctrl/Cmd копит несколько диапазонов.{' '}\n <b>cell</b> — одиночная ячейка. <b>disabled</b> — выделение ячеек\n отключено.\n <br />\n Клик/драг по колонке нумерации → выделение строки (Ctrl — группа).\n Клик по шапке → выделение колонки (Shift — диапазон, Ctrl — несмежно).\n Copy/Paste (Ctrl+C/V) работают по выделению.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: selectionMode },\n rowMarkers: { startIndex: 1 },\n editing: {\n onRowsChange: () => undefined,\n rowKeyGetter: (r) => `${r.id}`,\n defaultEnabled: true,\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53379
53521
  },
53380
53522
  {
53381
53523
  "exportName": "Controlled",
53382
53524
  "displayName": "Контролируемое выделение извне (controlled)",
53383
53525
  "type": "full-code",
53384
- "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n\n const gridSelectionState = useState<GridSelection>(EMPTY_SELECTION);\n const [gridSelection, setGridSelection] = gridSelectionState;\n\n const summary = gridSelection.current\n ? `x=${gridSelection.current.range.x} y=${gridSelection.current.range.y} w=${gridSelection.current.range.width} h=${gridSelection.current.range.height}`\n : 'нет выделения';\n\n return (\n <div>\n <div\n style={{\n display: 'flex',\n gap: 8,\n flexWrap: 'wrap',\n marginBottom: 12,\n }}\n >\n <Button onClick={() => setGridSelection(EMPTY_SELECTION)}>\n Сбросить выделение\n </Button>\n <Button\n onClick={() =>\n setGridSelection({\n current: {\n cell: [1, 0],\n range: { x: 1, y: 0, width: 2, height: 3 },\n rangeStack: [],\n },\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n })\n }\n >\n Выделить Title:Priority (строки 1–3)\n </Button>\n </div>\n\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Выделение — <b>controlled</b> через{' '}\n <code>tableConfig.cellsSelection.state</code> ([value, setter]).\n Кнопки задают/сбрасывают внешний стейт, таблица реагирует.\n Контролируется нативная ось (ячейки/диапазон/смежные строки и\n колонки); несмежный Ctrl-выбор — внутренний.\n <br />\n Текущее выделение: <b>{summary}</b>.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: 'range-cell', state: gridSelectionState },\n rowMarkers: { startIndex: 1 },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53526
+ "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n\n const gridSelectionState = useState<GridSelection>(EMPTY_SELECTION);\n const [gridSelection, setGridSelection] = gridSelectionState;\n\n const summary = gridSelection.current\n ? `x=${gridSelection.current.range.x} y=${gridSelection.current.range.y} w=${gridSelection.current.range.width} h=${gridSelection.current.range.height}`\n : 'нет выделения';\n\n return (\n <div>\n <div\n style={{\n display: 'flex',\n gap: 8,\n flexWrap: 'wrap',\n marginBottom: 12,\n }}\n >\n <Button onClick={() => setGridSelection(EMPTY_SELECTION)}>\n Сбросить выделение\n </Button>\n <Button\n onClick={() =>\n setGridSelection({\n current: {\n cell: [1, 0],\n range: { x: 1, y: 0, width: 2, height: 3 },\n rangeStack: [],\n },\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n })\n }\n >\n Выделить Title:Priority (строки 1–3)\n </Button>\n </div>\n\n <StoryHint>\n Выделение — <b>controlled</b> через{' '}\n <code>tableConfig.cellsSelection.state</code> ([value, setter]).\n Кнопки задают/сбрасывают внешний стейт, таблица реагирует.\n Контролируется нативная ось (ячейки/диапазон/смежные строки и\n колонки); несмежный Ctrl-выбор — внутренний.\n <br />\n Текущее выделение: <b>{summary}</b>.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: 'range-cell', state: gridSelectionState },\n rowMarkers: { startIndex: 1 },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53385
53527
  },
53386
53528
  {
53387
53529
  "exportName": "MultiRangeSelect",
53388
53530
  "displayName": "Мультивыбор диапазонов (multi-range-cell)",
53389
53531
  "type": "full-code",
53390
- "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n\n return (\n <div>\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n <b>multi-range-cell</b>: тяни мышкой диапазон, затем <b>Ctrl/Cmd</b> +\n клик/драг — докидывай ещё диапазоны. У всех выделенных — заливка и\n рамка.\n <br />\n <b>Copy (Ctrl+C):</b> работает, если разрозненный выбор в пределах{' '}\n <b>одной колонки</b> или <b>одной строки</b> — промежутки схлопываются\n (значения копируются подряд). Разброс сразу по строкам и колонкам не\n копируется.\n <br />\n <b>Paste</b> со схлопыванием промежутков — в режиме редактирования,\n см. <b>Copy-Paste-Fill</b>.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: 'multi-range-cell' },\n rowMarkers: { startIndex: 1 },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53532
+ "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n // Таблица сама тосты не рисует: ошибки copy/paste приходят в notifications,\n // показ — на стороне потребителя. Здесь показываем последнее событие плашкой.\n const [lastNotification, setLastNotification] = useState<string | null>(\n null,\n );\n\n return (\n <div>\n <StoryHint>\n <b>multi-range-cell</b>: тяни мышкой диапазон, затем <b>Ctrl/Cmd</b> +\n клик/драг — докидывай ещё диапазоны. У всех выделенных — заливка и\n рамка.\n <br />\n <b>Copy (Ctrl+C):</b> работает, если разрозненный выбор в пределах{' '}\n <b>одной колонки</b> или <b>одной строки</b> — промежутки схлопываются\n (значения копируются подряд), либо если куски складываются в сплошной\n прямоугольник. Разброс сразу по строкам и колонкам не копируется — в{' '}\n <b>notifications</b> уходит ошибка (плашка ниже).\n <br />\n <b>Paste</b> со схлопыванием промежутков — в режиме редактирования,\n см. <b>Copy-Paste-Fill</b>.\n </StoryHint>\n\n {lastNotification && (\n <p\n style={{\n fontSize: 13,\n marginBottom: 8,\n padding: '6px 10px',\n borderRadius: 6,\n background: '#fdecec',\n color: '#a33',\n }}\n >\n notifications: {lastNotification}\n </p>\n )}\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: { mode: 'multi-range-cell' },\n rowMarkers: { startIndex: 1 },\n notifications: {\n onNotification: (event) =>\n setLastNotification(\n `[${event.type}/${event.level}] ${event.message} (${event.code})`,\n ),\n },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53391
53533
  },
53392
53534
  {
53393
53535
  "exportName": "EnableToggles",
53394
53536
  "displayName": "Включение/выключение колонок и строк",
53395
53537
  "type": "full-code",
53396
- "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n const [enableColumnSelection, setEnableColumnSelection] = useState(true);\n const [enableRowSelection, setEnableRowSelection] = useState(true);\n\n return (\n <div>\n <div style={{ display: 'flex', gap: 20, marginBottom: 12 }}>\n <Checkbox\n checked={enableColumnSelection}\n onChange={(e) => setEnableColumnSelection(e.target.checked)}\n label={<div>enableColumnSelection</div>}\n />\n <Checkbox\n checked={enableRowSelection}\n onChange={(e) => setEnableRowSelection(e.target.checked)}\n label={<div>enableRowSelection</div>}\n />\n </div>\n\n <p style={{ fontSize: 13, color: '#888', marginBottom: 8 }}>\n Сними галочку — клик по шапке (колонки) или по колонке нумерации\n (строки) перестанет выделять. Выделение ячеек мышью при этом\n продолжает работать.\n </p>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: {\n mode: 'range-cell',\n enableColumnSelection,\n enableRowSelection,\n },\n rowMarkers: { startIndex: 1 },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53538
+ "code": "import {\n type ColumnConfig,\n CompactSelection,\n type GridSelection,\n type CellsSelectionMode,\n TableCanvas,\n} from '@daisforge/ui/components/TableCanvas';\n\nconst SELECTION_MODE_OPTIONS: Array<{ label: string; value: CellsSelectionMode }> = [\n { label: 'range-cell', value: 'range-cell' },\n { label: 'multi-range-cell', value: 'multi-range-cell' },\n { label: 'cell', value: 'cell' },\n { label: 'disabled', value: 'disabled' },\n];\n\nconst EMPTY_SELECTION: GridSelection = {\n current: undefined,\n rows: CompactSelection.empty(),\n columns: CompactSelection.empty(),\n};\n\nconst COLUMN_CONFIG: readonly ColumnConfig<Row>[] = [\n { key: 'id', name: 'ID', width: 90 },\n { key: 'task', name: 'Title', width: 260 },\n { key: 'priority', name: 'Priority', width: 180 },\n { key: 'issueType', name: 'Issue Type', width: 180 },\n { key: 'complete', name: '% Complete', width: 160 },\n];\n\n() => {\n const [rows] = useState(createRows);\n const columnConfig = useMemo(() => COLUMN_CONFIG, []);\n const [enableColumnSelection, setEnableColumnSelection] = useState(true);\n const [enableRowSelection, setEnableRowSelection] = useState(true);\n\n return (\n <div>\n <div style={{ display: 'flex', gap: 20, marginBottom: 12 }}>\n <Checkbox\n checked={enableColumnSelection}\n onChange={(e) => setEnableColumnSelection(e.target.checked)}\n label={<div>enableColumnSelection</div>}\n />\n <Checkbox\n checked={enableRowSelection}\n onChange={(e) => setEnableRowSelection(e.target.checked)}\n label={<div>enableRowSelection</div>}\n />\n </div>\n\n <StoryHint>\n Сними галочку — клик по шапке (колонки) или по колонке нумерации\n (строки) перестанет выделять. Выделение ячеек мышью при этом\n продолжает работать.\n </StoryHint>\n\n <TableCanvas\n tableConfig={{\n containerStyle: { height: '600px' },\n cellsSelection: {\n mode: 'range-cell',\n enableColumnSelection,\n enableRowSelection,\n },\n rowMarkers: { startIndex: 1 },\n }}\n columnConfig={columnConfig}\n rows={rows}\n />\n </div>\n );\n }"
53397
53539
  }
53398
53540
  ]
53399
53541
  },
@@ -53499,11 +53641,11 @@
53499
53641
  "legacy": false,
53500
53642
  "summary": "*tableConfig.subRows + columnConfig.subRow**",
53501
53643
  "docs": "# SubRows (TableCanvas)\n\n**tableConfig.subRows + columnConfig.subRow**\n\nИерархичное отображение данных с вложенными строками. Поддерживает произвольную глубину вложенности.\n\nКонфигурация состоит из двух частей:\n\n- **`tableConfig.subRows`** — дерево данных: функция получения дочерних строк, ключ строки, состояние раскрытия\n- **`columnConfig[].subRow`** — отображение на уровне колонки: маппинг ключей, рендер ячеек, стрелка раскрытия\n\n## tableConfig.subRows\n\n- **`getSubRows`** `(row) => RowType[] | undefined` — функция получения дочерних строк\n- **`rowKeyGetter`** `(row) => RowIdType` — уникальный ключ строки\n- **`expandedIdsState`** `[Set, setter]` — внешний стейт развёрнутых строк (опционально, иначе внутренний useState)\n\n## columnConfig.subRow\n\n- **`isColumnWithArrow`** `boolean | ((props) => boolean)` — стрелка раскрытия в этой колонке (default: `false`)\n- **`hideHeaderExpandAllArrow`** `boolean` — скрыть кнопку «развернуть все» в header (default: `false`)\n- **`parentKeyAsDefault`** `boolean` — использовать ключ родителя как fallback (default: `false`)\n- **`keyOfColumnInSubRow`** `string | number | ((lvl) => string | number)` — маппинг ключей колонок для каждого уровня\n- **`contentFormat`** `ContentFormat` — формат содержимого подстрок\n- **`renderSubRowCell`** `RenderSubRowCell` — кастомный рендер ячеек подстрок\n\n> Подробнее о типах — [SubRows API](?path=/docs/локальные-компоненты-tablecanvas-subrows-api--docs)",
53502
- "apiDocs": "# SubRows API\n\n## SubRows\n\n```typescript\ntype SubRows<RowType, RowIdType> = {\n /**\n * Функция получения дочерних строк.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если структура дерева может динамически меняться оберните в `useCallback`.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Функция получения уникального ключа строки.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если логика получения ключа может динамически меняться оберните в `useCallback`.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний `useState`.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n};\n```",
53644
+ "apiDocs": "# SubRows API\n\n## SubRows\n\n```typescript\ntype SubRows<RowType, RowIdType> = {\n /**\n * Возвращает дочерние строки узла (или undefined, если их нет).\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Возвращает уникальный ключ строки.\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний useState.\n * При view: 'merged' не используется: сворачивания в этом виде нет.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n /**\n * Как показывать дерево.\n * 'tree' (по умолчанию) — узлы с раскрытием (шевроны) и отступом по уровню.\n * 'merged' — дерево разворачивается в листья, а колонки-предки показаны\n * объединёнными ячейками. Структуру задаёт потребитель через getSubRows;\n * данные (пагинация, сортировка, поиск) остаются на его стороне.\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Колонки-предки по порядку уровней:\n * mergedColumns[0] — верхний уровень, дальше глубже. Каждая такая колонка\n * показывает значение своего узла-предка (узел должен нести поле с этим именем)\n * и объединяется по его границам. Верхний уровень задаёт чекбокс и номер блока.\n */\n mergedColumns?: string[];\n};\n```",
53503
53645
  "api": [
53504
53646
  {
53505
53647
  "typeName": "SubRows",
53506
- "source": "type SubRows<RowType, RowIdType> = {\n /**\n * Функция получения дочерних строк.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если структура дерева может динамически меняться оберните в `useCallback`.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Функция получения уникального ключа строки.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если логика получения ключа может динамически меняться оберните в `useCallback`.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний `useState`.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n};"
53648
+ "source": "type SubRows<RowType, RowIdType> = {\n /**\n * Возвращает дочерние строки узла (или undefined, если их нет).\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Возвращает уникальный ключ строки.\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний useState.\n * При view: 'merged' не используется: сворачивания в этом виде нет.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n /**\n * Как показывать дерево.\n * 'tree' (по умолчанию) — узлы с раскрытием (шевроны) и отступом по уровню.\n * 'merged' — дерево разворачивается в листья, а колонки-предки показаны\n * объединёнными ячейками. Структуру задаёт потребитель через getSubRows;\n * данные (пагинация, сортировка, поиск) остаются на его стороне.\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Колонки-предки по порядку уровней:\n * mergedColumns[0] — верхний уровень, дальше глубже. Каждая такая колонка\n * показывает значение своего узла-предка (узел должен нести поле с этим именем)\n * и объединяется по его границам. Верхний уровень задаёт чекбокс и номер блока.\n */\n mergedColumns?: string[];\n};"
53507
53649
  }
53508
53650
  ],
53509
53651
  "stories": [
@@ -55000,7 +55142,7 @@
55000
55142
  },
55001
55143
  "TableCanvas.ColumnConfig": {
55002
55144
  "name": "ColumnConfig",
55003
- "definition": "export type ColumnConfig<\n Row extends ObjectForExtending,\n SummRow = unknown,\n CustomCtxs extends ObjectForExtending = {},\n> = ColumnDefaultOmitted<Row, SummRow, CustomCtxs> & {\n renderCell?: (cellInfo: CellInfo<Row, SummRow, CustomCtxs>) => CellContent;\n renderCellPreview?: RenderCellPreview<Row, SummRow, CustomCtxs>;\n /**\n * Если в поле name был передан jsx, то нужно заполнить nameAsString для указания label в разделе Настройки столбцов, либо для отображения label в фильтрах в правом сайдбаре (но приоритет для фильтро будет ниже, чем то, что указали в filtering.sidebarConfig.items, там отдельно можно перезаписать label для фильтров сайдабара)\n */\n nameAsString?: string;\n /**\n * Текстовое представление ячейки для копирования в clipboard (Ctrl+C).\n * Если не задан, используется `row[column.key]?.toString()`.\n * Для кастомных canvas-ячеек (бейджи, кнопки) рекомендуется указывать явно.\n */\n copyData?: string | ((row: Row) => string);\n sortingType?: 'stringSort' | 'numberSort' | Comparator<Row>;\n searching?: {\n valueInRow: (row: Row) => string | number;\n };\n filtering?: {\n keyInFilterState: string;\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n valueInRow?: (row: Row) => string | number;\n /**\n * ```FilterCb - (filterValue: string, rowValue: string) => boolean```\n */\n } & (\n | {\n component: 'input';\n filter: 'includes' | 'startWith' | 'equal' | FilterCb<'single'>;\n }\n | {\n component: 'select';\n beforeList?: (\n props: FilterComponentInPopoverProps<\n ObjectForExtending,\n ColumnConfig<Row, SummRow>\n >,\n ) => ReactNode;\n filter:\n | {\n typeOfValue: 'single';\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n filteringType?: FilterCb<'single'>;\n }\n | {\n typeOfValue: 'multiple';\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n filteringType?: FilterCb<'multiple'>;\n };\n selectOptions:\n | {\n type: 'constant';\n options: SelectOptions[];\n }\n | {\n type: 'stateInHeaderContext';\n /**\n * тип optionsState, который будет в HeaderContext:\n * ```ts\n * {\n * value: string | number;\n * text: string | number;\n * }[];\n * ```\n */\n optionsKeyInHeaderContext: string;\n };\n /**\n * Максимальна высота выпадающего списка в фильтрах колонок / правом сайдбаре.\n * @default \"360px\"\n */\n listMaxHeight?: string;\n }\n | {\n component: 'custom';\n customRender: (\n props: FilterComponentInPopoverProps<\n ObjectForExtending,\n ColumnConfig<Row, SummRow>\n >,\n ) => ReactNode;\n filter?: (filterValue: unknown, rowValue: unknown) => boolean;\n compareWithClearedValue?: (\n clearedValue: unknown,\n currV: unknown,\n ) => boolean;\n }\n );\n\n contentFormat?: ContentFormat;\n\n editingCell?: Editing<Row, SummRow>;\n\n subRow?: {\n /**\n * @default false\n */\n isColumnWithArrow?: boolean | ((props: { keyText: KeyText }) => boolean);\n /**\n * @default false\n */\n hideHeaderExpandAllArrow?: boolean;\n /**\n * @default false\n */\n parentKeyAsDefault?: boolean;\n /**\n * @default undefined\n */\n contentFormat?: ContentFormat;\n\n renderSubRowCell?: RenderSubRowCell<ObjectForExtending>;\n } & (\n | {\n keyOfColumnInSubRow:\n | string\n | number\n | ((lvl: number) => string | number);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n editingCell: Editing<any, any>;\n }\n | {\n editingCell?: undefined;\n keyOfColumnInSubRow?:\n | string\n | number\n | ((lvl: number) => string | number);\n }\n );\n rowsGrouping?: ColumnRowsGrouping<Row, ColumnConfig<Row, SummRow>>;\n keyText?: KeyTextColumnConfig<Row, SummRow>;\n /**\n * Переопределение темы на уровне ячейки. Позволяет менять фон и паддинги для конкретных ячеек.\n * @param cellInfo — информация о ячейке (row, column, ctxs, theme)\n * @param lvl — уровень вложенности (0 для обычных строк, 1+ для subRows)\n * @returns Partial<Theme> — объект переопределений (bgCell, cellHorizontalPadding и др.)\n */\n themeOverride?: (\n cellInfo: CellInfo<Row, SummRow, CustomCtxs>,\n lvl: number,\n ) => CellThemeOverrideResult | undefined;\n /**\n * Выравнивание текста в объединённой шапке этой колонки. Действует, когда шапка\n * колонки объединена (`tableConfig.columnsGrouping.squashEmptyCells`); перекрывает\n * табличный `columnsGrouping.squashedHeaderAlign`.\n */\n squashedHeaderAlign?: HeaderAlignment;\n};",
55145
+ "definition": "export type ColumnConfig<\n Row extends ObjectForExtending,\n SummRow = unknown,\n CustomCtxs extends ObjectForExtending = {},\n> = ColumnDefaultOmitted<Row, SummRow, CustomCtxs> & {\n renderCell?: (cellInfo: CellInfo<Row, SummRow, CustomCtxs>) => CellContent;\n renderCellPreview?: RenderCellPreview<Row, SummRow, CustomCtxs>;\n /**\n * Если в поле name был передан jsx, то нужно заполнить nameAsString для указания label в разделе Настройки столбцов, либо для отображения label в фильтрах в правом сайдбаре (но приоритет для фильтро будет ниже, чем то, что указали в filtering.sidebarConfig.items, там отдельно можно перезаписать label для фильтров сайдабара)\n */\n nameAsString?: string;\n /**\n * Текстовое представление ячейки для копирования в clipboard (Ctrl+C).\n * Если не задан, используется `row[column.key]?.toString()`.\n * Для кастомных canvas-ячеек (бейджи, кнопки) рекомендуется указывать явно.\n */\n copyData?: string | ((row: Row) => string);\n sortingType?: 'stringSort' | 'numberSort' | Comparator<Row>;\n searching?: {\n valueInRow: (row: Row) => string | number;\n };\n filtering?: {\n keyInFilterState: string;\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n valueInRow?: (row: Row) => string | number;\n /**\n * ```FilterCb - (filterValue: string, rowValue: string) => boolean```\n */\n } & (\n | {\n component: 'input';\n filter: 'includes' | 'startWith' | 'equal' | FilterCb<'single'>;\n }\n | {\n component: 'select';\n beforeList?: (\n props: FilterComponentInPopoverProps<\n ObjectForExtending,\n ColumnConfig<Row, SummRow>\n >,\n ) => ReactNode;\n filter:\n | {\n typeOfValue: 'single';\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n filteringType?: FilterCb<'single'>;\n }\n | {\n typeOfValue: 'multiple';\n /**\n * Обязательно передать в случае НЕ ручной фильтрации. Если на уровне tableConfig.manualFiltering = false или не задан. Иначе фильтрация не отработает.\n */\n filteringType?: FilterCb<'multiple'>;\n };\n selectOptions:\n | {\n type: 'constant';\n options: SelectOptions[];\n }\n | {\n type: 'stateInHeaderContext';\n /**\n * тип optionsState, который будет в HeaderContext:\n * ```ts\n * {\n * value: string | number;\n * text: string | number;\n * }[];\n * ```\n */\n optionsKeyInHeaderContext: string;\n };\n /**\n * Максимальна высота выпадающего списка в фильтрах колонок / правом сайдбаре.\n * @default \"360px\"\n */\n listMaxHeight?: string;\n }\n | {\n component: 'custom';\n customRender: (\n props: FilterComponentInPopoverProps<\n ObjectForExtending,\n ColumnConfig<Row, SummRow>\n >,\n ) => ReactNode;\n filter?: (filterValue: unknown, rowValue: unknown) => boolean;\n compareWithClearedValue?: (\n clearedValue: unknown,\n currV: unknown,\n ) => boolean;\n }\n );\n\n contentFormat?: ContentFormat;\n\n editingCell?: Editing<Row, SummRow>;\n\n /**\n * Настройки колонки для дерева subRows (шеврон, свой контент и редактор на\n * вложенных уровнях). Работает только в виде дерева. При\n * `subRows.view: 'merged'` не действует: там листья рисуются как обычные\n * строки, вложенных уровней нет.\n */\n subRow?: {\n /**\n * @default false\n */\n isColumnWithArrow?: boolean | ((props: { keyText: KeyText }) => boolean);\n /**\n * @default false\n */\n hideHeaderExpandAllArrow?: boolean;\n /**\n * @default false\n */\n parentKeyAsDefault?: boolean;\n /**\n * @default undefined\n */\n contentFormat?: ContentFormat;\n\n renderSubRowCell?: RenderSubRowCell<ObjectForExtending>;\n } & (\n | {\n keyOfColumnInSubRow:\n | string\n | number\n | ((lvl: number) => string | number);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n editingCell: Editing<any, any>;\n }\n | {\n editingCell?: undefined;\n keyOfColumnInSubRow?:\n | string\n | number\n | ((lvl: number) => string | number);\n }\n );\n rowsGrouping?: ColumnRowsGrouping<Row, ColumnConfig<Row, SummRow>>;\n keyText?: KeyTextColumnConfig<Row, SummRow>;\n /**\n * Переопределение темы на уровне ячейки. Позволяет менять фон и паддинги для конкретных ячеек.\n * @param cellInfo — информация о ячейке (row, column, ctxs, theme)\n * @param lvl — уровень вложенности (0 для обычных строк, 1+ для subRows)\n * @returns Partial<Theme> — объект переопределений (bgCell, cellHorizontalPadding и др.)\n */\n themeOverride?: (\n cellInfo: CellInfo<Row, SummRow, CustomCtxs>,\n lvl: number,\n ) => CellThemeOverrideResult | undefined;\n /**\n * Выравнивание текста в объединённой шапке этой колонки. Действует, когда шапка\n * колонки объединена (`tableConfig.columnsGrouping.squashEmptyCells`); перекрывает\n * табличный `columnsGrouping.squashedHeaderAlign`.\n */\n squashedHeaderAlign?: HeaderAlignment;\n /**\n * Выравнивание контента в объединённых ячейках этой колонки. Перекрывает общий\n * дефолт; важнее только выравнивание конкретного региона.\n *\n * Значение — одинаково для всех блоков колонки. Функция (row) => выравнивание —\n * по данным строки: разным блокам можно задать разное выравнивание (в том числе\n * в merged-видах группировки/subRows, где блоки формирует таблица).\n…",
55004
55146
  "importPath": "@daisforge/ui/components/TableCanvas",
55005
55147
  "importStatement": "import { ColumnConfig } from '@daisforge/ui/components/TableCanvas';",
55006
55148
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/column-config.type.ts"
@@ -55035,7 +55177,7 @@
55035
55177
  },
55036
55178
  "ColumnGlideInstance": {
55037
55179
  "name": "ColumnGlideInstance",
55038
- "definition": "export type ColumnGlideInstance<\n R extends ObjectForExtending,\n SR,\n CustomCtxs extends ObjectForExtending = {},\n> = {\n key: string;\n name: string | CanvasEl;\n nameAsString?: string;\n readonly width?: number;\n isServiceColumn?: boolean;\n isErrorCell?: (row: R) => boolean;\n frozen?: boolean;\n /** Минимальная ширина колонки (px). Ограничивает ресайз и начальную ширину снизу. */\n minWidth?: number;\n /** Максимальная ширина колонки (px). Ограничивает ресайз и начальную ширину сверху. */\n maxWidth?: number;\n /** Максимальная ширина колонки при auto-sizing и grow-распределении (px). Не ограничивает ручной ресайз. */\n maxAutoWidth?: number;\n renderHeaderCell?: (\n cellInfo: HeaderCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CanvasContent;\n renderCell?: (\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellContent;\n contentAlign?: 'left' | 'right' | 'center';\n copyData?: string | ((row: R) => string);\n editable?: boolean | ((row: R) => boolean);\n renderEditCell?: (\n cellInfo: EditingCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => ReactNode;\n /**\n * Кастомный рендер preview-ячейки (при двойном клике на readonly-ячейку).\n * 'none' — отключает превью. 'cellEditorAsPreview' — стандартный текстовый превью.\n * Функция — полностью кастомный рендер.\n */\n renderCellPreview?:\n | 'none'\n | 'cellEditorAsPreview'\n | ((\n cellInfo: PreviewCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => ReactNode);\n renderSummaryCell?: (\n cellInfo: SummaryCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellContent;\n colSpan?:\n | number\n | ((cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>) => number);\n /** Выравнивание в слитой шапке; в adaptColumn glide `spanGroupHeaderAlign`. */\n squashedHeaderAlign?: HeaderAlignment;\n /** Тултип для ячеек этой колонки. Строка, объект или функция (context) => config | null. */\n cellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n row: R;\n column: ColumnGlideInstance<R, SR, CustomCtxs>;\n ctxs: CtxsType<CustomCtxs>;\n refTable?: React.Ref<DataEditorRef>;\n colInd?: number;\n rowInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n /** Тултип для шапки колонки. Строка, объект или функция (context) => config | null. */\n headerCellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n column: ColumnGlideInstance<R, SR, CustomCtxs>;\n ctxs: CtxsType<CustomCtxs>;\n refTable?: React.Ref<DataEditorRef>;\n colInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n columnThemeOverride?: (\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellThemeOverrideResult | undefined;\n};",
55180
+ "definition": "export type ColumnGlideInstance<\n R extends ObjectForExtending,\n SR,\n CustomCtxs extends ObjectForExtending = {},\n> = {\n key: string;\n name: string | CanvasEl;\n nameAsString?: string;\n readonly width?: number;\n isServiceColumn?: boolean;\n isErrorCell?: (row: R) => boolean;\n frozen?: boolean;\n /** Минимальная ширина колонки (px). Ограничивает ресайз и начальную ширину снизу. */\n minWidth?: number;\n /** Максимальная ширина колонки (px). Ограничивает ресайз и начальную ширину сверху. */\n maxWidth?: number;\n /** Максимальная ширина колонки при auto-sizing и grow-распределении (px). Не ограничивает ручной ресайз. */\n maxAutoWidth?: number;\n renderHeaderCell?: (\n cellInfo: HeaderCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CanvasContent;\n renderCell?: (\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellContent;\n contentAlign?: 'left' | 'right' | 'center';\n copyData?: string | ((row: R) => string);\n editable?: boolean | ((row: R) => boolean);\n renderEditCell?: (\n cellInfo: EditingCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => ReactNode;\n /**\n * Кастомный рендер preview-ячейки (при двойном клике на readonly-ячейку).\n * 'none' — отключает превью. 'cellEditorAsPreview' — стандартный текстовый превью.\n * Функция — полностью кастомный рендер.\n */\n renderCellPreview?:\n | 'none'\n | 'cellEditorAsPreview'\n | ((\n cellInfo: PreviewCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => ReactNode);\n renderSummaryCell?: (\n cellInfo: SummaryCellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellContent;\n colSpan?:\n | number\n | ((cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>) => number);\n /** Объединение строк: (cellInfo) => [перваяСтрока, последняяСтрока] | null (см. TableGlide RowSpan). */\n rowSpan?: (\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => readonly [number, number] | null;\n /** Выравнивание контента в объединённой ячейке тела; в adaptColumn идёт в glide как `spanAlign`. */\n spanAlign?:\n | SpanCellAlign\n | ((\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => SpanCellAlign | undefined);\n /** Выравнивание в объединённой шапке; в adaptColumn идёт в glide как `spanGroupHeaderAlign`. */\n squashedHeaderAlign?: HeaderAlignment;\n /** Тултип для ячеек этой колонки. Строка, объект или функция (context) => config | null. */\n cellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n row: R;\n column: ColumnGlideInstance<R, SR, CustomCtxs>;\n ctxs: CtxsType<CustomCtxs>;\n refTable?: React.Ref<DataEditorRef>;\n colInd?: number;\n rowInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n /** Тултип для шапки колонки. Строка, объект или функция (context) => config | null. */\n headerCellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n column: ColumnGlideInstance<R, SR, CustomCtxs>;\n ctxs: CtxsType<CustomCtxs>;\n refTable?: React.Ref<DataEditorRef>;\n colInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n columnThemeOverride?: (\n cellInfo: CellInfoGlideInstance<R, SR, CustomCtxs>,\n ) => CellThemeOverrideResult | undefined;\n};",
55039
55181
  "importPath": "@daisforge/ui/components/TableCanvas",
55040
55182
  "importStatement": "import { ColumnGlideInstance } from '@daisforge/ui/components/TableCanvas';",
55041
55183
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/TableGlideInstance/type.ts"
@@ -55427,7 +55569,7 @@
55427
55569
  },
55428
55570
  "TableCanvas.ColumnRowsGrouping": {
55429
55571
  "name": "ColumnRowsGrouping",
55430
- "definition": "export type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};",
55572
+ "definition": "export type ColumnRowsGrouping<\n RowType extends ObjectForExtending,\n ColumnConfig extends LocalColumnInstance,\n> = {\n /**\n * @default true - при наличии ColumnConfig.rowsGrouping\n */\n groupByColumn?: boolean;\n /**\n * @default ColumnConfig.key\n */\n columnGroupLabel?: string;\n /**\n * Свой контент ячейки строки-группы. Работает только в виде дерева: при\n * view: 'merged' строк-групп нет (группы показаны объединёнными ячейками),\n * поэтому этот рендер не вызывается.\n */\n renderGroupCell?: (\n props: RenderGroupCellProps<RowType, ColumnConfig>,\n ) => CanvasContent;\n};",
55431
55573
  "internal": true,
55432
55574
  "importNotice": "Тип \"ColumnRowsGrouping\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
55433
55575
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/feature-rows-grouping/types.ts"
@@ -55455,7 +55597,7 @@
55455
55597
  },
55456
55598
  "TableGlide.CellInfo": {
55457
55599
  "name": "CellInfo",
55458
- "definition": "export type CellInfo<R extends ObjectForExtending, SR = unknown> = {\n row: R;\n column: ColumnGlideLast<R, SR>;\n colInd: number;\n rowInd: number;\n ctxs: ObjectForExtending;\n theme: Theme;\n /** Состояние наведения для ячейки, которую сейчас рендерит renderCell. */\n hovered: CellHoverState;\n /** Состояние активного выделения для ячейки, которую сейчас рендерит renderCell. */\n active: CellActiveState;\n};",
55600
+ "definition": "export type CellInfo<R extends ObjectForExtending, SR = unknown> = {\n row: R;\n column: ColumnGlideLast<R, SR>;\n colInd: number;\n rowInd: number;\n ctxs: ObjectForExtending;\n theme: Theme;\n /** Состояние наведения для ячейки, которую сейчас рендерит renderCell. */\n hovered: CellHoverState;\n /** Состояние активного выделения для ячейки, которую сейчас рендерит renderCell. */\n active: CellActiveState;\n // Есть только у верхней-левой ячейки объединённого блока. align — выравнивание\n // блока (пусто = по центру). По нему ячейка-select расставляет контент и\n // уголок по высоте всего блока.\n __mergedCell?: { align?: SpanCellAlign };\n};",
55459
55601
  "internal": true,
55460
55602
  "importNotice": "Тип \"CellInfo\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
55461
55603
  "sourceFile": "packages/ui-kit/src/components/TableGlide/types.ts"
@@ -55488,6 +55630,13 @@
55488
55630
  "importStatement": "import { HeaderAlignment } from '@daisforge/ui/components/TableCanvas';",
55489
55631
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/columns-grouping.type.ts"
55490
55632
  },
55633
+ "MergedCellsAlign": {
55634
+ "name": "MergedCellsAlign",
55635
+ "definition": "export type MergedCellsAlign = {\n /** По горизонтали. @default из `contentAlign` колонки, иначе 'left' */\n horizontal?: 'left' | 'center' | 'right';\n /** По вертикали. @default 'center' */\n vertical?: 'top' | 'center' | 'bottom';\n};",
55636
+ "importPath": "@daisforge/ui/components/TableCanvas",
55637
+ "importStatement": "import { MergedCellsAlign } from '@daisforge/ui/components/TableCanvas';",
55638
+ "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/merged-cells.type.ts"
55639
+ },
55491
55640
  "CanvasEl": {
55492
55641
  "name": "CanvasEl",
55493
55642
  "definition": "export type CanvasEl = ReactElement;",
@@ -55537,6 +55686,13 @@
55537
55686
  "importStatement": "import { SummaryCellInfoGlideInstance } from '@daisforge/ui/components/TableCanvas';",
55538
55687
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/TableGlideInstance/type.ts"
55539
55688
  },
55689
+ "SpanCellAlign": {
55690
+ "name": "SpanCellAlign",
55691
+ "definition": "export type SpanCellAlign = {\n /** По горизонтали. @default из contentAlign колонки, иначе 'left' */\n horizontal?: 'left' | 'center' | 'right';\n /** По вертикали. @default 'center' */\n vertical?: 'top' | 'center' | 'bottom';\n};",
55692
+ "internal": true,
55693
+ "importNotice": "Тип \"SpanCellAlign\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
55694
+ "sourceFile": "packages/ui-kit/src/components/TableGlide/types.ts"
55695
+ },
55540
55696
  "CanvasNodeTooltipConfig": {
55541
55697
  "name": "CanvasNodeTooltipConfig",
55542
55698
  "definition": "export type CanvasNodeTooltipConfig =\n | string\n | (CanvasNodeTooltipProps & {\n /** Текст тултипа (только string, не ReactNode). */\n text: string;\n /** Задержка перед показом (мс). По умолчанию 0. */\n mouseEnterDelay?: number;\n /** Задержка перед скрытием после ухода мыши (мс). По умолчанию 0. */\n mouseLeaveDelay?: number;\n });",
@@ -55546,7 +55702,7 @@
55546
55702
  },
55547
55703
  "CtxsType": {
55548
55704
  "name": "CtxsType",
55549
- "definition": "export type CtxsType<Customs extends ObjectForExtending | void = {}> = {\n selectingRowCtx: SelectingContextType;\n headerCtx: HeaderContextValueTypeInstance<ObjectForExtending>;\n expandedRowsCtx: {\n expandedRowsIds: ExpandedRowsContextV | null;\n setExpandedRowsIds: ExpandedRowsChangeContextV | null;\n handleExpandRowDetail: HandleExpandDetail<ObjectForExtending>;\n expandButtonColumnKey: string | undefined;\n };\n rowCtx: ObjectForExtending;\n} & Customs;",
55705
+ "definition": "export type CtxsType<Customs extends ObjectForExtending | void = {}> = {\n selectingRowCtx: SelectingContextType;\n headerCtx: HeaderContextValueTypeInstance<ObjectForExtending>;\n expandedRowsCtx: {\n expandedRowsIds: ExpandedRowsContextV | null;\n setExpandedRowsIds: ExpandedRowsChangeContextV | null;\n handleExpandRowDetail: HandleExpandDetail<ObjectForExtending>;\n expandButtonColumnKey: string | undefined;\n };\n rowCtx: ObjectForExtending;\n /**\n * Высота ОДНОГО header-ряда (листового и каждого групп-уровня — они равны).\n * Нужна рендерам шапки, чтобы позиционировать контент в ПОСЛЕДНЕМ (листовом)\n * уровне слитой многоуровневой шапки (напр. общий чекбокс прижимается к низу).\n */\n headerHeight: number;\n /** Число групп-уровней шапки (0 — одноуровневая шапка). */\n groupHeaderLevels: number;\n} & Customs;",
55550
55706
  "internal": true,
55551
55707
  "importNotice": "Тип \"CtxsType\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
55552
55708
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/ctxs.type.ts"
@@ -55630,7 +55786,7 @@
55630
55786
  },
55631
55787
  "ColumnGlideCustoms": {
55632
55788
  "name": "ColumnGlideCustoms",
55633
- "definition": "export type ColumnGlideCustoms<R extends ObjectForExtending, SR = unknown> = {\n id: string;\n /** Служебные колонки (marker / checkbox / row instruments) живут по отдельным правилам подсветки. */\n isServiceColumn?: boolean;\n /** Внутренний helper для cell-level error outline, совпадающий по семантике с Table editing error. */\n isErrorCell?: (row: R) => boolean;\n renderHeaderCell?: (cellInfo: HeaderCellInfo<R, SR>) => CanvasContent;\n /** Текстовое представление ячейки для копирования в clipboard (Ctrl+C). */\n copyData?: string | ((row: R) => string);\n editable?: boolean | ((row: R) => boolean);\n contentAlign?: 'right' | 'left' | 'center';\n renderCell?: ColumnGlideGetCellContent<R, SR>;\n renderEditCell?: (cellInfo: EditingCellInfoGlide<R, SR>) => CellContent;\n renderSummaryCell?: (summaryCellInfo: SummaryCellInfo<R, SR>) => CellContent;\n colSpan?: ColSpan<R, SR>;\n /** Тултип для ячеек этой колонки. Строка, объект или функция (context) => config | null. */\n cellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n row: R;\n column: ColumnGlideLast<R, SR>;\n ctxs: ObjectForExtending;\n refTable?: Ref<DataEditorRef>;\n colInd?: number;\n rowInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n /** Тултип для шапки этой колонки. Строка, объект или функция (context) => config | null. */\n headerCellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n column: ColumnGlideLast<R, SR>;\n ctxs: ObjectForExtending;\n refTable?: Ref<DataEditorRef>;\n colInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n columnThemeOverride?: (\n cellInfo: CellInfo<R, SR>\n ) => CellThemeOverrideResult | undefined;\n /** Управление preview для колонки. true — включить overlay, 'none' — отключить (в т.ч. стандартный). */\n hasPreview?: boolean | 'none';\n /** Минимальная ширина колонки (px). Ограничивает ресайз и начальную ширину снизу. */\n minWidth?: number;\n /** Максимальная ширина колонки (px). Ограничивает ресайз и начальную ширину сверху. */\n maxWidth?: number;\n};",
55789
+ "definition": "export type ColumnGlideCustoms<R extends ObjectForExtending, SR = unknown> = {\n id: string;\n /** Служебные колонки (marker / checkbox / row instruments) живут по отдельным правилам подсветки. */\n isServiceColumn?: boolean;\n /** Внутренний helper для cell-level error outline, совпадающий по семантике с Table editing error. */\n isErrorCell?: (row: R) => boolean;\n renderHeaderCell?: (cellInfo: HeaderCellInfo<R, SR>) => CanvasContent;\n /** Текстовое представление ячейки для копирования в clipboard (Ctrl+C). */\n copyData?: string | ((row: R) => string);\n editable?: boolean | ((row: R) => boolean);\n contentAlign?: 'right' | 'left' | 'center';\n renderCell?: ColumnGlideGetCellContent<R, SR>;\n renderEditCell?: (cellInfo: EditingCellInfoGlide<R, SR>) => CellContent;\n renderSummaryCell?: (summaryCellInfo: SummaryCellInfo<R, SR>) => CellContent;\n colSpan?: ColSpan<R, SR>;\n rowSpan?: RowSpan<R, SR>;\n /** Выравнивание контента в объединённых ячейках колонки; функция — если нужно задавать для каждого блока отдельно. */\n spanAlign?: SpanCellAlign | ((cellInfo: CellInfo<R, SR>) => SpanCellAlign | undefined);\n /** Тултип для ячеек этой колонки. Строка, объект или функция (context) => config | null. */\n cellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n row: R;\n column: ColumnGlideLast<R, SR>;\n ctxs: ObjectForExtending;\n refTable?: Ref<DataEditorRef>;\n colInd?: number;\n rowInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n /** Тултип для шапки этой колонки. Строка, объект или функция (context) => config | null. */\n headerCellTooltip?:\n | CanvasNodeTooltipConfig\n | ((context: {\n column: ColumnGlideLast<R, SR>;\n ctxs: ObjectForExtending;\n refTable?: Ref<DataEditorRef>;\n colInd?: number;\n theme?: Theme;\n }) => CanvasNodeTooltipConfig | null);\n columnThemeOverride?: (\n cellInfo: CellInfo<R, SR>\n ) => CellThemeOverrideResult | undefined;\n /** Управление preview для колонки. true — включить overlay, 'none' — отключить (в т.ч. стандартный). */\n hasPreview?: boolean | 'none';\n /** Минимальная ширина колонки (px). Ограничивает ресайз и начальную ширину снизу. */\n minWidth?: number;\n /** Максимальная ширина колонки (px). Ограничивает ресайз и начальную ширину сверху. */\n maxWidth?: number;\n};",
55634
55790
  "internal": true,
55635
55791
  "importNotice": "Тип \"ColumnGlideCustoms\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
55636
55792
  "sourceFile": "packages/ui-kit/src/components/TableGlide/types.ts"
@@ -55882,9 +56038,9 @@
55882
56038
  },
55883
56039
  "PopupDFSize": {
55884
56040
  "name": "PopupDFSize",
55885
- "definition": "export type PopupDFSize = 's' | 'm';",
55886
- "internal": true,
55887
- "importNotice": "Тип \"PopupDFSize\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
56041
+ "definition": "export type PopupDFSize = 's' | 'm' | 'l';",
56042
+ "importPath": "@daisforge/ui",
56043
+ "importStatement": "import { PopupDFSize } from '@daisforge/ui';",
55888
56044
  "sourceFile": "packages/ui-kit/src/components/PopupDF/types.ts"
55889
56045
  },
55890
56046
  "SplitViewAdaptiveContent": {
@@ -55980,7 +56136,7 @@
55980
56136
  },
55981
56137
  "TableCanvas.TableConfig": {
55982
56138
  "name": "TableConfig",
55983
- "definition": "export type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *```jsx\n * import { Table } from '@daisforge/ui';\n *\n * <Table\n * tableConfig={{selecting: {stateAndSetter, rowKeyGetter}}}\n * {...rest}\n * />\n * ```\n */\n selecting?: {\n state: [\n ReadonlySet<RowIdType>,\n React.Dispatch<React.SetStateAction<ReadonlySet<RowIdType>>>,\n ];\n rowKeyGetter: (row: RowType) => RowIdType;\n } & Prettify<SelectingRowConfig<RowType, RowIdType>> & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n controlBlock?: {\n domMetadata?: DomMetadata;\n };\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам RightSidebar\n */\n sidebar?: {\n domMetadata?: DomMetadata;\n };\n };\n /**\n * editing - активация редактирования. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={editing: {onRowsChange, rowKeyGetter}}\n *\n * columnConfig=[{editingCell: {...}}]\n */\n editing?: EditingConfig<RowType, RowIdType, SummaryRowType>;\n\n /**\n * cellTransfer — конфигурация копирования, вставки и fill handle.\n * По умолчанию копирование (Ctrl+C) работает в режиме редактирования.\n * Данные копируются в формате TSV (tab-separated values), совместимом с Excel.\n *\n * Пример:\n *\n * tableConfig={{ cellTransfer: { enabled: true, onBeforeCopy: (data) => data } }}\n */\n cellTransfer?: CellTransferConfig;\n\n /**\n * Модуль нотификаций: стандартизированный канал событий для внешних\n * потребителей. Таблица UI не рисует вызывает `onNotification`, потребитель\n * показывает тост/модалку на своей стороне. Сейчас эмитятся ошибки copy/paste\n * (в т.ч. multi-range 2D-разброс, readonly/overflow-abort, пропуск валидацией).\n */\n notifications?: TableNotificationsConfig;\n\n /**\n * subRows - активация иерархии, отображения вложенных строк. Условие активации - наличие данн…",
56139
+ "definition": "export type TableConfig<\n RowType extends ObjectForExtending,\n SummaryRowType,\n RowIdType extends string | number,\n FilterStateType extends ObjectForExtending,\n // SubRowType\n> = {\n /**\n * Группировка колонок (многоуровневая шапка через `columnConfig.children`):\n * объединение пустых ячеек шапки (`squashEmptyCells`, по умолчанию включено) и\n * дефолт выравнивания в объединённых ячейках (`squashedHeaderAlign`).\n */\n columnsGrouping?: ColumnsGroupingConfig;\n /**\n * Подсветка активной сущности в canvas-таблице.\n * @deprecated Значения `'cell'`/`'range-cell'` устарели — используйте\n * `cellsSelection.mode`. Актуальны `'row'` (подсветка строки) и `'disabled'`.\n */\n highlightActiveType?: HighlightActiveType;\n /**\n * Единый конфиг выделения ячеек: режим (`mode`), оси\n * (`enableColumnSelection`/`enableRowSelection`/`enableSelectAll`) и controlled\n * (`state`). См. {@link CellsSelectionConfig}.\n */\n cellsSelection?: CellsSelectionConfig;\n /**\n * Подсвеченная строка (`highlightActiveType='row'`).\n * `state` (опционально) — controlled-кортеж `[value, setter]`: задан — значением\n * владеешь снаружи (сброс `setter(undefined)`); не задан — таблица крутит\n * подсветку внутренним стейтом, а `onChange` всё равно срабатывает (подписка\n * без владения значением).\n */\n highlightActiveRow?: {\n state?: [\n number | undefined,\n React.Dispatch<React.SetStateAction<number | undefined>>,\n ];\n /**\n * Колбэк смены подсвеченной строки: отдаёт её флэт-индекс и сам объект\n * строки (`rows[index]`). Позволяет открывать данные подсвеченной строки\n * (например, в SplitView) без `onCellClicked`.\n */\n onChange?: (params: {\n index: number | undefined;\n row: RowType | undefined;\n }) => void;\n };\n /**\n * Эффекты при наведении (hover)\n * `row` — подсветка строки под курсором.\n * Независимая визуальная ось: селектинг (`cellsSelection`) и `highlightActiveType='row'`\n * рисуются ПОВЕРХ hover-подсветки и визуально перекрывают её (hover\n * «остаётся под ними» и снова виден после сброса выделения).\n *\n * `{ row: true }` — включить с цветами из темы;\n * `{ row: { color: '#F7F9FB' } }` — переопределить цвет data-ячеек.\n * @default undefined (выключено)\n */\n hoverEffects?: HoverEffectsConfig;\n /**\n * Включает компенсацию тонких canvas-линий при browser zoom ниже 100%, когда devicePixelRatio меньше 1.\n * Влияет только на grid/header/sticky hairline borders.\n * @default false\n */\n enableLowDprHairline?: boolean;\n /**\n * rowSize - размеры строк таблицы\n */\n rowSize?: Prettify<RowSize>;\n /**\n * rowHeight - размер высоты строк таблицы\n */\n rowHeight?: Maybe<number | RowHeightFunc<RowType>>;\n /**\n * sorting - активация сортировки. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={sorting: {sortingState}}\n *\n * columnConfig=[{sortingType: 'numberSort'}]\n */\n sorting?: {\n state: [\n readonly SortColumn[],\n React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\n ];\n /**\n * manualSorting - пропс, активирующий ручную-кастомную (или на стороне бэкенда) сортировку\n */\n manualSorting?: boolean;\n };\n /** @deprecated Свойство view не используется и будет удалено в следующей мажорной версии. */\n view?: View;\n /**\n * filtering - активация фильтрации. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={filtering: {stateAndSetter, ...}}\n *\n * columnConfig=[{filtering: {...}}]\n */\n filtering?: FilteringConfig<FilterStateType>;\n /**\n * Сворачивание / разворачивание таблицы\n */\n collapsing?: TableCollapseConfig;\n /**\n * selecting - активация выбора строк. Условие активации - это наличие данного свойства.\n * Есть поддержка выбора строк с многоуровневой вложенностью.\n *\n * Пример:\n *\n *```jsx\n * import { Table } from '@daisforge/ui';\n *\n * <Table\n * tableConfig={{selecting: {stateAndSetter, rowKeyGetter}}}\n * {...rest}\n * />\n * ```\n */\n selecting?: {\n state: [\n ReadonlySet<RowIdType>,\n React.Dispatch<React.SetStateAction<ReadonlySet<RowIdType>>>,\n ];\n rowKeyGetter: (row: RowType) => RowIdType;\n } & Prettify<SelectingRowConfig<RowType, RowIdType>> & {\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n controlBlock?: {\n domMetadata?: DomMetadata;\n };\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам RightSidebar\n */\n sidebar?: {\n domMetadata?: DomMetadata;\n };\n };\n /**\n * editing - активация редактирования. Условие активации - это наличие данного свойства. Нужно также сконфигурировать колонки.\n *\n * Пример:\n *\n * tableConfig={editing: {onRowsChange, rowKeyGetter}}\n *\n * columnConfig=[{editingCell: {...}}]\n */\n editing?: EditingConfig<RowType, RowIdType, SummaryRowType>;\n\n /**\n * cellTransfer — конфигурация копирования, вставки и fill handle.\n * По умолчанию копирование (Ctrl+C) работает в режиме редактирования.\n * Данные копируются в формате TSV (tab-separated values), совместимом с Excel.\n *\n * Пример:\n *\n * tableConfig={{ cellTransfer: { enabled: true, onBeforeCopy: (data) => data } }}\n */\n cellTransfer?: CellTransferConfig;\n\n /**\n * Объединение ячеек тела таблицы.\n *\n * mergeByCellValues колонки, в которых подряд идущие одинаковые значения\n * сливаются в один блок (по вертикали). Границы блоков таблица считает сама из\n * данных; сортировка и фильтр их пересобирают. Данные не меняются — значение и\n * так повторяется в строках, объединение лишь показывает его одной ячейкой.\n *\n * mergedCellsRegions список блоков, заданных снаружи (как выделение): каждый\n * блок описан кл…",
55984
56140
  "importPath": "@daisforge/ui/components/TableCanvas",
55985
56141
  "importStatement": "import { TableConfig } from '@daisforge/ui/components/TableCanvas';",
55986
56142
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/table-config.type.ts"
@@ -56554,7 +56710,7 @@
56554
56710
  },
56555
56711
  "TableCanvas.DefaultOmittedKeys": {
56556
56712
  "name": "DefaultOmittedKeys",
56557
- "definition": "export type DefaultOmittedKeys = Extract<\n keyof ColumnGlideInstance<ObjectForExtending, unknown, ObjectForExtending>,\n | 'editable'\n | 'frozen'\n | 'isServiceColumn'\n | 'isErrorCell'\n | 'renderEditCell'\n | 'renderCellPreview'\n | 'renderCell'\n | 'columnThemeOverride'\n>;",
56713
+ "definition": "export type DefaultOmittedKeys = Extract<\n keyof ColumnGlideInstance<ObjectForExtending, unknown, ObjectForExtending>,\n | 'editable'\n | 'frozen'\n | 'isServiceColumn'\n | 'isErrorCell'\n | 'renderEditCell'\n | 'renderCellPreview'\n | 'renderCell'\n | 'columnThemeOverride'\n // Объединение ячеек тела задаётся только через tableConfig.mergeCells\n // (mergeByCellValues / mergedCellsRegions). Колоночные colSpan/rowSpan убраны:\n // они позиционные и небезопасны при реордере. Внутри (ColumnConfigInternal)\n // остаются как труба, в которую mergeCells синтезирует объединения.\n | 'colSpan'\n | 'rowSpan'\n // Выравнивание блока публично задаётся через mergedCellsAlign / mergeCells.align.\n | 'spanAlign'\n>;",
56558
56714
  "internal": true,
56559
56715
  "importNotice": "Тип \"DefaultOmittedKeys\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
56560
56716
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/glide-type.ts"
@@ -56783,6 +56939,13 @@
56783
56939
  "importNotice": "Тип \"ColSpan\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
56784
56940
  "sourceFile": "packages/ui-kit/src/components/TableGlide/types.ts"
56785
56941
  },
56942
+ "RowSpan": {
56943
+ "name": "RowSpan",
56944
+ "definition": "export type RowSpan<R extends ObjectForExtending, SR> = (\n cellInfo: CellInfo<R, SR>\n) => readonly [number, number] | null;",
56945
+ "internal": true,
56946
+ "importNotice": "Тип \"RowSpan\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
56947
+ "sourceFile": "packages/ui-kit/src/components/TableGlide/types.ts"
56948
+ },
56786
56949
  "TableCanvas.DomMetadata": {
56787
56950
  "name": "DomMetadata",
56788
56951
  "definition": "export type DomMetadata = {\n /**\n * CSS класс для стилизации и поиска элемента\n *\n * @example 'user-profile-card'\n * @example 'onboarding-step-1'\n */\n className?: string;\n /**\n * Data-атрибуты для тестирования, аналитики и онбординга\n *\n * @example { 'data-testid': 'submit-button', 'data-analytics': 'click-event' }\n */\n dataAttributes?: DataAttributes;\n /**\n * Обработчик клика для аналитики и трекинга\n *\n * Вызывается вместе с основным обработчиком элемента (не заменяет его).\n * Не вызывает stopPropagation — оригинальная логика кнопки сохраняется.\n *\n * @param e - MouseEvent (может быть undefined для элементов внутри Dropdown)\n * @param detail - контекст действия: action, columnKey, size и др.\n *\n * @example onClick: (e, detail) => analytics.track(detail?.action, detail)\n */\n onClick?: (\n e?: React.MouseEvent<HTMLElement>,\n detail?: Record<string, unknown>,\n ) => void;\n};",
@@ -56981,7 +57144,7 @@
56981
57144
  },
56982
57145
  "TableCanvas.SubRows": {
56983
57146
  "name": "SubRows",
56984
- "definition": "export type SubRows<RowType, RowIdType> = {\n /**\n * Функция получения дочерних строк.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если структура дерева может динамически меняться оберните в `useCallback`.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Функция получения уникального ключа строки.\n *\n * ⚠️ **Должна быть чистой и стабильной** — не менять логику между рендерами.\n *\n * Если логика получения ключа может динамически меняться оберните в `useCallback`.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний `useState`.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n};",
57147
+ "definition": "export type SubRows<RowType, RowIdType> = {\n /**\n * Возвращает дочерние строки узла (или undefined, если их нет).\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n getSubRows: (row: RowType) => RowType[] | undefined;\n /**\n * Возвращает уникальный ключ строки.\n *\n * Функция должна быть стабильной — не менять поведение между рендерами.\n * Если она зависит от чего-то меняющегося, оберните её в useCallback.\n */\n rowKeyGetter: (row: RowType) => RowIdType;\n /**\n * Внешний стейт развёрнутых строк.\n * Если не задан — используется внутренний useState.\n * При view: 'merged' не используется: сворачивания в этом виде нет.\n */\n expandedIdsState?: [\n Set<string | number>,\n (\n newV:\n | Set<string | number>\n | ((prev: Set<string | number>) => Set<string | number>),\n ) => void,\n ];\n /**\n * Как показывать дерево.\n * 'tree' (по умолчанию) — узлы с раскрытием (шевроны) и отступом по уровню.\n * 'merged' — дерево разворачивается в листья, а колонки-предки показаны\n * объединёнными ячейками. Структуру задаёт потребитель через getSubRows;\n * данные (пагинация, сортировка, поиск) остаются на его стороне.\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Колонки-предки по порядку уровней:\n * mergedColumns[0] — верхний уровень, дальше глубже. Каждая такая колонка\n * показывает значение своего узла-предка (узел должен нести поле с этим именем)\n * и объединяется по его границам. Верхний уровень задаёт чекбокс и номер блока.\n */\n mergedColumns?: string[];\n};",
56985
57148
  "internal": true,
56986
57149
  "importNotice": "Тип \"SubRows\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
56987
57150
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/feature-tree/types.ts"
@@ -56995,7 +57158,7 @@
56995
57158
  },
56996
57159
  "TableCanvas.RowsGrouping": {
56997
57160
  "name": "RowsGrouping",
56998
- "definition": "export type RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};",
57161
+ "definition": "export type RowsGrouping<\n RowType extends ObjectForExtending,\n SummaryRowType = unknown,\n ColumnConfig extends LocalColumnInstance = LocalColumnInstance,\n> = {\n /**\n * массив группированных колонок и его setter\n */\n groupByState: [string[], React.Dispatch<React.SetStateAction<string[]>>];\n /**\n * Как показывать группировку.\n * 'tree' (по умолчанию) — дерево с раскрытием (шевроны) и колонкой группировки.\n * 'merged' — плоский вид: строки идут по группам, а группирующие колонки\n * показаны объединёнными ячейками. Раскрытия нет; чекбокс и номер — на всю\n * группу верхнего уровня.\n * @default 'tree'\n */\n view?: 'tree' | 'merged';\n /**\n * Только для view: 'merged'. Выравнивание по умолчанию в объединённых ячейках\n * группирующих колонок. Колоночный mergedCellsAlign важнее.\n */\n mergedCellsAlign?: MergedCellsAlign;\n /**\n * Только для view: 'merged'. Запретить сортировку по группирующим колонкам\n * (как в дереве). По остальным колонкам сортировка остаётся.\n * @default false\n */\n disableGroupColumnsSort?: boolean;\n /**\n * Функция определения уникального id строки\n */\n rowKeyGetter: (row: RowType) => string | number;\n /**\n * Показывать ли кнопку «Группировать» в controlBlock.\n * При `false` кнопка скрыта и не участвует в компрессии,\n * но группировка строк продолжает работать от внешнего `groupByState`.\n * @default true\n */\n showInControl?: boolean;\n /**\n * Настройки колонки «Группировка» (имя, ширина, свои рендеры). Работает только\n * в виде дерева: при view: 'merged' эта колонка не создаётся, а сгруппированные\n * колонки остаются на своих местах.\n * @default 'Группировка'\n */\n groupedColumnProps?: Partial<Pick<ColumnConfig, 'name' | 'width'>> & {\n renderCell?: (\n props: CellInfoGlideInstance<RowType, SummaryRowType> & {\n parentGroupKey: string | undefined;\n groupByArr: string[];\n },\n ) => CanvasContent;\n rowsGrouping?: Pick<\n ColumnRowsGrouping<RowType, ColumnConfig>,\n 'renderGroupCell'\n >;\n };\n /** Кнопка «развернуть всё». Работает только в виде дерева: при view: 'merged' шевронов нет. */\n expandAllBtn?: {\n /**\n * Функция для расчета состояния открытости всех раскрываемых строк. Функция должна быть чистой.\n * @returns boolean\n */\n expandedAll: (p: {\n /**\n * allRowsIds - уникальные идентификаторы всех строк (группировок и обычных)\n */\n allRowsIds: Set<string | number> | null;\n /**\n * shownRows - представленные прямо сейчас строки (группировки и обычные). Или видимые строки - видимые не во viewport-е, а которые должны быть показаны из-за раскрытий)\n */\n shownRows: readonly RowType[];\n expandedRowsIds: Set<string | number>;\n }) => boolean;\n };\n /**\n * `groupButton` - свойство для кастомизации кнопки с выпадающим списком\n */\n groupButton?: {\n getGroupedCount?: (props: {\n /**\n * высчитанное автоматически количество активных группировок.\n */\n currCount: number;\n /**\n * активные группировки.\n */\n groupedCols: string[];\n columns: {\n /**\n * `visible` - видимые в выпадающем списке колонки-группировки.\n */\n visible: string[];\n /**\n * `hidden` - невидимые или не участвующие в группировке колонки\n *\n * (columnConfig.rowsGrouping=undefined ||\n * columnConfig.rowsGrouping.groupByColumn=false).\n */\n hidden: string[];\n };\n }) => number;\n /**\n * `defaultCustomItems` - свойство для добавления ключей группировки, которых нет в таблице.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n defaultCustomItems?: DropdownItemOption[];\n /**\n * `onItemSelect` - свойство для полного контроля над изменением группировки при выборе опций.\n *\n * `DropdownItemOption.value` - необходимо заполнять ключом, который существует в данных.\n */\n onItemSelect?: (props: {\n item: DropdownItemOption;\n setGroupedCols: React.Dispatch<React.SetStateAction<string[]>>;\n columns: {\n visible: string[];\n hidden: string[];\n };\n }) => void;\n };\n /**\n * `groupRowReplaceTo` - свойство для изменения данных сгруппированных строк. Может понадобиться в редких случаях.\n *\n * @param groupRow данные сгруппированной строки\n * @returns измененная группировка или строка без группировки — строка, которые заменит groupRow\n */\n groupRowReplaceTo?: (\n groupRow: GroupRow<RowType>,\n ) => GroupRow<RowType> | RowType;\n /**\n * Метки для автоматизированного тестирования и аналитики\n * Добавляет data-атрибуты и CSS-классы к элементам controlBlock\n */\n domMetadata?: DomMetadata;\n};",
56999
57162
  "internal": true,
57000
57163
  "importNotice": "Тип \"RowsGrouping\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
57001
57164
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/feature-rows-grouping/types.ts"
@@ -57037,7 +57200,7 @@
57037
57200
  },
57038
57201
  "TableCanvas.ColumnsControlConfig": {
57039
57202
  "name": "ColumnsControlConfig",
57040
- "definition": "export type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};",
57203
+ "definition": "export type ColumnsControlConfig = {\n enable: boolean;\n /**\n * @default enable\n */\n hiding?: boolean;\n /**\n * @default enable\n */\n pinning?: boolean;\n /**\n * @default enable\n */\n reorderingAside?: boolean;\n /**\n * reorderingHeader - активация изменения порядка колонок посредством перемещения шапок колонок таблицы\n */\n /**\n * @default enable\n */\n reorderingHeader?: boolean;\n onReorderingHeader?: (params: {\n newOrder: string[];\n sourceKey: string;\n targetKey: string;\n }) => void;\n\n columnsLabel?: Record<\n string,\n ReactNode | ((isHidden: boolean, isPinned: boolean) => ReactNode)\n >;\n\n /**\n * Виртуализация списка колонок в правом сайдбаре: в DOM держатся только\n * видимые строки списка. false — отключить полностью; { minCount } —\n * включать, когда строк в списке не меньше порога.\n * @default { minCount: 30 }\n */\n virtualization?: boolean | { minCount?: number };\n\n orderDefault?: string[];\n\n pinnedDefault?: string[];\n disablePinning?: string[];\n\n hiddenDefault?: string[];\n disableHiding?: string[];\n\n onConfirm?: (\n params: {\n pinned: string[];\n order: string[];\n hidden: string[];\n changed: { pinned?: 'pinned'; order?: 'order'; hidden?: 'hidden' };\n },\n setters: {\n setColumnsOrder: (value: React.SetStateAction<string[]>) => void;\n setPinnedCols: (value: React.SetStateAction<string[]>) => void;\n setHiddenCols: (value: React.SetStateAction<string[]>) => void;\n },\n ) => void;\n /**\n * DomMetadata для кнопок закрепления столбцов\n */\n pinDomMetadata?: DomMetadata;\n /**\n * DomMetadata для свитчеров скрытия/показа столбцов\n */\n switchDomMetadata?: DomMetadata;\n};",
57041
57204
  "internal": true,
57042
57205
  "importNotice": "Тип \"ColumnsControlConfig\" не экспортируется из публичной точки входа — импортировать его нельзя. Определение приведено для понимания структуры: опишите нужную форму по месту либо возьмите её из типа, который экспортирован.",
57043
57206
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/feature-column-control/types.ts"
@@ -57644,6 +57807,13 @@
57644
57807
  "importStatement": "import { CellTransferConfig } from '@daisforge/ui/components/TableCanvas';",
57645
57808
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/feature-cell-transfer/types.ts"
57646
57809
  },
57810
+ "MergedCellsRegion": {
57811
+ "name": "MergedCellsRegion",
57812
+ "definition": "export type MergedCellsRegion = {\n rowKeys: Array<string | number>;\n colKeys: string[];\n /** Выравнивание контента этого блока. Важнее колоночного и общего. */\n mergedCellsAlign?: MergedCellsAlign;\n};",
57813
+ "importPath": "@daisforge/ui/components/TableCanvas",
57814
+ "importStatement": "import { MergedCellsRegion } from '@daisforge/ui/components/TableCanvas';",
57815
+ "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/table-config.type.ts"
57816
+ },
57647
57817
  "TableNotificationsConfig": {
57648
57818
  "name": "TableNotificationsConfig",
57649
57819
  "definition": "export interface TableNotificationsConfig {\n /** Вызывается на каждое событие таблицы (ошибки copy/paste и т.д.). */\n onNotification: (event: TableNotification) => void;\n}",
@@ -57653,7 +57823,7 @@
57653
57823
  },
57654
57824
  "RowMarkersTableConfig": {
57655
57825
  "name": "RowMarkersTableConfig",
57656
- "definition": "export type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * ⚠️ **Нестабильный** — меняется при сворачивании/разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};",
57826
+ "definition": "export type RowMarkersTableConfig<\n RowType extends ObjectForExtending = ObjectForExtending,\n> = {\n /**\n * Стартовый индекс нумерации (для дефолтной логики без `getRowMarker`).\n * @default 1\n */\n startIndex?: number;\n\n /**\n * Ширина столбца с нумерацией (в px).\n * Если задан `size` — он приоритетнее.\n+ * Если ни `size`, ни `width` не заданы — ширина подстраивается\n+ * под глобальный `tableConfig.rowSize` (small=20, medium=32, big=48).\n * @default 32\n */\n width?: number;\n /**\n * Размер столбца. Приоритетнее `width`.\n * - `xs` = 20px\n * - `s` = 32px\n * - `m` = 48px\n */\n size?: 'xs' | 's' | 'm';\n /**\n * Кастомная функция нумерации. Полностью переопределяет дефолтную root-only логику.\n * Вызывается при рендере каждой ячейки столбца нумерации.\n *\n * ⚠️ Вызывается не только при полной отрисовке таблицы, но и при ховере/скролле —\n * для **одной** ячейки. Не используйте внешние переменные-счётчики —\n * используйте аргументы (`siblingPath`, `flatIndex` и др.), они всегда актуальны.\n *\n * @example\n * // Сквозная стабильная нумерация (все строки, номера не прыгают при сворачивании)\n * getRowMarker: ({ flatIndex }) => flatIndex + 1\n *\n * @example\n * // Корневые — номер, дочерние — прочерк\n * getRowMarker: ({ isSubRow, flatIndex }) => isSubRow ? '—' : flatIndex + 1\n *\n * @example\n * // Иерархическая нумерация любой глубины: 1, 1.1, 1.1.1, 1.2, 2\n * getRowMarker: ({ siblingPath }) => siblingPath.map(i => i + 1).join('.')\n */\n getRowMarker?: (args: {\n /** Данные текущей строки. */\n row: RowType;\n /** Уровень строки в дереве: `0` — корневая, `1` — дочерняя, `2` — внучка, и т.д. */\n level: number;\n /** `true`, если строка дочерняя (`level > 0`). */\n isSubRow: boolean;\n /**\n * Индекс строки в **видимых** строках.\n * **Нестабильный** — меняется при сворачивании и разворачивании веток.\n */\n rowIndex: number;\n /**\n * Стабильный 0-based индекс строки в **полном** дереве (включая скрытые/свёрнутые).\n * Не меняется при collapse/expand — подходит для сквозной нумерации.\n */\n flatIndex: number;\n /**\n * Порядковый номер среди siblings одного родителя (0-based).\n * Для корневых строк — порядковый номер среди всех корневых.\n * Для дочерних — порядковый номер среди children одного `parentKey`.\n */\n siblingIndex: number;\n /**\n * Путь siblingIndex-ов от корня до текущей строки (каждый элемент 0-based).\n *\n * Примеры:\n * - корневая строка (2-я): `[1]`\n * - дочерняя (3-й ребёнок 2-го корня): `[1, 2]`\n * - внучка (1-й ребёнок 1-го ребёнка 1-го корня): `[0, 0, 0]`\n *\n * Для иерархической нумерации: `siblingPath.map(i => i + 1).join('.')` → `\"1.1.1\"`\n *\n * Для плоской таблицы (без subRows): всегда `[siblingIndex]` (один элемент).\n */\n siblingPath: number[];\n /**\n * Ключ родительской строки (`rowKeyGetter(parent)`).\n * `null` для корневых строк. Работает для **любой** глубины вложенности.\n */\n parentKey: string | number | null;\n /** Текущий видимый `flattened` массив строк. */\n rows: readonly RowType[];\n }) => string | number;\n};",
57657
57827
  "importPath": "@daisforge/ui/components/TableCanvas",
57658
57828
  "importStatement": "import { RowMarkersTableConfig } from '@daisforge/ui/components/TableCanvas';",
57659
57829
  "sourceFile": "packages/ui-kit/src/components/TableCanvas/types/table-config.type.ts"