chuijs 4.0.1 → 4.0.3
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 +113 -0
- package/README.md +43 -632
- package/framework/appLayout/default/chui_app_layout.js +314 -273
- package/framework/appLayout/default/global_style.css +93 -53
- package/framework/components/chui_accordion/accordion.js +29 -26
- package/framework/components/chui_accordion/styles.css +6 -5
- package/framework/components/chui_action_sheet/action_sheet.js +59 -33
- package/framework/components/chui_action_sheet/styles.css +2 -1
- package/framework/components/chui_affix/affix.js +11 -6
- package/framework/components/chui_alert/alert.js +23 -14
- package/framework/components/chui_avatar/avatar.js +23 -22
- package/framework/components/chui_avatar_group/avatar_group.js +21 -19
- package/framework/components/chui_backtop/backtop.js +35 -19
- package/framework/components/chui_badge/badge.js +15 -13
- package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
- package/framework/components/chui_button/button.js +42 -22
- package/framework/components/chui_button/styles.css +2 -2
- package/framework/components/chui_calendar/calendar.js +12 -10
- package/framework/components/chui_card/card.js +43 -23
- package/framework/components/chui_chat/chat.js +72 -33
- package/framework/components/chui_clipboard_history/clipboard_history.js +30 -21
- package/framework/components/chui_clipboard_history/styles.css +4 -2
- package/framework/components/chui_code/code.js +2 -2
- package/framework/components/chui_code/styles.css +2 -2
- package/framework/components/chui_code_editor/code_editor.js +47 -31
- package/framework/components/chui_code_editor/styles.css +3 -2
- package/framework/components/chui_command_palette/command_palette.js +60 -37
- package/framework/components/chui_console/console.js +5 -5
- package/framework/components/chui_console/styles.css +1 -1
- package/framework/components/chui_content_block/content_block.js +33 -19
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +9 -4
- package/framework/components/chui_custom_element/custom_element.js +22 -15
- package/framework/components/chui_data_grid/data_grid.js +396 -234
- package/framework/components/chui_data_grid/styles.css +10 -8
- package/framework/components/chui_descriptions/descriptions.js +34 -17
- package/framework/components/chui_details/details.js +32 -26
- package/framework/components/chui_details/styles.css +5 -4
- package/framework/components/chui_diff_viewer/diff_viewer.js +91 -66
- package/framework/components/chui_diff_viewer/styles.css +1 -1
- package/framework/components/chui_divider/divider.js +13 -11
- package/framework/components/chui_drawer/drawer.js +27 -25
- package/framework/components/chui_drop_zone/drop_zone.js +31 -21
- package/framework/components/chui_empty_state/empty_state.js +20 -15
- package/framework/components/chui_fieldset/fieldset.js +37 -20
- package/framework/components/chui_fieldset/styles.css +1 -1
- package/framework/components/chui_file_explorer/file_explorer.js +157 -86
- package/framework/components/chui_file_explorer/styles.css +2 -1
- package/framework/components/chui_form/form.js +31 -19
- package/framework/components/chui_form/styles.css +1 -1
- package/framework/components/chui_gantt/gantt.js +64 -40
- package/framework/components/chui_graphs/graphs.js +301 -200
- package/framework/components/chui_graphs/styles.css +1 -1
- package/framework/components/chui_hero/hero.js +40 -22
- package/framework/components/chui_html_block/html_block.js +6 -6
- package/framework/components/chui_html_block/styles.css +2 -2
- package/framework/components/chui_hx/hx.js +3 -3
- package/framework/components/chui_hx/styles.css +11 -4
- package/framework/components/chui_icons/icons.js +2109 -2109
- package/framework/components/chui_icons/styles.css +2 -2
- package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
- package/framework/components/chui_inputs/chui_check_box/styles.css +18 -18
- package/framework/components/chui_inputs/chui_chips/chips.js +50 -40
- package/framework/components/chui_inputs/chui_chips/styles.css +4 -2
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +36 -24
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +100 -82
- package/framework/components/chui_inputs/chui_combo_box/styles.css +14 -12
- package/framework/components/chui_inputs/chui_date/date.js +102 -66
- package/framework/components/chui_inputs/chui_date/styles.css +12 -11
- package/framework/components/chui_inputs/chui_date_range/date_range.js +40 -29
- package/framework/components/chui_inputs/chui_datetime/datetime.js +35 -27
- package/framework/components/chui_inputs/chui_email/email.js +43 -31
- package/framework/components/chui_inputs/chui_email/styles.css +5 -5
- package/framework/components/chui_inputs/chui_file/file.js +68 -41
- package/framework/components/chui_inputs/chui_file/styles.css +13 -12
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +61 -36
- package/framework/components/chui_inputs/chui_masked/masked.js +39 -28
- package/framework/components/chui_inputs/chui_mention/mention.js +77 -30
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +173 -153
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +9 -7
- package/framework/components/chui_inputs/chui_number/number.js +58 -41
- package/framework/components/chui_inputs/chui_number/styles.css +7 -7
- package/framework/components/chui_inputs/chui_otp/otp.js +38 -29
- package/framework/components/chui_inputs/chui_password/password.js +58 -36
- package/framework/components/chui_inputs/chui_password/styles.css +8 -8
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
- package/framework/components/chui_inputs/chui_radio_button/styles.css +20 -20
- package/framework/components/chui_inputs/chui_rating/rating.js +26 -22
- package/framework/components/chui_inputs/chui_select_box/select_box.js +102 -78
- package/framework/components/chui_inputs/chui_select_box/styles.css +11 -11
- package/framework/components/chui_inputs/chui_slider/slider.js +29 -26
- package/framework/components/chui_inputs/chui_slider/styles.css +2 -1
- package/framework/components/chui_inputs/chui_text/styles.css +8 -8
- package/framework/components/chui_inputs/chui_text/text.js +65 -44
- package/framework/components/chui_inputs/chui_text_area/styles.css +6 -6
- package/framework/components/chui_inputs/chui_text_area/text_area.js +51 -28
- package/framework/components/chui_inputs/chui_time/styles.css +5 -3
- package/framework/components/chui_inputs/chui_time/time.js +38 -27
- package/framework/components/chui_inputs/chui_toggle/styles.css +3 -3
- package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
- package/framework/components/chui_json_editor/json_editor.js +53 -43
- package/framework/components/chui_json_viewer/json_viewer.js +52 -33
- package/framework/components/chui_json_viewer/styles.css +3 -2
- package/framework/components/chui_kanban/kanban.js +52 -34
- package/framework/components/chui_kbd/kbd.js +12 -10
- package/framework/components/chui_label/label.js +14 -12
- package/framework/components/chui_label/styles.css +2 -2
- package/framework/components/chui_link/link.js +27 -20
- package/framework/components/chui_list/list.js +31 -27
- package/framework/components/chui_list/styles.css +2 -1
- package/framework/components/chui_markdown_editor/markdown_editor.js +60 -44
- package/framework/components/chui_media/audio.js +326 -227
- package/framework/components/chui_media/audio_styles.css +40 -21
- package/framework/components/chui_media/image.js +70 -38
- package/framework/components/chui_media/image_compare.js +52 -21
- package/framework/components/chui_media/image_styles.css +3 -3
- package/framework/components/chui_media/video.js +155 -62
- package/framework/components/chui_media/video_styles.css +33 -16
- package/framework/components/chui_menu_bar/menu_bar.js +26 -24
- package/framework/components/chui_menu_bar/styles.css +4 -2
- package/framework/components/chui_modal/modal.js +44 -43
- package/framework/components/chui_modal/styles.css +3 -2
- package/framework/components/chui_notification/notification.js +50 -34
- package/framework/components/chui_notification/notification_download.js +31 -25
- package/framework/components/chui_notification/notification_download_progress.css +3 -2
- package/framework/components/chui_notification/notification_download_progress.js +47 -41
- package/framework/components/chui_notification/notification_update.js +23 -16
- package/framework/components/chui_notification/status_plate.js +2 -2
- package/framework/components/chui_notification/styles_download.css +1 -1
- package/framework/components/chui_notification/styles_update.css +1 -1
- package/framework/components/chui_org_chart/org_chart.js +74 -35
- package/framework/components/chui_pagination/pagination.js +62 -27
- package/framework/components/chui_paragraph/paragraph.js +2 -2
- package/framework/components/chui_paragraph/styles.css +1 -1
- package/framework/components/chui_popover/popover.js +49 -28
- package/framework/components/chui_popups/popups.js +137 -108
- package/framework/components/chui_popups/styles.css +2 -2
- package/framework/components/chui_print_button/print_button.js +33 -23
- package/framework/components/chui_progress_bar/progress_bar.js +23 -15
- package/framework/components/chui_progress_bar/styles.css +2 -2
- package/framework/components/chui_progress_ring/progress_ring.js +17 -15
- package/framework/components/chui_qrcode/qrcode.js +75 -31
- package/framework/components/chui_query_builder/query_builder.js +216 -123
- package/framework/components/chui_radio_group/radio_group.js +36 -26
- package/framework/components/chui_radio_group/styles.css +1 -1
- package/framework/components/chui_result/result.js +26 -16
- package/framework/components/chui_scheduler/scheduler.js +57 -35
- package/framework/components/chui_scheduler/styles.css +6 -2
- package/framework/components/chui_segmented_control/segmented_control.js +27 -21
- package/framework/components/chui_settings/settings.js +122 -48
- package/framework/components/chui_shortcut_help/shortcut_help.js +55 -32
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +33 -23
- package/framework/components/chui_skeleton/skeleton.js +29 -24
- package/framework/components/chui_slideshow/slideshow.js +60 -41
- package/framework/components/chui_slideshow/styles.css +18 -12
- package/framework/components/chui_spinner/spinner.js +8 -8
- package/framework/components/chui_spinner/styles.css +0 -1
- package/framework/components/chui_split_button/split_button.js +23 -18
- package/framework/components/chui_split_button/styles.css +1 -1
- package/framework/components/chui_splitter/splitter.js +35 -28
- package/framework/components/chui_statistic/statistic.js +65 -38
- package/framework/components/chui_status_bar/status_bar.js +11 -9
- package/framework/components/chui_stepper/stepper.js +27 -22
- package/framework/components/chui_sticky_bar/sticky_bar.js +9 -4
- package/framework/components/chui_sticky_bar/styles.css +3 -1
- package/framework/components/chui_tabs/chui_tabs.js +28 -22
- package/framework/components/chui_tabs/styles.css +3 -3
- package/framework/components/chui_text_editor/styles.css +20 -19
- package/framework/components/chui_text_editor/text_editor.js +85 -61
- package/framework/components/chui_text_editor/text_editor_panel.js +346 -284
- package/framework/components/chui_theme_toggle/theme_toggle.js +27 -22
- package/framework/components/chui_timeline/timeline.js +14 -12
- package/framework/components/chui_toast_queue/toast_queue.js +52 -46
- package/framework/components/chui_toolbar/toolbar.js +20 -13
- package/framework/components/chui_tooltip/tooltip.js +37 -24
- package/framework/components/chui_tour/styles.css +8 -3
- package/framework/components/chui_tour/tour.js +61 -27
- package/framework/components/chui_transfer/styles.css +2 -1
- package/framework/components/chui_transfer/transfer.js +75 -42
- package/framework/components/chui_tree_table/tree_table.js +43 -29
- package/framework/components/chui_tree_view/styles.css +4 -2
- package/framework/components/chui_tree_view/tree_view.js +142 -99
- package/framework/components/chui_watermark/watermark.js +24 -16
- package/framework/components/chui_webview/styles.css +2 -2
- package/framework/components/chui_webview/webview.js +62 -50
- package/framework/components/telegram_bot/chui_telegram_bot.js +71 -64
- package/framework/modules/chui_a11y/chui_a11y.js +77 -46
- package/framework/modules/chui_animations/animations.js +42 -42
- package/framework/modules/chui_animations/styles.css +1 -1
- package/framework/modules/chui_bridge/preload.js +56 -26
- package/framework/modules/chui_functions.js +52 -50
- package/framework/modules/chui_highlight/chui_highlight.js +25 -22
- package/framework/modules/chui_i18n/chui_i18n.js +9 -7
- 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 +3 -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 +8 -8
- package/framework/modules/chui_i18n/locales/validate.js +3 -3
- package/framework/modules/chui_json/chui_json.js +9 -9
- package/framework/modules/chui_layout/chui_layout.js +12 -10
- package/framework/modules/chui_logger/chui_logger.js +29 -25
- package/framework/modules/chui_page/page.js +33 -27
- package/framework/modules/chui_page/styles.css +3 -26
- package/framework/modules/chui_qr/chui_qr.js +158 -67
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +35 -24
- package/framework/modules/chui_scrollbar/styles.css +3 -1
- package/framework/modules/chui_services/chui_channels.js +76 -11
- package/framework/modules/chui_services/chui_services.js +39 -31
- package/framework/modules/chui_services/desktop.js +149 -30
- package/framework/modules/chui_sortable/chui_sortable.js +17 -12
- package/framework/modules/chui_validate/chui_validate.js +7 -4
- package/framework/tray_bar/chui_menu_items.js +136 -46
- package/index.d.ts +4814 -0
- package/index.js +449 -334
- package/package.json +79 -68
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
* (popup_button_ok и подобным) tabindex выставляет вызывающий компонент.
|
|
15
15
|
*/
|
|
16
16
|
const FOCUSABLE_SELECTOR = [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
"a[href]",
|
|
18
|
+
"button:not([disabled])",
|
|
19
|
+
"input:not([disabled])",
|
|
20
|
+
"select:not([disabled])",
|
|
21
|
+
"textarea:not([disabled])",
|
|
22
22
|
'[contenteditable="true"]',
|
|
23
23
|
'[tabindex]:not([tabindex="-1"])',
|
|
24
|
-
].join(
|
|
24
|
+
].join(", ");
|
|
25
25
|
|
|
26
26
|
class OverlayA11y {
|
|
27
27
|
// Стек открытых слоёв: нужен, чтобы вложенный слой не снимал фон-инерт у родителя
|
|
@@ -37,10 +37,16 @@ class OverlayA11y {
|
|
|
37
37
|
#previous_focus = undefined;
|
|
38
38
|
#is_open = false;
|
|
39
39
|
|
|
40
|
-
constructor(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
constructor(
|
|
41
|
+
element,
|
|
42
|
+
options = {
|
|
43
|
+
role: String(),
|
|
44
|
+
label: String(),
|
|
45
|
+
labelNode: undefined,
|
|
46
|
+
describedBy: undefined,
|
|
47
|
+
onEscape: undefined,
|
|
48
|
+
}
|
|
49
|
+
) {
|
|
44
50
|
this.#element = element;
|
|
45
51
|
if (options.role !== undefined) this.#role = options.role;
|
|
46
52
|
this.#label = options.label;
|
|
@@ -125,8 +131,9 @@ class OverlayA11y {
|
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
#focusables() {
|
|
128
|
-
return [...this.#element.querySelectorAll(FOCUSABLE_SELECTOR)]
|
|
129
|
-
|
|
134
|
+
return [...this.#element.querySelectorAll(FOCUSABLE_SELECTOR)].filter(
|
|
135
|
+
(element) => element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
|
|
136
|
+
);
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
#focusFirst() {
|
|
@@ -175,7 +182,7 @@ class OverlayA11y {
|
|
|
175
182
|
return;
|
|
176
183
|
}
|
|
177
184
|
if (event.key === "Tab") this.#trapFocus(event);
|
|
178
|
-
}
|
|
185
|
+
};
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
/**
|
|
@@ -194,16 +201,22 @@ function makeKeyboardButton(element, role = "button") {
|
|
|
194
201
|
return element;
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
exports.OverlayA11y = OverlayA11y
|
|
198
|
-
exports.makeKeyboardButton = makeKeyboardButton
|
|
199
|
-
exports.A11Y_FOCUSABLE_SELECTOR = FOCUSABLE_SELECTOR
|
|
204
|
+
exports.OverlayA11y = OverlayA11y;
|
|
205
|
+
exports.makeKeyboardButton = makeKeyboardButton;
|
|
206
|
+
exports.A11Y_FOCUSABLE_SELECTOR = FOCUSABLE_SELECTOR;
|
|
200
207
|
|
|
201
208
|
// Подсветка активного пункта меню/опции, когда фокус остаётся на поле ввода
|
|
202
209
|
const ACTIVE_CLASS = "chui_a11y_active";
|
|
203
210
|
|
|
204
|
-
const isVisible = (element) =>
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
const isVisible = (element) =>
|
|
212
|
+
element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement;
|
|
213
|
+
const isDisabled = (element) =>
|
|
214
|
+
element.disabled === true ||
|
|
215
|
+
element.getAttribute("aria-disabled") === "true" ||
|
|
216
|
+
(element.className !== undefined &&
|
|
217
|
+
String(element.className)
|
|
218
|
+
.split(" ")
|
|
219
|
+
.some((name) => name.endsWith("_disabled")));
|
|
207
220
|
|
|
208
221
|
/**
|
|
209
222
|
* Клавиатура для всплывающих меню (ContextMenu, MenuBar, меню шапки).
|
|
@@ -225,10 +238,17 @@ class MenuKeyboard {
|
|
|
225
238
|
#trigger = undefined;
|
|
226
239
|
#trigger_bound = false;
|
|
227
240
|
|
|
228
|
-
constructor(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
241
|
+
constructor(
|
|
242
|
+
container,
|
|
243
|
+
options = {
|
|
244
|
+
role: String(),
|
|
245
|
+
itemRole: String(),
|
|
246
|
+
itemSelector: String(),
|
|
247
|
+
submenuSelector: String(),
|
|
248
|
+
onEscape: undefined,
|
|
249
|
+
trigger: undefined,
|
|
250
|
+
}
|
|
251
|
+
) {
|
|
232
252
|
this.#container = container;
|
|
233
253
|
if (options.role !== undefined) this.#role = options.role;
|
|
234
254
|
if (options.itemRole !== undefined) this.#item_role = options.itemRole;
|
|
@@ -276,8 +296,9 @@ class MenuKeyboard {
|
|
|
276
296
|
|
|
277
297
|
// Все пункты своего уровня (и скрытые — им тоже нужны роли и tabindex)
|
|
278
298
|
#allItems() {
|
|
279
|
-
return [...this.#container.querySelectorAll(this.#item_selector)]
|
|
280
|
-
|
|
299
|
+
return [...this.#container.querySelectorAll(this.#item_selector)].filter(
|
|
300
|
+
(item) => item.closest('[role="menu"]') === this.#container
|
|
301
|
+
);
|
|
281
302
|
}
|
|
282
303
|
|
|
283
304
|
#focusItem(index) {
|
|
@@ -290,7 +311,7 @@ class MenuKeyboard {
|
|
|
290
311
|
}
|
|
291
312
|
|
|
292
313
|
#activeIndex(items) {
|
|
293
|
-
return items.findIndex(item => item === document.activeElement || item.classList.contains(ACTIVE_CLASS));
|
|
314
|
+
return items.findIndex((item) => item === document.activeElement || item.classList.contains(ACTIVE_CLASS));
|
|
294
315
|
}
|
|
295
316
|
|
|
296
317
|
// Для навигации — только видимые пункты своего уровня
|
|
@@ -382,7 +403,7 @@ class MenuKeyboard {
|
|
|
382
403
|
}
|
|
383
404
|
break;
|
|
384
405
|
}
|
|
385
|
-
}
|
|
406
|
+
};
|
|
386
407
|
|
|
387
408
|
#typeahead(char) {
|
|
388
409
|
const now = Date.now();
|
|
@@ -394,7 +415,7 @@ class MenuKeyboard {
|
|
|
394
415
|
const current = this.#activeIndex(items);
|
|
395
416
|
// Ищем по кругу, начиная со следующего пункта
|
|
396
417
|
const ordered = current === -1 ? items : [...items.slice(current + 1), ...items.slice(0, current + 1)];
|
|
397
|
-
const match = ordered.find(item => this.#label(item).startsWith(this.#type_buffer));
|
|
418
|
+
const match = ordered.find((item) => this.#label(item).startsWith(this.#type_buffer));
|
|
398
419
|
if (match !== undefined) this.#focusItem(items.indexOf(match));
|
|
399
420
|
}
|
|
400
421
|
|
|
@@ -412,7 +433,8 @@ class MenuKeyboard {
|
|
|
412
433
|
|
|
413
434
|
destroy() {
|
|
414
435
|
this.#container.removeEventListener("keydown", this.#keydownEvent);
|
|
415
|
-
if (this.#trigger !== undefined && this.#trigger_bound)
|
|
436
|
+
if (this.#trigger !== undefined && this.#trigger_bound)
|
|
437
|
+
this.#trigger.removeEventListener("keydown", this.#keydownEvent);
|
|
416
438
|
}
|
|
417
439
|
}
|
|
418
440
|
|
|
@@ -430,9 +452,16 @@ class ComboboxKeyboard {
|
|
|
430
452
|
#active = undefined;
|
|
431
453
|
#selection_state = undefined;
|
|
432
454
|
|
|
433
|
-
constructor(
|
|
434
|
-
|
|
435
|
-
|
|
455
|
+
constructor(
|
|
456
|
+
input,
|
|
457
|
+
options = {
|
|
458
|
+
dropdown: undefined,
|
|
459
|
+
optionSelector: String(),
|
|
460
|
+
onEscape: undefined,
|
|
461
|
+
onOpen: undefined,
|
|
462
|
+
selectionState: undefined,
|
|
463
|
+
}
|
|
464
|
+
) {
|
|
436
465
|
this.#input = input;
|
|
437
466
|
this.#dropdown = options.dropdown;
|
|
438
467
|
this.#option_selector = options.optionSelector;
|
|
@@ -455,7 +484,8 @@ class ComboboxKeyboard {
|
|
|
455
484
|
// Роли и id нужны всем опциям, даже когда дропдаун ещё закрыт
|
|
456
485
|
for (const option of this.#allOptions()) {
|
|
457
486
|
if (option.getAttribute("role") === null) option.setAttribute("role", "option");
|
|
458
|
-
if (option.id === undefined || option.id === "")
|
|
487
|
+
if (option.id === undefined || option.id === "")
|
|
488
|
+
option.id = "chui_a11y_option_" + Math.random().toString(36).slice(2, 9);
|
|
459
489
|
}
|
|
460
490
|
this.#applyActive();
|
|
461
491
|
}
|
|
@@ -477,7 +507,7 @@ class ComboboxKeyboard {
|
|
|
477
507
|
}
|
|
478
508
|
|
|
479
509
|
#options() {
|
|
480
|
-
return this.#allOptions().filter(option => isVisible(option) && !isDisabled(option));
|
|
510
|
+
return this.#allOptions().filter((option) => isVisible(option) && !isDisabled(option));
|
|
481
511
|
}
|
|
482
512
|
|
|
483
513
|
#applyActive() {
|
|
@@ -511,7 +541,8 @@ class ComboboxKeyboard {
|
|
|
511
541
|
case "ArrowDown":
|
|
512
542
|
case "ArrowUp": {
|
|
513
543
|
event.preventDefault();
|
|
514
|
-
if (this.#input.getAttribute("aria-expanded") !== "true" && this.#on_open !== undefined)
|
|
544
|
+
if (this.#input.getAttribute("aria-expanded") !== "true" && this.#on_open !== undefined)
|
|
545
|
+
this.#on_open();
|
|
515
546
|
this.#move(event.key === "ArrowDown" ? 1 : -1);
|
|
516
547
|
break;
|
|
517
548
|
}
|
|
@@ -547,7 +578,7 @@ class ComboboxKeyboard {
|
|
|
547
578
|
default:
|
|
548
579
|
break;
|
|
549
580
|
}
|
|
550
|
-
}
|
|
581
|
+
};
|
|
551
582
|
|
|
552
583
|
destroy() {
|
|
553
584
|
this.#input.removeEventListener("keydown", this.#keydownEvent);
|
|
@@ -582,7 +613,7 @@ class RovingKeyboard {
|
|
|
582
613
|
/** Перечитать пункты: остановка остаётся на прежнем, если он ещё доступен */
|
|
583
614
|
refresh() {
|
|
584
615
|
const visible = this.#items();
|
|
585
|
-
const current = visible.find(item => item.getAttribute("tabindex") === "0");
|
|
616
|
+
const current = visible.find((item) => item.getAttribute("tabindex") === "0");
|
|
586
617
|
// Панель может быть ещё не в документе (размеры нулевые) — тогда остановку получает
|
|
587
618
|
// первый пункт: без неё группа целиком выпала бы из обхода Tab
|
|
588
619
|
const fallback = visible[0] === undefined ? this.#allItems()[0] : visible[0];
|
|
@@ -598,13 +629,13 @@ class RovingKeyboard {
|
|
|
598
629
|
|
|
599
630
|
// Пункты, по которым ходит клавиатура: видимые и не запрещённые
|
|
600
631
|
#items() {
|
|
601
|
-
return this.#allItems().filter(item => !isDisabled(item) && isVisible(item));
|
|
632
|
+
return this.#allItems().filter((item) => !isDisabled(item) && isVisible(item));
|
|
602
633
|
}
|
|
603
634
|
|
|
604
635
|
#activeIndex(items) {
|
|
605
|
-
const focused = items.findIndex(item => item === document.activeElement);
|
|
636
|
+
const focused = items.findIndex((item) => item === document.activeElement);
|
|
606
637
|
if (focused !== -1) return focused;
|
|
607
|
-
return items.findIndex(item => item.getAttribute("tabindex") === "0");
|
|
638
|
+
return items.findIndex((item) => item.getAttribute("tabindex") === "0");
|
|
608
639
|
}
|
|
609
640
|
|
|
610
641
|
#applyStop(item) {
|
|
@@ -659,10 +690,10 @@ class RovingKeyboard {
|
|
|
659
690
|
}
|
|
660
691
|
}
|
|
661
692
|
|
|
662
|
-
exports.MenuKeyboard = MenuKeyboard
|
|
663
|
-
exports.ComboboxKeyboard = ComboboxKeyboard
|
|
664
|
-
exports.RovingKeyboard = RovingKeyboard
|
|
665
|
-
exports.A11Y_ACTIVE_CLASS = ACTIVE_CLASS
|
|
693
|
+
exports.MenuKeyboard = MenuKeyboard;
|
|
694
|
+
exports.ComboboxKeyboard = ComboboxKeyboard;
|
|
695
|
+
exports.RovingKeyboard = RovingKeyboard;
|
|
696
|
+
exports.A11Y_ACTIVE_CLASS = ACTIVE_CLASS;
|
|
666
697
|
|
|
667
698
|
/**
|
|
668
699
|
* Перенести фокус на следующий (или предыдущий) видимый фокусируемый элемент страницы.
|
|
@@ -670,7 +701,7 @@ exports.A11Y_ACTIVE_CLASS = ACTIVE_CLASS
|
|
|
670
701
|
* браузер уведёт фокус в `body`, а не к следующему полю. `skip` — узел или поддерево,
|
|
671
702
|
* которое в обходе не участвует (например, сам список).
|
|
672
703
|
*/
|
|
673
|
-
function moveFocusAway(from, options = {backwards: false, skip: undefined}) {
|
|
704
|
+
function moveFocusAway(from, options = { backwards: false, skip: undefined }) {
|
|
674
705
|
const nodes = [...document.querySelectorAll(FOCUSABLE_SELECTOR)]
|
|
675
706
|
.filter((node) => node.disabled !== true)
|
|
676
707
|
.filter((node) => isVisible(node))
|
|
@@ -684,4 +715,4 @@ function moveFocusAway(from, options = {backwards: false, skip: undefined}) {
|
|
|
684
715
|
return true;
|
|
685
716
|
}
|
|
686
717
|
|
|
687
|
-
exports.moveFocusAway = moveFocusAway
|
|
718
|
+
exports.moveFocusAway = moveFocusAway;
|
|
@@ -4,7 +4,7 @@ class Animation {
|
|
|
4
4
|
#element = undefined;
|
|
5
5
|
|
|
6
6
|
constructor(element) {
|
|
7
|
-
require(
|
|
7
|
+
require("../chui_functions").setStyles(__dirname + "/styles.css", "chUiJS_Animations");
|
|
8
8
|
if (element !== undefined) this.#element = element;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -14,101 +14,101 @@ class Animation {
|
|
|
14
14
|
* доиграла, этот обработчик прятал уже открытый слой — вид расходился с состоянием.
|
|
15
15
|
*/
|
|
16
16
|
#cancelHide() {
|
|
17
|
-
this.#element.removeEventListener(
|
|
18
|
-
this.#element.style.removeProperty(
|
|
17
|
+
this.#element.removeEventListener("animationend", removeEvents);
|
|
18
|
+
this.#element.style.removeProperty("animation-name");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
fadeIn() {
|
|
22
22
|
this.#cancelHide();
|
|
23
|
-
if (window.getComputedStyle(this.#element, null).display ===
|
|
24
|
-
if (this.#element.tagName !== "TABLE") this.#element.style.display =
|
|
23
|
+
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
24
|
+
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
25
25
|
}
|
|
26
|
-
this.#element.style.transition = "transform .0s"
|
|
27
|
-
this.#element.style.animationName =
|
|
26
|
+
this.#element.style.transition = "transform .0s";
|
|
27
|
+
this.#element.style.animationName = "fade-in";
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
fadeOut() {
|
|
31
|
-
this.#element.style.transition = "transform .0s"
|
|
31
|
+
this.#element.style.transition = "transform .0s";
|
|
32
32
|
if (this.#element.style.animationName) {
|
|
33
|
-
this.#element.style.animationName =
|
|
34
|
-
this.#element.addEventListener(
|
|
33
|
+
this.#element.style.animationName = "fade-out";
|
|
34
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// SCALE
|
|
39
39
|
scaleIn() {
|
|
40
40
|
this.#cancelHide();
|
|
41
|
-
if (window.getComputedStyle(this.#element, null).display ===
|
|
42
|
-
if (this.#element.tagName !== "TABLE") this.#element.style.display =
|
|
41
|
+
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
42
|
+
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
43
43
|
}
|
|
44
|
-
this.#element.style.animationName =
|
|
44
|
+
this.#element.style.animationName = "scale-in";
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
scaleOut() {
|
|
48
48
|
if (this.#element.style.animationName) {
|
|
49
|
-
this.#element.style.animationName =
|
|
50
|
-
this.#element.addEventListener(
|
|
49
|
+
this.#element.style.animationName = "scale-out";
|
|
50
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// SLIDE RIGHT
|
|
55
55
|
slideRightIn() {
|
|
56
56
|
this.#cancelHide();
|
|
57
|
-
if (window.getComputedStyle(this.#element, null).display ===
|
|
58
|
-
if (this.#element.tagName !== "TABLE") this.#element.style.display =
|
|
57
|
+
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
58
|
+
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
59
59
|
}
|
|
60
|
-
this.#element.style.animationName =
|
|
60
|
+
this.#element.style.animationName = "slide-right-in";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
slideRightOut() {
|
|
64
64
|
if (this.#element.style.animationName) {
|
|
65
|
-
this.#element.style.animationName =
|
|
66
|
-
this.#element.addEventListener(
|
|
65
|
+
this.#element.style.animationName = "slide-right-out";
|
|
66
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// SLIDE BOTTOM
|
|
71
71
|
slideBottomIn() {
|
|
72
72
|
this.#cancelHide();
|
|
73
|
-
if (window.getComputedStyle(this.#element, null).display ===
|
|
74
|
-
if (this.#element.tagName !== "TABLE") this.#element.style.display =
|
|
73
|
+
if (window.getComputedStyle(this.#element, null).display === "none") {
|
|
74
|
+
if (this.#element.tagName !== "TABLE") this.#element.style.display = "flex";
|
|
75
75
|
}
|
|
76
|
-
this.#element.style.animationName =
|
|
76
|
+
this.#element.style.animationName = "slide-bottom-in";
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
slideBottomOut() {
|
|
80
80
|
if (this.#element.style.animationName) {
|
|
81
|
-
this.#element.style.animationName =
|
|
82
|
-
this.#element.addEventListener(
|
|
81
|
+
this.#element.style.animationName = "slide-bottom-out";
|
|
82
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// REMOVE
|
|
87
87
|
slideBottomOutAndRemove() {
|
|
88
88
|
if (this.#element.style.animationName) {
|
|
89
|
-
this.#element.style.animationName =
|
|
90
|
-
this.#element.addEventListener(
|
|
89
|
+
this.#element.style.animationName = "slide-bottom-out";
|
|
90
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
slideRightOutAndRemove() {
|
|
95
95
|
if (this.#element.style.animationName) {
|
|
96
|
-
this.#element.style.animationName =
|
|
97
|
-
this.#element.addEventListener(
|
|
96
|
+
this.#element.style.animationName = "slide-right-out";
|
|
97
|
+
this.#element.addEventListener("animationend", removeEvents);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
scaleOutAndRemove() {
|
|
102
102
|
if (this.#element.style.animationName) {
|
|
103
|
-
this.#element.style.animationName =
|
|
104
|
-
this.#element.addEventListener(
|
|
103
|
+
this.#element.style.animationName = "scale-out";
|
|
104
|
+
this.#element.addEventListener("animationend", removeEvents2);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
fadeOutAndRemove() {
|
|
109
109
|
if (this.#element.style.animationName) {
|
|
110
|
-
this.#element.style.animationName =
|
|
111
|
-
this.#element.addEventListener(
|
|
110
|
+
this.#element.style.animationName = "fade-out";
|
|
111
|
+
this.#element.addEventListener("animationend", removeEvents2);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -118,19 +118,19 @@ class Animation {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
removeStyles() {
|
|
121
|
-
this.#element.removeAttribute("style")
|
|
121
|
+
this.#element.removeAttribute("style");
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const removeEvents2 = (event) => {
|
|
126
|
-
event.target.remove()
|
|
127
|
-
event.target.removeEventListener(
|
|
128
|
-
}
|
|
126
|
+
event.target.remove();
|
|
127
|
+
event.target.removeEventListener("animationend", removeEvents2);
|
|
128
|
+
};
|
|
129
129
|
|
|
130
130
|
const removeEvents = (event) => {
|
|
131
|
-
event.target.style.removeProperty(
|
|
132
|
-
event.target.style.removeProperty(
|
|
133
|
-
event.target.removeEventListener(
|
|
134
|
-
}
|
|
131
|
+
event.target.style.removeProperty("animation-name");
|
|
132
|
+
event.target.style.removeProperty("display");
|
|
133
|
+
event.target.removeEventListener("animationend", removeEvents);
|
|
134
|
+
};
|
|
135
135
|
|
|
136
|
-
exports.Animation = Animation
|
|
136
|
+
exports.Animation = Animation;
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
const {contextBridge, ipcRenderer} = require("electron");
|
|
2
|
-
const {
|
|
1
|
+
const { contextBridge, ipcRenderer } = require("electron");
|
|
2
|
+
const {
|
|
3
|
+
SERVICES_CHANNELS,
|
|
4
|
+
SERVICES_CALLS,
|
|
5
|
+
SERVICES_SYNC_CALLS,
|
|
6
|
+
BRIDGE_CHANNELS,
|
|
7
|
+
BRIDGE_ARGS,
|
|
8
|
+
} = require("../chui_services/chui_channels");
|
|
3
9
|
// Мост переиспользует ту же обёртку сервисов, что и страница: разница только в транспорте
|
|
4
10
|
// (`Desktop` в preload'е сам вызовет ipcRenderer, потому что `window.chui` ему не виден)
|
|
5
|
-
const {Desktop} = require("../chui_services/desktop");
|
|
11
|
+
const { Desktop } = require("../chui_services/desktop");
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* Preload безопасного режима (`Main({security: {contextIsolation: true}})`).
|
|
@@ -16,8 +22,10 @@ const {Desktop} = require("../chui_services/desktop");
|
|
|
16
22
|
* файл при `DOMContentLoaded`.
|
|
17
23
|
*
|
|
18
24
|
* Формы API:
|
|
19
|
-
* - `window.chui.desktop.{clipboard, recent, progress, shortcuts}` — то же, что `Desktop` из
|
|
25
|
+
* - `window.chui.desktop.{app, clipboard, recent, progress, shortcuts}` — то же, что `Desktop` из
|
|
20
26
|
* `require('chuijs')`, поэтому код страницы переносится между режимами почти без правок;
|
|
27
|
+
* `desktop.app.quit()`/`restart()` закрывают и перезапускают всё приложение, а не окно; ответ
|
|
28
|
+
* у них значением, без промиса (синхронный канал);
|
|
21
29
|
* - `window.chui.services` — плоская карта «метод → канал» (транспорт, который ищет `Desktop`);
|
|
22
30
|
* - `window.chui.window` — минимизировать, развернуть, закрыть, состояние окна;
|
|
23
31
|
* - `window.chui.theme` — текущая тема и подписка на её смену;
|
|
@@ -30,7 +38,10 @@ function argValue(prefix = String()) {
|
|
|
30
38
|
|
|
31
39
|
const renderPath = argValue(BRIDGE_ARGS.RENDER);
|
|
32
40
|
const csp = argValue(BRIDGE_ARGS.CSP);
|
|
33
|
-
const channels = (argValue(BRIDGE_ARGS.CHANNELS) ?? "")
|
|
41
|
+
const channels = (argValue(BRIDGE_ARGS.CHANNELS) ?? "")
|
|
42
|
+
.split(",")
|
|
43
|
+
.map((channel) => channel.trim())
|
|
44
|
+
.filter(Boolean);
|
|
34
45
|
|
|
35
46
|
/**
|
|
36
47
|
* Content-Security-Policy вставляется в документ до его скриптов: preload выполняется раньше
|
|
@@ -48,7 +59,7 @@ function applyCsp() {
|
|
|
48
59
|
return true;
|
|
49
60
|
}
|
|
50
61
|
|
|
51
|
-
if (!applyCsp()) document.addEventListener("readystatechange", () => applyCsp(), {once: true});
|
|
62
|
+
if (!applyCsp()) document.addEventListener("readystatechange", () => applyCsp(), { once: true });
|
|
52
63
|
|
|
53
64
|
const call = (method = String(), ...args) => {
|
|
54
65
|
const channel = SERVICES_CALLS[method];
|
|
@@ -74,6 +85,12 @@ const services = {};
|
|
|
74
85
|
for (const method of Object.keys(SERVICES_CALLS)) {
|
|
75
86
|
services[method] = (...args) => call(method, ...args);
|
|
76
87
|
}
|
|
88
|
+
// Синхронные вызовы — отдельным циклом и через `sendSync`: на синхронный канал `invoke`
|
|
89
|
+
// не отвечает, промис никогда бы не завершился. Конверт `{value}`/`{error}` разбирает
|
|
90
|
+
// `Desktop.#callSync`, поэтому мост отдаёт ответ как есть — как и обычный канал
|
|
91
|
+
for (const [method, channel] of Object.entries(SERVICES_SYNC_CALLS)) {
|
|
92
|
+
services[method] = (...args) => ipcRenderer.sendSync(channel, ...args);
|
|
93
|
+
}
|
|
77
94
|
// Событие глобальной клавиши: подписка возвращает функцию отписки
|
|
78
95
|
services.onShortcut = (handler = () => {}) => {
|
|
79
96
|
const listener = (_event, accelerator) => handler(accelerator);
|
|
@@ -92,7 +109,7 @@ contextBridge.exposeInMainWorld("chui", {
|
|
|
92
109
|
versions: {
|
|
93
110
|
node: process.versions.node,
|
|
94
111
|
chrome: process.versions.chrome,
|
|
95
|
-
electron: process.versions.electron
|
|
112
|
+
electron: process.versions.electron,
|
|
96
113
|
},
|
|
97
114
|
platform: process.platform,
|
|
98
115
|
/** Путь к странице окна (как он задан в опции `render`) */
|
|
@@ -112,53 +129,64 @@ contextBridge.exposeInMainWorld("chui", {
|
|
|
112
129
|
copy: (text = String()) => Desktop.clipboard.history.copy(text),
|
|
113
130
|
remove: (target = undefined) => Desktop.clipboard.history.remove(target),
|
|
114
131
|
clear: () => Desktop.clipboard.history.clear(),
|
|
115
|
-
onChange: (listener = () => {}) => services.onClipboardHistory(listener)
|
|
116
|
-
}
|
|
132
|
+
onChange: (listener = () => {}) => services.onClipboardHistory(listener),
|
|
133
|
+
},
|
|
117
134
|
},
|
|
118
135
|
recent: {
|
|
119
136
|
add: (path = String(), name = undefined) => Desktop.recent.add(path, name),
|
|
120
137
|
clear: () => Desktop.recent.clear(),
|
|
121
|
-
list: () => Desktop.recent.list()
|
|
138
|
+
list: () => Desktop.recent.list(),
|
|
122
139
|
},
|
|
123
140
|
progress: {
|
|
124
141
|
set: (value = Number(), mode = "normal") => Desktop.progress.set(value, mode),
|
|
125
|
-
clear: () => Desktop.progress.clear()
|
|
142
|
+
clear: () => Desktop.progress.clear(),
|
|
126
143
|
},
|
|
127
144
|
shell: {
|
|
128
|
-
openExternal: (url = String()) => Desktop.shell.openExternal(url)
|
|
145
|
+
openExternal: (url = String()) => Desktop.shell.openExternal(url),
|
|
129
146
|
},
|
|
130
147
|
app: {
|
|
148
|
+
// Путь приходит значением, без промиса (синхронный канал), а `Desktop` в preload'е
|
|
149
|
+
// идёт в него сам: `window.chui` своей же странице здесь ещё не виден
|
|
131
150
|
path: (name = String()) => Desktop.app.path(name),
|
|
132
|
-
info: () => Desktop.app.info()
|
|
151
|
+
info: () => Desktop.app.info(),
|
|
152
|
+
// Сырые модули Electron странице не отдают. Методы есть для единообразия
|
|
153
|
+
// с `Desktop`, но со страницы бросают ошибку с подсказкой, что взять взамен
|
|
154
|
+
get: () => Desktop.app.get(),
|
|
155
|
+
getSession: () => Desktop.app.getSession(),
|
|
156
|
+
getWebContents: () => Desktop.app.getWebContents(),
|
|
157
|
+
// Закрытие и перезапуск — как у `Desktop` из пакета: те же шаги, что у пункта меню.
|
|
158
|
+
// Отвечают значением, а не промисом (синхронный канал), поэтому `await` не нужен
|
|
159
|
+
quit: () => Desktop.app.quit(),
|
|
160
|
+
restart: () => Desktop.app.restart(),
|
|
133
161
|
},
|
|
134
162
|
dialog: {
|
|
135
163
|
open: (options = {}) => Desktop.dialog.open(options),
|
|
136
164
|
save: (options = {}) => Desktop.dialog.save(options),
|
|
137
|
-
message: (options = {}) => Desktop.dialog.message(options)
|
|
165
|
+
message: (options = {}) => Desktop.dialog.message(options),
|
|
138
166
|
},
|
|
139
167
|
session: {
|
|
140
168
|
getCacheSize: () => Desktop.session.getCacheSize(),
|
|
141
169
|
clearCache: () => Desktop.session.clearCache(),
|
|
142
|
-
clearStorageData: (options = {}) => Desktop.session.clearStorageData(options)
|
|
170
|
+
clearStorageData: (options = {}) => Desktop.session.clearStorageData(options),
|
|
143
171
|
},
|
|
144
172
|
print: {
|
|
145
173
|
page: (options = {}) => Desktop.print.page(options),
|
|
146
174
|
html: (html = String(), options = {}) => Desktop.print.html(html, options),
|
|
147
|
-
pdf: (html = String(), options = {}) => Desktop.print.pdf(html, options)
|
|
175
|
+
pdf: (html = String(), options = {}) => Desktop.print.pdf(html, options),
|
|
148
176
|
},
|
|
149
177
|
shortcuts: {
|
|
150
178
|
on: (accelerator = String(), handler = () => {}) => Desktop.shortcuts.on(accelerator, handler),
|
|
151
179
|
off: (accelerator = String(), handler = () => {}) => Desktop.shortcuts.off(accelerator, handler),
|
|
152
180
|
offAll: () => Desktop.shortcuts.offAll(),
|
|
153
|
-
get: () => Desktop.shortcuts.get()
|
|
154
|
-
}
|
|
181
|
+
get: () => Desktop.shortcuts.get(),
|
|
182
|
+
},
|
|
155
183
|
},
|
|
156
184
|
window: {
|
|
157
185
|
minimize: () => windowAction("minimize"),
|
|
158
186
|
toggleMaximize: () => windowAction("toggle-maximize"),
|
|
159
187
|
restore: () => windowAction("restore"),
|
|
160
188
|
close: () => windowAction("close"),
|
|
161
|
-
getState: () => ipcRenderer.invoke(BRIDGE_CHANNELS.WINDOW_STATE)
|
|
189
|
+
getState: () => ipcRenderer.invoke(BRIDGE_CHANNELS.WINDOW_STATE),
|
|
162
190
|
},
|
|
163
191
|
theme: {
|
|
164
192
|
get: () => ipcRenderer.invoke(BRIDGE_CHANNELS.THEME_GET),
|
|
@@ -168,7 +196,7 @@ contextBridge.exposeInMainWorld("chui", {
|
|
|
168
196
|
onChange: (listener = () => {}) => {
|
|
169
197
|
themeListeners.add(listener);
|
|
170
198
|
return () => themeListeners.delete(listener);
|
|
171
|
-
}
|
|
199
|
+
},
|
|
172
200
|
},
|
|
173
201
|
locale: {
|
|
174
202
|
get: () => ipcRenderer.invoke(BRIDGE_CHANNELS.LOCALE_GET),
|
|
@@ -177,17 +205,19 @@ contextBridge.exposeInMainWorld("chui", {
|
|
|
177
205
|
onChange: (listener = () => {}) => {
|
|
178
206
|
localeListeners.add(listener);
|
|
179
207
|
return () => localeListeners.delete(listener);
|
|
180
|
-
}
|
|
208
|
+
},
|
|
181
209
|
},
|
|
182
210
|
messaging: {
|
|
183
211
|
/** Каналы задаёт приложение: `security: {channels: ['preview:update']}` */
|
|
184
212
|
channels: [...channels],
|
|
185
213
|
send: (channel = String(), ...args) => {
|
|
186
|
-
if (!channels.includes(channel))
|
|
214
|
+
if (!channels.includes(channel))
|
|
215
|
+
throw new Error(`chui: канал «${channel}» не разрешён в security.channels`);
|
|
187
216
|
return ipcRenderer.invoke(BRIDGE_CHANNELS.MESSAGE, channel, ...args);
|
|
188
217
|
},
|
|
189
218
|
on: (channel = String(), listener = () => {}) => {
|
|
190
|
-
if (!channels.includes(channel))
|
|
219
|
+
if (!channels.includes(channel))
|
|
220
|
+
throw new Error(`chui: канал «${channel}» не разрешён в security.channels`);
|
|
191
221
|
const listeners = messageListeners.get(channel) ?? new Set();
|
|
192
222
|
listeners.add(listener);
|
|
193
223
|
messageListeners.set(channel, listeners);
|
|
@@ -195,8 +225,8 @@ contextBridge.exposeInMainWorld("chui", {
|
|
|
195
225
|
listeners.delete(listener);
|
|
196
226
|
if (listeners.size === 0) messageListeners.delete(channel);
|
|
197
227
|
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
228
|
+
},
|
|
229
|
+
},
|
|
200
230
|
});
|
|
201
231
|
|
|
202
232
|
// Скрипт страницы подключается здесь: в безопасном режиме `render` — это скрипт документа,
|
|
@@ -208,6 +238,6 @@ if (renderPath !== undefined) {
|
|
|
208
238
|
script.defer = true;
|
|
209
239
|
document.head.appendChild(script);
|
|
210
240
|
};
|
|
211
|
-
if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", addPageScript, {once: true});
|
|
241
|
+
if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", addPageScript, { once: true });
|
|
212
242
|
else addPageScript();
|
|
213
243
|
}
|