chuijs 4.0.1 → 4.0.3

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 (222) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +43 -632
  3. package/framework/appLayout/default/chui_app_layout.js +314 -273
  4. package/framework/appLayout/default/global_style.css +93 -53
  5. package/framework/components/chui_accordion/accordion.js +29 -26
  6. package/framework/components/chui_accordion/styles.css +6 -5
  7. package/framework/components/chui_action_sheet/action_sheet.js +59 -33
  8. package/framework/components/chui_action_sheet/styles.css +2 -1
  9. package/framework/components/chui_affix/affix.js +11 -6
  10. package/framework/components/chui_alert/alert.js +23 -14
  11. package/framework/components/chui_avatar/avatar.js +23 -22
  12. package/framework/components/chui_avatar_group/avatar_group.js +21 -19
  13. package/framework/components/chui_backtop/backtop.js +35 -19
  14. package/framework/components/chui_badge/badge.js +15 -13
  15. package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
  16. package/framework/components/chui_button/button.js +42 -22
  17. package/framework/components/chui_button/styles.css +2 -2
  18. package/framework/components/chui_calendar/calendar.js +12 -10
  19. package/framework/components/chui_card/card.js +43 -23
  20. package/framework/components/chui_chat/chat.js +72 -33
  21. package/framework/components/chui_clipboard_history/clipboard_history.js +30 -21
  22. package/framework/components/chui_clipboard_history/styles.css +4 -2
  23. package/framework/components/chui_code/code.js +2 -2
  24. package/framework/components/chui_code/styles.css +2 -2
  25. package/framework/components/chui_code_editor/code_editor.js +47 -31
  26. package/framework/components/chui_code_editor/styles.css +3 -2
  27. package/framework/components/chui_command_palette/command_palette.js +60 -37
  28. package/framework/components/chui_console/console.js +5 -5
  29. package/framework/components/chui_console/styles.css +1 -1
  30. package/framework/components/chui_content_block/content_block.js +33 -19
  31. package/framework/components/chui_content_block/styles.css +2 -2
  32. package/framework/components/chui_context_menu/context_menu.js +9 -4
  33. package/framework/components/chui_custom_element/custom_element.js +22 -15
  34. package/framework/components/chui_data_grid/data_grid.js +396 -234
  35. package/framework/components/chui_data_grid/styles.css +10 -8
  36. package/framework/components/chui_descriptions/descriptions.js +34 -17
  37. package/framework/components/chui_details/details.js +32 -26
  38. package/framework/components/chui_details/styles.css +5 -4
  39. package/framework/components/chui_diff_viewer/diff_viewer.js +91 -66
  40. package/framework/components/chui_diff_viewer/styles.css +1 -1
  41. package/framework/components/chui_divider/divider.js +13 -11
  42. package/framework/components/chui_drawer/drawer.js +27 -25
  43. package/framework/components/chui_drop_zone/drop_zone.js +31 -21
  44. package/framework/components/chui_empty_state/empty_state.js +20 -15
  45. package/framework/components/chui_fieldset/fieldset.js +37 -20
  46. package/framework/components/chui_fieldset/styles.css +1 -1
  47. package/framework/components/chui_file_explorer/file_explorer.js +157 -86
  48. package/framework/components/chui_file_explorer/styles.css +2 -1
  49. package/framework/components/chui_form/form.js +31 -19
  50. package/framework/components/chui_form/styles.css +1 -1
  51. package/framework/components/chui_gantt/gantt.js +64 -40
  52. package/framework/components/chui_graphs/graphs.js +301 -200
  53. package/framework/components/chui_graphs/styles.css +1 -1
  54. package/framework/components/chui_hero/hero.js +40 -22
  55. package/framework/components/chui_html_block/html_block.js +6 -6
  56. package/framework/components/chui_html_block/styles.css +2 -2
  57. package/framework/components/chui_hx/hx.js +3 -3
  58. package/framework/components/chui_hx/styles.css +11 -4
  59. package/framework/components/chui_icons/icons.js +2109 -2109
  60. package/framework/components/chui_icons/styles.css +2 -2
  61. package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
  62. package/framework/components/chui_inputs/chui_check_box/styles.css +18 -18
  63. package/framework/components/chui_inputs/chui_chips/chips.js +50 -40
  64. package/framework/components/chui_inputs/chui_chips/styles.css +4 -2
  65. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +36 -24
  66. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +100 -82
  67. package/framework/components/chui_inputs/chui_combo_box/styles.css +14 -12
  68. package/framework/components/chui_inputs/chui_date/date.js +102 -66
  69. package/framework/components/chui_inputs/chui_date/styles.css +12 -11
  70. package/framework/components/chui_inputs/chui_date_range/date_range.js +40 -29
  71. package/framework/components/chui_inputs/chui_datetime/datetime.js +35 -27
  72. package/framework/components/chui_inputs/chui_email/email.js +43 -31
  73. package/framework/components/chui_inputs/chui_email/styles.css +5 -5
  74. package/framework/components/chui_inputs/chui_file/file.js +68 -41
  75. package/framework/components/chui_inputs/chui_file/styles.css +13 -12
  76. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +61 -36
  77. package/framework/components/chui_inputs/chui_masked/masked.js +39 -28
  78. package/framework/components/chui_inputs/chui_mention/mention.js +77 -30
  79. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +173 -153
  80. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +9 -7
  81. package/framework/components/chui_inputs/chui_number/number.js +58 -41
  82. package/framework/components/chui_inputs/chui_number/styles.css +7 -7
  83. package/framework/components/chui_inputs/chui_otp/otp.js +38 -29
  84. package/framework/components/chui_inputs/chui_password/password.js +58 -36
  85. package/framework/components/chui_inputs/chui_password/styles.css +8 -8
  86. package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
  87. package/framework/components/chui_inputs/chui_radio_button/styles.css +20 -20
  88. package/framework/components/chui_inputs/chui_rating/rating.js +26 -22
  89. package/framework/components/chui_inputs/chui_select_box/select_box.js +102 -78
  90. package/framework/components/chui_inputs/chui_select_box/styles.css +11 -11
  91. package/framework/components/chui_inputs/chui_slider/slider.js +29 -26
  92. package/framework/components/chui_inputs/chui_slider/styles.css +2 -1
  93. package/framework/components/chui_inputs/chui_text/styles.css +8 -8
  94. package/framework/components/chui_inputs/chui_text/text.js +65 -44
  95. package/framework/components/chui_inputs/chui_text_area/styles.css +6 -6
  96. package/framework/components/chui_inputs/chui_text_area/text_area.js +51 -28
  97. package/framework/components/chui_inputs/chui_time/styles.css +5 -3
  98. package/framework/components/chui_inputs/chui_time/time.js +38 -27
  99. package/framework/components/chui_inputs/chui_toggle/styles.css +3 -3
  100. package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
  101. package/framework/components/chui_json_editor/json_editor.js +53 -43
  102. package/framework/components/chui_json_viewer/json_viewer.js +52 -33
  103. package/framework/components/chui_json_viewer/styles.css +3 -2
  104. package/framework/components/chui_kanban/kanban.js +52 -34
  105. package/framework/components/chui_kbd/kbd.js +12 -10
  106. package/framework/components/chui_label/label.js +14 -12
  107. package/framework/components/chui_label/styles.css +2 -2
  108. package/framework/components/chui_link/link.js +27 -20
  109. package/framework/components/chui_list/list.js +31 -27
  110. package/framework/components/chui_list/styles.css +2 -1
  111. package/framework/components/chui_markdown_editor/markdown_editor.js +60 -44
  112. package/framework/components/chui_media/audio.js +326 -227
  113. package/framework/components/chui_media/audio_styles.css +40 -21
  114. package/framework/components/chui_media/image.js +70 -38
  115. package/framework/components/chui_media/image_compare.js +52 -21
  116. package/framework/components/chui_media/image_styles.css +3 -3
  117. package/framework/components/chui_media/video.js +155 -62
  118. package/framework/components/chui_media/video_styles.css +33 -16
  119. package/framework/components/chui_menu_bar/menu_bar.js +26 -24
  120. package/framework/components/chui_menu_bar/styles.css +4 -2
  121. package/framework/components/chui_modal/modal.js +44 -43
  122. package/framework/components/chui_modal/styles.css +3 -2
  123. package/framework/components/chui_notification/notification.js +50 -34
  124. package/framework/components/chui_notification/notification_download.js +31 -25
  125. package/framework/components/chui_notification/notification_download_progress.css +3 -2
  126. package/framework/components/chui_notification/notification_download_progress.js +47 -41
  127. package/framework/components/chui_notification/notification_update.js +23 -16
  128. package/framework/components/chui_notification/status_plate.js +2 -2
  129. package/framework/components/chui_notification/styles_download.css +1 -1
  130. package/framework/components/chui_notification/styles_update.css +1 -1
  131. package/framework/components/chui_org_chart/org_chart.js +74 -35
  132. package/framework/components/chui_pagination/pagination.js +62 -27
  133. package/framework/components/chui_paragraph/paragraph.js +2 -2
  134. package/framework/components/chui_paragraph/styles.css +1 -1
  135. package/framework/components/chui_popover/popover.js +49 -28
  136. package/framework/components/chui_popups/popups.js +137 -108
  137. package/framework/components/chui_popups/styles.css +2 -2
  138. package/framework/components/chui_print_button/print_button.js +33 -23
  139. package/framework/components/chui_progress_bar/progress_bar.js +23 -15
  140. package/framework/components/chui_progress_bar/styles.css +2 -2
  141. package/framework/components/chui_progress_ring/progress_ring.js +17 -15
  142. package/framework/components/chui_qrcode/qrcode.js +75 -31
  143. package/framework/components/chui_query_builder/query_builder.js +216 -123
  144. package/framework/components/chui_radio_group/radio_group.js +36 -26
  145. package/framework/components/chui_radio_group/styles.css +1 -1
  146. package/framework/components/chui_result/result.js +26 -16
  147. package/framework/components/chui_scheduler/scheduler.js +57 -35
  148. package/framework/components/chui_scheduler/styles.css +6 -2
  149. package/framework/components/chui_segmented_control/segmented_control.js +27 -21
  150. package/framework/components/chui_settings/settings.js +122 -48
  151. package/framework/components/chui_shortcut_help/shortcut_help.js +55 -32
  152. package/framework/components/chui_sidebar_nav/sidebar_nav.js +33 -23
  153. package/framework/components/chui_skeleton/skeleton.js +29 -24
  154. package/framework/components/chui_slideshow/slideshow.js +60 -41
  155. package/framework/components/chui_slideshow/styles.css +18 -12
  156. package/framework/components/chui_spinner/spinner.js +8 -8
  157. package/framework/components/chui_spinner/styles.css +0 -1
  158. package/framework/components/chui_split_button/split_button.js +23 -18
  159. package/framework/components/chui_split_button/styles.css +1 -1
  160. package/framework/components/chui_splitter/splitter.js +35 -28
  161. package/framework/components/chui_statistic/statistic.js +65 -38
  162. package/framework/components/chui_status_bar/status_bar.js +11 -9
  163. package/framework/components/chui_stepper/stepper.js +27 -22
  164. package/framework/components/chui_sticky_bar/sticky_bar.js +9 -4
  165. package/framework/components/chui_sticky_bar/styles.css +3 -1
  166. package/framework/components/chui_tabs/chui_tabs.js +28 -22
  167. package/framework/components/chui_tabs/styles.css +3 -3
  168. package/framework/components/chui_text_editor/styles.css +20 -19
  169. package/framework/components/chui_text_editor/text_editor.js +85 -61
  170. package/framework/components/chui_text_editor/text_editor_panel.js +346 -284
  171. package/framework/components/chui_theme_toggle/theme_toggle.js +27 -22
  172. package/framework/components/chui_timeline/timeline.js +14 -12
  173. package/framework/components/chui_toast_queue/toast_queue.js +52 -46
  174. package/framework/components/chui_toolbar/toolbar.js +20 -13
  175. package/framework/components/chui_tooltip/tooltip.js +37 -24
  176. package/framework/components/chui_tour/styles.css +8 -3
  177. package/framework/components/chui_tour/tour.js +61 -27
  178. package/framework/components/chui_transfer/styles.css +2 -1
  179. package/framework/components/chui_transfer/transfer.js +75 -42
  180. package/framework/components/chui_tree_table/tree_table.js +43 -29
  181. package/framework/components/chui_tree_view/styles.css +4 -2
  182. package/framework/components/chui_tree_view/tree_view.js +142 -99
  183. package/framework/components/chui_watermark/watermark.js +24 -16
  184. package/framework/components/chui_webview/styles.css +2 -2
  185. package/framework/components/chui_webview/webview.js +62 -50
  186. package/framework/components/telegram_bot/chui_telegram_bot.js +71 -64
  187. package/framework/modules/chui_a11y/chui_a11y.js +77 -46
  188. package/framework/modules/chui_animations/animations.js +42 -42
  189. package/framework/modules/chui_animations/styles.css +1 -1
  190. package/framework/modules/chui_bridge/preload.js +56 -26
  191. package/framework/modules/chui_functions.js +52 -50
  192. package/framework/modules/chui_highlight/chui_highlight.js +25 -22
  193. package/framework/modules/chui_i18n/chui_i18n.js +9 -7
  194. package/framework/modules/chui_i18n/locales/a11y.js +5 -5
  195. package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
  196. package/framework/modules/chui_i18n/locales/calendar.js +3 -3
  197. package/framework/modules/chui_i18n/locales/common.js +3 -3
  198. package/framework/modules/chui_i18n/locales/data.js +36 -26
  199. package/framework/modules/chui_i18n/locales/editors.js +40 -13
  200. package/framework/modules/chui_i18n/locales/feedback.js +3 -3
  201. package/framework/modules/chui_i18n/locales/forms.js +8 -8
  202. package/framework/modules/chui_i18n/locales/graphics.js +8 -8
  203. package/framework/modules/chui_i18n/locales/media.js +4 -4
  204. package/framework/modules/chui_i18n/locales/navigation.js +8 -8
  205. package/framework/modules/chui_i18n/locales/validate.js +3 -3
  206. package/framework/modules/chui_json/chui_json.js +9 -9
  207. package/framework/modules/chui_layout/chui_layout.js +12 -10
  208. package/framework/modules/chui_logger/chui_logger.js +29 -25
  209. package/framework/modules/chui_page/page.js +33 -27
  210. package/framework/modules/chui_page/styles.css +3 -26
  211. package/framework/modules/chui_qr/chui_qr.js +158 -67
  212. package/framework/modules/chui_scrollbar/chui_scrollbar.js +35 -24
  213. package/framework/modules/chui_scrollbar/styles.css +3 -1
  214. package/framework/modules/chui_services/chui_channels.js +76 -11
  215. package/framework/modules/chui_services/chui_services.js +39 -31
  216. package/framework/modules/chui_services/desktop.js +149 -30
  217. package/framework/modules/chui_sortable/chui_sortable.js +17 -12
  218. package/framework/modules/chui_validate/chui_validate.js +7 -4
  219. package/framework/tray_bar/chui_menu_items.js +136 -46
  220. package/index.d.ts +4814 -0
  221. package/index.js +449 -334
  222. package/package.json +79 -68
