chuijs 4.0.3 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +536 -0
- package/framework/appLayout/default/chui_app_layout.js +361 -303
- package/framework/appLayout/default/global_style.css +243 -89
- package/framework/appLayout/default/main_theme_style.css +188 -101
- package/framework/components/chui_accordion/accordion.js +17 -17
- package/framework/components/chui_accordion/styles.css +7 -7
- package/framework/components/chui_action_sheet/action_sheet.js +35 -32
- package/framework/components/chui_action_sheet/styles.css +10 -4
- package/framework/components/chui_actions_bar/actions_bar.js +338 -0
- package/framework/components/chui_actions_bar/styles.css +139 -0
- package/framework/components/chui_affix/affix.js +4 -4
- package/framework/components/chui_affix/styles.css +5 -0
- package/framework/components/chui_alert/alert.js +11 -5
- package/framework/components/chui_alert/styles.css +7 -0
- package/framework/components/chui_avatar/avatar.js +13 -13
- package/framework/components/chui_avatar/styles.css +1 -1
- package/framework/components/chui_avatar_group/avatar_group.js +4 -4
- package/framework/components/chui_avatar_group/styles.css +1 -1
- package/framework/components/chui_backtop/backtop.js +25 -2
- package/framework/components/chui_backtop/styles.css +21 -0
- package/framework/components/chui_badge/badge.js +15 -15
- package/framework/components/chui_badge/styles.css +6 -6
- package/framework/components/chui_banner/banner.js +222 -0
- package/framework/components/chui_banner/styles.css +170 -0
- package/framework/components/chui_breadcrumb/styles.css +1 -1
- package/framework/components/chui_button/button.js +24 -28
- package/framework/components/chui_button/styles.css +5 -5
- package/framework/components/chui_calendar/calendar.js +6 -6
- package/framework/components/chui_card/card.js +14 -14
- package/framework/components/chui_card_scroll/card_scroll.js +169 -0
- package/framework/components/chui_card_scroll/styles.css +86 -0
- package/framework/components/chui_chat/chat.js +73 -51
- package/framework/components/chui_chat/styles.css +18 -7
- package/framework/components/chui_clipboard_history/clipboard_history.js +25 -20
- package/framework/components/chui_clipboard_history/styles.css +10 -8
- package/framework/components/chui_code/code.js +8 -8
- package/framework/components/chui_code/styles.css +2 -2
- package/framework/components/chui_code_editor/code_editor.js +26 -26
- package/framework/components/chui_code_editor/styles.css +3 -3
- package/framework/components/chui_command_palette/command_palette.js +29 -29
- package/framework/components/chui_command_palette/styles.css +5 -5
- package/framework/components/chui_console/console.js +9 -9
- package/framework/components/chui_console/styles.css +5 -2
- package/framework/components/chui_content_block/content_block.js +2 -2
- package/framework/components/chui_context_menu/context_menu.js +162 -54
- package/framework/components/chui_context_menu/styles.css +82 -8
- package/framework/components/chui_custom_element/custom_element.js +11 -11
- package/framework/components/chui_data_grid/data_grid.js +268 -268
- package/framework/components/chui_data_grid/styles.css +14 -14
- package/framework/components/chui_descriptions/styles.css +1 -1
- package/framework/components/chui_details/details.js +12 -12
- package/framework/components/chui_details/styles.css +7 -7
- package/framework/components/chui_diff_viewer/diff_viewer.js +88 -88
- package/framework/components/chui_diff_viewer/styles.css +9 -9
- package/framework/components/chui_divider/divider.js +6 -6
- package/framework/components/chui_divider/styles.css +2 -2
- package/framework/components/chui_drawer/drawer.js +37 -14
- package/framework/components/chui_drawer/styles.css +18 -4
- package/framework/components/chui_drop_zone/drop_zone.js +6 -6
- package/framework/components/chui_drop_zone/styles.css +10 -6
- package/framework/components/chui_fieldset/fieldset.js +6 -6
- package/framework/components/chui_fieldset/styles.css +5 -5
- package/framework/components/chui_file_explorer/file_explorer.js +115 -115
- package/framework/components/chui_file_explorer/styles.css +6 -6
- package/framework/components/chui_form/form.js +11 -11
- package/framework/components/chui_gantt/gantt.js +41 -41
- package/framework/components/chui_gantt/styles.css +5 -5
- package/framework/components/chui_graphs/graphs.js +82 -46
- package/framework/components/chui_graphs/styles.css +26 -5
- package/framework/components/chui_hero/hero.js +63 -20
- package/framework/components/chui_hero/styles.css +176 -7
- package/framework/components/chui_html_block/html_block.js +5 -5
- package/framework/components/chui_html_block/styles.css +1 -1
- package/framework/components/chui_hx/styles.css +2 -2
- package/framework/components/chui_icons/icons.js +7 -7
- package/framework/components/chui_icons/styles.css +2 -1
- package/framework/components/chui_inputs/chui_check_box/styles.css +12 -3
- package/framework/components/chui_inputs/chui_chips/chips.js +92 -31
- package/framework/components/chui_inputs/chui_chips/styles.css +28 -5
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +20 -15
- package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +58 -58
- package/framework/components/chui_inputs/chui_combo_box/styles.css +9 -9
- package/framework/components/chui_inputs/chui_date/date.js +93 -95
- package/framework/components/chui_inputs/chui_date/styles.css +8 -8
- package/framework/components/chui_inputs/chui_date_range/date_range.js +22 -22
- package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
- package/framework/components/chui_inputs/chui_datetime/datetime.js +12 -12
- package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
- package/framework/components/chui_inputs/chui_email/email.js +10 -10
- package/framework/components/chui_inputs/chui_email/styles.css +6 -6
- package/framework/components/chui_inputs/chui_file/file.js +8 -8
- package/framework/components/chui_inputs/chui_file/styles.css +12 -9
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +39 -39
- package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
- package/framework/components/chui_inputs/chui_masked/masked.js +44 -44
- package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
- package/framework/components/chui_inputs/chui_mention/mention.js +13 -13
- package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +147 -137
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +14 -8
- package/framework/components/chui_inputs/chui_number/number.js +42 -45
- package/framework/components/chui_inputs/chui_number/styles.css +6 -6
- package/framework/components/chui_inputs/chui_otp/otp.js +32 -32
- package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
- package/framework/components/chui_inputs/chui_password/password.js +30 -36
- package/framework/components/chui_inputs/chui_password/styles.css +7 -7
- package/framework/components/chui_inputs/chui_radio_button/styles.css +2 -2
- package/framework/components/chui_inputs/chui_rating/rating.js +28 -28
- package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
- package/framework/components/chui_inputs/chui_select_box/select_box.js +54 -54
- package/framework/components/chui_inputs/chui_select_box/styles.css +10 -10
- package/framework/components/chui_inputs/chui_slider/slider.js +14 -14
- package/framework/components/chui_inputs/chui_slider/styles.css +5 -4
- package/framework/components/chui_inputs/chui_text/styles.css +7 -7
- package/framework/components/chui_inputs/chui_text/text.js +13 -17
- package/framework/components/chui_inputs/chui_text_area/styles.css +8 -8
- package/framework/components/chui_inputs/chui_text_area/text_area.js +23 -23
- package/framework/components/chui_inputs/chui_time/styles.css +13 -17
- package/framework/components/chui_inputs/chui_time/time.js +35 -24
- package/framework/components/chui_inputs/chui_toggle/styles.css +10 -3
- package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
- package/framework/components/chui_json_editor/json_editor.js +87 -87
- package/framework/components/chui_json_editor/styles.css +5 -5
- package/framework/components/chui_json_viewer/json_viewer.js +80 -80
- package/framework/components/chui_json_viewer/styles.css +6 -6
- package/framework/components/chui_kanban/kanban.js +27 -27
- package/framework/components/chui_kanban/styles.css +8 -4
- package/framework/components/chui_kbd/styles.css +1 -1
- package/framework/components/chui_label/label.js +17 -17
- package/framework/components/chui_label/styles.css +1 -1
- package/framework/components/chui_link/link.js +9 -9
- package/framework/components/chui_link/styles.css +11 -4
- package/framework/components/chui_list/list.js +11 -11
- package/framework/components/chui_list/styles.css +2 -2
- package/framework/components/chui_markdown_editor/markdown_editor.js +66 -66
- package/framework/components/chui_markdown_editor/styles.css +5 -5
- package/framework/components/chui_media/audio.js +240 -246
- package/framework/components/chui_media/audio_styles.css +41 -23
- package/framework/components/chui_media/image.js +49 -46
- package/framework/components/chui_media/image_compare.js +35 -35
- package/framework/components/chui_media/image_compare_styles.css +5 -5
- package/framework/components/chui_media/image_styles.css +1 -1
- package/framework/components/chui_media/video.js +148 -148
- package/framework/components/chui_media/video_styles.css +27 -11
- package/framework/components/chui_menu_bar/menu_bar.js +54 -54
- package/framework/components/chui_menu_bar/styles.css +10 -10
- package/framework/components/chui_message_bar/message_bar.js +551 -0
- package/framework/components/chui_message_bar/styles.css +248 -0
- package/framework/components/chui_modal/modal.js +37 -13
- package/framework/components/chui_notification/notification.js +76 -54
- package/framework/components/chui_notification/notification_download.js +26 -81
- package/framework/components/chui_notification/notification_download_card.js +115 -0
- package/framework/components/chui_notification/notification_download_progress.css +2 -2
- package/framework/components/chui_notification/notification_download_progress.js +70 -108
- package/framework/components/chui_notification/notification_update.js +31 -31
- package/framework/components/chui_notification/status_plate.css +3 -3
- package/framework/components/chui_notification/styles.css +6 -1
- package/framework/components/chui_notification/styles_download.css +1 -1
- package/framework/components/chui_notification/styles_update.css +2 -2
- package/framework/components/chui_org_chart/org_chart.js +29 -29
- package/framework/components/chui_org_chart/styles.css +2 -2
- package/framework/components/chui_pagination/pagination.js +3 -3
- package/framework/components/chui_pagination/styles.css +1 -1
- package/framework/components/chui_paragraph/styles.css +2 -2
- package/framework/components/chui_popover/popover.js +29 -23
- package/framework/components/chui_popover/styles.css +1 -1
- package/framework/components/chui_popups/popups.js +124 -124
- package/framework/components/chui_popups/styles.css +5 -5
- package/framework/components/chui_print_button/print_button.js +15 -10
- package/framework/components/chui_progress_bar/progress_bar.js +34 -12
- package/framework/components/chui_progress_bar/styles.css +4 -4
- package/framework/components/chui_progress_ring/progress_ring.js +15 -15
- package/framework/components/chui_progress_ring/styles.css +1 -1
- package/framework/components/chui_qrcode/qrcode.js +12 -12
- package/framework/components/chui_query_builder/query_builder.js +46 -46
- package/framework/components/chui_query_builder/styles.css +4 -4
- package/framework/components/chui_radio_group/styles.css +1 -1
- package/framework/components/chui_result/result.js +15 -15
- package/framework/components/chui_result/styles.css +3 -3
- package/framework/components/chui_scheduler/scheduler.js +74 -74
- package/framework/components/chui_scheduler/styles.css +5 -5
- package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
- package/framework/components/chui_screen_spinner/styles.css +49 -0
- package/framework/components/chui_segmented_control/segmented_control.js +63 -6
- package/framework/components/chui_segmented_control/styles.css +32 -6
- package/framework/components/chui_settings/settings.js +45 -46
- package/framework/components/chui_settings/styles.css +4 -4
- package/framework/components/chui_shortcut_help/shortcut_help.js +32 -34
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +98 -29
- package/framework/components/chui_sidebar_nav/styles.css +9 -3
- package/framework/components/chui_slideshow/slideshow.js +85 -85
- package/framework/components/chui_slideshow/styles.css +6 -6
- package/framework/components/chui_spinner/spinner.js +10 -10
- package/framework/components/chui_split_button/split_button.js +30 -12
- package/framework/components/chui_split_button/styles.css +21 -5
- package/framework/components/chui_splitter/splitter.js +11 -11
- package/framework/components/chui_splitter/styles.css +2 -2
- package/framework/components/chui_statistic/statistic.js +47 -47
- package/framework/components/chui_status_bar/styles.css +2 -2
- package/framework/components/chui_stepper/stepper.js +202 -31
- package/framework/components/chui_stepper/styles.css +79 -14
- package/framework/components/chui_sticky_bar/sticky_bar.js +44 -13
- package/framework/components/chui_sticky_bar/styles.css +10 -8
- package/framework/components/chui_tabs/chui_tabs.js +74 -26
- package/framework/components/chui_tabs/styles.css +40 -12
- package/framework/components/chui_text_editor/styles.css +7 -7
- package/framework/components/chui_text_editor/text_editor.js +56 -56
- package/framework/components/chui_text_editor/text_editor_panel.js +173 -174
- package/framework/components/chui_theme_toggle/theme_toggle.js +21 -5
- package/framework/components/chui_timeline/styles.css +3 -1
- package/framework/components/chui_toast_queue/styles.css +11 -11
- package/framework/components/chui_toast_queue/toast_queue.js +30 -27
- package/framework/components/chui_toolbar/styles.css +2 -2
- package/framework/components/chui_tooltip/styles.css +2 -2
- package/framework/components/chui_tooltip/tooltip.js +3 -3
- package/framework/components/chui_tour/styles.css +8 -8
- package/framework/components/chui_tour/tour.js +72 -72
- package/framework/components/chui_transfer/styles.css +6 -6
- package/framework/components/chui_transfer/transfer.js +93 -106
- package/framework/components/chui_tree_table/styles.css +4 -4
- package/framework/components/chui_tree_table/tree_table.js +45 -45
- package/framework/components/chui_tree_view/styles.css +10 -8
- package/framework/components/chui_tree_view/tree_view.js +185 -183
- package/framework/components/chui_user_bar/styles.css +145 -0
- package/framework/components/chui_user_bar/user_bar.js +402 -0
- package/framework/components/chui_user_list_bar/styles.css +173 -0
- package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
- package/framework/components/chui_watermark/watermark.js +17 -17
- package/framework/components/chui_webview/webview.js +9 -9
- package/framework/components/chui_write_bar/styles.css +172 -0
- package/framework/components/chui_write_bar/write_bar.js +320 -0
- package/framework/main_window.js +1234 -0
- package/framework/modules/chui_a11y/chui_a11y.js +78 -79
- package/framework/modules/chui_animations/animations.js +77 -15
- package/framework/modules/chui_animations/motion.js +120 -0
- package/framework/modules/chui_animations/styles.css +36 -3
- package/framework/modules/chui_functions.js +31 -27
- package/framework/modules/chui_highlight/styles.css +13 -10
- package/framework/modules/chui_i18n/chui_i18n.js +3 -3
- package/framework/modules/chui_i18n/locales/feedback.js +62 -0
- package/framework/modules/chui_i18n/locales/navigation.js +4 -0
- package/framework/modules/chui_json/chui_json.js +2 -2
- package/framework/modules/chui_logger/chui_logger.js +23 -8
- package/framework/modules/chui_page/page.js +10 -10
- package/framework/modules/chui_palette/palette.js +61 -0
- package/framework/modules/chui_qr/chui_qr.js +23 -23
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +45 -45
- package/framework/modules/chui_scrollbar/styles.css +3 -3
- package/framework/modules/chui_services/chui_services.js +8 -8
- package/framework/modules/chui_services/desktop.js +77 -71
- package/framework/modules/chui_sortable/chui_sortable.js +17 -17
- package/framework/modules/chui_validate/chui_validate.js +3 -3
- package/framework/tray_bar/chui_menu_items.js +2 -2
- package/index.d.ts +266 -34
- package/index.js +20 -1168
- package/package.json +6 -4
- package/framework/components/telegram_bot/chui_telegram_bot.js +0 -191
|
@@ -26,16 +26,16 @@ const FOCUSABLE_SELECTOR = [
|
|
|
26
26
|
class OverlayA11y {
|
|
27
27
|
// Стек открытых слоёв: нужен, чтобы вложенный слой не снимал фон-инерт у родителя
|
|
28
28
|
static #stack = [];
|
|
29
|
-
static #
|
|
29
|
+
static #labelledCounter = 0;
|
|
30
30
|
|
|
31
31
|
#element = undefined;
|
|
32
32
|
#role = "dialog";
|
|
33
33
|
#label = undefined;
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
34
|
+
#labelNode = undefined;
|
|
35
|
+
#describedBy = undefined;
|
|
36
|
+
#onEscape = undefined;
|
|
37
|
+
#previousFocus = undefined;
|
|
38
|
+
#isOpen = false;
|
|
39
39
|
|
|
40
40
|
constructor(
|
|
41
41
|
element,
|
|
@@ -50,14 +50,14 @@ class OverlayA11y {
|
|
|
50
50
|
this.#element = element;
|
|
51
51
|
if (options.role !== undefined) this.#role = options.role;
|
|
52
52
|
this.#label = options.label;
|
|
53
|
-
this.#
|
|
54
|
-
this.#
|
|
55
|
-
this.#
|
|
53
|
+
this.#labelNode = options.labelNode;
|
|
54
|
+
this.#describedBy = options.describedBy;
|
|
55
|
+
this.#onEscape = options.onEscape;
|
|
56
56
|
this.#applyAria();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
get isOpen() {
|
|
60
|
-
return this.#
|
|
60
|
+
return this.#isOpen;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
setLabel(text = String()) {
|
|
@@ -66,18 +66,18 @@ class OverlayA11y {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
open() {
|
|
69
|
-
if (this.#
|
|
70
|
-
this.#
|
|
69
|
+
if (this.#isOpen) return;
|
|
70
|
+
this.#isOpen = true;
|
|
71
71
|
OverlayA11y.#stack.push(this);
|
|
72
|
-
this.#
|
|
72
|
+
this.#previousFocus = document.activeElement;
|
|
73
73
|
OverlayA11y.#applyInert();
|
|
74
74
|
document.addEventListener("keydown", this.#keydownEvent, true);
|
|
75
75
|
this.#focusFirst();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
close() {
|
|
79
|
-
if (!this.#
|
|
80
|
-
this.#
|
|
79
|
+
if (!this.#isOpen) return;
|
|
80
|
+
this.#isOpen = false;
|
|
81
81
|
const index = OverlayA11y.#stack.indexOf(this);
|
|
82
82
|
if (index !== -1) OverlayA11y.#stack.splice(index, 1);
|
|
83
83
|
document.removeEventListener("keydown", this.#keydownEvent, true);
|
|
@@ -89,21 +89,21 @@ class OverlayA11y {
|
|
|
89
89
|
const element = this.#element;
|
|
90
90
|
element.setAttribute("role", this.#role);
|
|
91
91
|
element.setAttribute("aria-modal", "true");
|
|
92
|
-
if (this.#
|
|
93
|
-
element.setAttribute("aria-labelledby", OverlayA11y.#ensureId(this.#
|
|
92
|
+
if (this.#labelNode !== undefined) {
|
|
93
|
+
element.setAttribute("aria-labelledby", OverlayA11y.#ensureId(this.#labelNode));
|
|
94
94
|
element.removeAttribute("aria-label");
|
|
95
95
|
} else if (this.#label !== undefined) {
|
|
96
96
|
element.setAttribute("aria-label", this.#label);
|
|
97
97
|
}
|
|
98
|
-
if (this.#
|
|
99
|
-
element.setAttribute("aria-describedby", OverlayA11y.#ensureId(this.#
|
|
98
|
+
if (this.#describedBy !== undefined) {
|
|
99
|
+
element.setAttribute("aria-describedby", OverlayA11y.#ensureId(this.#describedBy));
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
static #ensureId(element) {
|
|
104
104
|
if (element.id === undefined || element.id === "") {
|
|
105
|
-
OverlayA11y.#
|
|
106
|
-
element.id = "chui_a11y_node_" + OverlayA11y.#
|
|
105
|
+
OverlayA11y.#labelledCounter += 1;
|
|
106
|
+
element.id = "chui_a11y_node_" + OverlayA11y.#labelledCounter;
|
|
107
107
|
}
|
|
108
108
|
return element.id;
|
|
109
109
|
}
|
|
@@ -146,8 +146,8 @@ class OverlayA11y {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
#restoreFocus() {
|
|
149
|
-
const previous = this.#
|
|
150
|
-
this.#
|
|
149
|
+
const previous = this.#previousFocus;
|
|
150
|
+
this.#previousFocus = undefined;
|
|
151
151
|
if (previous !== undefined && previous !== null && document.body.contains(previous)) previous.focus();
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -177,7 +177,7 @@ class OverlayA11y {
|
|
|
177
177
|
if (event.key === "Escape") {
|
|
178
178
|
event.preventDefault();
|
|
179
179
|
event.stopPropagation();
|
|
180
|
-
if (this.#
|
|
180
|
+
if (this.#onEscape !== undefined) this.#onEscape();
|
|
181
181
|
else this.close();
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
@@ -226,17 +226,17 @@ const isDisabled = (element) =>
|
|
|
226
226
|
*/
|
|
227
227
|
class MenuKeyboard {
|
|
228
228
|
#container = undefined;
|
|
229
|
-
#
|
|
229
|
+
#itemSelector = undefined;
|
|
230
230
|
#role = "menu";
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
231
|
+
#itemRole = "menuitem";
|
|
232
|
+
#submenuSelector = undefined;
|
|
233
|
+
#onSubmenuOpen = undefined;
|
|
234
|
+
#isSubmenu = false;
|
|
235
|
+
#typeBuffer = "";
|
|
236
|
+
#typeAt = 0;
|
|
237
|
+
#onEscape = undefined;
|
|
238
238
|
#trigger = undefined;
|
|
239
|
-
#
|
|
239
|
+
#triggerBound = false;
|
|
240
240
|
|
|
241
241
|
constructor(
|
|
242
242
|
container,
|
|
@@ -251,12 +251,12 @@ class MenuKeyboard {
|
|
|
251
251
|
) {
|
|
252
252
|
this.#container = container;
|
|
253
253
|
if (options.role !== undefined) this.#role = options.role;
|
|
254
|
-
if (options.itemRole !== undefined) this.#
|
|
255
|
-
this.#
|
|
256
|
-
this.#
|
|
257
|
-
this.#
|
|
258
|
-
this.#
|
|
259
|
-
this.#
|
|
254
|
+
if (options.itemRole !== undefined) this.#itemRole = options.itemRole;
|
|
255
|
+
this.#itemSelector = options.itemSelector;
|
|
256
|
+
this.#submenuSelector = options.submenuSelector;
|
|
257
|
+
this.#onEscape = options.onEscape;
|
|
258
|
+
this.#onSubmenuOpen = options.onSubmenuOpen;
|
|
259
|
+
this.#isSubmenu = options.submenu === true;
|
|
260
260
|
this.#trigger = options.trigger;
|
|
261
261
|
container.setAttribute("role", this.#role);
|
|
262
262
|
this.refresh();
|
|
@@ -265,17 +265,17 @@ class MenuKeyboard {
|
|
|
265
265
|
// содержит контейнер (строка подменю — предок своей панели), то это тот же путь события:
|
|
266
266
|
// второй обработчик на предке обработал бы одну клавишу дважды — пункт подменю срабатывал бы
|
|
267
267
|
// от одного Enter дважды, а одна стрелка перескакивала бы через пункт.
|
|
268
|
-
this.#
|
|
269
|
-
if (this.#
|
|
268
|
+
this.#triggerBound = options.trigger !== undefined && options.trigger.contains(container) === false;
|
|
269
|
+
if (this.#triggerBound) options.trigger.addEventListener("keydown", this.#keydownEvent);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
/** Перечитать пункты: роли, tabindex, состояние триггера (в том числе в закрытом меню) */
|
|
273
273
|
refresh() {
|
|
274
274
|
for (const item of this.#allItems()) {
|
|
275
|
-
if (item.getAttribute("role") === null) item.setAttribute("role", this.#
|
|
275
|
+
if (item.getAttribute("role") === null) item.setAttribute("role", this.#itemRole);
|
|
276
276
|
if (item.getAttribute("tabindex") === null) item.setAttribute("tabindex", "-1");
|
|
277
277
|
if (isDisabled(item)) item.setAttribute("aria-disabled", "true");
|
|
278
|
-
if (this.#
|
|
278
|
+
if (this.#submenuSelector !== undefined && item.querySelector(this.#submenuSelector) !== null) {
|
|
279
279
|
item.setAttribute("aria-haspopup", "true");
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -296,7 +296,7 @@ class MenuKeyboard {
|
|
|
296
296
|
|
|
297
297
|
// Все пункты своего уровня (и скрытые — им тоже нужны роли и tabindex)
|
|
298
298
|
#allItems() {
|
|
299
|
-
return [...this.#container.querySelectorAll(this.#
|
|
299
|
+
return [...this.#container.querySelectorAll(this.#itemSelector)].filter(
|
|
300
300
|
(item) => item.closest('[role="menu"]') === this.#container
|
|
301
301
|
);
|
|
302
302
|
}
|
|
@@ -330,7 +330,7 @@ class MenuKeyboard {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
#openOrClick(item) {
|
|
333
|
-
if (this.#
|
|
333
|
+
if (this.#submenuSelector !== undefined && item.querySelector(this.#submenuSelector) !== null) {
|
|
334
334
|
this.#openSubmenu(item);
|
|
335
335
|
return;
|
|
336
336
|
}
|
|
@@ -338,11 +338,11 @@ class MenuKeyboard {
|
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
#openSubmenu(row) {
|
|
341
|
-
if (this.#
|
|
342
|
-
const submenu = row.querySelector(this.#
|
|
341
|
+
if (this.#submenuSelector === undefined) return;
|
|
342
|
+
const submenu = row.querySelector(this.#submenuSelector);
|
|
343
343
|
if (submenu === null) return;
|
|
344
|
-
if (this.#
|
|
345
|
-
const first = [...submenu.querySelectorAll(this.#
|
|
344
|
+
if (this.#onSubmenuOpen !== undefined) this.#onSubmenuOpen(row);
|
|
345
|
+
const first = [...submenu.querySelectorAll(this.#itemSelector)].filter(isVisible)[0];
|
|
346
346
|
if (first !== undefined) first.focus();
|
|
347
347
|
}
|
|
348
348
|
|
|
@@ -381,20 +381,20 @@ class MenuKeyboard {
|
|
|
381
381
|
break;
|
|
382
382
|
}
|
|
383
383
|
case "ArrowLeft": {
|
|
384
|
-
if (!this.#
|
|
384
|
+
if (!this.#isSubmenu) break;
|
|
385
385
|
event.preventDefault();
|
|
386
|
-
if (this.#
|
|
386
|
+
if (this.#onEscape !== undefined) this.#onEscape();
|
|
387
387
|
if (this.#trigger !== undefined && document.body.contains(this.#trigger)) this.#trigger.focus();
|
|
388
388
|
break;
|
|
389
389
|
}
|
|
390
390
|
case "Escape":
|
|
391
391
|
event.preventDefault();
|
|
392
392
|
event.stopPropagation();
|
|
393
|
-
if (this.#
|
|
393
|
+
if (this.#onEscape !== undefined) this.#onEscape();
|
|
394
394
|
if (this.#trigger !== undefined && document.body.contains(this.#trigger)) this.#trigger.focus();
|
|
395
395
|
break;
|
|
396
396
|
case "Tab":
|
|
397
|
-
if (this.#
|
|
397
|
+
if (this.#onEscape !== undefined) this.#onEscape();
|
|
398
398
|
break;
|
|
399
399
|
default:
|
|
400
400
|
// Type-ahead: набор букв переводит фокус на пункт с таким началом
|
|
@@ -408,14 +408,14 @@ class MenuKeyboard {
|
|
|
408
408
|
#typeahead(char) {
|
|
409
409
|
const now = Date.now();
|
|
410
410
|
// Буфер сбрасывается, если с прошлой буквы прошло больше 700 мс
|
|
411
|
-
this.#
|
|
412
|
-
this.#
|
|
411
|
+
this.#typeBuffer = (now - this.#typeAt > 700 ? "" : this.#typeBuffer) + char.toLowerCase();
|
|
412
|
+
this.#typeAt = now;
|
|
413
413
|
const items = this.#items();
|
|
414
414
|
if (items.length === 0) return;
|
|
415
415
|
const current = this.#activeIndex(items);
|
|
416
416
|
// Ищем по кругу, начиная со следующего пункта
|
|
417
417
|
const ordered = current === -1 ? items : [...items.slice(current + 1), ...items.slice(0, current + 1)];
|
|
418
|
-
const match = ordered.find((item) => this.#label(item).startsWith(this.#
|
|
418
|
+
const match = ordered.find((item) => this.#label(item).startsWith(this.#typeBuffer));
|
|
419
419
|
if (match !== undefined) this.#focusItem(items.indexOf(match));
|
|
420
420
|
}
|
|
421
421
|
|
|
@@ -433,7 +433,7 @@ class MenuKeyboard {
|
|
|
433
433
|
|
|
434
434
|
destroy() {
|
|
435
435
|
this.#container.removeEventListener("keydown", this.#keydownEvent);
|
|
436
|
-
if (this.#trigger !== undefined && this.#
|
|
436
|
+
if (this.#trigger !== undefined && this.#triggerBound)
|
|
437
437
|
this.#trigger.removeEventListener("keydown", this.#keydownEvent);
|
|
438
438
|
}
|
|
439
439
|
}
|
|
@@ -446,11 +446,11 @@ class MenuKeyboard {
|
|
|
446
446
|
class ComboboxKeyboard {
|
|
447
447
|
#input = undefined;
|
|
448
448
|
#dropdown = undefined;
|
|
449
|
-
#
|
|
450
|
-
#
|
|
451
|
-
#
|
|
449
|
+
#optionSelector = undefined;
|
|
450
|
+
#onEscape = undefined;
|
|
451
|
+
#onOpen = undefined;
|
|
452
452
|
#active = undefined;
|
|
453
|
-
#
|
|
453
|
+
#selectionState = undefined;
|
|
454
454
|
|
|
455
455
|
constructor(
|
|
456
456
|
input,
|
|
@@ -464,10 +464,10 @@ class ComboboxKeyboard {
|
|
|
464
464
|
) {
|
|
465
465
|
this.#input = input;
|
|
466
466
|
this.#dropdown = options.dropdown;
|
|
467
|
-
this.#
|
|
468
|
-
this.#
|
|
469
|
-
this.#
|
|
470
|
-
this.#
|
|
467
|
+
this.#optionSelector = options.optionSelector;
|
|
468
|
+
this.#onEscape = options.onEscape;
|
|
469
|
+
this.#onOpen = options.onOpen;
|
|
470
|
+
this.#selectionState = options.selectionState;
|
|
471
471
|
input.setAttribute("role", "combobox");
|
|
472
472
|
input.setAttribute("aria-haspopup", "listbox");
|
|
473
473
|
input.setAttribute("aria-expanded", "false");
|
|
@@ -503,7 +503,7 @@ class ComboboxKeyboard {
|
|
|
503
503
|
|
|
504
504
|
#allOptions() {
|
|
505
505
|
if (this.#dropdown === undefined) return [];
|
|
506
|
-
return [...this.#dropdown.querySelectorAll(this.#
|
|
506
|
+
return [...this.#dropdown.querySelectorAll(this.#optionSelector)];
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
#options() {
|
|
@@ -513,11 +513,11 @@ class ComboboxKeyboard {
|
|
|
513
513
|
#applyActive() {
|
|
514
514
|
// Множественный выбор: `aria-selected` показывает выбор, а не наведение (его несёт
|
|
515
515
|
// `aria-activedescendant`) — иначе пометка «под курсором» выглядела бы как отметка.
|
|
516
|
-
const
|
|
516
|
+
const hasSelection = typeof this.#selectionState === "function";
|
|
517
517
|
for (const option of this.#allOptions()) {
|
|
518
518
|
const active = option === this.#active;
|
|
519
519
|
option.classList.toggle(ACTIVE_CLASS, active);
|
|
520
|
-
const selected =
|
|
520
|
+
const selected = hasSelection ? this.#selectionState(option) === true : active;
|
|
521
521
|
option.setAttribute("aria-selected", selected ? "true" : "false");
|
|
522
522
|
}
|
|
523
523
|
if (this.#active === undefined) this.#input.removeAttribute("aria-activedescendant");
|
|
@@ -541,8 +541,7 @@ class ComboboxKeyboard {
|
|
|
541
541
|
case "ArrowDown":
|
|
542
542
|
case "ArrowUp": {
|
|
543
543
|
event.preventDefault();
|
|
544
|
-
if (this.#input.getAttribute("aria-expanded") !== "true" && this.#
|
|
545
|
-
this.#on_open();
|
|
544
|
+
if (this.#input.getAttribute("aria-expanded") !== "true" && this.#onOpen !== undefined) this.#onOpen();
|
|
546
545
|
this.#move(event.key === "ArrowDown" ? 1 : -1);
|
|
547
546
|
break;
|
|
548
547
|
}
|
|
@@ -572,7 +571,7 @@ class ComboboxKeyboard {
|
|
|
572
571
|
this.#applyActive();
|
|
573
572
|
break;
|
|
574
573
|
}
|
|
575
|
-
if (this.#
|
|
574
|
+
if (this.#onEscape !== undefined) this.#onEscape();
|
|
576
575
|
break;
|
|
577
576
|
}
|
|
578
577
|
default:
|
|
@@ -594,20 +593,20 @@ class ComboboxKeyboard {
|
|
|
594
593
|
*/
|
|
595
594
|
class RovingKeyboard {
|
|
596
595
|
#container = undefined;
|
|
597
|
-
#
|
|
598
|
-
#
|
|
596
|
+
#itemSelector = undefined;
|
|
597
|
+
#onClick = undefined;
|
|
599
598
|
|
|
600
599
|
constructor(container, options = { itemSelector: String() }) {
|
|
601
600
|
this.#container = container;
|
|
602
|
-
this.#
|
|
601
|
+
this.#itemSelector = options.itemSelector;
|
|
603
602
|
this.refresh();
|
|
604
|
-
this.#
|
|
605
|
-
const item = event.target instanceof Element ? event.target.closest(this.#
|
|
603
|
+
this.#onClick = (event) => {
|
|
604
|
+
const item = event.target instanceof Element ? event.target.closest(this.#itemSelector) : null;
|
|
606
605
|
if (item === null || !this.#container.contains(item)) return;
|
|
607
606
|
this.#applyStop(item);
|
|
608
607
|
};
|
|
609
608
|
container.addEventListener("keydown", this.#keydownEvent);
|
|
610
|
-
container.addEventListener("click", this.#
|
|
609
|
+
container.addEventListener("click", this.#onClick);
|
|
611
610
|
}
|
|
612
611
|
|
|
613
612
|
/** Перечитать пункты: остановка остаётся на прежнем, если он ещё доступен */
|
|
@@ -624,7 +623,7 @@ class RovingKeyboard {
|
|
|
624
623
|
|
|
625
624
|
// Все пункты группы, включая скрытые: tabindex проставляется и до появления панели на экране
|
|
626
625
|
#allItems() {
|
|
627
|
-
return [...this.#container.querySelectorAll(this.#
|
|
626
|
+
return [...this.#container.querySelectorAll(this.#itemSelector)];
|
|
628
627
|
}
|
|
629
628
|
|
|
630
629
|
// Пункты, по которым ходит клавиатура: видимые и не запрещённые
|
|
@@ -686,7 +685,7 @@ class RovingKeyboard {
|
|
|
686
685
|
|
|
687
686
|
destroy() {
|
|
688
687
|
this.#container.removeEventListener("keydown", this.#keydownEvent);
|
|
689
|
-
this.#container.removeEventListener("click", this.#
|
|
688
|
+
this.#container.removeEventListener("click", this.#onClick);
|
|
690
689
|
}
|
|
691
690
|
}
|
|
692
691
|
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/** Виды движения: имя кадра даёт JS, а длительность и кривую — класс движения в styles.css */
|
|
2
|
+
const MOTION = {
|
|
3
|
+
ENTER: "enter",
|
|
4
|
+
EXIT: "exit",
|
|
5
|
+
POP: "pop",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/** Кадр появления всплывающей панели у триггера — общий кадр оболочки */
|
|
9
|
+
const PANEL_FRAME = "chui_panel_in";
|
|
2
10
|
|
|
3
11
|
class Animation {
|
|
4
12
|
#element = undefined;
|
|
@@ -8,6 +16,16 @@ class Animation {
|
|
|
8
16
|
if (element !== undefined) this.#element = element;
|
|
9
17
|
}
|
|
10
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Запустить кадры `name` как движение вида `kind`. Длительность и кривая — в `styles.css`
|
|
21
|
+
* по классу `data-motion`: вход базовый, выход короче, отклик с лёгким перелётом. Прежде
|
|
22
|
+
* время приходило из общего `animation-duration` на `*`, и все движения шли одним временем.
|
|
23
|
+
*/
|
|
24
|
+
#play(name = String(), kind = String()) {
|
|
25
|
+
this.#element.dataset.motion = kind;
|
|
26
|
+
this.#element.style.animationName = name;
|
|
27
|
+
}
|
|
28
|
+
|
|
11
29
|
/**
|
|
12
30
|
* Отменить незавершённое скрытие: его `animationend` (removeEvents) снимает у слоя
|
|
13
31
|
* `animation-name` и `display`. Если слой открыли заново раньше, чем анимация закрытия
|
|
@@ -18,35 +36,78 @@ class Animation {
|
|
|
18
36
|
this.#element.style.removeProperty("animation-name");
|
|
19
37
|
}
|
|
20
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Кадр появления доигран — вид движения снимается, а имя кадра остаётся. По имени видно,
|
|
41
|
+
* что слой уже открыт (повторный показ не перезапускает кадр), а само оно без вида ничего
|
|
42
|
+
* не держит. Вид держит конечное состояние (`animation-fill-mode: both` в styles.css),
|
|
43
|
+
* и на слое оставался бы вычисленный `transform` — пусть и единичный: элемент с `transform`
|
|
44
|
+
* становится собственным «backdrop root», и размытие подложки перестаёт видеть содержимое
|
|
45
|
+
* под слоем (замер: меню контекста с блюром и без — картинка не менялась, пока вид стоял).
|
|
46
|
+
*
|
|
47
|
+
* Стрелочное поле, а не метод: у метода в слушателе нет `this`, и он падал бы на первом
|
|
48
|
+
* же конце кадра.
|
|
49
|
+
*/
|
|
50
|
+
#entranceEnd = (event) => {
|
|
51
|
+
if (event.target !== this.#element) return;
|
|
52
|
+
this.#element.removeEventListener("animationend", this.#entranceEnd);
|
|
53
|
+
this.#element.removeAttribute("data-motion");
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Показать слой кадром появления и снять вид по концу кадра */
|
|
57
|
+
#enter(name = String(), kind = String()) {
|
|
58
|
+
this.#play(name, kind);
|
|
59
|
+
this.#element.addEventListener("animationend", this.#entranceEnd);
|
|
60
|
+
}
|
|
61
|
+
|
|
21
62
|
fadeIn() {
|
|
22
63
|
this.#cancelHide();
|
|
23
64
|
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
24
65
|
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
25
66
|
}
|
|
26
|
-
this.#
|
|
27
|
-
this.#element.style.animationName = "fade-in";
|
|
67
|
+
this.#enter("fade-in", MOTION.ENTER);
|
|
28
68
|
}
|
|
29
69
|
|
|
30
70
|
fadeOut() {
|
|
31
|
-
this.#element.style.transition = "transform .0s";
|
|
32
71
|
if (this.#element.style.animationName) {
|
|
33
|
-
this.#
|
|
72
|
+
this.#play("fade-out", MOTION.EXIT);
|
|
34
73
|
this.#element.addEventListener("animationend", removeEvents);
|
|
35
74
|
}
|
|
36
75
|
}
|
|
37
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Появление всплывающей панели у триггера (список, меню, поповер): панель приходит
|
|
79
|
+
* сверху вниз и чуть вырастает. Вид `pop` — кривая с лёгким перелётом, поэтому
|
|
80
|
+
* открытие читается откликом, а не вспышкой: прежний `fadeIn` менял только прозрачность.
|
|
81
|
+
*
|
|
82
|
+
* Повторный показ уже открытой панели кадр не запускает: поле открывают и `focus`,
|
|
83
|
+
* и `click` — одно действие приходило сюда дважды, а `#cancelHide` перед кадром снимает
|
|
84
|
+
* имя кадра, поэтому второй вызов начинал его заново и панель мигала на открытии.
|
|
85
|
+
*/
|
|
86
|
+
panelIn() {
|
|
87
|
+
const open =
|
|
88
|
+
this.#element.style.animationName === PANEL_FRAME &&
|
|
89
|
+
window.getComputedStyle(this.#element, null).display !== "none";
|
|
90
|
+
if (open) return;
|
|
91
|
+
this.#cancelHide();
|
|
92
|
+
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
93
|
+
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
94
|
+
}
|
|
95
|
+
this.#play(PANEL_FRAME, MOTION.POP);
|
|
96
|
+
this.#element.addEventListener("animationend", this.#entranceEnd);
|
|
97
|
+
}
|
|
98
|
+
|
|
38
99
|
// SCALE
|
|
39
100
|
scaleIn() {
|
|
40
101
|
this.#cancelHide();
|
|
41
102
|
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
42
103
|
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
43
104
|
}
|
|
44
|
-
this.#
|
|
105
|
+
this.#enter("scale-in", MOTION.POP);
|
|
45
106
|
}
|
|
46
107
|
|
|
47
108
|
scaleOut() {
|
|
48
109
|
if (this.#element.style.animationName) {
|
|
49
|
-
this.#
|
|
110
|
+
this.#play("scale-out", MOTION.EXIT);
|
|
50
111
|
this.#element.addEventListener("animationend", removeEvents);
|
|
51
112
|
}
|
|
52
113
|
}
|
|
@@ -57,12 +118,12 @@ class Animation {
|
|
|
57
118
|
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
58
119
|
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
59
120
|
}
|
|
60
|
-
this.#
|
|
121
|
+
this.#enter("slide-right-in", MOTION.ENTER);
|
|
61
122
|
}
|
|
62
123
|
|
|
63
124
|
slideRightOut() {
|
|
64
125
|
if (this.#element.style.animationName) {
|
|
65
|
-
this.#
|
|
126
|
+
this.#play("slide-right-out", MOTION.EXIT);
|
|
66
127
|
this.#element.addEventListener("animationend", removeEvents);
|
|
67
128
|
}
|
|
68
129
|
}
|
|
@@ -73,12 +134,12 @@ class Animation {
|
|
|
73
134
|
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
74
135
|
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
75
136
|
}
|
|
76
|
-
this.#
|
|
137
|
+
this.#enter("slide-bottom-in", MOTION.ENTER);
|
|
77
138
|
}
|
|
78
139
|
|
|
79
140
|
slideBottomOut() {
|
|
80
141
|
if (this.#element.style.animationName) {
|
|
81
|
-
this.#
|
|
142
|
+
this.#play("slide-bottom-out", MOTION.EXIT);
|
|
82
143
|
this.#element.addEventListener("animationend", removeEvents);
|
|
83
144
|
}
|
|
84
145
|
}
|
|
@@ -86,28 +147,28 @@ class Animation {
|
|
|
86
147
|
// REMOVE
|
|
87
148
|
slideBottomOutAndRemove() {
|
|
88
149
|
if (this.#element.style.animationName) {
|
|
89
|
-
this.#
|
|
150
|
+
this.#play("slide-bottom-out", MOTION.EXIT);
|
|
90
151
|
this.#element.addEventListener("animationend", removeEvents);
|
|
91
152
|
}
|
|
92
153
|
}
|
|
93
154
|
|
|
94
155
|
slideRightOutAndRemove() {
|
|
95
156
|
if (this.#element.style.animationName) {
|
|
96
|
-
this.#
|
|
157
|
+
this.#play("slide-right-out", MOTION.EXIT);
|
|
97
158
|
this.#element.addEventListener("animationend", removeEvents);
|
|
98
159
|
}
|
|
99
160
|
}
|
|
100
161
|
|
|
101
162
|
scaleOutAndRemove() {
|
|
102
163
|
if (this.#element.style.animationName) {
|
|
103
|
-
this.#
|
|
164
|
+
this.#play("scale-out", MOTION.EXIT);
|
|
104
165
|
this.#element.addEventListener("animationend", removeEvents2);
|
|
105
166
|
}
|
|
106
167
|
}
|
|
107
168
|
|
|
108
169
|
fadeOutAndRemove() {
|
|
109
170
|
if (this.#element.style.animationName) {
|
|
110
|
-
this.#
|
|
171
|
+
this.#play("fade-out", MOTION.EXIT);
|
|
111
172
|
this.#element.addEventListener("animationend", removeEvents2);
|
|
112
173
|
}
|
|
113
174
|
}
|
|
@@ -118,6 +179,7 @@ class Animation {
|
|
|
118
179
|
}
|
|
119
180
|
|
|
120
181
|
removeStyles() {
|
|
182
|
+
this.#element.removeAttribute("data-motion");
|
|
121
183
|
this.#element.removeAttribute("style");
|
|
122
184
|
}
|
|
123
185
|
}
|