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
package/README.md
CHANGED
|
@@ -1,660 +1,71 @@
|
|
|
1
1
|
# chuijs
|
|
2
|
-
### CHROME_SANDBOX FIX
|
|
3
|
-
### CHROME\_SANDBOX FIX 1
|
|
4
|
-
```shell
|
|
5
|
-
cd node_modules/electron/dist && sudo chown root chrome-sandbox && sudo chmod 4755 chrome-sandbox
|
|
6
|
-
```
|
|
7
|
-
### Структура
|
|
8
|
-
#### exampleApp / app / views / main.js
|
|
9
|
-
```javascript
|
|
10
|
-
const { Page, Button, TextInput, Notification, Log } = require('chuijs');
|
|
11
|
-
class MainPage extends Page {
|
|
12
|
-
constructor() {
|
|
13
|
-
super();
|
|
14
|
-
this.setTitle('Сказать привет!');
|
|
15
|
-
this.setMain(false)
|
|
16
|
-
|
|
17
|
-
let name = new TextInput({
|
|
18
|
-
title: 'Введите ваше имя',
|
|
19
|
-
placeholder: 'Введите ваше имя',
|
|
20
|
-
required: false
|
|
21
|
-
});
|
|
22
|
-
let hello = new Button({
|
|
23
|
-
title: "Сказать привет!",
|
|
24
|
-
clickEvent: () => {
|
|
25
|
-
if (name.getValue() !== "") {
|
|
26
|
-
new Notification({
|
|
27
|
-
title: `Привет, ${name.getValue()}!`, text: `Привет, ${name.getValue()}!`,
|
|
28
|
-
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
29
|
-
}).show();
|
|
30
|
-
} else {
|
|
31
|
-
new Notification({
|
|
32
|
-
title: `Привет мир!`, text: `Привет мир!`,
|
|
33
|
-
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
34
|
-
}).show();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
this.add(name, hello)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
exports.MainPage = MainPage
|
|
42
|
-
```
|
|
43
|
-
#### exampleApp / app / app.js
|
|
44
|
-
```javascript
|
|
45
|
-
/** RENDERER ПРОЦЕСС */
|
|
46
|
-
/** ИМПОРТЫ */
|
|
47
|
-
const { AppLayout, render, Log } = require('chuijs');
|
|
48
|
-
/** СТРАНИЦЫ */
|
|
49
|
-
const { MainPage } = require('../app/views/main');
|
|
50
|
-
class App extends AppLayout {
|
|
51
|
-
constructor() {
|
|
52
|
-
super();
|
|
53
|
-
/** РОУТЫ */
|
|
54
|
-
this.setRoute(new MainPage());
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
render(() => new App()).catch(err => Log.error(err))
|
|
58
|
-
```
|
|
59
|
-
`render(callback)` возвращает `Promise`, который резолвится после `DOMContentLoaded` (или сразу, если документ уже загружен) результатом `callback` и реджектится его ошибкой.
|
|
60
|
-
#### exampleApp / main.js
|
|
61
|
-
```javascript
|
|
62
|
-
/** main.js */
|
|
63
|
-
const { Main, MenuItem, path, App } = require('chuijs');
|
|
64
|
-
const main = new Main({
|
|
65
|
-
name: "exampleApp",
|
|
66
|
-
// размеры окна задаются вложенным объектом sizes
|
|
67
|
-
sizes: {
|
|
68
|
-
width: 1366,
|
|
69
|
-
height: 768,
|
|
70
|
-
minWidth: 960,
|
|
71
|
-
minHeight: 540
|
|
72
|
-
},
|
|
73
|
-
render: `${__dirname}/app/app.js`,
|
|
74
|
-
devTools: false,
|
|
75
|
-
resizable: true,
|
|
76
|
-
// папка загрузок (по умолчанию App.downloadsPath())
|
|
77
|
-
paths: {
|
|
78
|
-
downloadPath: path.join(App.userDataPath(), "downloads")
|
|
79
|
-
}
|
|
80
|
-
/** icon: `${__dirname}/resources/icons/app/icon.png` */
|
|
81
|
-
});
|
|
82
|
-
main.start({
|
|
83
|
-
hideOnClose: false,
|
|
84
|
-
tray: [
|
|
85
|
-
new MenuItem().help(`exampleApp`),
|
|
86
|
-
new MenuItem().button('Консоль разработчика', () => main.toggleDevTools()),
|
|
87
|
-
new MenuItem().separator(),
|
|
88
|
-
new MenuItem().quit("Выход")
|
|
89
|
-
]
|
|
90
|
-
})
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
> `Main` — обычный класс, а не singleton: конструктор только сохраняет опции
|
|
94
|
-
> и настраивает `app.commandLine`. Окно создаётся в `start()` (внутри `app.whenReady()`),
|
|
95
|
-
> поэтому окно доступно через `main.getWindow()` только после старта.
|
|
96
|
-
#### Download Session в main.js
|
|
97
|
-
```javascript
|
|
98
|
-
const { App } = require('chuijs');
|
|
99
|
-
App.get().on('session-created', (session) => {
|
|
100
|
-
session.on('will-download', (e, item, contents) => {
|
|
101
|
-
// ...
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
#### TelegramBot API
|
|
106
|
-
```javascript
|
|
107
|
-
const { TelegramBot } = require('chuijs');
|
|
108
|
-
let bot = new TelegramBot("token");
|
|
109
|
-
bot.setToken("token");
|
|
110
|
-
|
|
111
|
-
// Функции
|
|
112
|
-
async function run() {
|
|
113
|
-
await bot.getMe();
|
|
114
|
-
await bot.getUpdates();
|
|
115
|
-
await bot.getChat("id");
|
|
116
|
-
/* ... */
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
### Модули
|
|
120
|
-
|
|
121
|
-
Инфраструктура и утилиты из корня пакета: `const { Main, MenuItem, Log, path } = require('chuijs');`
|
|
122
|
-
|
|
123
|
-
#### `Main`
|
|
124
|
-
Создаёт окно, трей и управляет жизненным циклом приложения.
|
|
125
|
-
|
|
126
|
-
```javascript
|
|
127
|
-
const { Main, MenuItem, App } = require('chuijs');
|
|
128
|
-
const main = new Main({
|
|
129
|
-
name: 'exampleApp',
|
|
130
|
-
sizes: { width: 1366, height: 768, minWidth: 960, minHeight: 540 },
|
|
131
|
-
render: `${__dirname}/app/app.js`,
|
|
132
|
-
devTools: false,
|
|
133
|
-
resizable: true,
|
|
134
|
-
paths: { downloadPath: path.join(App.userDataPath(), 'downloads') }
|
|
135
|
-
});
|
|
136
|
-
main.start({ hideOnClose: false, tray: [new MenuItem().quit('Выход')] });
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Опции конструктора: `name`, `sizes {width, height, minWidth, minHeight}`, `render`, `devTools`,
|
|
140
|
-
`webSecurity`, `resizable`, `security {contextIsolation, csp, channels}`, `paths {downloadPath}`, `icon`.
|
|
141
|
-
Если `icon` не передан, `Main` сам подставляет иконку приложения по умолчанию.
|
|
142
|
-
`security` включает безопасный режим окна (см. «Безопасный режим (preload + contextBridge + CSP)»),
|
|
143
|
-
по умолчанию режим прежний — Node в рендерере.
|
|
144
|
-
`paths.downloadPath` — папка загрузок: если она задана, `Main` подписывается на `will-download`
|
|
145
|
-
сессии и сохраняет файлы туда (папка создаётся при необходимости, существующий файл не перезаписывается).
|
|
146
|
-
Без опции Electron выбирает путь сам, как и раньше.
|
|
147
|
-
`start()` принимает `{hideOnClose, globalMenu, tray, extensions}` и создаёт окно внутри `app.whenReady()`.
|
|
148
|
-
API: `getWindow(name?)`, `getMainWindow()`, `getWindows()`, `createWindow(options)`, `focusWindow(name)`,
|
|
149
|
-
`closeWindow(name)`, `sendTo(name, channel, ...args)`, `toggleDevTools(name?)`, `enableServices()`,
|
|
150
|
-
`stop()`, `restart()`, `enableAutoUpdateApp(start)`, `sendDownload(title, body, windowName?)`,
|
|
151
|
-
`sendDownloadUpdate(...)`, `sendDownloadComplete(...)`, `sendDownloadError(...)`.
|
|
152
|
-
|
|
153
|
-
**Мультиоконность.** Главное окно регистрируется под именем `Main.MAIN_WINDOW` (`'main'`),
|
|
154
|
-
а дополнительные создаёт `createWindow()`: возвращает `BrowserWindow` и вызывается с `await`,
|
|
155
|
-
потому что окно можно создать только после `app.whenReady()`. У каждого окна свой скрипт
|
|
156
|
-
рендерера (`render`) и свой документ, поэтому `AppLayout` каждого окна ведёт свои узлы в
|
|
157
|
-
`chui_layout` (см. «Разметка и `chui_layout`») — окна не мешают друг другу.
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
const preview = await main.createWindow({
|
|
161
|
-
name: 'preview',
|
|
162
|
-
title: 'Просмотр',
|
|
163
|
-
sizes: { width: 900, height: 600 },
|
|
164
|
-
render: `${__dirname}/app/preview.js`,
|
|
165
|
-
hideOnClose: true
|
|
166
|
-
});
|
|
167
|
-
main.sendTo('preview', 'preview_update', payload); // сообщение в конкретное окно
|
|
168
|
-
main.focusWindow('preview');
|
|
169
|
-
main.closeWindow('preview'); // закрывает окно, не приложение
|
|
170
|
-
main.getWindows(); // [{name, window}, …]
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Закрытие окна снимает его подписки на глобальные клавиши и освобождает комбинации,
|
|
174
|
-
которые больше никто не слушает.
|
|
175
|
-
|
|
176
|
-
`enableAutoUpdateApp(start)` — задержка перед первой проверкой обновления в миллисекундах;
|
|
177
|
-
подписки апдейтера и подтверждение установки ставятся один раз на экземпляр `Main`.
|
|
178
|
-
`stop()` и `restart()` снимают свои обработчики: IPC (`show_system_notification`, `SEND_LOG_TEXT`,
|
|
179
|
-
`updateInstallConfirm`), обработчики десктоп-сервисов вместе с глобальными клавишами,
|
|
180
|
-
подписку апдейтера и глобальные обработчики ошибок процессов —
|
|
181
|
-
повторный `start()` подпишет их заново по одному разу.
|
|
182
|
-
|
|
183
|
-
#### `App`
|
|
184
|
-
Обёртка над `app` Electron. **Только для main-процесса:** странице модули main не отдают
|
|
185
|
-
(Electron не рекомендует `remote`), и её `App.get*` бросает ошибку с подсказкой. Со страницы
|
|
186
|
-
берут `Desktop.app`, `Desktop.session`, `Desktop.dialog` или свой канал из `security.channels`.
|
|
187
|
-
|
|
188
|
-
```javascript
|
|
189
|
-
const { App } = require('chuijs');
|
|
190
|
-
App.get(); // app
|
|
191
|
-
App.getSession(); // session
|
|
192
|
-
App.getWebContents(); // webContents
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
#### `App.*Path()`
|
|
196
|
-
Статические системные пути (все возвращают строку). **Только для main-процесса;** со страницы —
|
|
197
|
-
`await Desktop.app.path('userData')` (там путь приходит по IPC и потому асинхронно).
|
|
198
|
-
|
|
199
|
-
```javascript
|
|
200
|
-
const { App } = require('chuijs');
|
|
201
|
-
App.userDataPath(); // .../AppData/<name>
|
|
202
|
-
App.downloadsPath();
|
|
203
|
-
App.logsPath();
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Полный список: `homePath()`, `appDataPath()`, `userDataPath()`, `sessionDataPath()`, `logsPath()`, `tempPath()`, `exePath()`, `modulePath()`, `desktopPath()`, `documentsPath()`, `downloadsPath()`, `musicPath()`, `picturesPath()`, `videosPath()`, `recentPath()`, `crashDumpsPath()`.
|
|
207
|
-
|
|
208
|
-
#### `Log`
|
|
209
|
-
Пишет в `userData/logs/app_<date>.log`.
|
|
210
|
-
|
|
211
|
-
```javascript
|
|
212
|
-
Log.info('сообщение');
|
|
213
|
-
Log.error(new Error('что-то пошло не так'));
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Из renderer-процесса вызов уходит в main через `ipcRenderer.send("SEND_LOG_TEXT", ...)`.
|
|
217
|
-
|
|
218
|
-
Необработанные исключения и необработанные reject’ы промисов тоже попадают в этот лог:
|
|
219
|
-
в main — `process.on("uncaughtException"/"unhandledRejection")`, в renderer —
|
|
220
|
-
`window`-события `error` и `unhandledrejection`. Приложение при этом продолжает работу
|
|
221
|
-
(обработчики снимаются в `Main.stop()`/`restart()`).
|
|
222
|
-
|
|
223
|
-
#### `sleep`, `render`, `formatBytes`
|
|
224
|
-
Утилиты из `chui_functions`.
|
|
225
|
-
|
|
226
|
-
```javascript
|
|
227
|
-
await sleep(1000); // пауза, мс
|
|
228
|
-
render(() => new App()).catch(err => Log.error(err)); // точка входа renderer
|
|
229
|
-
formatBytes(1536); // "1.5 KiB"
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
`render(callback)` ждёт `DOMContentLoaded` и резолвится результатом `callback`.
|
|
233
|
-
|
|
234
|
-
#### Шрифты
|
|
235
|
-
`AppLayout` сам подключает базовую группу (текстовый `chui_I` + иконки `chui_mir`) — это ~1.5 МБ
|
|
236
|
-
CSS из 3.67 МБ всех шрифтов. Моноширинный `chui_mono` (~2.2 МБ) вставляется по требованию:
|
|
237
|
-
его вызывает `TextEditor`, а в своём CSS можно запросить явно:
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
require('chuijs/framework/modules/chui_fonts').installMono();
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
Повторные вызовы `install()`/`installMono()` бесплатны: если стиль уже в документе,
|
|
244
|
-
он не собирается заново.
|
|
245
|
-
|
|
246
|
-
#### Иконка приложения по умолчанию
|
|
247
|
-
`getDefaultIcon()` объявлена во внутреннем модуле `framework/modules/chui_functions` и
|
|
248
|
-
**наружу не реэкспортируется**: `require('chuijs').getDefaultIcon` — это `undefined`.
|
|
249
|
-
`Main` вызывает её сам, если опция `icon` не задана (на macOS результат дополнительно
|
|
250
|
-
прогоняется через `Main.#resizeIconTray()`). Свой путь задаётся опцией `icon`:
|
|
251
|
-
|
|
252
|
-
```javascript
|
|
253
|
-
new Main({ name: 'app', icon: `${__dirname}/resources/icon.png` });
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
#### `MenuItem`
|
|
257
|
-
Пункты меню трея и глобального меню (цепочка из 45 методов: `button`, `submenu`, `radio`,
|
|
258
|
-
`checkbox`, `help`, `quit`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `windowMenu` и др.).
|
|
259
|
-
|
|
260
|
-
```javascript
|
|
261
|
-
new MenuItem().help('exampleApp');
|
|
262
|
-
new MenuItem().button('Консоль разработчика', () => main.toggleDevTools());
|
|
263
|
-
new MenuItem().separator();
|
|
264
|
-
new MenuItem().submenu('Ещё', [new MenuItem().quit('Выход')]);
|
|
265
|
-
new MenuItem().quit('Выход');
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
#### `TelegramBot`
|
|
269
|
-
Обёртка над Telegram Bot HTTP API (26 методов).
|
|
270
2
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
await bot.getMe();
|
|
275
|
-
await bot.sendMessage({ chat_id: '123', text: 'привет' });
|
|
276
|
-
```
|
|
3
|
+
Компонентный фреймворк для Electron: интерфейс, десктоп-сервисы, локализация и доступность.
|
|
4
|
+
Около 140 компонентов — от кнопок и полей ввода до сетки данных, диаграммы Ганта и расписания —
|
|
5
|
+
плюс инфраструктура: окна и трей, IPC-мост, темы, словари, журнал, валидация.
|
|
277
6
|
|
|
278
|
-
|
|
279
|
-
Сервисы системы живут в main-процессе (`globalShortcut`, `clipboard`, `app.addRecentDocument`
|
|
280
|
-
доступны только там), а страница работает с ними через `Desktop`: он сам выбирает транспорт —
|
|
281
|
-
канал `ipcRenderer.invoke` в обычном режиме или методы `window.chui.services` (preload +
|
|
282
|
-
`contextBridge`) в безопасном. Имена каналов лежат в одном модуле `chui_services/chui_channels`,
|
|
283
|
-
поэтому main и рендерер не расходятся.
|
|
7
|
+
## Установка
|
|
284
8
|
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
await Desktop.clipboard.writeText('привет');
|
|
288
|
-
await Desktop.clipboard.readText(); // 'привет'
|
|
289
|
-
await Desktop.clipboard.readImage(); // data URL или undefined
|
|
290
|
-
await Desktop.recent.add('/path/report.pdf', 'report.pdf');
|
|
291
|
-
await Desktop.recent.list(); // [{path, name}] — своя история, см. ниже
|
|
292
|
-
await Desktop.progress.set(0.4); // 40% на иконке приложения (панель задач, док)
|
|
293
|
-
Desktop.progress.clear();
|
|
294
|
-
await Desktop.shell.openExternal('https://electronjs.org'); // ссылка — в браузере по умолчанию
|
|
295
|
-
const off = Desktop.shortcuts.on('Mod+Shift+P', () => palette.open());
|
|
296
|
-
off(); // снять подписку
|
|
297
|
-
await Desktop.theme.get(); // {theme: 'dark', source: 'light'}
|
|
298
|
-
await Desktop.theme.set(Desktop.theme.SOURCE.DARK); // 'dark' | 'light' | 'system'
|
|
299
|
-
await Desktop.theme.toggle(); // светлая ↔ тёмная
|
|
300
|
-
const offTheme = Desktop.theme.onChange((theme, source) => Log.info(theme));
|
|
301
|
-
offTheme();
|
|
302
|
-
await Desktop.print.page(); // системный диалог печати текущего окна
|
|
303
|
-
await Desktop.print.html('<h1>Отчёт</h1>'); // печать HTML из скрытого окна
|
|
304
|
-
await Desktop.print.pdf(html, {path: '/tmp/отчёт.pdf'}); // PDF без диалога (или с ним, без path)
|
|
305
|
-
await Desktop.app.path('userData'); // каталог настроек приложения
|
|
306
|
-
await Desktop.app.info(); // {name, version, locale, isPackaged}
|
|
307
|
-
const {canceled, filePaths} = await Desktop.dialog.open({properties: ['openFile']});
|
|
308
|
-
await Desktop.dialog.save({defaultPath: '/tmp/отчёт.pdf'});
|
|
309
|
-
await Desktop.dialog.message({type: 'question', message: 'Продолжить?'});
|
|
310
|
-
await Desktop.session.clearCache(); // кеш своей сессии
|
|
311
|
-
await Desktop.session.clearStorageData({storages: ['cookies']});
|
|
9
|
+
```shell
|
|
10
|
+
npm install chuijs
|
|
312
11
|
```
|
|
313
12
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
Main-сторона (для кода в `main.js`, без страницы):
|
|
319
|
-
|
|
320
|
-
```javascript
|
|
321
|
-
const { Shortcuts, Shell, Clipboard, RecentDocuments, TaskbarProgress, Theme, Print } = require('chuijs');
|
|
322
|
-
const shortcuts = new Shortcuts();
|
|
323
|
-
shortcuts.register('Mod+Shift+P', () => main.focusWindow('preview')); // false — комбинацию занял другой процесс
|
|
324
|
-
shortcuts.isRegistered('Mod+Shift+P');
|
|
325
|
-
shortcuts.destroy(); // снять клавиши и подписку на will-quit
|
|
326
|
-
|
|
327
|
-
const clipboard = new Clipboard();
|
|
328
|
-
clipboard.writeText('привет');
|
|
329
|
-
|
|
330
|
-
const shell = new Shell();
|
|
331
|
-
shell.openExternal('https://electronjs.org'); // false — схема не разрешена
|
|
332
|
-
Shell.isExternal('file:///etc/passwd'); // false: чужие схемы не открываются
|
|
13
|
+
Electron ставится в само приложение, фреймворк лишь использует его. Если под Linux Electron не
|
|
14
|
+
запускается из-за песочницы Chromium — нужен разовый шаг с `chrome-sandbox`, он описан в разделе
|
|
15
|
+
[«Начало работы»](docs/guide/start.html).
|
|
333
16
|
|
|
334
|
-
|
|
335
|
-
recent.add('/path/report.pdf', 'report.pdf');
|
|
336
|
-
recent.menuItem(); // {role: 'recentDocuments', submenu: [{role: 'clearRecentDocuments'}]}
|
|
17
|
+
## Быстрый старт
|
|
337
18
|
|
|
338
|
-
|
|
339
|
-
progress.set(0.4);
|
|
340
|
-
progress.setError();
|
|
341
|
-
progress.clear();
|
|
342
|
-
|
|
343
|
-
const printed = new Print();
|
|
344
|
-
printed.pdf('<h1>Отчёт</h1>', {path: '/tmp/отчёт.pdf'}); // без path откроется диалог сохранения
|
|
345
|
-
// прогресс загрузки на иконку приложения:
|
|
346
|
-
// session.on('will-download', (e, item) => TaskbarProgress.trackDownload(item, win));
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
`main.enableServices()` вызывается сам из `start()`/`createWindow()`, регистрирует обработчики IPC
|
|
350
|
-
и создаёт сервисы; повторный вызов ничего не делает, а `stop()`/`restart()` снимают обработчики
|
|
351
|
-
вместе с глобальными клавишами. Сервисы доступны и как `main.services.{shortcuts, clipboard,
|
|
352
|
-
recent, progress, theme}`.
|
|
353
|
-
|
|
354
|
-
**Тема (`Desktop.theme`).** Палитры разложены по `@media (prefers-color-scheme)`, поэтому смена
|
|
355
|
-
`nativeTheme.themeSource` перекрашивает интерфейс во всех окнах без правок в DOM. Источник
|
|
356
|
-
(`'system'`, `'light'`, `'dark'`) хранится в настройках приложения (`electron-store`) и
|
|
357
|
-
применяется при запуске — до создания окна, поэтому вспышки системной палитры нет. Подписка
|
|
358
|
-
`onChange(listener)` отдаёт фактическую тему при её смене — как системой, так и пользователем.
|
|
359
|
-
В main-процессе тот же сервис доступен как `main.services.theme` или отдельным классом:
|
|
19
|
+
`main.js` — окно и трей:
|
|
360
20
|
|
|
361
21
|
```javascript
|
|
362
|
-
const
|
|
363
|
-
theme.get(); // 'light' | 'dark' — фактическая тема
|
|
364
|
-
theme.getSource(); // 'system' | 'light' | 'dark' — выбор
|
|
365
|
-
theme.set('dark');
|
|
366
|
-
theme.toggle();
|
|
367
|
-
theme.restore(); // применить сохранённый выбор при старте
|
|
368
|
-
```
|
|
22
|
+
const { Main, MenuItem, Desktop, path } = require("chuijs");
|
|
369
23
|
|
|
370
|
-
Границы возможностей Electron: `RecentDocuments` ведёт свою историю (у Electron есть только
|
|
371
|
-
`app.addRecentDocument()` и `app.clearRecentDocuments()`, прочитать системный список нельзя),
|
|
372
|
-
`TaskbarProgress` помнит последнее значение сам (геттера прогресса нет), а `Shortcuts.register()`
|
|
373
|
-
до `app.whenReady()` бросает исключение вместо тихой ошибки. `Shell.openExternal()` открывает
|
|
374
|
-
только `http:`, `https:` и `mailto:` — так страница не сможет запустить локальный файл или
|
|
375
|
-
произвольную команду.
|
|
376
|
-
|
|
377
|
-
#### Безопасный режим (preload + `contextBridge` + CSP)
|
|
378
|
-
По умолчанию всё работает как раньше: в рендерер выдаётся Node (`nodeIntegration`), и компоненты
|
|
379
|
-
фреймворка живут в странице. Если окно показывает недоверенное содержимое, его можно открыть
|
|
380
|
-
без Node — опцией `security`:
|
|
381
|
-
|
|
382
|
-
```javascript
|
|
383
24
|
const main = new Main({
|
|
384
|
-
name:
|
|
25
|
+
name: "exampleApp",
|
|
26
|
+
sizes: { width: 1366, height: 768, minWidth: 960, minHeight: 540 },
|
|
385
27
|
render: `${__dirname}/app/app.js`,
|
|
386
|
-
|
|
387
|
-
security: { contextIsolation: true, csp: true, channels: ['preview:save'] }
|
|
28
|
+
paths: { downloadPath: path.join(Desktop.app.path("userData"), "downloads") },
|
|
388
29
|
});
|
|
389
30
|
|
|
390
|
-
|
|
391
|
-
const preview = await main.createWindow({
|
|
392
|
-
name: 'preview',
|
|
393
|
-
title: 'Предпросмотр',
|
|
394
|
-
sizes: { width: 800, height: 600 },
|
|
395
|
-
render: `${__dirname}/app/preview-page.js`, // скрипт документа, не preload
|
|
396
|
-
security: { contextIsolation: true, channels: ['preview:save'] }
|
|
397
|
-
});
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
Что меняется в таком окне:
|
|
401
|
-
|
|
402
|
-
- `nodeIntegration: false`, `contextIsolation: true`, `nodeIntegrationInSubFrames: false`,
|
|
403
|
-
`webviewTag: false` — `require`, `process` и `module` в странице недоступны, модули
|
|
404
|
-
main-процесса (в том числе `@electron/remote`) тоже;
|
|
405
|
-
- preload'ом становится мост фреймворка (`framework/modules/chui_bridge/preload.js`),
|
|
406
|
-
а `render` — **скрипт документа**: мост подключает его как обычный `<script>` после
|
|
407
|
-
`DOMContentLoaded`. Поэтому код вроде `require('chuijs')` в таком окне не работает —
|
|
408
|
-
страница пишется на `window.chui`;
|
|
409
|
-
- CSP вставляется в документ до его скриптов. `security.csp: true` — политика
|
|
410
|
-
`Main.SECURE_CSP` (`default-src 'none'; script-src 'self' file:; style-src … 'unsafe-inline';`
|
|
411
|
-
`img-src … data: blob:; connect-src 'none'`), строкой передаётся своя, `false` отключает.
|
|
412
|
-
|
|
413
|
-
```javascript
|
|
414
|
-
// Что видно странице
|
|
415
|
-
window.chui.versions; // {node, chrome, electron}
|
|
416
|
-
window.chui.platform;
|
|
417
|
-
window.chui.render; // путь страницы, как он задан в опции render
|
|
418
|
-
window.chui.desktop.clipboard.readText(); // то же, что Desktop из require('chuijs')
|
|
419
|
-
window.chui.desktop.recent.list();
|
|
420
|
-
window.chui.desktop.progress.set(0.4);
|
|
421
|
-
const off = window.chui.desktop.shortcuts.on('Mod+Shift+P', () => {});
|
|
422
|
-
await window.chui.desktop.app.path('userData'); // путь приложения — по IPC, потому асинхронно
|
|
423
|
-
await window.chui.desktop.app.info(); // {name, version, locale, isPackaged}
|
|
424
|
-
const res = await window.chui.desktop.dialog.open({properties: ['openFile']});
|
|
425
|
-
await window.chui.desktop.dialog.message({type: 'question', message: 'Продолжить?'});
|
|
426
|
-
await window.chui.desktop.session.clearCache();
|
|
427
|
-
window.chui.theme.get(); // 'light' | 'dark'
|
|
428
|
-
window.chui.theme.onChange((theme) => {});
|
|
429
|
-
window.chui.window.minimize(); window.chui.window.toggleMaximize(); window.chui.window.close();
|
|
430
|
-
window.chui.window.getState(); // {isMaximized, isMinimized, isFullScreen, isFocused}
|
|
431
|
-
await window.chui.messaging.send('preview:save', payload); // только каналы из security.channels
|
|
432
|
-
window.chui.messaging.on('preview:update', (payload) => {});
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
Прикладные каналы работают в обе стороны: страница зовёт `messaging.send(channel, payload)`, а main
|
|
436
|
-
подписывается через `main.onPageMessage(channel, handler)` и отвечает/пишет сам через
|
|
437
|
-
`main.sendToPage(channel, payload, windowName?)`. Мост отказывается отправлять канал, которого нет
|
|
438
|
-
в `security.channels`, и то же самое проверяет `messaging.on`.
|
|
439
|
-
|
|
440
|
-
Ограничения: `sandbox: true` не поддерживается (мосту нужен Node в preload'е, поэтому
|
|
441
|
-
`contextIsolation` без sandbox), а окно с интерфейсом на компонентах фреймворка должно остаться
|
|
442
|
-
в привычном режиме — безопасный режим рассчитан на страницы, которым фреймворк не нужен
|
|
443
|
-
(предпросмотр, справка, внешний HTML).
|
|
444
|
-
|
|
445
|
-
#### Реэкспорт Electron и зависимостей
|
|
446
|
-
Доступны прямо из пакета, без отдельного `require`:
|
|
447
|
-
|
|
448
|
-
```javascript
|
|
449
|
-
const { BrowserWindow, ipcMain, ipcRenderer, shell, path, fs, os, request,
|
|
450
|
-
transliterate, systemPreferences, chooseFile, store, downloadSession } = require('chuijs');
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
`chooseFile` — диалоги `showOpenDialog` / `showSaveDialog` / `showMessageBox`: со страницы они идут
|
|
454
|
-
по IPC в окно, которое их вызвало (`Desktop.dialog`), а в main — напрямую `dialog` Electron.
|
|
455
|
-
`store` — готовый экземпляр `new Store()`.
|
|
456
|
-
|
|
457
|
-
#### Автоматическое обновление приложения
|
|
458
|
-
Только AppImage и NSIS.
|
|
459
|
-
|
|
460
|
-
```javascript
|
|
461
|
-
main.enableAutoUpdateApp(5000);
|
|
462
|
-
main.sendDownload('Обновление', 'Загрузка...');
|
|
463
|
-
main.sendDownloadComplete('Обновление', 'Готово');
|
|
464
|
-
main.sendDownloadError('Обновление', 'Ошибка');
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
#### Themes
|
|
468
|
-
Оформление зависит от темы приложения: светлая, тёмная или системная. Палитры живут в
|
|
469
|
-
`framework/appLayout/default/main_theme_style.css` (`@media (prefers-color-scheme: light)` / `dark`),
|
|
470
|
-
а выбор темы хранит и применяет сервис `Theme`: он меняет `nativeTheme.themeSource`, поэтому Chromium
|
|
471
|
-
перерисовывает интерфейс сам, без правок в DOM. Выбор сохраняется в настройках и возвращается при
|
|
472
|
-
следующем запуске:
|
|
473
|
-
|
|
474
|
-
```javascript
|
|
475
|
-
await Desktop.theme.set(Desktop.theme.SOURCE.DARK); // 'dark' | 'light' | 'system'
|
|
476
|
-
await Desktop.theme.toggle();
|
|
477
|
-
const off = Desktop.theme.onChange((theme) => Log.info(theme));
|
|
31
|
+
main.start({ hideOnClose: false, tray: [new MenuItem().quit("Выход")] });
|
|
478
32
|
```
|
|
479
33
|
|
|
480
|
-
|
|
481
|
-
настроек — `Settings.ThemeRow(...)`.
|
|
482
|
-
|
|
483
|
-
Токены: поверхности — `--main_background`, `--header_background`, `--modal_background`, `--modal_scrim`,
|
|
484
|
-
`--element_background` (контролы), `--element_background_2` (карточки и контент-блоки),
|
|
485
|
-
`--element_background_hover`, `--element_background_disabled`; текст — `--text_color`, `--text_color_hover`,
|
|
486
|
-
`--text_color_disabled`; акценты — `--blue_prime_background`, `--red_prime_background`, `--link_color`
|
|
487
|
-
(ссылки в контенте); геометрия и тайминги — `--border_radius`, `--radius_circle`, `--control_height`
|
|
488
|
-
(29px — высота кнопки и поля), компактный шаг контролов `--main_padding_sm` / `--control_padding_sm`,
|
|
489
|
-
`--transition_time`.
|
|
490
|
-
|
|
491
|
-
Обводка — один токен: `--border_color` (волосяные линии всей вёрстки: карточки, поля,
|
|
492
|
-
разделители). Наведение на поле — `--border_color_hover` (приметнее волосяной, но тише акцента),
|
|
493
|
-
фокус — акцентная рамка и кольцо `--blue_prime_background_tint`. Окно создаётся без системной
|
|
494
|
-
рамки (`frame: false`), и своего края приложение
|
|
495
|
-
не рисует: границу показывают рабочий стол и тень композитора. Поэтому нет ни рамки вокруг
|
|
496
|
-
окна, ни отступа `body` под неё. Скруглений у окна и шапки тоже нет: углы окна всё равно
|
|
497
|
-
режет композитор (замер на KWin — дуга ≈8px), а нарисованное приложением скругление читалось
|
|
498
|
-
бы с ним спором. Непрозрачный `--main_background` на `html` остаётся: базовый цвет окна
|
|
499
|
-
белый, и незакрашенная область дала бы белую кромку.
|
|
500
|
-
|
|
501
|
-
Схема нативных частей — `color-scheme: light dark` в `:root`: палитры разложены по
|
|
502
|
-
`prefers-color-scheme`, поэтому браузер рисует и родные мелочи (каретка, выделение, системные
|
|
503
|
-
кнопки и крутилки полей) в той же схеме. Без этого токена они оставались светлыми в тёмной теме.
|
|
504
|
-
Нативные *виджеты* компоненты скрывают сами и только у себя: индикатор календаря — `chui_date`,
|
|
505
|
-
крутилку числа — `chui_number`, индикатор часов — `chui_time`, кнопку очистки поиска — поля
|
|
506
|
-
поиска (`FileExplorer`, `JsonViewer`, `Transfer`), а выпадающие списки везде свои (см. `Select`).
|
|
507
|
-
Правила, которые раньше были общими (`input[type=number]::-webkit-inner-spin-button`,
|
|
508
|
-
`::-webkit-calendar-picker-indicator`) — «протекали» на чужие поля, поэтому теперь у каждого
|
|
509
|
-
компонента они ограничены его узлом.
|
|
510
|
-
|
|
511
|
-
Свои цвета — либо переопределением токенов своим `<style>` после загрузки темы (кандидат —
|
|
512
|
-
конструктор своего layout), либо значениями в стилях конкретного компонента.
|
|
513
|
-
|
|
514
|
-
Системные настройки (`prefers-reduced-motion`) и утилита `.chui_visually_hidden` описаны в разделе
|
|
515
|
-
«Доступность».
|
|
516
|
-
|
|
517
|
-
#### Разметка и `chui_layout` (scoped-ссылки)
|
|
518
|
-
`framework/modules/chui_layout` — реестр частей layout'а. Общие узлы (шапка,
|
|
519
|
-
`page_name`, `center`, app-меню, панель и ящик уведомлений) `AppLayout` регистрирует сам, а
|
|
520
|
-
остальные модули берут их по роли через `LAYOUT_ROLE` / `findLayoutNode()` — без поиска по всему
|
|
521
|
-
документу. Свои узлы компоненты держат приватными полями: `Dialog`, `Popup`, `ImagePopup`, `Tabs`,
|
|
522
|
-
`Select`/`ComboBox`/`MultiComboBox`, уведомления, `Audio` (полоса управления и слайдеры эквалайзера),
|
|
523
|
-
`TextEditorPanel` (редактор передаётся ссылкой, а не id).
|
|
34
|
+
`app/app.js` — страница приложения:
|
|
524
35
|
|
|
525
36
|
```javascript
|
|
526
|
-
const {
|
|
527
|
-
|
|
528
|
-
const layout = getLayout(); // последний созданный layout
|
|
529
|
-
layout.get(LAYOUT_ROLE.CENTER); // узел по роли
|
|
530
|
-
findLayoutNode(LAYOUT_ROLE.NOTIFICATION_BOX_MAIN, myNode); // layout, которому принадлежит узел
|
|
531
|
-
requireLayoutNode(LAYOUT_ROLE.NOTIFICATION_PANEL); // то же, но с понятной ошибкой
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
`LayoutRefs.set(role, node)` / `.get(role)` / `.owns(node)` — узел принадлежит тому layout'у, в поддереве
|
|
535
|
-
которого он находится; если узел ещё не в дереве (только что созданное уведомление), берётся последний
|
|
536
|
-
созданный layout. `registerLayout(layout)` / `unregisterLayout(layout)` нужны сценариям с несколькими
|
|
537
|
-
layout'ами (новое окно, тестовый стенд).
|
|
538
|
-
|
|
539
|
-
Из-за этого в `framework/` не осталось поиска узлов по всему документу. Единственные оставшиеся
|
|
540
|
-
`getElementById` — проверка «стиль с таким id уже подключён» в `chui_functions` и `chui_fonts`
|
|
541
|
-
(у каждого окна свой документ, и это именно документная проверка), и
|
|
542
|
-
`document.querySelector('script[id=...]')` в `AppLayout.setScript()` для однократного подключения скрипта.
|
|
543
|
-
`getElementsByTagName` остался один раз — `Select.setDefaultOption()` перебирает опции в своём приватном
|
|
544
|
-
`#dropdown`, то есть внутри собственного поддерева, а не по документу.
|
|
545
|
-
|
|
546
|
-
### Локализация и RTL
|
|
547
|
-
|
|
548
|
-
Строки интерфейса берутся по ключу, а не на месте: `I18n.t("common.close")`. Один ключ — один текст
|
|
549
|
-
в словаре языка, поэтому подписи переводятся без правок кода компонентов.
|
|
550
|
-
|
|
551
|
-
```javascript
|
|
552
|
-
const { I18n, Desktop } = require('chuijs');
|
|
553
|
-
|
|
554
|
-
I18n.setLocale('en'); // 'ru' (по умолчанию) или 'en' — тексты и `lang` документа
|
|
555
|
-
I18n.t('validate.max', {max: 5}); // 'Value must be at most 5'
|
|
556
|
-
I18n.getLocales(); // ['ru', 'en'] — плюс добавленные приложением
|
|
557
|
-
I18n.extend('de', {'common.close': 'Schließen'}); // свой или дополненный словарь
|
|
558
|
-
I18n.getDirection(); // 'ltr' | 'rtl'
|
|
559
|
-
const off = I18n.onChange((locale, direction) => render()); // пересобрать экран после смены языка
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
Словари лежат в `framework/modules/chui_i18n/locales/` по пространствам имён (один файл — одна
|
|
563
|
-
группа компонентов):
|
|
564
|
-
|
|
565
|
-
| Пространство | Что переводит |
|
|
566
|
-
|---|---|
|
|
567
|
-
| `common`, `validate`, `a11y` | общие подписи («Закрыть», «Отмена»), тексты проверок и имена элементов доступности |
|
|
568
|
-
| `app_layout` | каркас окна: кнопки окна, меню, поиск, панель уведомлений, профиль, диалог обновления, а также меню «Открыть последние» и уведомления об обновлении из main-процесса |
|
|
569
|
-
| `calendar` | `DateInput`, `TimeInput`, `DateTimeInput`, `DateRangeInput`, `Calendar`, `Scheduler`, `Gantt` |
|
|
570
|
-
| `media` | `Audio` (включая пресеты эквалайзера), `Video` |
|
|
571
|
-
| `editors` | `TextEditor`, `CodeEditor`, `MarkdownEditor`, `DiffViewer`, `JsonViewer`, `JsonEditor`, разбор JSON (`Json.errorText`) |
|
|
572
|
-
| `data` | `DataGrid`, `TreeView`, `TreeTable`, `OrgChart`, `Transfer`, `FileExplorer`, `QueryBuilder` |
|
|
573
|
-
| `navigation` | вкладки, меню-бар, пагинация, хлебные крошки, шаги, `SidebarNav`, `CommandPalette`, `Toolbar`, `StatusBar`, `SplitButton`, `List` |
|
|
574
|
-
| `forms` | поля ввода: `Slider`, `Rating`, `ColorPicker`, `HotkeyInput`, `MaskedInput`, `OtpInput`, `Chips`, `NumberInput`, `DropZone` |
|
|
575
|
-
| `feedback` | сообщения и состояния: `Alert`, `Notification`, `ToastQueue`, `Drawer`, `Spinner`, `Skeleton`, `ProgressRing`, `SlideShow`, `Avatar`, панель истории буфера, индикатор печати |
|
|
576
|
-
| `graphics` | графики (`BarGraph`, `PieGraph`, `LineGraph`, `AreaGraph`, `ScatterGraph`, `Sparkline`, `RadarGraph`, `HeatmapGraph`, `Gauge` — включая подписи скрытых таблиц-альтернатив), `Kanban`, `QRCode`, `Settings`, `ThemeToggle`, `Timeline`, `PrintButton` |
|
|
577
|
-
|
|
578
|
-
Незнакомый ключ отдаётся как есть, а ключ, которого нет в текущем языке, падает на резервный (`ru`) —
|
|
579
|
-
интерфейс не ломается. Полноту словарей стережёт тест `test/chui_i18n_keys.test.js`: каждый
|
|
580
|
-
литеральный ключ `I18n.t("...")` из кода должен быть в обоих языках, любая строка вида
|
|
581
|
-
«пространство.ключ» (в том числе ключ из тернарного выражения или таблицы) тоже должна быть
|
|
582
|
-
в словаре, наборы ключей `ru` и `en` обязаны совпадать, а один и тот же ключ не может быть
|
|
583
|
-
объявлен дважды. Общие подписи живут в `common` и `a11y`: один и тот же текст (например,
|
|
584
|
-
«Развернуть «Имя»» у дерева-таблицы и просмотра JSON) описан один раз и переводится везде сразу.
|
|
585
|
-
|
|
586
|
-
**Что осталось на русском.** Служебные тексты, которые не видит пользователь: сообщения `Log`,
|
|
587
|
-
исключения конструкторов о неверных опциях, демонстрационное приложение (`app/app.js`, `main.js`)
|
|
588
|
-
и тестовые данные витрины (`app/views/0_maket.js`).
|
|
37
|
+
const { AppLayout, Page, Button, Log, render } = require("chuijs");
|
|
589
38
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
(
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
```javascript
|
|
598
|
-
I18n.setLocale('ar'); // dir="rtl" lang="ar" — вся вёрстка зеркалится
|
|
599
|
-
I18n.setDirection('rtl'); // то же без смены языка
|
|
600
|
-
```
|
|
39
|
+
class MainPage extends Page {
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
this.setTitle("Сказать привет!");
|
|
43
|
+
this.add(new Button({ title: "Привет", clickEvent: () => Log.info("привет") }));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
601
46
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
47
|
+
class App extends AppLayout {
|
|
48
|
+
constructor() {
|
|
49
|
+
super();
|
|
50
|
+
this.setRoute(new MainPage());
|
|
51
|
+
}
|
|
52
|
+
}
|
|
607
53
|
|
|
608
|
-
|
|
609
|
-
await Desktop.locale.set('en'); // main сохраннит выбор
|
|
610
|
-
Desktop.locale.onChange((locale) => I18n.setLocale(locale));
|
|
54
|
+
render(() => new App()).catch((err) => Log.error(err));
|
|
611
55
|
```
|
|
612
56
|
|
|
613
|
-
|
|
614
|
-
и
|
|
615
|
-
|
|
616
|
-
### Компоненты
|
|
617
|
-
|
|
618
|
-
Все классы и модули интерфейса из `require('chuijs')` (таблица иконок `Icons`, константы `Styles` и
|
|
619
|
-
`AcceptTypes`, а также `Desktop` — доступ к десктоп-сервисам, у него `set()` нет — описаны рядом отдельно).
|
|
620
|
-
Любой компонент реализует `set()` — возвращает готовый
|
|
621
|
-
DOM-элемент, поэтому внутри `Page.add(...)`, `Card.add(...)`, `ContentBlock.add(...)` передаётся
|
|
622
|
-
сам объект, а в `Form.components` / `SlideShow.SLIDE` / `FieldSet.components` — тоже объекты.
|
|
623
|
-
|
|
624
|
-
**Разметка `markdownText`.** Компоненты с этой опцией (`Label`, `Tooltip`, `Badge`, `Alert`,
|
|
625
|
-
`Notification`, `ToastQueue`, `Chat`, `Tour`, `MarkdownEditor`) рендерят её `markdown-it` в безопасном
|
|
626
|
-
режиме: сырой HTML из разметки не исполняется, а показывается текстом — так закрыт XSS через
|
|
627
|
-
`markdownText`. Таблицы и `~~зачёркивание~~` поддерживаются, авто-ссылок нет: адрес становится
|
|
628
|
-
ссылкой только явным `[текст](адрес)`. Обратный разбор (`htmlToMarkdown`) делает `turndown`.
|
|
629
|
-
|
|
630
|
-
Описание каждого компонента — отдельная страница статического сайта документации в [`docs/`](docs/):
|
|
631
|
-
[`docs/index.html`](docs/index.html) — полный индекс по группам и алфавиту, а у каждой страницы сверху
|
|
632
|
-
ссылка на группу и общий список. Сайт собирается генератором `node docs/build.js`
|
|
633
|
-
(`npm run docs:build`) из разметки `docs/content/*.md`, разделов этого README и скана кода
|
|
634
|
-
(`framework/**`); как открыть его локально и опубликовать — в [`docs/README.md`](docs/README.md).
|
|
635
|
-
Здесь — только вход в группы:
|
|
636
|
-
|
|
637
|
-
- [Компоновка и навигация](docs/groups/layout-navigation.html) — 12: `AppLayout`, `Route`, `Page`, `MenuBar`, `SidebarNav`, `ContextMenu`, `CommandPalette`, `Backtop`, `Affix`, `Styles`, `Icon`, `Icons`
|
|
638
|
-
- [Поля ввода](docs/groups/inputs.html) — 29: `TextInput`, `TextArea`, `NumberInput`, `Slider`, `Chips`, `DateInput`, `Select`, `ComboBox`, `MultiComboBox`, `Transfer`, `CheckBox`, `RadioGroup`, `Form`, `FieldSet` и другие
|
|
639
|
-
- [Отображение](docs/groups/display.html) — 73: `Button`, `Card`, `List`, `DataGrid`, `TreeView`, `TreeTable`, `Tabs`, `Kanban`, `Gantt`, `Scheduler`, `OrgChart`, `QRCode`, `QueryBuilder`, графики и другое
|
|
640
|
-
- [Окна, уведомления, медиа](docs/groups/overlays.html) — 21: `Dialog`, `Drawer`, `Popup`, `Alert`, `Notification`, `ToastQueue`, `Calendar`, `SlideShow`, `TextEditor`, `MarkdownEditor`, `Image`, `Audio`, `Video`, `WebView`, `Console`, `CustomElement`
|
|
57
|
+
Дальше — [«Начало работы»](docs/guide/start.html): структура проекта, все опции `Main`, страницы
|
|
58
|
+
и сервисы.
|
|
641
59
|
|
|
642
|
-
|
|
643
|
-
[Жизненный цикл `destroy()`](docs/topics/lifecycle.html), [Доступность](docs/topics/a11y.html),
|
|
644
|
-
[Константы и статические фабрики](docs/topics/constants.html), [Сценарии целиком](docs/topics/scenarios.html),
|
|
645
|
-
[Витрина компонентов](docs/topics/showcase.html).
|
|
60
|
+
## Документация
|
|
646
61
|
|
|
647
|
-
|
|
62
|
+
[`docs/index.html`](docs/index.html) — справочник по фреймворку: страница на каждый компонент с
|
|
63
|
+
примером, составом API и заметками о доступности, а также
|
|
64
|
+
[модули и сервисы](docs/modules/index.html) (`Main`, `Log`, `Desktop`, безопасный режим,
|
|
65
|
+
автообновление), [локализация и RTL](docs/guide/i18n.html),
|
|
66
|
+
[общие правила компонентов](docs/topics/options.html),
|
|
67
|
+
[планы](docs/guide/roadmap.html) и [история изменений](docs/changelog.html).
|
|
648
68
|
|
|
649
|
-
|
|
69
|
+
## Лицензия
|
|
650
70
|
|
|
651
|
-
|
|
652
|
-
Компоненты по-прежнему проверяются витриной `app/views/0_maket.js` и стендами Electron.
|
|
653
|
-
- Локализация охватывает все словари по пространствам имён: строки интерфейса framework берутся
|
|
654
|
-
по ключу (`I18n.t`). Непереведёнными остаются служебные тексты (журнал, исключения конструкторов),
|
|
655
|
-
демонстрационное приложение `app/`, меню демо-приложения в `main.js` и данные витрины; дописать
|
|
656
|
-
словарь для нового компонента — это добавить ключи в его пространство имён.
|
|
657
|
-
- RTL: вёрстка зеркалится логическими свойствами CSS; позиционирование всплывающих слоёв, которое
|
|
658
|
-
считает JavaScript (`left`/`right` в пикселях), и анимации-сдвиги пока не учитывают направление.
|
|
659
|
-
Запятые-кавычки в текстовом представлении `QueryBuilder.getText()` остались русскими «ёлочками»
|
|
660
|
-
(это текст-представление запроса, а не подпись интерфейса).
|
|
71
|
+
MIT — см. [LICENSE](LICENSE).
|