chuijs 4.0.2 → 4.0.4

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 (292) hide show
  1. package/CHANGELOG.md +597 -0
  2. package/README.md +43 -644
  3. package/framework/appLayout/default/chui_app_layout.js +602 -496
  4. package/framework/appLayout/default/global_style.css +288 -97
  5. package/framework/appLayout/default/main_theme_style.css +188 -101
  6. package/framework/components/chui_accordion/accordion.js +38 -35
  7. package/framework/components/chui_accordion/styles.css +13 -12
  8. package/framework/components/chui_action_sheet/action_sheet.js +93 -64
  9. package/framework/components/chui_action_sheet/styles.css +12 -5
  10. package/framework/components/chui_actions_bar/actions_bar.js +338 -0
  11. package/framework/components/chui_actions_bar/styles.css +139 -0
  12. package/framework/components/chui_affix/affix.js +16 -11
  13. package/framework/components/chui_affix/styles.css +5 -0
  14. package/framework/components/chui_alert/alert.js +34 -19
  15. package/framework/components/chui_alert/styles.css +7 -0
  16. package/framework/components/chui_avatar/avatar.js +36 -35
  17. package/framework/components/chui_avatar/styles.css +1 -1
  18. package/framework/components/chui_avatar_group/avatar_group.js +25 -23
  19. package/framework/components/chui_avatar_group/styles.css +1 -1
  20. package/framework/components/chui_backtop/backtop.js +60 -21
  21. package/framework/components/chui_backtop/styles.css +21 -0
  22. package/framework/components/chui_badge/badge.js +30 -28
  23. package/framework/components/chui_badge/styles.css +6 -6
  24. package/framework/components/chui_banner/banner.js +222 -0
  25. package/framework/components/chui_banner/styles.css +170 -0
  26. package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
  27. package/framework/components/chui_breadcrumb/styles.css +1 -1
  28. package/framework/components/chui_button/button.js +52 -36
  29. package/framework/components/chui_button/styles.css +7 -7
  30. package/framework/components/chui_calendar/calendar.js +18 -16
  31. package/framework/components/chui_card/card.js +52 -32
  32. package/framework/components/chui_card_scroll/card_scroll.js +169 -0
  33. package/framework/components/chui_card_scroll/styles.css +86 -0
  34. package/framework/components/chui_chat/chat.js +140 -79
  35. package/framework/components/chui_chat/styles.css +18 -7
  36. package/framework/components/chui_clipboard_history/clipboard_history.js +53 -39
  37. package/framework/components/chui_clipboard_history/styles.css +13 -9
  38. package/framework/components/chui_code/code.js +10 -10
  39. package/framework/components/chui_code/styles.css +4 -4
  40. package/framework/components/chui_code_editor/code_editor.js +73 -57
  41. package/framework/components/chui_code_editor/styles.css +6 -5
  42. package/framework/components/chui_command_palette/command_palette.js +87 -64
  43. package/framework/components/chui_command_palette/styles.css +5 -5
  44. package/framework/components/chui_console/console.js +13 -13
  45. package/framework/components/chui_console/styles.css +6 -3
  46. package/framework/components/chui_content_block/content_block.js +35 -21
  47. package/framework/components/chui_content_block/styles.css +2 -2
  48. package/framework/components/chui_context_menu/context_menu.js +171 -58
  49. package/framework/components/chui_context_menu/styles.css +82 -8
  50. package/framework/components/chui_custom_element/custom_element.js +25 -18
  51. package/framework/components/chui_data_grid/data_grid.js +633 -471
  52. package/framework/components/chui_data_grid/styles.css +24 -22
  53. package/framework/components/chui_descriptions/descriptions.js +34 -17
  54. package/framework/components/chui_descriptions/styles.css +1 -1
  55. package/framework/components/chui_details/details.js +42 -36
  56. package/framework/components/chui_details/styles.css +12 -11
  57. package/framework/components/chui_diff_viewer/diff_viewer.js +172 -147
  58. package/framework/components/chui_diff_viewer/styles.css +10 -10
  59. package/framework/components/chui_divider/divider.js +19 -17
  60. package/framework/components/chui_divider/styles.css +2 -2
  61. package/framework/components/chui_drawer/drawer.js +63 -38
  62. package/framework/components/chui_drawer/styles.css +18 -4
  63. package/framework/components/chui_drop_zone/drop_zone.js +37 -27
  64. package/framework/components/chui_drop_zone/styles.css +10 -6
  65. package/framework/components/chui_empty_state/empty_state.js +20 -15
  66. package/framework/components/chui_fieldset/fieldset.js +40 -23
  67. package/framework/components/chui_fieldset/styles.css +6 -6
  68. package/framework/components/chui_file_explorer/file_explorer.js +260 -189
  69. package/framework/components/chui_file_explorer/styles.css +8 -7
  70. package/framework/components/chui_form/form.js +40 -28
  71. package/framework/components/chui_form/styles.css +1 -1
  72. package/framework/components/chui_gantt/gantt.js +102 -78
  73. package/framework/components/chui_gantt/styles.css +5 -5
  74. package/framework/components/chui_graphs/graphs.js +376 -239
  75. package/framework/components/chui_graphs/styles.css +27 -6
  76. package/framework/components/chui_hero/hero.js +98 -37
  77. package/framework/components/chui_hero/styles.css +176 -7
  78. package/framework/components/chui_html_block/html_block.js +8 -8
  79. package/framework/components/chui_html_block/styles.css +3 -3
  80. package/framework/components/chui_hx/hx.js +3 -3
  81. package/framework/components/chui_hx/styles.css +13 -6
  82. package/framework/components/chui_icons/icons.js +2115 -2115
  83. package/framework/components/chui_icons/styles.css +4 -3
  84. package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
  85. package/framework/components/chui_inputs/chui_check_box/styles.css +30 -21
  86. package/framework/components/chui_inputs/chui_chips/chips.js +141 -70
  87. package/framework/components/chui_inputs/chui_chips/styles.css +32 -7
  88. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +55 -38
  89. package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
  90. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +127 -109
  91. package/framework/components/chui_inputs/chui_combo_box/styles.css +23 -21
  92. package/framework/components/chui_inputs/chui_date/date.js +161 -127
  93. package/framework/components/chui_inputs/chui_date/styles.css +20 -19
  94. package/framework/components/chui_inputs/chui_date_range/date_range.js +61 -50
  95. package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
  96. package/framework/components/chui_inputs/chui_datetime/datetime.js +47 -39
  97. package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
  98. package/framework/components/chui_inputs/chui_email/email.js +48 -36
  99. package/framework/components/chui_inputs/chui_email/styles.css +11 -11
  100. package/framework/components/chui_inputs/chui_file/file.js +71 -44
  101. package/framework/components/chui_inputs/chui_file/styles.css +23 -19
  102. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +101 -76
  103. package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
  104. package/framework/components/chui_inputs/chui_masked/masked.js +83 -72
  105. package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
  106. package/framework/components/chui_inputs/chui_mention/mention.js +87 -40
  107. package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
  108. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +242 -212
  109. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +23 -15
  110. package/framework/components/chui_inputs/chui_number/number.js +84 -70
  111. package/framework/components/chui_inputs/chui_number/styles.css +13 -13
  112. package/framework/components/chui_inputs/chui_otp/otp.js +71 -62
  113. package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
  114. package/framework/components/chui_inputs/chui_password/password.js +72 -56
  115. package/framework/components/chui_inputs/chui_password/styles.css +15 -15
  116. package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
  117. package/framework/components/chui_inputs/chui_radio_button/styles.css +22 -22
  118. package/framework/components/chui_inputs/chui_rating/rating.js +54 -50
  119. package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
  120. package/framework/components/chui_inputs/chui_select_box/select_box.js +132 -108
  121. package/framework/components/chui_inputs/chui_select_box/styles.css +21 -21
  122. package/framework/components/chui_inputs/chui_slider/slider.js +41 -38
  123. package/framework/components/chui_inputs/chui_slider/styles.css +7 -5
  124. package/framework/components/chui_inputs/chui_text/styles.css +14 -14
  125. package/framework/components/chui_inputs/chui_text/text.js +69 -52
  126. package/framework/components/chui_inputs/chui_text_area/styles.css +14 -14
  127. package/framework/components/chui_inputs/chui_text_area/text_area.js +59 -36
  128. package/framework/components/chui_inputs/chui_time/styles.css +18 -20
  129. package/framework/components/chui_inputs/chui_time/time.js +71 -49
  130. package/framework/components/chui_inputs/chui_toggle/styles.css +13 -6
  131. package/framework/components/chui_inputs/chui_toggle/toggle.js +27 -27
  132. package/framework/components/chui_json_editor/json_editor.js +137 -127
  133. package/framework/components/chui_json_editor/styles.css +5 -5
  134. package/framework/components/chui_json_viewer/json_viewer.js +133 -114
  135. package/framework/components/chui_json_viewer/styles.css +9 -8
  136. package/framework/components/chui_kanban/kanban.js +78 -60
  137. package/framework/components/chui_kanban/styles.css +8 -4
  138. package/framework/components/chui_kbd/kbd.js +12 -10
  139. package/framework/components/chui_kbd/styles.css +1 -1
  140. package/framework/components/chui_label/label.js +31 -29
  141. package/framework/components/chui_label/styles.css +3 -3
  142. package/framework/components/chui_link/link.js +36 -29
  143. package/framework/components/chui_link/styles.css +11 -4
  144. package/framework/components/chui_list/list.js +42 -38
  145. package/framework/components/chui_list/styles.css +4 -3
  146. package/framework/components/chui_markdown_editor/markdown_editor.js +125 -109
  147. package/framework/components/chui_markdown_editor/styles.css +5 -5
  148. package/framework/components/chui_media/audio.js +457 -364
  149. package/framework/components/chui_media/audio_styles.css +74 -37
  150. package/framework/components/chui_media/image.js +99 -64
  151. package/framework/components/chui_media/image_compare.js +86 -55
  152. package/framework/components/chui_media/image_compare_styles.css +5 -5
  153. package/framework/components/chui_media/image_styles.css +4 -4
  154. package/framework/components/chui_media/video.js +264 -171
  155. package/framework/components/chui_media/video_styles.css +57 -24
  156. package/framework/components/chui_menu_bar/menu_bar.js +67 -65
  157. package/framework/components/chui_menu_bar/styles.css +14 -12
  158. package/framework/components/chui_message_bar/message_bar.js +551 -0
  159. package/framework/components/chui_message_bar/styles.css +248 -0
  160. package/framework/components/chui_modal/modal.js +81 -56
  161. package/framework/components/chui_modal/styles.css +3 -2
  162. package/framework/components/chui_notification/notification.js +107 -69
  163. package/framework/components/chui_notification/notification_download.js +28 -77
  164. package/framework/components/chui_notification/notification_download_card.js +115 -0
  165. package/framework/components/chui_notification/notification_download_progress.css +5 -4
  166. package/framework/components/chui_notification/notification_download_progress.js +73 -105
  167. package/framework/components/chui_notification/notification_update.js +44 -37
  168. package/framework/components/chui_notification/status_plate.css +3 -3
  169. package/framework/components/chui_notification/status_plate.js +2 -2
  170. package/framework/components/chui_notification/styles.css +6 -1
  171. package/framework/components/chui_notification/styles_download.css +2 -2
  172. package/framework/components/chui_notification/styles_update.css +3 -3
  173. package/framework/components/chui_org_chart/org_chart.js +101 -62
  174. package/framework/components/chui_org_chart/styles.css +2 -2
  175. package/framework/components/chui_pagination/pagination.js +65 -30
  176. package/framework/components/chui_pagination/styles.css +1 -1
  177. package/framework/components/chui_paragraph/paragraph.js +2 -2
  178. package/framework/components/chui_paragraph/styles.css +3 -3
  179. package/framework/components/chui_popover/popover.js +77 -50
  180. package/framework/components/chui_popover/styles.css +1 -1
  181. package/framework/components/chui_popups/popups.js +210 -181
  182. package/framework/components/chui_popups/styles.css +7 -7
  183. package/framework/components/chui_print_button/print_button.js +48 -33
  184. package/framework/components/chui_progress_bar/progress_bar.js +49 -19
  185. package/framework/components/chui_progress_bar/styles.css +6 -6
  186. package/framework/components/chui_progress_ring/progress_ring.js +32 -30
  187. package/framework/components/chui_progress_ring/styles.css +1 -1
  188. package/framework/components/chui_qrcode/qrcode.js +85 -41
  189. package/framework/components/chui_query_builder/query_builder.js +263 -170
  190. package/framework/components/chui_query_builder/styles.css +4 -4
  191. package/framework/components/chui_radio_group/radio_group.js +36 -26
  192. package/framework/components/chui_radio_group/styles.css +2 -2
  193. package/framework/components/chui_result/result.js +41 -31
  194. package/framework/components/chui_result/styles.css +3 -3
  195. package/framework/components/chui_scheduler/scheduler.js +126 -104
  196. package/framework/components/chui_scheduler/styles.css +11 -7
  197. package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
  198. package/framework/components/chui_screen_spinner/styles.css +49 -0
  199. package/framework/components/chui_segmented_control/segmented_control.js +90 -27
  200. package/framework/components/chui_segmented_control/styles.css +32 -6
  201. package/framework/components/chui_settings/settings.js +160 -87
  202. package/framework/components/chui_settings/styles.css +4 -4
  203. package/framework/components/chui_shortcut_help/shortcut_help.js +82 -61
  204. package/framework/components/chui_sidebar_nav/sidebar_nav.js +132 -53
  205. package/framework/components/chui_sidebar_nav/styles.css +9 -3
  206. package/framework/components/chui_skeleton/skeleton.js +29 -24
  207. package/framework/components/chui_slideshow/slideshow.js +122 -103
  208. package/framework/components/chui_slideshow/styles.css +22 -16
  209. package/framework/components/chui_spinner/spinner.js +17 -17
  210. package/framework/components/chui_spinner/styles.css +0 -1
  211. package/framework/components/chui_split_button/split_button.js +53 -30
  212. package/framework/components/chui_split_button/styles.css +22 -6
  213. package/framework/components/chui_splitter/splitter.js +46 -39
  214. package/framework/components/chui_splitter/styles.css +2 -2
  215. package/framework/components/chui_statistic/statistic.js +111 -84
  216. package/framework/components/chui_status_bar/status_bar.js +11 -9
  217. package/framework/components/chui_status_bar/styles.css +2 -2
  218. package/framework/components/chui_stepper/stepper.js +230 -54
  219. package/framework/components/chui_stepper/styles.css +79 -14
  220. package/framework/components/chui_sticky_bar/sticky_bar.js +53 -17
  221. package/framework/components/chui_sticky_bar/styles.css +11 -7
  222. package/framework/components/chui_tabs/chui_tabs.js +100 -46
  223. package/framework/components/chui_tabs/styles.css +43 -15
  224. package/framework/components/chui_text_editor/styles.css +24 -23
  225. package/framework/components/chui_text_editor/text_editor.js +103 -79
  226. package/framework/components/chui_text_editor/text_editor_panel.js +428 -367
  227. package/framework/components/chui_theme_toggle/theme_toggle.js +46 -25
  228. package/framework/components/chui_timeline/styles.css +3 -1
  229. package/framework/components/chui_timeline/timeline.js +14 -12
  230. package/framework/components/chui_toast_queue/styles.css +11 -11
  231. package/framework/components/chui_toast_queue/toast_queue.js +82 -73
  232. package/framework/components/chui_toolbar/styles.css +2 -2
  233. package/framework/components/chui_toolbar/toolbar.js +20 -13
  234. package/framework/components/chui_tooltip/styles.css +2 -2
  235. package/framework/components/chui_tooltip/tooltip.js +40 -27
  236. package/framework/components/chui_tour/styles.css +12 -7
  237. package/framework/components/chui_tour/tour.js +132 -98
  238. package/framework/components/chui_transfer/styles.css +8 -7
  239. package/framework/components/chui_transfer/transfer.js +143 -123
  240. package/framework/components/chui_tree_table/styles.css +4 -4
  241. package/framework/components/chui_tree_table/tree_table.js +88 -74
  242. package/framework/components/chui_tree_view/styles.css +14 -10
  243. package/framework/components/chui_tree_view/tree_view.js +298 -253
  244. package/framework/components/chui_user_bar/styles.css +145 -0
  245. package/framework/components/chui_user_bar/user_bar.js +402 -0
  246. package/framework/components/chui_user_list_bar/styles.css +173 -0
  247. package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
  248. package/framework/components/chui_watermark/watermark.js +41 -33
  249. package/framework/components/chui_webview/styles.css +2 -2
  250. package/framework/components/chui_webview/webview.js +64 -52
  251. package/framework/components/chui_write_bar/styles.css +172 -0
  252. package/framework/components/chui_write_bar/write_bar.js +320 -0
  253. package/framework/main_window.js +1234 -0
  254. package/framework/modules/chui_a11y/chui_a11y.js +153 -123
  255. package/framework/modules/chui_animations/animations.js +105 -43
  256. package/framework/modules/chui_animations/motion.js +120 -0
  257. package/framework/modules/chui_animations/styles.css +37 -4
  258. package/framework/modules/chui_bridge/preload.js +36 -25
  259. package/framework/modules/chui_functions.js +69 -63
  260. package/framework/modules/chui_highlight/chui_highlight.js +25 -22
  261. package/framework/modules/chui_highlight/styles.css +13 -10
  262. package/framework/modules/chui_i18n/chui_i18n.js +12 -10
  263. package/framework/modules/chui_i18n/locales/a11y.js +5 -5
  264. package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
  265. package/framework/modules/chui_i18n/locales/calendar.js +3 -3
  266. package/framework/modules/chui_i18n/locales/common.js +3 -3
  267. package/framework/modules/chui_i18n/locales/data.js +36 -26
  268. package/framework/modules/chui_i18n/locales/editors.js +40 -13
  269. package/framework/modules/chui_i18n/locales/feedback.js +65 -3
  270. package/framework/modules/chui_i18n/locales/forms.js +8 -8
  271. package/framework/modules/chui_i18n/locales/graphics.js +8 -8
  272. package/framework/modules/chui_i18n/locales/media.js +4 -4
  273. package/framework/modules/chui_i18n/locales/navigation.js +12 -8
  274. package/framework/modules/chui_i18n/locales/validate.js +3 -3
  275. package/framework/modules/chui_json/chui_json.js +11 -11
  276. package/framework/modules/chui_layout/chui_layout.js +12 -10
  277. package/framework/modules/chui_logger/chui_logger.js +47 -28
  278. package/framework/modules/chui_page/page.js +39 -33
  279. package/framework/modules/chui_palette/palette.js +61 -0
  280. package/framework/modules/chui_qr/chui_qr.js +179 -88
  281. package/framework/modules/chui_scrollbar/chui_scrollbar.js +79 -68
  282. package/framework/modules/chui_scrollbar/styles.css +4 -2
  283. package/framework/modules/chui_services/chui_channels.js +27 -13
  284. package/framework/modules/chui_services/chui_services.js +47 -39
  285. package/framework/modules/chui_services/desktop.js +108 -101
  286. package/framework/modules/chui_sortable/chui_sortable.js +33 -28
  287. package/framework/modules/chui_validate/chui_validate.js +10 -7
  288. package/framework/tray_bar/chui_menu_items.js +136 -46
  289. package/index.d.ts +5046 -0
  290. package/index.js +199 -1254
  291. package/package.json +81 -68
  292. package/framework/components/telegram_bot/chui_telegram_bot.js +0 -184
