chuijs 4.0.3 → 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 (258) hide show
  1. package/CHANGELOG.md +536 -0
  2. package/framework/appLayout/default/chui_app_layout.js +361 -303
  3. package/framework/appLayout/default/global_style.css +243 -89
  4. package/framework/appLayout/default/main_theme_style.css +188 -101
  5. package/framework/components/chui_accordion/accordion.js +17 -17
  6. package/framework/components/chui_accordion/styles.css +7 -7
  7. package/framework/components/chui_action_sheet/action_sheet.js +35 -32
  8. package/framework/components/chui_action_sheet/styles.css +10 -4
  9. package/framework/components/chui_actions_bar/actions_bar.js +338 -0
  10. package/framework/components/chui_actions_bar/styles.css +139 -0
  11. package/framework/components/chui_affix/affix.js +4 -4
  12. package/framework/components/chui_affix/styles.css +5 -0
  13. package/framework/components/chui_alert/alert.js +11 -5
  14. package/framework/components/chui_alert/styles.css +7 -0
  15. package/framework/components/chui_avatar/avatar.js +13 -13
  16. package/framework/components/chui_avatar/styles.css +1 -1
  17. package/framework/components/chui_avatar_group/avatar_group.js +4 -4
  18. package/framework/components/chui_avatar_group/styles.css +1 -1
  19. package/framework/components/chui_backtop/backtop.js +25 -2
  20. package/framework/components/chui_backtop/styles.css +21 -0
  21. package/framework/components/chui_badge/badge.js +15 -15
  22. package/framework/components/chui_badge/styles.css +6 -6
  23. package/framework/components/chui_banner/banner.js +222 -0
  24. package/framework/components/chui_banner/styles.css +170 -0
  25. package/framework/components/chui_breadcrumb/styles.css +1 -1
  26. package/framework/components/chui_button/button.js +24 -28
  27. package/framework/components/chui_button/styles.css +5 -5
  28. package/framework/components/chui_calendar/calendar.js +6 -6
  29. package/framework/components/chui_card/card.js +14 -14
  30. package/framework/components/chui_card_scroll/card_scroll.js +169 -0
  31. package/framework/components/chui_card_scroll/styles.css +86 -0
  32. package/framework/components/chui_chat/chat.js +73 -51
  33. package/framework/components/chui_chat/styles.css +18 -7
  34. package/framework/components/chui_clipboard_history/clipboard_history.js +25 -20
  35. package/framework/components/chui_clipboard_history/styles.css +10 -8
  36. package/framework/components/chui_code/code.js +8 -8
  37. package/framework/components/chui_code/styles.css +2 -2
  38. package/framework/components/chui_code_editor/code_editor.js +26 -26
  39. package/framework/components/chui_code_editor/styles.css +3 -3
  40. package/framework/components/chui_command_palette/command_palette.js +29 -29
  41. package/framework/components/chui_command_palette/styles.css +5 -5
  42. package/framework/components/chui_console/console.js +9 -9
  43. package/framework/components/chui_console/styles.css +5 -2
  44. package/framework/components/chui_content_block/content_block.js +2 -2
  45. package/framework/components/chui_context_menu/context_menu.js +162 -54
  46. package/framework/components/chui_context_menu/styles.css +82 -8
  47. package/framework/components/chui_custom_element/custom_element.js +11 -11
  48. package/framework/components/chui_data_grid/data_grid.js +268 -268
  49. package/framework/components/chui_data_grid/styles.css +14 -14
  50. package/framework/components/chui_descriptions/styles.css +1 -1
  51. package/framework/components/chui_details/details.js +12 -12
  52. package/framework/components/chui_details/styles.css +7 -7
  53. package/framework/components/chui_diff_viewer/diff_viewer.js +88 -88
  54. package/framework/components/chui_diff_viewer/styles.css +9 -9
  55. package/framework/components/chui_divider/divider.js +6 -6
  56. package/framework/components/chui_divider/styles.css +2 -2
  57. package/framework/components/chui_drawer/drawer.js +37 -14
  58. package/framework/components/chui_drawer/styles.css +18 -4
  59. package/framework/components/chui_drop_zone/drop_zone.js +6 -6
  60. package/framework/components/chui_drop_zone/styles.css +10 -6
  61. package/framework/components/chui_fieldset/fieldset.js +6 -6
  62. package/framework/components/chui_fieldset/styles.css +5 -5
  63. package/framework/components/chui_file_explorer/file_explorer.js +115 -115
  64. package/framework/components/chui_file_explorer/styles.css +6 -6
  65. package/framework/components/chui_form/form.js +11 -11
  66. package/framework/components/chui_gantt/gantt.js +41 -41
  67. package/framework/components/chui_gantt/styles.css +5 -5
  68. package/framework/components/chui_graphs/graphs.js +82 -46
  69. package/framework/components/chui_graphs/styles.css +26 -5
  70. package/framework/components/chui_hero/hero.js +63 -20
  71. package/framework/components/chui_hero/styles.css +176 -7
  72. package/framework/components/chui_html_block/html_block.js +5 -5
  73. package/framework/components/chui_html_block/styles.css +1 -1
  74. package/framework/components/chui_hx/styles.css +2 -2
  75. package/framework/components/chui_icons/icons.js +7 -7
  76. package/framework/components/chui_icons/styles.css +2 -1
  77. package/framework/components/chui_inputs/chui_check_box/styles.css +12 -3
  78. package/framework/components/chui_inputs/chui_chips/chips.js +92 -31
  79. package/framework/components/chui_inputs/chui_chips/styles.css +28 -5
  80. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +20 -15
  81. package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
  82. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +58 -58
  83. package/framework/components/chui_inputs/chui_combo_box/styles.css +9 -9
  84. package/framework/components/chui_inputs/chui_date/date.js +93 -95
  85. package/framework/components/chui_inputs/chui_date/styles.css +8 -8
  86. package/framework/components/chui_inputs/chui_date_range/date_range.js +22 -22
  87. package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
  88. package/framework/components/chui_inputs/chui_datetime/datetime.js +12 -12
  89. package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
  90. package/framework/components/chui_inputs/chui_email/email.js +10 -10
  91. package/framework/components/chui_inputs/chui_email/styles.css +6 -6
  92. package/framework/components/chui_inputs/chui_file/file.js +8 -8
  93. package/framework/components/chui_inputs/chui_file/styles.css +12 -9
  94. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +39 -39
  95. package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
  96. package/framework/components/chui_inputs/chui_masked/masked.js +44 -44
  97. package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
  98. package/framework/components/chui_inputs/chui_mention/mention.js +13 -13
  99. package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
  100. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +147 -137
  101. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +14 -8
  102. package/framework/components/chui_inputs/chui_number/number.js +42 -45
  103. package/framework/components/chui_inputs/chui_number/styles.css +6 -6
  104. package/framework/components/chui_inputs/chui_otp/otp.js +32 -32
  105. package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
  106. package/framework/components/chui_inputs/chui_password/password.js +30 -36
  107. package/framework/components/chui_inputs/chui_password/styles.css +7 -7
  108. package/framework/components/chui_inputs/chui_radio_button/styles.css +2 -2
  109. package/framework/components/chui_inputs/chui_rating/rating.js +28 -28
  110. package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
  111. package/framework/components/chui_inputs/chui_select_box/select_box.js +54 -54
  112. package/framework/components/chui_inputs/chui_select_box/styles.css +10 -10
  113. package/framework/components/chui_inputs/chui_slider/slider.js +14 -14
  114. package/framework/components/chui_inputs/chui_slider/styles.css +5 -4
  115. package/framework/components/chui_inputs/chui_text/styles.css +7 -7
  116. package/framework/components/chui_inputs/chui_text/text.js +13 -17
  117. package/framework/components/chui_inputs/chui_text_area/styles.css +8 -8
  118. package/framework/components/chui_inputs/chui_text_area/text_area.js +23 -23
  119. package/framework/components/chui_inputs/chui_time/styles.css +13 -17
  120. package/framework/components/chui_inputs/chui_time/time.js +35 -24
  121. package/framework/components/chui_inputs/chui_toggle/styles.css +10 -3
  122. package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
  123. package/framework/components/chui_json_editor/json_editor.js +87 -87
  124. package/framework/components/chui_json_editor/styles.css +5 -5
  125. package/framework/components/chui_json_viewer/json_viewer.js +80 -80
  126. package/framework/components/chui_json_viewer/styles.css +6 -6
  127. package/framework/components/chui_kanban/kanban.js +27 -27
  128. package/framework/components/chui_kanban/styles.css +8 -4
  129. package/framework/components/chui_kbd/styles.css +1 -1
  130. package/framework/components/chui_label/label.js +17 -17
  131. package/framework/components/chui_label/styles.css +1 -1
  132. package/framework/components/chui_link/link.js +9 -9
  133. package/framework/components/chui_link/styles.css +11 -4
  134. package/framework/components/chui_list/list.js +11 -11
  135. package/framework/components/chui_list/styles.css +2 -2
  136. package/framework/components/chui_markdown_editor/markdown_editor.js +66 -66
  137. package/framework/components/chui_markdown_editor/styles.css +5 -5
  138. package/framework/components/chui_media/audio.js +240 -246
  139. package/framework/components/chui_media/audio_styles.css +41 -23
  140. package/framework/components/chui_media/image.js +49 -46
  141. package/framework/components/chui_media/image_compare.js +35 -35
  142. package/framework/components/chui_media/image_compare_styles.css +5 -5
  143. package/framework/components/chui_media/image_styles.css +1 -1
  144. package/framework/components/chui_media/video.js +148 -148
  145. package/framework/components/chui_media/video_styles.css +27 -11
  146. package/framework/components/chui_menu_bar/menu_bar.js +54 -54
  147. package/framework/components/chui_menu_bar/styles.css +10 -10
  148. package/framework/components/chui_message_bar/message_bar.js +551 -0
  149. package/framework/components/chui_message_bar/styles.css +248 -0
  150. package/framework/components/chui_modal/modal.js +37 -13
  151. package/framework/components/chui_notification/notification.js +76 -54
  152. package/framework/components/chui_notification/notification_download.js +26 -81
  153. package/framework/components/chui_notification/notification_download_card.js +115 -0
  154. package/framework/components/chui_notification/notification_download_progress.css +2 -2
  155. package/framework/components/chui_notification/notification_download_progress.js +70 -108
  156. package/framework/components/chui_notification/notification_update.js +31 -31
  157. package/framework/components/chui_notification/status_plate.css +3 -3
  158. package/framework/components/chui_notification/styles.css +6 -1
  159. package/framework/components/chui_notification/styles_download.css +1 -1
  160. package/framework/components/chui_notification/styles_update.css +2 -2
  161. package/framework/components/chui_org_chart/org_chart.js +29 -29
  162. package/framework/components/chui_org_chart/styles.css +2 -2
  163. package/framework/components/chui_pagination/pagination.js +3 -3
  164. package/framework/components/chui_pagination/styles.css +1 -1
  165. package/framework/components/chui_paragraph/styles.css +2 -2
  166. package/framework/components/chui_popover/popover.js +29 -23
  167. package/framework/components/chui_popover/styles.css +1 -1
  168. package/framework/components/chui_popups/popups.js +124 -124
  169. package/framework/components/chui_popups/styles.css +5 -5
  170. package/framework/components/chui_print_button/print_button.js +15 -10
  171. package/framework/components/chui_progress_bar/progress_bar.js +34 -12
  172. package/framework/components/chui_progress_bar/styles.css +4 -4
  173. package/framework/components/chui_progress_ring/progress_ring.js +15 -15
  174. package/framework/components/chui_progress_ring/styles.css +1 -1
  175. package/framework/components/chui_qrcode/qrcode.js +12 -12
  176. package/framework/components/chui_query_builder/query_builder.js +46 -46
  177. package/framework/components/chui_query_builder/styles.css +4 -4
  178. package/framework/components/chui_radio_group/styles.css +1 -1
  179. package/framework/components/chui_result/result.js +15 -15
  180. package/framework/components/chui_result/styles.css +3 -3
  181. package/framework/components/chui_scheduler/scheduler.js +74 -74
  182. package/framework/components/chui_scheduler/styles.css +5 -5
  183. package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
  184. package/framework/components/chui_screen_spinner/styles.css +49 -0
  185. package/framework/components/chui_segmented_control/segmented_control.js +63 -6
  186. package/framework/components/chui_segmented_control/styles.css +32 -6
  187. package/framework/components/chui_settings/settings.js +45 -46
  188. package/framework/components/chui_settings/styles.css +4 -4
  189. package/framework/components/chui_shortcut_help/shortcut_help.js +32 -34
  190. package/framework/components/chui_sidebar_nav/sidebar_nav.js +98 -29
  191. package/framework/components/chui_sidebar_nav/styles.css +9 -3
  192. package/framework/components/chui_slideshow/slideshow.js +85 -85
  193. package/framework/components/chui_slideshow/styles.css +6 -6
  194. package/framework/components/chui_spinner/spinner.js +10 -10
  195. package/framework/components/chui_split_button/split_button.js +30 -12
  196. package/framework/components/chui_split_button/styles.css +21 -5
  197. package/framework/components/chui_splitter/splitter.js +11 -11
  198. package/framework/components/chui_splitter/styles.css +2 -2
  199. package/framework/components/chui_statistic/statistic.js +47 -47
  200. package/framework/components/chui_status_bar/styles.css +2 -2
  201. package/framework/components/chui_stepper/stepper.js +202 -31
  202. package/framework/components/chui_stepper/styles.css +79 -14
  203. package/framework/components/chui_sticky_bar/sticky_bar.js +44 -13
  204. package/framework/components/chui_sticky_bar/styles.css +10 -8
  205. package/framework/components/chui_tabs/chui_tabs.js +74 -26
  206. package/framework/components/chui_tabs/styles.css +40 -12
  207. package/framework/components/chui_text_editor/styles.css +7 -7
  208. package/framework/components/chui_text_editor/text_editor.js +56 -56
  209. package/framework/components/chui_text_editor/text_editor_panel.js +173 -174
  210. package/framework/components/chui_theme_toggle/theme_toggle.js +21 -5
  211. package/framework/components/chui_timeline/styles.css +3 -1
  212. package/framework/components/chui_toast_queue/styles.css +11 -11
  213. package/framework/components/chui_toast_queue/toast_queue.js +30 -27
  214. package/framework/components/chui_toolbar/styles.css +2 -2
  215. package/framework/components/chui_tooltip/styles.css +2 -2
  216. package/framework/components/chui_tooltip/tooltip.js +3 -3
  217. package/framework/components/chui_tour/styles.css +8 -8
  218. package/framework/components/chui_tour/tour.js +72 -72
  219. package/framework/components/chui_transfer/styles.css +6 -6
  220. package/framework/components/chui_transfer/transfer.js +93 -106
  221. package/framework/components/chui_tree_table/styles.css +4 -4
  222. package/framework/components/chui_tree_table/tree_table.js +45 -45
  223. package/framework/components/chui_tree_view/styles.css +10 -8
  224. package/framework/components/chui_tree_view/tree_view.js +185 -183
  225. package/framework/components/chui_user_bar/styles.css +145 -0
  226. package/framework/components/chui_user_bar/user_bar.js +402 -0
  227. package/framework/components/chui_user_list_bar/styles.css +173 -0
  228. package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
  229. package/framework/components/chui_watermark/watermark.js +17 -17
  230. package/framework/components/chui_webview/webview.js +9 -9
  231. package/framework/components/chui_write_bar/styles.css +172 -0
  232. package/framework/components/chui_write_bar/write_bar.js +320 -0
  233. package/framework/main_window.js +1234 -0
  234. package/framework/modules/chui_a11y/chui_a11y.js +78 -79
  235. package/framework/modules/chui_animations/animations.js +77 -15
  236. package/framework/modules/chui_animations/motion.js +120 -0
  237. package/framework/modules/chui_animations/styles.css +36 -3
  238. package/framework/modules/chui_functions.js +31 -27
  239. package/framework/modules/chui_highlight/styles.css +13 -10
  240. package/framework/modules/chui_i18n/chui_i18n.js +3 -3
  241. package/framework/modules/chui_i18n/locales/feedback.js +62 -0
  242. package/framework/modules/chui_i18n/locales/navigation.js +4 -0
  243. package/framework/modules/chui_json/chui_json.js +2 -2
  244. package/framework/modules/chui_logger/chui_logger.js +23 -8
  245. package/framework/modules/chui_page/page.js +10 -10
  246. package/framework/modules/chui_palette/palette.js +61 -0
  247. package/framework/modules/chui_qr/chui_qr.js +23 -23
  248. package/framework/modules/chui_scrollbar/chui_scrollbar.js +45 -45
  249. package/framework/modules/chui_scrollbar/styles.css +3 -3
  250. package/framework/modules/chui_services/chui_services.js +8 -8
  251. package/framework/modules/chui_services/desktop.js +77 -71
  252. package/framework/modules/chui_sortable/chui_sortable.js +17 -17
  253. package/framework/modules/chui_validate/chui_validate.js +3 -3
  254. package/framework/tray_bar/chui_menu_items.js +2 -2
  255. package/index.d.ts +266 -34
  256. package/index.js +20 -1168
  257. package/package.json +6 -4
  258. package/framework/components/telegram_bot/chui_telegram_bot.js +0 -191
