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,7 +1,7 @@
|
|
|
1
|
-
const {setStyles, optionOr} = require(
|
|
2
|
-
const {TextInput} = require(
|
|
3
|
-
const {Kbd} = require(
|
|
4
|
-
const {I18n} = require(
|
|
1
|
+
const { setStyles, optionOr } = require("../../modules/chui_functions");
|
|
2
|
+
const { TextInput } = require("../chui_inputs/chui_text/text");
|
|
3
|
+
const { Kbd } = require("../chui_kbd/kbd");
|
|
4
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Шпаргалка горячих клавиш: сочетания с пояснениями, разложенные по группам.
|
|
@@ -41,35 +41,44 @@ class ShortcutHelp {
|
|
|
41
41
|
|
|
42
42
|
#root = document.createElement("chui_shortcut_help");
|
|
43
43
|
#id = undefined;
|
|
44
|
-
#
|
|
44
|
+
#idTitle = undefined;
|
|
45
45
|
#title = document.createElement("shortcut_help_title");
|
|
46
46
|
#search = document.createElement("shortcut_help_search");
|
|
47
47
|
#input = undefined;
|
|
48
48
|
#status = document.createElement("shortcut_help_status");
|
|
49
|
-
#
|
|
49
|
+
#groupsNode = document.createElement("shortcut_help_groups");
|
|
50
50
|
#empty = document.createElement("shortcut_help_empty");
|
|
51
51
|
#source = [];
|
|
52
52
|
#groups = [];
|
|
53
|
-
#
|
|
54
|
-
#
|
|
53
|
+
#titleHidden = false;
|
|
54
|
+
#titleValue = undefined;
|
|
55
55
|
#label = undefined;
|
|
56
56
|
#columns = 1;
|
|
57
57
|
#searchable = false;
|
|
58
58
|
#placeholder = undefined;
|
|
59
|
-
#
|
|
60
|
-
#
|
|
59
|
+
#emptyText = undefined;
|
|
60
|
+
#kbdSize = Kbd.SIZE.DEFAULT;
|
|
61
61
|
|
|
62
|
-
constructor(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
constructor(
|
|
63
|
+
options = {
|
|
64
|
+
id: String(),
|
|
65
|
+
title: undefined,
|
|
66
|
+
groups: [],
|
|
67
|
+
items: [],
|
|
68
|
+
columns: Number(),
|
|
69
|
+
searchable: Boolean(),
|
|
70
|
+
searchPlaceholder: String(),
|
|
71
|
+
emptyText: String(),
|
|
72
|
+
label: String(),
|
|
73
|
+
kbdSize: String(),
|
|
74
|
+
}
|
|
75
|
+
) {
|
|
67
76
|
setStyles(__dirname + "/styles.css", "chUiJS_ShortcutHelp");
|
|
68
77
|
ShortcutHelp.#counter += 1;
|
|
69
78
|
this.#id = optionOr(options.id, `chui_shortcut_help_${ShortcutHelp.#counter}`);
|
|
70
|
-
this.#
|
|
79
|
+
this.#idTitle = `${this.#id}_title`;
|
|
71
80
|
this.#root.id = this.#id;
|
|
72
|
-
this.#title.id = this.#
|
|
81
|
+
this.#title.id = this.#idTitle;
|
|
73
82
|
// Заголовок — настоящий заголовок: к шпаргалке можно перейти навигацией по заголовкам
|
|
74
83
|
this.#title.setAttribute("role", "heading");
|
|
75
84
|
this.#title.setAttribute("aria-level", "2");
|
|
@@ -80,16 +89,16 @@ class ShortcutHelp {
|
|
|
80
89
|
this.#root.appendChild(this.#title);
|
|
81
90
|
this.#root.appendChild(this.#search);
|
|
82
91
|
this.#root.appendChild(this.#status);
|
|
83
|
-
this.#root.appendChild(this.#
|
|
92
|
+
this.#root.appendChild(this.#groupsNode);
|
|
84
93
|
this.#root.appendChild(this.#empty);
|
|
85
94
|
this.#label = options.label;
|
|
86
|
-
this.#
|
|
87
|
-
this.#
|
|
95
|
+
this.#titleHidden = options.title === null;
|
|
96
|
+
this.#titleValue = typeof options.title === "string" && options.title !== "" ? options.title : undefined;
|
|
88
97
|
this.#placeholder = options.searchPlaceholder;
|
|
89
|
-
this.#
|
|
90
|
-
if (options.kbdSize !== undefined) this.#
|
|
98
|
+
this.#emptyText = options.emptyText;
|
|
99
|
+
if (options.kbdSize !== undefined) this.#kbdSize = options.kbdSize;
|
|
91
100
|
this.setColumns(options.columns === undefined ? 1 : options.columns);
|
|
92
|
-
this.setTitle(this.#
|
|
101
|
+
this.setTitle(this.#titleHidden ? null : this.#titleValue);
|
|
93
102
|
if (options.searchable === true) this.setSearchable(true);
|
|
94
103
|
else this.#syncSearch();
|
|
95
104
|
this.#source = ShortcutHelp.#normalizeSource(options);
|
|
@@ -103,14 +112,18 @@ class ShortcutHelp {
|
|
|
103
112
|
|
|
104
113
|
/** Заголовок на экране (`""`, если он убран) */
|
|
105
114
|
getTitle() {
|
|
106
|
-
return this.#
|
|
115
|
+
return this.#titleHidden ? "" : this.#title.innerText;
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
/** Группы со строками: `[{title, items: [{keys, description, label}]}]` */
|
|
110
119
|
getGroups() {
|
|
111
120
|
return this.#source.map((group) => ({
|
|
112
121
|
title: group.title,
|
|
113
|
-
items: group.items.map((item) => ({
|
|
122
|
+
items: group.items.map((item) => ({
|
|
123
|
+
keys: [...item.keys],
|
|
124
|
+
description: item.description,
|
|
125
|
+
label: item.label,
|
|
126
|
+
})),
|
|
114
127
|
}));
|
|
115
128
|
}
|
|
116
129
|
|
|
@@ -129,7 +142,10 @@ class ShortcutHelp {
|
|
|
129
142
|
|
|
130
143
|
/** Сколько строк видно с учётом фильтра */
|
|
131
144
|
getVisibleCount() {
|
|
132
|
-
return this.#groups.reduce(
|
|
145
|
+
return this.#groups.reduce(
|
|
146
|
+
(sum, group) => sum + group.rows.filter((row) => row.node.hidden !== true).length,
|
|
147
|
+
0
|
|
148
|
+
);
|
|
133
149
|
}
|
|
134
150
|
|
|
135
151
|
// SET
|
|
@@ -141,10 +157,10 @@ class ShortcutHelp {
|
|
|
141
157
|
|
|
142
158
|
/** Заголовок; `null` убирает его (тогда блок называется по `label`) */
|
|
143
159
|
setTitle(text = undefined) {
|
|
144
|
-
this.#
|
|
145
|
-
this.#
|
|
146
|
-
this.#title.innerText = this.#
|
|
147
|
-
this.#title.hidden = this.#
|
|
160
|
+
this.#titleHidden = text === null;
|
|
161
|
+
this.#titleValue = typeof text === "string" && text !== "" ? text : undefined;
|
|
162
|
+
this.#title.innerText = this.#titleHidden ? "" : optionOr(this.#titleValue, I18n.t("navigation.shortcutHelp"));
|
|
163
|
+
this.#title.hidden = this.#titleHidden;
|
|
148
164
|
this.#syncAria();
|
|
149
165
|
return this;
|
|
150
166
|
}
|
|
@@ -172,12 +188,15 @@ class ShortcutHelp {
|
|
|
172
188
|
placeholder: optionOr(this.#placeholder, I18n.t("navigation.shortcutHelpSearch")),
|
|
173
189
|
width: "100%",
|
|
174
190
|
// Слушатель обновляет и строки, и объявление найденного числа
|
|
175
|
-
inputListener: () => this.#syncSearch()
|
|
191
|
+
inputListener: () => this.#syncSearch(),
|
|
176
192
|
});
|
|
177
193
|
// Видимой подписи у поля нет, а пл҃ейсхолдер — не имя: имя ставим явно
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
|
|
194
|
+
const searchField = this.#input.set().querySelector("input");
|
|
195
|
+
if (searchField !== null) {
|
|
196
|
+
searchField.setAttribute(
|
|
197
|
+
"aria-label",
|
|
198
|
+
optionOr(this.#placeholder, I18n.t("navigation.shortcutHelpSearch"))
|
|
199
|
+
);
|
|
181
200
|
}
|
|
182
201
|
this.#search.appendChild(this.#input.set());
|
|
183
202
|
}
|
|
@@ -189,7 +208,7 @@ class ShortcutHelp {
|
|
|
189
208
|
|
|
190
209
|
/** Заменить содержимое целиком */
|
|
191
210
|
setGroups(groups = []) {
|
|
192
|
-
this.#source = ShortcutHelp.#normalizeSource({groups: groups});
|
|
211
|
+
this.#source = ShortcutHelp.#normalizeSource({ groups: groups });
|
|
193
212
|
this.#render();
|
|
194
213
|
return this;
|
|
195
214
|
}
|
|
@@ -197,7 +216,7 @@ class ShortcutHelp {
|
|
|
197
216
|
/** Добавить группу: `add({title: 'Правка', items: [...]})` */
|
|
198
217
|
addGroup(group = undefined) {
|
|
199
218
|
if (group === undefined || group === null) return this;
|
|
200
|
-
this.#source = ShortcutHelp.#normalizeSource({groups: [...this.#source, group]});
|
|
219
|
+
this.#source = ShortcutHelp.#normalizeSource({ groups: [...this.#source, group] });
|
|
201
220
|
this.#render();
|
|
202
221
|
return this;
|
|
203
222
|
}
|
|
@@ -217,46 +236,49 @@ class ShortcutHelp {
|
|
|
217
236
|
/** `groups` плюс короткая форма `items` (одна группа без заголовка) */
|
|
218
237
|
static #normalizeSource(options = {}) {
|
|
219
238
|
const groups = [];
|
|
220
|
-
const items = (options.items === undefined ? [] : options.items).map((item) =>
|
|
221
|
-
|
|
239
|
+
const items = (options.items === undefined ? [] : options.items).map((item) =>
|
|
240
|
+
ShortcutHelp.#normalizeItem(item)
|
|
241
|
+
);
|
|
242
|
+
if (items.length > 0) groups.push({ title: "", items: items });
|
|
222
243
|
for (const group of options.groups === undefined ? [] : options.groups) {
|
|
223
244
|
groups.push({
|
|
224
245
|
title: group.title === undefined ? "" : String(group.title),
|
|
225
|
-
items: (group.items === undefined ? [] : group.items).map((item) => ShortcutHelp.#normalizeItem(item))
|
|
246
|
+
items: (group.items === undefined ? [] : group.items).map((item) => ShortcutHelp.#normalizeItem(item)),
|
|
226
247
|
});
|
|
227
248
|
}
|
|
228
249
|
return groups;
|
|
229
250
|
}
|
|
230
251
|
|
|
231
252
|
static #normalizeItem(item = {}) {
|
|
232
|
-
const keys =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
.
|
|
236
|
-
|
|
253
|
+
const keys =
|
|
254
|
+
item.keys === undefined || item.keys === null
|
|
255
|
+
? []
|
|
256
|
+
: (Array.isArray(item.keys) ? item.keys : String(item.keys).split("+"))
|
|
257
|
+
.map((key) => String(key).trim())
|
|
258
|
+
.filter((key) => key.length > 0);
|
|
237
259
|
return {
|
|
238
260
|
keys: keys,
|
|
239
261
|
description: item.description === undefined ? "" : String(item.description),
|
|
240
|
-
label: item.label
|
|
262
|
+
label: item.label,
|
|
241
263
|
};
|
|
242
264
|
}
|
|
243
265
|
|
|
244
266
|
#render() {
|
|
245
|
-
this.#
|
|
267
|
+
this.#groupsNode.innerHTML = "";
|
|
246
268
|
this.#groups = [];
|
|
247
269
|
this.#source.forEach((group, index) => {
|
|
248
270
|
const node = document.createElement("div");
|
|
249
271
|
node.className = "shortcut_help_group";
|
|
250
|
-
const
|
|
272
|
+
const idGroupTitle = `${this.#id}_group_${index}`;
|
|
251
273
|
node.setAttribute("role", "group");
|
|
252
274
|
const title = document.createElement("shortcut_help_group_title");
|
|
253
|
-
title.id =
|
|
275
|
+
title.id = idGroupTitle;
|
|
254
276
|
title.setAttribute("role", "heading");
|
|
255
277
|
title.setAttribute("aria-level", "3");
|
|
256
278
|
title.innerText = group.title;
|
|
257
279
|
title.hidden = group.title === "";
|
|
258
280
|
node.appendChild(title);
|
|
259
|
-
if (group.title !== "") node.setAttribute("aria-labelledby",
|
|
281
|
+
if (group.title !== "") node.setAttribute("aria-labelledby", idGroupTitle);
|
|
260
282
|
const list = document.createElement("ul");
|
|
261
283
|
list.className = "shortcut_help_list";
|
|
262
284
|
const rows = [];
|
|
@@ -270,7 +292,7 @@ class ShortcutHelp {
|
|
|
270
292
|
if (item.keys.length > 0) {
|
|
271
293
|
const keys = document.createElement("shortcut_help_keys");
|
|
272
294
|
// Сочетание рисует Kbd: клавиша — настоящий <kbd>, значковым нужен label
|
|
273
|
-
keys.appendChild(new Kbd({keys: item.keys, size: this.#
|
|
295
|
+
keys.appendChild(new Kbd({ keys: item.keys, size: this.#kbdSize, label: item.label }).set());
|
|
274
296
|
line.appendChild(keys);
|
|
275
297
|
}
|
|
276
298
|
const text = document.createElement("shortcut_help_text");
|
|
@@ -278,13 +300,13 @@ class ShortcutHelp {
|
|
|
278
300
|
line.appendChild(text);
|
|
279
301
|
row.appendChild(line);
|
|
280
302
|
list.appendChild(row);
|
|
281
|
-
rows.push({node: row, search: `${item.description} ${item.keys.join(" ")}`.toLowerCase()});
|
|
303
|
+
rows.push({ node: row, search: `${item.description} ${item.keys.join(" ")}`.toLowerCase() });
|
|
282
304
|
}
|
|
283
305
|
node.appendChild(list);
|
|
284
|
-
this.#
|
|
285
|
-
this.#groups.push({node: node, rows: rows});
|
|
306
|
+
this.#groupsNode.appendChild(node);
|
|
307
|
+
this.#groups.push({ node: node, rows: rows });
|
|
286
308
|
});
|
|
287
|
-
this.#empty.innerText = optionOr(this.#
|
|
309
|
+
this.#empty.innerText = optionOr(this.#emptyText, I18n.t("navigation.shortcutHelpEmpty"));
|
|
288
310
|
this.#syncSearch();
|
|
289
311
|
this.#syncAria();
|
|
290
312
|
}
|
|
@@ -304,7 +326,7 @@ class ShortcutHelp {
|
|
|
304
326
|
visible += shown;
|
|
305
327
|
}
|
|
306
328
|
const nothing = visible === 0 && this.#groups.length > 0;
|
|
307
|
-
this.#
|
|
329
|
+
this.#groupsNode.hidden = nothing;
|
|
308
330
|
this.#empty.hidden = !nothing;
|
|
309
331
|
return visible;
|
|
310
332
|
}
|
|
@@ -313,15 +335,14 @@ class ShortcutHelp {
|
|
|
313
335
|
#syncSearch() {
|
|
314
336
|
this.#search.hidden = this.#searchable !== true;
|
|
315
337
|
const visible = this.#filter(this.#input === undefined ? "" : this.#input.getValue());
|
|
316
|
-
this.#status.innerText =
|
|
317
|
-
? I18n.t("navigation.shortcutHelpCount", {count: visible})
|
|
318
|
-
: "";
|
|
338
|
+
this.#status.innerText =
|
|
339
|
+
this.#searchable === true ? I18n.t("navigation.shortcutHelpCount", { count: visible }) : "";
|
|
319
340
|
// Без поиска пустая строка не показывается: скрывать нечего
|
|
320
341
|
if (this.#searchable !== true) this.#empty.hidden = true;
|
|
321
342
|
}
|
|
322
343
|
|
|
323
344
|
#syncAria() {
|
|
324
|
-
if (this.#
|
|
345
|
+
if (this.#titleHidden) {
|
|
325
346
|
this.#root.setAttribute("role", "group");
|
|
326
347
|
this.#root.removeAttribute("aria-labelledby");
|
|
327
348
|
this.#root.setAttribute("aria-label", optionOr(this.#label, I18n.t("navigation.shortcutHelp")));
|
|
@@ -329,8 +350,8 @@ class ShortcutHelp {
|
|
|
329
350
|
}
|
|
330
351
|
this.#root.setAttribute("role", "region");
|
|
331
352
|
this.#root.removeAttribute("aria-label");
|
|
332
|
-
this.#root.setAttribute("aria-labelledby", this.#
|
|
353
|
+
this.#root.setAttribute("aria-labelledby", this.#idTitle);
|
|
333
354
|
}
|
|
334
355
|
}
|
|
335
356
|
|
|
336
|
-
exports.ShortcutHelp = ShortcutHelp
|
|
357
|
+
exports.ShortcutHelp = ShortcutHelp;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
const {setStyles} = require("../../modules/chui_functions");
|
|
2
|
-
const {
|
|
3
|
-
const {
|
|
1
|
+
const { setStyles } = require("../../modules/chui_functions");
|
|
2
|
+
const { reducedMotion } = require("../../modules/chui_animations/motion");
|
|
3
|
+
const { Icon, Icons } = require("../chui_icons/icons");
|
|
4
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
4
5
|
|
|
5
6
|
/** Служебные ключи пункта: всё остальное — данные пункта */
|
|
6
7
|
const SERVICE_KEYS = ["id", "type", "title", "icon", "badge", "children", "disabled", "href"];
|
|
@@ -61,33 +62,35 @@ class SidebarNav {
|
|
|
61
62
|
#collapsed = false;
|
|
62
63
|
#collapsible = false;
|
|
63
64
|
#id = undefined;
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
65
|
+
#titleText = undefined;
|
|
66
|
+
#labelText = undefined;
|
|
67
|
+
#onSelect = undefined;
|
|
68
|
+
#onToggle = undefined;
|
|
68
69
|
#listeners = [];
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
constructor(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
#activeId = undefined;
|
|
71
|
+
#focusId = undefined;
|
|
72
|
+
|
|
73
|
+
constructor(
|
|
74
|
+
options = {
|
|
75
|
+
id: String(),
|
|
76
|
+
title: String(),
|
|
77
|
+
label: String(),
|
|
78
|
+
items: Array(),
|
|
79
|
+
value: String(),
|
|
80
|
+
collapsible: Boolean(),
|
|
81
|
+
collapsed: Boolean(),
|
|
82
|
+
onSelect: undefined,
|
|
83
|
+
onToggle: undefined,
|
|
84
|
+
changeEvent: undefined,
|
|
85
|
+
}
|
|
86
|
+
) {
|
|
84
87
|
setStyles(__dirname + "/styles.css", "chUiJS_SidebarNav");
|
|
85
88
|
SidebarNav.#counter += 1;
|
|
86
89
|
this.#id = options.id === undefined ? `chui_sidebar_nav_${SidebarNav.#counter}` : options.id;
|
|
87
90
|
this.#root.id = this.#id;
|
|
88
91
|
this.#root.setAttribute("role", "navigation");
|
|
89
|
-
this.#
|
|
90
|
-
this.#
|
|
92
|
+
this.#onSelect = options.onSelect;
|
|
93
|
+
this.#onToggle = options.onToggle;
|
|
91
94
|
this.#collapsible = options.collapsible === true;
|
|
92
95
|
if (options.changeEvent !== undefined) this.addChangeListener(options.changeEvent);
|
|
93
96
|
this.#title.className = "sidebar_nav_title";
|
|
@@ -116,12 +119,12 @@ class SidebarNav {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
getTitle() {
|
|
119
|
-
return this.#
|
|
122
|
+
return this.#titleText;
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
/** Доступное имя навигации, заданное в `label` */
|
|
123
126
|
getLabel() {
|
|
124
|
-
return this.#
|
|
127
|
+
return this.#labelText;
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/** Пункты деревом: `{id, title, icon, badge, disabled, expanded, children}`; разделы — `{type: "section", title, children}` */
|
|
@@ -174,7 +177,7 @@ class SidebarNav {
|
|
|
174
177
|
|
|
175
178
|
/** Подпись в шапке навигации; `undefined` её убирает */
|
|
176
179
|
setTitle(text = undefined) {
|
|
177
|
-
this.#
|
|
180
|
+
this.#titleText = text;
|
|
178
181
|
if (text === undefined || text === "") {
|
|
179
182
|
this.#title.innerText = "";
|
|
180
183
|
this.#title.hidden = true;
|
|
@@ -189,7 +192,7 @@ class SidebarNav {
|
|
|
189
192
|
|
|
190
193
|
/** Имя навигации, когда подписи в шапке нет */
|
|
191
194
|
setLabel(text = undefined) {
|
|
192
|
-
this.#
|
|
195
|
+
this.#labelText = text;
|
|
193
196
|
this.#syncLabel();
|
|
194
197
|
return this;
|
|
195
198
|
}
|
|
@@ -203,7 +206,7 @@ class SidebarNav {
|
|
|
203
206
|
let section = undefined;
|
|
204
207
|
for (const item of Array.isArray(items) ? items : []) {
|
|
205
208
|
if (item !== null && typeof item === "object" && item.type === "section") {
|
|
206
|
-
section = {type: "section", title: item.title === undefined ? "" : String(item.title), children: []};
|
|
209
|
+
section = { type: "section", title: item.title === undefined ? "" : String(item.title), children: [] };
|
|
207
210
|
this.#items.push(section);
|
|
208
211
|
continue;
|
|
209
212
|
}
|
|
@@ -274,8 +277,76 @@ class SidebarNav {
|
|
|
274
277
|
if (next === this.#expanded.has(node.id)) return this;
|
|
275
278
|
if (next) this.#expanded.add(node.id);
|
|
276
279
|
else this.#expanded.delete(node.id);
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
// Панель переключается на месте, а не пересборкой списка: новый узел не помнит прошлого
|
|
281
|
+
// состояния, поэтому и стрелка, и содержимое появлялись готовыми — без движения
|
|
282
|
+
this.#setPanel(node, next);
|
|
283
|
+
if (!silent && this.#onToggle !== undefined) this.#onToggle(this.#describe(node), next, this);
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Узел панели пункта — по её id, тому же, что в `aria-controls` пункта */
|
|
288
|
+
#panelNode(node = Object()) {
|
|
289
|
+
return this.#root.querySelector(`[id="${this.#id}_panel_${node.id}"]`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Узел пункта: он несёт `aria-expanded` (по нему поворачивается стрелка)
|
|
293
|
+
и панель */
|
|
294
|
+
#linkNode(node = Object()) {
|
|
295
|
+
return this.#links().find((link) => link.dataset.id === node.id) ?? null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Раскрытие и сворачивание панели пункта.
|
|
300
|
+
*
|
|
301
|
+
* Высоту списка ведёт компонент, а время и кривую берут токены движения из стилей: высоту
|
|
302
|
+
* кадром не задать — её знает только раскладка, поэтому её считает JS и переводит переходом.
|
|
303
|
+
* `overflow` на время движения прячет содержимое, которое ещё не помещается в панель,
|
|
304
|
+
* а после снимается: иначе вложенные списки и счётчики обрезались бы по краю.
|
|
305
|
+
*/
|
|
306
|
+
#setPanel(node = Object(), open = Boolean()) {
|
|
307
|
+
const panel = this.#panelNode(node);
|
|
308
|
+
const link = this.#linkNode(node);
|
|
309
|
+
// В свёрнутой навигации подписи скрыты, поэтому пункт всегда заявляет себя свёрнутым
|
|
310
|
+
if (link !== null) link.setAttribute("aria-expanded", open && !this.#collapsed ? "true" : "false");
|
|
311
|
+
if (panel === null) return this;
|
|
312
|
+
if (open) {
|
|
313
|
+
panel.hidden = false;
|
|
314
|
+
this.#slidePanel(panel, 0, panel.scrollHeight);
|
|
315
|
+
} else {
|
|
316
|
+
this.#slidePanel(panel, panel.scrollHeight, 0, () => {
|
|
317
|
+
panel.hidden = true;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
this.#applyStop();
|
|
321
|
+
return this;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Перевод высоты панели от `from` к `to`; `done` — по концу перехода */
|
|
325
|
+
#slidePanel(panel = undefined, from = Number(), to = Number(), done = () => {}) {
|
|
326
|
+
const finish = (event) => {
|
|
327
|
+
if (event !== undefined && event.propertyName !== "max-height") return;
|
|
328
|
+
panel.removeEventListener("transitionend", finish);
|
|
329
|
+
panel.style.removeProperty("max-height");
|
|
330
|
+
panel.style.removeProperty("opacity");
|
|
331
|
+
panel.style.removeProperty("overflow");
|
|
332
|
+
done();
|
|
333
|
+
};
|
|
334
|
+
// Уменьшенное движение: состояние применяется сразу — перехода не будет,
|
|
335
|
+
// и `transitionend` не придёт
|
|
336
|
+
if (reducedMotion()) {
|
|
337
|
+
finish();
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
panel.style.overflow = "hidden";
|
|
341
|
+
panel.style.maxHeight = `${from}px`;
|
|
342
|
+
panel.style.opacity = from === 0 ? "0" : "1";
|
|
343
|
+
panel.addEventListener("transitionend", finish);
|
|
344
|
+
// Следующий кадр — уже с конечными значениями: в том же кадре браузер не увидел бы
|
|
345
|
+
// перехода, и панель встала бы готовой
|
|
346
|
+
requestAnimationFrame(() => {
|
|
347
|
+
panel.style.maxHeight = `${to}px`;
|
|
348
|
+
panel.style.opacity = to === 0 ? "0" : "1";
|
|
349
|
+
});
|
|
279
350
|
return this;
|
|
280
351
|
}
|
|
281
352
|
|
|
@@ -285,7 +356,10 @@ class SidebarNav {
|
|
|
285
356
|
this.#root.classList.toggle("sidebar_nav_collapsed", this.#collapsed);
|
|
286
357
|
if (this.#collapse !== undefined) {
|
|
287
358
|
this.#collapse.setAttribute("aria-expanded", this.#collapsed ? "false" : "true");
|
|
288
|
-
this.#collapse.setAttribute(
|
|
359
|
+
this.#collapse.setAttribute(
|
|
360
|
+
"aria-label",
|
|
361
|
+
this.#collapsed ? I18n.t("navigation.sidebarExpand") : I18n.t("navigation.sidebarCollapse")
|
|
362
|
+
);
|
|
289
363
|
}
|
|
290
364
|
this.#render();
|
|
291
365
|
return this;
|
|
@@ -319,14 +393,14 @@ class SidebarNav {
|
|
|
319
393
|
|
|
320
394
|
// === ВНУТРЕННЕЕ ===
|
|
321
395
|
static #toNode(item = Object()) {
|
|
322
|
-
const source = typeof item === "string" ? {title: item} : item;
|
|
396
|
+
const source = typeof item === "string" ? { title: item } : item;
|
|
323
397
|
const data = {};
|
|
324
398
|
for (const key of Object.keys(source === null ? {} : source)) {
|
|
325
399
|
if (!SERVICE_KEYS.includes(key)) data[key] = source[key];
|
|
326
400
|
}
|
|
327
|
-
SidebarNav.#
|
|
401
|
+
SidebarNav.#itemCounter += 1;
|
|
328
402
|
return {
|
|
329
|
-
id: source.id === undefined ? `sidebar_nav_item_${SidebarNav.#
|
|
403
|
+
id: source.id === undefined ? `sidebar_nav_item_${SidebarNav.#itemCounter}` : String(source.id),
|
|
330
404
|
title: source.title === undefined ? "" : String(source.title),
|
|
331
405
|
icon: source.icon,
|
|
332
406
|
badge: source.badge === undefined ? undefined : String(source.badge),
|
|
@@ -337,17 +411,21 @@ class SidebarNav {
|
|
|
337
411
|
};
|
|
338
412
|
}
|
|
339
413
|
|
|
340
|
-
static #
|
|
414
|
+
static #itemCounter = 0;
|
|
341
415
|
|
|
342
416
|
#describeEntry(node) {
|
|
343
417
|
if (node.type === "section") {
|
|
344
|
-
return {
|
|
418
|
+
return {
|
|
419
|
+
type: "section",
|
|
420
|
+
title: node.title,
|
|
421
|
+
children: node.children.map((child) => this.#describeEntry(child)),
|
|
422
|
+
};
|
|
345
423
|
}
|
|
346
424
|
return this.#describe(node);
|
|
347
425
|
}
|
|
348
426
|
|
|
349
427
|
#describe(node) {
|
|
350
|
-
const descriptor = {id: node.id, title: node.title, icon: node.icon};
|
|
428
|
+
const descriptor = { id: node.id, title: node.title, icon: node.icon };
|
|
351
429
|
if (node.badge !== undefined) descriptor.badge = node.badge;
|
|
352
430
|
for (const key of Object.keys(node.data)) descriptor[key] = node.data[key];
|
|
353
431
|
descriptor.disabled = node.disabled;
|
|
@@ -377,18 +455,19 @@ class SidebarNav {
|
|
|
377
455
|
#siblingsOf(node) {
|
|
378
456
|
const parent = this.#parentNode(node);
|
|
379
457
|
if (parent !== undefined) return parent.children;
|
|
380
|
-
for (const entry of this.#items)
|
|
458
|
+
for (const entry of this.#items)
|
|
459
|
+
if (entry.type === "section" && entry.children.includes(node)) return entry.children;
|
|
381
460
|
return this.#items;
|
|
382
461
|
}
|
|
383
462
|
|
|
384
463
|
#accessibleName() {
|
|
385
|
-
if (this.#
|
|
386
|
-
if (this.#
|
|
464
|
+
if (this.#titleText !== undefined && this.#titleText !== "") return this.#titleText;
|
|
465
|
+
if (this.#labelText !== undefined && this.#labelText !== "") return this.#labelText;
|
|
387
466
|
return I18n.t("navigation.sidebarNav");
|
|
388
467
|
}
|
|
389
468
|
|
|
390
469
|
#syncLabel() {
|
|
391
|
-
if (this.#
|
|
470
|
+
if (this.#titleText !== undefined && this.#titleText !== "") {
|
|
392
471
|
this.#root.setAttribute("aria-labelledby", this.#title.id);
|
|
393
472
|
this.#root.removeAttribute("aria-label");
|
|
394
473
|
return;
|
|
@@ -420,7 +499,7 @@ class SidebarNav {
|
|
|
420
499
|
// Пункты пересобираются: запоминаем, где стоял фокус, чтобы вернуть его
|
|
421
500
|
const active = document.activeElement;
|
|
422
501
|
const focused = active === null || !this.#root.contains(active) ? null : active.closest(".sidebar_nav_link");
|
|
423
|
-
if (focused !== null) this.#
|
|
502
|
+
if (focused !== null) this.#focusId = focused.dataset.id;
|
|
424
503
|
this.#body.innerHTML = "";
|
|
425
504
|
this.#head.hidden = this.#title.hidden === true && this.#collapse === undefined;
|
|
426
505
|
this.#body.appendChild(this.#listsNode(this.#items));
|
|
@@ -549,13 +628,13 @@ class SidebarNav {
|
|
|
549
628
|
const list = this.#visibleLinks();
|
|
550
629
|
if (list.length === 0) return;
|
|
551
630
|
const stop =
|
|
552
|
-
list.find((link) => link.dataset.id === this.#
|
|
553
|
-
list.find((link) => link.dataset.id === this.#
|
|
631
|
+
list.find((link) => link.dataset.id === this.#activeId) ??
|
|
632
|
+
list.find((link) => link.dataset.id === this.#focusId) ??
|
|
554
633
|
list[0];
|
|
555
|
-
this.#
|
|
634
|
+
this.#activeId = stop.dataset.id;
|
|
556
635
|
for (const link of this.#links()) link.setAttribute("tabindex", link === stop ? "0" : "-1");
|
|
557
|
-
if (this.#
|
|
558
|
-
this.#
|
|
636
|
+
if (this.#focusId !== undefined && stop.dataset.id === this.#focusId) {
|
|
637
|
+
this.#focusId = undefined;
|
|
559
638
|
stop.focus();
|
|
560
639
|
}
|
|
561
640
|
return this;
|
|
@@ -564,7 +643,7 @@ class SidebarNav {
|
|
|
564
643
|
#focusLink(id = String()) {
|
|
565
644
|
const link = this.#links().find((node) => node.dataset.id === id);
|
|
566
645
|
if (link === undefined) return;
|
|
567
|
-
this.#
|
|
646
|
+
this.#activeId = id;
|
|
568
647
|
for (const other of this.#links()) other.setAttribute("tabindex", other === link ? "0" : "-1");
|
|
569
648
|
link.focus();
|
|
570
649
|
}
|
|
@@ -597,14 +676,14 @@ class SidebarNav {
|
|
|
597
676
|
const node = this.#value === undefined ? undefined : this.#index.get(this.#value);
|
|
598
677
|
const item = node === undefined ? undefined : this.#describe(node);
|
|
599
678
|
for (const listener of [...this.#listeners]) listener(item, this);
|
|
600
|
-
if (this.#
|
|
679
|
+
if (this.#onSelect !== undefined) this.#onSelect(item, this);
|
|
601
680
|
}
|
|
602
681
|
|
|
603
682
|
#currentLink(target = undefined) {
|
|
604
683
|
const link = target === undefined ? null : target.closest(".sidebar_nav_link");
|
|
605
684
|
if (link !== null && link !== undefined) return link;
|
|
606
|
-
const id = this.#
|
|
607
|
-
return id === undefined ? null : this.#links().find((node) => node.dataset.id === id) ?? null;
|
|
685
|
+
const id = this.#activeId;
|
|
686
|
+
return id === undefined ? null : (this.#links().find((node) => node.dataset.id === id) ?? null);
|
|
608
687
|
}
|
|
609
688
|
|
|
610
689
|
#onClick = (event) => {
|
|
@@ -663,4 +742,4 @@ class SidebarNav {
|
|
|
663
742
|
};
|
|
664
743
|
}
|
|
665
744
|
|
|
666
|
-
exports.SidebarNav = SidebarNav
|
|
745
|
+
exports.SidebarNav = SidebarNav;
|
|
@@ -8,9 +8,9 @@ chui_sidebar_nav {
|
|
|
8
8
|
width: 260px;
|
|
9
9
|
max-width: 100%;
|
|
10
10
|
height: max-content;
|
|
11
|
-
margin: var(--
|
|
11
|
+
margin: var(--block_margin);
|
|
12
12
|
border: 1px solid var(--border_color);
|
|
13
|
-
border-radius: var(--
|
|
13
|
+
border-radius: var(--radius_sm);
|
|
14
14
|
background: var(--element_background_2);
|
|
15
15
|
color: var(--text_color);
|
|
16
16
|
}
|
|
@@ -223,7 +223,7 @@ chui_sidebar_nav {
|
|
|
223
223
|
.sidebar_nav_arrow chui_icon {
|
|
224
224
|
color: inherit;
|
|
225
225
|
font-size: 18px;
|
|
226
|
-
transition: transform var(--
|
|
226
|
+
transition: transform var(--motion_base) var(--motion_ease);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
/* Раскрытие показывает стрелка: состояние берётся из aria-expanded самого пункта */
|
|
@@ -238,6 +238,12 @@ chui_sidebar_nav {
|
|
|
238
238
|
.sidebar_nav_panel {
|
|
239
239
|
display: block;
|
|
240
240
|
padding-inline-start: 14px;
|
|
241
|
+
/* Высоту списка ведёт компонент (`max-height` инлайном: её знает только раскладка),
|
|
242
|
+
а время и кривую берут токены движения. Свойство `transition` на элементе заменяет
|
|
243
|
+
общий список целиком — у самой панели других переходов нет, список короткий и намеренный */
|
|
244
|
+
transition:
|
|
245
|
+
max-height var(--motion_base) var(--motion_ease),
|
|
246
|
+
opacity var(--motion_base) var(--motion_ease);
|
|
241
247
|
}
|
|
242
248
|
|
|
243
249
|
.sidebar_nav_panel[hidden] {
|