@@ -1,13 +1,13 @@
1
- const {setStyles, optionOr} = require('../../modules/chui_functions');
2
- const {A11Y_ACTIVE_CLASS} = require('../../modules/chui_a11y/chui_a11y');
3
- const {Icon, Icons} = require('../chui_icons/icons');
4
- const {Dialog} = require('../chui_modal/modal');
5
- const {ContentBlock} = require('../chui_content_block/content_block');
6
- const {Button} = require('../chui_button/button');
7
- const {I18n} = require('../../modules/chui_i18n/chui_i18n');
8
- const {Desktop} = require('../../modules/chui_services/desktop');
9
- const {CheckBox} = require('../chui_inputs/chui_check_box/check_box');
10
- const {Select} = require('../chui_inputs/chui_select_box/select_box');
1
+ const { setStyles, optionOr } = require("../../modules/chui_functions");
2
+ const { A11Y_ACTIVE_CLASS } = require("../../modules/chui_a11y/chui_a11y");
3
+ const { Icon, Icons } = require("../chui_icons/icons");
4
+ const { Dialog } = require("../chui_modal/modal");
5
+ const { ContentBlock } = require("../chui_content_block/content_block");
6
+ const { Button } = require("../chui_button/button");
7
+ const { I18n } = require("../../modules/chui_i18n/chui_i18n");
8
+ const { Desktop } = require("../../modules/chui_services/desktop");
9
+ const { CheckBox } = require("../chui_inputs/chui_check_box/check_box");
10
+ const { Select } = require("../chui_inputs/chui_select_box/select_box");
11
11
 
12
12
  /**
13
13
  * `DataGrid` — сетка данных с описанием колонок.
@@ -81,75 +81,78 @@ class DataGridBody {
81
81
  START: "start",
82
82
  CENTER: "center",
83
83
  END: "end",
84
- NEAREST: "nearest"
84
+ NEAREST: "nearest",
85
85
  };
86
86
  static DEFAULT_BUFFER = 6;
87
87
  static DEFAULT_LOAD_THRESHOLD = 3;
88
88
  static #counter = 0;
89
89
 
90
90
  #root = document.createElement("data_grid_body");
91
- #spacer_top = document.createElement("data_grid_spacer");
92
- #spacer_bottom = document.createElement("data_grid_spacer");
91
+ #spacerTop = document.createElement("data_grid_spacer");
92
+ #spacerBottom = document.createElement("data_grid_spacer");
93
93
  #empty = document.createElement("data_grid_body_empty");
94
94
  #loader = document.createElement("data_grid_body_loader");
95
95
  // Окно в DOM: индекс строки → узел
96
96
  #nodes = new Map();
97
97
  #id = undefined;
98
98
  #items = [];
99
- #item_height = 34;
99
+ #itemHeight = 34;
100
100
  #buffer = DataGridBody.DEFAULT_BUFFER;
101
- #item_tag = "data_grid_row";
102
- #render_item = undefined;
103
- #on_item_click = undefined;
104
- #on_activate = undefined;
105
- #on_render = undefined;
106
- #on_load_more = undefined;
107
- #loading_text = undefined;
108
- #load_threshold = DataGridBody.DEFAULT_LOAD_THRESHOLD;
109
- #has_more = false;
110
- #loading_more = false;
101
+ #itemTag = "data_grid_row";
102
+ #renderItem = undefined;
103
+ #onItemClickCallback = undefined;
104
+ #onActivate = undefined;
105
+ #onRender = undefined;
106
+ #onLoadMore = undefined;
107
+ #loadingText = undefined;
108
+ #loadThreshold = DataGridBody.DEFAULT_LOAD_THRESHOLD;
109
+ #hasMore = false;
110
+ #loadingMore = false;
111
111
  // Длина, для которой запрос уже сделан: не просим ещё раз, пока данных не прибавилось
112
- #requested_at = -1;
113
- #window_state = {from: 0, to: 0};
112
+ #requestedAt = -1;
113
+ #windowState = { from: 0, to: 0 };
114
114
  #raf = null;
115
- #refresh_raf = null;
116
- #resize_observer = undefined;
115
+ #refreshRaf = null;
116
+ #resizeObserver = undefined;
117
117
  #bound = false;
118
118
 
119
- constructor(options = {
120
- id: String(),
121
- itemTag: String(),
122
- itemHeight: Number(),
123
- buffer: Number(),
124
- height: String(),
125
- emptyText: String(),
126
- loadingText: String(),
127
- hasMore: Boolean(),
128
- loadMoreThreshold: Number(),
129
- renderItem: undefined,
130
- onItemClick: undefined,
131
- onActivate: undefined,
132
- onRender: undefined,
133
- onLoadMore: undefined
134
- }) {
119
+ constructor(
120
+ options = {
121
+ id: String(),
122
+ itemTag: String(),
123
+ itemHeight: Number(),
124
+ buffer: Number(),
125
+ height: String(),
126
+ emptyText: String(),
127
+ loadingText: String(),
128
+ hasMore: Boolean(),
129
+ loadMoreThreshold: Number(),
130
+ renderItem: undefined,
131
+ onItemClick: undefined,
132
+ onActivate: undefined,
133
+ onRender: undefined,
134
+ onLoadMore: undefined,
135
+ }
136
+ ) {
135
137
  DataGridBody.#counter += 1;
136
138
  this.#id = optionOr(options.id, `chui_data_grid_body_${DataGridBody.#counter}`);
137
- this.#item_tag = optionOr(options.itemTag, "data_grid_row");
138
- this.#item_height = options.itemHeight === undefined ? 34 : options.itemHeight;
139
+ this.#itemTag = optionOr(options.itemTag, "data_grid_row");
140
+ this.#itemHeight = options.itemHeight === undefined ? 34 : options.itemHeight;
139
141
  this.#buffer = options.buffer === undefined ? DataGridBody.DEFAULT_BUFFER : options.buffer;
140
- this.#render_item = options.renderItem;
141
- this.#on_item_click = options.onItemClick;
142
- this.#on_activate = options.onActivate;
143
- this.#on_render = options.onRender;
144
- this.#on_load_more = options.onLoadMore;
145
- this.#loading_text = optionOr(options.loadingText, I18n.t("common.loading"));
146
- this.#load_threshold = options.loadMoreThreshold === undefined ? DataGridBody.DEFAULT_LOAD_THRESHOLD : options.loadMoreThreshold;
142
+ this.#renderItem = options.renderItem;
143
+ this.#onItemClickCallback = options.onItemClick;
144
+ this.#onActivate = options.onActivate;
145
+ this.#onRender = options.onRender;
146
+ this.#onLoadMore = options.onLoadMore;
147
+ this.#loadingText = optionOr(options.loadingText, I18n.t("common.loading"));
148
+ this.#loadThreshold =
149
+ options.loadMoreThreshold === undefined ? DataGridBody.DEFAULT_LOAD_THRESHOLD : options.loadMoreThreshold;
147
150
  // Без onLoadMore тело считает, что загружено всё
148
- this.#has_more = options.hasMore === undefined ? options.onLoadMore !== undefined : options.hasMore === true;
151
+ this.#hasMore = options.hasMore === undefined ? options.onLoadMore !== undefined : options.hasMore === true;
149
152
  this.#root.id = this.#id;
150
- this.#spacer_top.dataset.pos = "top";
151
- this.#spacer_bottom.dataset.pos = "bottom";
152
- for (const spacer of [this.#spacer_top, this.#spacer_bottom]) {
153
+ this.#spacerTop.dataset.pos = "top";
154
+ this.#spacerBottom.dataset.pos = "bottom";
155
+ for (const spacer of [this.#spacerTop, this.#spacerBottom]) {
153
156
  spacer.setAttribute("aria-hidden", "true");
154
157
  spacer.style.height = "0px";
155
158
  this.#root.appendChild(spacer);
@@ -163,30 +166,40 @@ class DataGridBody {
163
166
  this.#root.appendChild(this.#loader);
164
167
  this.#applyLoader();
165
168
  this.setHeight(optionOr(options.height, DataGrid.DEFAULT_HEIGHT));
166
- this.#root.addEventListener("scroll", this.#onScroll, {passive: true});
169
+ this.#root.addEventListener("scroll", this.#onScroll, { passive: true });
167
170
  // Размер тела зависит от layout'а и появляется только после вставки в документ:
168
171
  // по нему считается окно, поэтому пересобираем его при изменении размера. Работу
169
172
  // уводим в кадр: правки layout внутри колбэка ResizeObserver дают
170
173
  // «ResizeObserver loop completed with undelivered notifications»
171
- this.#resize_observer = new ResizeObserver(() => this.#scheduleRefresh());
172
- this.#resize_observer.observe(this.#root);
174
+ this.#resizeObserver = new ResizeObserver(() => this.#scheduleRefresh());
175
+ this.#resizeObserver.observe(this.#root);
173
176
  this.#bound = true;
174
177
  }
175
178
 
176
179
  // === ЧТЕНИЕ ===
177
- getId() { return this.#id; }
178
- getItemHeight() { return this.#item_height; }
180
+ getId() {
181
+ return this.#id;
182
+ }
183
+ getItemHeight() {
184
+ return this.#itemHeight;
185
+ }
179
186
  /** Окно, которое сейчас в DOM (вместе с запасом `buffer`) */