@@ -26,16 +26,16 @@ const FOCUSABLE_SELECTOR = [
26
26
  class OverlayA11y {
27
27
  // Стек открытых слоёв: нужен, чтобы вложенный слой не снимал фон-инерт у родителя
28
28
  static #stack = [];
29
- static #labelled_counter = 0;
29
+ static #labelledCounter = 0;
30
30
 
31
31
  #element = undefined;
32
32
  #role = "dialog";
33
33
  #label = undefined;
34
- #label_node = undefined;
35
- #described_by = undefined;
36
- #on_escape = undefined;
37
- #previous_focus = undefined;
38
- #is_open = false;
34
+ #labelNode = undefined;
35
+ #describedBy = undefined;
36
+ #onEscape = undefined;
37
+ #previousFocus = undefined;
38
+ #isOpen = false;
39
39
 
40
40
  constructor(
41
41
  element,
@@ -50,14 +50,14 @@ class OverlayA11y {
50
50
  this.#element = element;
51
51
  if (options.role !== undefined) this.#role = options.role;
52
52
  this.#label = options.label;
53
- this.#label_node = options.labelNode;
54
- this.#described_by = options.describedBy;
55
- this.#on_escape = options.onEscape;
53
+ this.#labelNode = options.labelNode;
54
+ this.#describedBy = options.describedBy;
55
+ this.#onEscape = options.onEscape;
56
56
  this.#applyAria();
57
57
  }
58
58
 
59
59
  get isOpen() {
60
- return this.#is_open;
60
+ return this.#isOpen;
61
61
  }
62
62
 
63
63
  setLabel(text = String()) {
@@ -66,18 +66,18 @@ class OverlayA11y {
66
66
  }
67
67
 
68
68
  open() {
69
- if (this.#is_open) return;
70
- this.#is_open = true;
69
+ if (this.#isOpen) return;
70
+ this.#isOpen = true;
71
71
  OverlayA11y.#stack.push(this);
72
- this.#previous_focus = document.activeElement;
72
+ this.#previousFocus = document.activeElement;
73
73
  OverlayA11y.#applyInert();
74
74
  document.addEventListener("keydown", this.#keydownEvent, true);
75
75
  this.#focusFirst();
76
76
  }
77
77
 
78
78
  close() {
79
- if (!this.#is_open) return;
80
- this.#is_open = false;
79
+ if (!this.#isOpen) return;
80
+ this.#isOpen = false;
81
81
  const index = OverlayA11y.#stack.indexOf(this);
82
82
  if (index !== -1) OverlayA11y.#stack.splice(index, 1);
83
83
  document.removeEventListener("keydown", this.#keydownEvent, true);
@@ -89,21 +89,21 @@ class OverlayA11y {
89
89
  const element = this.#element;
90
90
  element.setAttribute("role", this.#role);
91
91
  element.setAttribute("aria-modal", "true");
92
- if (this.#label_node !== undefined) {
93
- element.setAttribute("aria-labelledby", OverlayA11y.#ensureId(this.#label_node));
92
+ if (this.#labelNode !== undefined) {
93
+ element.setAttribute("aria-labelledby", OverlayA11y.#ensureId(this.#labelNode));
94
94
  element.removeAttribute("aria-label");
95
95
  } else if (this.#label !== undefined) {
96
96
  element.setAttribute("aria-label", this.#label);
97
97
  }
98
- if (this.#described_by !== undefined) {
99
- element.setAttribute("aria-describedby", OverlayA11y.#ensureId(this.#described_by));
98
+ if (this.#describedBy !== undefined) {
99
+ element.setAttribute("aria-describedby", OverlayA11y.#ensureId(this.#describedBy));
100
100
  }
101
101
  }
102
102
 
103
103
  static #ensureId(element) {
104
104
  if (element.id === undefined || element.id === "") {
105
- OverlayA11y.#labelled_counter += 1;
106
- element.id = "chui_a11y_node_" + OverlayA11y.#labelled_counter;
105
+ OverlayA11y.#labelledCounter += 1;
106
+ element.id = "chui_a11y_node_" + OverlayA11y.#labelledCounter;
107
107
  }
108
108
  return element.id;
109
109
  }
@@ -146,8 +146,8 @@ class OverlayA11y {
146
146
  }
147
147
 
148
148
  #restoreFocus() {
149
- const previous = this.#previous_focus;
150
- this.#previous_focus = undefined;
149
+ const previous = this.#previousFocus;
150
+ this.#previousFocus = undefined;
151
151
  if (previous !== undefined && previous !== null && document.body.contains(previous)) previous.focus();
152
152
  }
153
153
 
@@ -177,7 +177,7 @@ class OverlayA11y {
177
177
  if (event.key === "Escape") {
178
178
  event.preventDefault();
179
179
  event.stopPropagation();
180
- if (this.#on_escape !== undefined) this.#on_escape();
180
+ if (this.#onEscape !== undefined) this.#onEscape();
181
181
  else this.close();
182
182
  return;
183
183
  }
@@ -226,17 +226,17 @@ const isDisabled = (element) =>
226
226
  */
227
227
  class MenuKeyboard {
228
228
  #container = undefined;
229
- #item_selector = undefined;
229
+ #itemSelector = undefined;
230
230
  #role = "menu";
231
- #item_role = "menuitem";
232
- #submenu_selector = undefined;
233
- #on_submenu_open = undefined;
234
- #is_submenu = false;
235
- #type_buffer = "";
236
- #type_at = 0;
237
- #on_escape = undefined;
231
+ #itemRole = "menuitem";
232
+ #submenuSelector = undefined;
233
+ #onSubmenuOpen = undefined;
234
+ #isSubmenu = false;
235
+ #typeBuffer = "";
236
+ #typeAt = 0;
237
+ #onEscape = undefined;
238
238
  #trigger = undefined;
239
- #trigger_bound = false;
239
+ #triggerBound = false;
240
240
 
241
241
  constructor(
242
242
  container,
@@ -251,12 +251,12 @@ class MenuKeyboard {
251
251
  ) {
252
252
  this.#container = container;
253
253
  if (options.role !== undefined) this.#role = options.role;
254
- if (options.itemRole !== undefined) this.#item_role = options.itemRole;
255
- this.#item_selector = options.itemSelector;
256
- this.#submenu_selector = options.submenuSelector;
257
- this.#on_escape = options.onEscape;
258
- this.#on_submenu_open = options.onSubmenuOpen;
259
- this.#is_submenu = options.submenu === true;
254
+ if (options.itemRole !== undefined) this.#itemRole = options.itemRole;
255
+ this.#itemSelector = options.itemSelector;
256
+ this.#submenuSelector = options.submenuSelector;
257
+ this.#onEscape = options.onEscape;
258
+ this.#onSubmenuOpen = options.onSubmenuOpen;
259
+ this.#isSubmenu = options.submenu === true;
260
260
  this.#trigger = options.trigger;
261
261
  container.setAttribute("role", this.#role);
262
262
  this.refresh();
@@ -265,17 +265,17 @@ class MenuKeyboard {
265
265
  // содержит контейнер (строка подменю — предок своей панели), то это тот же путь события:
266
266
  // второй обработчик на предке обработал бы одну клавишу дважды — пункт подменю срабатывал бы
267
267
  // от одного Enter дважды, а одна стрелка перескакивала бы через пункт.
268
- this.#trigger_bound = options.trigger !== undefined && options.trigger.contains(container) === false;
269
- if (this.#trigger_bound) options.trigger.addEventListener("keydown", this.#keydownEvent);
268
+ this.#triggerBound = options.trigger !== undefined && options.trigger.contains(container) === false;
269
+ if (this.#triggerBound) options.trigger.addEventListener("keydown", this.#keydownEvent);
270
270
  }
271
271
 
272
272
  /** Перечитать пункты: роли, tabindex, состояние триггера (в том числе в закрытом меню) */
273
273
  refresh() {
274
274
  for (const item of this.#allItems()) {
275
- if (item.getAttribute("role") === null) item.setAttribute("role", this.#item_role);
275
+ if (item.getAttribute("role") === null) item.setAttribute("role", this.#itemRole);
276
276
  if (item.getAttribute("tabindex") === null) item.setAttribute("tabindex", "-1");
277
277
  if (isDisabled(item)) item.setAttribute("aria-disabled", "true");
278
- if (this.#submenu_selector !== undefined && item.querySelector(this.#submenu_selector) !== null) {
278
+ if (this.#submenuSelector !== undefined && item.querySelector(this.#submenuSelector) !== null) {
279
279
  item.setAttribute("aria-haspopup", "true");
280
280
  }
281
281
  }
@@ -296,7 +296,7 @@ class MenuKeyboard {
296
296
 
297
297
  // Все пункты своего уровня (и скрытые — им тоже нужны роли и tabindex)
298
298
  #allItems() {
299
- return [...this.#container.querySelectorAll(this.#item_selector)].filter(
299
+ return [...this.#container.querySelectorAll(this.#itemSelector)].filter(
300
300
  (item) => item.closest('[role="menu"]') === this.#container
301
301
  );
302
302
  }
@@ -330,7 +330,7 @@ class MenuKeyboard {
330
330
  }
331
331
 
332
332
  #openOrClick(item) {
333
- if (this.#submenu_selector !== undefined && item.querySelector(this.#submenu_selector) !== null) {
333
+ if (this.#submenuSelector !== undefined && item.querySelector(this.#submenuSelector) !== null) {
334
334
  this.#openSubmenu(item);
335
335
  return;
336
336
  }
@@ -338,11 +338,11 @@ class MenuKeyboard {
338
338
  }
339
339
 
340
340
  #openSubmenu(row) {
341
- if (this.#submenu_selector === undefined) return;
342
- const submenu = row.querySelector(this.#submenu_selector);
341
+ if (this.#submenuSelector === undefined) return;
342
+ const submenu = row.querySelector(this.#submenuSelector);
343
343
  if (submenu === null) return;
344
- if (this.#on_submenu_open !== undefined) this.#on_submenu_open(row);
345
- const first = [...submenu.querySelectorAll(this.#item_selector)].filter(isVisible)[0];
344
+ if (this.#onSubmenuOpen !== undefined) this.#onSubmenuOpen(row);
345
+ const first = [...submenu.querySelectorAll(this.#itemSelector)].filter(isVisible)[0];
346
346
  if (first !== undefined) first.focus();
347
347
  }
348
348
 
@@ -381,20 +381,20 @@ class MenuKeyboard {
381
381
  break;
382
382
  }
383
383
  case "ArrowLeft": {
384
- if (!this.#is_submenu) break;
384
+ if (!this.#isSubmenu) break;
385
385
  event.preventDefault();
386
- if (this.#on_escape !== undefined) this.#on_escape();
386
+ if (this.#onEscape !== undefined) this.#onEscape();
387
387
  if (this.#trigger !== undefined && document.body.contains(this.#trigger)) this.#trigger.focus();
388
388
  break;
389
389
  }
390
390
  case "Escape":
391
391
  event.preventDefault();
392
392
  event.stopPropagation();
393
- if (this.#on_escape !== undefined) this.#on_escape();
393
+ if (this.#onEscape !== undefined) this.#onEscape();
394
394
  if (this.#trigger !== undefined && document.body.contains(this.#trigger)) this.#trigger.focus();
395
395
  break;
396
396
  case "Tab":
397
- if (this.#on_escape !== undefined) this.#on_escape();
397
+ if (this.#onEscape !== undefined) this.#onEscape();
398
398
  break;
399
399
  default:
400
400
  // Type-ahead: набор букв переводит фокус на пункт с таким началом
@@ -408,14 +408,14 @@ class MenuKeyboard {
408
408
  #typeahead(char) {
409
409
  const now = Date.now();
410
410
  // Буфер сбрасывается, если с прошлой буквы прошло больше 700 мс
411
- this.#type_buffer = (now - this.#type_at > 700 ? "" : this.#type_buffer) + char.toLowerCase();
412
- this.#type_at = now;
411
+ this.#typeBuffer = (now - this.#typeAt > 700 ? "" : this.#typeBuffer) + char.toLowerCase();
412
+ this.#typeAt = now;
413
413
  const items = this.#items();
414
414
  if (items.length === 0) return;
415
415
  const current = this.#activeIndex(items);
416
416
  // Ищем по кругу, начиная со следующего пункта
417
417
  const ordered = current === -1 ? items : [...items.slice(current + 1), ...items.slice(0, current + 1)];
418
- const match = ordered.find((item) => this.#label(item).startsWith(this.#type_buffer));
418
+ const match = ordered.find((item) => this.#label(item).startsWith(this.#typeBuffer));
419
419
  if (match !== undefined) this.#focusItem(items.indexOf(match));
420
420
  }
421
421
 
@@ -433,7 +433,7 @@ class MenuKeyboard {
433
433
 
434
434
  destroy() {
435
435
  this.#container.removeEventListener("keydown", this.#keydownEvent);
436
- if (this.#trigger !== undefined && this.#trigger_bound)
436
+ if (this.#trigger !== undefined && this.#triggerBound)
437
437
  this.#trigger.removeEventListener("keydown", this.#keydownEvent);
438
438
  }
439
439
  }
@@ -446,11 +446,11 @@ class MenuKeyboard {
446
446
  class ComboboxKeyboard {
447
447
  #input = undefined;
448
448
  #dropdown = undefined;
449
- #option_selector = undefined;
450
- #on_escape = undefined;
451
- #on_open = undefined;
449
+ #optionSelector = undefined;
450
+ #onEscape = undefined;
451
+ #onOpen = undefined;
452
452
  #active = undefined;
453
- #selection_state = undefined;
453
+ #selectionState = undefined;
454
454
 
455
455
  constructor(
456
456
  input,
@@ -464,10 +464,10 @@ class ComboboxKeyboard {
464
464
  ) {
465
465
  this.#input = input;
466
466
  this.#dropdown = options.dropdown;
467
- this.#option_selector = options.optionSelector;
468
- this.#on_escape = options.onEscape;
469
- this.#on_open = options.onOpen;
470
- this.#selection_state = options.selectionState;
467
+ this.#optionSelector = options.optionSelector;
468
+ this.#onEscape = options.onEscape;
469
+ this.#onOpen = options.onOpen;
470
+ this.#selectionState = options.selectionState;
471
471
  input.setAttribute("role", "combobox");
472
472
  input.setAttribute("aria-haspopup", "listbox");
473
473
  input.setAttribute("aria-expanded", "false");
@@ -503,7 +503,7 @@ class ComboboxKeyboard {
503
503
 
504
504
  #allOptions() {
505
505
  if (this.#dropdown === undefined) return [];
506
- return [...this.#dropdown.querySelectorAll(this.#option_selector)];
506
+ return [...this.#dropdown.querySelectorAll(this.#optionSelector)];
507
507
  }
508
508
 
509
509
  #options() {
@@ -513,11 +513,11 @@ class ComboboxKeyboard {
513
513
  #applyActive() {
514
514
  // Множественный выбор: `aria-selected` показывает выбор, а не наведение (его несёт
515
515
  // `aria-activedescendant`) — иначе пометка «под курсором» выглядела бы как отметка.
516
- const has_selection = typeof this.#selection_state === "function";
516
+ const hasSelection = typeof this.#selectionState === "function";
517
517
  for (const option of this.#allOptions()) {
518
518
  const active = option === this.#active;
519
519
  option.classList.toggle(ACTIVE_CLASS, active);
520
- const selected = has_selection ? this.#selection_state(option) === true : active;
520
+ const selected = hasSelection ? this.#selectionState(option) === true : active;
521
521
  option.setAttribute("aria-selected", selected ? "true" : "false");
522
522
  }
523
523
  if (this.#active === undefined) this.#input.removeAttribute("aria-activedescendant");
@@ -541,8 +541,7 @@ class ComboboxKeyboard {
541
541
  case "ArrowDown":
542
542
  case "ArrowUp": {
543
543
  event.preventDefault();
544
- if (this.#input.getAttribute("aria-expanded") !== "true" && this.#on_open !== undefined)
545
- this.#on_open();
544
+ if (this.#input.getAttribute("aria-expanded") !== "true" && this.#onOpen !== undefined) this.#onOpen();
546
545
  this.#move(event.key === "ArrowDown" ? 1 : -1);
547
546
  break;
548
547
  }
@@ -572,7 +571,7 @@ class ComboboxKeyboard {
572
571
  this.#applyActive();
573
572
  break;
574
573
  }
575
- if (this.#on_escape !== undefined) this.#on_escape();
574
+ if (this.#onEscape !== undefined) this.#onEscape();
576
575
  break;
577
576
  }
578
577
  default:
@@ -594,20 +593,20 @@ class ComboboxKeyboard {
594
593
  */
595
594
  class RovingKeyboard {
596
595
  #container = undefined;
597
- #item_selector = undefined;
598
- #on_click = undefined;
596
+ #itemSelector = undefined;
597
+ #onClick = undefined;
599
598
 
600
599
  constructor(container, options = { itemSelector: String() }) {
601
600
  this.#container = container;
602
- this.#item_selector = options.itemSelector;
601
+ this.#itemSelector = options.itemSelector;
603
602
  this.refresh();
604
- this.#on_click = (event) => {
605
- const item = event.target instanceof Element ? event.target.closest(this.#item_selector) : null;
603
+ this.#onClick = (event) => {
604
+ const item = event.target instanceof Element ? event.target.closest(this.#itemSelector) : null;
606
605
  if (item === null || !this.#container.contains(item)) return;
607
606
  this.#applyStop(item);
608
607
  };
609
608
  container.addEventListener("keydown", this.#keydownEvent);
610
- container.addEventListener("click", this.#on_click);
609
+ container.addEventListener("click", this.#onClick);
611
610
  }
612
611
 
613
612
  /** Перечитать пункты: остановка остаётся на прежнем, если он ещё доступен */
@@ -624,7 +623,7 @@ class RovingKeyboard {
624
623
 
625
624
  // Все пункты группы, включая скрытые: tabindex проставляется и до появления панели на экране
626
625
  #allItems() {
627
- return [...this.#container.querySelectorAll(this.#item_selector)];
626
+ return [...this.#container.querySelectorAll(this.#itemSelector)];
628
627
  }
629
628
 
630
629
  // Пункты, по которым ходит клавиатура: видимые и не запрещённые
@@ -686,7 +685,7 @@ class RovingKeyboard {
686
685
 
687
686
  destroy() {
688
687
  this.#container.removeEventListener("keydown", this.#keydownEvent);
689
- this.#container.removeEventListener("click", this.#on_click);
688
+ this.#container.removeEventListener("click", this.#onClick);
690
689
  }
691
690
  }
692
691
 
@@ -1,4 +1,12 @@
1
- // transition: all var(--transition_time);
1
+ /** Виды движения: имя кадра даёт JS, а длительность и кривую — класс движения в styles.css */
2
+ const MOTION = {
3
+ ENTER: "enter",
4
+ EXIT: "exit",
5
+ POP: "pop",
6
+ };
7
+
8
+ /** Кадр появления всплывающей панели у триггера — общий кадр оболочки */
9
+ const PANEL_FRAME = "chui_panel_in";
2
10
 
3
11
  class Animation {
4
12
  #element = undefined;
@@ -8,6 +16,16 @@ class Animation {
8
16
  if (element !== undefined) this.#element = element;
9
17
  }
10
18
 
19
+ /**
20
+ * Запустить кадры `name` как движение вида `kind`. Длительность и кривая — в `styles.css`
21
+ * по классу `data-motion`: вход базовый, выход короче, отклик с лёгким перелётом. Прежде
22
+ * время приходило из общего `animation-duration` на `*`, и все движения шли одним временем.
23
+ */
24
+ #play(name = String(), kind = String()) {
25
+ this.#element.dataset.motion = kind;
26
+ this.#element.style.animationName = name;
27
+ }
28
+
11
29
  /**
12
30
  * Отменить незавершённое скрытие: его `animationend` (removeEvents) снимает у слоя
13
31
  * `animation-name` и `display`. Если слой открыли заново раньше, чем анимация закрытия
@@ -18,35 +36,78 @@ class Animation {
18
36
  this.#element.style.removeProperty("animation-name");
19
37
  }
20
38
 
39
+ /**
40
+ * Кадр появления доигран — вид движения снимается, а имя кадра остаётся. По имени видно,
41
+ * что слой уже открыт (повторный показ не перезапускает кадр), а само оно без вида ничего
42
+ * не держит. Вид держит конечное состояние (`animation-fill-mode: both` в styles.css),
43
+ * и на слое оставался бы вычисленный `transform` — пусть и единичный: элемент с `transform`
44
+ * становится собственным «backdrop root», и размытие подложки перестаёт видеть содержимое
45
+ * под слоем (замер: меню контекста с блюром и без — картинка не менялась, пока вид стоял).
46
+ *
47
+ * Стрелочное поле, а не метод: у метода в слушателе нет `this`, и он падал бы на первом
48
+ * же конце кадра.
49
+ */
50
+ #entranceEnd = (event) => {
51
+ if (event.target !== this.#element) return;
52
+ this.#element.removeEventListener("animationend", this.#entranceEnd);
53
+ this.#element.removeAttribute("data-motion");
54
+ };
55
+
56
+ /** Показать слой кадром появления и снять вид по концу кадра */
57
+ #enter(name = String(), kind = String()) {
58
+ this.#play(name, kind);
59
+ this.#element.addEventListener("animationend", this.#entranceEnd);
60
+ }
61
+
21
62
  fadeIn() {
22
63
  this.#cancelHide();
23
64
  if (window.getComputedStyle(this.#element, null).display === "none") {
24
65
  if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
25
66
  }
26
- this.#element.style.transition = "transform .0s";
27
- this.#element.style.animationName = "fade-in";
67
+ this.#enter("fade-in", MOTION.ENTER);
28
68
  }
29
69
 
30
70
  fadeOut() {
31
- this.#element.style.transition = "transform .0s";
32
71
  if (this.#element.style.animationName) {
33
- this.#element.style.animationName = "fade-out";
72
+ this.#play("fade-out", MOTION.EXIT);
34
73
  this.#element.addEventListener("animationend", removeEvents);
35
74
  }
36
75
  }
37
76
 
77
+ /**
78
+ * Появление всплывающей панели у триггера (список, меню, поповер): панель приходит
79
+ * сверху вниз и чуть вырастает. Вид `pop` — кривая с лёгким перелётом, поэтому
80
+ * открытие читается откликом, а не вспышкой: прежний `fadeIn` менял только прозрачность.
81
+ *
82
+ * Повторный показ уже открытой панели кадр не запускает: поле открывают и `focus`,
83
+ * и `click` — одно действие приходило сюда дважды, а `#cancelHide` перед кадром снимает
84
+ * имя кадра, поэтому второй вызов начинал его заново и панель мигала на открытии.
85
+ */
86
+ panelIn() {
87
+ const open =
88
+ this.#element.style.animationName === PANEL_FRAME &&
89
+ window.getComputedStyle(this.#element, null).display !== "none";
90
+ if (open) return;
91
+ this.#cancelHide();
92
+ if (window.getComputedStyle(this.#element, null).display === "none") {
93
+ if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
94
+ }
95
+ this.#play(PANEL_FRAME, MOTION.POP);
96
+ this.#element.addEventListener("animationend", this.#entranceEnd);
97
+ }
98
+
38
99
  // SCALE
39
100
  scaleIn() {
40
101
  this.#cancelHide();
41
102
  if (window.getComputedStyle(this.#element, null).display === "none") {
42
103
  if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
43
104
  }
44
- this.#element.style.animationName = "scale-in";
105
+ this.#enter("scale-in", MOTION.POP);
45
106
  }
46
107
 
47
108
  scaleOut() {
48
109
  if (this.#element.style.animationName) {
49
- this.#element.style.animationName = "scale-out";
110
+ this.#play("scale-out", MOTION.EXIT);
50
111
  this.#element.addEventListener("animationend", removeEvents);
51
112
  }
52
113
  }
@@ -57,12 +118,12 @@ class Animation {
57
118
  if (window.getComputedStyle(this.#element, null).display === "none") {
58
119
  if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
59
120
  }
60
- this.#element.style.animationName = "slide-right-in";
121
+ this.#enter("slide-right-in", MOTION.ENTER);
61
122
  }
62
123
 
63
124
  slideRightOut() {
64
125
  if (this.#element.style.animationName) {
65
- this.#element.style.animationName = "slide-right-out";
126
+ this.#play("slide-right-out", MOTION.EXIT);
66
127
  this.#element.addEventListener("animationend", removeEvents);
67
128
  }
68
129
  }
@@ -73,12 +134,12 @@ class Animation {
73
134
  if (window.getComputedStyle(this.#element, null).display === "none") {
74
135
  if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
75
136
  }
76
- this.#element.style.animationName = "slide-bottom-in";
137
+ this.#enter("slide-bottom-in", MOTION.ENTER);
77
138
  }
78
139
 
79
140
  slideBottomOut() {
80
141
  if (this.#element.style.animationName) {
81
- this.#element.style.animationName = "slide-bottom-out";
142
+ this.#play("slide-bottom-out", MOTION.EXIT);
82
143
  this.#element.addEventListener("animationend", removeEvents);
83
144
  }
84
145
  }
@@ -86,28 +147,28 @@ class Animation {
86
147
  // REMOVE
87
148
  slideBottomOutAndRemove() {
88
149
  if (this.#element.style.animationName) {
89
- this.#element.style.animationName = "slide-bottom-out";
150
+ this.#play("slide-bottom-out", MOTION.EXIT);
90
151
  this.#element.addEventListener("animationend", removeEvents);
91
152
  }
92
153
  }
93
154
 
94
155
  slideRightOutAndRemove() {
95
156
  if (this.#element.style.animationName) {
96
- this.#element.style.animationName = "slide-right-out";
157
+ this.#play("slide-right-out", MOTION.EXIT);
97
158
  this.#element.addEventListener("animationend", removeEvents);
98
159
  }
99
160
  }
100
161
 
101
162
  scaleOutAndRemove() {
102
163
  if (this.#element.style.animationName) {
103
- this.#element.style.animationName = "scale-out";
164
+ this.#play("scale-out", MOTION.EXIT);
104
165
  this.#element.addEventListener("animationend", removeEvents2);
105
166
  }
106
167
  }
107
168
 
108
169
  fadeOutAndRemove() {
109
170
  if (this.#element.style.animationName) {
110
- this.#element.style.animationName = "fade-out";
171
+ this.#play("fade-out", MOTION.EXIT);
111
172
  this.#element.addEventListener("animationend", removeEvents2);
112
173
  }
113
174
  }
@@ -118,6 +179,7 @@ class Animation {
118
179
  }
119
180
 
120
181
  removeStyles() {
182
+ this.#element.removeAttribute("data-motion");
121
183
  this.#element.removeAttribute("style");
122
184
  }
123
185
  }