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
|
@@ -31,8 +31,11 @@ const { setStyles, optionOr, scrollParent } = require("../../modules/chui_functi
|
|
|
31
31
|
* пересекает границу ровно в момент закрепления и даёт неустойчивый сигнал на дробных пикселях.
|
|
32
32
|
* Поэтому раз в кадр сравниваются верх полосы и верх области прокрутки; слушатель `scroll` —
|
|
33
33
|
* пассивный, работа сведена к одному замеру на кадр через `requestAnimationFrame`.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* * **Пока узел не в документе, контейнер не ищется.** Прокручиваемого предка у такого узла нет,
|
|
35
|
+
* и поиск вернул бы документ: порог встал бы на нуле, и полоса никогда не «закрепилась» бы —
|
|
36
|
+
* в разметке она осталась бы без фона, рамки и тени. Поэтому компонент ждёт появления узла
|
|
37
|
+
* в документе (наблюдателем, а не опросом по кадрам) и только потом подключается.
|
|
38
|
+
* * Доступность: компонент ничего не объявляет и не перехватывает — содержимое остаётся обычным
|
|
36
39
|
* содержимым. `destroy()` снимает слушатели; повторный вызов безвреден.
|
|
37
40
|
*/
|
|
38
41
|
class StickyBar {
|
|
@@ -44,11 +47,13 @@ class StickyBar {
|
|
|
44
47
|
#offset = "0px";
|
|
45
48
|
#target = undefined;
|
|
46
49
|
#stuck = false;
|
|
47
|
-
#
|
|
50
|
+
#onChange = undefined;
|
|
48
51
|
#container = undefined;
|
|
49
52
|
#destroyed = false;
|
|
50
53
|
#frame = 0;
|
|
51
54
|
#pending = 0;
|
|
55
|
+
// Наблюдатель за вставкой узла в документ: до неё области прокрутки ещё нет
|
|
56
|
+
#insert = undefined;
|
|
52
57
|
|
|
53
58
|
constructor(
|
|
54
59
|
options = {
|
|
@@ -66,7 +71,7 @@ class StickyBar {
|
|
|
66
71
|
this.#root.dataset.stuck = "false";
|
|
67
72
|
this.#root.appendChild(this.#content);
|
|
68
73
|
this.#target = options.target;
|
|
69
|
-
this.#
|
|
74
|
+
this.#onChange = options.onChange;
|
|
70
75
|
this.setOffset(options.offset === undefined ? 0 : options.offset);
|
|
71
76
|
this.#observe();
|
|
72
77
|
}
|
|
@@ -112,13 +117,13 @@ class StickyBar {
|
|
|
112
117
|
/** Отступ закрепления: число пикселей или строка CSS (`"12px"`, `"var(--header_height)"`) */
|
|
113
118
|
setOffset(offset = Number()) {
|
|
114
119
|
this.#offset = typeof offset === "string" ? offset : `${Number(offset) || 0}px`;
|
|
115
|
-
this.#root.style.setProperty("--
|
|
120
|
+
this.#root.style.setProperty("--sticky_bar_offset", this.#offset);
|
|
116
121
|
this.update();
|
|
117
122
|
return this;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
setOnChange(listener = undefined) {
|
|
121
|
-
this.#
|
|
126
|
+
this.#onChange = listener;
|
|
122
127
|
return this;
|
|
123
128
|
}
|
|
124
129
|
|
|
@@ -160,8 +165,9 @@ class StickyBar {
|
|
|
160
165
|
// === ВНУТРЕННЕЕ ===
|
|
161
166
|
/**
|
|
162
167
|
* Первое подключение — на следующем кадре: узел обычно вставляют в документ сразу после
|
|
163
|
-
* `set()`, и только тогда виден настоящий контейнер прокрутки.
|
|
164
|
-
*
|
|
168
|
+
* `set()`, и только тогда виден настоящий контейнер прокрутки. Если к этому кадру узел ещё
|
|
169
|
+
* не в документе (страница собирается позже), ждём вставки — иначе контейнером стал бы документ.
|
|
170
|
+
* Повторные вызовы ничего не делают, пока подключение не снято.
|
|
165
171
|
*/
|
|
166
172
|
#observe() {
|
|
167
173
|
if (this.#destroyed === true) return;
|
|
@@ -174,6 +180,13 @@ class StickyBar {
|
|
|
174
180
|
|
|
175
181
|
#attach() {
|
|
176
182
|
if (this.#destroyed === true) return;
|
|
183
|
+
// Узел ещё не в документе: прокручиваемого предка у него нет, и поиск контейнера вернул бы
|
|
184
|
+
// документ — порог встал бы на нуле, и полоса никогда не «закрепилась» бы (без фона, рамки
|
|
185
|
+
// и тени). Ждём вставки наблюдателем, а не опросом по кадрам
|
|
186
|
+
if (this.#root.isConnected === false) {
|
|
187
|
+
this.#awaitInsert();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
177
190
|
this.#container = this.getTarget();
|
|
178
191
|
if (this.#container instanceof Element) {
|
|
179
192
|
this.#container.addEventListener("scroll", this.#onScroll, { passive: true });
|
|
@@ -182,7 +195,25 @@ class StickyBar {
|
|
|
182
195
|
this.#schedule();
|
|
183
196
|
}
|
|
184
197
|
|
|
198
|
+
/** Ждём появления узла в документе; наблюдатель снимается сразу после вставки или в `detach()` */
|
|
199
|
+
#awaitInsert() {
|
|
200
|
+
if (this.#insert !== undefined || this.#destroyed === true) return;
|
|
201
|
+
this.#insert = new MutationObserver(() => {
|
|
202
|
+
if (this.#root.isConnected === false) return;
|
|
203
|
+
this.#dropInsert();
|
|
204
|
+
this.#attach();
|
|
205
|
+
});
|
|
206
|
+
this.#insert.observe(document.body, { childList: true, subtree: true });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#dropInsert() {
|
|
210
|
+
if (this.#insert === undefined) return;
|
|
211
|
+
this.#insert.disconnect();
|
|
212
|
+
this.#insert = undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
185
215
|
#detach() {
|
|
216
|
+
this.#dropInsert();
|
|
186
217
|
if (this.#container instanceof Element) {
|
|
187
218
|
this.#container.removeEventListener("scroll", this.#onScroll);
|
|
188
219
|
}
|
|
@@ -218,17 +249,17 @@ class StickyBar {
|
|
|
218
249
|
if (!(container instanceof Element) || this.#root.isConnected === false) return;
|
|
219
250
|
// Порог — от края содержимого контейнера: собственный отступ прокрутки браузер
|
|
220
251
|
// учитывает сам, а рамку — нет, поэтому она прибавляется отдельно
|
|
221
|
-
const
|
|
222
|
-
const padding = Number.parseFloat(
|
|
223
|
-
const
|
|
252
|
+
const containerStyle = window.getComputedStyle(container);
|
|
253
|
+
const padding = Number.parseFloat(containerStyle.paddingTop) || 0;
|
|
254
|
+
const referenceTop = container.getBoundingClientRect().top + container.clientTop + padding;
|
|
224
255
|
const inset = Number.parseFloat(window.getComputedStyle(this.#root).top);
|
|
225
256
|
const offset = Number.isFinite(inset) ? inset : 0;
|
|
226
|
-
const stuck = this.#root.getBoundingClientRect().top <=
|
|
257
|
+
const stuck = this.#root.getBoundingClientRect().top <= referenceTop + offset + 1;
|
|
227
258
|
if (stuck === this.#stuck) return;
|
|
228
259
|
this.#stuck = stuck;
|
|
229
260
|
this.#root.dataset.stuck = String(stuck);
|
|
230
261
|
this.#root.classList.toggle("sticky_bar_stuck", stuck);
|
|
231
|
-
if (typeof this.#
|
|
262
|
+
if (typeof this.#onChange === "function") this.#onChange(stuck);
|
|
232
263
|
}
|
|
233
264
|
}
|
|
234
265
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* Прилипающая полоса: корень сам липнет к верху области прокрутки, поэтому место в потоке
|
|
2
|
-
сохраняется и подпорка-заглушка не нужна. Отступ задаёт компонент переменной `--
|
|
2
|
+
сохраняется и подпорка-заглушка не нужна. Отступ задаёт компонент переменной `--sticky_bar_offset`
|
|
3
3
|
(число или CSS-значение); отсчитывается он от края содержимого области прокрутки. */
|
|
4
4
|
chui_sticky_bar {
|
|
5
|
-
--
|
|
5
|
+
--sticky_bar_offset: 0px;
|
|
6
6
|
display: block;
|
|
7
7
|
position: sticky;
|
|
8
|
-
inset-block-start: var(--
|
|
8
|
+
inset-block-start: var(--sticky_bar_offset);
|
|
9
9
|
width: -webkit-fill-available;
|
|
10
10
|
/* Ниже шапки (999) и выше содержимого страницы: полоса — слой над текстом, но под оболочкой */
|
|
11
11
|
z-index: 6;
|
|
@@ -25,15 +25,17 @@ sticky_bar_content {
|
|
|
25
25
|
border: 1px solid transparent;
|
|
26
26
|
border-radius: var(--radius_md);
|
|
27
27
|
transition:
|
|
28
|
-
background-color var(--
|
|
29
|
-
box-shadow var(--
|
|
28
|
+
background-color var(--motion_fast) var(--motion_ease),
|
|
29
|
+
box-shadow var(--motion_fast) var(--motion_ease);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/* Закреплённая полоса читается отдельным слоем:
|
|
33
|
-
|
|
32
|
+
/* Закреплённая полоса читается отдельным слоем: волосяной край и тень, а заливка — цвет
|
|
33
|
+
подложки (`--bar_background`), как у шапки, меню маршрутов и ящика уведомлений: полоса
|
|
34
|
+
выходит из подложки, а не лежит на ней светлой плашкой. В покое она — обычная строка
|
|
35
|
+
содержимого, ни фона, ни тени */
|
|
34
36
|
chui_sticky_bar[data-stuck="true"] sticky_bar_content {
|
|
35
37
|
border-color: var(--border_color);
|
|
36
|
-
background: var(--
|
|
38
|
+
background: var(--bar_background);
|
|
37
39
|
backdrop-filter: var(--global_blur);
|
|
38
40
|
box-shadow: var(--box_shadow_main);
|
|
39
41
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
const { Animation } = require("../../modules/chui_animations/animations");
|
|
2
2
|
const { Icon, Icons } = require("../chui_icons/icons");
|
|
3
3
|
const { makeKeyboardButton } = require("../../modules/chui_a11y/chui_a11y");
|
|
4
4
|
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
5
5
|
|
|
6
6
|
/** Идентификаторы вкладок для aria-controls/aria-labelledby */
|
|
7
|
-
let
|
|
7
|
+
let a11yTabCounter = 0;
|
|
8
8
|
|
|
9
9
|
class Tab {
|
|
10
10
|
#tab = document.createElement("tab");
|
|
11
|
-
#
|
|
11
|
+
#tabContent = [];
|
|
12
12
|
#title = String();
|
|
13
13
|
#closable = false;
|
|
14
14
|
#close = undefined;
|
|
@@ -19,7 +19,7 @@ class Tab {
|
|
|
19
19
|
if (options.closable === true) this.setClosable(true);
|
|
20
20
|
}
|
|
21
21
|
addContent(...contents) {
|
|
22
|
-
for (
|
|
22
|
+
for (const content of contents) this.#tabContent.push(content);
|
|
23
23
|
}
|
|
24
24
|
getTab() {
|
|
25
25
|
return this.#tab;
|
|
@@ -28,7 +28,7 @@ class Tab {
|
|
|
28
28
|
return this.#title;
|
|
29
29
|
}
|
|
30
30
|
getContent() {
|
|
31
|
-
return this.#
|
|
31
|
+
return this.#tabContent;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Закрываемая вкладка: у неё появляется кнопка закрытия, а `Tabs` закрывает её
|
|
@@ -72,14 +72,16 @@ class Tabs {
|
|
|
72
72
|
#active = -1;
|
|
73
73
|
// Вкладка, содержимое которой сейчас в панели: индекс меняется при закрытии соседей
|
|
74
74
|
#rendered = undefined;
|
|
75
|
-
#
|
|
75
|
+
#idContents = require("randomstring").generate();
|
|
76
76
|
#tabs = document.createElement("tabs");
|
|
77
|
-
#
|
|
77
|
+
#idList = require("randomstring").generate();
|
|
78
78
|
#list = document.createElement("list");
|
|
79
79
|
#content = document.createElement("content");
|
|
80
80
|
#status = document.createElement("tabs_status");
|
|
81
|
-
|
|
82
|
-
#
|
|
81
|
+
// Отложенный кадр пересчёта подложки выбранной вкладки
|
|
82
|
+
#indicatorFrame = undefined;
|
|
83
|
+
#onTabClose = undefined;
|
|
84
|
+
#animationendEvent = () => {
|
|
83
85
|
// Следы анимации снимаются точечно: `removeAttribute("style")` снёс бы и чужие
|
|
84
86
|
// инлайновые значения, если они появятся
|
|
85
87
|
this.#content.style.removeProperty("animation-name");
|
|
@@ -90,9 +92,9 @@ class Tabs {
|
|
|
90
92
|
options = { width: String(), default: Number(), tabs: [], closable: Boolean(), onTabClose: undefined }
|
|
91
93
|
) {
|
|
92
94
|
require("../../modules/chui_functions").setStyles(__dirname + "/styles.css", "chUiJS_Tabs");
|
|
93
|
-
this.#
|
|
94
|
-
this.#list.id = this.#
|
|
95
|
-
this.#content.id = this.#
|
|
95
|
+
this.#onTabClose = options.onTabClose;
|
|
96
|
+
this.#list.id = this.#idList;
|
|
97
|
+
this.#content.id = this.#idContents;
|
|
96
98
|
// Доступность: tablist/tab/tabpanel с roving tabindex
|
|
97
99
|
this.#list.setAttribute("role", "tablist");
|
|
98
100
|
this.#content.setAttribute("role", "tabpanel");
|
|
@@ -166,8 +168,10 @@ class Tabs {
|
|
|
166
168
|
if (!Number.isInteger(at) || at < 0 || at >= this.#items.length) return this;
|
|
167
169
|
const tab = this.#items[at];
|
|
168
170
|
// Смотрим на вкладку, а не на её место: индекс активной мог сдвинуться при закрытии
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
+
const closingRendered = tab === this.#rendered;
|
|
172
|
+
// Уход идёт движением: узел убирается по концу кадра, а состав и индексы меняются
|
|
173
|
+
// сразу — иначе активная вкладка считалась бы по несуществующему узлу
|
|
174
|
+
this.#leaveNode(tab.getTab());
|
|
171
175
|
this.#items.splice(at, 1);
|
|
172
176
|
// Активная вкладка осталась собой, но её место сдвинулось
|
|
173
177
|
if (at < this.#active) this.#active -= 1;
|
|
@@ -179,10 +183,10 @@ class Tabs {
|
|
|
179
183
|
this.#announce(I18n.t("navigation.tabClosedNothing", { title: tab.getTitle() }));
|
|
180
184
|
return this;
|
|
181
185
|
}
|
|
182
|
-
if (
|
|
183
|
-
this.#activate(this.#active,
|
|
186
|
+
if (closingRendered) this.#active = Math.min(at, this.#items.length - 1);
|
|
187
|
+
this.#activate(this.#active, closingRendered);
|
|
184
188
|
this.#announce(
|
|
185
|
-
|
|
189
|
+
closingRendered
|
|
186
190
|
? I18n.t("navigation.tabClosedOpened", {
|
|
187
191
|
title: tab.getTitle(),
|
|
188
192
|
active: this.#items[this.#active].getTitle(),
|
|
@@ -196,11 +200,11 @@ class Tabs {
|
|
|
196
200
|
// так `Tab.setClosable()` работает и после того, как вкладка встала в панель
|
|
197
201
|
#bindTab(tab = new Tab(), at = Number()) {
|
|
198
202
|
const node = tab.getTab();
|
|
199
|
-
if (node.id === undefined || node.id === "") node.id = "chui_a11y_tab_" + (
|
|
203
|
+
if (node.id === undefined || node.id === "") node.id = "chui_a11y_tab_" + (a11yTabCounter += 1);
|
|
200
204
|
node.setAttribute("role", "tab");
|
|
201
205
|
node.setAttribute("tabindex", "-1");
|
|
202
206
|
node.setAttribute("aria-selected", "false");
|
|
203
|
-
node.setAttribute("aria-controls", this.#
|
|
207
|
+
node.setAttribute("aria-controls", this.#idContents);
|
|
204
208
|
makeKeyboardButton(node, "tab");
|
|
205
209
|
node.addEventListener("click", () => this.#activate(this.#items.indexOf(tab), false));
|
|
206
210
|
const before = this.#list.children[at];
|
|
@@ -222,7 +226,7 @@ class Tabs {
|
|
|
222
226
|
#requestClose(tab = new Tab()) {
|
|
223
227
|
const at = this.#items.indexOf(tab);
|
|
224
228
|
if (at === -1 || !tab.isClosable()) return false;
|
|
225
|
-
if (this.#
|
|
229
|
+
if (this.#onTabClose !== undefined && this.#onTabClose(tab, at) === false) return false;
|
|
226
230
|
this.closeTab(at);
|
|
227
231
|
return true;
|
|
228
232
|
}
|
|
@@ -246,17 +250,61 @@ class Tabs {
|
|
|
246
250
|
this.#renderContent(tab);
|
|
247
251
|
this.#rendered = tab;
|
|
248
252
|
}
|
|
253
|
+
// Подложка выбранной вкладки: место и ширину снимаем по вёрстке
|
|
254
|
+
this.#scheduleIndicator();
|
|
249
255
|
if (focus) tab.getTab().focus();
|
|
250
256
|
return true;
|
|
251
257
|
}
|
|
252
258
|
|
|
259
|
+
/** Подложку двигают после раскладки: место вкладки читается только из вёрстки */
|
|
260
|
+
#scheduleIndicator() {
|
|
261
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
262
|
+
this.#syncIndicator();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (this.#indicatorFrame !== undefined) return;
|
|
266
|
+
this.#indicatorFrame = requestAnimationFrame(() => {
|
|
267
|
+
this.#indicatorFrame = undefined;
|
|
268
|
+
this.#syncIndicator();
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Уход узла: он гаснет и сходит вверх, а из документа убирается по концу кадра */
|
|
273
|
+
#leaveNode(node) {
|
|
274
|
+
node.classList.add("tab_leaving");
|
|
275
|
+
const done = (event) => {
|
|
276
|
+
if (event.target !== node) return;
|
|
277
|
+
node.removeEventListener("animationend", done);
|
|
278
|
+
node.remove();
|
|
279
|
+
};
|
|
280
|
+
node.addEventListener("animationend", done);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Место и ширина подложки выбранной вкладки. Ноль — выбора нет или вёрстки ещё нет:
|
|
285
|
+
* подложки нулевой ширины не видно, поэтому до первой раскладки вид прежний.
|
|
286
|
+
*/
|
|
287
|
+
#syncIndicator() {
|
|
288
|
+
const item = this.#items[this.#active];
|
|
289
|
+
const tab = item === undefined ? undefined : item.getTab();
|
|
290
|
+
if (tab === undefined || tab.offsetWidth === 0) {
|
|
291
|
+
this.#list.style.setProperty("--tabs_width", "0px");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
// В RTL началом ряда служит правый край: подложка стоит у него и едет влево
|
|
295
|
+
const rtl = window.getComputedStyle(this.#list).direction === "rtl";
|
|
296
|
+
const x = rtl ? this.#list.clientWidth - (tab.offsetLeft + tab.offsetWidth) : tab.offsetLeft;
|
|
297
|
+
this.#list.style.setProperty("--tabs_offset", `${rtl ? -x : x}px`);
|
|
298
|
+
this.#list.style.setProperty("--tabs_width", `${tab.offsetWidth}px`);
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
#renderContent(tab = new Tab()) {
|
|
254
302
|
this.#content.innerHTML = "";
|
|
255
303
|
for (const con of tab.getContent()) this.#content.appendChild(con.set());
|
|
256
304
|
new Animation(this.#content).fadeIn();
|
|
257
305
|
// Анимация ставит инлайн-стиль: снимаем его по завершении, иначе он копится
|
|
258
|
-
this.#content.removeEventListener("animationend", this.#
|
|
259
|
-
this.#content.addEventListener("animationend", this.#
|
|
306
|
+
this.#content.removeEventListener("animationend", this.#animationendEvent);
|
|
307
|
+
this.#content.addEventListener("animationend", this.#animationendEvent);
|
|
260
308
|
}
|
|
261
309
|
|
|
262
310
|
#onListKeyDown = (event) => {
|
|
@@ -280,15 +328,15 @@ class Tabs {
|
|
|
280
328
|
};
|
|
281
329
|
|
|
282
330
|
// Активная вкладка: aria-selected, tabindex и связь с панелью
|
|
283
|
-
#applyAria(
|
|
331
|
+
#applyAria(activeTab) {
|
|
284
332
|
for (const item of this.#items) {
|
|
285
333
|
const tab = item.getTab();
|
|
286
|
-
const selected = tab ===
|
|
334
|
+
const selected = tab === activeTab;
|
|
287
335
|
tab.setAttribute("aria-selected", selected ? "true" : "false");
|
|
288
336
|
tab.setAttribute("tabindex", selected ? "0" : "-1");
|
|
289
337
|
}
|
|
290
|
-
if (
|
|
291
|
-
this.#content.setAttribute("aria-labelledby",
|
|
338
|
+
if (activeTab.id !== undefined && activeTab.id !== "") {
|
|
339
|
+
this.#content.setAttribute("aria-labelledby", activeTab.id);
|
|
292
340
|
}
|
|
293
341
|
}
|
|
294
342
|
|
|
@@ -9,16 +9,38 @@ list {
|
|
|
9
9
|
вокруг которого вкладки плавали без границ */
|
|
10
10
|
padding: 3px;
|
|
11
11
|
border: 1px solid var(--border_color);
|
|
12
|
-
border-radius: var(--
|
|
12
|
+
border-radius: var(--radius_sm);
|
|
13
13
|
background: var(--element_background);
|
|
14
|
-
margin: var(--
|
|
14
|
+
margin: var(--block_margin) 0;
|
|
15
|
+
/* Место и ширина подложки выбранной вкладки: считает JS по вёрстке, нули — выбора нет */
|
|
16
|
+
--tabs_offset: 0px;
|
|
17
|
+
--tabs_width: 0px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Подложка выбранной вкладки: одна и та же деталь переезжает между вкладками, вместо
|
|
21
|
+
мгновенной смены заливки — как у SegmentedControl. Отступ по вертикали равен отступу
|
|
22
|
+
дорожки, поэтому подложка встаёт в ряд вкладок */
|
|
23
|
+
list::before {
|
|
24
|
+
content: "";
|
|
25
|
+
position: absolute;
|
|
26
|
+
z-index: 0;
|
|
27
|
+
inset-block: 3px;
|
|
28
|
+
inset-inline-start: 0;
|
|
29
|
+
width: var(--tabs_width);
|
|
30
|
+
border-radius: var(--radius_sm);
|
|
31
|
+
background: var(--blue_prime_background);
|
|
32
|
+
transform: translateX(var(--tabs_offset));
|
|
33
|
+
transition:
|
|
34
|
+
transform var(--motion_base) var(--motion_ease),
|
|
35
|
+
width var(--motion_base) var(--motion_ease);
|
|
36
|
+
pointer-events: none;
|
|
15
37
|
}
|
|
16
38
|
|
|
17
39
|
tab {
|
|
18
40
|
cursor: pointer;
|
|
19
41
|
/* Отступ даёт только `gap` дорожки: свой `margin` добавлял к нему ещё по 1 px
|
|
20
42
|
и дорожка вырастала на 2 px без причины */
|
|
21
|
-
padding: var(--
|
|
43
|
+
padding: var(--block_padding_sm);
|
|
22
44
|
min-height: var(--control_height);
|
|
23
45
|
box-sizing: border-box;
|
|
24
46
|
font-size: var(--font_default_size);
|
|
@@ -28,12 +50,13 @@ tab {
|
|
|
28
50
|
gap: 4px;
|
|
29
51
|
font-weight: 500;
|
|
30
52
|
color: var(--text_color);
|
|
31
|
-
border-radius: var(--
|
|
53
|
+
border-radius: var(--radius_sm);
|
|
32
54
|
}
|
|
33
55
|
|
|
34
56
|
/* Наведение — нейтральная тонировка, а не заливка во весь акцент: полная заливка
|
|
35
|
-
срабатывала на каждое наведение и перетягивала внимание (то же решение, что у Button)
|
|
36
|
-
|
|
57
|
+
срабатывала на каждое наведение и перетягивала внимание (то же решение, что у Button).
|
|
58
|
+
У выбранной вкладки заливку несёт подложка, поэтому она наведение не ловит */
|
|
59
|
+
tab:not(.tab_active):hover {
|
|
37
60
|
background: var(--element_background_hover);
|
|
38
61
|
}
|
|
39
62
|
|
|
@@ -43,7 +66,7 @@ tab:active {
|
|
|
43
66
|
}
|
|
44
67
|
|
|
45
68
|
tabs {
|
|
46
|
-
margin: var(--
|
|
69
|
+
margin: var(--block_margin);
|
|
47
70
|
display: flex;
|
|
48
71
|
flex-direction: column;
|
|
49
72
|
justify-content: flex-start;
|
|
@@ -55,23 +78,28 @@ content {
|
|
|
55
78
|
flex-direction: column;
|
|
56
79
|
width: -webkit-fill-available;
|
|
57
80
|
padding: var(--control_padding);
|
|
58
|
-
border-radius: var(--
|
|
81
|
+
border-radius: var(--radius_sm);
|
|
59
82
|
background: transparent;
|
|
60
83
|
border: none;
|
|
61
84
|
}
|
|
62
85
|
|
|
63
86
|
.tab_active {
|
|
64
|
-
/* Выбранная вкладка —
|
|
87
|
+
/* Выбранная вкладка — белый текст на подложке-акценте, как у выбранного сегмента
|
|
65
88
|
SegmentedControl и у нажатой Button. Прежняя «тонировка + акцентный текст» давала
|
|
66
89
|
синий текст на синей тонировке (контраст ≈2.5:1) и читалась грязным пятном */
|
|
67
|
-
background:
|
|
90
|
+
background: transparent;
|
|
68
91
|
color: var(--text_color_hover);
|
|
69
92
|
font-weight: 600;
|
|
70
93
|
}
|
|
71
94
|
|
|
72
|
-
/*
|
|
95
|
+
/* Уход закрываемой вкладки: она гаснет и сходит вверх, а из дорожки убирается по концу кадра */
|
|
96
|
+
.tab_leaving {
|
|
97
|
+
animation: chui_leave_up var(--motion_fast) var(--motion_ease) forwards;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Наведение на выбранной вкладке ничего не меняет: отклик уже в самой подложке */
|
|
73
101
|
.tab_active:hover {
|
|
74
|
-
background:
|
|
102
|
+
background: transparent;
|
|
75
103
|
color: var(--text_color_hover);
|
|
76
104
|
}
|
|
77
105
|
|
|
@@ -6,7 +6,7 @@ chui_text_editor_test {
|
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
width: -webkit-fill-available;
|
|
8
8
|
height: max-content;
|
|
9
|
-
margin: var(--
|
|
9
|
+
margin: var(--block_margin);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
chui_text_editor {
|
|
@@ -26,7 +26,7 @@ chui_editor_controls {
|
|
|
26
26
|
padding: 4px;
|
|
27
27
|
border: 1px solid var(--border_color);
|
|
28
28
|
border-bottom: none;
|
|
29
|
-
border-radius: var(--
|
|
29
|
+
border-radius: var(--radius_sm) var(--radius_sm) 0 0;
|
|
30
30
|
background: var(--element_background_2);
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -36,7 +36,7 @@ chui_editor_text_input {
|
|
|
36
36
|
width: -webkit-fill-available;
|
|
37
37
|
padding: 8px 10px;
|
|
38
38
|
border: 1px solid var(--border_color);
|
|
39
|
-
border-radius: 0 0 var(--
|
|
39
|
+
border-radius: 0 0 var(--radius_sm) var(--radius_sm);
|
|
40
40
|
background: var(--element_background);
|
|
41
41
|
color: var(--text_color);
|
|
42
42
|
font-size: var(--font_default_size);
|
|
@@ -60,7 +60,7 @@ chui_editor_text_input div {
|
|
|
60
60
|
.text_editor_label {
|
|
61
61
|
height: max-content;
|
|
62
62
|
width: max-content;
|
|
63
|
-
margin: var(--
|
|
63
|
+
margin: var(--block_margin);
|
|
64
64
|
font-size: var(--font_labels_size);
|
|
65
65
|
font-weight: 500;
|
|
66
66
|
color: var(--text_color);
|
|
@@ -76,7 +76,7 @@ p img {
|
|
|
76
76
|
width: auto;
|
|
77
77
|
height: auto;
|
|
78
78
|
border: 2px solid transparent;
|
|
79
|
-
border-radius: var(--
|
|
79
|
+
border-radius: var(--radius_sm);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/* Кнопки панели — как действия панели разметки у MarkdownEditor: сторона из токена
|
|
@@ -184,7 +184,7 @@ text_editor_selectbox_dropdown {
|
|
|
184
184
|
display: none;
|
|
185
185
|
height: max-content;
|
|
186
186
|
width: max-content;
|
|
187
|
-
border-radius: var(--
|
|
187
|
+
border-radius: var(--radius_sm);
|
|
188
188
|
margin-top: 0;
|
|
189
189
|
padding: 6px;
|
|
190
190
|
font-size: var(--font_default_size);
|
|
@@ -209,7 +209,7 @@ text_editor_select_button_open {
|
|
|
209
209
|
text_editor_selectbox_option {
|
|
210
210
|
text-align: start;
|
|
211
211
|
padding: 6px 12px;
|
|
212
|
-
border-radius: var(--
|
|
212
|
+
border-radius: var(--radius_sm);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
/* Заголовок пункта — абзац или заголовок (тег задаёт вызывающий): отступы снимает CSS.
|