180
- getRenderedRange() { return {...this.#window_state}; }
187
+ getRenderedRange() {
188
+ return { ...this.#windowState };
189
+ }
181
190
  /** Только видимые строки — без запаса; это то, что реально видит пользователь */
182
191
  getVisibleRange() {
183
192
  const from = this.#firstVisible();
184
- return {from: from, to: Math.min(this.#items.length, from + this.#visibleCount())};
193
+ return { from: from, to: Math.min(this.#items.length, from + this.#visibleCount()) };
185
194
  }
186
195
  /** Узел строки, если он сейчас в окне: узлов на всё содержимое нет */
187
- getItemElement(index = Number()) { return this.#nodes.get(index); }
196
+ getItemElement(index = Number()) {
197
+ return this.#nodes.get(index);
198
+ }
188
199
  /** Корень тела: его вставляет сетка */
189
- set() { return this.#root; }
200
+ set() {
201
+ return this.#root;
202
+ }
190
203
 
191
204
  // === ИЗМЕНЕНИЕ ===
192
205
  setHeight(height = String()) {
@@ -200,7 +213,7 @@ class DataGridBody {
200
213
  }
201
214
 
202
215
  setItemHeight(height = Number()) {
203
- this.#item_height = height;
216
+ this.#itemHeight = height;
204
217
  for (const node of this.#nodes.values()) node.style.height = `${height}px`;
205
218
  return this.refresh();
206
219
  }
@@ -214,18 +227,18 @@ class DataGridBody {
214
227
  return this;
215
228
  }
216
229
 
217
- setHasMore(has_more = true) {
218
- this.#has_more = has_more === true;
230
+ setHasMore(hasMore = true) {
231
+ this.#hasMore = hasMore === true;
219
232
  this.#applyLoader();
220
- if (this.#has_more) this.#checkLoadMore(this.#window_state.to);
233
+ if (this.#hasMore) this.#checkLoadMore(this.#windowState.to);
221
234
  return this;
222
235
  }
223
236
 
224
237
  setLoadingMore(loading = true) {
225
- this.#loading_more = loading === true;
238
+ this.#loadingMore = loading === true;
226
239
  this.#applyLoader();
227
240
  // Загрузка завершилась, но окно всё ещё у конца — возможно, нужен следующий запрос
228
- if (!this.#loading_more) this.#checkLoadMore(this.#window_state.to);
241
+ if (!this.#loadingMore) this.#checkLoadMore(this.#windowState.to);
229
242
  return this;
230
243
  }
231
244
 
@@ -239,7 +252,7 @@ class DataGridBody {
239
252
  const count = this.#items.length;
240
253
  if (count === 0) return this;
241
254
  const target = Math.max(0, Math.min(index, count - 1));
242
- const height = this.#item_height;
255
+ const height = this.#itemHeight;
243
256
  const view = this.#root.clientHeight || this.#visibleCount() * height;
244
257
  let top = this.#root.scrollTop;
245
258
  if (align === DataGridBody.ALIGN.START) {
@@ -255,8 +268,8 @@ class DataGridBody {
255
268
  if (target < from) top = target * height;
256
269
  else if (target >= to) top = (target + 1) * height - view;
257
270
  }
258
- const max_top = Math.max(0, count * height - view);
259
- top = Math.max(0, Math.min(top, max_top));
271
+ const maximumTop = Math.max(0, count * height - view);
272
+ top = Math.max(0, Math.min(top, maximumTop));
260
273
  if (top !== this.#root.scrollTop) this.#root.scrollTop = top;
261
274
  // Окно пересобираем сразу: не ждём события scroll, иначе состояние видно «на кадр позже»
262
275
  this.#renderWindow(this.#firstVisible());
@@ -271,14 +284,14 @@ class DataGridBody {
271
284
  cancelAnimationFrame(this.#raf);
272
285
  this.#raf = null;
273
286
  }
274
- if (this.#refresh_raf !== null) {
275
- cancelAnimationFrame(this.#refresh_raf);
276
- this.#refresh_raf = null;
287
+ if (this.#refreshRaf !== null) {
288
+ cancelAnimationFrame(this.#refreshRaf);
289
+ this.#refreshRaf = null;
277
290
  }
278
291
  this.#root.removeEventListener("scroll", this.#onScroll);
279
- if (this.#resize_observer !== undefined) {
280
- this.#resize_observer.disconnect();
281
- this.#resize_observer = undefined;
292
+ if (this.#resizeObserver !== undefined) {
293
+ this.#resizeObserver.disconnect();
294
+ this.#resizeObserver = undefined;
282
295
  }
283
296
  for (const node of this.#nodes.values()) {
284
297
  node.removeEventListener("click", this.#onItemClick);
@@ -289,35 +302,35 @@ class DataGridBody {
289
302
 
290
303
  // === ВНУТРЕННЕЕ ===
291
304
  #scheduleRefresh() {
292
- if (this.#refresh_raf !== null) return;
293
- this.#refresh_raf = requestAnimationFrame(() => {
294
- this.#refresh_raf = null;
305
+ if (this.#refreshRaf !== null) return;
306
+ this.#refreshRaf = requestAnimationFrame(() => {
307
+ this.#refreshRaf = null;
295
308
  if (this.#bound) this.refresh();
296
309
  });
297
310
  }
298
311
 
299
312
  #applyLoader() {
300
- this.#loader.innerText = this.#loading_text;
301
- this.#loader.hidden = !this.#loading_more;
302
- this.#root.dataset.loading = this.#loading_more ? "true" : "false";
313
+ this.#loader.innerText = this.#loadingText;
314
+ this.#loader.hidden = !this.#loadingMore;
315
+ this.#root.dataset.loading = this.#loadingMore ? "true" : "false";
303
316
  this.#syncEmpty();
304
317
  }
305
318
 
306
319
  // «Нет данных» показываем только когда данных нет и новых не ждём: иначе текст пустого
307
320
  // состояния спорил бы с индикатором догрузки на время запроса
308
321
  #syncEmpty() {
309
- this.#empty.hidden = this.#items.length > 0 || this.#has_more || this.#loading_more;
322
+ this.#empty.hidden = this.#items.length > 0 || this.#hasMore || this.#loadingMore;
310
323
  }
311
324
 
312
325
  #firstVisible() {
313
326
  const count = this.#items.length;
314
327
  if (count === 0) return 0;
315
- return Math.min(count - 1, Math.max(0, Math.floor(Math.max(0, this.#root.scrollTop) / this.#item_height)));
328
+ return Math.min(count - 1, Math.max(0, Math.floor(Math.max(0, this.#root.scrollTop) / this.#itemHeight)));
316
329
  }
317
330
 
318
331
  #visibleCount() {
319
- const view = this.#root.clientHeight || this.#item_height * 8;
320
- return Math.max(1, Math.ceil(view / this.#item_height));
332
+ const view = this.#root.clientHeight || this.#itemHeight * 8;
333
+ return Math.max(1, Math.ceil(view / this.#itemHeight));
321
334
  }
322
335
 
323
336
  /** Последний индекс окна: запас `buffer` считается в строках, а не в пикселях */
@@ -336,7 +349,7 @@ class DataGridBody {
336
349
  this.#nodes.delete(index);
337
350
  }
338
351
  }
339
- let previous = this.#spacer_top;
352
+ let previous = this.#spacerTop;
340
353
  for (let index = from; index < to; index++) {
341
354
  let node = this.#nodes.get(index);
342
355
  if (node === undefined) {
@@ -344,49 +357,49 @@ class DataGridBody {
344
357
  this.#nodes.set(index, node);
345
358
  }
346
359
  // Высоту задаём инлайном: иначе фактическая геометрия разошлась бы с распорками
347
- const height = `${this.#item_height}px`;
360
+ const height = `${this.#itemHeight}px`;
348
361
  if (node.style.height !== height) node.style.height = height;
349
362
  // insertBefore(previous.nextSibling) ставит узел сразу за предыдущим и не трогает
350
363
  // остальные, если порядок уже верный
351
364
  if (node.previousSibling !== previous) this.#root.insertBefore(node, previous.nextSibling);
352
365
  previous = node;
353
366
  }
354
- this.#window_state = {from: from, to: to};
367
+ this.#windowState = { from: from, to: to };
355
368
  this.#syncSpacers();
356
- if (this.#on_render !== undefined) this.#on_render({from: from, to: to});
369
+ if (this.#onRender !== undefined) this.#onRender({ from: from, to: to });
357
370
  this.#checkLoadMore(to);
358
371
  }
359
372
 
360
373
  #syncSpacers() {
361
374
  const count = this.#items.length;
362
- const {from, to} = this.#window_state;
363
- this.#spacer_top.style.height = count === 0 ? "0px" : `${from * this.#item_height}px`;
364
- this.#spacer_bottom.style.height = count === 0 ? "0px" : `${Math.max(0, (count - to) * this.#item_height)}px`;
375
+ const { from, to } = this.#windowState;
376
+ this.#spacerTop.style.height = count === 0 ? "0px" : `${from * this.#itemHeight}px`;
377
+ this.#spacerBottom.style.height = count === 0 ? "0px" : `${Math.max(0, (count - to) * this.#itemHeight)}px`;
365
378
  }
366
379
 
367
380
  #createItem(index = Number()) {
368
- const node = document.createElement(this.#item_tag);
381
+ const node = document.createElement(this.#itemTag);
369
382
  node.id = `${this.#id}_item_${index}`;
370
383
  node.dataset.index = String(index);
371
- if (this.#render_item === undefined) node.innerText = String(this.#items[index]);
372
- else this.#render_item(this.#items[index], index, node);
384
+ if (this.#renderItem === undefined) node.innerText = String(this.#items[index]);
385
+ else this.#renderItem(this.#items[index], index, node);
373
386
  node.addEventListener("click", this.#onItemClick);
374
387
  node.addEventListener("dblclick", this.#onItemDblClick);
375
388
  return node;
376
389
  }
377
390
 
378
391
  #checkLoadMore(to = Number()) {
379
- if (this.#on_load_more === undefined || this.#loading_more || !this.#has_more) return;
392
+ if (this.#onLoadMore === undefined || this.#loadingMore || !this.#hasMore) return;
380
393
  const count = this.#items.length;
381
394
  // Тело пусто или окно дошло до конца загруженного
382
- if (count > 0 && to < count - this.#load_threshold) return;
395
+ if (count > 0 && to < count - this.#loadThreshold) return;
383
396
  // Запрос на такую же длину уже был: ждём данных, а не повторяем в пустоту
384
- if (this.#requested_at === count) return;
385
- this.#requested_at = count;
386
- this.#loading_more = true;
397
+ if (this.#requestedAt === count) return;
398
+ this.#requestedAt = count;
399
+ this.#loadingMore = true;
387
400
  this.#applyLoader();
388
401
  try {
389
- this.#on_load_more(count);
402
+ this.#onLoadMore(count);
390
403
  } catch (error) {
391
404
  // Ошибка обработчика не должна оставлять тело «в загрузке»;
392
405
  // #requested_at не сбрасываем — повтор это `DataGrid.loadMore()`
@@ -399,24 +412,24 @@ class DataGridBody {
399
412
  let node = event.target;
400
413
  while (node !== null && node !== this.#root) {
401
414
  if (node.parentElement === this.#root && node.dataset.index !== undefined) {
402
- return {index: Number(node.dataset.index), node: node};
415
+ return { index: Number(node.dataset.index), node: node };
403
416
  }
404
417
  node = node.parentElement;
405
418
  }
406
- return {index: -1, node: undefined};
419
+ return { index: -1, node: undefined };
407
420
  }
408
421
 
409
422
  #onItemClick = (event) => {
410
- const {index, node} = this.#itemFromEvent(event);
423
+ const { index, node } = this.#itemFromEvent(event);
411
424
  if (index === -1) return;
412
425
  // Событие сетке нужно целиком: по нему она читает модификаторы Ctrl/Shift
413
- if (this.#on_item_click !== undefined) this.#on_item_click(this.#items[index], index, node, event);
426
+ if (this.#onItemClickCallback !== undefined) this.#onItemClickCallback(this.#items[index], index, node, event);
414
427
  };
415
428
 
416
429
  #onItemDblClick = (event) => {
417
- const {index, node} = this.#itemFromEvent(event);
430
+ const { index, node } = this.#itemFromEvent(event);
418
431
  if (index === -1) return;
419
- if (this.#on_activate !== undefined) this.#on_activate(this.#items[index], index, node);
432
+ if (this.#onActivate !== undefined) this.#onActivate(this.#items[index], index, node);
420
433
  };
421
434
 
422
435
  #onScroll = () => {
@@ -424,7 +437,7 @@ class DataGridBody {
424
437
  this.#raf = requestAnimationFrame(() => {
425
438
  this.#raf = null;
426
439
  const from = this.#firstVisible();
427
- const state = this.#window_state;
440
+ const state = this.#windowState;
428
441
  // Видимая часть уже внутри отрисованного окна — перерисовка не нужна
429
442
  if (from >= state.from && this.#endIndex(from) <= state.to) return;
430
443
  this.#renderWindow(from);
@@ -436,24 +449,24 @@ class DataGrid {
436
449
  static SELECTION = {
437
450
  NONE: "none",
438
451
  SINGLE: "single",
439
- MULTI: "multi"
452
+ MULTI: "multi",
440
453
  };
441
454
  static SORT = {
442
455
  NONE: "none",
443
456
  ASC: "asc",
444
- DESC: "desc"
457
+ DESC: "desc",
445
458
  };
446
459
  static SORT_ATTRIBUTE = {
447
460
  ASC: "ascending",
448
461
  DESC: "descending",
449
- NONE: "none"
462
+ NONE: "none",
450
463
  };
451
464
  /** Тип редактора ячейки (`column.editType`) */
452
465
  static EDIT_TYPE = {
453
466
  TEXT: "text",
454
467
  NUMBER: "number",
455
468
  CHECKBOX: "checkbox",
456
- SELECT: "select"
469
+ SELECT: "select",
457
470
  };
458
471
  /** Виды агрегатов для группировки (`aggregates`) */
459
472
  static AGGREGATE = {
@@ -461,7 +474,7 @@ class DataGrid {
461
474
  AVG: "avg",
462
475
  MIN: "min",
463
476
  MAX: "max",
464
- COUNT: "count"
477
+ COUNT: "count",
465
478
  };
466
479
  // Значения — ключи словаря: подпись агрегата берётся при показе
467
480
  static AGGREGATE_LABEL = {
@@ -469,12 +482,12 @@ class DataGrid {
469
482
  avg: "dataGrid.aggregate.avg",
470
483
  min: "dataGrid.aggregate.min",
471
484
  max: "dataGrid.aggregate.max",
472
- count: "dataGrid.aggregate.count"
485
+ count: "dataGrid.aggregate.count",
473
486
  };
474
487
  /** Что экспортирует `exportCSV()`: текущий вид или все загруженные строки */
475
488
  static EXPORT = {
476
489
  VIEW: "view",
477
- ALL: "all"
490
+ ALL: "all",
478
491
  };
479
492
  /** Куда выравнивать строку в `scrollToRow()` (тело сетки — своё, см. `DataGridBody`) */
480
493
  static ALIGN = DataGridBody.ALIGN;
@@ -492,13 +505,13 @@ class DataGrid {
492
505
  #scroll = document.createElement("data_grid_scroll");
493
506
  #bar = document.createElement("data_grid_bar");
494
507
  #head = document.createElement("data_grid_head");
495
- #head_row = document.createElement("data_grid_row");
508
+ #headRow = document.createElement("data_grid_row");
496
509
  #error = document.createElement("data_grid_error");
497
- #error_text = document.createElement("data_grid_error_text");
510
+ #errorText = document.createElement("data_grid_error_text");
498
511
  #status = document.createElement("data_grid_status");
499
512
  #list = undefined;
500
- #chooser_dialog = undefined;
501
- #chooser_block = undefined;
513
+ #chooserDialog = undefined;
514
+ #chooserBlock = undefined;
502
515
 
503
516
  // === СОСТОЯНИЕ ===
504
517
  #id = String();
@@ -507,26 +520,26 @@ class DataGrid {
507
520
  // Видимость по колонкам: ключ скрытой колонки лежит здесь, поэтому DOM собирается по видимым
508
521
  #hidden = new Set();
509
522
  #pinned = [];
510
- #column_reorder = false;
523
+ #columnReorder = false;
511
524
  #data = [];
512
- #row_height = DataGrid.DEFAULT_ROW_HEIGHT;
525
+ #rowHeight = DataGrid.DEFAULT_ROW_HEIGHT;
513
526
  #selection = DataGrid.SELECTION.SINGLE;
514
527
  #selected = new Set();
515
528
  #anchor = undefined;
516
529
  // Индекс строки в исходных данных: `render(row, index)` и ключ по умолчанию
517
- #row_index = new Map();
530
+ #rowIndex = new Map();
518
531
  #filter = "";
519
- #filter_keys = [];
520
- #sort = {key: undefined, direction: DataGrid.SORT.NONE};
521
- #group_by = undefined;
522
- #group_label = undefined;
532
+ #filterKeys = [];
533
+ #sort = { key: undefined, direction: DataGrid.SORT.NONE };
534
+ #groupBy = undefined;
535
+ #groupLabel = undefined;
523
536
  #aggregates = {};
524
- #group_collapsed_default = false;
537
+ #groupCollapsedDefault = false;
525
538
  #collapsed = new Map();
526
539
  #editable = undefined;
527
540
  #editing = undefined;
528
- #active = {row: 0, col: 0};
529
- #entry_count = 0;
541
+ #active = { row: 0, col: 0 };
542
+ #entryCount = 0;
530
543
 
531
544
  // Производные данные (пересобираются одним проходом — см. #recompute)
532
545
  #filtered = [];
@@ -535,103 +548,107 @@ class DataGrid {
535
548
  #entries = [];
536
549
 
537
550
  // Серверный режим
538
- #data_source = undefined;
539
- #page_size = DataGrid.DEFAULT_PAGE_SIZE;
540
- #server_total = undefined;
551
+ #dataSource = undefined;
552
+ #pageSize = DataGrid.DEFAULT_PAGE_SIZE;
553
+ #serverTotal = undefined;
541
554
  #loading = false;
542
555
  #failed = false;
543
- #has_more = false;
556
+ #hasMore = false;
544
557
  #request = 0;
545
558
 
546
559
  // Подписчики
547
- #on_row_activate = undefined;
548
- #on_selection_change = undefined;
549
- #on_cell_edit = undefined;
550
- #on_active_cell_change = undefined;
551
- #on_column_order_change = undefined;
552
- #on_data_load = undefined;
553
- #on_data_error = undefined;
554
- #row_class = undefined;
555
- #custom_key = undefined;
556
- #empty_text = undefined;
557
- #context_menu = undefined;
558
- #resize_observer = undefined;
560
+ #onRowActivate = undefined;
561
+ #onSelectionChange = undefined;
562
+ #onCellEdit = undefined;
563
+ #onActiveCellChange = undefined;
564
+ #onColumnOrderChange = undefined;
565
+ #onDataLoad = undefined;
566
+ #onDataError = undefined;
567
+ #rowClass = undefined;
568
+ #customKey = undefined;
569
+ #emptyText = undefined;
570
+ #contextMenu = undefined;
571
+ #resizeObserver = undefined;
559
572
 
560
573
  // Перестановка колонок
561
574
  #drag = undefined;
562
575
 
563
- constructor(options = {
564
- id: String(),
565
- columns: Array(),
566
- data: Array(),
567
- rowHeight: Number(),
568
- height: String(),
569
- buffer: Number(),
570
- selectable: String(),
571
- rowKey: undefined,
572
- filterKeys: Array(),
573
- emptyText: String(),
574
- label: String(),
575
- editable: undefined,
576
- pinned: undefined,
577
- hiddenColumns: Array(),
578
- columnChooser: Boolean(),
579
- columnReorder: Boolean(),
580
- dataSource: undefined,
581
- pageSize: Number(),
582
- rowClass: undefined,
583
- groupBy: undefined,
584
- groupLabel: undefined,
585
- aggregates: Object(),
586
- groupCollapsed: Boolean(),
587
- onRowActivate: undefined,
588
- onSelectionChange: undefined,
589
- onCellEdit: undefined,
590
- onActiveCellChange: undefined,
591
- onColumnOrderChange: undefined,
592
- onDataLoad: undefined,
593
- onDataError: undefined,
594
- }) {
595
- setStyles(__dirname + "/styles.css", 'chUiJS_DataGrid');
576
+ constructor(
577
+ options = {
578
+ id: String(),
579
+ columns: Array(),
580
+ data: Array(),
581
+ rowHeight: Number(),
582
+ height: String(),
583
+ buffer: Number(),
584
+ selectable: String(),
585
+ rowKey: undefined,
586
+ filterKeys: Array(),
587
+ emptyText: String(),
588
+ label: String(),
589
+ editable: undefined,
590
+ pinned: undefined,
591
+ hiddenColumns: Array(),
592
+ columnChooser: Boolean(),
593
+ columnReorder: Boolean(),
594
+ dataSource: undefined,
595
+ pageSize: Number(),
596
+ rowClass: undefined,
597
+ groupBy: undefined,
598
+ groupLabel: undefined,
599
+ aggregates: Object(),
600
+ groupCollapsed: Boolean(),
601
+ onRowActivate: undefined,
602
+ onSelectionChange: undefined,
603
+ onCellEdit: undefined,
604
+ onActiveCellChange: undefined,
605
+ onColumnOrderChange: undefined,
606
+ onDataLoad: undefined,
607
+ onDataError: undefined,
608
+ }
609
+ ) {
610
+ setStyles(__dirname + "/styles.css", "chUiJS_DataGrid");
596
611
  DataGrid.#counter = DataGrid.#counter + 1;
597
612
  this.#id = optionOr(options.id, `chui_data_grid_${DataGrid.#counter}`);
598
613
  this.#columns = DataGrid.#normalizeColumns(options.columns);
599
614
  this.#hidden = new Set(DataGrid.#normalizeKeys(options.hiddenColumns, this.#columns));
600
615
  this.#data = Array.isArray(options.data) ? options.data : [];
601
- this.#row_height = options.rowHeight === undefined ? DataGrid.DEFAULT_ROW_HEIGHT : options.rowHeight;
616
+ this.#rowHeight = options.rowHeight === undefined ? DataGrid.DEFAULT_ROW_HEIGHT : options.rowHeight;
602
617
  this.#selection = DataGrid.#normalizeSelection(options.selectable);
603
- this.#custom_key = options.rowKey;
604
- this.#filter_keys = DataGrid.#normalizeKeys(options.filterKeys, this.#columns);
605
- this.#empty_text = options.emptyText;
618
+ this.#customKey = options.rowKey;
619
+ this.#filterKeys = DataGrid.#normalizeKeys(options.filterKeys, this.#columns);
620
+ this.#emptyText = options.emptyText;
606
621
  this.#editable = options.editable;
607
- this.#group_by = options.groupBy;
608
- this.#group_label = options.groupLabel;
622
+ this.#groupBy = options.groupBy;
623
+ this.#groupLabel = options.groupLabel;
609
624
  this.#aggregates = options.aggregates === undefined ? {} : options.aggregates;
610
- this.#group_collapsed_default = options.groupCollapsed === true;
611
- this.#row_class = options.rowClass;
612
- this.#on_row_activate = options.onRowActivate;
613
- this.#on_selection_change = options.onSelectionChange;
614
- this.#on_cell_edit = options.onCellEdit;
615
- this.#on_active_cell_change = options.onActiveCellChange;
616
- this.#on_column_order_change = options.onColumnOrderChange;
617
- this.#on_data_load = options.onDataLoad;
618
- this.#on_data_error = options.onDataError;
619
- this.#data_source = options.dataSource;
620
- this.#page_size = DataGrid.#normalizePageSize(options.pageSize);
621
- this.#column_reorder = options.columnReorder === true;
625
+ this.#groupCollapsedDefault = options.groupCollapsed === true;
626
+ this.#rowClass = options.rowClass;
627
+ this.#onRowActivate = options.onRowActivate;
628
+ this.#onSelectionChange = options.onSelectionChange;
629
+ this.#onCellEdit = options.onCellEdit;
630
+ this.#onActiveCellChange = options.onActiveCellChange;
631
+ this.#onColumnOrderChange = options.onColumnOrderChange;
632
+ this.#onDataLoad = options.onDataLoad;
633
+ this.#onDataError = options.onDataError;
634
+ this.#dataSource = options.dataSource;
635
+ this.#pageSize = DataGrid.#normalizePageSize(options.pageSize);
636
+ this.#columnReorder = options.columnReorder === true;
622
637
  this.setPinned(options.pinned);
623
638
 
624
639
  // === сборка разметки ===
625
640
  this.#root.id = this.#id;
626
641
  this.#root.setAttribute("role", "grid");
627
642
  this.#head.setAttribute("role", "rowgroup");
628
- this.#head_row.setAttribute("role", "row");
629
- this.#head_row.setAttribute("aria-rowindex", "1");
630
- this.#head.appendChild(this.#head_row);
643
+ this.#headRow.setAttribute("role", "row");
644
+ this.#headRow.setAttribute("aria-rowindex", "1");
645
+ this.#head.appendChild(this.#headRow);
631
646
  this.#scroll.appendChild(this.#head);
632
647
  this.#root.appendChild(this.#scroll);
633
- this.#error.appendChild(this.#error_text);
634
- this.#error.appendChild(new Button({title: I18n.t("dataGrid.retry"), clickEvent: () => this.retryLoad()}).set());
648
+ this.#error.appendChild(this.#errorText);
649
+ this.#error.appendChild(
650
+ new Button({ title: I18n.t("dataGrid.retry"), clickEvent: () => this.retryLoad() }).set()
651
+ );
635
652
  this.#error.hidden = true;
636
653
  this.#root.insertBefore(this.#error, this.#scroll);
637
654
  this.#status.classList.add("chui_visually_hidden");
@@ -644,10 +661,10 @@ class DataGrid {
644
661
  this.#syncColumns();
645
662
  this.#list = new DataGridBody({
646
663
  itemTag: "data_grid_row",
647
- itemHeight: this.#row_height,
664
+ itemHeight: this.#rowHeight,
648
665
  buffer: options.buffer,
649
666
  height: options.height === undefined ? DataGrid.DEFAULT_HEIGHT : options.height,
650
- emptyText: this.#empty_text,
667
+ emptyText: this.#emptyText,
651
668
  hasMore: false,
652
669
  onLoadMore: () => this.loadMore(),
653
670
  renderItem: (entry, index, node) => this.#renderRow(entry, index, node),
@@ -666,32 +683,32 @@ class DataGrid {
666
683
  this.#syncActiveCells();
667
684
  // Смещения закреплённых колонок считаются по отрисованным узлам
668
685
  this.#syncPinned();
669
- }
686
+ },
670
687
  });
671
688
  this.#scroll.appendChild(this.#list.set());
672
689
  this.#root.appendChild(this.#status);
673
690
 
674
691
  // Заголовок: делегирование вместо подписки на каждую ячейку
675
- this.#head_row.addEventListener("click", this.#onHeaderClick);
676
- this.#head_row.addEventListener("keydown", this.#onHeaderKeyDown);
677
- this.#head_row.addEventListener("dragstart", this.#onHeaderDragStart);
678
- this.#head_row.addEventListener("dragover", this.#onHeaderDragOver);
679
- this.#head_row.addEventListener("dragleave", this.#onHeaderDragLeave);
680
- this.#head_row.addEventListener("drop", this.#onHeaderDrop);
681
- this.#head_row.addEventListener("dragend", this.#onHeaderDragEnd);
692
+ this.#headRow.addEventListener("click", this.#onHeaderClick);
693
+ this.#headRow.addEventListener("keydown", this.#onHeaderKeyDown);
694
+ this.#headRow.addEventListener("dragstart", this.#onHeaderDragStart);
695
+ this.#headRow.addEventListener("dragover", this.#onHeaderDragOver);
696
+ this.#headRow.addEventListener("dragleave", this.#onHeaderDragLeave);
697
+ this.#headRow.addEventListener("drop", this.#onHeaderDrop);
698
+ this.#headRow.addEventListener("dragend", this.#onHeaderDragEnd);
682
699
  // capture: правку начинает перехват, иначе тело списка успеет сообщить об активации
683
700
  this.#root.addEventListener("dblclick", this.#onDoubleClick, true);
684
701
  this.#root.addEventListener("keydown", this.#onKeyDown);
685
702
  // Ширины закреплённых колонок зависят от размера окна — пересчитываем при изменении
686
- this.#resize_observer = new ResizeObserver(() => this.#syncPinned());
687
- this.#resize_observer.observe(this.#scroll);
703
+ this.#resizeObserver = new ResizeObserver(() => this.#syncPinned());
704
+ this.#resizeObserver.observe(this.#scroll);
688
705
  // Горизонтальная прокрутка: закреплённые колонки удерживаем на месте
689
- this.#scroll.addEventListener("scroll", this.#onScrollX, {passive: true});
706
+ this.#scroll.addEventListener("scroll", this.#onScrollX, { passive: true });
690
707
 
691
708
  this.#reindex();
692
709
  this.#buildHeader();
693
710
  this.#recompute();
694
- if (this.#data_source !== undefined) this.reload();
711
+ if (this.#dataSource !== undefined) this.reload();
695
712
  }
696
713
 
697
714
  // === НОРМАЛИЗАЦИЯ ВХОДА ===
@@ -778,8 +795,8 @@ class DataGrid {
778
795
  /** Ячейка CSV: кавычки удваиваются, разделитель и перевод строки берутся в кавычки */
779
796
  static #csvCell(value = undefined, delimiter = String()) {
780
797
  const text = String(value === undefined || value === null ? "" : value);
781
- const quoted = text.includes("\"") || text.includes(delimiter) || text.includes("\n") || text.includes("\r");
782
- return quoted ? `"${text.replace(/"/g, "\"\"")}"` : text;
798
+ const quoted = text.includes('"') || text.includes(delimiter) || text.includes("\n") || text.includes("\r");
799
+ return quoted ? `"${text.replace(/"/g, '""')}"` : text;
783
800
  }
784
801
 
785
802
  /** Ответ сервера: массив строк или `{rows, total}` */
@@ -826,13 +843,16 @@ class DataGrid {
826
843
  }
827
844
  }
828
845
  }
829
- this.#entry_count = this.#entries.length;
846
+ this.#entryCount = this.#entries.length;
830
847
  // Число строк для скринридера: элементы списка плюс строка заголовка, а в серверном
831
848
  // режиме — то, что сообщил сервер (иначе он знал бы только про загруженное окно)
832
- this.#root.setAttribute("aria-rowcount", String((this.#server_total === undefined ? this.#entry_count : this.#server_total) + 1));
849
+ this.#root.setAttribute(
850
+ "aria-rowcount",
851
+ String((this.#serverTotal === undefined ? this.#entryCount : this.#serverTotal) + 1)
852
+ );
833
853
  this.#syncColumns();
834
854
  this.#list.setItems(this.#entries);
835
- this.#list.setHasMore(this.#has_more);
855
+ this.#list.setHasMore(this.#hasMore);
836
856
  this.#syncActiveCells();
837
857
  this.#syncPinned();
838
858
  }
@@ -857,12 +877,14 @@ class DataGrid {
857
877
 
858
878
  #filterRows(rows = []) {
859
879
  // Серверный режим фильтрует сам: локальный фильтр включился бы поверх серверного
860
- if (this.#data_source !== undefined) return rows.slice();
880
+ if (this.#dataSource !== undefined) return rows.slice();
861
881
  const text = this.#filter.trim().toLowerCase();
862
- if (text === "" || this.#filter_keys.length === 0) return rows.slice();
863
- return rows.filter((row) => this.#filter_keys.some((key) => {
864
- return DataGrid.#valueText(row[key]).toLowerCase().includes(text);
865
- }));
882
+ if (text === "" || this.#filterKeys.length === 0) return rows.slice();
883
+ return rows.filter((row) =>
884
+ this.#filterKeys.some((key) => {
885
+ return DataGrid.#valueText(row[key]).toLowerCase().includes(text);
886
+ })
887
+ );
866
888
  }
867
889
 
868
890
  /** Сортировка стабильная: при равных значениях строки остаются в исходном порядке */
@@ -870,11 +892,13 @@ class DataGrid {
870
892
  const key = this.#sort.key;
871
893
  if (key === undefined || this.#sort.direction === DataGrid.SORT.NONE) return rows.slice();
872
894
  const column = this.#columns.find((item) => item.key === key);
873
- const compare = column !== undefined && typeof column.compare === "function"
874
- ? column.compare
875
- : (left, right) => DataGrid.#compareValues(left[key], right[key]);
895
+ const compare =
896
+ column !== undefined && typeof column.compare === "function"
897
+ ? column.compare
898
+ : (left, right) => DataGrid.#compareValues(left[key], right[key]);
876
899
  const sign = this.#sort.direction === DataGrid.SORT.DESC ? -1 : 1;
877
- return rows.map((row, index) => ({row: row, index: index}))
900
+ return rows
901
+ .map((row, index) => ({ row: row, index: index }))
878
902
  .sort((left, right) => {
879
903
  const result = compare(left.row, right.row);
880
904
  if (result === 0 || Number.isNaN(result)) return left.index - right.index;
@@ -893,29 +917,27 @@ class DataGrid {
893
917
  const leftNumber = Number(left);
894
918
  const rightNumber = Number(right);
895
919
  if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber - rightNumber;
896
- return String(left).localeCompare(String(right), undefined, {numeric: true, sensitivity: "base"});
920
+ return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: "base" });
897
921
  }
898
922
 
899
923
  /** Группы: порядок — первое появление ключа, поэтому он не зависит от сортировки */
900
924
  #buildGroups(rows = []) {
901
- if (this.#group_by === undefined) return [];
902
- const keyOf = typeof this.#group_by === "function" ? this.#group_by : (row) => row[this.#group_by];
925
+ if (this.#groupBy === undefined) return [];
926
+ const keyOf = typeof this.#groupBy === "function" ? this.#groupBy : (row) => row[this.#groupBy];
903
927
  const map = new Map();
904
928
  for (const row of rows) {
905
929
  const raw = keyOf(row);
906
- const key = raw === undefined || raw === null || raw === ""
907
- ? I18n.t("dataGrid.groupNoValue")
908
- : String(raw);
930
+ const key = raw === undefined || raw === null || raw === "" ? I18n.t("dataGrid.groupNoValue") : String(raw);
909
931
  if (map.has(key) === false) map.set(key, []);
910
932
  map.get(key).push(row);
911
933
  }
912
934
  const out = [];
913
- for (const [key, group_rows] of map) {
935
+ for (const [key, groupRows] of map) {
914
936
  out.push({
915
937
  key: key,
916
- rows: group_rows,
917
- aggregates: this.#aggregateValues(group_rows),
918
- collapsed: this.#collapsed.has(key) ? this.#collapsed.get(key) : this.#group_collapsed_default
938
+ rows: groupRows,
939
+ aggregates: this.#aggregateValues(groupRows),
940
+ collapsed: this.#collapsed.has(key) ? this.#collapsed.get(key) : this.#groupCollapsedDefault,
919
941
  });
920
942
  }
921
943
  return out;
@@ -928,23 +950,30 @@ class DataGrid {
928
950
  const spec = this.#aggregates[key];
929
951
  const column = this.#columns.find((item) => item.key === key);
930
952
  if (typeof spec === "function") {
931
- out[key] = {text: String(spec(rows, key)), label: column === undefined ? key : column.title};
953
+ out[key] = { text: String(spec(rows, key)), label: column === undefined ? key : column.title };
932
954
  continue;
933
955
  }
934
956
  const values = rows.map((row) => row[key]).filter((value) => value !== undefined && value !== null);
935
957
  const numbers = values.map((value) => Number(value)).filter((value) => Number.isFinite(value));
936
958
  let value = undefined;
937
959
  if (spec === DataGrid.AGGREGATE.COUNT) value = values.length;
938
- else if (spec === DataGrid.AGGREGATE.SUM) value = DataGrid.#round(numbers.reduce((sum, item) => sum + item, 0));
939
- else if (spec === DataGrid.AGGREGATE.AVG) value = numbers.length === 0 ? undefined : DataGrid.#round(numbers.reduce((sum, item) => sum + item, 0) / numbers.length);
960
+ else if (spec === DataGrid.AGGREGATE.SUM)
961
+ value = DataGrid.#round(numbers.reduce((sum, item) => sum + item, 0));
962
+ else if (spec === DataGrid.AGGREGATE.AVG)
963
+ value =
964
+ numbers.length === 0
965
+ ? undefined
966
+ : DataGrid.#round(numbers.reduce((sum, item) => sum + item, 0) / numbers.length);
940
967
  else if (spec === DataGrid.AGGREGATE.MIN) value = numbers.length === 0 ? undefined : Math.min(...numbers);
941
968
  else if (spec === DataGrid.AGGREGATE.MAX) value = numbers.length === 0 ? undefined : Math.max(...numbers);
942
- const label = I18n.t(DataGrid.AGGREGATE_LABEL[spec] === undefined ? "dataGrid.aggregate.sum" : DataGrid.AGGREGATE_LABEL[spec]);
969
+ const label = I18n.t(
970
+ DataGrid.AGGREGATE_LABEL[spec] === undefined ? "dataGrid.aggregate.sum" : DataGrid.AGGREGATE_LABEL[spec]
971
+ );
943
972
  out[key] = {
944
973
  text: value === undefined ? "" : String(value),
945
974
  label: column === undefined ? key : column.title,
946
975
  kind: label,
947
- area: `${column === undefined ? key : column.title}: ${label} ${value === undefined ? "" : value}`
976
+ area: `${column === undefined ? key : column.title}: ${label} ${value === undefined ? "" : value}`,
948
977
  };
949
978
  }
950
979
  return out;
@@ -959,7 +988,7 @@ class DataGrid {
959
988
  #syncColumns() {
960
989
  const visible = this.#visibleColumns();
961
990
  this.#root.style.setProperty("--data_grid_columns", visible.map((entry) => entry.column.width).join(" "));
962
- this.#root.style.setProperty("--data_grid_row_height", `${this.#row_height}px`);
991
+ this.#root.style.setProperty("--data_grid_row_height", `${this.#rowHeight}px`);
963
992
  const explicit = DataGrid.#explicitWidth(visible.map((entry) => entry.column));
964
993
  if (explicit > 0) this.#root.style.setProperty("--data_grid_min_width", `${explicit}px`);
965
994
  else this.#root.style.removeProperty("--data_grid_min_width");
@@ -972,7 +1001,7 @@ class DataGrid {
972
1001
  for (let index = 0; index < this.#columns.length; index++) {
973
1002
  const column = this.#columns[index];
974
1003
  if (this.#hidden.has(column.key)) continue;
975
- out.push({column: column, index: index});
1004
+ out.push({ column: column, index: index });
976
1005
  }
977
1006
  return out;
978
1007
  }
@@ -993,7 +1022,7 @@ class DataGrid {
993
1022
  }
994
1023
  }
995
1024
  this.#scroll.classList.toggle("data_grid_h_scroll", this.#scroll.scrollWidth > this.#scroll.clientWidth + 1);
996
- const nodes = [this.#head_row, ...this.#renderedRows()];
1025
+ const nodes = [this.#headRow, ...this.#renderedRows()];
997
1026
  for (const row of nodes) {
998
1027
  for (const cell of row.querySelectorAll("data_grid_cell[data-column]")) {
999
1028
  const index = Number(cell.dataset.column);
@@ -1008,7 +1037,7 @@ class DataGrid {
1008
1037
  }
1009
1038
 
1010
1039
  #cellWidth(index = Number()) {
1011
- const cell = this.#head_row.querySelector(`data_grid_cell[data-column="${index}"]`);
1040
+ const cell = this.#headRow.querySelector(`data_grid_cell[data-column="${index}"]`);
1012
1041
  return cell === null ? 0 : Math.round(cell.getBoundingClientRect().width);
1013
1042
  }
1014
1043
 
@@ -1028,7 +1057,7 @@ class DataGrid {
1028
1057
 
1029
1058
  // === ШАПКА ===
1030
1059
  #buildHeader() {
1031
- this.#head_row.innerHTML = "";
1060
+ this.#headRow.innerHTML = "";
1032
1061
  for (const entry of this.#visibleColumns()) {
1033
1062
  const column = entry.column;
1034
1063
  const cell = document.createElement("data_grid_cell");
@@ -1041,16 +1070,21 @@ class DataGrid {
1041
1070
  const sort = document.createElement("data_grid_sort");
1042
1071
  sort.setAttribute("aria-hidden", "true");
1043
1072
  cell.appendChild(sort);
1044
- cell.setAttribute("aria-sort", this.#sort.key === column.key ? DataGrid.SORT_ATTRIBUTE[this.#sort.direction === DataGrid.SORT.ASC ? "ASC" : "DESC"] : DataGrid.SORT_ATTRIBUTE.NONE);
1073
+ cell.setAttribute(
1074
+ "aria-sort",
1075
+ this.#sort.key === column.key
1076
+ ? DataGrid.SORT_ATTRIBUTE[this.#sort.direction === DataGrid.SORT.ASC ? "ASC" : "DESC"]
1077
+ : DataGrid.SORT_ATTRIBUTE.NONE
1078
+ );
1045
1079
  cell.setAttribute("tabindex", "0");
1046
1080
  // Подпись заголовка объясняет, что Enter и Space сортируют колонку
1047
- cell.setAttribute("aria-label", I18n.t("table.sortBy", {head: column.title}));
1081
+ cell.setAttribute("aria-label", I18n.t("table.sortBy", { head: column.title }));
1048
1082
  }
1049
- if (this.#column_reorder) {
1083
+ if (this.#columnReorder) {
1050
1084
  cell.setAttribute("draggable", "true");
1051
1085
  if (column.sortable === false) cell.setAttribute("tabindex", "0");
1052
1086
  }
1053
- this.#head_row.appendChild(cell);
1087
+ this.#headRow.appendChild(cell);
1054
1088
  this.#syncSortMark(cell, entry.index);
1055
1089
  }
1056
1090
  }
@@ -1061,20 +1095,24 @@ class DataGrid {
1061
1095
  const sort = cell.querySelector("data_grid_sort");
1062
1096
  if (sort === null) return;
1063
1097
  const active = this.#sort.key === column.key && this.#sort.direction !== DataGrid.SORT.NONE;
1064
- cell.setAttribute("aria-sort", active
1065
- ? DataGrid.SORT_ATTRIBUTE[this.#sort.direction === DataGrid.SORT.ASC ? "ASC" : "DESC"]
1066
- : DataGrid.SORT_ATTRIBUTE.NONE);
1098
+ cell.setAttribute(
1099
+ "aria-sort",
1100
+ active
1101
+ ? DataGrid.SORT_ATTRIBUTE[this.#sort.direction === DataGrid.SORT.ASC ? "ASC" : "DESC"]
1102
+ : DataGrid.SORT_ATTRIBUTE.NONE
1103
+ );
1067
1104
  if (active === false) {
1068
1105
  sort.innerHTML = "";
1069
1106
  return;
1070
1107
  }
1071
- const icon = this.#sort.direction === DataGrid.SORT.ASC ? Icons.NAVIGATION.EXPAND_LESS : Icons.NAVIGATION.EXPAND_MORE;
1108
+ const icon =
1109
+ this.#sort.direction === DataGrid.SORT.ASC ? Icons.NAVIGATION.EXPAND_LESS : Icons.NAVIGATION.EXPAND_MORE;
1072
1110
  // Значок состояния — акцент, как подпись отсортированной колонки
1073
1111
  sort.innerHTML = new Icon(icon, "16px", "var(--blue_prime_background)").getHTML();
1074
1112
  }
1075
1113
 
1076
1114
  #onHeaderClick = (event) => {
1077
- const cell = DataGrid.#columnFromEvent(event, this.#head_row);
1115
+ const cell = DataGrid.#columnFromEvent(event, this.#headRow);
1078
1116
  if (cell === undefined) return;
1079
1117
  const index = Number(cell.dataset.column);
1080
1118
  const column = this.#columns[index];
@@ -1083,7 +1121,7 @@ class DataGrid {
1083
1121
  };
1084
1122
 
1085
1123
  #onHeaderKeyDown = (event) => {
1086
- const cell = DataGrid.#columnFromEvent(event, this.#head_row);
1124
+ const cell = DataGrid.#columnFromEvent(event, this.#headRow);
1087
1125
  if (cell === undefined) return;
1088
1126
  const index = Number(cell.dataset.column);
1089
1127
  if (event.key === "Enter" || event.key === " ") {
@@ -1092,7 +1130,7 @@ class DataGrid {
1092
1130
  return;
1093
1131
  }
1094
1132
  if (event.altKey && (event.key === "ArrowLeft" || event.key === "ArrowRight")) {
1095
- if (this.#column_reorder === false) return;
1133
+ if (this.#columnReorder === false) return;
1096
1134
  event.preventDefault();
1097
1135
  this.#moveColumn(index, event.key === "ArrowLeft" ? -1 : 1);
1098
1136
  return;
@@ -1104,7 +1142,7 @@ class DataGrid {
1104
1142
  const place = visible.findIndex((entry) => entry.index === index);
1105
1143
  const next = visible[place + (event.key === "ArrowRight" ? 1 : -1)];
1106
1144
  if (next === undefined) return;
1107
- const node = this.#head_row.querySelector(`data_grid_cell[data-column="${next.index}"]`);
1145
+ const node = this.#headRow.querySelector(`data_grid_cell[data-column="${next.index}"]`);
1108
1146
  if (node !== null) node.focus();
1109
1147
  }
1110
1148
  };
@@ -1114,15 +1152,16 @@ class DataGrid {
1114
1152
  const column = this.#columns[index];
1115
1153
  if (column === undefined) return;
1116
1154
  const same = this.#sort.key === column.key;
1117
- if (same === false) this.#sort = {key: column.key, direction: DataGrid.SORT.ASC};
1118
- else if (this.#sort.direction === DataGrid.SORT.ASC) this.#sort = {key: column.key, direction: DataGrid.SORT.DESC};
1119
- else this.#sort = {key: undefined, direction: DataGrid.SORT.NONE};
1155
+ if (same === false) this.#sort = { key: column.key, direction: DataGrid.SORT.ASC };
1156
+ else if (this.#sort.direction === DataGrid.SORT.ASC)
1157
+ this.#sort = { key: column.key, direction: DataGrid.SORT.DESC };
1158
+ else this.#sort = { key: undefined, direction: DataGrid.SORT.NONE };
1120
1159
  this.#applySort();
1121
1160
  }
1122
1161
 
1123
1162
  #applySort() {
1124
1163
  this.#recompute();
1125
- for (const cell of this.#head_row.querySelectorAll("data_grid_cell[data-column]")) {
1164
+ for (const cell of this.#headRow.querySelectorAll("data_grid_cell[data-column]")) {
1126
1165
  this.#syncSortMark(cell, Number(cell.dataset.column));
1127
1166
  }
1128
1167
  this.#announce(this.#sortSummary());
@@ -1133,7 +1172,7 @@ class DataGrid {
1133
1172
  const column = this.#columns.find((item) => item.key === this.#sort.key);
1134
1173
  return I18n.t("dataGrid.sortSummary", {
1135
1174
  title: column === undefined ? this.#sort.key : column.title,
1136
- direction: I18n.t(this.#sort.direction === DataGrid.SORT.DESC ? "data.descending" : "data.ascending")
1175
+ direction: I18n.t(this.#sort.direction === DataGrid.SORT.DESC ? "data.descending" : "data.ascending"),
1137
1176
  });
1138
1177
  }
1139
1178
 
@@ -1163,9 +1202,10 @@ class DataGrid {
1163
1202
  // Свой класс строки ставим ДО класса выделения: запись в `className` затирает уже
1164
1203
  // проставленные классы, и после перерисовки строки (правка ячейки) выделенная
1165
1204
  // строка теряла бы подсветку при включённом `rowClass`
1166
- if (typeof this.#row_class === "function") {
1167
- const classes = this.#row_class(row, entry.rowIndex);
1168
- if (classes !== undefined && classes !== null && String(classes).trim() !== "") node.className = String(classes).trim();
1205
+ if (typeof this.#rowClass === "function") {
1206
+ const classes = this.#rowClass(row, entry.rowIndex);
1207
+ if (classes !== undefined && classes !== null && String(classes).trim() !== "")
1208
+ node.className = String(classes).trim();
1169
1209
  }
1170
1210
  node.classList.toggle("data_grid_row_selected", this.#selected.has(key));
1171
1211
  if (this.#selection === DataGrid.SELECTION.NONE) node.removeAttribute("aria-selected");
@@ -1177,7 +1217,8 @@ class DataGrid {
1177
1217
  cell.setAttribute("aria-colindex", String(visible.index + 1));
1178
1218
  cell.dataset.column = String(visible.index);
1179
1219
  DataGrid.#applyAlign(cell, visible.column.align);
1180
- if (this.#editable !== undefined) cell.setAttribute("aria-readonly", this.#isColumnEditable(visible.index) ? "false" : "true");
1220
+ if (this.#editable !== undefined)
1221
+ cell.setAttribute("aria-readonly", this.#isColumnEditable(visible.index) ? "false" : "true");
1181
1222
  if (editing && this.#editing.col === visible.index && this.#editing.node !== undefined) {
1182
1223
  cell.classList.add("data_grid_cell_editing");
1183
1224
  // У редактора-списка свой отступ: его рамку рисует сам компонент
@@ -1196,8 +1237,12 @@ class DataGrid {
1196
1237
  if (typeof column.render === "function") {
1197
1238
  const content = column.render(row, index);
1198
1239
  if (content instanceof Node) cell.appendChild(content);
1199
- else if (content !== undefined && content !== null && typeof content.set === "function") cell.appendChild(content.set());
1200
- else cell.appendChild(document.createTextNode(content === undefined || content === null ? "" : String(content)));
1240
+ else if (content !== undefined && content !== null && typeof content.set === "function")
1241
+ cell.appendChild(content.set());
1242
+ else
1243
+ cell.appendChild(
1244
+ document.createTextNode(content === undefined || content === null ? "" : String(content))
1245
+ );
1201
1246
  return;
1202
1247
  }
1203
1248
  const value = row[column.key];
@@ -1223,15 +1268,20 @@ class DataGrid {
1223
1268
  cell.dataset.column = String(first.index);
1224
1269
  cell.tabIndex = 0;
1225
1270
  const toggle = document.createElement("data_grid_group_toggle");
1226
- toggle.innerHTML = new Icon(entry.group.collapsed ? Icons.NAVIGATION.CHEVRON_RIGHT : Icons.NAVIGATION.EXPAND_MORE, "14px", "var(--text_color_disabled)").getHTML();
1271
+ toggle.innerHTML = new Icon(
1272
+ entry.group.collapsed ? Icons.NAVIGATION.CHEVRON_RIGHT : Icons.NAVIGATION.EXPAND_MORE,
1273
+ "14px",
1274
+ "var(--text_color_disabled)"
1275
+ ).getHTML();
1227
1276
  cell.appendChild(toggle);
1228
1277
  const title = document.createElement("data_grid_group_title");
1229
1278
  // Подпись группы — вся целиком: и ключ, и число строк. Отдельного счётчика рядом нет,
1230
1279
  // иначе он повторял бы то же число (прежде там стояла подпись «групп: N», которая
1231
1280
  // называла строки группы группами)
1232
- title.innerText = typeof this.#group_label === "function"
1233
- ? String(this.#group_label(entry.group.key, entry.group.rows))
1234
- : I18n.t("dataGrid.groupTitle", {key: entry.group.key, count: entry.group.rows.length});
1281
+ title.innerText =
1282
+ typeof this.#groupLabel === "function"
1283
+ ? String(this.#groupLabel(entry.group.key, entry.group.rows))
1284
+ : I18n.t("dataGrid.groupTitle", { key: entry.group.key, count: entry.group.rows.length });
1235
1285
  cell.appendChild(title);
1236
1286
  node.appendChild(cell);
1237
1287
  for (let place = 1; place < visible.length; place++) {
@@ -1262,9 +1312,9 @@ class DataGrid {
1262
1312
  #syncRowActive(node = new HTMLElement(), index = Number()) {
1263
1313
  const active = this.#active.row === index;
1264
1314
  for (const cell of node.querySelectorAll("data_grid_cell[data-column]")) {
1265
- const is_active = active && Number(cell.dataset.column) === this.#active.col;
1266
- cell.classList.toggle(A11Y_ACTIVE_CLASS, is_active);
1267
- cell.tabIndex = is_active ? 0 : -1;
1315
+ const isActive = active && Number(cell.dataset.column) === this.#active.col;
1316
+ cell.classList.toggle(A11Y_ACTIVE_CLASS, isActive);
1317
+ cell.tabIndex = isActive ? 0 : -1;
1268
1318
  }
1269
1319
  }
1270
1320
 
@@ -1274,14 +1324,14 @@ class DataGrid {
1274
1324
 
1275
1325
  #rowsSummary() {
1276
1326
  const shown = this.#sorted.length;
1277
- if (this.#groups.length === 0) return I18n.t("dataGrid.rowsSummary", {shown: shown});
1278
- return `${I18n.t("dataGrid.rowsSummary", {shown: shown})}, ${I18n.t("dataGrid.groupsSummary", {count: this.#groups.length})}`;
1327
+ if (this.#groups.length === 0) return I18n.t("dataGrid.rowsSummary", { shown: shown });
1328
+ return `${I18n.t("dataGrid.rowsSummary", { shown: shown })}, ${I18n.t("dataGrid.groupsSummary", { count: this.#groups.length })}`;
1279
1329
  }
1280
1330
  // === ВЫБОР СТРОК ВНУТРЕННЕЕ ===
1281
1331
  /** Ключ строки: имя поля, функция или место в исходных данных */
1282
1332
  #keyOf(entry = new DataGridEntry()) {
1283
- if (typeof this.#custom_key === "function") return this.#custom_key(entry.row);
1284
- if (this.#custom_key !== undefined) return entry.row[this.#custom_key];
1333
+ if (typeof this.#customKey === "function") return this.#customKey(entry.row);
1334
+ if (this.#customKey !== undefined) return entry.row[this.#customKey];
1285
1335
  return this.#rowIndexOf(entry.row);
1286
1336
  }
1287
1337
 
@@ -1312,7 +1362,8 @@ class DataGrid {
1312
1362
  if (from === -1 || until === -1) return this.#selectRow(to);
1313
1363
  const [start, end] = from <= until ? [from, until] : [until, from];
1314
1364
  this.#selected.clear();
1315
- for (let index = start; index <= end; index++) this.#selected.add(this.#keyOf(DataGrid.#entryOf(rows[index], this.#rowIndexOf(rows[index]))));
1365
+ for (let index = start; index <= end; index++)
1366
+ this.#selected.add(this.#keyOf(DataGrid.#entryOf(rows[index], this.#rowIndexOf(rows[index]))));
1316
1367
  this.#applySelection();
1317
1368
  }
1318
1369
 
@@ -1322,33 +1373,36 @@ class DataGrid {
1322
1373
  if (entry === undefined || entry.kind !== "row") continue;
1323
1374
  const selected = this.#selected.has(this.#keyOf(entry));
1324
1375
  node.classList.toggle("data_grid_row_selected", selected);
1325
- if (this.#selection !== DataGrid.SELECTION.NONE) node.setAttribute("aria-selected", selected ? "true" : "false");
1376
+ if (this.#selection !== DataGrid.SELECTION.NONE)
1377
+ node.setAttribute("aria-selected", selected ? "true" : "false");
1326
1378
  }
1327
1379
  this.#syncPinned();
1328
- if (this.#on_selection_change !== undefined) {
1329
- const rows = this.#viewRows().filter((row) => this.#selected.has(this.#keyOf(DataGrid.#entryOf(row, this.#rowIndexOf(row)))));
1330
- this.#on_selection_change(rows, Array.from(this.#selected));
1380
+ if (this.#onSelectionChange !== undefined) {
1381
+ const rows = this.#viewRows().filter((row) =>
1382
+ this.#selected.has(this.#keyOf(DataGrid.#entryOf(row, this.#rowIndexOf(row))))
1383
+ );
1384
+ this.#onSelectionChange(rows, Array.from(this.#selected));
1331
1385
  }
1332
- this.#announce(I18n.t("dataGrid.rowsSummary", {shown: this.#selected.size}));
1386
+ this.#announce(I18n.t("dataGrid.rowsSummary", { shown: this.#selected.size }));
1333
1387
  }
1334
1388
 
1335
1389
  // === АКТИВНАЯ ЯЧЕЙКА И КЛАВИАТУРА ===
1336
1390
  #setActive(row = Number(), col = Number(), focus = true) {
1337
- if (this.#entry_count === 0) return;
1338
- const place = Math.min(Math.max(row, 0), this.#entry_count - 1);
1391
+ if (this.#entryCount === 0) return;
1392
+ const place = Math.min(Math.max(row, 0), this.#entryCount - 1);
1339
1393
  const entry = this.#entryAt(place);
1340
1394
  const visible = this.#visibleColumns();
1341
1395
  if (visible.length === 0) return;
1342
1396
  const known = visible.some((item) => item.index === col) ? col : visible[0].index;
1343
- this.#active = {row: place, col: known};
1397
+ this.#active = { row: place, col: known };
1344
1398
  if (entry !== undefined && entry.kind === "row") {
1345
1399
  this.#list.scrollToIndex(place, DataGrid.ALIGN.NEAREST);
1346
1400
  }
1347
1401
  this.#syncActiveCells();
1348
1402
  const node = this.#cellNode(place, known);
1349
- if (node !== undefined && focus) node.focus({preventScroll: true});
1350
- if (this.#on_active_cell_change !== undefined && entry !== undefined && entry.kind === "row") {
1351
- this.#on_active_cell_change(place, known, entry.row);
1403
+ if (node !== undefined && focus) node.focus({ preventScroll: true });
1404
+ if (this.#onActiveCellChange !== undefined && entry !== undefined && entry.kind === "row") {
1405
+ this.#onActiveCellChange(place, known, entry.row);
1352
1406
  }
1353
1407
  }
1354
1408
 
@@ -1365,23 +1419,24 @@ class DataGrid {
1365
1419
  const place = visible.findIndex((item) => item.index === col);
1366
1420
  if (place === -1) return undefined;
1367
1421
  let next = place + step;
1368
- let next_row = row;
1422
+ let nextRow = row;
1369
1423
  if (next >= visible.length) {
1370
1424
  next = 0;
1371
- next_row = row + 1;
1425
+ nextRow = row + 1;
1372
1426
  }
1373
1427
  if (next < 0) {
1374
1428
  next = visible.length - 1;
1375
- next_row = row - 1;
1429
+ nextRow = row - 1;
1376
1430
  }
1377
- if (next_row < 0 || next_row >= this.#entry_count) return undefined;
1378
- const entry = this.#entryAt(next_row);
1431
+ if (nextRow < 0 || nextRow >= this.#entryCount) return undefined;
1432
+ const entry = this.#entryAt(nextRow);
1379
1433
  if (entry === undefined || entry.kind !== "row") return undefined;
1380
- return {row: next_row, col: visible[next].index};
1434
+ return { row: nextRow, col: visible[next].index };
1381
1435
  }
1382
1436
 
1383
1437
  #onRowClick = (entry = new DataGridEntry(), index = Number(), _node = new HTMLElement(), event = Object()) => {
1384
- const cell = event === undefined || event.target === undefined ? undefined : event.target.closest("data_grid_cell");
1438
+ const cell =
1439
+ event === undefined || event.target === undefined ? undefined : event.target.closest("data_grid_cell");
1385
1440
  const col = cell === undefined || cell === null ? this.#active.col : Number(cell.dataset.column);
1386
1441
  if (entry.kind === "group") {
1387
1442
  this.#setGroupCollapsed(entry.group.key, entry.group.collapsed === false);
@@ -1413,13 +1468,13 @@ class DataGrid {
1413
1468
  // Space добавлял бы в выборку несуществующую строку, а перестановка колонки
1414
1469
  // (Alt+←/→) пересобирает шапку и ячейка под уже всплывающим событием осиротеет —
1415
1470
  // `closest` вернул бы null и обработчик падал бы с TypeError
1416
- const row_node = cell.closest("data_grid_row");
1417
- if (row_node === null || row_node.dataset.index === undefined) return;
1418
- const row = Number(row_node.dataset.index);
1471
+ const rowNode = cell.closest("data_grid_row");
1472
+ if (rowNode === null || rowNode.dataset.index === undefined) return;
1473
+ const row = Number(rowNode.dataset.index);
1419
1474
  const col = Number(cell.dataset.column);
1420
1475
  const visible = this.#visibleColumns();
1421
1476
  const place = visible.findIndex((item) => item.index === col);
1422
- const last = this.#entry_count - 1;
1477
+ const last = this.#entryCount - 1;
1423
1478
  const entry = this.#entryAt(row);
1424
1479
  if (entry !== undefined && entry.kind === "group") {
1425
1480
  if (event.key === "Enter" || event.key === " ") {
@@ -1440,9 +1495,9 @@ class DataGrid {
1440
1495
  return;
1441
1496
  }
1442
1497
  }
1443
- const move = (next_row, next_col) => {
1498
+ const move = (nextRow, nextColumn) => {
1444
1499
  event.preventDefault();
1445
- this.#setActive(next_row, next_col === undefined ? col : next_col, true);
1500
+ this.#setActive(nextRow, nextColumn === undefined ? col : nextColumn, true);
1446
1501
  };
1447
1502
  if (event.key === "ArrowDown") return move(Math.min(row + 1, last));
1448
1503
  if (event.key === "ArrowUp") return move(Math.max(row - 1, 0));
@@ -1482,7 +1537,7 @@ class DataGrid {
1482
1537
  #activateRow(index = Number()) {
1483
1538
  const entry = this.#entryAt(index);
1484
1539
  if (entry === undefined || entry.kind !== "row") return;
1485
- if (this.#on_row_activate !== undefined) this.#on_row_activate(entry.row, entry.rowIndex);
1540
+ if (this.#onRowActivate !== undefined) this.#onRowActivate(entry.row, entry.rowIndex);
1486
1541
  }
1487
1542
 
1488
1543
  // === ПРАВКА ЯЧЕЕК ===
@@ -1517,7 +1572,7 @@ class DataGrid {
1517
1572
  old_value: entry.row[column.key],
1518
1573
  forced: undefined,
1519
1574
  component: undefined,
1520
- node: undefined
1575
+ node: undefined,
1521
1576
  };
1522
1577
  const node = this.#createEditor(column, entry.row, row, editing);
1523
1578
  if (node === undefined) return false;
@@ -1528,7 +1583,7 @@ class DataGrid {
1528
1583
  if (editing.component !== undefined) editing.component.focus();
1529
1584
  else node.focus();
1530
1585
  if (typeof node.select === "function" && editing.component === undefined) node.select();
1531
- this.#announce(I18n.t("dataGrid.editStart", {title: column.title, row: row + 1}));
1586
+ this.#announce(I18n.t("dataGrid.editStart", { title: column.title, row: row + 1 }));
1532
1587
  return true;
1533
1588
  }
1534
1589
 
@@ -1540,7 +1595,7 @@ class DataGrid {
1540
1595
  if (this.#editing === editing && value !== undefined) editing.forced = value;
1541
1596
  this.#finishEdit(true, true, true);
1542
1597
  },
1543
- cancel: () => this.#finishEdit(false, false, true)
1598
+ cancel: () => this.#finishEdit(false, false, true),
1544
1599
  };
1545
1600
  const custom = column.editor(row, index, api);
1546
1601
  if (custom === undefined || custom === null) return undefined;
@@ -1630,23 +1685,25 @@ class DataGrid {
1630
1685
  }
1631
1686
 
1632
1687
  #readEditorValue(column = new DataGridColumn(), editing = Object()) {
1633
- if (editing.forced !== undefined) return {ok: true, value: editing.forced};
1688
+ if (editing.forced !== undefined) return { ok: true, value: editing.forced };
1634
1689
  const editor = editing.node;
1635
- if (column.editType === DataGrid.EDIT_TYPE.CHECKBOX) return {ok: true, value: editor.checked === true};
1690
+ if (column.editType === DataGrid.EDIT_TYPE.CHECKBOX) return { ok: true, value: editor.checked === true };
1636
1691
  if (column.editType === DataGrid.EDIT_TYPE.SELECT) {
1637
1692
  // Значение списка отдаёт компонент: у него подпись и значение живут отдельно
1638
- if (editing.component !== undefined) return {ok: true, value: editing.component.getSelectedValue()};
1639
- return {ok: true, value: editor.value};
1693
+ if (editing.component !== undefined) return { ok: true, value: editing.component.getSelectedValue() };
1694
+ return { ok: true, value: editor.value };
1640
1695
  }
1641
1696
  const text = editor.value;
1642
1697
  if (column.editType === DataGrid.EDIT_TYPE.NUMBER) {
1643
1698
  const normalized = String(text).trim().replace(",", ".");
1644
- if (normalized === "") return {ok: false, message: I18n.t("dataGrid.enterNumber", {title: column.title})};
1699
+ if (normalized === "")
1700
+ return { ok: false, message: I18n.t("dataGrid.enterNumber", { title: column.title }) };
1645
1701
  const number = Number(normalized);
1646
- if (Number.isNaN(number)) return {ok: false, message: I18n.t("dataGrid.notNumber", {title: column.title, text: text})};
1647
- return {ok: true, value: number};
1702
+ if (Number.isNaN(number))
1703
+ return { ok: false, message: I18n.t("dataGrid.notNumber", { title: column.title, text: text }) };
1704
+ return { ok: true, value: number };
1648
1705
  }
1649
- return {ok: true, value: text};
1706
+ return { ok: true, value: text };
1650
1707
  }
1651
1708
 
1652
1709
  /**
@@ -1658,7 +1715,7 @@ class DataGrid {
1658
1715
  if (editing === undefined) return false;
1659
1716
  const column = this.#columns[editing.col];
1660
1717
  const entry = this.#entryAt(editing.row);
1661
- const parsed = save ? this.#readEditorValue(column, editing) : {ok: true, value: editing.old_value};
1718
+ const parsed = save ? this.#readEditorValue(column, editing) : { ok: true, value: editing.old_value };
1662
1719
  if (parsed.ok === false && keepInvalid) {
1663
1720
  // Неверный ввод объясняем и оставляем редактор: начать другую правку нельзя
1664
1721
  this.#announce(parsed.message);
@@ -1666,21 +1723,22 @@ class DataGrid {
1666
1723
  }
1667
1724
  const value = parsed.ok === false ? editing.old_value : parsed.value;
1668
1725
  this.#editing = undefined;
1669
- if (editing.component !== undefined && typeof editing.component.destroy === "function") editing.component.destroy();
1726
+ if (editing.component !== undefined && typeof editing.component.destroy === "function")
1727
+ editing.component.destroy();
1670
1728
  if (entry !== undefined && entry.kind === "row" && save && value !== editing.old_value) {
1671
1729
  entry.row[editing.key] = value;
1672
- if (this.#on_cell_edit !== undefined) this.#on_cell_edit(entry.row, editing.key, value, editing.old_value);
1673
- this.#announce(I18n.t("dataGrid.saved", {title: column.title, value: DataGrid.#valueText(value)}));
1730
+ if (this.#onCellEdit !== undefined) this.#onCellEdit(entry.row, editing.key, value, editing.old_value);
1731
+ this.#announce(I18n.t("dataGrid.saved", { title: column.title, value: DataGrid.#valueText(value) }));
1674
1732
  } else if (save === false && parsed.ok === false) {
1675
- this.#announce(I18n.t("dataGrid.editCanceledReason", {message: parsed.message}));
1733
+ this.#announce(I18n.t("dataGrid.editCanceledReason", { message: parsed.message }));
1676
1734
  } else if (save === false) {
1677
- this.#announce(I18n.t("dataGrid.editCanceled", {title: column.title}));
1735
+ this.#announce(I18n.t("dataGrid.editCanceled", { title: column.title }));
1678
1736
  }
1679
1737
  // Агрегаты группы считаются по строкам — после правки их надо пересчитать
1680
1738
  this.#recompute();
1681
1739
  if (focus) {
1682
1740
  const node = this.#cellNode(editing.row, editing.col);
1683
- if (node !== undefined) node.focus({preventScroll: true});
1741
+ if (node !== undefined) node.focus({ preventScroll: true });
1684
1742
  }
1685
1743
  return true;
1686
1744
  }
@@ -1701,7 +1759,9 @@ class DataGrid {
1701
1759
  #setGroupCollapsed(key = String(), collapsed = true) {
1702
1760
  this.#collapsed.set(String(key), collapsed === true);
1703
1761
  this.#recompute();
1704
- this.#announce(collapsed === true ? I18n.t("dataGrid.groupsSummary", {count: this.#groups.length}) : this.#rowsSummary());
1762
+ this.#announce(
1763
+ collapsed === true ? I18n.t("dataGrid.groupsSummary", { count: this.#groups.length }) : this.#rowsSummary()
1764
+ );
1705
1765
  }
1706
1766
 
1707
1767
  // === ЗАКРЕПЛЁННЫЕ КОЛОНКИ И ГОРИЗОНТАЛЬНАЯ ПРОКРУТКА ===
@@ -1711,8 +1771,8 @@ class DataGrid {
1711
1771
 
1712
1772
  // === ПЕРЕСТАНОВКА КОЛОНОК ===
1713
1773
  #onHeaderDragStart = (event) => {
1714
- if (this.#column_reorder === false) return;
1715
- const cell = DataGrid.#columnFromEvent(event, this.#head_row);
1774
+ if (this.#columnReorder === false) return;
1775
+ const cell = DataGrid.#columnFromEvent(event, this.#headRow);
1716
1776
  if (cell === undefined) return;
1717
1777
  this.#drag = Number(cell.dataset.column);
1718
1778
  cell.classList.add("data_grid_cell_dragging");
@@ -1720,7 +1780,7 @@ class DataGrid {
1720
1780
  event.dataTransfer.effectAllowed = "move";
1721
1781
  event.dataTransfer.setData("text/plain", String(this.#drag));
1722
1782
  }
1723
- this.#announce(I18n.t("dataGrid.columnMove", {title: this.#columns[this.#drag].title}));
1783
+ this.#announce(I18n.t("dataGrid.columnMove", { title: this.#columns[this.#drag].title }));
1724
1784
  };
1725
1785
 
1726
1786
  #onHeaderDragOver = (event) => {
@@ -1743,13 +1803,14 @@ class DataGrid {
1743
1803
 
1744
1804
  #onHeaderDragEnd = () => {
1745
1805
  this.#markDrop(undefined);
1746
- for (const cell of this.#head_row.querySelectorAll("data_grid_cell")) cell.classList.remove("data_grid_cell_dragging");
1806
+ for (const cell of this.#headRow.querySelectorAll("data_grid_cell"))
1807
+ cell.classList.remove("data_grid_cell_dragging");
1747
1808
  this.#drag = undefined;
1748
1809
  };
1749
1810
 
1750
1811
  /** Место вставки по координате курсора: середина каждой видимой колонки */
1751
1812
  #dropSlot(clientX = Number()) {
1752
- const cells = Array.from(this.#head_row.querySelectorAll("data_grid_cell[data-column]"));
1813
+ const cells = Array.from(this.#headRow.querySelectorAll("data_grid_cell[data-column]"));
1753
1814
  for (let place = 0; place < cells.length; place++) {
1754
1815
  const rect = cells[place].getBoundingClientRect();
1755
1816
  if (clientX < rect.left + rect.width / 2) return place;
@@ -1758,9 +1819,10 @@ class DataGrid {
1758
1819
  }
1759
1820
 
1760
1821
  #markDrop(slot = undefined) {
1761
- for (const cell of this.#head_row.querySelectorAll("data_grid_cell")) cell.classList.remove("data_grid_cell_drop_after");
1822
+ for (const cell of this.#headRow.querySelectorAll("data_grid_cell"))
1823
+ cell.classList.remove("data_grid_cell_drop_after");
1762
1824
  if (slot === undefined) return;
1763
- const cells = Array.from(this.#head_row.querySelectorAll("data_grid_cell[data-column]"));
1825
+ const cells = Array.from(this.#headRow.querySelectorAll("data_grid_cell[data-column]"));
1764
1826
  const target = cells[slot === 0 ? 0 : slot - 1];
1765
1827
  if (target !== undefined) target.classList.add("data_grid_cell_drop_after");
1766
1828
  }
@@ -1776,14 +1838,15 @@ class DataGrid {
1776
1838
  const target = slot >= visible.length ? this.#columns.length : visible[slot].index;
1777
1839
  const column = this.#columns[from];
1778
1840
  const rest = this.#columns.filter((item) => item !== column);
1779
- const place = slot >= visible.length ? rest.length : rest.findIndex((item) => item.key === visible[slot].column.key);
1841
+ const place =
1842
+ slot >= visible.length ? rest.length : rest.findIndex((item) => item.key === visible[slot].column.key);
1780
1843
  rest.splice(place < 0 ? rest.length : place, 0, column);
1781
1844
  this.#columns = rest;
1782
1845
  this.#onHeaderDragEnd();
1783
1846
  this.#recompute();
1784
1847
  this.#buildHeader();
1785
- if (this.#on_column_order_change !== undefined) {
1786
- this.#on_column_order_change(this.getColumnOrder(), column.key, from, target);
1848
+ if (this.#onColumnOrderChange !== undefined) {
1849
+ this.#onColumnOrderChange(this.getColumnOrder(), column.key, from, target);
1787
1850
  }
1788
1851
  }
1789
1852
 
@@ -1801,10 +1864,13 @@ class DataGrid {
1801
1864
  this.#buildHeader();
1802
1865
  const from = index;
1803
1866
  const to = this.#columns.indexOf(column);
1804
- const node = this.#head_row.querySelector(`data_grid_cell[data-column="${to}"]`);
1867
+ const node = this.#headRow.querySelector(`data_grid_cell[data-column="${to}"]`);
1805
1868
  if (node !== null) node.focus();
1806
- this.#announce(I18n.t("dataGrid.columnPlace", {title: column.title, place: to + 1, total: this.#columns.length}));
1807
- if (this.#on_column_order_change !== undefined) this.#on_column_order_change(this.getColumnOrder(), column.key, from, to);
1869
+ this.#announce(
1870
+ I18n.t("dataGrid.columnPlace", { title: column.title, place: to + 1, total: this.#columns.length })
1871
+ );
1872
+ if (this.#onColumnOrderChange !== undefined)
1873
+ this.#onColumnOrderChange(this.getColumnOrder(), column.key, from, to);
1808
1874
  }
1809
1875
 
1810
1876
  // === ПОЛОСА НАД СЕТКОЙ И ДИАЛОГ КОЛОНОК ===
@@ -1812,7 +1878,7 @@ class DataGrid {
1812
1878
  const button = new Button({
1813
1879
  title: I18n.t("dataGrid.columns"),
1814
1880
  icon: Icons.ACTIONS.VIEW_COLUMN,
1815
- clickEvent: () => this.openColumnChooser()
1881
+ clickEvent: () => this.openColumnChooser(),
1816
1882
  });
1817
1883
  const element = button.set().querySelector("button");
1818
1884
  if (element !== null) element.setAttribute("aria-haspopup", "dialog");
@@ -1821,33 +1887,38 @@ class DataGrid {
1821
1887
  }
1822
1888
 
1823
1889
  #ensureChooser() {
1824
- if (this.#chooser_dialog !== undefined) {
1890
+ if (this.#chooserDialog !== undefined) {
1825
1891
  this.#refreshChooser();
1826
- return this.#chooser_dialog;
1892
+ return this.#chooserDialog;
1827
1893
  }
1828
- const dialog = new Dialog({width: "340px", height: "max-content", closeOutSideClick: true, title: I18n.t("dataGrid.columns")});
1829
- const block = new ContentBlock({direction: "column", wrap: "nowrap", width: "-webkit-fill-available"});
1894
+ const dialog = new Dialog({
1895
+ width: "340px",
1896
+ height: "max-content",
1897
+ closeOutSideClick: true,
1898
+ title: I18n.t("dataGrid.columns"),
1899
+ });
1900
+ const block = new ContentBlock({ direction: "column", wrap: "nowrap", width: "-webkit-fill-available" });
1830
1901
  dialog.addToBody(block);
1831
- dialog.addToFooter(new Button({title: I18n.t("dataGrid.showAll"), clickEvent: () => this.showAllColumns()}));
1832
- this.#chooser_dialog = dialog;
1833
- this.#chooser_block = block;
1902
+ dialog.addToFooter(new Button({ title: I18n.t("dataGrid.showAll"), clickEvent: () => this.showAllColumns() }));
1903
+ this.#chooserDialog = dialog;
1904
+ this.#chooserBlock = block;
1834
1905
  document.body.appendChild(dialog.set());
1835
1906
  this.#refreshChooser();
1836
1907
  return dialog;
1837
1908
  }
1838
1909
 
1839
1910
  #refreshChooser() {
1840
- if (this.#chooser_block === undefined) return;
1841
- this.#chooser_block.clear();
1911
+ if (this.#chooserBlock === undefined) return;
1912
+ this.#chooserBlock.clear();
1842
1913
  for (const column of this.#columns) {
1843
1914
  const checkbox = new CheckBox({
1844
1915
  name: `${this.#id}_column_${column.key}`,
1845
1916
  title: column.title,
1846
- changeListener: (event) => this.setColumnVisible(column.key, event.target.checked)
1917
+ changeListener: (event) => this.setColumnVisible(column.key, event.target.checked),
1847
1918
  });
1848
1919
  // У CheckBox нет опции начального значения: ставим его отдельно
1849
1920
  checkbox.setValue(this.isColumnVisible(column.key));
1850
- this.#chooser_block.add(checkbox);
1921
+ this.#chooserBlock.add(checkbox);
1851
1922
  }
1852
1923
  }
1853
1924
 
@@ -1855,48 +1926,51 @@ class DataGrid {
1855
1926
  #requestParams(offset = Number()) {
1856
1927
  return {
1857
1928
  offset: offset,
1858
- limit: this.#page_size,
1929
+ limit: this.#pageSize,
1859
1930
  filter: this.#filter,
1860
- filterKeys: this.#filter_keys,
1861
- sort: {key: this.#sort.key, direction: this.#sort.direction}
1931
+ filterKeys: this.#filterKeys,
1932
+ sort: { key: this.#sort.key, direction: this.#sort.direction },
1862
1933
  };
1863
1934
  }
1864
1935
 
1865
1936
  async #load(mode = "first") {
1866
- if (this.#data_source === undefined || this.#loading) return;
1937
+ if (this.#dataSource === undefined || this.#loading) return;
1867
1938
  const request = this.#request + 1;
1868
1939
  this.#request = request;
1869
1940
  this.#loading = true;
1870
1941
  this.#setError(undefined);
1871
1942
  this.#list.setLoadingMore(true);
1872
1943
  try {
1873
- const payload = await this.#data_source(this.#requestParams(mode === "first" ? 0 : this.#data.length));
1944
+ const payload = await this.#dataSource(this.#requestParams(mode === "first" ? 0 : this.#data.length));
1874
1945
  if (request !== this.#request) return;
1875
1946
  const rows = DataGrid.#payloadRows(payload);
1876
1947
  const total = DataGrid.#payloadTotal(payload);
1877
1948
  this.#data = mode === "first" ? rows : this.#data.concat(rows);
1878
- this.#server_total = total;
1879
- this.#has_more = total === undefined ? rows.length >= this.#page_size : this.#data.length < total;
1949
+ this.#serverTotal = total;
1950
+ this.#hasMore = total === undefined ? rows.length >= this.#pageSize : this.#data.length < total;
1880
1951
  this.#reindex();
1881
1952
  this.#recompute();
1882
- this.#announce(total === undefined
1883
- ? I18n.t("dataGrid.loadedRows", {shown: this.#data.length})
1884
- : I18n.t("dataGrid.loadedRowsOf", {shown: this.#data.length, total: total}));
1885
- if (this.#on_data_load !== undefined) this.#on_data_load(rows, total);
1953
+ this.#announce(
1954
+ total === undefined
1955
+ ? I18n.t("dataGrid.loadedRows", { shown: this.#data.length })
1956
+ : I18n.t("dataGrid.loadedRowsOf", { shown: this.#data.length, total: total })
1957
+ );
1958
+ if (this.#onDataLoad !== undefined) this.#onDataLoad(rows, total);
1886
1959
  } catch (error) {
1887
1960
  if (request !== this.#request) return;
1888
1961
  this.#failed = true;
1889
- const message = error !== undefined && error !== null && error.message !== undefined
1890
- ? String(error.message)
1891
- : I18n.t("dataGrid.unknownError");
1892
- this.#setError(I18n.t("dataGrid.loadFailed", {message: message}));
1893
- this.#announce(I18n.t("dataGrid.loadFailedAnnounce", {message: message}));
1894
- if (this.#on_data_error !== undefined) this.#on_data_error(error, this.#requestParams(this.#data.length));
1962
+ const message =
1963
+ error !== undefined && error !== null && error.message !== undefined
1964
+ ? String(error.message)
1965
+ : I18n.t("dataGrid.unknownError");
1966
+ this.#setError(I18n.t("dataGrid.loadFailed", { message: message }));
1967
+ this.#announce(I18n.t("dataGrid.loadFailedAnnounce", { message: message }));
1968
+ if (this.#onDataError !== undefined) this.#onDataError(error, this.#requestParams(this.#data.length));
1895
1969
  } finally {
1896
1970
  if (request === this.#request) {
1897
1971
  this.#loading = false;
1898
1972
  this.#list.setLoadingMore(false);
1899
- this.#list.setHasMore(this.#has_more && this.#failed === false);
1973
+ this.#list.setHasMore(this.#hasMore && this.#failed === false);
1900
1974
  }
1901
1975
  }
1902
1976
  }
@@ -1904,11 +1978,11 @@ class DataGrid {
1904
1978
  #setError(message = undefined) {
1905
1979
  const broken = message !== undefined;
1906
1980
  this.#error.hidden = broken === false;
1907
- this.#error_text.innerText = broken ? String(message) : "";
1981
+ this.#errorText.innerText = broken ? String(message) : "";
1908
1982
  // «Нет данных» и «не удалось загрузить» — разное, поэтому подпись пустого списка меняется
1909
- this.#list.setEmptyText(broken
1910
- ? I18n.t("dataGrid.notLoaded")
1911
- : optionOr(this.#empty_text, I18n.t("dataGrid.empty")));
1983
+ this.#list.setEmptyText(
1984
+ broken ? I18n.t("dataGrid.notLoaded") : optionOr(this.#emptyText, I18n.t("dataGrid.empty"))
1985
+ );
1912
1986
  }
1913
1987
 
1914
1988
  // === ЭКСПОРТ ===
@@ -1916,26 +1990,35 @@ class DataGrid {
1916
1990
  * CSV текущего вида: значения берутся из `render`, если он есть (в файле видно то же,
1917
1991
  * что в ячейках), заголовки групп не попадают, кавычки и разделитель экранируются.
1918
1992
  */
1919
- exportCSV(options = {delimiter: String(), columns: Array(), source: String(), includeHeaders: Boolean()}) {
1993
+ exportCSV(options = { delimiter: String(), columns: Array(), source: String(), includeHeaders: Boolean() }) {
1920
1994
  const delimiter = optionOr(options.delimiter, ",");
1921
1995
  const source = optionOr(options.source, DataGrid.EXPORT.VIEW);
1922
1996
  const rows = source === DataGrid.EXPORT.ALL ? this.#data : this.#sorted;
1923
1997
  // По умолчанию — видимые колонки: скрытой колонки нет в файле, как нет и в сетке
1924
- const columns = options.columns === undefined
1925
- ? this.#visibleColumns().map((entry) => entry.column)
1926
- : this.#columns.filter((column) => options.columns.includes(column.key));
1998
+ const columns =
1999
+ options.columns === undefined
2000
+ ? this.#visibleColumns().map((entry) => entry.column)
2001
+ : this.#columns.filter((column) => options.columns.includes(column.key));
1927
2002
  const lines = [];
1928
- if (options.includeHeaders !== false) lines.push(columns.map((column) => DataGrid.#csvCell(column.title, delimiter)).join(delimiter));
2003
+ if (options.includeHeaders !== false)
2004
+ lines.push(columns.map((column) => DataGrid.#csvCell(column.title, delimiter)).join(delimiter));
1929
2005
  for (const row of rows) {
1930
- lines.push(columns.map((column) => {
1931
- const index = this.#rowIndexOf(row);
1932
- const value = typeof column.render === "function" ? column.render(row, index) : row[column.key];
1933
- // Компонент превращаем в текст: в файле должно быть видно то же, что в ячейке
1934
- const node = value instanceof Node
1935
- ? value
1936
- : (value !== undefined && value !== null && typeof value.set === "function" ? value.set() : value);
1937
- return DataGrid.#csvCell(DataGrid.#valueText(node), delimiter);
1938
- }).join(delimiter));
2006
+ lines.push(
2007
+ columns
2008
+ .map((column) => {
2009
+ const index = this.#rowIndexOf(row);
2010
+ const value = typeof column.render === "function" ? column.render(row, index) : row[column.key];
2011
+ // Компонент превращаем в текст: в файле должно быть видно то же, что в ячейке
2012
+ const node =
2013
+ value instanceof Node
2014
+ ? value
2015
+ : value !== undefined && value !== null && typeof value.set === "function"
2016
+ ? value.set()
2017
+ : value;
2018
+ return DataGrid.#csvCell(DataGrid.#valueText(node), delimiter);
2019
+ })
2020
+ .join(delimiter)
2021
+ );
1939
2022
  }
1940
2023
  return lines.join("\r\n");
1941
2024
  }
@@ -1972,13 +2055,13 @@ class DataGrid {
1972
2055
  // === ДАННЫЕ И СОСТОЯНИЕ СЕТКИ ===
1973
2056
  /** Индекс строки в исходных данных: нужен и `render(row, index)`, и ключу по умолчанию */
1974
2057
  #rowIndexOf(row = Object()) {
1975
- const index = this.#row_index.get(row);
2058
+ const index = this.#rowIndex.get(row);
1976
2059
  return index === undefined ? -1 : index;
1977
2060
  }
1978
2061
 
1979
2062
  #reindex() {
1980
- this.#row_index = new Map();
1981
- for (let index = 0; index < this.#data.length; index++) this.#row_index.set(this.#data[index], index);
2063
+ this.#rowIndex = new Map();
2064
+ for (let index = 0; index < this.#data.length; index++) this.#rowIndex.set(this.#data[index], index);
1982
2065
  }
1983
2066
 
1984
2067
  /** Замыкание выравнивания и прокрутки: после смены колонок всё считается заново */
@@ -1995,53 +2078,125 @@ class DataGrid {
1995
2078
  return out;
1996
2079
  }
1997
2080
  // === ПУБЛИЧНОЕ: ЧТЕНИЕ ===
1998
- getId() { return this.#id; }
1999
- getLabel() { return this.#label; }
2081
+ getId() {
2082
+ return this.#id;
2083
+ }
2084
+ getLabel() {
2085
+ return this.#label;
2086
+ }
2000
2087
  /** Колонки в текущем порядке (те же объекты, что читает разметка) */
2001
- getColumns() { return this.#columns.map((column) => ({...column})); }
2002
- getData() { return this.#data.slice(); }
2088
+ getColumns() {
2089
+ return this.#columns.map((column) => ({ ...column }));
2090
+ }
2091
+ getData() {
2092
+ return this.#data.slice();
2093
+ }
2003
2094
  /** Строки данных в порядке показа (без заголовков групп) */
2004
- getRows() { return this.#viewRows(); }
2005
- getRowCount() { return this.#viewRows().length; }
2095
+ getRows() {
2096
+ return this.#viewRows();
2097
+ }
2098
+ getRowCount() {
2099
+ return this.#viewRows().length;
2100
+ }
2006
2101
  /** Элементов списка: заголовки групп тоже считаются */
2007
- getEntryCount() { return this.#entry_count; }
2008
- getRowHeight() { return this.#row_height; }
2009
- getVisibleRange() { return this.#list.getVisibleRange(); }
2010
- getRenderedRange() { return this.#list.getRenderedRange(); }
2011
- getRowElement(index = Number()) { return this.#list.getItemElement(index); }
2012
- getActiveCell() { return {...this.#active, key: this.#columns[this.#active.col] === undefined ? undefined : this.#columns[this.#active.col].key}; }
2013
- getSelected() { return this.#selectedRows(); }
2014
- getSelectedKeys() { return Array.from(this.#selected); }
2015
- getFilter() { return this.#filter; }
2016
- getSort() { return {...this.#sort}; }
2017
- getSelectionMode() { return this.#selection; }
2018
- isServer() { return this.#data_source !== undefined; }
2019
- isLoading() { return this.#loading; }
2020
- getServerTotal() { return this.#server_total; }
2021
- getPageSize() { return this.#page_size; }
2022
- isEditing() { return this.#editing !== undefined; }
2023
- getEditingCell() { return this.#editing === undefined ? undefined : {row: this.#editing.row, col: this.#editing.col, key: this.#editing.key}; }
2024
- isEditable() { return this.#editable !== undefined && this.#editable !== false; }
2025
- isColumnEditable(col = Number()) { return this.#isColumnEditable(col); }
2102
+ getEntryCount() {
2103
+ return this.#entryCount;
2104
+ }
2105
+ getRowHeight() {
2106
+ return this.#rowHeight;
2107
+ }
2108
+ getVisibleRange() {
2109
+ return this.#list.getVisibleRange();
2110
+ }
2111
+ getRenderedRange() {
2112
+ return this.#list.getRenderedRange();
2113
+ }
2114
+ getRowElement(index = Number()) {
2115
+ return this.#list.getItemElement(index);
2116
+ }
2117
+ getActiveCell() {
2118
+ return {
2119
+ ...this.#active,
2120
+ key: this.#columns[this.#active.col] === undefined ? undefined : this.#columns[this.#active.col].key,
2121
+ };
2122
+ }
2123
+ getSelected() {
2124
+ return this.#selectedRows();
2125
+ }
2126
+ getSelectedKeys() {
2127
+ return Array.from(this.#selected);
2128
+ }
2129
+ getFilter() {
2130
+ return this.#filter;
2131
+ }
2132
+ getSort() {
2133
+ return { ...this.#sort };
2134
+ }
2135
+ getSelectionMode() {
2136
+ return this.#selection;
2137
+ }
2138
+ isServer() {
2139
+ return this.#dataSource !== undefined;
2140
+ }
2141
+ isLoading() {
2142
+ return this.#loading;
2143
+ }
2144
+ getServerTotal() {
2145
+ return this.#serverTotal;
2146
+ }
2147
+ getPageSize() {
2148
+ return this.#pageSize;
2149
+ }
2150
+ isEditing() {
2151
+ return this.#editing !== undefined;
2152
+ }
2153
+ getEditingCell() {
2154
+ return this.#editing === undefined
2155
+ ? undefined
2156
+ : { row: this.#editing.row, col: this.#editing.col, key: this.#editing.key };
2157
+ }
2158
+ isEditable() {
2159
+ return this.#editable !== undefined && this.#editable !== false;
2160
+ }
2161
+ isColumnEditable(col = Number()) {
2162
+ return this.#isColumnEditable(col);
2163
+ }
2026
2164
  /** Колонки в порядке показа: место в полном списке рядом с самой колонкой */
2027
- getVisibleColumns() { return this.#visibleColumns().map((entry) => ({...entry.column})); }
2028
- getHiddenColumns() { return this.#columns.filter((column) => this.#hidden.has(column.key)).map((column) => ({...column})); }
2029
- getPinned() { return this.#pinned.slice(); }
2030
- isPinned(key = String()) { return this.#isPinned(key); }
2031
- isColumnVisible(key = String()) { return this.#hidden.has(key) === false; }
2032
- isColumnReorder() { return this.#column_reorder; }
2033
- getColumnOrder() { return this.#columns.map((column) => column.key); }
2034
- getGroupBy() { return this.#group_by; }
2165
+ getVisibleColumns() {
2166
+ return this.#visibleColumns().map((entry) => ({ ...entry.column }));
2167
+ }
2168
+ getHiddenColumns() {
2169
+ return this.#columns.filter((column) => this.#hidden.has(column.key)).map((column) => ({ ...column }));
2170
+ }
2171
+ getPinned() {
2172
+ return this.#pinned.slice();
2173
+ }
2174
+ isPinned(key = String()) {
2175
+ return this.#isPinned(key);
2176
+ }
2177
+ isColumnVisible(key = String()) {
2178
+ return this.#hidden.has(key) === false;
2179
+ }
2180
+ isColumnReorder() {
2181
+ return this.#columnReorder;
2182
+ }
2183
+ getColumnOrder() {
2184
+ return this.#columns.map((column) => column.key);
2185
+ }
2186
+ getGroupBy() {
2187
+ return this.#groupBy;
2188
+ }
2035
2189
  /** Группы с агрегатами и состоянием свёрнутости */
2036
2190
  getGroups() {
2037
2191
  return this.#groups.map((group) => ({
2038
2192
  key: group.key,
2039
- title: typeof this.#group_label === "function"
2040
- ? String(this.#group_label(group.key, group.rows))
2041
- : I18n.t("dataGrid.groupTitle", {key: group.key, count: group.rows.length}),
2193
+ title:
2194
+ typeof this.#groupLabel === "function"
2195
+ ? String(this.#groupLabel(group.key, group.rows))
2196
+ : I18n.t("dataGrid.groupTitle", { key: group.key, count: group.rows.length }),
2042
2197
  rows: group.rows.slice(),
2043
2198
  aggregates: group.aggregates,
2044
- collapsed: group.collapsed
2199
+ collapsed: group.collapsed,
2045
2200
  }));
2046
2201
  }
2047
2202
  isGroupCollapsed(key = String()) {
@@ -2071,9 +2226,9 @@ class DataGrid {
2071
2226
  setData(data = []) {
2072
2227
  this.#data = Array.isArray(data) ? data : [];
2073
2228
  // Ручная замена данных выключает догрузку сервера: иначе она перезаписала бы их
2074
- this.#data_source = undefined;
2075
- this.#has_more = false;
2076
- this.#server_total = undefined;
2229
+ this.#dataSource = undefined;
2230
+ this.#hasMore = false;
2231
+ this.#serverTotal = undefined;
2077
2232
  this.#selected.clear();
2078
2233
  this.#editing = undefined;
2079
2234
  this.#failed = false;
@@ -2093,26 +2248,26 @@ class DataGrid {
2093
2248
  }
2094
2249
 
2095
2250
  setPageSize(size = Number()) {
2096
- this.#page_size = DataGrid.#normalizePageSize(size);
2251
+ this.#pageSize = DataGrid.#normalizePageSize(size);
2097
2252
  return this;
2098
2253
  }
2099
2254
 
2100
2255
  setRowHeight(height = Number()) {
2101
- this.#row_height = Number(height);
2102
- this.#list.setItemHeight(this.#row_height);
2256
+ this.#rowHeight = Number(height);
2257
+ this.#list.setItemHeight(this.#rowHeight);
2103
2258
  this.#syncColumns();
2104
2259
  return this;
2105
2260
  }
2106
2261
 
2107
2262
  /** Перечитать страницы с сервера с текущим фильтром и сортировкой */
2108
2263
  reload() {
2109
- if (this.#data_source === undefined) return this;
2264
+ if (this.#dataSource === undefined) return this;
2110
2265
  this.#request = this.#request + 1;
2111
2266
  this.#loading = false;
2112
2267
  this.#failed = false;
2113
2268
  this.#data = [];
2114
- this.#has_more = true;
2115
- this.#server_total = undefined;
2269
+ this.#hasMore = true;
2270
+ this.#serverTotal = undefined;
2116
2271
  this.#reindex();
2117
2272
  this.#recompute();
2118
2273
  this.#load("first");
@@ -2120,27 +2275,30 @@ class DataGrid {
2120
2275
  }
2121
2276
 
2122
2277
  loadMore() {
2123
- if (this.#data_source === undefined || this.#has_more === false || this.#failed) return this;
2278
+ if (this.#dataSource === undefined || this.#hasMore === false || this.#failed) return this;
2124
2279
  this.#load("more");
2125
2280
  return this;
2126
2281
  }
2127
2282
 
2128
2283
  retryLoad() {
2129
- if (this.#data_source === undefined) return this;
2284
+ if (this.#dataSource === undefined) return this;
2130
2285
  this.#failed = false;
2131
- this.#list.setHasMore(this.#has_more);
2286
+ this.#list.setHasMore(this.#hasMore);
2132
2287
  this.#load("more");
2133
2288
  return this;
2134
2289
  }
2135
2290
 
2136
2291
  setFilter(text = String()) {
2137
2292
  this.#filter = String(text);
2138
- if (this.#data_source !== undefined) return this.reload();
2293
+ if (this.#dataSource !== undefined) return this.reload();
2139
2294
  this.#recompute();
2140
2295
  // Про фильтр сообщаем числом строк: «фильтр «n»: 3 из 12»
2141
2296
  const shown = this.#sorted.length;
2142
2297
  if (this.#filter.trim() === "") this.#announce(this.#rowsSummary());
2143
- else this.#announce(I18n.t("dataGrid.filterSummary", {filter: this.#filter, shown: shown, total: this.#data.length}));
2298
+ else
2299
+ this.#announce(
2300
+ I18n.t("dataGrid.filterSummary", { filter: this.#filter, shown: shown, total: this.#data.length })
2301
+ );
2144
2302
  return this;
2145
2303
  }
2146
2304
 
@@ -2151,8 +2309,8 @@ class DataGrid {
2151
2309
  /** Сортировка: `NONE` снимает её; в серверном режиме строки спрашиваются заново */
2152
2310
  setSort(key = undefined, direction = DataGrid.SORT.NONE) {
2153
2311
  const known = this.#columns.some((column) => column.key === key) ? key : undefined;
2154
- this.#sort = {key: known, direction: known === undefined ? DataGrid.SORT.NONE : direction};
2155
- if (this.#data_source !== undefined) return this.reload();
2312
+ this.#sort = { key: known, direction: known === undefined ? DataGrid.SORT.NONE : direction };
2313
+ if (this.#dataSource !== undefined) return this.reload();
2156
2314
  this.#applySort();
2157
2315
  return this;
2158
2316
  }
@@ -2168,14 +2326,15 @@ class DataGrid {
2168
2326
  }
2169
2327
 
2170
2328
  setOnActiveCellChange(listener = undefined) {
2171
- this.#on_active_cell_change = listener;
2329
+ this.#onActiveCellChange = listener;
2172
2330
  return this;
2173
2331
  }
2174
2332
 
2175
2333
  setContextMenu(ctx = undefined) {
2176
- if (this.#context_menu === ctx) return this;
2177
- if (this.#context_menu !== undefined && typeof this.#context_menu.detach === "function") this.#context_menu.detach();
2178
- this.#context_menu = ctx;
2334
+ if (this.#contextMenu === ctx) return this;
2335
+ if (this.#contextMenu !== undefined && typeof this.#contextMenu.detach === "function")
2336
+ this.#contextMenu.detach();
2337
+ this.#contextMenu = ctx;
2179
2338
  if (ctx !== undefined && typeof ctx.attach === "function") ctx.attach(this.#root);
2180
2339
  return this;
2181
2340
  }
@@ -2231,7 +2390,9 @@ class DataGrid {
2231
2390
  if (visible) this.#hidden.delete(key);
2232
2391
  else this.#hidden.add(key);
2233
2392
  // Скрытая колонка не может быть активной: курсор уходит в первую видимую
2234
- if (this.#hidden.has(this.#columns[this.#active.col] === undefined ? "" : this.#columns[this.#active.col].key)) {
2393
+ if (
2394
+ this.#hidden.has(this.#columns[this.#active.col] === undefined ? "" : this.#columns[this.#active.col].key)
2395
+ ) {
2235
2396
  this.#active.col = this.#visibleColumns().length === 0 ? 0 : this.#visibleColumns()[0].index;
2236
2397
  }
2237
2398
  this.#afterLayout();
@@ -2268,13 +2429,13 @@ class DataGrid {
2268
2429
  }
2269
2430
 
2270
2431
  setColumnReorder(enabled = Boolean()) {
2271
- this.#column_reorder = enabled === true;
2432
+ this.#columnReorder = enabled === true;
2272
2433
  this.#buildHeader();
2273
2434
  return this;
2274
2435
  }
2275
2436
 
2276
2437
  setGroupBy(groupBy = undefined) {
2277
- this.#group_by = groupBy;
2438
+ this.#groupBy = groupBy;
2278
2439
  this.#collapsed.clear();
2279
2440
  this.#recompute();
2280
2441
  return this;
@@ -2322,26 +2483,27 @@ class DataGrid {
2322
2483
 
2323
2484
  /** Снять подписки и отпустить ресурсы; узел остаётся в документе */
2324
2485
  destroy() {
2325
- this.#resize_observer.disconnect();
2326
- this.#head_row.removeEventListener("click", this.#onHeaderClick);
2327
- this.#head_row.removeEventListener("keydown", this.#onHeaderKeyDown);
2328
- this.#head_row.removeEventListener("dragstart", this.#onHeaderDragStart);
2329
- this.#head_row.removeEventListener("dragover", this.#onHeaderDragOver);
2330
- this.#head_row.removeEventListener("dragleave", this.#onHeaderDragLeave);
2331
- this.#head_row.removeEventListener("drop", this.#onHeaderDrop);
2332
- this.#head_row.removeEventListener("dragend", this.#onHeaderDragEnd);
2486
+ this.#resizeObserver.disconnect();
2487
+ this.#headRow.removeEventListener("click", this.#onHeaderClick);
2488
+ this.#headRow.removeEventListener("keydown", this.#onHeaderKeyDown);
2489
+ this.#headRow.removeEventListener("dragstart", this.#onHeaderDragStart);
2490
+ this.#headRow.removeEventListener("dragover", this.#onHeaderDragOver);
2491
+ this.#headRow.removeEventListener("dragleave", this.#onHeaderDragLeave);
2492
+ this.#headRow.removeEventListener("drop", this.#onHeaderDrop);
2493
+ this.#headRow.removeEventListener("dragend", this.#onHeaderDragEnd);
2333
2494
  this.#root.removeEventListener("dblclick", this.#onDoubleClick, true);
2334
2495
  this.#root.removeEventListener("keydown", this.#onKeyDown);
2335
2496
  this.#scroll.removeEventListener("scroll", this.#onScrollX);
2336
2497
  if (this.#editing !== undefined) this.#finishEdit(false, false, false);
2337
- if (this.#context_menu !== undefined && typeof this.#context_menu.detach === "function") this.#context_menu.detach();
2498
+ if (this.#contextMenu !== undefined && typeof this.#contextMenu.detach === "function")
2499
+ this.#contextMenu.detach();
2338
2500
  this.#list.destroy();
2339
- if (this.#chooser_dialog !== undefined) {
2501
+ if (this.#chooserDialog !== undefined) {
2340
2502
  // У диалога нет своего «удали себя»: узел снимаем с документа сами
2341
- const node = this.#chooser_dialog.set();
2503
+ const node = this.#chooserDialog.set();
2342
2504
  if (node !== undefined && node.parentNode !== null) node.parentNode.removeChild(node);
2343
- this.#chooser_dialog = undefined;
2344
- this.#chooser_block = undefined;
2505
+ this.#chooserDialog = undefined;
2506
+ this.#chooserBlock = undefined;
2345
2507
  }
2346
2508
  return this;
2347
2509
  }