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,17 +1,17 @@
1
1
  const fs = require("fs");
2
- const node_path = require("path");
3
- const {setStyles, formatBytes, optionOr} = require("../../modules/chui_functions");
4
- const {Icon, Icons} = require("../chui_icons/icons");
5
- const {Breadcrumb} = require("../chui_breadcrumb/breadcrumb");
6
- const {RovingKeyboard} = require("../../modules/chui_a11y/chui_a11y");
7
- const {I18n} = require("../../modules/chui_i18n/chui_i18n");
2
+ const nodePath = require("path");
3
+ const { setStyles, formatBytes, optionOr } = require("../../modules/chui_functions");
4
+ const { Icon, Icons } = require("../chui_icons/icons");
5
+ const { Breadcrumb } = require("../chui_breadcrumb/breadcrumb");
6
+ const { RovingKeyboard } = require("../../modules/chui_a11y/chui_a11y");
7
+ const { I18n } = require("../../modules/chui_i18n/chui_i18n");
8
8
 
9
9
  /** Вид записи: папка или файл */
10
- const TYPE = {FOLDER: "folder", FILE: "file"};
10
+ const TYPE = { FOLDER: "folder", FILE: "file" };
11
11
  /** Колонка сортировки */
12
- const SORT = {NAME: "name", SIZE: "size", MODIFIED: "modified"};
12
+ const SORT = { NAME: "name", SIZE: "size", MODIFIED: "modified" };
13
13
  /** Направление сортировки */
14
- const DIRECTION = {ASC: "asc", DESC: "desc"};
14
+ const DIRECTION = { ASC: "asc", DESC: "desc" };
15
15
 
16
16
  /** Значок по расширению: одна таблица на все виды файлов */
