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
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const {Animation} = require(
|
|
2
|
-
const {OverlayA11y} = require(
|
|
3
|
-
const {Icon} = require(
|
|
4
|
-
const {setStyles, optionOr} = require(
|
|
5
|
-
const {I18n} = require(
|
|
1
|
+
const { Animation } = require("../../modules/chui_animations/animations");
|
|
2
|
+
const { OverlayA11y } = require("../../modules/chui_a11y/chui_a11y");
|
|
3
|
+
const { Icon } = require("../chui_icons/icons");
|
|
4
|
+
const { setStyles, optionOr } = require("../../modules/chui_functions");
|
|
5
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Список действий из нижней части экрана (action sheet): заголовок, пояснение,
|
|
@@ -43,7 +43,7 @@ const {I18n} = require('../../modules/chui_i18n/chui_i18n');
|
|
|
43
43
|
class ActionSheet {
|
|
44
44
|
static STYLE = {
|
|
45
45
|
DEFAULT: "default",
|
|
46
|
-
DESTRUCTIVE: "destructive"
|
|
46
|
+
DESTRUCTIVE: "destructive",
|
|
47
47
|
};
|
|
48
48
|
static #counter = 0;
|
|
49
49
|
|
|
@@ -55,34 +55,43 @@ class ActionSheet {
|
|
|
55
55
|
#head = document.createElement("action_sheet_head");
|
|
56
56
|
#title = document.createElement("action_sheet_title");
|
|
57
57
|
#message = document.createElement("action_sheet_message");
|
|
58
|
-
#
|
|
58
|
+
#actionsNode = document.createElement("action_sheet_actions");
|
|
59
59
|
#cancel = document.createElement("button");
|
|
60
60
|
#actions = [];
|
|
61
61
|
#rows = [];
|
|
62
|
-
#
|
|
62
|
+
#cancelText = undefined;
|
|
63
63
|
#closable = true;
|
|
64
|
-
#
|
|
65
|
-
#
|
|
64
|
+
#closeOutside = true;
|
|
65
|
+
#labelledByTitle = false;
|
|
66
66
|
#chosen = undefined;
|
|
67
67
|
#a11y = undefined;
|
|
68
|
-
#
|
|
69
|
-
#
|
|
68
|
+
#onSelect = undefined;
|
|
69
|
+
#onClose = undefined;
|
|
70
70
|
isOpen = false;
|
|
71
71
|
|
|
72
|
-
constructor(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
constructor(
|
|
73
|
+
options = {
|
|
74
|
+
id: String(),
|
|
75
|
+
title: String(),
|
|
76
|
+
message: String(),
|
|
77
|
+
label: String(),
|
|
78
|
+
actions: [],
|
|
79
|
+
cancelText: String(),
|
|
80
|
+
closable: Boolean(),
|
|
81
|
+
closeOnOutsideClick: Boolean(),
|
|
82
|
+
onSelect: undefined,
|
|
83
|
+
onClose: undefined,
|
|
84
|
+
}
|
|
85
|
+
) {
|
|
77
86
|
setStyles(__dirname + "/styles.css", "chUiJS_ActionSheet");
|
|
78
87
|
ActionSheet.#counter += 1;
|
|
79
88
|
this.#id = options.id === undefined ? `chui_action_sheet_${ActionSheet.#counter}` : options.id;
|
|
80
89
|
this.#sheet.id = this.#id;
|
|
81
90
|
this.#closable = options.closable !== false;
|
|
82
|
-
if (options.closeOnOutsideClick !== undefined) this.#
|
|
83
|
-
this.#
|
|
84
|
-
this.#
|
|
85
|
-
this.#
|
|
91
|
+
if (options.closeOnOutsideClick !== undefined) this.#closeOutside = options.closeOnOutsideClick !== false;
|
|
92
|
+
this.#cancelText = options.cancelText;
|
|
93
|
+
this.#onSelect = options.onSelect;
|
|
94
|
+
this.#onClose = options.onClose;
|
|
86
95
|
|
|
87
96
|
this.#title.id = `${this.#id}_title`;
|
|
88
97
|
this.#message.id = `${this.#id}_message`;
|
|
@@ -91,21 +100,21 @@ class ActionSheet {
|
|
|
91
100
|
this.#cancel.addEventListener("click", this.#onCancelClick);
|
|
92
101
|
|
|
93
102
|
this.#group.appendChild(this.#head);
|
|
94
|
-
this.#group.appendChild(this.#
|
|
103
|
+
this.#group.appendChild(this.#actionsNode);
|
|
95
104
|
this.#panel.appendChild(this.#group);
|
|
96
105
|
this.#panel.appendChild(this.#cancel);
|
|
97
106
|
this.#sheet.appendChild(this.#panel);
|
|
98
107
|
document.body.appendChild(this.#sheet);
|
|
99
108
|
// Заголовок в разметке — имя слоя; иначе имя берётся из label или словаря
|
|
100
109
|
const title = optionOr(options.title, undefined);
|
|
101
|
-
this.#
|
|
110
|
+
this.#labelledByTitle = title !== undefined;
|
|
102
111
|
if (title !== undefined) this.setTitle(title);
|
|
103
112
|
if (optionOr(options.message, undefined) !== undefined) this.setMessage(options.message);
|
|
104
113
|
this.#a11y = new OverlayA11y(this.#sheet, {
|
|
105
114
|
role: "dialog",
|
|
106
|
-
labelNode: this.#
|
|
107
|
-
label: this.#
|
|
108
|
-
onEscape: () => this.close()
|
|
115
|
+
labelNode: this.#labelledByTitle ? this.#title : undefined,
|
|
116
|
+
label: this.#labelledByTitle ? undefined : optionOr(options.label, I18n.t("feedback.actionSheet")),
|
|
117
|
+
onEscape: () => this.close(),
|
|
109
118
|
});
|
|
110
119
|
this.#syncCancelText();
|
|
111
120
|
this.setActions(options.actions === undefined ? [] : options.actions);
|
|
@@ -116,18 +125,28 @@ class ActionSheet {
|
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
// GET
|
|
119
|
-
getId() {
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
getId() {
|
|
129
|
+
return this.#id;
|
|
130
|
+
}
|
|
131
|
+
getTitle() {
|
|
132
|
+
return this.#title.innerText;
|
|
133
|
+
}
|
|
134
|
+
getMessage() {
|
|
135
|
+
return this.#message.innerText;
|
|
136
|
+
}
|
|
122
137
|
/** Описания действий — те же объекты, что вернула фабрика `ActionSheet.Item` */
|
|
123
|
-
getActions() {
|
|
124
|
-
|
|
138
|
+
getActions() {
|
|
139
|
+
return [...this.#actions];
|
|
140
|
+
}
|
|
141
|
+
getCancelText() {
|
|
142
|
+
return this.#cancel.innerText;
|
|
143
|
+
}
|
|
125
144
|
|
|
126
145
|
// SET
|
|
127
146
|
setTitle(text = String()) {
|
|
128
147
|
this.#title.innerText = text;
|
|
129
148
|
this.#syncHead();
|
|
130
|
-
if (!this.#
|
|
149
|
+
if (!this.#labelledByTitle) this.#a11y.setLabel(text);
|
|
131
150
|
return this;
|
|
132
151
|
}
|
|
133
152
|
setMessage(text = String()) {
|
|
@@ -136,7 +155,7 @@ class ActionSheet {
|
|
|
136
155
|
return this;
|
|
137
156
|
}
|
|
138
157
|
setCancelText(text = String()) {
|
|
139
|
-
this.#
|
|
158
|
+
this.#cancelText = text;
|
|
140
159
|
this.#syncCancelText();
|
|
141
160
|
return this;
|
|
142
161
|
}
|
|
@@ -144,7 +163,7 @@ class ActionSheet {
|
|
|
144
163
|
setActions(actions = []) {
|
|
145
164
|
this.#actions = [];
|
|
146
165
|
this.#rows = [];
|
|
147
|
-
this.#
|
|
166
|
+
this.#actionsNode.innerHTML = "";
|
|
148
167
|
for (const action of actions) this.#appendAction(action);
|
|
149
168
|
return this;
|
|
150
169
|
}
|
|
@@ -158,6 +177,7 @@ class ActionSheet {
|
|
|
158
177
|
this.isOpen = true;
|
|
159
178
|
this.#chosen = undefined;
|
|
160
179
|
this.#panel.style.animationName = "action_sheet_in";
|
|
180
|
+
this.#panel.dataset.motion = "enter";
|
|
161
181
|
new Animation(this.#sheet).fadeIn();
|
|
162
182
|
this.#a11y.open();
|
|
163
183
|
return this;
|
|
@@ -171,8 +191,9 @@ class ActionSheet {
|
|
|
171
191
|
this.isOpen = false;
|
|
172
192
|
this.#a11y.close();
|
|
173
193
|
this.#panel.style.animationName = "action_sheet_out";
|
|
194
|
+
this.#panel.dataset.motion = "exit";
|
|
174
195
|
new Animation(this.#sheet).fadeOut();
|
|
175
|
-
if (this.#chosen === undefined && this.#
|
|
196
|
+
if (this.#chosen === undefined && this.#onClose !== undefined) this.#onClose();
|
|
176
197
|
return this;
|
|
177
198
|
}
|
|
178
199
|
/** Полное удаление: закрыть слой, снять слушатели и убрать занавес из документа */
|
|
@@ -184,24 +205,32 @@ class ActionSheet {
|
|
|
184
205
|
this.#sheet.remove();
|
|
185
206
|
return this;
|
|
186
207
|
}
|
|
187
|
-
set() {
|
|
208
|
+
set() {
|
|
209
|
+
return this.#sheet;
|
|
210
|
+
}
|
|
188
211
|
|
|
189
212
|
/** Готовое обещание: выбор даёт описание действия (`value` — ваше поле), отмена — `undefined`.
|
|
190
213
|
* Временный слой после ответа убирается: иначе каждый вызов оставлял бы узел в документе */
|
|
191
214
|
static show(options = {}) {
|
|
192
215
|
return new Promise((resolve) => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
216
|
+
const answer = (value) => {
|
|
217
|
+
resolve(value);
|
|
218
|
+
sheet.destroy();
|
|
219
|
+
};
|
|
220
|
+
// Слой собирается один раз, и `answer` зовут только после сборки:
|
|
221
|
+
// к моменту вызова имя уже определено, поэтому здесь можно `const`
|
|
222
|
+
const sheet = new ActionSheet(
|
|
223
|
+
Object.assign({}, options, {
|
|
224
|
+
onSelect: (action, index) => {
|
|
225
|
+
if (options.onSelect !== undefined) options.onSelect(action, index);
|
|
226
|
+
answer(action);
|
|
227
|
+
},
|
|
228
|
+
onClose: () => {
|
|
229
|
+
if (options.onClose !== undefined) options.onClose();
|
|
230
|
+
answer(undefined);
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
);
|
|
205
234
|
sheet.open();
|
|
206
235
|
});
|
|
207
236
|
}
|
|
@@ -230,45 +259,45 @@ class ActionSheet {
|
|
|
230
259
|
row.addEventListener("click", () => this.#choose(action, this.#actions.indexOf(action)));
|
|
231
260
|
this.#actions.push(action);
|
|
232
261
|
this.#rows.push(row);
|
|
233
|
-
this.#
|
|
262
|
+
this.#actionsNode.appendChild(row);
|
|
234
263
|
return row;
|
|
235
264
|
}
|
|
236
265
|
#choose(action = {}, index = -1) {
|
|
237
266
|
if (action.disabled === true) return;
|
|
238
267
|
this.#chosen = action;
|
|
239
268
|
if (action.onClick !== undefined) action.onClick(action, index);
|
|
240
|
-
if (this.#
|
|
269
|
+
if (this.#onSelect !== undefined) this.#onSelect(action, index);
|
|
241
270
|
this.close();
|
|
242
271
|
}
|
|
243
272
|
#onCancelClick = () => this.close();
|
|
244
273
|
#onSheetClick = (event) => {
|
|
245
|
-
if (!this.#
|
|
274
|
+
if (!this.#closeOutside || event.target !== this.#sheet) return;
|
|
246
275
|
this.close();
|
|
247
|
-
}
|
|
276
|
+
};
|
|
248
277
|
// После анимации имя снимается: иначе следующий показ не проиграется заново
|
|
249
278
|
#onPanelAnimationEnd = (event) => {
|
|
250
279
|
if (event.target !== this.#panel) return;
|
|
251
280
|
this.#panel.style.animationName = "";
|
|
252
|
-
}
|
|
281
|
+
};
|
|
253
282
|
/** Заголовок и пояснение показываются, только когда заданы; пояснение — описание слоя */
|
|
254
283
|
#syncHead() {
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
if (
|
|
258
|
-
if (!
|
|
259
|
-
if (
|
|
260
|
-
if (!
|
|
261
|
-
this.#head.style.display =
|
|
262
|
-
if (
|
|
284
|
+
const hasTitle = this.#title.innerText.trim() !== "";
|
|
285
|
+
const hasMessage = this.#message.innerText.trim() !== "";
|
|
286
|
+
if (hasTitle && this.#title.parentElement === null) this.#head.appendChild(this.#title);
|
|
287
|
+
if (!hasTitle && this.#title.parentElement !== null) this.#title.remove();
|
|
288
|
+
if (hasMessage && this.#message.parentElement === null) this.#head.appendChild(this.#message);
|
|
289
|
+
if (!hasMessage && this.#message.parentElement !== null) this.#message.remove();
|
|
290
|
+
this.#head.style.display = hasTitle || hasMessage ? "" : "none";
|
|
291
|
+
if (hasMessage) this.#sheet.setAttribute("aria-describedby", this.#message.id);
|
|
263
292
|
else this.#sheet.removeAttribute("aria-describedby");
|
|
264
293
|
}
|
|
265
294
|
#syncCancelText() {
|
|
266
|
-
this.#cancel.innerText = this.#
|
|
295
|
+
this.#cancel.innerText = this.#cancelText === undefined ? I18n.t("common.cancel") : this.#cancelText;
|
|
267
296
|
this.#cancel.style.display = this.#closable ? "" : "none";
|
|
268
297
|
}
|
|
269
298
|
}
|
|
270
299
|
|
|
271
|
-
exports.ActionSheet = ActionSheet
|
|
300
|
+
exports.ActionSheet = ActionSheet;
|
|
272
301
|
|
|
273
302
|
// Описание действия собирает фабрика — как у ContextMenu и SplitButton
|
|
274
|
-
ActionSheet.Item = (options = {}) => Object.assign({style: ActionSheet.STYLE.DEFAULT}, options);
|
|
303
|
+
ActionSheet.Item = (options = {}) => Object.assign({ style: ActionSheet.STYLE.DEFAULT }, options);
|
|
@@ -26,7 +26,13 @@ action_sheet_panel {
|
|
|
26
26
|
width: min(480px, 100%);
|
|
27
27
|
max-height: 80vh;
|
|
28
28
|
overflow-y: auto;
|
|
29
|
-
animation: action_sheet_in var(--
|
|
29
|
+
animation: action_sheet_in var(--motion_base) var(--motion_ease);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Вид движения ставит action_sheet.js: выезд идёт базовым временем из правила выше, а уход
|
|
33
|
+
берёт короткое — закрытие не задерживает следующий экран */
|
|
34
|
+
action_sheet_panel[data-motion="exit"] {
|
|
35
|
+
animation-duration: var(--motion_fast);
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
@keyframes action_sheet_in {
|
|
@@ -68,7 +74,7 @@ action_sheet_head {
|
|
|
68
74
|
display: flex;
|
|
69
75
|
flex-direction: column;
|
|
70
76
|
gap: 2px;
|
|
71
|
-
padding: var(--control_padding) var(--
|
|
77
|
+
padding: var(--control_padding) var(--block_padding);
|
|
72
78
|
text-align: center;
|
|
73
79
|
border-bottom: 1px solid var(--border_color);
|
|
74
80
|
}
|
|
@@ -102,7 +108,7 @@ action_sheet_actions {
|
|
|
102
108
|
width: 100%;
|
|
103
109
|
box-sizing: border-box;
|
|
104
110
|
min-height: 44px;
|
|
105
|
-
padding: var(--
|
|
111
|
+
padding: var(--block_padding);
|
|
106
112
|
background: transparent;
|
|
107
113
|
border: none;
|
|
108
114
|
border-top: 1px solid var(--border_color);
|
|
@@ -121,7 +127,8 @@ action_sheet_actions .action_sheet_action:first-child {
|
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
.action_sheet_action:active {
|
|
124
|
-
background: var(--element_background_prime);
|
|
130
|
+
background: var(--element_background_prime);
|
|
131
|
+
color: var(--text_color_hover);
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
.action_sheet_action:disabled {
|
|
@@ -171,7 +178,7 @@ action_sheet_action_description {
|
|
|
171
178
|
width: 100%;
|
|
172
179
|
box-sizing: border-box;
|
|
173
180
|
min-height: 44px;
|
|
174
|
-
padding: var(--
|
|
181
|
+
padding: var(--block_padding);
|
|
175
182
|
border-radius: var(--radius_md);
|
|
176
183
|
background: var(--modal_background);
|
|
177
184
|
border: 1px solid var(--border_color);
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
const { Icon } = require("../chui_icons/icons");
|
|
2
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
3
|
+
const { Toolbar } = require("../chui_toolbar/toolbar");
|
|
4
|
+
const { optionOr, setStyles } = require("../../modules/chui_functions");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Действие панели: значок с подписью, состоянием переключателя и счётчиком.
|
|
8
|
+
*
|
|
9
|
+
* Это настоящий `<button>`: клавиатура, фокус и роль у него уже есть. Переключатель
|
|
10
|
+
* (`toggle`) объявляет себя `aria-pressed` — «закреплено», «без звука» это состояние,
|
|
11
|
+
* а не действие, и скринридер обязан его читать. Опасное действие (удалить, очистить)
|
|
12
|
+
* помечается `danger` — подписью красного цвета, без отдельной иконки-предупреждения.
|
|
13
|
+
*/
|
|
14
|
+
class ActionsBarButton {
|
|
15
|
+
static #counter = 0;
|
|
16
|
+
|
|
17
|
+
#root = document.createElement("button");
|
|
18
|
+
#iconNode = document.createElement("actions_bar_button_icon");
|
|
19
|
+
#text = document.createElement("actions_bar_button_text");
|
|
20
|
+
#badge = document.createElement("actions_bar_badge");
|
|
21
|
+
#id = undefined;
|
|
22
|
+
#label = undefined;
|
|
23
|
+
#active = false;
|
|
24
|
+
#toggle = false;
|
|
25
|
+
#disabled = false;
|
|
26
|
+
#danger = false;
|
|
27
|
+
#onClick = undefined;
|
|
28
|
+
#onToggle = undefined;
|
|
29
|
+
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
const id = optionOr(options.id, undefined);
|
|
32
|
+
this.#id = id === undefined ? String((ActionsBarButton.#counter += 1)) : String(id);
|
|
33
|
+
this.#label = options.label === undefined ? "" : String(options.label);
|
|
34
|
+
this.#toggle = options.toggle === true;
|
|
35
|
+
this.#danger = options.danger === true;
|
|
36
|
+
this.#onClick = typeof options.onClick === "function" ? options.onClick : undefined;
|
|
37
|
+
// Свой слушатель состояния — им о переключении узнаёт панель (публичного `onToggle` нет)
|
|
38
|
+
this.#onToggle = typeof options.onToggle === "function" ? options.onToggle : undefined;
|
|
39
|
+
this.#root.type = "button";
|
|
40
|
+
this.#root.className = "actions_bar_button";
|
|
41
|
+
this.#root.dataset.id = this.#id;
|
|
42
|
+
if (optionOr(options.icon, undefined) !== undefined)
|
|
43
|
+
this.#iconNode.innerHTML = new Icon(options.icon, "18px").getHTML();
|
|
44
|
+
this.#iconNode.setAttribute("aria-hidden", "true");
|
|
45
|
+
this.#text.innerText = this.#label;
|
|
46
|
+
this.#root.append(this.#iconNode, this.#text);
|
|
47
|
+
// Значка нет — кнопка подписью не обходится: имя ей даёт подсказка или словарь
|
|
48
|
+
if (this.#label === "") this.setLabel(optionOr(options.hint, I18n.t("feedback.actionsBarButton")));
|
|
49
|
+
else this.#syncName();
|
|
50
|
+
if (this.#danger === true) this.#root.dataset.danger = "true";
|
|
51
|
+
if (this.#toggle === true) this.setActive(options.active === true);
|
|
52
|
+
if (options.badge !== undefined) this.setBadge(options.badge);
|
|
53
|
+
if (options.disabled === true) this.setDisabled(true);
|
|
54
|
+
this.#root.addEventListener("click", () => {
|
|
55
|
+
if (this.#disabled === true) return;
|
|
56
|
+
if (this.#toggle === true) {
|
|
57
|
+
this.setActive(this.#active === false);
|
|
58
|
+
if (this.#onToggle !== undefined) this.#onToggle(this);
|
|
59
|
+
}
|
|
60
|
+
if (this.#onClick !== undefined) this.#onClick(this, this.#active);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getId() {
|
|
65
|
+
return this.#id;
|
|
66
|
+
}
|
|
67
|
+
getLabel() {
|
|
68
|
+
return this.#text.innerText;
|
|
69
|
+
}
|
|
70
|
+
/** Переключатель включён: «закреплено», «без звука» */
|
|
71
|
+
isActive() {
|
|
72
|
+
return this.#active;
|
|
73
|
+
}
|
|
74
|
+
isDisabled() {
|
|
75
|
+
return this.#disabled;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Состояние переключателя: видно подложкой, скринридеру — через `aria-pressed`.
|
|
80
|
+
* У обычного действия состояния нет: `setActive` на нём ничего не делает — подсветка
|
|
81
|
+
* без программного состояния читалась бы как выбранное действие
|
|
82
|
+
*/
|
|
83
|
+
setActive(boolean = Boolean()) {
|
|
84
|
+
if (this.#toggle !== true) return this;
|
|
85
|
+
this.#active = boolean === true;
|
|
86
|
+
this.#root.dataset.active = this.#active ? "true" : "false";
|
|
87
|
+
this.#root.setAttribute("aria-pressed", this.#active ? "true" : "false");
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
setDisabled(boolean = Boolean()) {
|
|
91
|
+
this.#disabled = boolean === true;
|
|
92
|
+
this.#root.disabled = this.#disabled;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
/** Счётчик действия: число показывает плашка, скринридеру оно попадает в имя */
|
|
96
|
+
setBadge(count = Number()) {
|
|
97
|
+
const value = Number(count);
|
|
98
|
+
const shown = Number.isFinite(value) && value > 0 ? String(value) : "";
|
|
99
|
+
this.#badge.innerText = shown;
|
|
100
|
+
this.#badge.hidden = shown === "";
|
|
101
|
+
if (shown !== "" && this.#badge.parentNode === null) this.#root.appendChild(this.#badge);
|
|
102
|
+
this.#syncName(shown);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
setLabel(text = String()) {
|
|
106
|
+
const label = String(text);
|
|
107
|
+
if (this.#label === "") {
|
|
108
|
+
// Кнопка без подписи: имя живёт только в атрибутах и подсказке
|
|
109
|
+
this.#root.setAttribute("aria-label", label);
|
|
110
|
+
this.#root.title = label;
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
this.#root.setAttribute("aria-label", label);
|
|
114
|
+
this.#text.innerText = label;
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
/** Имя кнопки: своя подпись или значок с подсказкой, плюс счётчик, если он есть */
|
|
118
|
+
#syncName(count = undefined) {
|
|
119
|
+
if (this.#label === "") return;
|
|
120
|
+
const shown = count === undefined ? (this.#badge.hidden ? "" : this.#badge.innerText) : count;
|
|
121
|
+
this.#root.setAttribute(
|
|
122
|
+
"aria-label",
|
|
123
|
+
shown === "" ? this.#label : `${this.#label}, ${I18n.t("feedback.actionsBarBadge", { value: shown })}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
set() {
|
|
128
|
+
return this.#root;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Панель действий — что можно сделать с тем, о чём сейчас речь.
|
|
134
|
+
*
|
|
135
|
+
* Пятая часть мессенджера: список разговоров слева, шапка разговора, **панель действий**,
|
|
136
|
+
* лента сообщений и панель ввода — так собирается экран переписки. Панель ставят и под
|
|
137
|
+
* шапкой разговора, и в карточке файла или профиля: она не полагает, о чём именно действия.
|
|
138
|
+
*
|
|
139
|
+
* Действия — подписанные кнопки со значком: «Закрепить», «Без звука», «Найти в переписке»,
|
|
140
|
+
* «Очистить историю». Часть из них — переключатели (`toggle: true`): у них есть состояние,
|
|
141
|
+
* и оно объявляется `aria-pressed`, а видно по подложке. Опасное действие помечается
|
|
142
|
+
* `danger: true` — подписью красного цвета.
|
|
143
|
+
*
|
|
144
|
+
* Панель держит `Toolbar` внутри: стрелки ←/→ ходят по действиям, Home и End ведут к краям,
|
|
145
|
+
* а Tab проходит по кнопкам по очереди — каждая из них настоящий `<button>`, как у полосы
|
|
146
|
+
* пользователя и панели ввода. Строка клавиатуры внутри панели своей кромки не рисует:
|
|
147
|
+
* панель и полоса — одна поверхность, и вложенная ступень здесь была бы лишней.
|
|
148
|
+
*
|
|
149
|
+
* ```javascript
|
|
150
|
+
* const actions = new ActionsBar({
|
|
151
|
+
* title: 'Разговор',
|
|
152
|
+
* actions: [
|
|
153
|
+
* { id: 'pin', icon: Icons.CONTENT.PUSH_PIN, label: 'Закрепить', toggle: true },
|
|
154
|
+
* { id: 'mute', icon: Icons.SOCIAL.NOTIFICATIONS_OFF, label: 'Без звука', toggle: true, active: true },
|
|
155
|
+
* { id: 'find', icon: Icons.ACTIONS.SEARCH, label: 'Найти в переписке', onClick: () => find() },
|
|
156
|
+
* { id: 'clear', icon: Icons.ACTIONS.DELETE, label: 'Очистить историю', danger: true, onClick: () => clear() },
|
|
157
|
+
* ],
|
|
158
|
+
* onChange: (button) => Log.info(button.getId(), button.isActive()),
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* Опции: `id`, `title`, `label`, `orientation`, `actions`, `onChange`.
|
|
163
|
+
* Действие: `{ id, icon, label, hint, badge, toggle, active, danger, disabled, onClick }`;
|
|
164
|
+
* фабрика — `ActionsBar.Button(...)`.
|
|
165
|
+
* API: `setTitle(text)`, `getTitle()`, `setLabel(text)`, `setOrientation(o)`, `getOrientation()`,
|
|
166
|
+
* `addAction(options)`, `removeAction(id)`, `setActions(list)`, `clear()`, `getActions()`,
|
|
167
|
+
* `getAction(id)`, `setActive(id, bool)`, `toggle(id)`, `getActive()`, `setDisabled(id, bool)`,
|
|
168
|
+
* `setBadge(id, count)`, `getToolbar()`, `set()`, `destroy()`.
|
|
169
|
+
* Константа: `ActionsBar.ORIENTATION.{HORIZONTAL, VERTICAL}`.
|
|
170
|
+
*
|
|
171
|
+
* **Строка или столбец.** По умолчанию действия идут строкой с переносом — так панель стоит
|
|
172
|
+
* под шапкой разговора. `ORIENTATION.VERTICAL` ставит их столбцом во всю ширину, как пункты
|
|
173
|
+
* меню: в таком виде панель кладут в выпадающее меню — например, в кнопку «⋯» полосы
|
|
174
|
+
* пользователя (`UserBar({ menu: [...] })`), и отдельная полоса в шапке не нужна.
|
|
175
|
+
*/
|
|
176
|
+
class ActionsBar {
|
|
177
|
+
/** Фабрика действия: `ActionsBar.Button({ icon, label, toggle, danger, onClick })` */
|
|
178
|
+
static Button = (options = {}) => new ActionsBarButton(options);
|
|
179
|
+
static ORIENTATION = Toolbar.ORIENTATION;
|
|
180
|
+
static #counter = 0;
|
|
181
|
+
|
|
182
|
+
#id = undefined;
|
|
183
|
+
#root = document.createElement("chui_actions_bar");
|
|
184
|
+
#title = document.createElement("actions_bar_title");
|
|
185
|
+
#toolbar = undefined;
|
|
186
|
+
#actions = [];
|
|
187
|
+
#label = undefined;
|
|
188
|
+
#orientation = undefined;
|
|
189
|
+
#onChange = undefined;
|
|
190
|
+
|
|
191
|
+
constructor(
|
|
192
|
+
options = {
|
|
193
|
+
id: String(),
|
|
194
|
+
title: String(),
|
|
195
|
+
label: String(),
|
|
196
|
+
orientation: String(),
|
|
197
|
+
actions: [],
|
|
198
|
+
onChange: undefined,
|
|
199
|
+
}
|
|
200
|
+
) {
|
|
201
|
+
setStyles(__dirname + "/styles.css", "chUiJS_ActionsBar");
|
|
202
|
+
ActionsBar.#counter += 1;
|
|
203
|
+
this.#id = options.id === undefined ? `chui_actions_bar_${ActionsBar.#counter}` : options.id;
|
|
204
|
+
this.#root.id = this.#id;
|
|
205
|
+
this.#label = options.label;
|
|
206
|
+
this.#onChange = typeof options.onChange === "function" ? options.onChange : undefined;
|
|
207
|
+
this.#toolbar = new Toolbar({ label: this.#labelText() });
|
|
208
|
+
this.setOrientation(
|
|
209
|
+
options.orientation === undefined ? ActionsBar.ORIENTATION.HORIZONTAL : options.orientation
|
|
210
|
+
);
|
|
211
|
+
this.#title.hidden = true;
|
|
212
|
+
this.#root.append(this.#title, this.#toolbar.set());
|
|
213
|
+
if (options.title !== undefined) this.setTitle(options.title);
|
|
214
|
+
if (Array.isArray(options.actions)) for (const action of options.actions) this.addAction(action);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// GET
|
|
218
|
+
getId() {
|
|
219
|
+
return this.#id;
|
|
220
|
+
}
|
|
221
|
+
getTitle() {
|
|
222
|
+
return this.#title.hidden ? "" : this.#title.innerText;
|
|
223
|
+
}
|
|
224
|
+
getOrientation() {
|
|
225
|
+
return this.#orientation;
|
|
226
|
+
}
|
|
227
|
+
getActions() {
|
|
228
|
+
return this.#actions.slice();
|
|
229
|
+
}
|
|
230
|
+
getAction(id = String()) {
|
|
231
|
+
return this.#actions.find((button) => button.getId() === String(id));
|
|
232
|
+
}
|
|
233
|
+
/** Идентификаторы включённых переключателей — состояние панели одним списком */
|
|
234
|
+
getActive() {
|
|
235
|
+
return this.#actions.filter((button) => button.isActive() === true).map((button) => button.getId());
|
|
236
|
+
}
|
|
237
|
+
/** Полоса действий: нужна тому, кто ставит в панель своё рядом с кнопками */
|
|
238
|
+
getToolbar() {
|
|
239
|
+
return this.#toolbar;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// SET
|
|
243
|
+
/** Подпись панели: над действиями, объясняет, к чему они относятся */
|
|
244
|
+
setTitle(text = String()) {
|
|
245
|
+
this.#title.innerText = String(text);
|
|
246
|
+
this.#title.hidden = this.#title.innerText.trim() === "";
|
|
247
|
+
return this;
|
|
248
|
+
}
|
|
249
|
+
/** Имя панели для скринридера: по умолчанию — общая подпись из словаря */
|
|
250
|
+
setLabel(text = String()) {
|
|
251
|
+
this.#label = text;
|
|
252
|
+
this.#toolbar.setLabel(this.#labelText());
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Строка или столбец: столбец — вид меню, в нём действия идут во всю ширину.
|
|
257
|
+
* Ориентацию несёт и полоса внутри (`Toolbar`), и сама панель — по ней считаются отступы
|
|
258
|
+
*/
|
|
259
|
+
setOrientation(orientation = String()) {
|
|
260
|
+
this.#orientation =
|
|
261
|
+
orientation === ActionsBar.ORIENTATION.VERTICAL
|
|
262
|
+
? ActionsBar.ORIENTATION.VERTICAL
|
|
263
|
+
: ActionsBar.ORIENTATION.HORIZONTAL;
|
|
264
|
+
this.#root.dataset.orientation = this.#orientation;
|
|
265
|
+
this.#toolbar.setOrientation(this.#orientation);
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
setActions(actions = []) {
|
|
269
|
+
this.clear();
|
|
270
|
+
for (const action of actions) this.addAction(action);
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
addAction(options = {}) {
|
|
274
|
+
const button =
|
|
275
|
+
options instanceof ActionsBarButton
|
|
276
|
+
? options
|
|
277
|
+
: new ActionsBarButton(Object.assign({}, options, { onToggle: (instance) => this.#notify(instance) }));
|
|
278
|
+
this.#actions.push(button);
|
|
279
|
+
this.#toolbar.add(button);
|
|
280
|
+
return button;
|
|
281
|
+
}
|
|
282
|
+
removeAction(id = String()) {
|
|
283
|
+
const index = this.#actions.findIndex((button) => button.getId() === String(id));
|
|
284
|
+
if (index === -1) return this;
|
|
285
|
+
const [button] = this.#actions.splice(index, 1);
|
|
286
|
+
button.set().remove();
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
clear() {
|
|
290
|
+
for (const button of this.#actions) button.set().remove();
|
|
291
|
+
this.#actions = [];
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
/** Включить или выключить переключатель из кода: состояние то же, что после щелчка */
|
|
295
|
+
setActive(id = String(), boolean = Boolean()) {
|
|
296
|
+
const button = this.getAction(id);
|
|
297
|
+
if (button === undefined || button.isActive() === boolean) return this;
|
|
298
|
+
button.setActive(boolean);
|
|
299
|
+
this.#notify(button);
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
toggle(id = String()) {
|
|
303
|
+
const button = this.getAction(id);
|
|
304
|
+
if (button === undefined) return this;
|
|
305
|
+
return this.setActive(id, button.isActive() === false);
|
|
306
|
+
}
|
|
307
|
+
setDisabled(id = String(), boolean = Boolean()) {
|
|
308
|
+
const button = this.getAction(id);
|
|
309
|
+
if (button !== undefined) button.setDisabled(boolean);
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
setBadge(id = String(), count = Number()) {
|
|
313
|
+
const button = this.getAction(id);
|
|
314
|
+
if (button !== undefined) button.setBadge(count);
|
|
315
|
+
return this;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
set() {
|
|
319
|
+
return this.#root;
|
|
320
|
+
}
|
|
321
|
+
destroy() {
|
|
322
|
+
this.#actions = [];
|
|
323
|
+
this.#onChange = undefined;
|
|
324
|
+
this.#root.remove();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// === ВНУТРЕННЕЕ ===
|
|
328
|
+
/** О смене состояния сообщается один раз: и после щелчка, и после правки из кода */
|
|
329
|
+
#notify(button) {
|
|
330
|
+
if (this.#onChange !== undefined) this.#onChange(button, this);
|
|
331
|
+
}
|
|
332
|
+
#labelText() {
|
|
333
|
+
return optionOr(this.#label, undefined) === undefined ? I18n.t("feedback.actionsBar") : String(this.#label);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
exports.ActionsBar = ActionsBar;
|
|
338
|
+
exports.ActionsBarButton = ActionsBarButton;
|