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.
- package/CHANGELOG.md +536 -0
- package/framework/appLayout/default/chui_app_layout.js +361 -303
- package/framework/appLayout/default/global_style.css +243 -89
- package/framework/appLayout/default/main_theme_style.css +188 -101
- package/framework/components/chui_accordion/accordion.js +17 -17
- package/framework/components/chui_accordion/styles.css +7 -7
- package/framework/components/chui_action_sheet/action_sheet.js +35 -32
- package/framework/components/chui_action_sheet/styles.css +10 -4
- package/framework/components/chui_actions_bar/actions_bar.js +338 -0
- package/framework/components/chui_actions_bar/styles.css +139 -0
- package/framework/components/chui_affix/affix.js +4 -4
- package/framework/components/chui_affix/styles.css +5 -0
- package/framework/components/chui_alert/alert.js +11 -5
- package/framework/components/chui_alert/styles.css +7 -0
- package/framework/components/chui_avatar/avatar.js +13 -13
- package/framework/components/chui_avatar/styles.css +1 -1
- package/framework/components/chui_avatar_group/avatar_group.js +4 -4
- package/framework/components/chui_avatar_group/styles.css +1 -1
- package/framework/components/chui_backtop/backtop.js +25 -2
- package/framework/components/chui_backtop/styles.css +21 -0
- package/framework/components/chui_badge/badge.js +15 -15
- package/framework/components/chui_badge/styles.css +6 -6
- package/framework/components/chui_banner/banner.js +222 -0
- package/framework/components/chui_banner/styles.css +170 -0
- package/framework/components/chui_breadcrumb/styles.css +1 -1
- package/framework/components/chui_button/button.js +24 -28
- package/framework/components/chui_button/styles.css +5 -5
- package/framework/components/chui_calendar/calendar.js +6 -6
- package/framework/components/chui_card/card.js +14 -14
- package/framework/components/chui_card_scroll/card_scroll.js +169 -0
- package/framework/components/chui_card_scroll/styles.css +86 -0
- package/framework/components/chui_chat/chat.js +73 -51
- package/framework/components/chui_chat/styles.css +18 -7
- package/framework/components/chui_clipboard_history/clipboard_history.js +25 -20
- package/framework/components/chui_clipboard_history/styles.css +10 -8
- package/framework/components/chui_code/code.js +8 -8
- package/framework/components/chui_code/styles.css +2 -2
- package/framework/components/chui_code_editor/code_editor.js +26 -26
- package/framework/components/chui_code_editor/styles.css +3 -3
- package/framework/components/chui_command_palette/command_palette.js +29 -29
- package/framework/components/chui_command_palette/styles.css +5 -5
- package/framework/components/chui_console/console.js +9 -9
- package/framework/components/chui_console/styles.css +5 -2
- package/framework/components/chui_content_block/content_block.js +2 -2
- package/framework/components/chui_context_menu/context_menu.js +162 -54
- package/framework/components/chui_context_menu/styles.css +82 -8
- package/framework/components/chui_custom_element/custom_element.js +11 -11
- package/framework/components/chui_data_grid/data_grid.js +268 -268
- package/framework/components/chui_data_grid/styles.css +14 -14
- package/framework/components/chui_descriptions/styles.css +1 -1
- package/framework/components/chui_details/details.js +12 -12
- package/framework/components/chui_details/styles.css +7 -7
- package/framework/components/chui_diff_viewer/diff_viewer.js +88 -88
- package/framework/components/chui_diff_viewer/styles.css +9 -9
- package/framework/components/chui_divider/divider.js +6 -6
- package/framework/components/chui_divider/styles.css +2 -2
- package/framework/components/chui_drawer/drawer.js +37 -14
- package/framework/components/chui_drawer/styles.css +18 -4
- package/framework/components/chui_drop_zone/drop_zone.js +6 -6
- package/framework/components/chui_drop_zone/styles.css +10 -6
- package/framework/components/chui_fieldset/fieldset.js +6 -6
- package/framework/components/chui_fieldset/styles.css +5 -5
- package/framework/components/chui_file_explorer/file_explorer.js +115 -115
- package/framework/components/chui_file_explorer/styles.css +6 -6
- package/framework/components/chui_form/form.js +11 -11
- package/framework/components/chui_gantt/gantt.js +41 -41
- package/framework/components/chui_gantt/styles.css +5 -5
- package/framework/components/chui_graphs/graphs.js +82 -46
- package/framework/components/chui_graphs/styles.css +26 -5
- package/framework/components/chui_hero/hero.js +63 -20
- package/framework/components/chui_hero/styles.css +176 -7
- package/framework/components/chui_html_block/html_block.js +5 -5
- package/framework/components/chui_html_block/styles.css +1 -1
- package/framework/components/chui_hx/styles.css +2 -2
- package/framework/components/chui_icons/icons.js +7 -7
- package/framework/components/chui_icons/styles.css +2 -1
- package/framework/components/chui_inputs/chui_check_box/styles.css +12 -3
- package/framework/components/chui_inputs/chui_chips/chips.js +92 -31
- package/framework/components/chui_inputs/chui_chips/styles.css +28 -5
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +20 -15
- package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +58 -58
- package/framework/components/chui_inputs/chui_combo_box/styles.css +9 -9
- package/framework/components/chui_inputs/chui_date/date.js +93 -95
- package/framework/components/chui_inputs/chui_date/styles.css +8 -8
- package/framework/components/chui_inputs/chui_date_range/date_range.js +22 -22
- package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
- package/framework/components/chui_inputs/chui_datetime/datetime.js +12 -12
- package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
- package/framework/components/chui_inputs/chui_email/email.js +10 -10
- package/framework/components/chui_inputs/chui_email/styles.css +6 -6
- package/framework/components/chui_inputs/chui_file/file.js +8 -8
- package/framework/components/chui_inputs/chui_file/styles.css +12 -9
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +39 -39
- package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
- package/framework/components/chui_inputs/chui_masked/masked.js +44 -44
- package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
- package/framework/components/chui_inputs/chui_mention/mention.js +13 -13
- package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +147 -137
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +14 -8
- package/framework/components/chui_inputs/chui_number/number.js +42 -45
- package/framework/components/chui_inputs/chui_number/styles.css +6 -6
- package/framework/components/chui_inputs/chui_otp/otp.js +32 -32
- package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
- package/framework/components/chui_inputs/chui_password/password.js +30 -36
- package/framework/components/chui_inputs/chui_password/styles.css +7 -7
- package/framework/components/chui_inputs/chui_radio_button/styles.css +2 -2
- package/framework/components/chui_inputs/chui_rating/rating.js +28 -28
- package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
- package/framework/components/chui_inputs/chui_select_box/select_box.js +54 -54
- package/framework/components/chui_inputs/chui_select_box/styles.css +10 -10
- package/framework/components/chui_inputs/chui_slider/slider.js +14 -14
- package/framework/components/chui_inputs/chui_slider/styles.css +5 -4
- package/framework/components/chui_inputs/chui_text/styles.css +7 -7
- package/framework/components/chui_inputs/chui_text/text.js +13 -17
- package/framework/components/chui_inputs/chui_text_area/styles.css +8 -8
- package/framework/components/chui_inputs/chui_text_area/text_area.js +23 -23
- package/framework/components/chui_inputs/chui_time/styles.css +13 -17
- package/framework/components/chui_inputs/chui_time/time.js +35 -24
- package/framework/components/chui_inputs/chui_toggle/styles.css +10 -3
- package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
- package/framework/components/chui_json_editor/json_editor.js +87 -87
- package/framework/components/chui_json_editor/styles.css +5 -5
- package/framework/components/chui_json_viewer/json_viewer.js +80 -80
- package/framework/components/chui_json_viewer/styles.css +6 -6
- package/framework/components/chui_kanban/kanban.js +27 -27
- package/framework/components/chui_kanban/styles.css +8 -4
- package/framework/components/chui_kbd/styles.css +1 -1
- package/framework/components/chui_label/label.js +17 -17
- package/framework/components/chui_label/styles.css +1 -1
- package/framework/components/chui_link/link.js +9 -9
- package/framework/components/chui_link/styles.css +11 -4
- package/framework/components/chui_list/list.js +11 -11
- package/framework/components/chui_list/styles.css +2 -2
- package/framework/components/chui_markdown_editor/markdown_editor.js +66 -66
- package/framework/components/chui_markdown_editor/styles.css +5 -5
- package/framework/components/chui_media/audio.js +240 -246
- package/framework/components/chui_media/audio_styles.css +41 -23
- package/framework/components/chui_media/image.js +49 -46
- package/framework/components/chui_media/image_compare.js +35 -35
- package/framework/components/chui_media/image_compare_styles.css +5 -5
- package/framework/components/chui_media/image_styles.css +1 -1
- package/framework/components/chui_media/video.js +148 -148
- package/framework/components/chui_media/video_styles.css +27 -11
- package/framework/components/chui_menu_bar/menu_bar.js +54 -54
- package/framework/components/chui_menu_bar/styles.css +10 -10
- package/framework/components/chui_message_bar/message_bar.js +551 -0
- package/framework/components/chui_message_bar/styles.css +248 -0
- package/framework/components/chui_modal/modal.js +37 -13
- package/framework/components/chui_notification/notification.js +76 -54
- package/framework/components/chui_notification/notification_download.js +26 -81
- package/framework/components/chui_notification/notification_download_card.js +115 -0
- package/framework/components/chui_notification/notification_download_progress.css +2 -2
- package/framework/components/chui_notification/notification_download_progress.js +70 -108
- package/framework/components/chui_notification/notification_update.js +31 -31
- package/framework/components/chui_notification/status_plate.css +3 -3
- package/framework/components/chui_notification/styles.css +6 -1
- package/framework/components/chui_notification/styles_download.css +1 -1
- package/framework/components/chui_notification/styles_update.css +2 -2
- package/framework/components/chui_org_chart/org_chart.js +29 -29
- package/framework/components/chui_org_chart/styles.css +2 -2
- package/framework/components/chui_pagination/pagination.js +3 -3
- package/framework/components/chui_pagination/styles.css +1 -1
- package/framework/components/chui_paragraph/styles.css +2 -2
- package/framework/components/chui_popover/popover.js +29 -23
- package/framework/components/chui_popover/styles.css +1 -1
- package/framework/components/chui_popups/popups.js +124 -124
- package/framework/components/chui_popups/styles.css +5 -5
- package/framework/components/chui_print_button/print_button.js +15 -10
- package/framework/components/chui_progress_bar/progress_bar.js +34 -12
- package/framework/components/chui_progress_bar/styles.css +4 -4
- package/framework/components/chui_progress_ring/progress_ring.js +15 -15
- package/framework/components/chui_progress_ring/styles.css +1 -1
- package/framework/components/chui_qrcode/qrcode.js +12 -12
- package/framework/components/chui_query_builder/query_builder.js +46 -46
- package/framework/components/chui_query_builder/styles.css +4 -4
- package/framework/components/chui_radio_group/styles.css +1 -1
- package/framework/components/chui_result/result.js +15 -15
- package/framework/components/chui_result/styles.css +3 -3
- package/framework/components/chui_scheduler/scheduler.js +74 -74
- package/framework/components/chui_scheduler/styles.css +5 -5
- package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
- package/framework/components/chui_screen_spinner/styles.css +49 -0
- package/framework/components/chui_segmented_control/segmented_control.js +63 -6
- package/framework/components/chui_segmented_control/styles.css +32 -6
- package/framework/components/chui_settings/settings.js +45 -46
- package/framework/components/chui_settings/styles.css +4 -4
- package/framework/components/chui_shortcut_help/shortcut_help.js +32 -34
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +98 -29
- package/framework/components/chui_sidebar_nav/styles.css +9 -3
- package/framework/components/chui_slideshow/slideshow.js +85 -85
- package/framework/components/chui_slideshow/styles.css +6 -6
- package/framework/components/chui_spinner/spinner.js +10 -10
- package/framework/components/chui_split_button/split_button.js +30 -12
- package/framework/components/chui_split_button/styles.css +21 -5
- package/framework/components/chui_splitter/splitter.js +11 -11
- package/framework/components/chui_splitter/styles.css +2 -2
- package/framework/components/chui_statistic/statistic.js +47 -47
- package/framework/components/chui_status_bar/styles.css +2 -2
- package/framework/components/chui_stepper/stepper.js +202 -31
- package/framework/components/chui_stepper/styles.css +79 -14
- package/framework/components/chui_sticky_bar/sticky_bar.js +44 -13
- package/framework/components/chui_sticky_bar/styles.css +10 -8
- package/framework/components/chui_tabs/chui_tabs.js +74 -26
- package/framework/components/chui_tabs/styles.css +40 -12
- package/framework/components/chui_text_editor/styles.css +7 -7
- package/framework/components/chui_text_editor/text_editor.js +56 -56
- package/framework/components/chui_text_editor/text_editor_panel.js +173 -174
- package/framework/components/chui_theme_toggle/theme_toggle.js +21 -5
- package/framework/components/chui_timeline/styles.css +3 -1
- package/framework/components/chui_toast_queue/styles.css +11 -11
- package/framework/components/chui_toast_queue/toast_queue.js +30 -27
- package/framework/components/chui_toolbar/styles.css +2 -2
- package/framework/components/chui_tooltip/styles.css +2 -2
- package/framework/components/chui_tooltip/tooltip.js +3 -3
- package/framework/components/chui_tour/styles.css +8 -8
- package/framework/components/chui_tour/tour.js +72 -72
- package/framework/components/chui_transfer/styles.css +6 -6
- package/framework/components/chui_transfer/transfer.js +93 -106
- package/framework/components/chui_tree_table/styles.css +4 -4
- package/framework/components/chui_tree_table/tree_table.js +45 -45
- package/framework/components/chui_tree_view/styles.css +10 -8
- package/framework/components/chui_tree_view/tree_view.js +185 -183
- package/framework/components/chui_user_bar/styles.css +145 -0
- package/framework/components/chui_user_bar/user_bar.js +402 -0
- package/framework/components/chui_user_list_bar/styles.css +173 -0
- package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
- package/framework/components/chui_watermark/watermark.js +17 -17
- package/framework/components/chui_webview/webview.js +9 -9
- package/framework/components/chui_write_bar/styles.css +172 -0
- package/framework/components/chui_write_bar/write_bar.js +320 -0
- package/framework/main_window.js +1234 -0
- package/framework/modules/chui_a11y/chui_a11y.js +78 -79
- package/framework/modules/chui_animations/animations.js +77 -15
- package/framework/modules/chui_animations/motion.js +120 -0
- package/framework/modules/chui_animations/styles.css +36 -3
- package/framework/modules/chui_functions.js +31 -27
- package/framework/modules/chui_highlight/styles.css +13 -10
- package/framework/modules/chui_i18n/chui_i18n.js +3 -3
- package/framework/modules/chui_i18n/locales/feedback.js +62 -0
- package/framework/modules/chui_i18n/locales/navigation.js +4 -0
- package/framework/modules/chui_json/chui_json.js +2 -2
- package/framework/modules/chui_logger/chui_logger.js +23 -8
- package/framework/modules/chui_page/page.js +10 -10
- package/framework/modules/chui_palette/palette.js +61 -0
- package/framework/modules/chui_qr/chui_qr.js +23 -23
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +45 -45
- package/framework/modules/chui_scrollbar/styles.css +3 -3
- package/framework/modules/chui_services/chui_services.js +8 -8
- package/framework/modules/chui_services/desktop.js +77 -71
- package/framework/modules/chui_sortable/chui_sortable.js +17 -17
- package/framework/modules/chui_validate/chui_validate.js +3 -3
- package/framework/tray_bar/chui_menu_items.js +2 -2
- package/index.d.ts +266 -34
- package/index.js +20 -1168
- package/package.json +6 -4
- package/framework/components/telegram_bot/chui_telegram_bot.js +0 -191
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const { Icon, Icons } = require("../chui_icons/icons");
|
|
2
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
3
|
+
const { setStyles, optionOr } = require("../../modules/chui_functions");
|
|
4
|
+
const { reducedMotion } = require("../../modules/chui_animations/motion");
|
|
5
|
+
|
|
6
|
+
/** Зазор между карточками по умолчанию: тот же шаг, что у отступов компонентов */
|
|
7
|
+
const DEFAULT_GAP = 12;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Лента карточек — горизонтальный список, который прокручивается колесом, стрелками на краях
|
|
11
|
+
* и клавиатурой.
|
|
12
|
+
*
|
|
13
|
+
* От `SlideShow` отличается тем, что не показывает слайды по одному: карточки идут подряд,
|
|
14
|
+
* лента листается свободно, а прилипание к началу карточки лишь помогает остановиться ровно.
|
|
15
|
+
* От `List` — направлением: строки идут вбок и не переносятся.
|
|
16
|
+
*
|
|
17
|
+
* Опции: `id`, `gap`, `snap` (прилипание к карточке, по умолчанию включено), `arrows`
|
|
18
|
+
* (стрелки на краях, по умолчанию включены), `label` (имя списка для скринридера),
|
|
19
|
+
* `items` (карточки).
|
|
20
|
+
*
|
|
21
|
+
* Методы: `addCard(component)`, `getCards()`, `scrollToCard(index)`, `scrollToEnd()`,
|
|
22
|
+
* `destroy()`, `set()`. В ленту принимаются компоненты фреймворка (у них есть `set()`)
|
|
23
|
+
* и готовые узлы.
|
|
24
|
+
*/
|
|
25
|
+
class CardScroll {
|
|
26
|
+
#root = document.createElement("chui_card_scroll");
|
|
27
|
+
#track = document.createElement("card_scroll_track");
|
|
28
|
+
#start = undefined;
|
|
29
|
+
#end = undefined;
|
|
30
|
+
#gap = DEFAULT_GAP;
|
|
31
|
+
#observer = undefined;
|
|
32
|
+
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
setStyles(__dirname + "/styles.css", "chUiJS_CardScroll");
|
|
35
|
+
if (options.id !== undefined) this.#root.id = options.id;
|
|
36
|
+
const gap = Number(options.gap);
|
|
37
|
+
this.#gap = Number.isFinite(gap) && gap >= 0 ? gap : DEFAULT_GAP;
|
|
38
|
+
this.#track.style.gap = `${this.#gap}px`;
|
|
39
|
+
// Список, а не просто лента: скринридер объявляет число карточек, а каждая —
|
|
40
|
+
// свой `listitem` (роль ставит `addCard`)
|
|
41
|
+
this.#track.setAttribute("role", "list");
|
|
42
|
+
this.#track.setAttribute("aria-label", optionOr(options.label, I18n.t("feedback.cardScroll")));
|
|
43
|
+
// Лента сама принимает фокус: стрелки клавиатуры прокручивают её, не уводя фокус в карточки
|
|
44
|
+
this.#track.setAttribute("tabindex", "0");
|
|
45
|
+
this.#track.addEventListener("keydown", (event) => this.#onKeydown(event));
|
|
46
|
+
// Вертикальное колесо прокручивает ленту вбок — так её листают мышью с колесом
|
|
47
|
+
this.#track.addEventListener("wheel", (event) => this.#onWheel(event), { passive: false });
|
|
48
|
+
this.#track.addEventListener("scroll", () => this.#syncArrows());
|
|
49
|
+
if (options.snap === false) this.#root.dataset.snap = "false";
|
|
50
|
+
if (options.arrows !== false) {
|
|
51
|
+
this.#start = this.#makeArrow(Icons.NAVIGATION.CHEVRON_LEFT, I18n.t("feedback.cardScrollPrev"), -1);
|
|
52
|
+
this.#end = this.#makeArrow(Icons.NAVIGATION.CHEVRON_RIGHT, I18n.t("feedback.cardScrollNext"), 1);
|
|
53
|
+
this.#root.append(this.#start, this.#track, this.#end);
|
|
54
|
+
} else {
|
|
55
|
+
this.#root.appendChild(this.#track);
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(options.items)) for (const item of options.items) this.addCard(item);
|
|
58
|
+
// Стрелки появляются только там, где лента действительно уходит за край: следим
|
|
59
|
+
// за размером (карточку могли добавить после сборки) и за прокруткой
|
|
60
|
+
if (typeof ResizeObserver === "function") {
|
|
61
|
+
this.#observer = new ResizeObserver(() => this.#syncArrows());
|
|
62
|
+
this.#observer.observe(this.#track);
|
|
63
|
+
}
|
|
64
|
+
this.#syncArrows();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Карточка ленты: компонент фреймворка (у него есть `set()`) или готовый узел */
|
|
68
|
+
addCard(component = undefined) {
|
|
69
|
+
if (component === undefined || component === null) return this;
|
|
70
|
+
const item = document.createElement("card_scroll_item");
|
|
71
|
+
item.setAttribute("role", "listitem");
|
|
72
|
+
item.appendChild(typeof component.set === "function" ? component.set() : component);
|
|
73
|
+
this.#track.appendChild(item);
|
|
74
|
+
this.#syncArrows();
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getCards() {
|
|
79
|
+
return [...this.#track.children];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Переход к карточке по номеру: место берут у самой карточки, а не считают по ширине */
|
|
83
|
+
scrollToCard(index = Number()) {
|
|
84
|
+
const cards = this.getCards();
|
|
85
|
+
const card = cards[Number(index)];
|
|
86
|
+
if (card === undefined) return this;
|
|
87
|
+
this.#scrollTo(card.offsetLeft - this.#track.offsetLeft);
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
scrollToEnd() {
|
|
92
|
+
this.#scrollTo(this.#track.scrollWidth);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set() {
|
|
97
|
+
return this.#root;
|
|
98
|
+
}
|
|
99
|
+
destroy() {
|
|
100
|
+
if (this.#observer !== undefined) this.#observer.disconnect();
|
|
101
|
+
this.#observer = undefined;
|
|
102
|
+
this.#root.remove();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// === ВНУТРЕННЕЕ ===
|
|
106
|
+
#makeArrow(icon = String(), label = String(), direction = 1) {
|
|
107
|
+
const button = document.createElement("button");
|
|
108
|
+
button.type = "button";
|
|
109
|
+
button.className = "card_scroll_arrow";
|
|
110
|
+
// Сторона видна и стилям, и замерам: край ленты — то, что здесь важно
|
|
111
|
+
button.dataset.side = direction < 0 ? "start" : "end";
|
|
112
|
+
button.innerHTML = new Icon(icon, "20px").getHTML();
|
|
113
|
+
button.setAttribute("aria-label", label);
|
|
114
|
+
button.title = label;
|
|
115
|
+
button.addEventListener("click", () => this.#step(direction));
|
|
116
|
+
return button;
|
|
117
|
+
}
|
|
118
|
+
#scrollTo(left = Number()) {
|
|
119
|
+
// При уменьшенном движении лента перескакивает без плавности
|
|
120
|
+
this.#track.scrollTo({ left: left, behavior: reducedMotion() ? "auto" : "smooth" });
|
|
121
|
+
}
|
|
122
|
+
/** Шаг — одна карточка с зазором: он берётся у самой карточки, а не из настроек */
|
|
123
|
+
#step(direction = 1) {
|
|
124
|
+
const card = this.#track.querySelector("card_scroll_item");
|
|
125
|
+
const step = card === null ? this.#track.clientWidth : card.getBoundingClientRect().width + this.#gap;
|
|
126
|
+
this.#scrollTo(this.#track.scrollLeft + step * direction);
|
|
127
|
+
}
|
|
128
|
+
#onKeydown(event = {}) {
|
|
129
|
+
if (event.key === "ArrowRight" || event.key === "ArrowLeft") {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
this.#step(event.key === "ArrowRight" ? 1 : -1);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (event.key === "Home") {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
this.#scrollTo(0);
|
|
137
|
+
}
|
|
138
|
+
if (event.key === "End") {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
this.scrollToEnd();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Вертикальное колесо двигает ленту вбок. Когда лента упирается, событие отдаётся странице:
|
|
145
|
+
* иначе список «съел» бы прокрутку, и до содержимого ниже было бы не добраться.
|
|
146
|
+
*/
|
|
147
|
+
#onWheel(event = {}) {
|
|
148
|
+
const track = this.#track;
|
|
149
|
+
if (event.deltaY === 0) return;
|
|
150
|
+
const max = track.scrollWidth - track.clientWidth;
|
|
151
|
+
if (max <= 0) return;
|
|
152
|
+
if ((track.scrollLeft <= 0 && event.deltaY < 0) || (track.scrollLeft >= max && event.deltaY > 0)) return;
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
track.scrollLeft += event.deltaY;
|
|
155
|
+
}
|
|
156
|
+
/** Стрелка видна только там, где за краем есть карточки */
|
|
157
|
+
#syncArrows() {
|
|
158
|
+
if (this.#start === undefined || this.#end === undefined) return;
|
|
159
|
+
const track = this.#track;
|
|
160
|
+
const max = track.scrollWidth - track.clientWidth;
|
|
161
|
+
// Отступ ленты (он держит тень карточек) сдвигает нулевую позицию: без него
|
|
162
|
+
// лента с прилипанием стоит на нём, и стрелка «назад» светилась бы на старте
|
|
163
|
+
const offset = parseFloat(getComputedStyle(track).paddingLeft) || 0;
|
|
164
|
+
this.#start.hidden = track.scrollLeft <= offset + 1;
|
|
165
|
+
this.#end.hidden = track.scrollLeft >= max - 1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
exports.CardScroll = CardScroll;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Лента карточек: горизонтальный список со стрелками на краях.
|
|
2
|
+
Своей палитры у компонента нет: карточки приносят её сами, полоса прокрутки — общая
|
|
3
|
+
(`chui_scrollbar`). */
|
|
4
|
+
|
|
5
|
+
chui_card_scroll {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: row;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
/* Компонент сам задаёт ширину: в flex-колонке с `align-items: flex-start` лента
|
|
11
|
+
без ширины получила бы `max-content` и вылезла за содержимое */
|
|
12
|
+
width: -webkit-fill-available;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
card_scroll_track {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: row;
|
|
19
|
+
flex: 1 1 auto;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
/* Лента идёт вбок: по вертикали содержимое не переносится и не обрезается тенью карточек */
|
|
23
|
+
overflow-x: auto;
|
|
24
|
+
overflow-y: hidden;
|
|
25
|
+
/* Прилипание к началу карточки помогает остановиться ровно, но не заставляет ленту
|
|
26
|
+
прыгать через карточку: `proximity`, а не `mandatory` */
|
|
27
|
+
scroll-snap-type: x proximity;
|
|
28
|
+
/* Отступ под тень карточек: у обрезанного слоя тень срезалась бы по краю */
|
|
29
|
+
padding: 4px;
|
|
30
|
+
margin: -4px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
chui_card_scroll[data-snap="false"] card_scroll_track {
|
|
34
|
+
scroll-snap-type: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Лента сама принимает фокус: стрелки клавиатуры прокручивают её, а не уводят фокус в карточку */
|
|
38
|
+
card_scroll_track:focus-visible {
|
|
39
|
+
outline: var(--focus_outline);
|
|
40
|
+
outline-offset: var(--focus_outline_offset);
|
|
41
|
+
border-radius: var(--radius_sm);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
card_scroll_item {
|
|
45
|
+
flex: none;
|
|
46
|
+
display: flex;
|
|
47
|
+
scroll-snap-align: start;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Стрелка на краю ленты — круглая кнопка со значком: имя ей даёт словарь, значка скринридер
|
|
51
|
+
не читает (`Icon` помечает его `aria-hidden`) */
|
|
52
|
+
.card_scroll_arrow {
|
|
53
|
+
flex: none;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
width: var(--control_height);
|
|
60
|
+
height: var(--control_height);
|
|
61
|
+
padding: 0;
|
|
62
|
+
border: 1px solid var(--border_color);
|
|
63
|
+
border-radius: var(--radius_circle);
|
|
64
|
+
background: var(--element_background_2);
|
|
65
|
+
color: var(--text_color);
|
|
66
|
+
box-shadow: var(--box_shadow_main);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.card_scroll_arrow:hover {
|
|
70
|
+
background: var(--element_background_hover);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Нажатие — акцент, как у прочих кнопок тонкой настройки */
|
|
74
|
+
.card_scroll_arrow:active {
|
|
75
|
+
background: var(--blue_prime_background);
|
|
76
|
+
color: var(--text_color_hover);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.card_scroll_arrow chui_icon {
|
|
80
|
+
color: inherit;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* За краем карточек нет — стрелки нет: `hidden` убирает её и из обхода клавиатурой */
|
|
84
|
+
.card_scroll_arrow[hidden] {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
@@ -76,23 +76,23 @@ class Chat {
|
|
|
76
76
|
#typing = document.createElement("chat_typing");
|
|
77
77
|
#composer = document.createElement("chat_composer");
|
|
78
78
|
#input = document.createElement("textarea");
|
|
79
|
-
#
|
|
79
|
+
#sendButton = document.createElement("button");
|
|
80
80
|
#messages = [];
|
|
81
81
|
#own = undefined;
|
|
82
82
|
#placeholder = undefined;
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
83
|
+
#sendText = undefined;
|
|
84
|
+
#sendLabel = undefined;
|
|
85
|
+
#dateSeparators = true;
|
|
86
|
+
#composerVisible = true;
|
|
87
87
|
#disabled = false;
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
88
|
+
#typingName = undefined;
|
|
89
|
+
#sendListeners = [];
|
|
90
|
+
#onSend = undefined;
|
|
91
|
+
#onMessageClick = undefined;
|
|
92
92
|
// Текущий день и текущая группа — по ним решается, дописывать пузырь или начать новую;
|
|
93
93
|
// группа — это {node, bubbles, avatar, author, day}
|
|
94
|
-
#
|
|
95
|
-
#
|
|
94
|
+
#lastDate = undefined;
|
|
95
|
+
#lastGroup = undefined;
|
|
96
96
|
|
|
97
97
|
constructor(
|
|
98
98
|
options = {
|
|
@@ -118,12 +118,12 @@ class Chat {
|
|
|
118
118
|
this.#root.id = this.#id;
|
|
119
119
|
this.#own = options.own;
|
|
120
120
|
this.#placeholder = options.placeholder;
|
|
121
|
-
this.#
|
|
122
|
-
this.#
|
|
123
|
-
this.#
|
|
124
|
-
this.#
|
|
125
|
-
this.#
|
|
126
|
-
this.#
|
|
121
|
+
this.#sendText = options.sendText;
|
|
122
|
+
this.#sendLabel = options.sendLabel;
|
|
123
|
+
this.#onSend = options.onSend;
|
|
124
|
+
this.#onMessageClick = options.onMessageClick;
|
|
125
|
+
this.#dateSeparators = options.dateSeparators !== false;
|
|
126
|
+
this.#composerVisible = options.composer !== false;
|
|
127
127
|
if (options.width !== undefined) this.#root.style.width = options.width;
|
|
128
128
|
// Высота журнала: своя переменная, чтобы поле ввода и шапка оставались на месте
|
|
129
129
|
this.#log.style.setProperty("--chat_height", options.height === undefined ? "320px" : options.height);
|
|
@@ -145,17 +145,17 @@ class Chat {
|
|
|
145
145
|
this.#input.placeholder =
|
|
146
146
|
this.#placeholder === undefined ? I18n.t("feedback.chatPlaceholder") : this.#placeholder;
|
|
147
147
|
this.#input.setAttribute("aria-label", I18n.t("feedback.chatComposer"));
|
|
148
|
-
this.#
|
|
149
|
-
this.#
|
|
150
|
-
this.#
|
|
151
|
-
this.#
|
|
152
|
-
this.#
|
|
153
|
-
this.#
|
|
148
|
+
this.#sendButton.type = "button";
|
|
149
|
+
this.#sendButton.className = "chat_send";
|
|
150
|
+
this.#sendButton.disabled = true;
|
|
151
|
+
this.#sendButton.setAttribute("aria-label", this.#sendLabelText());
|
|
152
|
+
this.#sendButton.setAttribute("title", this.#sendTextText());
|
|
153
|
+
this.#sendButton.innerHTML = new Icon(Icons.CONTENT.SEND, "16px").getHTML();
|
|
154
154
|
|
|
155
155
|
this.#header.appendChild(this.#title);
|
|
156
156
|
this.#header.appendChild(this.#subtitle);
|
|
157
157
|
this.#composer.appendChild(this.#input);
|
|
158
|
-
this.#composer.appendChild(this.#
|
|
158
|
+
this.#composer.appendChild(this.#sendButton);
|
|
159
159
|
this.#root.appendChild(this.#header);
|
|
160
160
|
this.#root.appendChild(this.#log);
|
|
161
161
|
this.#root.appendChild(this.#typing);
|
|
@@ -164,12 +164,12 @@ class Chat {
|
|
|
164
164
|
this.#subtitle.hidden = true;
|
|
165
165
|
if (optionOr(options.title, undefined) !== undefined) this.setTitle(options.title);
|
|
166
166
|
if (optionOr(options.subtitle, undefined) !== undefined) this.setSubtitle(options.subtitle);
|
|
167
|
-
if (!this.#
|
|
167
|
+
if (!this.#composerVisible) this.#composer.hidden = true;
|
|
168
168
|
if (this.#disabled) this.setDisabled(true);
|
|
169
169
|
|
|
170
170
|
this.#input.addEventListener("input", () => this.#syncSendState());
|
|
171
171
|
this.#input.addEventListener("keydown", this.#onInputKeyDown);
|
|
172
|
-
this.#
|
|
172
|
+
this.#sendButton.addEventListener("click", () => this.send());
|
|
173
173
|
this.#log.addEventListener("click", this.#onLogClick);
|
|
174
174
|
this.setMessages(options.messages === undefined ? [] : options.messages);
|
|
175
175
|
}
|
|
@@ -193,7 +193,7 @@ class Chat {
|
|
|
193
193
|
return this.#messages.map((message) => Object.assign({}, message));
|
|
194
194
|
}
|
|
195
195
|
isTyping() {
|
|
196
|
-
return this.#
|
|
196
|
+
return this.#typingName !== undefined;
|
|
197
197
|
}
|
|
198
198
|
isDisabled() {
|
|
199
199
|
return this.#disabled;
|
|
@@ -238,7 +238,7 @@ class Chat {
|
|
|
238
238
|
return this;
|
|
239
239
|
}
|
|
240
240
|
addSendListener(listener = () => {}) {
|
|
241
|
-
this.#
|
|
241
|
+
this.#sendListeners.push(listener);
|
|
242
242
|
return this;
|
|
243
243
|
}
|
|
244
244
|
set() {
|
|
@@ -249,8 +249,8 @@ class Chat {
|
|
|
249
249
|
/** Полная замена списка: журнал собирается заново, группы схлопываются по автору и дню */
|
|
250
250
|
setMessages(messages = []) {
|
|
251
251
|
this.#messages = [];
|
|
252
|
-
this.#
|
|
253
|
-
this.#
|
|
252
|
+
this.#lastDate = undefined;
|
|
253
|
+
this.#lastGroup = undefined;
|
|
254
254
|
this.#log.innerHTML = "";
|
|
255
255
|
for (const message of messages) this.#appendMessage(Object.assign({}, message), false);
|
|
256
256
|
this.scrollToBottom(false);
|
|
@@ -275,18 +275,40 @@ class Chat {
|
|
|
275
275
|
}
|
|
276
276
|
clear() {
|
|
277
277
|
this.#messages = [];
|
|
278
|
-
this.#
|
|
279
|
-
this.#
|
|
278
|
+
this.#lastDate = undefined;
|
|
279
|
+
this.#lastGroup = undefined;
|
|
280
280
|
this.#log.innerHTML = "";
|
|
281
281
|
return this;
|
|
282
282
|
}
|
|
283
283
|
/** «Иван печатает…»: имя автора; `undefined` убирает строку */
|
|
284
284
|
setTyping(name = undefined) {
|
|
285
|
-
this.#
|
|
286
|
-
this.#typing.hidden = name === undefined;
|
|
285
|
+
this.#typingName = name;
|
|
287
286
|
this.#typing.innerText = name === undefined ? "" : I18n.t("feedback.chatTyping", { name: name });
|
|
287
|
+
this.#showTyping(name !== undefined);
|
|
288
288
|
return this;
|
|
289
289
|
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Строка «печатает» появляется и уходит движением: `hidden` возвращается по концу кадра,
|
|
293
|
+
* поэтому строка не пропадает рывком. Открытие за время ухода отменяет снятие.
|
|
294
|
+
*/
|
|
295
|
+
#showTyping(visible) {
|
|
296
|
+
if (visible === true) {
|
|
297
|
+
this.#typing.classList.remove("chat_typing_leaving");
|
|
298
|
+
this.#typing.hidden = false;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (this.#typing.hidden === true) return;
|
|
302
|
+
this.#typing.classList.add("chat_typing_leaving");
|
|
303
|
+
const done = (event) => {
|
|
304
|
+
if (event.target !== this.#typing) return;
|
|
305
|
+
this.#typing.removeEventListener("animationend", done);
|
|
306
|
+
if (this.#typing.classList.contains("chat_typing_leaving") === false) return;
|
|
307
|
+
this.#typing.classList.remove("chat_typing_leaving");
|
|
308
|
+
this.#typing.hidden = true;
|
|
309
|
+
};
|
|
310
|
+
this.#typing.addEventListener("animationend", done);
|
|
311
|
+
}
|
|
290
312
|
scrollToBottom(smooth = true) {
|
|
291
313
|
// Плавная прокрутка — движение; при `prefers-reduced-motion` она идёт сразу
|
|
292
314
|
const behavior = smooth && this.#reducedMotion() === false ? "smooth" : "auto";
|
|
@@ -313,18 +335,18 @@ class Chat {
|
|
|
313
335
|
this.#input.value = "";
|
|
314
336
|
this.#syncSendState();
|
|
315
337
|
const added = this.#appendMessage(message, true);
|
|
316
|
-
if (this.#
|
|
317
|
-
for (const listener of [...this.#
|
|
338
|
+
if (this.#onSend !== undefined) this.#onSend(text, added);
|
|
339
|
+
for (const listener of [...this.#sendListeners]) listener(text, added);
|
|
318
340
|
return this;
|
|
319
341
|
}
|
|
320
342
|
|
|
321
343
|
// === ВНУТРЕННЕЕ ===
|
|
322
344
|
#sendTextText() {
|
|
323
|
-
return this.#
|
|
345
|
+
return this.#sendText === undefined ? I18n.t("feedback.chatSend") : this.#sendText;
|
|
324
346
|
}
|
|
325
347
|
/** Имя кнопки отправки: своё (`sendLabel`), иначе `sendText`, иначе словарь */
|
|
326
348
|
#sendLabelText() {
|
|
327
|
-
if (this.#
|
|
349
|
+
if (this.#sendLabel !== undefined) return this.#sendLabel;
|
|
328
350
|
return this.#sendTextText();
|
|
329
351
|
}
|
|
330
352
|
#reducedMotion() {
|
|
@@ -333,7 +355,7 @@ class Chat {
|
|
|
333
355
|
return query.matches === true;
|
|
334
356
|
}
|
|
335
357
|
#syncSendState() {
|
|
336
|
-
this.#
|
|
358
|
+
this.#sendButton.disabled = this.#disabled || this.#input.value.trim() === "";
|
|
337
359
|
}
|
|
338
360
|
/**
|
|
339
361
|
* Добавить сообщение в журнал. Если автор и день совпадают с прошлой группой, пузырь
|
|
@@ -361,27 +383,27 @@ class Chat {
|
|
|
361
383
|
}
|
|
362
384
|
/** Разделитель дня: между сообщениями разных дней и в начале журнала */
|
|
363
385
|
#insertDateSeparator(date) {
|
|
364
|
-
if (date === undefined || this.#
|
|
386
|
+
if (date === undefined || this.#dateSeparators === false) return;
|
|
365
387
|
const key = this.#dayKey(date);
|
|
366
|
-
if (key === this.#
|
|
367
|
-
this.#
|
|
388
|
+
if (key === this.#lastDate) return;
|
|
389
|
+
this.#lastDate = key;
|
|
368
390
|
const separator = document.createElement("chat_date");
|
|
369
391
|
separator.dataset.day = key;
|
|
370
392
|
const label = document.createElement("chat_date_label");
|
|
371
393
|
label.innerText = this.#dayLabel(date);
|
|
372
394
|
separator.appendChild(label);
|
|
373
395
|
this.#log.appendChild(separator);
|
|
374
|
-
this.#
|
|
396
|
+
this.#lastGroup = undefined;
|
|
375
397
|
}
|
|
376
398
|
/** Группа для сообщения: та же, если автор и день совпадают, иначе новая */
|
|
377
399
|
#groupFor(message) {
|
|
378
400
|
const author = this.#authorOf(message);
|
|
379
401
|
if (
|
|
380
|
-
this.#
|
|
381
|
-
this.#
|
|
382
|
-
this.#
|
|
402
|
+
this.#lastGroup !== undefined &&
|
|
403
|
+
this.#lastGroup.author === author &&
|
|
404
|
+
this.#lastGroup.day === this.#lastDate
|
|
383
405
|
) {
|
|
384
|
-
return this.#
|
|
406
|
+
return this.#lastGroup;
|
|
385
407
|
}
|
|
386
408
|
const node = document.createElement("chat_group");
|
|
387
409
|
node.dataset.own = message.own === true ? "true" : "false";
|
|
@@ -399,8 +421,8 @@ class Chat {
|
|
|
399
421
|
node.prepend(avatar.set());
|
|
400
422
|
}
|
|
401
423
|
this.#log.appendChild(node);
|
|
402
|
-
this.#
|
|
403
|
-
return this.#
|
|
424
|
+
this.#lastGroup = { node: node, bubbles: bubbles, avatar: avatar, author: author, day: this.#lastDate };
|
|
425
|
+
return this.#lastGroup;
|
|
404
426
|
}
|
|
405
427
|
#bubble(message, first) {
|
|
406
428
|
const bubble = document.createElement("chat_bubble");
|
|
@@ -495,11 +517,11 @@ class Chat {
|
|
|
495
517
|
this.send();
|
|
496
518
|
};
|
|
497
519
|
#onLogClick = (event) => {
|
|
498
|
-
if (this.#
|
|
520
|
+
if (this.#onMessageClick === undefined) return;
|
|
499
521
|
const bubble = event.target instanceof Element ? event.target.closest("chat_bubble") : null;
|
|
500
522
|
if (bubble === null) return;
|
|
501
523
|
const message = this.#messages.find((entry) => entry.id === bubble.dataset.id);
|
|
502
|
-
if (message !== undefined) this.#
|
|
524
|
+
if (message !== undefined) this.#onMessageClick(message);
|
|
503
525
|
};
|
|
504
526
|
}
|
|
505
527
|
|
|
@@ -4,9 +4,9 @@ chui_chat {
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
height: max-content;
|
|
6
6
|
width: 360px;
|
|
7
|
-
margin: var(--
|
|
7
|
+
margin: var(--block_margin);
|
|
8
8
|
overflow: hidden;
|
|
9
|
-
border-radius: var(--
|
|
9
|
+
border-radius: var(--radius_sm);
|
|
10
10
|
background: var(--element_background);
|
|
11
11
|
border: 1px solid var(--border_color);
|
|
12
12
|
font-size: var(--font_default_size);
|
|
@@ -17,7 +17,7 @@ chat_header {
|
|
|
17
17
|
display: flex;
|
|
18
18
|
flex-direction: column;
|
|
19
19
|
gap: 2px;
|
|
20
|
-
padding: var(--control_padding) var(--
|
|
20
|
+
padding: var(--control_padding) var(--block_padding);
|
|
21
21
|
border-bottom: 1px solid var(--border_color);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -100,10 +100,21 @@ chat_bubble {
|
|
|
100
100
|
flex-direction: column;
|
|
101
101
|
gap: 2px;
|
|
102
102
|
padding: 8px 10px;
|
|
103
|
-
border-radius: var(--
|
|
103
|
+
border-radius: var(--radius_sm);
|
|
104
104
|
background: var(--element_background);
|
|
105
105
|
border: 1px solid var(--border_color);
|
|
106
106
|
color: var(--text_color);
|
|
107
|
+
/* Новое сообщение встаёт на место: кадр общий — см. `chui_appear_up` в global_style.css */
|
|
108
|
+
animation: chui_appear_up var(--motion_base) var(--motion_ease);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Строка «печатает…»: появляется и уходит движением, `hidden` возвращается по концу кадра */
|
|
112
|
+
chat_typing {
|
|
113
|
+
animation: chui_appear_up var(--motion_base) var(--motion_ease);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.chat_typing_leaving {
|
|
117
|
+
animation: chui_leave_up var(--motion_fast) var(--motion_ease) forwards;
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
/* Свои сообщения — акцентом и справа */
|
|
@@ -149,7 +160,7 @@ chat_meta {
|
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
chat_bubble[data-own="true"] chat_meta {
|
|
152
|
-
color: var(--
|
|
163
|
+
color: var(--text_color_hover_disabled);
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
chat_status {
|
|
@@ -169,7 +180,7 @@ chat_status[data-status="failed"] {
|
|
|
169
180
|
|
|
170
181
|
/* Строка «печатает…» */
|
|
171
182
|
chat_typing {
|
|
172
|
-
padding: 0 var(--
|
|
183
|
+
padding: 0 var(--block_padding) var(--control_padding);
|
|
173
184
|
color: var(--text_color_disabled);
|
|
174
185
|
font-size: var(--font_labels_size);
|
|
175
186
|
}
|
|
@@ -220,7 +231,7 @@ chat_composer {
|
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
.chat_send:hover {
|
|
223
|
-
background: var(--
|
|
234
|
+
background: var(--blue_prime_background_tint_strong);
|
|
224
235
|
}
|
|
225
236
|
|
|
226
237
|
.chat_send:disabled {
|