17
17
  const ICON_BY_EXTENSION = {
@@ -20,7 +20,7 @@ const ICON_BY_EXTENSION = {
20
20
  audio: "mp3 wav ogg flac m4a aac",
21
21
  video: "mp4 mkv avi webm mov",
22
22
  text: "md txt rtf pdf doc docx odt",
23
- code: "js mjs ts tsx jsx json css html htm php py rb java c cpp h sh sql yml yaml"
23
+ code: "js mjs ts tsx jsx json css html htm php py rb java c cpp h sh sql yml yaml",
24
24
  };
25
25
 
26
26
  /**
@@ -73,12 +73,12 @@ class FileExplorer {
73
73
  #up = document.createElement("button");
74
74
  #refresh = document.createElement("button");
75
75
  #search = document.createElement("input");
76
- #search_box = document.createElement("file_explorer_search_box");
77
- #search_clear = document.createElement("button");
76
+ #searchBox = document.createElement("file_explorer_search_box");
77
+ #searchClear = document.createElement("button");
78
78
  #hidden = document.createElement("button");
79
- #path_bar = undefined;
79
+ #pathBar = undefined;
80
80
  #grid = document.createElement("file_explorer_grid");
81
- #head_row = document.createElement("file_explorer_head_row");
81
+ #headRow = document.createElement("file_explorer_head_row");
82
82
  #body = document.createElement("file_explorer_body");
83
83
  #empty = document.createElement("file_explorer_empty");
84
84
  #error = document.createElement("file_explorer_error");
@@ -86,68 +86,70 @@ class FileExplorer {
86
86
  #id = undefined;
87
87
  #provider = undefined;
88
88
  #path = "";
89
- #root_path = undefined;
89
+ #rootPath = undefined;
90
90
  #history = [];
91
- #history_index = -1;
91
+ #historyIndex = -1;
92
92
  #entries = [];
93
93
  #selected = new Set();
94
94
  #anchor = undefined;
95
95
  #filter = "";
96
- #sort = {key: SORT.NAME, direction: DIRECTION.ASC};
97
- #show_hidden = false;
96
+ #sort = { key: SORT.NAME, direction: DIRECTION.ASC };
97
+ #showHidden = false;
98
98
  #loading = false;
99
- #load_token = 0;
100
- #error_object = undefined;
99
+ #loadToken = 0;
100
+ #errorObject = undefined;
101
101
  #selectable = true;
102
- #show_size = true;
103
- #show_modified = true;
102
+ #showSize = true;
103
+ #showModified = true;
104
104
  #height = "300px";
105
- #title_text = undefined;
106
- #label_text = undefined;
107
- #empty_text = undefined;
108
- #active_path = undefined;
109
- #focus_path = undefined;
105
+ #titleText = undefined;
106
+ #labelText = undefined;
107
+ #emptyText = undefined;
108
+ #activePath = undefined;
109
+ #focusPath = undefined;
110
110
  #listeners = [];
111
- #on_open = undefined;
112
- #on_select = undefined;
111
+ #onOpen = undefined;
112
+ #onSelect = undefined;
113
113
  #roving = undefined;
114
114
 
115
- constructor(options = {
116
- id: String(),
117
- title: String(),
118
- label: String(),
119
- path: String(),
120
- root: String(),
121
- provider: undefined,
122
- filter: String(),
123
- sort: Object(),
124
- showHidden: Boolean,
125
- selectable: Boolean,
126
- showSize: Boolean,
127
- showModified: Boolean,
128
- height: String(),
129
- emptyText: String(),
130
- errorText: String(),
131
- onOpen: undefined,
132
- onSelect: undefined,
133
- changeListener: undefined
134
- }) {
115
+ constructor(
116
+ options = {
117
+ id: String(),
118
+ title: String(),
119
+ label: String(),
120
+ path: String(),
121
+ root: String(),
122
+ provider: undefined,
123
+ filter: String(),
124
+ sort: Object(),
125
+ showHidden: Boolean,
126
+ selectable: Boolean,
127
+ showSize: Boolean,
128
+ showModified: Boolean,
129
+ height: String(),
130
+ emptyText: String(),
131
+ errorText: String(),
132
+ onOpen: undefined,
133
+ onSelect: undefined,
134
+ changeListener: undefined,
135
+ }
136
+ ) {
135
137
  setStyles(__dirname + "/styles.css", "chUiJS_FileExplorer");
136
138
  FileExplorer.#counter += 1;
137
139
  this.#id = options.id === undefined ? `chui_file_explorer_${FileExplorer.#counter}` : options.id;
138
140
  this.#root.id = this.#id;
139
141
  this.#root.setAttribute("role", "group");
140
- this.#label_text = options.label;
142
+ this.#labelText = options.label;
141
143
  this.#provider = typeof options.provider === "function" ? options.provider : FileExplorer.localProvider();
142
- this.#root_path = options.root === undefined ? undefined : String(options.root);
144
+ this.#rootPath = options.root === undefined ? undefined : String(options.root);
143
145
  this.#selectable = options.selectable !== false;
144
- this.#show_size = options.showSize !== false;
145
- this.#show_modified = options.showModified !== false;
146
- this.#show_hidden = options.showHidden === true;
147
- this.#on_open = options.onOpen;
148
- this.#on_select = options.onSelect;
149
- if (options.emptyText !== undefined) this.#empty_text = String(options.emptyText);
150
- else this.#empty_text = I18n.t("fileExplorer.empty");
146
+ this.#showSize = options.showSize !== false;
147
+ this.#showModified = options.showModified !== false;
148
+ this.#showHidden = options.showHidden === true;
149
+ this.#onOpen = options.onOpen;
150
+ this.#onSelect = options.onSelect;
151
+ if (options.emptyText !== undefined) this.#emptyText = String(options.emptyText);
152
+ else this.#emptyText = I18n.t("fileExplorer.empty");
151
153
  if (options.filter !== undefined) this.#filter = String(options.filter);
152
154
  this.#title.className = "file_explorer_title";
153
155
  this.#title.id = `${this.#id}_title`;
@@ -157,8 +159,8 @@ class FileExplorer {
157
159
  this.#actions.className = "file_explorer_actions";
158
160
  this.#grid.className = "file_explorer_grid";
159
161
  this.#grid.setAttribute("role", "grid");
160
- this.#head_row.className = "file_explorer_head_row";
161
- this.#head_row.setAttribute("role", "row");
162
+ this.#headRow.className = "file_explorer_head_row";
163
+ this.#headRow.setAttribute("role", "row");
162
164
  this.#body.className = "file_explorer_body";
163
165
  this.#body.setAttribute("role", "rowgroup");
164
166
  this.#empty.className = "file_explorer_empty";
@@ -169,12 +171,12 @@ class FileExplorer {
169
171
  this.#status.setAttribute("role", "status");
170
172
  this.#status.setAttribute("aria-live", "polite");
171
173
  this.#buildHead();
172
- this.#path_bar = new Breadcrumb({label: I18n.t("fileExplorer.path")});
173
- this.#path_bar.set().classList.add("file_explorer_path");
174
+ this.#pathBar = new Breadcrumb({ label: I18n.t("fileExplorer.path") });
175
+ this.#pathBar.set().classList.add("file_explorer_path");
174
176
  this.#root.appendChild(this.#title);
175
177
  this.#root.appendChild(this.#head);
176
- this.#root.appendChild(this.#path_bar.set());
177
- this.#grid.appendChild(this.#head_row);
178
+ this.#root.appendChild(this.#pathBar.set());
179
+ this.#grid.appendChild(this.#headRow);
178
180
  this.#grid.appendChild(this.#body);
179
181
  this.#root.appendChild(this.#grid);
180
182
  this.#root.appendChild(this.#empty);
@@ -189,9 +191,9 @@ class FileExplorer {
189
191
  this.refreshSearch();
190
192
  // Сортировка применяется только теперь: `setSort()` перерисовывает список, а строку пути
191
193
  // и прочие узлы разметки он застал бы ещё не созданными
192
- const sort_key = optionOr(options.sort === undefined ? undefined : options.sort.key, undefined);
193
- const sort_direction = optionOr(options.sort === undefined ? undefined : options.sort.direction, undefined);
194
- if (sort_key !== undefined || sort_direction !== undefined) this.setSort(sort_key, sort_direction);
194
+ const sortKey = optionOr(options.sort === undefined ? undefined : options.sort.key, undefined);
195
+ const sortDirection = optionOr(options.sort === undefined ? undefined : options.sort.direction, undefined);
196
+ if (sortKey !== undefined || sortDirection !== undefined) this.setSort(sortKey, sortDirection);
195
197
  this.#grid.addEventListener("keydown", this.#onKeyDown);
196
198
  this.#grid.addEventListener("click", this.#onClick);
197
199
  this.#grid.addEventListener("dblclick", this.#onDoubleClick);
@@ -204,9 +206,9 @@ class FileExplorer {
204
206
  /** Провайдер по умолчанию: настоящая файловая система */
205
207
  static localProvider() {
206
208
  return (path) => {
207
- const list = fs.readdirSync(String(path), {withFileTypes: true});
209
+ const list = fs.readdirSync(String(path), { withFileTypes: true });
208
210
  return list.map((entry) => {
209
- const full = node_path.join(String(path), entry.name);
211
+ const full = nodePath.join(String(path), entry.name);
210
212
  let size;
211
213
  let modified;
212
214
  try {
@@ -224,7 +226,7 @@ class FileExplorer {
224
226
  type: entry.isDirectory() ? TYPE.FOLDER : TYPE.FILE,
225
227
  size: size,
226
228
  modified: modified,
227
- hidden: entry.name.startsWith(".")
229
+ hidden: entry.name.startsWith("."),
228
230
  };
229
231
  });
230
232
  };
@@ -236,11 +238,11 @@ class FileExplorer {
236
238
  }
237
239
 
238
240
  getTitle() {
239
- return this.#title_text;
241
+ return this.#titleText;
240
242
  }
241
243
 
242
244
  getLabel() {
243
- return this.#label_text;
245
+ return this.#labelText;
244
246
  }
245
247
 
246
248
  /** Текущая папка */
@@ -250,7 +252,7 @@ class FileExplorer {
250
252
 
251
253
  /** Корень навигации (`undefined` — подниматься можно до корня пути) */
252
254
  getRoot() {
253
- return this.#root_path;
255
+ return this.#rootPath;
254
256
  }
255
257
 
256
258
  /** Все загруженные записи текущей папки (со скрытыми), в исходном порядке провайдера */
@@ -282,11 +284,11 @@ class FileExplorer {
282
284
  }
283
285
 
284
286
  getSort() {
285
- return {key: this.#sort.key, direction: this.#sort.direction};
287
+ return { key: this.#sort.key, direction: this.#sort.direction };
286
288
  }
287
289
 
288
290
  isShowHidden() {
289
- return this.#show_hidden;
291
+ return this.#showHidden;
290
292
  }
291
293
 
292
294
  isSelectable() {
@@ -302,33 +304,33 @@ class FileExplorer {
302
304
  }
303
305
 
304
306
  getEmptyText() {
305
- return this.#empty_text;
307
+ return this.#emptyText;
306
308
  }
307
309
 
308
310
  /** Текст ошибки чтения (пустая строка, если папка прочитана) */
309
311
  getErrorText() {
310
- if (this.#error_object === undefined) return "";
311
- const message = this.#error_object instanceof Error ? this.#error_object.message : String(this.#error_object);
312
- return I18n.t("fileExplorer.readFailed", {message: message});
312
+ if (this.#errorObject === undefined) return "";
313
+ const message = this.#errorObject instanceof Error ? this.#errorObject.message : String(this.#errorObject);
314
+ return I18n.t("fileExplorer.readFailed", { message: message });
313
315
  }
314
316
 
315
317
  /** История переходов и место в ней — для кнопок «назад/вперёд» */
316
318
  getHistory() {
317
- return {paths: [...this.#history], index: this.#history_index};
319
+ return { paths: [...this.#history], index: this.#historyIndex };
318
320
  }
319
321
 
320
322
  canGoBack() {
321
- return this.#history_index > 0;
323
+ return this.#historyIndex > 0;
322
324
  }
323
325
 
324
326
  canGoForward() {
325
- return this.#history_index >= 0 && this.#history_index < this.#history.length - 1;
327
+ return this.#historyIndex >= 0 && this.#historyIndex < this.#history.length - 1;
326
328
  }
327
329
 
328
330
  /** Выше корня (или выше самого верхнего уровня пути) подняться нельзя */
329
331
  canGoUp() {
330
332
  if (this.#path === "") return false;
331
- if (this.#root_path !== undefined && this.#path === this.#root_path.replace(/[\\/]+$/, "")) return false;
333
+ if (this.#rootPath !== undefined && this.#path === this.#rootPath.replace(/[\\/]+$/, "")) return false;
332
334
  return FileExplorer.#parentOf(this.#path) !== undefined;
333
335
  }
334
336
 
@@ -342,7 +344,7 @@ class FileExplorer {
342
344
  }
343
345
 
344
346
  setTitle(text = undefined) {
345
- this.#title_text = text;
347
+ this.#titleText = text;
346
348
  if (text === undefined || text === "") {
347
349
  this.#title.hidden = true;
348
350
  this.#title.innerText = "";
@@ -356,7 +358,7 @@ class FileExplorer {
356
358
  }
357
359
 
358
360
  setLabel(text = undefined) {
359
- this.#label_text = text;
361
+ this.#labelText = text;
360
362
  this.#syncLabel();
361
363
  return this;
362
364
  }
@@ -368,7 +370,7 @@ class FileExplorer {
368
370
  }
369
371
 
370
372
  setEmptyText(text = String()) {
371
- this.#empty_text = text === undefined ? "" : String(text);
373
+ this.#emptyText = text === undefined ? "" : String(text);
372
374
  this.#render();
373
375
  return this;
374
376
  }
@@ -400,14 +402,17 @@ class FileExplorer {
400
402
  if (column !== undefined && Object.values(SORT).includes(column)) this.#sort.key = column;
401
403
  if (order !== undefined && Object.values(DIRECTION).includes(order)) this.#sort.direction = order;
402
404
  // До конца сборки разметки перерисовывать нечего (вызов из конструктора)
403
- if (this.#path_bar !== undefined) this.#render();
405
+ if (this.#pathBar !== undefined) this.#render();
404
406
  return this;
405
407
  }
406
408
 
407
409
  setShowHidden(shown = Boolean()) {
408
- this.#show_hidden = shown === true;
409
- this.#hidden.setAttribute("aria-pressed", this.#show_hidden ? "true" : "false");
410
- this.#hidden.setAttribute("aria-label", this.#show_hidden ? I18n.t("fileExplorer.hideHidden") : I18n.t("fileExplorer.showHidden"));
410
+ this.#showHidden = shown === true;
411
+ this.#hidden.setAttribute("aria-pressed", this.#showHidden ? "true" : "false");
412
+ this.#hidden.setAttribute(
413
+ "aria-label",
414
+ this.#showHidden ? I18n.t("fileExplorer.hideHidden") : I18n.t("fileExplorer.showHidden")
415
+ );
411
416
  this.#render();
412
417
  this.#announceState();
413
418
  return this;
@@ -416,7 +421,9 @@ class FileExplorer {
416
421
  setSelected(paths = Array(), silent = false) {
417
422
  if (!this.#selectable) return this;
418
423
  const allowed = new Set(this.#entries.map((entry) => entry.path));
419
- this.#selected = new Set((Array.isArray(paths) ? paths : []).map((path) => String(path)).filter((path) => allowed.has(path)));
424
+ this.#selected = new Set(
425
+ (Array.isArray(paths) ? paths : []).map((path) => String(path)).filter((path) => allowed.has(path))
426
+ );
420
427
  this.#anchor = this.#selected.size === 0 ? undefined : [...this.#selected][this.#selected.size - 1];
421
428
  this.#applySelection();
422
429
  if (silent === false) this.#notifySelect();
@@ -431,8 +438,8 @@ class FileExplorer {
431
438
  open(entry = Object()) {
432
439
  if (entry === undefined || entry === null) return this;
433
440
  if (entry.type === TYPE.FOLDER) return this.go(entry.path);
434
- this.#announce(I18n.t("fileExplorer.openedFile", {name: entry.name}));
435
- if (this.#on_open !== undefined) this.#on_open(Object.assign({}, entry), this);
441
+ this.#announce(I18n.t("fileExplorer.openedFile", { name: entry.name }));
442
+ if (this.#onOpen !== undefined) this.#onOpen(Object.assign({}, entry), this);
436
443
  return this;
437
444
  }
438
445
 
@@ -443,35 +450,37 @@ class FileExplorer {
443
450
  }
444
451
 
445
452
  /** Загрузка папки: промис разрешается, когда данные пришли (устаревший ответ отбрасывается) */
446
- async load(path = String(), options = {history: true, keepSelection: false}) {
453
+ async load(path = String(), options = { history: true, keepSelection: false }) {
447
454
  const target = path === undefined || path === null || path === "" ? this.#path : String(path);
448
- const token = this.#load_token + 1;
449
- this.#load_token = token;
455
+ const token = this.#loadToken + 1;
456
+ this.#loadToken = token;
450
457
  this.#loading = true;
451
458
  this.#grid.setAttribute("aria-busy", "true");
452
- this.#status.innerText = I18n.t("fileExplorer.loading", {target: target});
459
+ this.#status.innerText = I18n.t("fileExplorer.loading", { target: target });
453
460
  let list;
454
461
  try {
455
462
  list = await Promise.resolve(this.#provider(target));
456
463
  } catch (error) {
457
- if (token !== this.#load_token) return this;
464
+ if (token !== this.#loadToken) return this;
458
465
  this.#loading = false;
459
466
  this.#entries = [];
460
- this.#error_object = error;
467
+ this.#errorObject = error;
461
468
  this.#path = target;
462
469
  this.#render();
463
- this.#announce(I18n.t("fileExplorer.readFailed", {message: error instanceof Error ? error.message : String(error)}));
470
+ this.#announce(
471
+ I18n.t("fileExplorer.readFailed", { message: error instanceof Error ? error.message : String(error) })
472
+ );
464
473
  return this;
465
474
  }
466
- if (token !== this.#load_token) return this;
475
+ if (token !== this.#loadToken) return this;
467
476
  this.#entries = FileExplorer.#normalize(list, target);
468
- this.#error_object = undefined;
477
+ this.#errorObject = undefined;
469
478
  this.#loading = false;
470
479
  this.#path = target;
471
480
  if (options.keepSelection !== true) this.#selected.clear();
472
481
  if (options.history !== false) {
473
- this.#history = this.#history.slice(0, this.#history_index + 1).concat([target]);
474
- this.#history_index = this.#history.length - 1;
482
+ this.#history = this.#history.slice(0, this.#historyIndex + 1).concat([target]);
483
+ this.#historyIndex = this.#history.length - 1;
475
484
  }
476
485
  this.#render();
477
486
  this.#announceState();
@@ -480,14 +489,14 @@ class FileExplorer {
480
489
 
481
490
  back() {
482
491
  if (!this.canGoBack()) return this;
483
- this.#history_index -= 1;
484
- return this.load(this.#history[this.#history_index], {history: false});
492
+ this.#historyIndex -= 1;
493
+ return this.load(this.#history[this.#historyIndex], { history: false });
485
494
  }
486
495
 
487
496
  forward() {
488
497
  if (!this.canGoForward()) return this;
489
- this.#history_index += 1;
490
- return this.load(this.#history[this.#history_index], {history: false});
498
+ this.#historyIndex += 1;
499
+ return this.load(this.#history[this.#historyIndex], { history: false });
491
500
  }
492
501
 
493
502
  /** На уровень выше; выше корня не поднимаемся */
@@ -498,7 +507,7 @@ class FileExplorer {
498
507
 
499
508
  /** Перечитать текущую папку, сохранив выбор */
500
509
  refresh() {
501
- return this.load(this.#path, {history: false, keepSelection: true});
510
+ return this.load(this.#path, { history: false, keepSelection: true });
502
511
  }
503
512
 
504
513
  /** Фокус на запись по пути (или на первую видимую) */
@@ -526,7 +535,8 @@ class FileExplorer {
526
535
  static #normalize(list = Array(), path = String()) {
527
536
  const source = Array.isArray(list) ? list : [];
528
537
  return source.map((item, index) => {
529
- const entry = typeof item === "string" ? {name: item} : (item === null || typeof item !== "object" ? {} : item);
538
+ const entry =
539
+ typeof item === "string" ? { name: item } : item === null || typeof item !== "object" ? {} : item;
530
540
  const name = entry.name === undefined ? String(index) : String(entry.name);
531
541
  return {
532
542
  name: name,
@@ -535,7 +545,7 @@ class FileExplorer {
535
545
  size: entry.size === undefined ? undefined : Number(entry.size),
536
546
  modified: entry.modified === undefined ? undefined : entry.modified,
537
547
  hidden: entry.hidden === undefined ? name.startsWith(".") : entry.hidden === true,
538
- icon: entry.icon
548
+ icon: entry.icon,
539
549
  };
540
550
  });
541
551
  }
@@ -590,7 +600,8 @@ class FileExplorer {
590
600
  #compare(a = Object(), b = Object()) {
591
601
  const direction = this.#sort.direction === DIRECTION.DESC ? -1 : 1;
592
602
  if (a.type !== b.type) return a.type === TYPE.FOLDER ? -1 : 1;
593
- if (this.#sort.key === SORT.NAME) return direction * a.name.localeCompare(b.name, "ru", {numeric: true, sensitivity: "base"});
603
+ if (this.#sort.key === SORT.NAME)
604
+ return direction * a.name.localeCompare(b.name, "ru", { numeric: true, sensitivity: "base" });
594
605
  const value = (entry) => {
595
606
  if (this.#sort.key === SORT.SIZE) return entry.size === undefined ? undefined : Number(entry.size);
596
607
  if (entry.modified === undefined || entry.modified === null) return undefined;
@@ -599,7 +610,8 @@ class FileExplorer {
599
610
  };
600
611
  const left = value(a);
601
612
  const right = value(b);
602
- if (left === undefined && right === undefined) return direction * a.name.localeCompare(b.name, "ru", {numeric: true});
613
+ if (left === undefined && right === undefined)
614
+ return direction * a.name.localeCompare(b.name, "ru", { numeric: true });
603
615
  if (left === undefined) return 1;
604
616
  if (right === undefined) return -1;
605
617
  return direction * (left - right);
@@ -607,16 +619,29 @@ class FileExplorer {
607
619
 
608
620
  #visibleEntries() {
609
621
  const filter = this.#filter.trim().toLowerCase();
610
- const list = this.#entries.filter((entry) => (this.#show_hidden || entry.hidden !== true) &&
611
- (filter === "" || entry.name.toLowerCase().includes(filter)));
622
+ const list = this.#entries.filter(
623
+ (entry) =>
624
+ (this.#showHidden || entry.hidden !== true) &&
625
+ (filter === "" || entry.name.toLowerCase().includes(filter))
626
+ );
612
627
  return list.sort((a, b) => this.#compare(a, b));
613
628
  }
614
629
 
615
630
  #buildHead() {
616
- this.#actions.appendChild(this.#navButton(this.#back, Icons.NAVIGATION.ARROW_BACK, I18n.t("a11y.previous"), () => this.back()));
617
- this.#actions.appendChild(this.#navButton(this.#forward, Icons.NAVIGATION.ARROW_FORWARD, I18n.t("a11y.next"), () => this.forward()));
618
- this.#actions.appendChild(this.#navButton(this.#up, Icons.NAVIGATION.ARROW_UPWARD, I18n.t("fileExplorer.up"), () => this.up()));
619
- this.#actions.appendChild(this.#navButton(this.#refresh, Icons.NAVIGATION.REFRESH, I18n.t("fileExplorer.refresh"), () => this.refresh()));
631
+ this.#actions.appendChild(
632
+ this.#navButton(this.#back, Icons.NAVIGATION.ARROW_BACK, I18n.t("a11y.previous"), () => this.back())
633
+ );
634
+ this.#actions.appendChild(
635
+ this.#navButton(this.#forward, Icons.NAVIGATION.ARROW_FORWARD, I18n.t("a11y.next"), () => this.forward())
636
+ );
637
+ this.#actions.appendChild(
638
+ this.#navButton(this.#up, Icons.NAVIGATION.ARROW_UPWARD, I18n.t("fileExplorer.up"), () => this.up())
639
+ );
640
+ this.#actions.appendChild(
641
+ this.#navButton(this.#refresh, Icons.NAVIGATION.REFRESH, I18n.t("fileExplorer.refresh"), () =>
642
+ this.refresh()
643
+ )
644
+ );
620
645
  this.#head.appendChild(this.#actions);
621
646
  this.#search.className = "file_explorer_search";
622
647
  this.#search.type = "search";
@@ -627,32 +652,38 @@ class FileExplorer {
627
652
  this.#search.addEventListener("input", () => this.setFilter(this.#search.value));
628
653
  // Кнопка очистки — своя: у `input[type=search]` системная кнопка очистки скрыта,
629
654
  // чтобы поле выглядело как остальные (см. стили)
630
- this.#search_box.className = "file_explorer_search_box";
631
- this.#search_clear.type = "button";
632
- this.#search_clear.className = "file_explorer_search_clear";
633
- this.#search_clear.setAttribute("aria-label", I18n.t("fileExplorer.clearSearch"));
634
- const clear_glyph = new Icon(Icons.NAVIGATION.CLOSE, "16px").set();
635
- clear_glyph.setAttribute("aria-hidden", "true");
636
- this.#search_clear.appendChild(clear_glyph);
637
- this.#search_clear.addEventListener("click", () => {
655
+ this.#searchBox.className = "file_explorer_search_box";
656
+ this.#searchClear.type = "button";
657
+ this.#searchClear.className = "file_explorer_search_clear";
658
+ this.#searchClear.setAttribute("aria-label", I18n.t("fileExplorer.clearSearch"));
659
+ const clearGlyph = new Icon(Icons.NAVIGATION.CLOSE, "16px").set();
660
+ clearGlyph.setAttribute("aria-hidden", "true");
661
+ this.#searchClear.appendChild(clearGlyph);
662
+ this.#searchClear.addEventListener("click", () => {
638
663
  this.setFilter("");
639
664
  this.#search.focus();
640
665
  });
641
- this.#search_box.appendChild(this.#search);
642
- this.#search_box.appendChild(this.#search_clear);
643
- this.#head.appendChild(this.#search_box);
666
+ this.#searchBox.appendChild(this.#search);
667
+ this.#searchBox.appendChild(this.#searchClear);
668
+ this.#head.appendChild(this.#searchBox);
644
669
  this.#syncClear();
645
670
  this.#hidden.className = "file_explorer_hidden";
646
671
  this.#hidden.type = "button";
647
- this.#hidden.setAttribute("aria-pressed", this.#show_hidden ? "true" : "false");
648
- this.#hidden.setAttribute("aria-label", this.#show_hidden ? I18n.t("fileExplorer.hideHidden") : I18n.t("fileExplorer.showHidden"));
649
- const glyph = new Icon(this.#show_hidden ? Icons.ACTIONS.VISIBILITY : Icons.ACTIONS.VISIBILITY_OFF, "18px").set();
672
+ this.#hidden.setAttribute("aria-pressed", this.#showHidden ? "true" : "false");
673
+ this.#hidden.setAttribute(
674
+ "aria-label",
675
+ this.#showHidden ? I18n.t("fileExplorer.hideHidden") : I18n.t("fileExplorer.showHidden")
676
+ );
677
+ const glyph = new Icon(
678
+ this.#showHidden ? Icons.ACTIONS.VISIBILITY : Icons.ACTIONS.VISIBILITY_OFF,
679
+ "18px"
680
+ ).set();
650
681
  glyph.setAttribute("aria-hidden", "true");
651
682
  this.#hidden.appendChild(glyph);
652
- this.#hidden.addEventListener("click", () => this.setShowHidden(!this.#show_hidden));
683
+ this.#hidden.addEventListener("click", () => this.setShowHidden(!this.#showHidden));
653
684
  this.#head.appendChild(this.#hidden);
654
685
  // У панели одна остановка обхода: по кнопкам ходят стрелки/Home/End
655
- this.#roving = new RovingKeyboard(this.#actions, {itemSelector: "button"});
686
+ this.#roving = new RovingKeyboard(this.#actions, { itemSelector: "button" });
656
687
  return this;
657
688
  }
658
689
 
@@ -672,27 +703,40 @@ class FileExplorer {
672
703
  const cell = document.createElement("file_explorer_head_cell");
673
704
  cell.className = "file_explorer_head_cell";
674
705
  cell.setAttribute("role", "columnheader");
675
- cell.setAttribute("aria-sort", this.#sort.key === key
676
- ? (this.#sort.direction === DIRECTION.DESC ? "descending" : "ascending")
677
- : "none");
706
+ cell.setAttribute(
707
+ "aria-sort",
708
+ this.#sort.key === key ? (this.#sort.direction === DIRECTION.DESC ? "descending" : "ascending") : "none"
709
+ );
678
710
  const button = document.createElement("button");
679
711
  button.type = "button";
680
712
  button.className = "file_explorer_sort";
681
- button.setAttribute("aria-label", I18n.t("fileExplorer.sortBy", {title: title}));
713
+ button.setAttribute("aria-label", I18n.t("fileExplorer.sortBy", { title: title }));
682
714
  const text = document.createElement("file_explorer_sort_title");
683
715
  text.innerText = title;
684
716
  button.appendChild(text);
685
717
  if (this.#sort.key === key) {
686
- const glyph = new Icon(this.#sort.direction === DIRECTION.DESC ? Icons.NAVIGATION.ARROW_DOWNWARD : Icons.NAVIGATION.ARROW_UPWARD, "16px").set();
718
+ const glyph = new Icon(
719
+ this.#sort.direction === DIRECTION.DESC
720
+ ? Icons.NAVIGATION.ARROW_DOWNWARD
721
+ : Icons.NAVIGATION.ARROW_UPWARD,
722
+ "16px"
723
+ ).set();
687
724
  glyph.setAttribute("aria-hidden", "true");
688
725
  button.appendChild(glyph);
689
726
  }
690
727
  button.addEventListener("click", () => {
691
728
  // Повторный клик по активной колонке переворачивает порядок: «без сортировки»
692
729
  // у списка файлов смысла не имеет, поэтому цикла из трёх состояний здесь нет
693
- if (this.#sort.key === key) this.setSort(key, this.#sort.direction === DIRECTION.ASC ? DIRECTION.DESC : DIRECTION.ASC);
730
+ if (this.#sort.key === key)
731
+ this.setSort(key, this.#sort.direction === DIRECTION.ASC ? DIRECTION.DESC : DIRECTION.ASC);
694
732
  else this.setSort(key, DIRECTION.ASC);
695
- this.#announce(I18n.t("fileExplorer.sorted", {title: title, direction: this.#sort.direction === DIRECTION.ASC ? I18n.t("data.ascending") : I18n.t("data.descending")}));
733
+ this.#announce(
734
+ I18n.t("fileExplorer.sorted", {
735
+ title: title,
736
+ direction:
737
+ this.#sort.direction === DIRECTION.ASC ? I18n.t("data.ascending") : I18n.t("data.descending"),
738
+ })
739
+ );
696
740
  });
697
741
  cell.appendChild(button);
698
742
  return cell;
@@ -713,9 +757,12 @@ class FileExplorer {
713
757
  return cell;
714
758
  }
715
759
  cell.classList.add("file_explorer_cell_meta");
716
- cell.innerText = key === SORT.SIZE
717
- ? (entry.size === undefined ? "" : formatBytes(entry.size))
718
- : FileExplorer.#dateText(entry.modified);
760
+ cell.innerText =
761
+ key === SORT.SIZE
762
+ ? entry.size === undefined
763
+ ? ""
764
+ : formatBytes(entry.size)
765
+ : FileExplorer.#dateText(entry.modified);
719
766
  return cell;
720
767
  }
721
768
 
@@ -728,8 +775,8 @@ class FileExplorer {
728
775
  row.classList.toggle("file_explorer_row_selected", this.#selected.has(entry.path));
729
776
  row.classList.toggle("file_explorer_row_folder", entry.type === TYPE.FOLDER);
730
777
  row.appendChild(this.#cell(entry, SORT.NAME));
731
- if (this.#show_size) row.appendChild(this.#cell(entry, SORT.SIZE));
732
- if (this.#show_modified) row.appendChild(this.#cell(entry, SORT.MODIFIED));
778
+ if (this.#showSize) row.appendChild(this.#cell(entry, SORT.SIZE));
779
+ if (this.#showModified) row.appendChild(this.#cell(entry, SORT.MODIFIED));
733
780
  return row;
734
781
  }
735
782
 
@@ -737,27 +784,36 @@ class FileExplorer {
737
784
  #render() {
738
785
  // Строки пересобираются, а фокус живёт на строке: запоминаем её до перерисовки
739
786
  const active = document.activeElement;
740
- const focused = active === null || active === undefined || !this.#grid.contains(active) ? null : active.closest("file_explorer_row");
741
- if (focused !== null) this.#focus_path = focused.dataset.path;
742
- this.#head_row.innerHTML = "";
787
+ const focused =
788
+ active === null || active === undefined || !this.#grid.contains(active)
789
+ ? null
790
+ : active.closest("file_explorer_row");
791
+ if (focused !== null) this.#focusPath = focused.dataset.path;
792
+ this.#headRow.innerHTML = "";
743
793
  this.#body.innerHTML = "";
744
- this.#head_row.appendChild(this.#sortButton(SORT.NAME, I18n.t("fileExplorer.columnName")));
745
- if (this.#show_size) this.#head_row.appendChild(this.#sortButton(SORT.SIZE, I18n.t("fileExplorer.columnSize")));
746
- if (this.#show_modified) this.#head_row.appendChild(this.#sortButton(SORT.MODIFIED, I18n.t("fileExplorer.columnModified")));
794
+ this.#headRow.appendChild(this.#sortButton(SORT.NAME, I18n.t("fileExplorer.columnName")));
795
+ if (this.#showSize) this.#headRow.appendChild(this.#sortButton(SORT.SIZE, I18n.t("fileExplorer.columnSize")));
796
+ if (this.#showModified)
797
+ this.#headRow.appendChild(this.#sortButton(SORT.MODIFIED, I18n.t("fileExplorer.columnModified")));
747
798
  // Ширины колонок задаёт компонент: сетка шапки и сетка строк обязаны совпасть,
748
799
  // а какие колонки показаны, знает только он
749
800
  const columns = ["minmax(0, 1fr)"];
750
- if (this.#show_size) columns.push("110px");
751
- if (this.#show_modified) columns.push("160px");
801
+ if (this.#showSize) columns.push("110px");
802
+ if (this.#showModified) columns.push("160px");
752
803
  this.#grid.style.setProperty("--file_explorer_columns", columns.join(" "));
753
804
  const visible = this.#visibleEntries();
754
805
  for (const entry of visible) this.#body.appendChild(this.#rowNode(entry));
755
806
  this.#grid.hidden = visible.length === 0;
756
- this.#empty.hidden = visible.length > 0 || this.#error_object !== undefined;
757
- this.#empty.innerText = this.isFiltered() ? I18n.t("common.nothingFound") : this.#empty_text;
758
- this.#error.hidden = this.#error_object === undefined;
807
+ this.#empty.hidden = visible.length > 0 || this.#errorObject !== undefined;
808
+ this.#empty.innerText = this.isFiltered() ? I18n.t("common.nothingFound") : this.#emptyText;
809
+ this.#error.hidden = this.#errorObject === undefined;
759
810
  this.#error.innerText = this.getErrorText();
760
- this.#grid.setAttribute("aria-label", I18n.t("fileExplorer.gridLabel", {path: this.#path === "" ? I18n.t("fileExplorer.noFolderInline") : this.#path}));
811
+ this.#grid.setAttribute(
812
+ "aria-label",
813
+ I18n.t("fileExplorer.gridLabel", {
814
+ path: this.#path === "" ? I18n.t("fileExplorer.noFolderInline") : this.#path,
815
+ })
816
+ );
761
817
  this.#grid.setAttribute("aria-rowcount", String(visible.length + 1));
762
818
  this.#grid.setAttribute("aria-busy", this.#loading ? "true" : "false");
763
819
  this.#back.disabled = !this.canGoBack();
@@ -772,31 +828,35 @@ class FileExplorer {
772
828
 
773
829
  #renderPath() {
774
830
  const items = this.#pathItems();
775
- this.#path_bar.setItems(items.map((item, index) => (index === items.length - 1
776
- ? {title: item.title}
777
- : {title: item.title, clickEvent: () => this.go(item.path)})));
831
+ this.#pathBar.setItems(
832
+ items.map((item, index) =>
833
+ index === items.length - 1
834
+ ? { title: item.title }
835
+ : { title: item.title, clickEvent: () => this.go(item.path) }
836
+ )
837
+ );
778
838
  return this;
779
839
  }
780
840
 
781
841
  /** Звенья пути: с корнем оставляем только путь от него */
782
842
  #pathItems() {
783
843
  const full = String(this.#path);
784
- if (full === "") return [{title: I18n.t("fileExplorer.noFolder"), path: ""}];
785
- const root = this.#root_path === undefined ? undefined : String(this.#root_path).replace(/[\\/]+$/, "");
844
+ if (full === "") return [{ title: I18n.t("fileExplorer.noFolder"), path: "" }];
845
+ const root = this.#rootPath === undefined ? undefined : String(this.#rootPath).replace(/[\\/]+$/, "");
786
846
  const items = [];
787
- if (full.startsWith("/")) items.push({title: "/", path: "/"});
788
- else if (/^[A-Za-z]:/.test(full)) items.push({title: `${full.slice(0, 2)}/`, path: `${full.slice(0, 2)}/`});
847
+ if (full.startsWith("/")) items.push({ title: "/", path: "/" });
848
+ else if (/^[A-Za-z]:/.test(full)) items.push({ title: `${full.slice(0, 2)}/`, path: `${full.slice(0, 2)}/` });
789
849
  const base = items.length > 0 ? items[0].path : "";
790
850
  const rest = full.slice(base.length).replace(/^[\\/]+/, "");
791
851
  let current = base;
792
852
  for (const part of rest.split(/[\\/]+/).filter((piece) => piece !== "")) {
793
- current = current === "" ? part : (current.endsWith("/") ? `${current}${part}` : `${current}/${part}`);
794
- items.push({title: part, path: current});
853
+ current = current === "" ? part : current.endsWith("/") ? `${current}${part}` : `${current}/${part}`;
854
+ items.push({ title: part, path: current });
795
855
  }
796
- if (items.length === 0) items.push({title: full, path: full});
856
+ if (items.length === 0) items.push({ title: full, path: full });
797
857
  if (root !== undefined && root !== "") {
798
858
  const index = items.findIndex((item) => item.path === root);
799
- if (index > 0) return [{title: items[index].title, path: root}].concat(items.slice(index + 1));
859
+ if (index > 0) return [{ title: items[index].title, path: root }].concat(items.slice(index + 1));
800
860
  }
801
861
  return items;
802
862
  }
@@ -815,14 +875,14 @@ class FileExplorer {
815
875
  const list = [...this.#body.querySelectorAll("file_explorer_row")];
816
876
  if (list.length === 0) return;
817
877
  const stop =
818
- list.find((row) => row.dataset.path === this.#active_path) ??
819
- list.find((row) => row.dataset.path === this.#focus_path) ??
878
+ list.find((row) => row.dataset.path === this.#activePath) ??
879
+ list.find((row) => row.dataset.path === this.#focusPath) ??
820
880
  list[0];
821
- this.#active_path = stop.dataset.path;
881
+ this.#activePath = stop.dataset.path;
822
882
  for (const row of list) row.setAttribute("tabindex", row === stop ? "0" : "-1");
823
883
  // Фокус возвращаем только если он был в сетке: иначе перерисовка его бы украла
824
- if (this.#focus_path !== undefined && stop.dataset.path === this.#focus_path) {
825
- this.#focus_path = undefined;
884
+ if (this.#focusPath !== undefined && stop.dataset.path === this.#focusPath) {
885
+ this.#focusPath = undefined;
826
886
  stop.focus();
827
887
  }
828
888
  return this;
@@ -830,7 +890,7 @@ class FileExplorer {
830
890
 
831
891
  #focusRow(row = undefined) {
832
892
  if (row === undefined || row === null) return this;
833
- this.#active_path = row.dataset.path;
893
+ this.#activePath = row.dataset.path;
834
894
  for (const other of this.#body.querySelectorAll("file_explorer_row")) {
835
895
  other.setAttribute("tabindex", other === row ? "0" : "-1");
836
896
  }
@@ -839,7 +899,10 @@ class FileExplorer {
839
899
  }
840
900
 
841
901
  #currentRow(target = undefined) {
842
- const row = target === undefined || target === null || typeof target.closest !== "function" ? null : target.closest("file_explorer_row");
902
+ const row =
903
+ target === undefined || target === null || typeof target.closest !== "function"
904
+ ? null
905
+ : target.closest("file_explorer_row");
843
906
  if (row !== null) return row;
844
907
  return this.#body.querySelector('file_explorer_row[tabindex="0"]');
845
908
  }
@@ -891,25 +954,33 @@ class FileExplorer {
891
954
 
892
955
  #notifySelect() {
893
956
  const entries = this.getSelected();
894
- this.#announce(entries.length === 0 ? I18n.t("fileExplorer.selectionCleared") : I18n.t("fileExplorer.selectedCount", {count: entries.length}));
957
+ this.#announce(
958
+ entries.length === 0
959
+ ? I18n.t("fileExplorer.selectionCleared")
960
+ : I18n.t("fileExplorer.selectedCount", { count: entries.length })
961
+ );
895
962
  for (const listener of [...this.#listeners]) listener(entries, this);
896
- if (this.#on_select !== undefined) this.#on_select(entries, this);
963
+ if (this.#onSelect !== undefined) this.#onSelect(entries, this);
897
964
  return this;
898
965
  }
899
966
 
900
967
  /** Что видно сейчас: количество, пустота, поиск и ошибка чтения */
901
968
  #announceState() {
902
- if (this.#error_object !== undefined) {
969
+ if (this.#errorObject !== undefined) {
903
970
  this.#status.innerText = this.getErrorText();
904
971
  return this;
905
972
  }
906
973
  const visible = this.#visibleEntries().length;
907
- const total = this.#entries.filter((entry) => this.#show_hidden || entry.hidden !== true).length;
974
+ const total = this.#entries.filter((entry) => this.#showHidden || entry.hidden !== true).length;
908
975
  if (this.isFiltered()) {
909
- this.#status.innerText = visible === 0 ? I18n.t("common.nothingFound") : I18n.t("fileExplorer.foundCount", {found: visible, total: total});
976
+ this.#status.innerText =
977
+ visible === 0
978
+ ? I18n.t("common.nothingFound")
979
+ : I18n.t("fileExplorer.foundCount", { found: visible, total: total });
910
980
  return this;
911
981
  }
912
- this.#status.innerText = visible === 0 ? this.#empty_text : I18n.t("fileExplorer.entriesCount", {count: visible});
982
+ this.#status.innerText =
983
+ visible === 0 ? this.#emptyText : I18n.t("fileExplorer.entriesCount", { count: visible });
913
984
  return this;
914
985
  }
915
986
 
@@ -922,12 +993,12 @@ class FileExplorer {
922
993
 
923
994
  /** Кнопка очистки видна, только когда есть что очищать */
924
995
  #syncClear() {
925
- this.#search_clear.hidden = this.#filter === "";
996
+ this.#searchClear.hidden = this.#filter === "";
926
997
  return this;
927
998
  }
928
999
 
929
1000
  #syncLabel() {
930
- if (this.#title_text !== undefined && this.#title_text !== "") {
1001
+ if (this.#titleText !== undefined && this.#titleText !== "") {
931
1002
  this.#root.setAttribute("aria-labelledby", this.#title.id);
932
1003
  this.#root.removeAttribute("aria-label");
933
1004
  } else {
@@ -938,8 +1009,8 @@ class FileExplorer {
938
1009
  }
939
1010
 
940
1011
  #accessibleName() {
941
- if (this.#title_text !== undefined && this.#title_text !== "") return this.#title_text;
942
- if (this.#label_text !== undefined && this.#label_text !== "") return this.#label_text;
1012
+ if (this.#titleText !== undefined && this.#titleText !== "") return this.#titleText;
1013
+ if (this.#labelText !== undefined && this.#labelText !== "") return this.#labelText;
943
1014
  return I18n.t("fileExplorer.title");
944
1015
  }
945
1016
 
@@ -953,7 +1024,7 @@ class FileExplorer {
953
1024
  // иначе ближайшая перерисовка вернула бы фокус не туда, где он был
954
1025
  const row = event.target instanceof Element ? event.target.closest("file_explorer_row") : null;
955
1026
  if (row === null || row.dataset.path === undefined) return;
956
- this.#active_path = row.dataset.path;
1027
+ this.#activePath = row.dataset.path;
957
1028
  };
958
1029
 
959
1030
  #onClick = (event) => {
@@ -1034,4 +1105,4 @@ class FileExplorer {
1034
1105
  };
1035
1106
  }
1036
1107
 
1037
- exports.FileExplorer = FileExplorer
1108
+ exports.FileExplorer = FileExplorer;