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,6 +1,6 @@
1
- const {setStyles, optionOr, scrollParent} = require('../../modules/chui_functions');
2
- const {Icon, Icons} = require('../chui_icons/icons');
3
- const {I18n} = require('../../modules/chui_i18n/chui_i18n');
1
+ const { setStyles, optionOr, scrollParent } = require("../../modules/chui_functions");
2
+ const { Icon, Icons } = require("../chui_icons/icons");
3
+ const { I18n } = require("../../modules/chui_i18n/chui_i18n");
4
4
 
5
5
  /**
6
6
  * Кнопка «наверх»: появляется, когда контейнер прокручен дальше порога, и возвращает его к началу.
@@ -32,7 +32,7 @@ const {I18n} = require('../../modules/chui_i18n/chui_i18n');
32
32
  class Backtop {
33
33
  static POSITION = {
34
34
  FIXED: "fixed",
35
- STICKY: "sticky"
35
+ STICKY: "sticky",
36
36
  };
37
37
  static #counter = 0;
38
38
  static #DEFAULT_THRESHOLD = 200;
@@ -50,11 +50,19 @@ class Backtop {
50
50
  #visible = false;
51
51
  #destroyed = false;
52
52
 
53
- constructor(options = {
54
- id: String(), target: undefined, threshold: Number(),
55
- position: String(), bottom: String(), inlineEnd: String(),
56
- title: String(), icon: undefined, smooth: Boolean()
57
- }) {
53
+ constructor(
54
+ options = {
55
+ id: String(),
56
+ target: undefined,
57
+ threshold: Number(),
58
+ position: String(),
59
+ bottom: String(),
60
+ inlineEnd: String(),
61
+ title: String(),
62
+ icon: undefined,
63
+ smooth: Boolean(),
64
+ }
65
+ ) {
58
66
  setStyles(__dirname + "/styles.css", "chUiJS_Backtop");
59
67
  Backtop.#counter += 1;
60
68
  this.#id = optionOr(options.id, `chui_backtop_${Backtop.#counter}`);
@@ -74,7 +82,7 @@ class Backtop {
74
82
  this.setTitle(options.title);
75
83
  this.setIcon(options.icon);
76
84
  this.setSmooth(options.smooth !== false);
77
- window.addEventListener("scroll", this.#onScroll, {capture: true, passive: true});
85
+ window.addEventListener("scroll", this.#onScroll, { capture: true, passive: true });
78
86
  }
79
87
 
80
88
  // GET
@@ -150,9 +158,14 @@ class Backtop {
150
158
 
151
159
  /** Свой значок (имя из `Icons`, `Icon`, узел или компонент); `undefined` — стрелка вверх */
