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
@@ -1,17 +1,94 @@
1
+ /* Шапка страницы: значок, заголовок, описание и строка статистики.
2
+ Украшение — из акцентов палитры, смешанных с нейтральной заливкой и рамкой
3
+ (`color-mix` над токенами), поэтому мимо таблицы цвет не заведён и обе схемы работают
4
+ сами. Движение — по системе движения: шапка приходит кадром появления из
5
+ `global_style.css`, знак и плитки встают со ступенькой задержки, а по подложке один
6
+ раз проходит блик. Непрерывных кадров нет намеренно: шапка — не индикатор ожидания,
7
+ а неподвижная поверхность, по которой не должно бегать движение. */
1
8
  chui_hero {
9
+ position: relative;
2
10
  display: flex;
3
11
  flex-direction: column;
4
12
  gap: 16px;
5
13
  box-sizing: border-box;
6
14
  width: -webkit-fill-available;
7
15
  padding: 20px 22px;
16
+ /* Блик проходит по подложке и уходит за край — его и обрезает коробка */
17
+ overflow: hidden;
8
18
  border: 1px solid var(--border_color);
9
19
  border-radius: var(--radius_md);
10
- background: var(--element_background);
20
+ /* Свечение акцентов лежит на нейтральной заливке: она читается и на карточке,
21
+ и на подложке (вложенная поверхность), а свечение даёт цвет там, где его не хватает */
22
+ background:
23
+ radial-gradient(
24
+ 120% 100% at 100% 0%,
25
+ color-mix(in srgb, var(--blue_prime_background) 18%, transparent),
26
+ transparent 62%
27
+ ),
28
+ radial-gradient(
29
+ 100% 90% at 0% 100%,
30
+ color-mix(in srgb, var(--teal_prime_background) 16%, transparent),
31
+ transparent 66%
32
+ ),
33
+ var(--element_background);
11
34
  box-shadow: var(--box_shadow_main);
12
35
  font-size: var(--font_default_size);
13
36
  color: var(--text_color);
14
37
  text-align: start;
38
+ animation: chui_appear_up var(--motion_base) var(--motion_ease) both;
39
+ }
40
+
41
+ /* Акцентная линия сверху: три акцента палитры одним градиентом — синий, циан, мята.
42
+ Без неё шапка читалась бы обычной карточкой. Линия не появляется готовой, а прочерчивается
43
+ слева направо: это первый кадр шапки, и он задаёт направление всему появлению */
44
+ chui_hero::before {
45
+ content: "";
46
+ position: absolute;
47
+ inset-inline: 0;
48
+ top: 0;
49
+ height: 3px;
50
+ background: linear-gradient(
51
+ 90deg,
52
+ var(--blue_prime_background),
53
+ var(--cyan_prime_background) 55%,
54
+ var(--mint_prime_background)
55
+ );
56
+ transform-origin: left center;
57
+ animation: hero_line_in var(--motion_slow) var(--motion_ease) both;
58
+ }
59
+
60
+ @keyframes hero_line_in {
61
+ from {
62
+ transform: scaleX(0);
63
+ }
64
+ to {
65
+ transform: scaleX(1);
66
+ }
67
+ }
68
+
69
+ /* Блик: один раз проходит по подложке и уходит за край. Цвет берётся из текста темы —
70
+ белая полоса на светлой схеме не видна вовсе */
71
+ chui_hero::after {
72
+ content: "";
73
+ position: absolute;
74
+ top: -20%;
75
+ bottom: -20%;
76
+ width: 42%;
77
+ background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text_color) 7%, transparent), transparent);
78
+ transform: translateX(-140%) skewX(-14deg);
79
+ pointer-events: none;
80
+ /* `both` держит конечное положение — блик остаётся за краем, а не возвращается назад.
81
+ Идёт последним: подложка к этому времени уже собралась, и блик не спорит с появлением */
82
+ animation: hero_shine var(--motion_slow) var(--motion_ease) calc(var(--motion_fast) * 2) both;
83
+ }
84
+
85
+ @keyframes hero_shine {
86
+ from {
87
+ transform: translateX(-140%) skewX(-14deg);
88
+ }
89
+ to {
90
+ transform: translateX(240%) skewX(-14deg);
91
+ }
15
92
  }
16
93
 
