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.
- package/CHANGELOG.md +597 -0
- package/README.md +43 -644
- package/framework/appLayout/default/chui_app_layout.js +602 -496
- package/framework/appLayout/default/global_style.css +288 -97
- package/framework/appLayout/default/main_theme_style.css +188 -101
- package/framework/components/chui_accordion/accordion.js +38 -35
- package/framework/components/chui_accordion/styles.css +13 -12
- package/framework/components/chui_action_sheet/action_sheet.js +93 -64
- package/framework/components/chui_action_sheet/styles.css +12 -5
- 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 +16 -11
- package/framework/components/chui_affix/styles.css +5 -0
- package/framework/components/chui_alert/alert.js +34 -19
- package/framework/components/chui_alert/styles.css +7 -0
- package/framework/components/chui_avatar/avatar.js +36 -35
- package/framework/components/chui_avatar/styles.css +1 -1
- package/framework/components/chui_avatar_group/avatar_group.js +25 -23
- package/framework/components/chui_avatar_group/styles.css +1 -1
- package/framework/components/chui_backtop/backtop.js +60 -21
- package/framework/components/chui_backtop/styles.css +21 -0
- package/framework/components/chui_badge/badge.js +30 -28
- 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/breadcrumb.js +15 -13
- package/framework/components/chui_breadcrumb/styles.css +1 -1
- package/framework/components/chui_button/button.js +52 -36
- package/framework/components/chui_button/styles.css +7 -7
- package/framework/components/chui_calendar/calendar.js +18 -16
- package/framework/components/chui_card/card.js +52 -32
- 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 +140 -79
- package/framework/components/chui_chat/styles.css +18 -7
- package/framework/components/chui_clipboard_history/clipboard_history.js +53 -39
- package/framework/components/chui_clipboard_history/styles.css +13 -9
- package/framework/components/chui_code/code.js +10 -10
- package/framework/components/chui_code/styles.css +4 -4
- package/framework/components/chui_code_editor/code_editor.js +73 -57
- package/framework/components/chui_code_editor/styles.css +6 -5
- package/framework/components/chui_command_palette/command_palette.js +87 -64
- package/framework/components/chui_command_palette/styles.css +5 -5
- package/framework/components/chui_console/console.js +13 -13
- package/framework/components/chui_console/styles.css +6 -3
- package/framework/components/chui_content_block/content_block.js +35 -21
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +171 -58
- package/framework/components/chui_context_menu/styles.css +82 -8
- package/framework/components/chui_custom_element/custom_element.js +25 -18
- package/framework/components/chui_data_grid/data_grid.js +633 -471
- package/framework/components/chui_data_grid/styles.css +24 -22
- package/framework/components/chui_descriptions/descriptions.js +34 -17
- package/framework/components/chui_descriptions/styles.css +1 -1
- package/framework/components/chui_details/details.js +42 -36
- package/framework/components/chui_details/styles.css +12 -11
- package/framework/components/chui_diff_viewer/diff_viewer.js +172 -147
- package/framework/components/chui_diff_viewer/styles.css +10 -10
- package/framework/components/chui_divider/divider.js +19 -17
- package/framework/components/chui_divider/styles.css +2 -2
- package/framework/components/chui_drawer/drawer.js +63 -38
- package/framework/components/chui_drawer/styles.css +18 -4
- package/framework/components/chui_drop_zone/drop_zone.js +37 -27
- package/framework/components/chui_drop_zone/styles.css +10 -6
- package/framework/components/chui_empty_state/empty_state.js +20 -15
- package/framework/components/chui_fieldset/fieldset.js +40 -23
- package/framework/components/chui_fieldset/styles.css +6 -6
- package/framework/components/chui_file_explorer/file_explorer.js +260 -189
- package/framework/components/chui_file_explorer/styles.css +8 -7
- package/framework/components/chui_form/form.js +40 -28
- package/framework/components/chui_form/styles.css +1 -1
- package/framework/components/chui_gantt/gantt.js +102 -78
- package/framework/components/chui_gantt/styles.css +5 -5
- package/framework/components/chui_graphs/graphs.js +376 -239
- package/framework/components/chui_graphs/styles.css +27 -6
- package/framework/components/chui_hero/hero.js +98 -37
- package/framework/components/chui_hero/styles.css +176 -7
- package/framework/components/chui_html_block/html_block.js +8 -8
- package/framework/components/chui_html_block/styles.css +3 -3
- package/framework/components/chui_hx/hx.js +3 -3
- package/framework/components/chui_hx/styles.css +13 -6
- package/framework/components/chui_icons/icons.js +2115 -2115
- package/framework/components/chui_icons/styles.css +4 -3
- package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
- package/framework/components/chui_inputs/chui_check_box/styles.css +30 -21
- package/framework/components/chui_inputs/chui_chips/chips.js +141 -70
- package/framework/components/chui_inputs/chui_chips/styles.css +32 -7
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +55 -38
- package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +127 -109
- package/framework/components/chui_inputs/chui_combo_box/styles.css +23 -21
- package/framework/components/chui_inputs/chui_date/date.js +161 -127
- package/framework/components/chui_inputs/chui_date/styles.css +20 -19
- package/framework/components/chui_inputs/chui_date_range/date_range.js +61 -50
- package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
- package/framework/components/chui_inputs/chui_datetime/datetime.js +47 -39
- package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
- package/framework/components/chui_inputs/chui_email/email.js +48 -36
- package/framework/components/chui_inputs/chui_email/styles.css +11 -11
- package/framework/components/chui_inputs/chui_file/file.js +71 -44
- package/framework/components/chui_inputs/chui_file/styles.css +23 -19
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +101 -76
- package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
- package/framework/components/chui_inputs/chui_masked/masked.js +83 -72
- package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
- package/framework/components/chui_inputs/chui_mention/mention.js +87 -40
- package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +242 -212
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +23 -15
- package/framework/components/chui_inputs/chui_number/number.js +84 -70
- package/framework/components/chui_inputs/chui_number/styles.css +13 -13
- package/framework/components/chui_inputs/chui_otp/otp.js +71 -62
- package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
- package/framework/components/chui_inputs/chui_password/password.js +72 -56
- package/framework/components/chui_inputs/chui_password/styles.css +15 -15
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
- package/framework/components/chui_inputs/chui_radio_button/styles.css +22 -22
- package/framework/components/chui_inputs/chui_rating/rating.js +54 -50
- package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
- package/framework/components/chui_inputs/chui_select_box/select_box.js +132 -108
- package/framework/components/chui_inputs/chui_select_box/styles.css +21 -21
- package/framework/components/chui_inputs/chui_slider/slider.js +41 -38
- package/framework/components/chui_inputs/chui_slider/styles.css +7 -5
- package/framework/components/chui_inputs/chui_text/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text/text.js +69 -52
- package/framework/components/chui_inputs/chui_text_area/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text_area/text_area.js +59 -36
- package/framework/components/chui_inputs/chui_time/styles.css +18 -20
- package/framework/components/chui_inputs/chui_time/time.js +71 -49
- package/framework/components/chui_inputs/chui_toggle/styles.css +13 -6
- package/framework/components/chui_inputs/chui_toggle/toggle.js +27 -27
- package/framework/components/chui_json_editor/json_editor.js +137 -127
- package/framework/components/chui_json_editor/styles.css +5 -5
- package/framework/components/chui_json_viewer/json_viewer.js +133 -114
- package/framework/components/chui_json_viewer/styles.css +9 -8
- package/framework/components/chui_kanban/kanban.js +78 -60
- package/framework/components/chui_kanban/styles.css +8 -4
- package/framework/components/chui_kbd/kbd.js +12 -10
- package/framework/components/chui_kbd/styles.css +1 -1
- package/framework/components/chui_label/label.js +31 -29
- package/framework/components/chui_label/styles.css +3 -3
- package/framework/components/chui_link/link.js +36 -29
- package/framework/components/chui_link/styles.css +11 -4
- package/framework/components/chui_list/list.js +42 -38
- package/framework/components/chui_list/styles.css +4 -3
- package/framework/components/chui_markdown_editor/markdown_editor.js +125 -109
- package/framework/components/chui_markdown_editor/styles.css +5 -5
- package/framework/components/chui_media/audio.js +457 -364
- package/framework/components/chui_media/audio_styles.css +74 -37
- package/framework/components/chui_media/image.js +99 -64
- package/framework/components/chui_media/image_compare.js +86 -55
- package/framework/components/chui_media/image_compare_styles.css +5 -5
- package/framework/components/chui_media/image_styles.css +4 -4
- package/framework/components/chui_media/video.js +264 -171
- package/framework/components/chui_media/video_styles.css +57 -24
- package/framework/components/chui_menu_bar/menu_bar.js +67 -65
- package/framework/components/chui_menu_bar/styles.css +14 -12
- 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 +81 -56
- package/framework/components/chui_modal/styles.css +3 -2
- package/framework/components/chui_notification/notification.js +107 -69
- package/framework/components/chui_notification/notification_download.js +28 -77
- package/framework/components/chui_notification/notification_download_card.js +115 -0
- package/framework/components/chui_notification/notification_download_progress.css +5 -4
- package/framework/components/chui_notification/notification_download_progress.js +73 -105
- package/framework/components/chui_notification/notification_update.js +44 -37
- package/framework/components/chui_notification/status_plate.css +3 -3
- package/framework/components/chui_notification/status_plate.js +2 -2
- package/framework/components/chui_notification/styles.css +6 -1
- package/framework/components/chui_notification/styles_download.css +2 -2
- package/framework/components/chui_notification/styles_update.css +3 -3
- package/framework/components/chui_org_chart/org_chart.js +101 -62
- package/framework/components/chui_org_chart/styles.css +2 -2
- package/framework/components/chui_pagination/pagination.js +65 -30
- package/framework/components/chui_pagination/styles.css +1 -1
- package/framework/components/chui_paragraph/paragraph.js +2 -2
- package/framework/components/chui_paragraph/styles.css +3 -3
- package/framework/components/chui_popover/popover.js +77 -50
- package/framework/components/chui_popover/styles.css +1 -1
- package/framework/components/chui_popups/popups.js +210 -181
- package/framework/components/chui_popups/styles.css +7 -7
- package/framework/components/chui_print_button/print_button.js +48 -33
- package/framework/components/chui_progress_bar/progress_bar.js +49 -19
- package/framework/components/chui_progress_bar/styles.css +6 -6
- package/framework/components/chui_progress_ring/progress_ring.js +32 -30
- package/framework/components/chui_progress_ring/styles.css +1 -1
- package/framework/components/chui_qrcode/qrcode.js +85 -41
- package/framework/components/chui_query_builder/query_builder.js +263 -170
- package/framework/components/chui_query_builder/styles.css +4 -4
- package/framework/components/chui_radio_group/radio_group.js +36 -26
- package/framework/components/chui_radio_group/styles.css +2 -2
- package/framework/components/chui_result/result.js +41 -31
- package/framework/components/chui_result/styles.css +3 -3
- package/framework/components/chui_scheduler/scheduler.js +126 -104
- package/framework/components/chui_scheduler/styles.css +11 -7
- 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 +90 -27
- package/framework/components/chui_segmented_control/styles.css +32 -6
- package/framework/components/chui_settings/settings.js +160 -87
- package/framework/components/chui_settings/styles.css +4 -4
- package/framework/components/chui_shortcut_help/shortcut_help.js +82 -61
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +132 -53
- package/framework/components/chui_sidebar_nav/styles.css +9 -3
- package/framework/components/chui_skeleton/skeleton.js +29 -24
- package/framework/components/chui_slideshow/slideshow.js +122 -103
- package/framework/components/chui_slideshow/styles.css +22 -16
- package/framework/components/chui_spinner/spinner.js +17 -17
- package/framework/components/chui_spinner/styles.css +0 -1
- package/framework/components/chui_split_button/split_button.js +53 -30
- package/framework/components/chui_split_button/styles.css +22 -6
- package/framework/components/chui_splitter/splitter.js +46 -39
- package/framework/components/chui_splitter/styles.css +2 -2
- package/framework/components/chui_statistic/statistic.js +111 -84
- package/framework/components/chui_status_bar/status_bar.js +11 -9
- package/framework/components/chui_status_bar/styles.css +2 -2
- package/framework/components/chui_stepper/stepper.js +230 -54
- package/framework/components/chui_stepper/styles.css +79 -14
- package/framework/components/chui_sticky_bar/sticky_bar.js +53 -17
- package/framework/components/chui_sticky_bar/styles.css +11 -7
- package/framework/components/chui_tabs/chui_tabs.js +100 -46
- package/framework/components/chui_tabs/styles.css +43 -15
- package/framework/components/chui_text_editor/styles.css +24 -23
- package/framework/components/chui_text_editor/text_editor.js +103 -79
- package/framework/components/chui_text_editor/text_editor_panel.js +428 -367
- package/framework/components/chui_theme_toggle/theme_toggle.js +46 -25
- package/framework/components/chui_timeline/styles.css +3 -1
- package/framework/components/chui_timeline/timeline.js +14 -12
- package/framework/components/chui_toast_queue/styles.css +11 -11
- package/framework/components/chui_toast_queue/toast_queue.js +82 -73
- package/framework/components/chui_toolbar/styles.css +2 -2
- package/framework/components/chui_toolbar/toolbar.js +20 -13
- package/framework/components/chui_tooltip/styles.css +2 -2
- package/framework/components/chui_tooltip/tooltip.js +40 -27
- package/framework/components/chui_tour/styles.css +12 -7
- package/framework/components/chui_tour/tour.js +132 -98
- package/framework/components/chui_transfer/styles.css +8 -7
- package/framework/components/chui_transfer/transfer.js +143 -123
- package/framework/components/chui_tree_table/styles.css +4 -4
- package/framework/components/chui_tree_table/tree_table.js +88 -74
- package/framework/components/chui_tree_view/styles.css +14 -10
- package/framework/components/chui_tree_view/tree_view.js +298 -253
- 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 +41 -33
- package/framework/components/chui_webview/styles.css +2 -2
- package/framework/components/chui_webview/webview.js +64 -52
- 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 +153 -123
- package/framework/modules/chui_animations/animations.js +105 -43
- package/framework/modules/chui_animations/motion.js +120 -0
- package/framework/modules/chui_animations/styles.css +37 -4
- package/framework/modules/chui_bridge/preload.js +36 -25
- package/framework/modules/chui_functions.js +69 -63
- package/framework/modules/chui_highlight/chui_highlight.js +25 -22
- package/framework/modules/chui_highlight/styles.css +13 -10
- package/framework/modules/chui_i18n/chui_i18n.js +12 -10
- package/framework/modules/chui_i18n/locales/a11y.js +5 -5
- package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
- package/framework/modules/chui_i18n/locales/calendar.js +3 -3
- package/framework/modules/chui_i18n/locales/common.js +3 -3
- package/framework/modules/chui_i18n/locales/data.js +36 -26
- package/framework/modules/chui_i18n/locales/editors.js +40 -13
- package/framework/modules/chui_i18n/locales/feedback.js +65 -3
- package/framework/modules/chui_i18n/locales/forms.js +8 -8
- package/framework/modules/chui_i18n/locales/graphics.js +8 -8
- package/framework/modules/chui_i18n/locales/media.js +4 -4
- package/framework/modules/chui_i18n/locales/navigation.js +12 -8
- package/framework/modules/chui_i18n/locales/validate.js +3 -3
- package/framework/modules/chui_json/chui_json.js +11 -11
- package/framework/modules/chui_layout/chui_layout.js +12 -10
- package/framework/modules/chui_logger/chui_logger.js +47 -28
- package/framework/modules/chui_page/page.js +39 -33
- package/framework/modules/chui_palette/palette.js +61 -0
- package/framework/modules/chui_qr/chui_qr.js +179 -88
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +79 -68
- package/framework/modules/chui_scrollbar/styles.css +4 -2
- package/framework/modules/chui_services/chui_channels.js +27 -13
- package/framework/modules/chui_services/chui_services.js +47 -39
- package/framework/modules/chui_services/desktop.js +108 -101
- package/framework/modules/chui_sortable/chui_sortable.js +33 -28
- package/framework/modules/chui_validate/chui_validate.js +10 -7
- package/framework/tray_bar/chui_menu_items.js +136 -46
- package/index.d.ts +5046 -0
- package/index.js +199 -1254
- package/package.json +81 -68
- package/framework/components/telegram_bot/chui_telegram_bot.js +0 -184
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
const { Icon, Icons } = require("../chui_icons/icons");
|
|
2
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
3
|
+
const { optionOr, setStyles } = require("../../modules/chui_functions");
|
|
4
|
+
|
|
5
|
+
/** Предел роста поля: дальше текст уходит в прокрутку самого поля, а не растёт панель */
|
|
6
|
+
const DEFAULT_MAX_HEIGHT = 120;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Кнопка панели ввода: значок с подписью для скринридера и счётчиком.
|
|
10
|
+
*
|
|
11
|
+
* Это настоящий `<button>`, а не кастомный тег: клавиатура, фокус и роль у него уже есть,
|
|
12
|
+
* и скринридер читает его как кнопку без дополнительных договоров.
|
|
13
|
+
*
|
|
14
|
+
* Подпись обязательна — лигатура значка («attach_file») ничего не говорит скринридеру,
|
|
15
|
+
* а сам значок скрыт от дерева доступности (`Icon` ставит `aria-hidden`). Счётчик нужен
|
|
16
|
+
* кнопкам, за которыми стоит число: выбранные вложения, эмодзи, черновик.
|
|
17
|
+
*/
|
|
18
|
+
class WriteBarIcon {
|
|
19
|
+
#root = document.createElement("button");
|
|
20
|
+
#label = String();
|
|
21
|
+
#onClick = undefined;
|
|
22
|
+
#disabled = false;
|
|
23
|
+
#badge = document.createElement("write_bar_badge");
|
|
24
|
+
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
const icon = optionOr(options.icon, String());
|
|
27
|
+
const label = optionOr(options.label, String());
|
|
28
|
+
this.#label = label === "" ? I18n.t("feedback.writeBarButton") : label;
|
|
29
|
+
this.#root.type = "button";
|
|
30
|
+
this.#root.className = "write_bar_button";
|
|
31
|
+
if (icon !== "") this.#root.innerHTML = new Icon(icon, "20px").getHTML();
|
|
32
|
+
if (options.accent === true) this.#root.dataset.accent = "true";
|
|
33
|
+
this.#root.setAttribute("aria-label", this.#label);
|
|
34
|
+
// Подсказка мышью повторяет имя кнопки: подписи в панели нет, а догадываться — не дело
|
|
35
|
+
this.#root.title = this.#label;
|
|
36
|
+
this.#onClick = typeof options.onClick === "function" ? options.onClick : undefined;
|
|
37
|
+
this.#root.addEventListener("click", () => {
|
|
38
|
+
if (this.#disabled === true) return;
|
|
39
|
+
if (this.#onClick !== undefined) this.#onClick(this);
|
|
40
|
+
});
|
|
41
|
+
if (options.badge !== undefined) this.setBadge(options.badge);
|
|
42
|
+
if (options.disabled === true) this.setDisabled(true);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Счётчик на кнопке: число показывает плашка, скринридеру оно попадает в подпись */
|
|
46
|
+
setBadge(count = Number()) {
|
|
47
|
+
const value = Number(count);
|
|
48
|
+
const shown = Number.isFinite(value) && value > 0 ? String(value) : "";
|
|
49
|
+
this.#badge.innerText = shown;
|
|
50
|
+
this.#badge.hidden = shown === "";
|
|
51
|
+
this.#root.setAttribute(
|
|
52
|
+
"aria-label",
|
|
53
|
+
shown === "" ? this.#label : `${this.#label}, ${I18n.t("feedback.writeBarBadge", { value: shown })}`
|
|
54
|
+
);
|
|
55
|
+
if (shown !== "" && this.#badge.parentNode === null) this.#root.appendChild(this.#badge);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setDisabled(boolean = Boolean()) {
|
|
60
|
+
this.#disabled = boolean === true;
|
|
61
|
+
this.#root.disabled = this.#disabled;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setLabel(text = String()) {
|
|
66
|
+
this.#label = String(text);
|
|
67
|
+
this.#root.setAttribute("aria-label", this.#label);
|
|
68
|
+
this.#root.title = this.#label;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set() {
|
|
73
|
+
return this.#root;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Панель ввода — нижняя строка приложения: слева вложения и эмодзи, по центру поле текста,
|
|
79
|
+
* справа отправка.
|
|
80
|
+
*
|
|
81
|
+
* Поле растёт под содержимое до `maxHeight` (по умолчанию 120 px), дальше текст уходит
|
|
82
|
+
* в прокрутку поля — панель места не отнимает. Enter отправляет, Shift+Enter переносит
|
|
83
|
+
* строку (`setEnterSends(false)` возвращает обычное поле с переносом), во время набора
|
|
84
|
+
* иероглифов Enter выбирает вариант и не отправляет.
|
|
85
|
+
*
|
|
86
|
+
* Опции: `id`, `placeholder`, `value`, `maxHeight`, `disabled`, `shadow` (тень вместо
|
|
87
|
+
* верхней волосяной линии — для панели поверх содержимого), `enterSends`, `sendText`
|
|
88
|
+
* (подпись кнопки отправки), `buttons` (кнопки панели), `onSend`.
|
|
89
|
+
*
|
|
90
|
+
* Методы: `getValue()`, `setValue(text)`, `clear()`, `send()`, `addButton(options)`,
|
|
91
|
+
* `addSendListener(fn)`, `removeSendListener(fn)`, `setPlaceholder(text)`, `setDisabled(bool)`,
|
|
92
|
+
* `isDisabled()`, `setEnterSends(bool)`, `setSendLabel(text)`, `focus()`, `blur()`, `getField()`,
|
|
93
|
+
* `getSendButton()`, `set()`, `destroy()`.
|
|
94
|
+
*
|
|
95
|
+
* Кнопки панели собираются фабрикой `WriteBar.Icon({ icon, label, onClick, badge, accent })`:
|
|
96
|
+
* своей кнопке нужен значок из `Icons` и подпись для скринридера.
|
|
97
|
+
*
|
|
98
|
+
* Прилипание к низу окна компонент не делает сам — для этого в фреймворке есть `StickyBar`;
|
|
99
|
+
* панель не полагает, где она стоит, и потому одинаково работает и в карточке, и в окне.
|
|
100
|
+
*/
|
|
101
|
+
class WriteBar {
|
|
102
|
+
/** Фабрика кнопок панели: `WriteBar.Icon({ icon, label, onClick, badge, accent })` */
|
|
103
|
+
static Icon = (options = {}) => new WriteBarIcon(options);
|
|
104
|
+
|
|
105
|
+
#root = document.createElement("chui_write_bar");
|
|
106
|
+
#row = document.createElement("write_bar_row");
|
|
107
|
+
#field = document.createElement("textarea");
|
|
108
|
+
#before = document.createElement("write_bar_slot");
|
|
109
|
+
#after = document.createElement("write_bar_slot");
|
|
110
|
+
#send = undefined;
|
|
111
|
+
#icons = [];
|
|
112
|
+
#maxHeight = DEFAULT_MAX_HEIGHT;
|
|
113
|
+
#disabled = false;
|
|
114
|
+
#enterSends = true;
|
|
115
|
+
#onSend = undefined;
|
|
116
|
+
#sendListeners = [];
|
|
117
|
+
|
|
118
|
+
constructor(options = {}) {
|
|
119
|
+
setStyles(__dirname + "/styles.css", "chUiJS_WriteBar");
|
|
120
|
+
if (options.id !== undefined) this.#root.id = options.id;
|
|
121
|
+
const maxHeight = Number(options.maxHeight);
|
|
122
|
+
this.#maxHeight = Number.isFinite(maxHeight) && maxHeight > 0 ? maxHeight : DEFAULT_MAX_HEIGHT;
|
|
123
|
+
this.#onSend = typeof options.onSend === "function" ? options.onSend : undefined;
|
|
124
|
+
this.#enterSends = options.enterSends !== false;
|
|
125
|
+
|
|
126
|
+
this.#field.className = "write_bar_field";
|
|
127
|
+
// Одна строка: `rows` держит высоту в покое, растущую высоту ведёт `#autosize`
|
|
128
|
+
this.#field.rows = 1;
|
|
129
|
+
this.#field.setAttribute("aria-multiline", "true");
|
|
130
|
+
this.#field.setAttribute("aria-keyshortcuts", "Enter");
|
|
131
|
+
this.#field.setAttribute("aria-label", I18n.t("feedback.writeBarField"));
|
|
132
|
+
this.#syncHint();
|
|
133
|
+
this.#root.setAttribute("role", "group");
|
|
134
|
+
this.#root.setAttribute("aria-label", I18n.t("feedback.writeBar"));
|
|
135
|
+
|
|
136
|
+
this.#send = new WriteBarIcon({
|
|
137
|
+
icon: Icons.CONTENT.SEND,
|
|
138
|
+
label: I18n.t("feedback.writeBarSend"),
|
|
139
|
+
accent: true,
|
|
140
|
+
onClick: () => this.send(),
|
|
141
|
+
});
|
|
142
|
+
this.#after.appendChild(this.#send.set());
|
|
143
|
+
// Слоты: слева — вложения и эмодзи, справа — свои действия перед отправкой
|
|
144
|
+
this.#row.append(this.#before, this.#field, this.#after);
|
|
145
|
+
this.#root.appendChild(this.#row);
|
|
146
|
+
|
|
147
|
+
this.#field.addEventListener("input", () => {
|
|
148
|
+
this.#autosize();
|
|
149
|
+
this.#syncSendState();
|
|
150
|
+
});
|
|
151
|
+
this.#field.addEventListener("keydown", (event) => this.#onKeydown(event));
|
|
152
|
+
|
|
153
|
+
this.setPlaceholder(
|
|
154
|
+
options.placeholder === undefined ? I18n.t("feedback.writeBarPlaceholder") : options.placeholder
|
|
155
|
+
);
|
|
156
|
+
if (options.sendText !== undefined) this.setSendLabel(options.sendText);
|
|
157
|
+
if (options.value !== undefined) this.setValue(options.value);
|
|
158
|
+
if (Array.isArray(options.buttons)) for (const button of options.buttons) this.#addBefore(button);
|
|
159
|
+
if (options.shadow === true) this.#root.dataset.shadow = "true";
|
|
160
|
+
if (options.disabled === true) this.setDisabled(true);
|
|
161
|
+
this.#autosize();
|
|
162
|
+
this.#syncSendState();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// GET
|
|
166
|
+
getValue() {
|
|
167
|
+
return this.#field.value;
|
|
168
|
+
}
|
|
169
|
+
isDisabled() {
|
|
170
|
+
return this.#disabled;
|
|
171
|
+
}
|
|
172
|
+
getField() {
|
|
173
|
+
return this.#field;
|
|
174
|
+
}
|
|
175
|
+
getSendButton() {
|
|
176
|
+
return this.#send;
|
|
177
|
+
}
|
|
178
|
+
/** Кнопки, добавленные слотом (отправка сюда не входит — у неё своё место) */
|
|
179
|
+
getIcons() {
|
|
180
|
+
return this.#icons.slice();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// SET
|
|
184
|
+
setValue(text = String()) {
|
|
185
|
+
this.#field.value = String(text);
|
|
186
|
+
this.#autosize();
|
|
187
|
+
this.#syncSendState();
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
clear() {
|
|
191
|
+
return this.setValue("");
|
|
192
|
+
}
|
|
193
|
+
setPlaceholder(text = String()) {
|
|
194
|
+
this.#field.placeholder = String(text);
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
setSendLabel(text = String()) {
|
|
198
|
+
this.#send.setLabel(String(text));
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
setDisabled(boolean = Boolean()) {
|
|
202
|
+
this.#disabled = boolean === true;
|
|
203
|
+
this.#root.dataset.disabled = this.#disabled ? "true" : "false";
|
|
204
|
+
this.#field.disabled = this.#disabled;
|
|
205
|
+
this.#syncSendState();
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Enter отправляет (по умолчанию) или переносит строку. Выключают там, где панель —
|
|
210
|
+
* обычное поле в форме: случайная отправка посреди набора там дороже лишнего щелчка.
|
|
211
|
+
*/
|
|
212
|
+
setEnterSends(boolean = Boolean()) {
|
|
213
|
+
this.#enterSends = boolean === true;
|
|
214
|
+
this.#syncHint();
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
focus() {
|
|
218
|
+
this.#field.focus();
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
blur() {
|
|
222
|
+
this.#field.blur();
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Кнопка в левом слоте — вложения, эмодзи, что угодно со значком и подписью */
|
|
227
|
+
addButton(options = {}) {
|
|
228
|
+
const button = options instanceof WriteBarIcon ? options : new WriteBarIcon(options);
|
|
229
|
+
this.#icons.push(button);
|
|
230
|
+
return button;
|
|
231
|
+
}
|
|
232
|
+
#addBefore(options = {}) {
|
|
233
|
+
const button = this.addButton(options);
|
|
234
|
+
this.#before.appendChild(button.set());
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Отправка: пустое поле и выключенная панель ничего не делают — как нажатие кнопки */
|
|
238
|
+
send() {
|
|
239
|
+
if (this.#disabled === true) return this;
|
|
240
|
+
const text = this.#field.value.trim();
|
|
241
|
+
if (text === "") return this;
|
|
242
|
+
this.#field.value = "";
|
|
243
|
+
this.#autosize();
|
|
244
|
+
this.#syncSendState();
|
|
245
|
+
if (this.#onSend !== undefined) this.#onSend(text, this);
|
|
246
|
+
for (const listener of this.#sendListeners) listener(text, this);
|
|
247
|
+
this.focus();
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
addSendListener(listener = () => {}) {
|
|
251
|
+
if (typeof listener === "function") this.#sendListeners.push(listener);
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
/** Снять слушателя отправки: нужен тому, кто связывает панель с лентой и разрывает связь */
|
|
255
|
+
removeSendListener(listener = () => {}) {
|
|
256
|
+
const index = this.#sendListeners.indexOf(listener);
|
|
257
|
+
if (index !== -1) this.#sendListeners.splice(index, 1);
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
set() {
|
|
262
|
+
return this.#root;
|
|
263
|
+
}
|
|
264
|
+
destroy() {
|
|
265
|
+
this.#sendListeners = [];
|
|
266
|
+
this.#icons = [];
|
|
267
|
+
this.#onSend = undefined;
|
|
268
|
+
this.#root.remove();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// === ВНУТРЕННЕЕ ===
|
|
272
|
+
/**
|
|
273
|
+
* Подсказка про Enter висит на поле только тогда, когда Enter действительно отправляет:
|
|
274
|
+
* иначе она обещала бы то, чего панель не делает.
|
|
275
|
+
*/
|
|
276
|
+
#syncHint() {
|
|
277
|
+
if (this.#enterSends === true) this.#field.title = I18n.t("feedback.writeBarHint");
|
|
278
|
+
else this.#field.removeAttribute("title");
|
|
279
|
+
}
|
|
280
|
+
/** Enter отправляет, Shift+Enter переносит; во время набора иероглифов Enter не наш */
|
|
281
|
+
#onKeydown(event = {}) {
|
|
282
|
+
if (this.#enterSends === false) return;
|
|
283
|
+
if (event.key !== "Enter" || event.shiftKey === true) return;
|
|
284
|
+
if (event.isComposing === true) return;
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
this.send();
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Высота поля по содержимому, до предела. Сначала высоту снимаем (`auto`): иначе поле
|
|
290
|
+
* никогда не уменьшается — `scrollHeight` не бывает меньше текущей высоты. На пределе
|
|
291
|
+
* содержимое уходит в прокрутку поля, а панель перестаёт расти.
|
|
292
|
+
*
|
|
293
|
+
* Пустое поле высоты не получает вовсе: её держит `rows="1"` и нижний предел стилей
|
|
294
|
+
* (`min-height: --control_height` — по нему же выровнены кнопки панели), поэтому
|
|
295
|
+
* панель не прыгает на первом же символе: в одну линию поле всегда высотой с кнопку.
|
|
296
|
+
*/
|
|
297
|
+
#autosize() {
|
|
298
|
+
const field = this.#field;
|
|
299
|
+
const limit = this.#maxHeight;
|
|
300
|
+
if (field.value === "") {
|
|
301
|
+
field.style.height = "";
|
|
302
|
+
field.dataset.scrollable = "false";
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
field.style.height = "auto";
|
|
306
|
+
// `scrollHeight` — содержимое с отступами, но без рамки: в `border-box` её надо добавить,
|
|
307
|
+
// иначе поле «съёживается» на толщину рамки при первом же символе
|
|
308
|
+
const border = field.offsetHeight - field.clientHeight;
|
|
309
|
+
const height = field.scrollHeight + border;
|
|
310
|
+
field.style.height = Math.min(height, limit) + "px";
|
|
311
|
+
field.dataset.scrollable = height > limit ? "true" : "false";
|
|
312
|
+
}
|
|
313
|
+
/** Отправка гаснет на пустом поле: так панель говорит, что отправлять нечего */
|
|
314
|
+
#syncSendState() {
|
|
315
|
+
this.#send.setDisabled(this.#disabled === true || this.#field.value.trim() === "");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
exports.WriteBar = WriteBar;
|
|
320
|
+
exports.WriteBarIcon = WriteBarIcon;
|