@@ -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,7 +81,7 @@ 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;
@@ -110,28 +110,30 @@ class DataGridBody {
110
110
  #loading_more = false;
111
111
  // Длина, для которой запрос уже сделан: не просим ещё раз, пока данных не прибавилось
112
112
  #requested_at = -1;
113
- #window_state = {from: 0, to: 0};
113
+ #window_state = { from: 0, to: 0 };
114
114
  #raf = null;
115
115
  #refresh_raf = null;
116
116
  #resize_observer = 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
139
  this.#item_tag = optionOr(options.itemTag, "data_grid_row");
@@ -143,7 +145,8 @@ class DataGridBody {
143
145
  this.#on_render = options.onRender;
144
146
  this.#on_load_more = options.onLoadMore;
145
147
  this.#loading_text = optionOr(options.loadingText, I18n.t("common.loading"));
146
- this.#load_threshold = options.loadMoreThreshold === undefined ? DataGridBody.DEFAULT_LOAD_THRESHOLD : options.loadMoreThreshold;
148
+ this.#load_threshold =
149
+ options.loadMoreThreshold === undefined ? DataGridBody.DEFAULT_LOAD_THRESHOLD : options.loadMoreThreshold;
147
150
  // Без onLoadMore тело считает, что загружено всё
148
151
  this.#has_more = options.hasMore === undefined ? options.onLoadMore !== undefined : options.hasMore === true;
149
152
  this.#root.id = this.#id;
@@ -163,7 +166,7 @@ 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 дают
@@ -174,19 +177,29 @@ class DataGridBody {
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.#item_height;
185
+ }
179
186
  /** Окно, которое сейчас в DOM (вместе с запасом `buffer`) */
180
- getRenderedRange() { return {...this.#window_state}; }
187
+ getRenderedRange() {
188
+ return { ...this.#window_state };
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()) {
@@ -351,15 +364,15 @@ class DataGridBody {
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.#window_state = { from: from, to: to };
355
368
  this.#syncSpacers();
356
- if (this.#on_render !== undefined) this.#on_render({from: from, to: to});
369
+ if (this.#on_render !== undefined) this.#on_render({ 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;
375
+ const { from, to } = this.#window_state;
363
376
  this.#spacer_top.style.height = count === 0 ? "0px" : `${from * this.#item_height}px`;
364
377
  this.#spacer_bottom.style.height = count === 0 ? "0px" : `${Math.max(0, (count - to) * this.#item_height)}px`;
365
378
  }
@@ -399,22 +412,22 @@ 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
426
  if (this.#on_item_click !== undefined) this.#on_item_click(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
432
  if (this.#on_activate !== undefined) this.#on_activate(this.#items[index], index, node);
420
433
  };
@@ -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;
@@ -517,7 +530,7 @@ class DataGrid {
517
530
  #row_index = new Map();
518
531
  #filter = "";
519
532
  #filter_keys = [];
520
- #sort = {key: undefined, direction: DataGrid.SORT.NONE};
533
+ #sort = { key: undefined, direction: DataGrid.SORT.NONE };
521
534
  #group_by = undefined;
522
535
  #group_label = undefined;
523
536
  #aggregates = {};
@@ -525,7 +538,7 @@ class DataGrid {
525
538
  #collapsed = new Map();
526
539
  #editable = undefined;
527
540
  #editing = undefined;
528
- #active = {row: 0, col: 0};
541
+ #active = { row: 0, col: 0 };
529
542
  #entry_count = 0;
530
543
 
531
544
  // Производные данные (пересобираются одним проходом — см. #recompute)
@@ -560,39 +573,41 @@ class DataGrid {
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);
@@ -631,7 +646,9 @@ class DataGrid {
631
646
  this.#scroll.appendChild(this.#head);
632
647
  this.#root.appendChild(this.#scroll);
633
648
  this.#error.appendChild(this.#error_text);
634
- this.#error.appendChild(new Button({title: I18n.t("dataGrid.retry"), clickEvent: () => this.retryLoad()}).set());
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");
@@ -666,7 +683,7 @@ 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);
@@ -686,7 +703,7 @@ class DataGrid {
686
703
  this.#resize_observer = new ResizeObserver(() => this.#syncPinned());
687
704
  this.#resize_observer.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();
@@ -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}` */
@@ -829,7 +846,10 @@ class DataGrid {
829
846
  this.#entry_count = 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.#server_total === undefined ? this.#entry_count : this.#server_total) + 1)
852
+ );
833
853
  this.#syncColumns();
834
854
  this.#list.setItems(this.#entries);
835
855
  this.#list.setHasMore(this.#has_more);
@@ -860,9 +880,11 @@ class DataGrid {
860
880
  if (this.#data_source !== undefined) return rows.slice();
861
881
  const text = this.#filter.trim().toLowerCase();
862
882
  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
- }));
883
+ return rows.filter((row) =>
884
+ this.#filter_keys.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,7 +917,7 @@ 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
  /** Группы: порядок — первое появление ключа, поэтому он не зависит от сортировки */
@@ -903,9 +927,7 @@ class DataGrid {
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
  }
@@ -915,7 +937,7 @@ class DataGrid {
915
937
  key: key,
916
938
  rows: group_rows,
917
939
  aggregates: this.#aggregateValues(group_rows),
918
- collapsed: this.#collapsed.has(key) ? this.#collapsed.get(key) : this.#group_collapsed_default
940
+ collapsed: this.#collapsed.has(key) ? this.#collapsed.get(key) : this.#group_collapsed_default,
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;
@@ -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
  }
@@ -1041,10 +1070,15 @@ 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
1083
  if (this.#column_reorder) {
1050
1084
  cell.setAttribute("draggable", "true");
@@ -1061,14 +1095,18 @@ 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
  }
@@ -1114,9 +1152,10 @@ 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
 
@@ -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
 
@@ -1165,7 +1204,8 @@ class DataGrid {
1165
1204
  // строка теряла бы подсветку при включённом `rowClass`
1166
1205
  if (typeof this.#row_class === "function") {
1167
1206
  const classes = this.#row_class(row, entry.rowIndex);
1168
- if (classes !== undefined && classes !== null && String(classes).trim() !== "") node.className = String(classes).trim();
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.#group_label === "function"
1283
+ ? String(this.#group_label(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++) {
@@ -1274,8 +1324,8 @@ 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
  /** Ключ строки: имя поля, функция или место в исходных данных */
@@ -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,14 +1373,17 @@ 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
1380
  if (this.#on_selection_change !== undefined) {
1329
- const rows = this.#viewRows().filter((row) => this.#selected.has(this.#keyOf(DataGrid.#entryOf(row, this.#rowIndexOf(row)))));
1381
+ const rows = this.#viewRows().filter((row) =>
1382
+ this.#selected.has(this.#keyOf(DataGrid.#entryOf(row, this.#rowIndexOf(row))))
1383
+ );
1330
1384
  this.#on_selection_change(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
  // === АКТИВНАЯ ЯЧЕЙКА И КЛАВИАТУРА ===
@@ -1340,13 +1394,13 @@ class DataGrid {
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});
1403
+ if (node !== undefined && focus) node.focus({ preventScroll: true });
1350
1404
  if (this.#on_active_cell_change !== undefined && entry !== undefined && entry.kind === "row") {
1351
1405
  this.#on_active_cell_change(place, known, entry.row);
1352
1406
  }
@@ -1377,11 +1431,12 @@ class DataGrid {
1377
1431
  if (next_row < 0 || next_row >= this.#entry_count) return undefined;
1378
1432
  const entry = this.#entryAt(next_row);
1379
1433
  if (entry === undefined || entry.kind !== "row") return undefined;
1380
- return {row: next_row, col: visible[next].index};
1434
+ return { row: next_row, 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);
@@ -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
1730
  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)}));
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
  // === ЗАКРЕПЛЁННЫЕ КОЛОНКИ И ГОРИЗОНТАЛЬНАЯ ПРОКРУТКА ===
@@ -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,7 +1803,8 @@ 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.#head_row.querySelectorAll("data_grid_cell"))
1807
+ cell.classList.remove("data_grid_cell_dragging");
1747
1808
  this.#drag = undefined;
1748
1809
  };
1749
1810
 
@@ -1758,7 +1819,8 @@ 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.#head_row.querySelectorAll("data_grid_cell"))
1823
+ cell.classList.remove("data_grid_cell_drop_after");
1762
1824
  if (slot === undefined) return;
1763
1825
  const cells = Array.from(this.#head_row.querySelectorAll("data_grid_cell[data-column]"));
1764
1826
  const target = cells[slot === 0 ? 0 : slot - 1];
@@ -1776,7 +1838,8 @@ 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();
@@ -1803,8 +1866,11 @@ class DataGrid {
1803
1866
  const to = this.#columns.indexOf(column);
1804
1867
  const node = this.#head_row.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.#on_column_order_change !== undefined)
1873
+ this.#on_column_order_change(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");
@@ -1825,10 +1891,15 @@ class DataGrid {
1825
1891
  this.#refreshChooser();
1826
1892
  return this.#chooser_dialog;
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()}));
1902
+ dialog.addToFooter(new Button({ title: I18n.t("dataGrid.showAll"), clickEvent: () => this.showAllColumns() }));
1832
1903
  this.#chooser_dialog = dialog;
1833
1904
  this.#chooser_block = block;
1834
1905
  document.body.appendChild(dialog.set());
@@ -1843,7 +1914,7 @@ class DataGrid {
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));
@@ -1858,7 +1929,7 @@ class DataGrid {
1858
1929
  limit: this.#page_size,
1859
1930
  filter: this.#filter,
1860
1931
  filterKeys: this.#filter_keys,
1861
- sort: {key: this.#sort.key, direction: this.#sort.direction}
1932
+ sort: { key: this.#sort.key, direction: this.#sort.direction },
1862
1933
  };
1863
1934
  }
1864
1935
 
@@ -1879,18 +1950,21 @@ class DataGrid {
1879
1950
  this.#has_more = total === undefined ? rows.length >= this.#page_size : 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}));
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
+ );
1885
1958
  if (this.#on_data_load !== undefined) this.#on_data_load(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}));
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 }));
1894
1968
  if (this.#on_data_error !== undefined) this.#on_data_error(error, this.#requestParams(this.#data.length));
1895
1969
  } finally {
1896
1970
  if (request === this.#request) {
@@ -1906,9 +1980,9 @@ class DataGrid {
1906
1980
  this.#error.hidden = broken === false;
1907
1981
  this.#error_text.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.#empty_text, 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
  }
@@ -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.#entry_count;
2104
+ }
2105
+ getRowHeight() {
2106
+ return this.#row_height;
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.#data_source !== undefined;
2140
+ }
2141
+ isLoading() {
2142
+ return this.#loading;
2143
+ }
2144
+ getServerTotal() {
2145
+ return this.#server_total;
2146
+ }
2147
+ getPageSize() {
2148
+ return this.#page_size;
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.#column_reorder;
2182
+ }
2183
+ getColumnOrder() {
2184
+ return this.#columns.map((column) => column.key);
2185
+ }
2186
+ getGroupBy() {
2187
+ return this.#group_by;
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.#group_label === "function"
2195
+ ? String(this.#group_label(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()) {
@@ -2140,7 +2295,10 @@ class DataGrid {
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,7 +2309,7 @@ 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};
2312
+ this.#sort = { key: known, direction: known === undefined ? DataGrid.SORT.NONE : direction };
2155
2313
  if (this.#data_source !== undefined) return this.reload();
2156
2314
  this.#applySort();
2157
2315
  return this;
@@ -2174,7 +2332,8 @@ class DataGrid {
2174
2332
 
2175
2333
  setContextMenu(ctx = undefined) {
2176
2334
  if (this.#context_menu === ctx) return this;
2177
- if (this.#context_menu !== undefined && typeof this.#context_menu.detach === "function") this.#context_menu.detach();
2335
+ if (this.#context_menu !== undefined && typeof this.#context_menu.detach === "function")
2336
+ this.#context_menu.detach();
2178
2337
  this.#context_menu = ctx;
2179
2338
  if (ctx !== undefined && typeof ctx.attach === "function") ctx.attach(this.#root);
2180
2339
  return this;
@@ -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();
@@ -2334,7 +2495,8 @@ class DataGrid {
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.#context_menu !== undefined && typeof this.#context_menu.detach === "function")
2499
+ this.#context_menu.detach();
2338
2500
  this.#list.destroy();
2339
2501
  if (this.#chooser_dialog !== undefined) {
2340
2502
  // У диалога нет своего «удали себя»: узел снимаем с документа сами