152
160
  setIcon(icon = undefined) {
153
- const node = icon === undefined || icon === null || icon === ""
154
- ? new Icon(Icons.NAVIGATION.ARROW_UPWARD, "18px").set()
155
- : (typeof icon === "string" ? new Icon(icon, "18px").set() : (typeof icon.set === "function" ? icon.set() : icon));
161
+ const node =
162
+ icon === undefined || icon === null || icon === ""
163
+ ? new Icon(Icons.NAVIGATION.ARROW_UPWARD, "18px").set()
164
+ : typeof icon === "string"
165
+ ? new Icon(icon, "18px").set()
166
+ : typeof icon.set === "function"
167
+ ? icon.set()
168
+ : icon;
156
169
  this.#icon.innerHTML = "";
157
170
  this.#icon.appendChild(node);
158
171
  return this;
@@ -175,8 +188,8 @@ class Backtop {
175
188
  const container = this.#resolveTarget();
176
189
  if (container === undefined) return this;
177
190
  const behavior = this.#smooth && Backtop.#reducedMotion() === false ? "smooth" : "auto";
178
- if (container === document.scrollingElement) window.scrollTo({top: 0, behavior: behavior});
179
- else container.scrollTo({top: 0, behavior: behavior});
191
+ if (container === document.scrollingElement) window.scrollTo({ top: 0, behavior: behavior });
192
+ else container.scrollTo({ top: 0, behavior: behavior });
180
193
  return this;
181
194
  }
182
195
 
@@ -184,7 +197,8 @@ class Backtop {
184
197
  destroy() {
185
198
  if (this.#destroyed) return this;
186
199
  this.#destroyed = true;
187
- window.removeEventListener("scroll", this.#onScroll, {capture: true});
200
+ window.removeEventListener("scroll", this.#onScroll, { capture: true });
201
+ this.#root.removeEventListener("transitionend", this.#onHideEnd);
188
202
  this.#root.remove();
189
203
  return this;
190
204
  }
@@ -196,8 +210,11 @@ class Backtop {
196
210
 
197
211
  // === ВНУТРЕННЕЕ ===
198
212
  static #reducedMotion() {
199
- return typeof window !== "undefined" && typeof window.matchMedia === "function" &&
200
- window.matchMedia("(prefers-reduced-motion: reduce)").matches;
213
+ return (
214
+ typeof window !== "undefined" &&
215
+ typeof window.matchMedia === "function" &&
216
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
217
+ );
201
218
  }
202
219
 
203
220
  /** Контейнер прокрутки: свой `target`, иначе ближайший прокручиваемый предок кнопки */
@@ -222,9 +239,31 @@ class Backtop {
222
239
  const visible = top > this.#threshold;
223
240
  if (visible === this.#visible) return;
224
241
  this.#visible = visible;
225
- this.#root.hidden = visible === false;
226
- this.#root.classList.toggle("backtop_visible", visible);
242
+ this.#show(visible);
243
+ }
244
+
245
+ /**
246
+ * Показ и уход идут движением (переход в styles.css). Узел встаёт в раскладку до класса —
247
+ * иначе перехода не было бы вовсе, — а `hidden` возвращается по концу ухода: в закреплённом
248
+ * положении он держит место в раскладке панели, и снять его до ухода значило бы оборвать его.
249
+ */
250
+ #show(visible) {
251
+ if (visible) {
252
+ this.#root.hidden = false;
253
+ requestAnimationFrame(() => {
254
+ if (this.#visible === true) this.#root.classList.add("backtop_visible");
255
+ });
256
+ return;
257
+ }
258
+ this.#root.classList.remove("backtop_visible");
259
+ this.#root.addEventListener("transitionend", this.#onHideEnd);
227
260
  }
261
+
262
+ #onHideEnd = (event) => {
263
+ if (event.target !== this.#root || event.propertyName !== "opacity") return;
264
+ this.#root.removeEventListener("transitionend", this.#onHideEnd);
265
+ if (this.#visible === false) this.#root.hidden = true;
266
+ };
228
267
  }
229
268
 
230
- exports.Backtop = Backtop
269
+ exports.Backtop = Backtop;
@@ -6,6 +6,27 @@ chui_backtop {
6
6
  --backtop_inline_end: 24px;
7
7
  width: max-content;
8
8
  height: max-content;
9
+ opacity: 1;
10
+ transform: none;
11
+ /* Появление и уход — движение, а не снятие узла: кнопка поднимается на месте и гаснет.
12
+ `hidden` включает JS по концу ухода: в закреплённом положении он держит место
13
+ в раскладке панели, поэтому снимать его заранее нельзя */
14
+ transition:
15
+ opacity var(--motion_base) var(--motion_ease),
16
+ transform var(--motion_base) var(--motion_ease),
17
+ visibility var(--motion_none) linear;
18
+ }
19
+
20
+ /* Скрытая кнопка не ловит курсор и не попадает в обход Tab: `visibility` переключается
21
+ сразу при показе и только в конце ухода */
22
+ chui_backtop:not(.backtop_visible) {
23
+ opacity: 0;
24
+ transform: translateY(8px);
25
+ visibility: hidden;
26
+ transition:
27
+ opacity var(--motion_fast) var(--motion_ease),
28
+ transform var(--motion_fast) var(--motion_ease),
29
+ visibility var(--motion_none) linear var(--motion_fast);
9
30
  }
10
31
 
11
32
  chui_backtop[hidden] {
@@ -1,61 +1,63 @@
1
- const {setStyles, markdownToHtml, htmlToMarkdown, optionOr} = require("../../modules/chui_functions");
1
+ const { setStyles, markdownToHtml, htmlToMarkdown, optionOr } = require("../../modules/chui_functions");
2
2
 
3
3
  class Badge {
4
- #chui_badge = document.createElement(`chui_badge`);
5
- constructor(options = {
6
- id: String(),
7
- text: String(),
8
- markdownText: String(),
9
- style: undefined
10
- }) {
11
- setStyles(__dirname + "/styles.css", 'chUiJS_Badge');
12
- if (options.id !== undefined) this.#chui_badge.id = options.id;
4
+ #chuiBadge = document.createElement(`chui_badge`);
5
+ constructor(
6
+ options = {
7
+ id: String(),
8
+ text: String(),
9
+ markdownText: String(),
10
+ style: undefined,
11
+ }
12
+ ) {
13
+ setStyles(__dirname + "/styles.css", "chUiJS_Badge");
14
+ if (options.id !== undefined) this.#chuiBadge.id = options.id;
13
15
  const style = optionOr(options.style, undefined);
14
- if (style !== undefined) this.#chui_badge.classList.add(style);
16
+ if (style !== undefined) this.#chuiBadge.classList.add(style);
15
17
  // Стили текста баджей: пустая заготовка опции равносильна отсутствующей
16
18
  const text = optionOr(options.text, undefined);
17
- const markdown_text = optionOr(options.markdownText, undefined);
18
- if (text !== undefined && markdown_text !== undefined) {
19
+ const markdownText = optionOr(options.markdownText, undefined);
20
+ if (text !== undefined && markdownText !== undefined) {
19
21
  throw new Error("Должна быть установлена одна опция text или markdownText");
20
22
  } else {
21
- if (text !== undefined) this.#chui_badge.innerText = text;
22
- if (markdown_text !== undefined) this.#chui_badge.innerHTML = markdownToHtml(markdown_text);
23
+ if (text !== undefined) this.#chuiBadge.innerText = text;
24
+ if (markdownText !== undefined) this.#chuiBadge.innerHTML = markdownToHtml(markdownText);
23
25
  }
24
26
  }
25
27
  // GET
26
28
  getId() {
27
- return this.#chui_badge.id;
29
+ return this.#chuiBadge.id;
28
30
  }
29
31
  getText() {
30
- return this.#chui_badge.innerText;
32
+ return this.#chuiBadge.innerText;
31
33
  }
32
34
  getMarkdownText() {
33
- return htmlToMarkdown(this.#chui_badge.innerHTML);
35
+ return htmlToMarkdown(this.#chuiBadge.innerHTML);
34
36
  }
35
37
  // SET
36
38
  setText(text = String()) {
37
- this.#chui_badge.innerText = text;
39
+ this.#chuiBadge.innerText = text;
38
40
  }
39
41
  setMarkdownText(text = String()) {
40
- this.#chui_badge.innerHTML = markdownToHtml(text);
42
+ this.#chuiBadge.innerHTML = markdownToHtml(text);
41
43
  }
42
44
  setId(id = String()) {
43
- this.#chui_badge.id = id;
45
+ this.#chuiBadge.id = id;
44
46
  }
45
47
  setStyle(style = String()) {
46
48
  // Пустая строка — «не задана»: `classList.add("")` бросает исключение
47
49
  const known = optionOr(style, undefined);
48
- if (known !== undefined) this.#chui_badge.classList.add(known);
50
+ if (known !== undefined) this.#chuiBadge.classList.add(known);
49
51
  }
50
52
  // RENDER
51
53
  set() {
52
- return this.#chui_badge;
54
+ return this.#chuiBadge;
53
55
  }
54
56
  static STYLE = {
55
- ERROR: 'badge_error',
56
- SUCCESS: 'badge_success',
57
- WARNING: 'badge_warning'
58
- }
57
+ ERROR: "badge_error",
58
+ SUCCESS: "badge_success",
59
+ WARNING: "badge_warning",
60
+ };
59
61
  }
60
62
 
61
- exports.Badge = Badge
63
+ exports.Badge = Badge;
@@ -12,9 +12,9 @@ chui_badge {
12
12
  height: max-content;
13
13
  width: max-content;
14
14
  padding: 2px 8px;
15
- margin: var(--margin);
15
+ margin: var(--block_margin);
16
16
  border-radius: var(--radius_pill);
17
- background: var(--badge_cancel_back);
17
+ background: var(--badge_cancel_background);
18
18
  color: var(--badge_cancel_text);
19
19
  font-size: var(--font_labels_size);
20
20
  font-weight: 500;
@@ -41,20 +41,20 @@ chui_badge strong {
41
41
 
42
42
  /* Код в метке — тем же моношрифтом, что в остальных местах с разметкой */
43
43
  chui_badge code {
44
- font-family: var(--code_font, monospace);
44
+ font-family: var(--font_mono);
45
45
  }
46
46
 
47
47
  .badge_error {
48
- background: var(--badge_error_back);
48
+ background: var(--badge_error_background);
49
49
  color: var(--badge_error_text);
50
50
  }
51
51
 
52
52
  .badge_success {
53
- background: var(--badge_success_back);
53
+ background: var(--badge_success_background);
54
54
  color: var(--badge_success_text);
55
55
  }
56
56
 
57
57
  .badge_warning {
58
- background: var(--badge_warning_back);
58
+ background: var(--badge_warning_background);
59
59
  color: var(--badge_warning_text);
60
60
  }
@@ -0,0 +1,222 @@
1
+ const { setStyles, optionOr, markdownToHtml } = require("../../modules/chui_functions");
2
+ const { Icon } = require("../chui_icons/icons");
3
+ const { I18n } = require("../../modules/chui_i18n/chui_i18n");
4
+ const { makeKeyboardButton } = require("../../modules/chui_a11y/chui_a11y");
5
+ const { tokenTime } = require("../../modules/chui_animations/motion");
6
+
7
+ /**
8
+ * Баннер — карточка-объявление: картинка или значок, заголовок с подзаголовком, действия
9
+ * и закрытие.
10
+ *
11
+ * Баннер стоит в потоке страницы, а не всплывает слоем: для модального сообщения есть
12
+ * `Popup`, для сообщения о состоянии — `Alert`, для всплывающего — `Toast`. От `Alert` он
13
+ * отличается задачей: там состояние (ошибка, предупреждение), здесь предложение — «попробуйте
14
+ * новое», «настройте», «посмотрите» — поэтому у баннера крупный материал, картинка и действия
15
+ * кнопками.
16
+ *
17
+ * Два вида (`mode`) и два размера (`size`):
18
+ * - `tint` — значок в цветной плашке, `image` — фотография; фото занимает кромку карточки
19
+ * целиком и не затемняется: текст лежит рядом, а не поверх снимка, поэтому подпись читается
20
+ * при любом кадре (всплывающие подписи поверх фото требуют затемнения на глаз — в тёмной
21
+ * и светлой схеме оно даёт разный результат);
22
+ * - `s` — строка: плашка слева, заголовок и подзаголовок рядом, действия справа;
23
+ * - `m` — карточка: картинка сверху во всю ширину, под ней заголовок, подзаголовок, текст
24
+ * и действия.
25
+ *
26
+ * Опции: `id`, `title`, `subtitle`, `text`, `markdownText`, `icon`, `image`, `mode`, `size`,
27
+ * `actions`, `closable`, `onClose`, `onClick`.
28
+ *
29
+ * Методы: `setTitle(text)`, `setSubtitle(text)`, `setText(text)`, `setMarkdownText(md)`,
30
+ * `setIcon(glyph)`, `setImage(src)`, `setMode(mode)`, `setSize(size)`, `addActions(...actions)`,
31
+ * `setClosable(bool)`, `close()`, `isClosed()`, `set()`, `destroy()`.
32
+ */
33
+ class Banner {
34
+ static MODE = { TINT: "tint", IMAGE: "image" };
35
+ static SIZE = { S: "s", M: "m" };
36
+
37
+ #root = document.createElement("chui_banner");
38
+ #media = document.createElement("banner_media");
39
+ #content = document.createElement("banner_content");
40
+ #title = undefined;
41
+ #subtitle = undefined;
42
+ #text = undefined;
43
+ #actions = document.createElement("banner_actions");
44
+ #close = undefined;
45
+ #image = undefined;
46
+ #onClose = undefined;
47
+ #onClick = undefined;
48
+ #closed = false;
49
+ #removed = false;
50
+
51
+ constructor(options = {}) {
52
+ setStyles(__dirname + "/styles.css", "chUiJS_Banner");
53
+ if (options.id !== undefined) this.#root.id = options.id;
54
+ this.setSize(optionOr(options.size, Banner.SIZE.M));
55
+ this.setMode(optionOr(options.mode, Banner.MODE.TINT));
56
+ this.#onClose = typeof options.onClose === "function" ? options.onClose : undefined;
57
+ this.#onClick = typeof options.onClick === "function" ? options.onClick : undefined;
58
+ this.#root.append(this.#media, this.#content, this.#actions);
59
+ if (options.title !== undefined) this.setTitle(options.title);
60
+ if (options.subtitle !== undefined) this.setSubtitle(options.subtitle);
61
+ if (options.markdownText !== undefined) this.setMarkdownText(options.markdownText);
62
+ else if (options.text !== undefined) this.setText(options.text);
63
+ if (options.icon !== undefined) this.setIcon(options.icon);
64
+ if (options.image !== undefined) this.setImage(options.image);
65
+ if (Array.isArray(options.actions)) this.addActions(...options.actions);
66
+ if (options.closable === true) this.setClosable(true);
67
+ if (this.#onClick !== undefined) this.#makeClickable();
68
+ this.#syncName();
69
+ }
70
+
71
+ // GET
72
+ isClosed() {
73
+ return this.#closed;
74
+ }
75
+
76
+ // SET
77
+ setTitle(text = String()) {
78
+ this.#title = this.#writeText(this.#title, "banner_title", text, "title");
79
+ this.#syncName();
80
+ return this;
81
+ }
82
+ setSubtitle(text = String()) {
83
+ this.#subtitle = this.#writeText(this.#subtitle, "banner_subtitle", text, "subtitle");
84
+ return this;
85
+ }
86
+ setText(text = String()) {
87
+ this.#text = this.#writeText(this.#text, "banner_text", text, "text");
88
+ return this;
89
+ }
90
+ /** Разметка в теле баннера: тот же приём, что у `Alert` — текст приходит готовым HTML */
91
+ setMarkdownText(markdown = String()) {
92
+ if (this.#text === undefined) this.#text = this.#insertText("banner_text", "text");
93
+ this.#text.innerHTML = markdownToHtml(String(markdown));
94
+ return this;
95
+ }
96
+ /** Значок в плашке: состояние `tint`. Прежняя картинка при этом уходит */
97
+ setIcon(glyph = String()) {
98
+ const icon = new Icon(String(glyph), "22px").set();
99
+ this.#media.replaceChildren(icon);
100
+ this.#root.dataset.mode = Banner.MODE.TINT;
101
+ this.#image = undefined;
102
+ return this;
103
+ }
104
+ /** Фотография на кромке карточки: состояние `image` */
105
+ setImage(src = String()) {
106
+ if (this.#image === undefined) {
107
+ this.#image = document.createElement("img");
108
+ // Картинка здесь украшение: смысл несёт заголовок, поэтому подпись пуста
109
+ this.#image.alt = "";
110
+ this.#image.loading = "lazy";
111
+ }
112
+ this.#image.src = String(src);
113
+ this.#media.replaceChildren(this.#image);
114
+ this.#root.dataset.mode = Banner.MODE.IMAGE;
115
+ return this;
116
+ }
117
+ setMode(mode = String()) {
118
+ this.#root.dataset.mode = mode === Banner.MODE.IMAGE ? Banner.MODE.IMAGE : Banner.MODE.TINT;
119
+ return this;
120
+ }
121
+ setSize(size = String()) {
122
+ this.#root.dataset.size = size === Banner.SIZE.S ? Banner.SIZE.S : Banner.SIZE.M;
123
+ return this;
124
+ }
125
+ /** Действия баннера: компоненты фреймворка (у них есть `set()`) или готовые узлы */
126
+ addActions(...actions) {
127
+ for (const action of actions) {
128
+ if (action === undefined || action === null) continue;
129
+ this.#actions.appendChild(typeof action.set === "function" ? action.set() : action);
130
+ }
131
+ return this;
132
+ }
133
+ /**
134
+ * Кнопка закрытия — настоящий `<button>`: у неё есть мышь, клавиатура и имя
135
+ * для скринридера (подписи в баннере нет, имя даёт словарь).
136
+ */
137
+ setClosable(boolean = Boolean()) {
138
+ if (boolean !== true) {
139
+ if (this.#close !== undefined) this.#close.remove();
140
+ this.#close = undefined;
141
+ return this;
142
+ }
143
+ if (this.#close === undefined) {
144
+ this.#close = document.createElement("button");
145
+ this.#close.type = "button";
146
+ this.#close.className = "banner_close";
147
+ this.#close.innerHTML = new Icon("close", "16px").getHTML();
148
+ this.#close.setAttribute("aria-label", I18n.t("a11y.closeMessage"));
149
+ this.#close.setAttribute("title", I18n.t("common.close"));
150
+ this.#close.addEventListener("click", () => this.close());
151
+ this.#root.appendChild(this.#close);
152
+ }
153
+ return this;
154
+ }
155
+
156
+ /**
157
+ * Закрытие: баннер гаснет и сходит общим кадром ухода, а узел убирается по его концу.
158
+ * Слушатель одноразовый (проверка цели): иначе он поймал бы любой конец кадра на узле.
159
+ * Срок из токена страхует случай, когда кадр не проиграется вовсе — у скрытого узла
160
+ * `animationend` не приходит.
161
+ */
162
+ close() {
163
+ if (this.#closed === true) return this;
164
+ this.#closed = true;
165
+ if (this.#onClose !== undefined) this.#onClose(this);
166
+ const finish = () => {
167
+ if (this.#removed === true) return;
168
+ this.#removed = true;
169
+ this.#root.removeEventListener("animationend", finish);
170
+ this.#root.remove();
171
+ };
172
+ this.#root.addEventListener("animationend", finish);
173
+ this.#root.classList.add("banner_leaving");
174
+ setTimeout(finish, tokenTime("--motion_fast"));
175
+ return this;
176
+ }
177
+
178
+ set() {
179
+ return this.#root;
180
+ }
181
+ /** Узлы баннера уходят из документа вместе со своими слушателями; работа снимается явно */
182
+ destroy() {
183
+ this.#onClick = undefined;
184
+ this.#onClose = undefined;
185
+ this.#removed = true;
186
+ this.#root.remove();
187
+ }
188
+
189
+ // === ВНУТРЕННЕЕ ===
190
+ #writeText(node = undefined, className = String(), text = String(), key = String()) {
191
+ const target = node === undefined ? this.#insertText(className, key) : node;
192
+ target.innerText = String(text);
193
+ return target;
194
+ }
195
+ #insertText(className = String(), key = String()) {
196
+ const node = document.createElement(`banner_${key}`);
197
+ node.className = className;
198
+ this.#content.appendChild(node);
199
+ return node;
200
+ }
201
+ /** Клик по всей карточке: роль кнопки, обход клавиатурой и активация Enter/Space */
202
+ #makeClickable() {
203
+ makeKeyboardButton(this.#root);
204
+ this.#root.addEventListener("click", () => {
205
+ if (this.#onClick !== undefined) this.#onClick(this);
206
+ });
207
+ }
208
+ /**
209
+ * Имя баннера для скринридера: заголовок, а без него — общая подпись из словаря.
210
+ * У кликабельного баннера имя обязательно: без него роль кнопки читается как «кнопка».
211
+ */
212
+ #syncName() {
213
+ const title = this.#title === undefined ? "" : this.#title.innerText.trim();
214
+ const name = title === "" ? I18n.t("feedback.banner") : title;
215
+ if (this.#onClick === undefined && this.#root.getAttribute("role") === null) {
216
+ this.#root.setAttribute("role", "region");
217
+ }
218
+ this.#root.setAttribute("aria-label", name);
219
+ }
220
+ }
221
+
222
+ exports.Banner = Banner;