17
94
  chui_hero hero_head {
@@ -28,8 +105,46 @@ chui_hero hero_badge {
28
105
  width: 54px;
29
106
  height: 54px;
30
107
  border-radius: var(--radius_md);
31
- background: var(--blue_prime_background);
32
- box-shadow: var(--box_shadow_main);
108
+ /* Знак — единственная заливка во весь цвет: он и держит акцент, когда текст
109
+ читается цветом темы */
110
+ background: linear-gradient(135deg, var(--blue_prime_background), var(--indigo_prime_background));
111
+ box-shadow: 0 6px 18px color-mix(in srgb, var(--blue_prime_background) 35%, transparent);
112
+ animation: hero_badge_in var(--motion_base) var(--motion_spring) both;
113
+ }
114
+
115
+ /* Знак встаёт с лёгким перелётом и поворотом: он приходит первым в шапке */
116
+ @keyframes hero_badge_in {
117
+ from {
118
+ opacity: 0;
119
+ transform: scale(0.82) rotate(-8deg);
120
+ }
121
+ to {
122
+ opacity: 1;
123
+ transform: scale(1) rotate(0deg);
124
+ }
125
+ }
126
+
127
+ /* Отклик знака: от него один раз расходится кольцо — знак «встал на место».
128
+ Не по кругу: шапка — не индикатор ожидания, и непрерывного движения в ней быть не должно */
129
+ chui_hero hero_badge::after {
130
+ content: "";
131
+ position: absolute;
132
+ inset: 0;
133
+ border: 2px solid var(--blue_prime_background);
134
+ border-radius: var(--radius_md);
135
+ pointer-events: none;
136
+ animation: hero_badge_ring var(--motion_slow) var(--motion_ease) var(--motion_fast) both;
137
+ }
138
+
139
+ @keyframes hero_badge_ring {
140
+ from {
141
+ opacity: 0.45;
142
+ transform: scale(0.86);
143
+ }
144
+ to {
145
+ opacity: 0;
146
+ transform: scale(1.5);
147
+ }
33
148
  }
34
149
 
35
150
  chui_hero hero_badge chui_icon {
@@ -51,7 +166,12 @@ chui_hero hero_title {
51
166
  font-size: var(--font_title_size);
52
167
  font-weight: 600;
53
168
  letter-spacing: -0.02em;
169
+ /* Заголовок переносится по смыслу, а не по длине строки */
170
+ text-wrap: balance;
54
171
  color: var(--text_color);
172
+ /* Ступенька: линия — знак — заголовок и дальше. Шаг — доля токена движения,
173
+ а не своё число */
174
+ animation: chui_appear_up var(--motion_base) var(--motion_ease) calc(var(--motion_fast) / 2) both;
55
175
  }
56
176
 
57
177
  chui_hero hero_description {
@@ -63,6 +183,7 @@ chui_hero hero_description {
63
183
  /* Описание героя — обычный текст (было 13px, размера вне шкалы) */
64
184
  font-size: var(--font_default_size);
65
185
  color: var(--text_color_disabled);
186
+ animation: chui_appear_up var(--motion_base) var(--motion_ease) var(--motion_fast) both;
66
187
  }
67
188
 
68
189
  chui_hero hero_stats {
@@ -71,21 +192,69 @@ chui_hero hero_stats {
71
192
  gap: 10px;
72
193
  }
73
194
 
195
+ /* Плитка статистики. Свой акцент задаёт `data-accent` (его ставит компонент, ведя список
196
+ по кругу): цвет идёт заливкой, рамкой и полосой, а число читается цветом темы —
197
+ акцентный текст на тинте дал бы контраст ниже 3 даже для крупной строки */
74
198
  chui_hero hero_stat {
199
+ --hero_accent: var(--blue_prime_background);
200
+ position: relative;
75
201
  display: flex;
76
202
  flex-direction: column;
77
203
  gap: 1px;
78
204
  min-width: 104px;
79
- padding: 8px 14px;
80
- border: 1px solid var(--border_color);
205
+ padding: 8px 14px 8px 18px;
206
+ border: 1px solid color-mix(in srgb, var(--hero_accent) 34%, var(--border_color));
81
207
  border-radius: var(--radius_md);
82
- background: var(--element_background_2);
208
+ background: color-mix(in srgb, var(--hero_accent) 12%, var(--element_background_2));
209
+ /* Плитки встают по очереди: номер ставит компонент переменной `--hero_stat_index`,
210
+ а ступенька считается от той же задержки, что у описания, поэтому порядок
211
+ появления один: линия, знак, заголовок, описание, плитки, блик */
212
+ animation: chui_appear_up var(--motion_base) var(--motion_ease) both;
213
+ animation-delay: calc(var(--motion_base) + var(--motion_fast) * var(--hero_stat_index, 0));
214
+ }
215
+
216
+ /* Акцентная полоса слева: даже когда плиток много, их ряд читается цветным.
217
+ Она прочерчивается сверху вниз вместе с приходом своей плитки */
218
+ chui_hero hero_stat::before {
219
+ content: "";
220
+ position: absolute;
221
+ inset-block: 8px;
222
+ inset-inline-start: 8px;
223
+ width: 3px;
224
+ border-radius: var(--radius_pill);
225
+ background: var(--hero_accent);
226
+ transform-origin: top center;
227
+ animation: hero_stat_bar_in var(--motion_base) var(--motion_ease) both;
228
+ animation-delay: calc(var(--motion_base) + var(--motion_fast) * var(--hero_stat_index, 0));
229
+ }
230
+
231
+ @keyframes hero_stat_bar_in {
232
+ from {
233
+ transform: scaleY(0);
234
+ }
235
+ to {
236
+ transform: scaleY(1);
237
+ }
238
+ }
239
+
240
+ chui_hero hero_stat[data-accent="purple"] {
241
+ --hero_accent: var(--purple_prime_background);
242
+ }
243
+
244
+ chui_hero hero_stat[data-accent="green"] {
245
+ --hero_accent: var(--green_prime_background);
246
+ }
247
+
248
+ chui_hero hero_stat[data-accent="orange"] {
249
+ --hero_accent: var(--orange_prime_background);
83
250
  }
84
251
 
85
252
  chui_hero hero_stat_value {
86
253
  font-size: var(--font_lead_size);
87
254
  font-weight: 600;
88
- color: var(--blue_prime_background);
255
+ /* Числа не "плывут" при смене значения: ширины цифр равны */
256
+ font-variant-numeric: tabular-nums;
257
+ color: var(--text_color);
89
258
  }
90
259
 
91
260
  chui_hero hero_stat_label {
@@ -1,16 +1,16 @@
1
1
  class HtmlBlock {
2
2
  #id = require("randomstring").generate();
3
- #html_block = document.createElement("chui_html_block");
3
+ #htmlBlock = document.createElement("chui_html_block");
4
4
  constructor(width = String()) {
5
5
  require("../../modules/chui_functions").setStyles(__dirname + "/styles.css", "chUiJS_HtmlBlock");
6
- this.#html_block.setAttribute("id", this.#id);
7
- this.#html_block.style.width = width;
6
+ this.#htmlBlock.setAttribute("id", this.#id);
7
+ this.#htmlBlock.style.width = width;
8
8
  }
9
9
  setHtml(html) {
10
- this.#html_block.innerHTML = html;
10
+ this.#htmlBlock.innerHTML = html;
11
11
  }
12
12
  set() {
13
- return this.#html_block;
13
+ return this.#htmlBlock;
14
14
  }
15
15
  }
16
16
 
@@ -1,6 +1,6 @@
1
1
  chui_html_block {
2
2
  display: block;
3
3
  color: var(--text_color);
4
- margin: var(--margin);
4
+ margin: var(--block_margin);
5
5
  height: max-content;
6
6
  }
@@ -6,12 +6,12 @@ h5,
6
6
  h6 {
7
7
  height: max-content;
8
8
  /* Ширина по контейнеру: с max-content длинный заголовок не переносился,
9
- а с margin: var(--margin) уезжал вправо за край родителя */
9
+ а с margin: var(--block_margin) уезжал вправо за край родителя */
10
10
  width: -webkit-fill-available;
11
11
  /* Слово шире строки разрывается, а не вылезает за заголовок */
12
12
  overflow-wrap: break-word;
13
13
  color: var(--text_color);
14
- margin: var(--margin);
14
+ margin: var(--block_margin);
15
15
  font-weight: 500;
16
16
  }
17
17
 
@@ -1,22 +1,22 @@
1
1
  class Icon {
2
- #chui_icon = document.createElement("chui_icon");
2
+ #chuiIcon = document.createElement("chui_icon");
3
3
  constructor(name = String(), size = String(), color = String()) {
4
4
  require("../../modules/chui_functions").setStyles(__dirname + "/styles.css", "chUiJS_Icons");
5
5
  // Пустое имя — пустая иконка: без этого `innerHTML = undefined` рисовал лигатуру «undefined»
6
- this.#chui_icon.textContent = name === undefined || name === null ? "" : String(name);
7
- this.#chui_icon.style.fontSize = size === undefined ? "" : size;
8
- this.#chui_icon.style.color = color === undefined ? "" : color;
6
+ this.#chuiIcon.textContent = name === undefined || name === null ? "" : String(name);
7
+ this.#chuiIcon.style.fontSize = size === undefined ? "" : size;
8
+ this.#chuiIcon.style.color = color === undefined ? "" : color;
9
9
  // Значок — украшение: его лигатура («table_chart», «expand_more») ничего не говорит
10
10
  // скринридеру, поэтому по умолчанию он скрыт от дерева доступности, а имя даёт тот,
11
11
  // кто значок поставил (подпись кнопки, пункта меню или скрытая подпись рядом).
12
12
  // Значащий значок можно вернуть: `icon.set().removeAttribute("aria-hidden")`
13
- this.#chui_icon.setAttribute("aria-hidden", "true");
13
+ this.#chuiIcon.setAttribute("aria-hidden", "true");
14
14
  }
15
15
  getHTML() {
16
- return this.#chui_icon.outerHTML;
16
+ return this.#chuiIcon.outerHTML;
17
17
  }
18
18
  set() {
19
- return this.#chui_icon;
19
+ return this.#chuiIcon;
20
20
  }
21
21
  }
22
22
 
@@ -10,5 +10,6 @@ chui_icon {
10
10
  direction: ltr;
11
11
  color: var(--text_color);
12
12
  pointer-events: none;
13
- transition: all var(--transition_time);
13
+ /* Глиф меняет только цвет: `all` тянул бы за собой и вёрстку, если она когда-нибудь изменится */
14
+ transition: color var(--motion_fast) var(--motion_ease);
14
15
  }
@@ -4,12 +4,12 @@ checkbox {
4
4
  карточку. Токены переопределены у корня поля, поэтому действуют только внутри него
5
5
  (тот же приём у полосы меню-бара). */
6
6
  --control_padding: var(--control_padding_sm);
7
- --main_padding: var(--main_padding_sm);
7
+ --block_padding: var(--block_padding_sm);
8
8
  display: flex;
9
9
  align-items: center;
10
10
  justify-content: center;
11
11
  font-size: var(--font_default_size);
12
- margin: var(--margin);
12
+ margin: var(--block_margin);
13
13
  width: max-content;
14
14
  }
15
15
 
@@ -44,7 +44,15 @@ checkbox {
44
44
  box-shadow: 0 0 1px 0 var(--border_color);
45
45
  background-repeat: no-repeat;
46
46
  background-position: center center;
47
- background-size: 50% 50%;
47
+ /* Галка прорастает из нуля: смена картинки мгновенная, а размер её растёт —
48
+ так отметка «попадает» в коробку, а не появляется готовой */
49
+ background-size: 0% 0%;
50
+ /* Свой список перехода заменяет общий, поэтому цвет и тень перечислены рядом */
51
+ transition:
52
+ background-color var(--motion_fast) var(--motion_ease),
53
+ color var(--motion_fast) var(--motion_ease),
54
+ box-shadow var(--motion_fast) var(--motion_ease),
55
+ background-size var(--motion_base) var(--motion_spring);
48
56
  }
49
57
 
50
58
  .checkbox:not(:disabled):not(:checked) + label:hover::before {
@@ -84,6 +92,7 @@ checkbox {
84
92
  color: var(--text_color_hover);
85
93
  border: none;
86
94
  box-shadow: 0 0 1px 0 var(--blue_prime_background);
95
+ background-size: 50% 50%;
87
96
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
88
97
  }
89
98
 
@@ -29,9 +29,11 @@ const { I18n } = require("../../../modules/chui_i18n/chui_i18n");
29
29
  * Валидация: `Validate.installField` — для этого поля осмысленны `required` и границы
30
30
  * **числа меток** (`min`, `max`), потому что проверяется количество, а не текст.
31
31
  *
32
- * Слушателей на `window`/`document` и таймеров нет, поэтому `destroy()` не нужен:
33
- * достаточно убрать узел из документа. За шириной строки меток следит `ResizeObserver`
34
- * на самой строке — он перестаёт работать вместе с отсоединённым узлом.
32
+ * Слушателей на `window`/`document` и таймеров нет, но `destroy()` нужен: за шириной строки
33
+ * меток следит `ResizeObserver`, а пересчёт счётчика «+N» отложен в кадр
34
+ * (`requestAnimationFrame`). Прежний комментарий утверждал, что достаточно убрать узел из
35
+ * документа, — это неверно: наблюдатель держит цель живой, а отложенный кадр продолжал бы
36
+ * считать метки уже удалённого поля.
35
37
  *
36
38
  * ```javascript
37
39
  * const tags = new Chips({name: 'tags', title: 'Теги', placeholder: 'Добавить тег',
@@ -50,8 +52,8 @@ class Chips {
50
52
  #input = document.createElement("input");
51
53
  #more = document.createElement("chips_more");
52
54
  #dropdown = document.createElement("chips_dropdown");
53
- #overflow_frame = undefined;
54
- #overflow_observer = undefined;
55
+ #overflowFrame = undefined;
56
+ #overflowObserver = undefined;
55
57
  #a11y = undefined;
56
58
  #hint = document.createElement("chips_hint");
57
59
  #status = document.createElement("chips_status");
@@ -61,11 +63,11 @@ class Chips {
61
63
  #placeholder = I18n.t("forms.chipsPlaceholder");
62
64
  #disabled = false;
63
65
  #unique = true;
64
- #allow_custom = true;
65
- #commit_on_blur = false;
66
- #max_chips = undefined;
66
+ #allowCustom = true;
67
+ #commitOnBlur = false;
68
+ #maxChips = undefined;
67
69
  #suggestions = [];
68
- #change_listeners = [];
70
+ #changeListeners = [];
69
71
 
70
72
  constructor(
71
73
  options = {
@@ -97,9 +99,9 @@ class Chips {
97
99
  this.#root.id = this.#id;
98
100
  if (options.placeholder !== undefined) this.#placeholder = options.placeholder;
99
101
  if (options.unique === false) this.#unique = false;
100
- if (options.allowCustom === false) this.#allow_custom = false;
101
- if (options.commitOnBlur === true) this.#commit_on_blur = true;
102
- if (options.maxChips !== undefined) this.#max_chips = options.maxChips;
102
+ if (options.allowCustom === false) this.#allowCustom = false;
103
+ if (options.commitOnBlur === true) this.#commitOnBlur = true;
104
+ if (options.maxChips !== undefined) this.#maxChips = options.maxChips;
103
105
  if (options.suggestions !== undefined) this.#suggestions = [...options.suggestions];
104
106
  if (options.title !== undefined) this.setTitle(options.title);
105
107
  else this.#title.hidden = true;
@@ -144,8 +146,8 @@ class Chips {
144
146
  // Ширину строки задаёт вёрстка вокруг, поэтому о смене размера сообщает наблюдатель
145
147
  // за самой строкой: поле может получить ширину уже после создания (ленивые разделы витрины)
146
148
  if (typeof ResizeObserver === "function") {
147
- this.#overflow_observer = new ResizeObserver(() => this.#scheduleOverflow());
148
- this.#overflow_observer.observe(this.#row);
149
+ this.#overflowObserver = new ResizeObserver(() => this.#scheduleOverflow());
150
+ this.#overflowObserver.observe(this.#row);
149
151
  }
150
152
  this.#syncFieldAria();
151
153
 
@@ -197,7 +199,7 @@ class Chips {
197
199
  }
198
200
 
199
201
  getMaxChips() {
200
- return this.#max_chips;
202
+ return this.#maxChips;
201
203
  }
202
204
 
203
205
  getSuggestions() {
@@ -236,7 +238,7 @@ class Chips {
236
238
  }
237
239
 
238
240
  setMaxChips(max = Number()) {
239
- this.#max_chips = max;
241
+ this.#maxChips = max;
240
242
  this.#syncDisabledState();
241
243
  return this;
242
244
  }
@@ -280,7 +282,9 @@ class Chips {
280
282
  if (index === -1) return this;
281
283
  this.#chips.splice(index, 1);
282
284
  const node = this.#list.children[index];
283
- if (node !== undefined) node.remove();
285
+ // Метка уходит движением: из строки она убирается по концу кадра, а счётчик «+N»
286
+ // пересчитывается заново — иначе он считал бы уходящую метку живой
287
+ if (node !== undefined) this.#leaveChip(node);
284
288
  // Место в строке освободилось: прежде скрытые метки могут вернуться
285
289
  this.#scheduleOverflow();
286
290
  this.#renderSuggestions();
@@ -305,7 +309,7 @@ class Chips {
305
309
  return this;
306
310
  }
307
311
  addChangeListener(listener = () => {}) {
308
- this.#change_listeners.push(listener);
312
+ this.#changeListeners.push(listener);
309
313
  return this;
310
314
  }
311
315
 
@@ -324,14 +328,27 @@ class Chips {
324
328
  set() {
325
329
  return this.#root;
326
330
  }
331
+
332
+ /** Полное удаление: снять наблюдатель за строкой, отложенный кадр и клавиатурный слой */
333
+ destroy() {
334
+ if (this.#overflowObserver !== undefined) {
335
+ this.#overflowObserver.disconnect();
336
+ this.#overflowObserver = undefined;
337
+ }
338
+ if (this.#overflowFrame !== undefined) {
339
+ cancelAnimationFrame(this.#overflowFrame);
340
+ this.#overflowFrame = undefined;
341
+ }
342
+ if (this.#a11y !== undefined) this.#a11y.destroy();
343
+ }
327
344
  // === ВНУТРЕННЕЕ ===
328
345
  // Общая точка добавления: возвращает признак «метка действительно добавлена»
329
346
  #tryAdd(text = String()) {
330
347
  const value = String(text).trim();
331
348
  if (value === "") return false;
332
349
  if (this.#unique && this.#chips.includes(value)) return false;
333
- if (this.#max_chips !== undefined && this.#chips.length >= this.#max_chips) return false;
334
- if (!this.#allow_custom && !this.#suggestions.includes(value)) return false;
350
+ if (this.#maxChips !== undefined && this.#chips.length >= this.#maxChips) return false;
351
+ if (!this.#allowCustom && !this.#suggestions.includes(value)) return false;
335
352
  this.#chips.push(value);
336
353
  this.#appendChip(value);
337
354
  this.#renderSuggestions();
@@ -342,6 +359,18 @@ class Chips {
342
359
  return true;
343
360
  }
344
361
 
362
+ /** Уход метки: она гаснет и сжимается, а из строки убирается по концу кадра */
363
+ #leaveChip(node) {
364
+ node.classList.add("chips_item_leaving");
365
+ const done = (event) => {
366
+ if (event.target !== node) return;
367
+ node.removeEventListener("animationend", done);
368
+ node.remove();
369
+ this.#scheduleOverflow();
370
+ };
371
+ node.addEventListener("animationend", done);
372
+ }
373
+
345
374
  #appendChip(text = String()) {
346
375
  const item = document.createElement("chips_item");
347
376
  const label = document.createElement("chips_text");
@@ -358,6 +387,15 @@ class Chips {
358
387
  if (this.#disabled) remove.disabled = true;
359
388
  item.appendChild(remove);
360
389
  this.#list.appendChild(item);
390
+ // Новая метка встаёт на место общим кадром появления, а не возникает рывком.
391
+ // Класс снимается по концу кадра: при пересборке списка метки его не переигрывают
392
+ item.classList.add("chips_item_appearing");
393
+ const appeared = (event) => {
394
+ if (event.target !== item) return;
395
+ item.removeEventListener("animationend", appeared);
396
+ item.classList.remove("chips_item_appearing");
397
+ };
398
+ item.addEventListener("animationend", appeared);
361
399
  // Метка могла не поместиться в строку — пересчёт счётчика «+N»
362
400
  this.#scheduleOverflow();
363
401
  }
@@ -397,13 +435,31 @@ class Chips {
397
435
 
398
436
  /** Пересчёт счётчика после текущего кадра: к этому моменту разметка меток уже уложена */
399
437
  #scheduleOverflow() {
400
- if (this.#overflow_frame !== undefined) return;
401
- this.#overflow_frame = requestAnimationFrame(() => {
402
- this.#overflow_frame = undefined;
438
+ if (this.#overflowFrame !== undefined) return;
439
+ this.#overflowFrame = requestAnimationFrame(() => {
440
+ this.#overflowFrame = undefined;
403
441
  this.#syncOverflow();
404
442
  });
405
443
  }
406
444
 
445
+ /**
446
+ * Уход списка идёт движением: `hidden` возвращается по концу кадра, поэтому список
447
+ * не пропадает рывком. Открытие за время ухода отменяет снятие — иначе список
448
+ * погас бы уже открытым.
449
+ */
450
+ #hideDropdown() {
451
+ if (this.#dropdown.hidden === true) return;
452
+ this.#dropdown.classList.add("chips_dropdown_leaving");
453
+ const done = (event) => {
454
+ if (event.target !== this.#dropdown) return;
455
+ this.#dropdown.removeEventListener("animationend", done);
456
+ if (this.#dropdown.classList.contains("chips_dropdown_leaving") === false) return;
457
+ this.#dropdown.classList.remove("chips_dropdown_leaving");
458
+ this.#dropdown.hidden = true;
459
+ };
460
+ this.#dropdown.addEventListener("animationend", done);
461
+ }
462
+
407
463
  // Список подсказок: свой выпадающий список в материале остальных дропдаунов
408
464
  #renderSuggestions() {
409
465
  // Уже добавленные метки в подсказках не нужны, набранный текст сужает список
@@ -435,8 +491,13 @@ class Chips {
435
491
  this.#scheduleOverflow();
436
492
  }
437
493
  const visible = open === true && this.#disabled === false && this.#dropdown.childElementCount > 0;
438
- this.#dropdown.hidden = visible === false;
439
- if (visible) this.#placeDropdown();
494
+ if (visible === true) {
495
+ this.#dropdown.classList.remove("chips_dropdown_leaving");
496
+ this.#dropdown.hidden = false;
497
+ this.#placeDropdown();
498
+ } else {
499
+ this.#hideDropdown();
500
+ }
440
501
  if (this.#a11y !== undefined) this.#a11y.setExpanded(visible);
441
502
  return visible;
442
503
  }
@@ -470,12 +531,12 @@ class Chips {
470
531
  }
471
532
 
472
533
  #syncDisabledState() {
473
- const full = this.#max_chips !== undefined && this.#chips.length >= this.#max_chips;
534
+ const full = this.#maxChips !== undefined && this.#chips.length >= this.#maxChips;
474
535
  // Поле ввода остаётся доступным и при достигнутом пределе: иначе нельзя было бы
475
536
  // убрать метку с клавиатуры, а причина отказа объясняется в статусе
476
537
  this.#input.disabled = this.#disabled;
477
538
  this.#input.placeholder = full
478
- ? I18n.t("forms.chipsLimitPlaceholder", { count: this.#max_chips })
539
+ ? I18n.t("forms.chipsLimitPlaceholder", { count: this.#maxChips })
479
540
  : this.#placeholder;
480
541
  for (const button of this.#list.querySelectorAll(".chips_remove")) button.disabled = this.#disabled;
481
542
  }
@@ -486,7 +547,7 @@ class Chips {
486
547
 
487
548
  #emit() {
488
549
  const chips = this.getValue();
489
- for (const listener of [...this.#change_listeners]) listener(chips);
550
+ for (const listener of [...this.#changeListeners]) listener(chips);
490
551
  }
491
552
 
492
553
  #commitInput() {
@@ -499,8 +560,8 @@ class Chips {
499
560
  return;
500
561
  }
501
562
  // Текст не теряем: либо он уже есть, либо метка не из подсказок, либо предел
502
- if (this.#max_chips !== undefined && this.#chips.length >= this.#max_chips) {
503
- this.#announce(I18n.t("forms.chipsLimitReached", { count: this.#max_chips }));
563
+ if (this.#maxChips !== undefined && this.#chips.length >= this.#maxChips) {
564
+ this.#announce(I18n.t("forms.chipsLimitReached", { count: this.#maxChips }));
504
565
  return;
505
566
  }
506
567
  if (this.#unique && this.#chips.includes(value.trim()))
@@ -566,7 +627,7 @@ class Chips {
566
627
  };
567
628
 
568
629
  #onInputBlur = () => {
569
- if (this.#commit_on_blur) this.#commitInput();
630
+ if (this.#commitOnBlur) this.#commitInput();
570
631
  };
571
632
 
572
633
  #onFieldClick = (event) => {