chuijs 3.6.2 → 3.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +1203 -162
- package/framework/appLayout/default/chui_app_layout.js +747 -747
- package/framework/appLayout/default/global_style.css +592 -568
- package/framework/appLayout/default/main_theme_style.css +118 -101
- package/framework/components/chui_accordion/accordion.js +63 -63
- package/framework/components/chui_accordion/styles.css +79 -75
- package/framework/components/chui_badge/badge.js +55 -55
- package/framework/components/chui_badge/styles.css +29 -29
- package/framework/components/chui_button/button.js +48 -48
- package/framework/components/chui_button/styles.css +75 -70
- package/framework/components/chui_calendar/calendar.js +97 -97
- package/framework/components/chui_card/card.js +87 -0
- package/framework/components/chui_card/styles.css +72 -0
- package/framework/components/chui_code/code.js +18 -18
- package/framework/components/chui_code/styles.css +9 -9
- package/framework/components/chui_console/console.js +27 -27
- package/framework/components/chui_console/styles.css +31 -31
- package/framework/components/chui_content_block/content_block.js +51 -51
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +230 -0
- package/framework/components/chui_context_menu/styles.css +136 -0
- package/framework/components/chui_custom_element/custom_element.js +24 -24
- package/framework/components/chui_details/details.js +85 -85
- package/framework/components/chui_details/styles.css +70 -66
- package/framework/components/chui_fieldset/fieldset.js +40 -40
- package/framework/components/chui_fieldset/styles.css +24 -24
- package/framework/components/chui_form/form.js +40 -40
- package/framework/components/chui_form/styles.css +21 -21
- package/framework/components/chui_graphs/graphs.js +234 -190
- package/framework/components/chui_graphs/styles.css +37 -32
- package/framework/components/chui_hero/hero.js +98 -0
- package/framework/components/chui_hero/styles.css +91 -0
- package/framework/components/chui_html_block/html_block.js +16 -16
- package/framework/components/chui_html_block/styles.css +5 -5
- package/framework/components/chui_hx/hx.js +15 -15
- package/framework/components/chui_hx/styles.css +38 -38
- package/framework/components/chui_icons/icons.js +2144 -2144
- package/framework/components/chui_icons/styles.css +13 -13
- package/framework/components/chui_inputs/chui_check_box/check_box.js +32 -32
- package/framework/components/chui_inputs/chui_check_box/styles.css +91 -86
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +166 -150
- package/framework/components/chui_inputs/chui_combo_box/styles.css +113 -107
- package/framework/components/chui_inputs/chui_date/date.js +177 -174
- package/framework/components/chui_inputs/chui_date/styles.css +163 -128
- package/framework/components/chui_inputs/chui_email/email.js +61 -61
- package/framework/components/chui_inputs/chui_email/styles.css +69 -71
- package/framework/components/chui_inputs/chui_file/file.js +76 -76
- package/framework/components/chui_inputs/chui_file/styles.css +99 -99
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +295 -295
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +212 -235
- package/framework/components/chui_inputs/chui_number/number.js +108 -108
- package/framework/components/chui_inputs/chui_number/styles.css +84 -84
- package/framework/components/chui_inputs/chui_password/password.js +99 -99
- package/framework/components/chui_inputs/chui_password/styles.css +97 -97
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +45 -45
- package/framework/components/chui_inputs/chui_radio_button/styles.css +76 -73
- package/framework/components/chui_inputs/chui_select_box/select_box.js +187 -170
- package/framework/components/chui_inputs/chui_select_box/styles.css +123 -117
- package/framework/components/chui_inputs/chui_text/styles.css +83 -84
- package/framework/components/chui_inputs/chui_text/text.js +91 -91
- package/framework/components/chui_inputs/chui_text_area/styles.css +57 -57
- package/framework/components/chui_inputs/chui_text_area/text_area.js +61 -61
- package/framework/components/chui_inputs/chui_toggle/styles.css +66 -61
- package/framework/components/chui_inputs/chui_toggle/toggle.js +36 -36
- package/framework/components/chui_label/label.js +54 -54
- package/framework/components/chui_label/styles.css +11 -11
- package/framework/components/chui_media/audio.js +908 -908
- package/framework/components/chui_media/audio_styles.css +458 -458
- package/framework/components/chui_media/image.js +105 -105
- package/framework/components/chui_media/image_styles.css +61 -61
- package/framework/components/chui_media/video.js +248 -248
- package/framework/components/chui_media/video_styles.css +160 -160
- package/framework/components/chui_menu_bar/menu_bar.js +66 -74
- package/framework/components/chui_menu_bar/styles.css +114 -76
- package/framework/components/chui_modal/modal.js +142 -135
- package/framework/components/chui_modal/styles.css +36 -36
- package/framework/components/chui_notification/notification.js +72 -72
- package/framework/components/chui_notification/notification_download.js +78 -78
- package/framework/components/chui_notification/notification_download_progress.css +119 -123
- package/framework/components/chui_notification/notification_download_progress.js +105 -105
- package/framework/components/chui_notification/notification_update.js +54 -54
- package/framework/components/chui_notification/styles.css +91 -91
- package/framework/components/chui_notification/styles_download.css +85 -85
- package/framework/components/chui_notification/styles_update.css +73 -73
- package/framework/components/chui_paragraph/paragraph.js +14 -14
- package/framework/components/chui_paragraph/styles.css +6 -6
- package/framework/components/chui_popups/popups.js +250 -250
- package/framework/components/chui_popups/styles.css +129 -115
- package/framework/components/chui_progress_bar/progress_bar.js +28 -28
- package/framework/components/chui_progress_bar/styles.css +28 -28
- package/framework/components/chui_radio_group/radio_group.js +57 -57
- package/framework/components/chui_radio_group/styles.css +3 -3
- package/framework/components/chui_slideshow/slideshow.js +75 -75
- package/framework/components/chui_slideshow/styles.css +94 -85
- package/framework/components/chui_spinner/spinner.js +45 -45
- package/framework/components/chui_spinner/styles.css +23 -23
- package/framework/components/chui_table/styles.css +42 -42
- package/framework/components/chui_table/table.js +234 -234
- package/framework/components/chui_tabs/chui_tabs.js +77 -77
- package/framework/components/chui_tabs/styles.css +54 -48
- package/framework/components/chui_text_editor/styles.css +217 -217
- package/framework/components/chui_text_editor/text_editor.js +144 -144
- package/framework/components/chui_text_editor/text_editor_panel.js +568 -568
- package/framework/components/chui_tree_view/styles.css +81 -75
- package/framework/components/chui_tree_view/tree_view.js +90 -90
- package/framework/components/chui_webview/styles.css +27 -27
- package/framework/components/chui_webview/webview.js +103 -103
- package/framework/components/telegram_bot/chui_telegram_bot.js +183 -183
- package/framework/modules/chui_animations/animations.js +121 -121
- package/framework/modules/chui_animations/styles.css +79 -79
- package/framework/modules/chui_fonts.js +7129 -6974
- package/framework/modules/chui_functions.js +106 -78
- package/framework/modules/chui_logger/chui_logger.js +53 -53
- package/framework/modules/chui_page/page.js +86 -86
- package/framework/modules/chui_page/styles.css +9 -9
- package/framework/tray_bar/chui_menu_items.js +48 -48
- package/index.js +570 -555
- package/package.json +51 -46
package/README.md
CHANGED
|
@@ -1,162 +1,1203 @@
|
|
|
1
|
-
# chuijs
|
|
2
|
-
### CHROME_SANDBOX FIX
|
|
3
|
-
```shell
|
|
4
|
-
cd node_modules/electron/dist && sudo chown root chrome-sandbox && sudo chmod 4755 chrome-sandbox
|
|
5
|
-
```
|
|
6
|
-
### Структура
|
|
7
|
-
#### exampleApp / app / views / main.js
|
|
8
|
-
```javascript
|
|
9
|
-
const { Page, Button, TextInput, Notification, Log } = require('chuijs');
|
|
10
|
-
class MainPage extends Page {
|
|
11
|
-
constructor() {
|
|
12
|
-
super();
|
|
13
|
-
this.setTitle('Сказать привет!');
|
|
14
|
-
this.setMain(false)
|
|
15
|
-
|
|
16
|
-
let name = new TextInput({
|
|
17
|
-
title: 'Введите ваше имя',
|
|
18
|
-
placeholder: 'Введите ваше имя',
|
|
19
|
-
required: false
|
|
20
|
-
});
|
|
21
|
-
let hello = new Button({
|
|
22
|
-
title: "Сказать привет!",
|
|
23
|
-
clickEvent: () => {
|
|
24
|
-
if (name.getValue() !== "") {
|
|
25
|
-
new Notification({
|
|
26
|
-
title: `Привет, ${name.getValue()}!`, text: `Привет, ${name.getValue()}!`,
|
|
27
|
-
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
28
|
-
}).show();
|
|
29
|
-
} else {
|
|
30
|
-
new Notification({
|
|
31
|
-
title: `Привет мир!`, text: `Привет мир!`,
|
|
32
|
-
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
33
|
-
}).show();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
this.add(name, hello)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
exports.MainPage = MainPage
|
|
41
|
-
```
|
|
42
|
-
#### exampleApp / app / app.js
|
|
43
|
-
```javascript
|
|
44
|
-
/** RENDERER ПРОЦЕСС */
|
|
45
|
-
/** ИМПОРТЫ */
|
|
46
|
-
const { AppLayout, render } = require('chuijs');
|
|
47
|
-
/** СТРАНИЦЫ */
|
|
48
|
-
const { MainPage } = require('../app/views/main');
|
|
49
|
-
class App extends AppLayout {
|
|
50
|
-
constructor() {
|
|
51
|
-
super();
|
|
52
|
-
/** РОУТЫ */
|
|
53
|
-
this.setRoute(new MainPage());
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
render(() => new App()).catch(err => Log.error(err))
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
1
|
+
# chuijs
|
|
2
|
+
### CHROME_SANDBOX FIX
|
|
3
|
+
```shell
|
|
4
|
+
cd node_modules/electron/dist && sudo chown root chrome-sandbox && sudo chmod 4755 chrome-sandbox
|
|
5
|
+
```
|
|
6
|
+
### Структура
|
|
7
|
+
#### exampleApp / app / views / main.js
|
|
8
|
+
```javascript
|
|
9
|
+
const { Page, Button, TextInput, Notification, Log } = require('chuijs');
|
|
10
|
+
class MainPage extends Page {
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
this.setTitle('Сказать привет!');
|
|
14
|
+
this.setMain(false)
|
|
15
|
+
|
|
16
|
+
let name = new TextInput({
|
|
17
|
+
title: 'Введите ваше имя',
|
|
18
|
+
placeholder: 'Введите ваше имя',
|
|
19
|
+
required: false
|
|
20
|
+
});
|
|
21
|
+
let hello = new Button({
|
|
22
|
+
title: "Сказать привет!",
|
|
23
|
+
clickEvent: () => {
|
|
24
|
+
if (name.getValue() !== "") {
|
|
25
|
+
new Notification({
|
|
26
|
+
title: `Привет, ${name.getValue()}!`, text: `Привет, ${name.getValue()}!`,
|
|
27
|
+
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
28
|
+
}).show();
|
|
29
|
+
} else {
|
|
30
|
+
new Notification({
|
|
31
|
+
title: `Привет мир!`, text: `Привет мир!`,
|
|
32
|
+
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
33
|
+
}).show();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
this.add(name, hello)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.MainPage = MainPage
|
|
41
|
+
```
|
|
42
|
+
#### exampleApp / app / app.js
|
|
43
|
+
```javascript
|
|
44
|
+
/** RENDERER ПРОЦЕСС */
|
|
45
|
+
/** ИМПОРТЫ */
|
|
46
|
+
const { AppLayout, render, Log } = require('chuijs');
|
|
47
|
+
/** СТРАНИЦЫ */
|
|
48
|
+
const { MainPage } = require('../app/views/main');
|
|
49
|
+
class App extends AppLayout {
|
|
50
|
+
constructor() {
|
|
51
|
+
super();
|
|
52
|
+
/** РОУТЫ */
|
|
53
|
+
this.setRoute(new MainPage());
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
render(() => new App()).catch(err => Log.error(err))
|
|
57
|
+
```
|
|
58
|
+
`render(callback)` возвращает `Promise`, который резолвится после `DOMContentLoaded` (или сразу, если документ уже загружен) результатом `callback` и реджектится его ошибкой.
|
|
59
|
+
#### exampleApp / main.js
|
|
60
|
+
```javascript
|
|
61
|
+
/** main.js */
|
|
62
|
+
const { Main, MenuItem, path, App } = require('chuijs');
|
|
63
|
+
const main = new Main({
|
|
64
|
+
name: "exampleApp",
|
|
65
|
+
// размеры окна задаются вложенным объектом sizes
|
|
66
|
+
sizes: {
|
|
67
|
+
width: 1366,
|
|
68
|
+
height: 768,
|
|
69
|
+
minWidth: 960,
|
|
70
|
+
minHeight: 540
|
|
71
|
+
},
|
|
72
|
+
render: `${__dirname}/app/app.js`,
|
|
73
|
+
devTools: false,
|
|
74
|
+
resizable: true,
|
|
75
|
+
// папка загрузок (по умолчанию App.downloadsPath())
|
|
76
|
+
paths: {
|
|
77
|
+
downloadPath: path.join(App.userDataPath(), "downloads")
|
|
78
|
+
}
|
|
79
|
+
/** icon: `${__dirname}/resources/icons/app/icon.png` */
|
|
80
|
+
});
|
|
81
|
+
main.start({
|
|
82
|
+
hideOnClose: false,
|
|
83
|
+
tray: [
|
|
84
|
+
new MenuItem().help(`exampleApp`),
|
|
85
|
+
new MenuItem().button('Консоль разработчика', () => main.toggleDevTools()),
|
|
86
|
+
new MenuItem().separator(),
|
|
87
|
+
new MenuItem().quit("Выход")
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> `Main` — обычный класс, а не singleton: конструктор только сохраняет опции
|
|
93
|
+
> и настраивает `app.commandLine`. Окно создаётся в `start()` (внутри `app.whenReady()`),
|
|
94
|
+
> поэтому окно доступно через `main.getWindow()` только после старта.
|
|
95
|
+
#### Download Session в main.js
|
|
96
|
+
```javascript
|
|
97
|
+
const { App } = require('chuijs');
|
|
98
|
+
App.get().on('session-created', (session) => {
|
|
99
|
+
session.on('will-download', (e, item, contents) => {
|
|
100
|
+
// ...
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
#### TelegramBot API
|
|
105
|
+
```javascript
|
|
106
|
+
const { TelegramBot } = require('chuijs');
|
|
107
|
+
let bot = new TelegramBot("token");
|
|
108
|
+
bot.setToken("token");
|
|
109
|
+
|
|
110
|
+
// Функции
|
|
111
|
+
async function run() {
|
|
112
|
+
await bot.getMe();
|
|
113
|
+
await bot.getUpdates();
|
|
114
|
+
await bot.getChat("id");
|
|
115
|
+
/* ... */
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
### Модули
|
|
119
|
+
|
|
120
|
+
Инфраструктура и утилиты из корня пакета: `const { Main, MenuItem, Log, path } = require('chuijs');`
|
|
121
|
+
|
|
122
|
+
#### `Main`
|
|
123
|
+
Создаёт окно, трей и управляет жизненным циклом приложения.
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const { Main, MenuItem, App } = require('chuijs');
|
|
127
|
+
const main = new Main({
|
|
128
|
+
name: 'exampleApp',
|
|
129
|
+
sizes: { width: 1366, height: 768, minWidth: 960, minHeight: 540 },
|
|
130
|
+
render: `${__dirname}/app/app.js`,
|
|
131
|
+
devTools: false,
|
|
132
|
+
resizable: true,
|
|
133
|
+
paths: { downloadPath: path.join(App.userDataPath(), 'downloads') }
|
|
134
|
+
});
|
|
135
|
+
main.start({ hideOnClose: false, tray: [new MenuItem().quit('Выход')] });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Опции конструктора: `name`, `sizes {width, height, minWidth, minHeight}`, `render`, `devTools`,
|
|
139
|
+
`webSecurity`, `resizable`, `paths {downloadPath}`, `icon`. Если `icon` не передан, `Main` сам
|
|
140
|
+
подставляет иконку приложения по умолчанию.
|
|
141
|
+
`start()` принимает `{hideOnClose, globalMenu, tray, extensions}` и создаёт окно внутри `app.whenReady()`.
|
|
142
|
+
API: `getWindow()`, `toggleDevTools()`, `stop()`, `restart()`, `enableAutoUpdateApp(interval)`,
|
|
143
|
+
`sendDownload(title, body)`, `sendDownloadUpdate(title, body)`, `sendDownloadComplete(title, body)`,
|
|
144
|
+
`sendDownloadError(title, body)`.
|
|
145
|
+
|
|
146
|
+
#### `App`
|
|
147
|
+
Обёртка над `app` Electron.
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
const { App } = require('chuijs');
|
|
151
|
+
App.get(); // app
|
|
152
|
+
App.getSession(); // session
|
|
153
|
+
App.getWebContents(); // webContents
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `App.*Path()`
|
|
157
|
+
Статические системные пути (все возвращают строку).
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const { App } = require('chuijs');
|
|
161
|
+
App.userDataPath(); // .../AppData/<name>
|
|
162
|
+
App.downloadsPath();
|
|
163
|
+
App.logsPath();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Полный список: `homePath()`, `appDataPath()`, `userDataPath()`, `sessionDataPath()`, `logsPath()`, `tempPath()`, `exePath()`, `modulePath()`, `desktopPath()`, `documentsPath()`, `downloadsPath()`, `musicPath()`, `picturesPath()`, `videosPath()`, `recentPath()`, `crashDumpsPath()`.
|
|
167
|
+
|
|
168
|
+
#### `Log`
|
|
169
|
+
Пишет в `userData/logs/app_<date>.log`.
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
Log.info('сообщение');
|
|
173
|
+
Log.error(new Error('что-то пошло не так'));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Из renderer-процесса вызов уходит в main через `ipcRenderer.send("SEND_LOG_TEXT", ...)`.
|
|
177
|
+
|
|
178
|
+
#### `sleep`, `render`, `formatBytes`
|
|
179
|
+
Утилиты из `chui_functions`.
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
await sleep(1000); // пауза, мс
|
|
183
|
+
render(() => new App()).catch(err => Log.error(err)); // точка входа renderer
|
|
184
|
+
formatBytes(1536); // "1.5 KiB"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`render(callback)` ждёт `DOMContentLoaded` и резолвится результатом `callback`.
|
|
188
|
+
|
|
189
|
+
#### Иконка приложения по умолчанию
|
|
190
|
+
`getDefaultIcon()` объявлена во внутреннем модуле `framework/modules/chui_functions` и
|
|
191
|
+
**наружу не реэкспортируется**: `require('chuijs').getDefaultIcon` — это `undefined`.
|
|
192
|
+
`Main` вызывает её сам, если опция `icon` не задана (на macOS результат дополнительно
|
|
193
|
+
прогоняется через `Main.#resizeIconTray()`). Свой путь задаётся опцией `icon`:
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
new Main({ name: 'app', icon: `${__dirname}/resources/icon.png` });
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### `MenuItem`
|
|
200
|
+
Пункты меню трея и глобального меню (цепочка из 45 методов: `button`, `submenu`, `radio`,
|
|
201
|
+
`checkbox`, `help`, `quit`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `windowMenu` и др.).
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
new MenuItem().help('exampleApp');
|
|
205
|
+
new MenuItem().button('Консоль разработчика', () => main.toggleDevTools());
|
|
206
|
+
new MenuItem().separator();
|
|
207
|
+
new MenuItem().submenu('Ещё', [new MenuItem().quit('Выход')]);
|
|
208
|
+
new MenuItem().quit('Выход');
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `TelegramBot`
|
|
212
|
+
Обёртка над Telegram Bot HTTP API (26 методов).
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const bot = new TelegramBot('token');
|
|
216
|
+
bot.setToken('token');
|
|
217
|
+
await bot.getMe();
|
|
218
|
+
await bot.sendMessage({ chat_id: '123', text: 'привет' });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### Реэкспорт Electron и зависимостей
|
|
222
|
+
Доступны прямо из пакета, без отдельного `require`:
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const { BrowserWindow, ipcMain, ipcRenderer, shell, path, fs, os, request,
|
|
226
|
+
transliterate, systemPreferences, chooseFile, store, downloadSession } = require('chuijs');
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`chooseFile` — это `dialog` из `@electron/remote/main`, `store` — готовый экземпляр `new Store()`.
|
|
230
|
+
|
|
231
|
+
#### Автоматическое обновление приложения
|
|
232
|
+
Только AppImage и NSIS.
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
main.enableAutoUpdateApp(5000);
|
|
236
|
+
main.sendDownload('Обновление', 'Загрузка...');
|
|
237
|
+
main.sendDownloadComplete('Обновление', 'Готово');
|
|
238
|
+
main.sendDownloadError('Обновление', 'Ошибка');
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Themes
|
|
242
|
+
Оформление зависит от системной темы (светлая/тёмная), переключателя в API нет.
|
|
243
|
+
|
|
244
|
+
### Компоненты
|
|
245
|
+
|
|
246
|
+
Все 57 классов-компонентов из `require('chuijs')` (таблица иконок `Icons`, константы `Styles` и
|
|
247
|
+
`AcceptTypes` описаны рядом отдельно). Любой компонент реализует `set()` — возвращает готовый
|
|
248
|
+
DOM-элемент, поэтому внутри `Page.add(...)`, `Card.add(...)`, `ContentBlock.add(...)` передаётся
|
|
249
|
+
сам объект, а в `Form.components` / `SlideShow.SLIDE` / `FieldSet.components` — тоже объекты.
|
|
250
|
+
|
|
251
|
+
#### Компоновка и навигация
|
|
252
|
+
|
|
253
|
+
##### `AppLayout`
|
|
254
|
+
Корневой layout: шапка, app-меню, панель уведомлений. От него наследуется класс приложения.
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
class App extends AppLayout {
|
|
258
|
+
constructor() {
|
|
259
|
+
super();
|
|
260
|
+
this.setRoute(new MainPage());
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
API: `setRoute(page)`, `setWindowControlsLeft()`, `setSearchToAppMenu()`, `setAutoCloseRouteMenu()`,
|
|
266
|
+
`addToHeaderRight([])`, `removeToHeaderRight([])`, `addToHeaderLeft([])`, `addToHeaderLeftBeforeTitle([])`,
|
|
267
|
+
`disableAppMenu()`, `setScript(pathToJS, id)`.
|
|
268
|
+
Статические фабрики: `AppLayout.TABS({width, default, tabs})`, `AppLayout.BUTTON({title, icon, reverse, clickEvent})`,
|
|
269
|
+
`AppLayout.USER_PROFILE({username, image: {noImage, imageLink, imageBase64}, items})`,
|
|
270
|
+
`AppLayout.USER_PROFILE_ITEM({title, icon, clickEvent})`,
|
|
271
|
+
`AppLayout.DIALOG({title, icon, reverse, dialogOptions: {title, closeOutSideClick, style, components}})`.
|
|
272
|
+
|
|
273
|
+
##### `Route`
|
|
274
|
+
Переключатель страниц: `Route extends Events` (сам `Events` из пакета не экспортируется).
|
|
275
|
+
`go(page)` очищает центр окна, вставляет `page.render()` и рассылает событие
|
|
276
|
+
`route_event_<ИмяКласса>`. `Page` потомком `Route` не является — `new Route()` создаётся внутри
|
|
277
|
+
`Page.setBackButton()`.
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
this.setRoute(new MainPage()); // AppLayout: регистрирует страницу в app-меню
|
|
281
|
+
new Route().go(new MainPage()); // переключить центр окна на страницу
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
##### `Page`
|
|
285
|
+
Страница-контейнер: то, что попадает в окно через `setRoute`.
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
class MainPage extends Page {
|
|
289
|
+
constructor() {
|
|
290
|
+
super();
|
|
291
|
+
this.setTitle('Главная');
|
|
292
|
+
this.setMain(false);
|
|
293
|
+
this.add(new Button({ title: 'Кнопка' }));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
API: `add(...components)`, `remove(...components)`, `setBackButton({title, icon, reverse, page})`,
|
|
299
|
+
`setMenuBar(menuBar)`, `getMenuBar()`, `addRouteEvent(self, event)`, `setIcon/setTitle/setMain`,
|
|
300
|
+
`getIcon/getTitle/getMain`, `setFullHeight()`, `setFullWidth()`, `disablePadding()`,
|
|
301
|
+
`getMediaStream({video, audio})`, `render()`.
|
|
302
|
+
|
|
303
|
+
##### `MenuBar`
|
|
304
|
+
Строка меню окна (пункты и выпадающие списки).
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
const menuBar = new MenuBar({ test: true });
|
|
308
|
+
menuBar.addMenuItems(
|
|
309
|
+
new Button({ icon: Icons.NAVIGATION.MENU, clickEvent: () => {} }),
|
|
310
|
+
MenuBar.DROPDOWN({ title: 'Ещё', items: [new Button({ title: 'О программе' })] })
|
|
311
|
+
);
|
|
312
|
+
page.setMenuBar(menuBar);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Опции: `{test}`. API: `addMenuItems(...components)`.
|
|
316
|
+
Фабрика: `MenuBar.DROPDOWN({title, items})`.
|
|
317
|
+
|
|
318
|
+
##### `ContextMenu`
|
|
319
|
+
Контекстное меню для любого элемента.
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
const menu = new ContextMenu({
|
|
323
|
+
items: [
|
|
324
|
+
ContextMenu.Item({ title: 'Открыть', icon: Icons.NAVIGATION.APPS, clickEvent: () => {} }),
|
|
325
|
+
ContextMenu.Separator(),
|
|
326
|
+
ContextMenu.SubMenu({ title: 'Ещё', items: [ContextMenu.Item({ title: 'Свойство' })] })
|
|
327
|
+
]
|
|
328
|
+
});
|
|
329
|
+
menu.attach(table); // table — компонент или DOM-элемент
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Опции: `{items: []}`. API: `attach(target)`, `detach(target)`, `show(x, y)`, `hide()`, `destroy()`.
|
|
333
|
+
Фабрики: `ContextMenu.Item({title, icon, shortcut, disabled, clickEvent})`, `ContextMenu.Separator()`,
|
|
334
|
+
`ContextMenu.SubMenu({title, icon, items})`.
|
|
335
|
+
|
|
336
|
+
##### `Styles`
|
|
337
|
+
Константы CSS-значений для опций `width`, `style` и объектов `styles`.
|
|
338
|
+
|
|
339
|
+
```javascript
|
|
340
|
+
new ContentBlock({ direction: Styles.DIRECTION.COLUMN, justify: Styles.JUSTIFY.SPACE_BEETWEEN });
|
|
341
|
+
Styles.SIZE.WEBKIT_FILL; // "-webkit-fill-available"
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Группы: `WORD_BREAK`, `TEXT_ALIGN`, `ALIGN`, `JUSTIFY` (в `.JUSTIFY` опечатка — `SPACE_BEETWEEN`,
|
|
345
|
+
в коде именно так), `DIRECTION`, `WRAP`, `SIZE`.
|
|
346
|
+
|
|
347
|
+
##### `Icon`
|
|
348
|
+
Отдельная иконка Material Symbols.
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
new Icon(Icons.NAVIGATION.CLOSE, '16px', 'red');
|
|
352
|
+
el.innerHTML = new Icon(Icons.CONTENT.ADD, '20px').getHTML();
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Сигнатура: `new Icon(name, size, color)`. API: `getHTML()`, `set()`.
|
|
356
|
+
|
|
357
|
+
##### `Icons`
|
|
358
|
+
Справочник имён иконок — 18 групп: `NAVIGATION`, `TOGGLE`, `SOCIAL`, `SEARCH`, `PLACES`,
|
|
359
|
+
`NOTIFICATION`, `MAPS`, `IMAGE`, `HOME`, `HARDWARE`, `FILE`, `EDITOR`, `DEVICE`, `CONTENT`,
|
|
360
|
+
`ALERT`, `AUDIO_VIDEO`, `COMMUNICATION`, `ACTIONS`.
|
|
361
|
+
|
|
362
|
+
```javascript
|
|
363
|
+
Icons.ACTIONS.HOME;
|
|
364
|
+
Icons.CONTENT.UNDO;
|
|
365
|
+
Icons.NAVIGATION.ARROW_BACK;
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### Поля ввода
|
|
369
|
+
|
|
370
|
+
##### `TextInput`
|
|
371
|
+
Однострочное текстовое поле.
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const input = new TextInput({
|
|
375
|
+
name: 'name', title: 'Имя', placeholder: 'Введите имя', required: true
|
|
376
|
+
});
|
|
377
|
+
input.setValue('Пётр');
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`, `value`, `disableFocus`,
|
|
381
|
+
`inputListener`, `focusListener`, `blurListener`.
|
|
382
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `setErrorMessage()`,
|
|
383
|
+
`addInputListener()`, `addFocusListener()`, `addBlurListener()`, `set()`.
|
|
384
|
+
|
|
385
|
+
##### `TextArea`
|
|
386
|
+
Многострочное текстовое поле.
|
|
387
|
+
|
|
388
|
+
```javascript
|
|
389
|
+
const area = new TextArea({ name: 'text', title: 'Сообщение', width: '300px', required: true });
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Опции: `name`, `title`, `placeholder`, `width`, `height`, `required`.
|
|
393
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `set()`.
|
|
394
|
+
|
|
395
|
+
##### `PasswordInput`
|
|
396
|
+
Поле пароля.
|
|
397
|
+
|
|
398
|
+
```javascript
|
|
399
|
+
const pass = new PasswordInput({ name: 'pass', title: 'Пароль', required: true });
|
|
400
|
+
pass.setErrorMessage('Слишком короткий пароль');
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`.
|
|
404
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `setErrorMessage()`, `set()`.
|
|
405
|
+
|
|
406
|
+
##### `EmailInput`
|
|
407
|
+
Поле e-mail.
|
|
408
|
+
|
|
409
|
+
```javascript
|
|
410
|
+
const email = new EmailInput({ name: 'email', title: 'E-mail', placeholder: 'mail@example.com' });
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`.
|
|
414
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `set()`.
|
|
415
|
+
|
|
416
|
+
##### `NumberInput`
|
|
417
|
+
Числовое поле.
|
|
418
|
+
|
|
419
|
+
```javascript
|
|
420
|
+
const count = new NumberInput({ name: 'count', title: 'Количество', width: '120px' });
|
|
421
|
+
count.setValue(3);
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Опции: `name`, `title`, `required`, `width`.
|
|
425
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `set()`.
|
|
426
|
+
|
|
427
|
+
##### `DateInput`
|
|
428
|
+
Поле даты со встроенным календарём.
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
const date = new DateInput({ name: 'date', title: 'Дата' });
|
|
432
|
+
date.getValue(); // строка из input
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Опции: `name`, `title`, `required`.
|
|
436
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `setDisabled()`, `set()`.
|
|
437
|
+
|
|
438
|
+
##### `CheckBox`
|
|
439
|
+
Флажок.
|
|
440
|
+
|
|
441
|
+
```javascript
|
|
442
|
+
const box = new CheckBox({ name: 'agree', title: 'Согласен', changeListener: () => {} });
|
|
443
|
+
box.setValue(true);
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Опции: `name`, `title`, `required`, `changeListener`.
|
|
447
|
+
API: `getName()`, `getTitle()`, `getValue()`, `setValue()`, `addChangeListener()`, `setDisabled()`, `set()`.
|
|
448
|
+
|
|
449
|
+
##### `Toggle`
|
|
450
|
+
Переключатель без подписи.
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
const toggle = new Toggle();
|
|
454
|
+
toggle.setValue(true);
|
|
455
|
+
toggle.setMargin({ top: '8px', bottom: '8px' });
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Конструктор без опций. API: `getValue()`, `setValue(boolean)`, `setId(id)`,
|
|
459
|
+
`setMargin({top, bottom, left, right})`, `addChangeListener(fn)`, `set()`.
|
|
460
|
+
|
|
461
|
+
##### `RadioButton`
|
|
462
|
+
Одна радиокнопка.
|
|
463
|
+
|
|
464
|
+
```javascript
|
|
465
|
+
const radio = new RadioButton({ name: 'mode', title: 'Вариант', value: 'a' });
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Опции: `name`, `title`, `stringValue`, `value`, `required`, `width`.
|
|
469
|
+
API: `getName()`, `getValue()`, `setValue()`, `addChangeListener()`, `setDisabled()`, `set()`.
|
|
470
|
+
|
|
471
|
+
##### `RadioGroup`
|
|
472
|
+
Группа радиокнопок, собирается из массива опций.
|
|
473
|
+
|
|
474
|
+
```javascript
|
|
475
|
+
const group = new RadioGroup({ styles: { direction: Styles.DIRECTION.COLUMN } });
|
|
476
|
+
group.addOptions([{ name: 'a', value: 'Вариант A' }, { name: 'b', value: 'Вариант B' }]);
|
|
477
|
+
group.addChangeListener(() => {});
|
|
478
|
+
group.getValue(); // бросает исключение, если ничего не выбрано
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Опции: `styles {direction, wrap, align, justify, width}`.
|
|
482
|
+
API: `addOptions([{name, value}])`, `addChangeListener(fn)`, `clear()`, `getValue()`, `set()`.
|
|
483
|
+
|
|
484
|
+
##### `Select`
|
|
485
|
+
Одиночный выпадающий список.
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
const select = new Select({ name: 'city', title: 'Город', required: true });
|
|
489
|
+
select.addOptions({ title: 'Москва', value: 'msk' }, { title: 'Питер', value: 'spb' });
|
|
490
|
+
select.setDefaultOption({ value: 'msk' });
|
|
491
|
+
select.addValueChangeListener(() => {});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`.
|
|
495
|
+
API: `addOption(title, value)`, `addOptions(...options)`, `setDefaultOption(object)`, `setDropdownHeight(height)`,
|
|
496
|
+
`addValueChangeListener(fn)`, `getName()`, `getValue()`, `setDisabled()`, `setBottonOpenVisible(visible)`
|
|
497
|
+
(имя метода с опечаткой — именно `Botton`), `set()`.
|
|
498
|
+
|
|
499
|
+
##### `ComboBox`
|
|
500
|
+
Список с поиском по вводу.
|
|
501
|
+
|
|
502
|
+
```javascript
|
|
503
|
+
const combo = new ComboBox({ name: 'item', title: 'Поиск', width: '300px', optionsLen: 5 });
|
|
504
|
+
combo.addOptions({ title: 'Пункт', value: '1' });
|
|
505
|
+
combo.addValueChangeListener(() => {});
|
|
506
|
+
combo.getValue();
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`, `optionsLen`.
|
|
510
|
+
API: `addOptions(...options)`, `addValueChangeListener(fn)`, `getName()`, `getValue()`, `setDisabled()`, `clear()`, `set()`.
|
|
511
|
+
|
|
512
|
+
##### `MultiComboBox`
|
|
513
|
+
Множественный выбор с секциями.
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
const multi = new MultiComboBox({ name: 'tags', title: 'Теги', width: '300px' });
|
|
517
|
+
multi.addOptionsWithSections([{ title: 'Секция', options: [{ title: 'Пункт', value: '1' }] }]);
|
|
518
|
+
multi.getValue(); // массив выбранных значений
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Опции: `name`, `title`, `placeholder`, `width`, `required`, `optionsLen`.
|
|
522
|
+
API: `addOptions(...options)`, `addOptionsWithSections([{title, options}])`, `clearDropdown()`,
|
|
523
|
+
`addValueChangeListener(fn)`, `getName()`, `getValue()`, `setDisabled()`, `clear()`, `set()`.
|
|
524
|
+
|
|
525
|
+
##### `FileInput`
|
|
526
|
+
Выбор файлов с диска.
|
|
527
|
+
|
|
528
|
+
```javascript
|
|
529
|
+
const file = new FileInput({
|
|
530
|
+
name: 'file', title: 'Изображение', accept: [AcceptTypes.ALL_IMAGE],
|
|
531
|
+
multiple: false, changeListener: () => {}
|
|
532
|
+
});
|
|
533
|
+
file.getFiles();
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Опции: `name`, `title`, `accept`, `required`, `disableFocus`, `multiple`, `changeListener`, `focusListener`, `blurListener`.
|
|
537
|
+
API: `getFile(index)`, `getFiles()`, `getName()`, `getTitle()`, `setDisabled()`, `addChangeListener()`,
|
|
538
|
+
`addFocusListener()`, `addBlurListener()`, `set()`.
|
|
539
|
+
|
|
540
|
+
##### `AcceptTypes`
|
|
541
|
+
Константа MIME-типов для опции `accept` у `FileInput` (не компонент, а набор строк).
|
|
542
|
+
|
|
543
|
+
```javascript
|
|
544
|
+
AcceptTypes.ALL_IMAGE; // 'image/*'
|
|
545
|
+
AcceptTypes.PNG; // 'image/png'
|
|
546
|
+
AcceptTypes.GIF; // 'image/gif'
|
|
547
|
+
AcceptTypes.JPEG; // 'image/jpeg'
|
|
548
|
+
AcceptTypes.ALL_AUDIO; // 'audio/*'
|
|
549
|
+
AcceptTypes.ALL_VIDEO; // 'video/*'
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
##### `Form`
|
|
553
|
+
HTML-форма с отправкой и перехватом `submit`.
|
|
554
|
+
|
|
555
|
+
```javascript
|
|
556
|
+
const form = new Form({
|
|
557
|
+
method: Form.METHOD.POST,
|
|
558
|
+
components: [
|
|
559
|
+
new TextInput({ name: 'name', title: 'Имя', required: true }),
|
|
560
|
+
Form.SubmitButton('Отправить')
|
|
561
|
+
],
|
|
562
|
+
submitEvent: (e) => {
|
|
563
|
+
e.preventDefault();
|
|
564
|
+
new Notification({ title: 'Form', text: 'Отправлено', showTime: 3000 }).show();
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Опции: `action`, `method`, `components`, `submitEvent`.
|
|
570
|
+
API: `set()`, статики `Form.SubmitButton(title)` и `Form.METHOD.{GET, POST}`.
|
|
571
|
+
|
|
572
|
+
##### `FieldSet`
|
|
573
|
+
Группа полей с легендой.
|
|
574
|
+
|
|
575
|
+
```javascript
|
|
576
|
+
const set = new FieldSet({
|
|
577
|
+
id: 'f1', name: 'group', title: 'Данные',
|
|
578
|
+
style: { direction: Styles.DIRECTION.COLUMN, width: Styles.SIZE.WEBKIT_FILL },
|
|
579
|
+
components: [new TextInput({ name: 'name', title: 'Имя' })]
|
|
580
|
+
});
|
|
581
|
+
set.add(new Button({ title: 'Готово' }));
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Опции: `id`, `name`, `title`, `style {width, height, direction, wrap, align, justify}`, `components`.
|
|
585
|
+
API: `getId()`, `getLegend()`, `setLegend(text)`, `add(...components)`, `set()`.
|
|
586
|
+
|
|
587
|
+
#### Отображение
|
|
588
|
+
|
|
589
|
+
##### `Button`
|
|
590
|
+
Кнопка с текстом и/или иконкой.
|
|
591
|
+
|
|
592
|
+
```javascript
|
|
593
|
+
const button = new Button({ title: 'Нажать', icon: Icons.CONTENT.ADD, clickEvent: () => {} });
|
|
594
|
+
button.setDisabled(true);
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Опции: `reverse`, `title`, `icon`, `clickEvent`.
|
|
598
|
+
API: `getText()`, `setText()`, `addClickListener()`, `setDisabled(boolean)`, `set()`.
|
|
599
|
+
|
|
600
|
+
> `disabled` в конструкторе не читается — состояние задаётся только через `setDisabled(true)`.
|
|
601
|
+
|
|
602
|
+
##### `H`
|
|
603
|
+
Заголовок `H1`…`H6`.
|
|
604
|
+
|
|
605
|
+
```javascript
|
|
606
|
+
new H(1, 'Заголовок');
|
|
607
|
+
new H(3, 'Подзаголовок').setText('Другой текст');
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
Сигнатура: `new H(num, text)`. API: `setText()`, `set()`.
|
|
611
|
+
|
|
612
|
+
##### `Label`
|
|
613
|
+
Подпись с обычным или markdown-текстом.
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
const label = new Label({ text: 'Подпись', fontSize: '14px' });
|
|
617
|
+
const md = new Label({ markdownText: '**Жирный** текст' });
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Опции: `id`, `text`, `markdownText` (взаимоисключающие), `textAlign`, `wordBreak`, `width`, `fontSize`.
|
|
621
|
+
API: `getId()`, `getText()`, `getMarkdownText()`, `setId()`, `setText()`, `setMarkdownText()`, `set()`.
|
|
622
|
+
|
|
623
|
+
##### `Paragraph`
|
|
624
|
+
Абзац текста.
|
|
625
|
+
|
|
626
|
+
```javascript
|
|
627
|
+
new Paragraph('Текст абзаца');
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Сигнатура: `new Paragraph(text)`. API: `setText()`, `set()`.
|
|
631
|
+
|
|
632
|
+
##### `Badge`
|
|
633
|
+
Небольшой цветной бейдж.
|
|
634
|
+
|
|
635
|
+
```javascript
|
|
636
|
+
new Badge({ text: 'ERROR', style: Badge.STYLE.ERROR });
|
|
637
|
+
new Badge({ markdownText: '**OK**', style: Badge.STYLE.SUCCESS });
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Опции: `id`, `text`, `markdownText`, `style`.
|
|
641
|
+
API: `getId()`, `getText()`, `getMarkdownText()`, `setId()`, `setText()`, `setMarkdownText()`, `setStyle()`, `set()`.
|
|
642
|
+
Константа: `Badge.STYLE.{ERROR, SUCCESS, WARNING}`.
|
|
643
|
+
|
|
644
|
+
##### `Spinner`
|
|
645
|
+
Индикатор загрузки.
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
const spinner = new Spinner(Spinner.SIZE.DEFAULT, 'auto');
|
|
649
|
+
spinner.remove();
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
Сигнатура: `new Spinner(size, margin)`. API: `remove()`, `set()`.
|
|
653
|
+
Константа: `Spinner.SIZE.{V_SMALL, SMALL, DEFAULT, BIG, V_BIG}`, у `V_SMALL` — `{SIZE: 30, BORDER: 3}`.
|
|
654
|
+
|
|
655
|
+
##### `ProgressBar`
|
|
656
|
+
Полоса прогресса с подписями.
|
|
657
|
+
|
|
658
|
+
```javascript
|
|
659
|
+
const progress = new ProgressBar({ max: 100 });
|
|
660
|
+
progress.setValue(40);
|
|
661
|
+
progress.setProgressText('40%');
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Опции: `{max}`.
|
|
665
|
+
API: `setMax()`, `setWidth()`, `setValue()`, `setProgressText()`, `setProgressCountText()`, `set()`.
|
|
666
|
+
|
|
667
|
+
##### `ContentBlock`
|
|
668
|
+
Flex-контейнер для произвольной компоновки.
|
|
669
|
+
|
|
670
|
+
```javascript
|
|
671
|
+
const block = new ContentBlock({
|
|
672
|
+
direction: Styles.DIRECTION.COLUMN, align: Styles.ALIGN.START, justify: Styles.JUSTIFY.START
|
|
673
|
+
});
|
|
674
|
+
block.add(new Paragraph('Строка'), new Button({ title: 'Кнопка' }));
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Опции: `display`, `direction`, `wrap`, `align`, `justify`, `disableMarginChild`.
|
|
678
|
+
API: `add(...components)`, `remove(...components)`, `clear()`, `disableMarginChild()`, `setAutoOverflow()`,
|
|
679
|
+
`setContentEditable()`, `setWidth()`, `setHeight()`, `setPadding()`, `set()`.
|
|
680
|
+
|
|
681
|
+
##### `Card`
|
|
682
|
+
Карточка с шапкой (иконка, заголовок, описание) и телом. Внутри витрины `app/views/0_maket.js`.
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
const card = new Card({ id: 'c1', title: 'Заголовок', description: 'Пояснение', icon: Icons.CONTENT.ADD });
|
|
686
|
+
card.add(new Paragraph('Содержимое'));
|
|
687
|
+
card.getBody(); // <card_body>
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
Опции: `id`, `title`, `description`, `icon`, `width`, `height`, `style`, `components`.
|
|
691
|
+
API: `getId()`, `getTitle()`, `getDescription()`, `getBody()`, `setId()`, `setTitle()`, `setDescription()`,
|
|
692
|
+
`setIcon()`, `setWidth()`, `setHeight()`, `setStyle()`, `add(...components)`, `clear()`, `set()`.
|
|
693
|
+
|
|
694
|
+
##### `Hero`
|
|
695
|
+
Шапка страницы с иконкой, текстом и статистикой.
|
|
696
|
+
|
|
697
|
+
```javascript
|
|
698
|
+
new Hero({
|
|
699
|
+
title: 'chUiJS', description: 'Витрина компонентов', icon: Icons.HOME.SENSOR_DOOR,
|
|
700
|
+
stats: [{ value: '56', label: 'КОМПОНЕНТОВ' }]
|
|
701
|
+
});
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
Опции: `id`, `title`, `description`, `icon`, `stats: [{value, label}]`, `style`, `width`, `height`.
|
|
705
|
+
API: `getId()`, `getTitle()`, `getDescription()`, `setId()`, `setTitle()`, `setDescription()`, `setIcon()`,
|
|
706
|
+
`setWidth()`, `setHeight()`, `setStyle()`, `addStat(value, label)`, `clearStats()`, `set()`.
|
|
707
|
+
|
|
708
|
+
##### `Details`
|
|
709
|
+
Раскрывающийся блок.
|
|
710
|
+
|
|
711
|
+
```javascript
|
|
712
|
+
const details = new Details({ title: 'Подробнее', width: Styles.SIZE.WEBKIT_FILL });
|
|
713
|
+
details.add(new Paragraph('Скрытый текст'));
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
Опции: `title`, `contenteditable`, `direction`, `wrap`, `align`, `justify`, `width`.
|
|
717
|
+
API: `add(...components)`, `set()`.
|
|
718
|
+
|
|
719
|
+
##### `Accordion`
|
|
720
|
+
Аккордеон из секций; на вход идёт **массив**, а не объект.
|
|
721
|
+
|
|
722
|
+
```javascript
|
|
723
|
+
new Accordion([{ b_text: 'Раздел 1', p_text: 'Текст раздела 1' }]);
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
##### `CodeBlock`
|
|
727
|
+
Блок с предформатированным текстом (команды, код).
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
new CodeBlock('npm run start', { width: Styles.SIZE.MAX_CONTENT });
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Сигнатура: `new CodeBlock(text, options {textAlign, wordBreak, width})`. API: `setText()`, `set()`.
|
|
734
|
+
|
|
735
|
+
##### `HtmlBlock`
|
|
736
|
+
Произвольный HTML внутри layout.
|
|
737
|
+
|
|
738
|
+
```javascript
|
|
739
|
+
const html = new HtmlBlock(Styles.SIZE.WEBKIT_FILL);
|
|
740
|
+
html.setHtml('<b>жирный</b> и <i>курсив</i>');
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Сигнатура: `new HtmlBlock(width)`. API: `setHtml(html)`, `set()`.
|
|
744
|
+
|
|
745
|
+
##### `Table`
|
|
746
|
+
Таблица по массиву объектов (ключи объекта — колонки).
|
|
747
|
+
|
|
748
|
+
```javascript
|
|
749
|
+
class Car { constructor(car, model, size) { this.car = car; this.model = model; this.size = size; } }
|
|
750
|
+
const table = new Table({ data: [new Car('Acura', 'NSX', 10)], sorted: true });
|
|
751
|
+
table.setFilterByProperty(Table.FILTER_TYPE.PARTIAL_MATCH, 'model', 'n');
|
|
752
|
+
table.setContextMenu(menu);
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
Опции: `data`, `customName`, `userSelect`, `sorted`, `columnsWidth`.
|
|
756
|
+
API: `setData()`, `getData()`, `refresh(data)`, `getColumn(col)`, `setContextMenu(ctx)`,
|
|
757
|
+
`setFilterByProperty(type, property, filterValue)`, `setFilterByMultiProperty(type, properties, filterValue)`,
|
|
758
|
+
`removeFilterByProperty(property, filterValue)`, `set()`.
|
|
759
|
+
Константа: `Table.FILTER_TYPE.{CLEAR_MATCH, PARTIAL_MATCH}`.
|
|
760
|
+
|
|
761
|
+
##### `TreeView`
|
|
762
|
+
Дерево из кнопок и раскрывающихся узлов.
|
|
763
|
+
|
|
764
|
+
```javascript
|
|
765
|
+
new TreeView({
|
|
766
|
+
width: Styles.SIZE.WEBKIT_FILL,
|
|
767
|
+
components: [
|
|
768
|
+
TreeView.Button({ title: 'Кнопка', listener: () => {} }),
|
|
769
|
+
TreeView.ExpandButton({ title: 'Папка', subButtons: [], components: [new Paragraph('Внутри')] })
|
|
770
|
+
]
|
|
771
|
+
});
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
Опции: `width`, `components`.
|
|
775
|
+
Фабрики: `TreeView.Button({title, listener})`, `TreeView.ExpandButton({title, subButtons, components})`.
|
|
776
|
+
|
|
777
|
+
##### `Tabs` и `Tab`
|
|
778
|
+
Вкладки: `Tab` создаётся отдельно, `Tabs` собирает их в панель.
|
|
779
|
+
|
|
780
|
+
```javascript
|
|
781
|
+
const first = new Tab('Вкладка 1');
|
|
782
|
+
first.addContent(new TextInput({ title: 'Поле 1', width: '300px' }));
|
|
783
|
+
new Tabs({ default: 0, width: Styles.SIZE.WEBKIT_FILL, tabs: [first, new Tab('Вкладка 2')] });
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
`Tab(title)`: `addContent(...contents)`, `getTab()`, `getContent()`, `set()`.
|
|
787
|
+
`Tabs({width, default, tabs})`: `set()`.
|
|
788
|
+
|
|
789
|
+
##### `BarGraph`
|
|
790
|
+
Столбчатая диаграмма (клик по столбцу показывает `Notification`).
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
new BarGraph({ data: { 'Пн': 4, 'Вт': 7 }, colors: ['#0a84ff'] });
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
##### `PieGraph`
|
|
797
|
+
Круговая или кольцевая диаграмма.
|
|
798
|
+
|
|
799
|
+
```javascript
|
|
800
|
+
new PieGraph({ data: { 'Да': 30, 'Нет': 70 }, colors: ['#34c759', '#ff3b30'], legend: true, doughnutHoleSize: 0.5 });
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
#### Окна, уведомления, медиа
|
|
804
|
+
|
|
805
|
+
##### `Dialog`
|
|
806
|
+
Модальное окно с шапкой, телом и подвалом.
|
|
807
|
+
|
|
808
|
+
```javascript
|
|
809
|
+
const dialog = new Dialog({ width: '420px', height: '260px', closeOutSideClick: true });
|
|
810
|
+
dialog.addToHeader(new Paragraph('Заголовок'));
|
|
811
|
+
dialog.addToBody(new Paragraph('Содержимое'));
|
|
812
|
+
dialog.addToFooter(new Button({ title: 'Понятно', clickEvent: () => dialog.close() }));
|
|
813
|
+
dialog.open();
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
Опции: `width` (по умолчанию `max-content`), `height`, `closeOutSideClick`.
|
|
817
|
+
API: `addToHeader()`, `addToBody()`, `addToFooter()`, `removeFromHeader()`, `removeFromBody()`,
|
|
818
|
+
`removeFromFooter()`, `open()`, `openAndClose()`, `close()`, свойство `isOpen`, `set()`.
|
|
819
|
+
|
|
820
|
+
##### `Popup`
|
|
821
|
+
Готовые системные попапы: `alert`, `confirm`, `prompt`.
|
|
822
|
+
|
|
823
|
+
```javascript
|
|
824
|
+
const popup = new Popup();
|
|
825
|
+
popup.alert({ title: 'Внимание', message: 'Текст' });
|
|
826
|
+
const ok = await popup.confirm({ title: 'Удалить?', message: 'Действие необратимо' });
|
|
827
|
+
const data = await popup.prompt({ title: 'Ввод', message: 'Заполните поля',
|
|
828
|
+
inputs: { text: { placeholder: 'Логин', errorMessage: 'Обязательное поле' },
|
|
829
|
+
password: { placeholder: 'Пароль' } } });
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
`alert` ничего не возвращает, `confirm` → `Promise<boolean>`,
|
|
833
|
+
`prompt` → `Promise<{text?, password?}>`. Классы `PopupAlert`, `PopupConfirm`, `PopupPrompt` наружу не экспортируются.
|
|
834
|
+
|
|
835
|
+
##### `Notification`
|
|
836
|
+
Тост в панели уведомлений.
|
|
837
|
+
|
|
838
|
+
```javascript
|
|
839
|
+
new Notification({
|
|
840
|
+
title: 'Готово', text: 'Файл сохранён',
|
|
841
|
+
style: Notification.STYLE.SUCCESS, showTime: 5000
|
|
842
|
+
}).show(true); // true — продублировать системным уведомлением
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
Опции: `title` **или** `markdownTitle`, `text` **или** `markdownText`, `style`, `showTime`.
|
|
846
|
+
API: `show(showOnSystem)`. Константа: `Notification.STYLE.{ERROR, SUCCESS, WARNING}`.
|
|
847
|
+
|
|
848
|
+
##### `UpdateNotification`
|
|
849
|
+
Уведомление о процессе обновления со спиннером.
|
|
850
|
+
|
|
851
|
+
```javascript
|
|
852
|
+
const update = new UpdateNotification({ title: 'Обновление', text: 'Скачивание', spinner: true });
|
|
853
|
+
update.show(true);
|
|
854
|
+
update.hide();
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
Опции: `{title, text, spinner}`. API: `show(showOnSystem)`, `hide()`.
|
|
858
|
+
|
|
859
|
+
##### `DownloadNotification`
|
|
860
|
+
Уведомление о загрузке файла.
|
|
861
|
+
|
|
862
|
+
```javascript
|
|
863
|
+
const download = new DownloadNotification({ title: 'Файл', text: 'Загрузка...', type: 'image' });
|
|
864
|
+
download.show();
|
|
865
|
+
download.update('Файл', 'Почти готово');
|
|
866
|
+
download.done(); // или download.error();
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
Опции: `{title, text, type}`. API: `show()`, `update(title, text)`, `done()`, `error()`.
|
|
870
|
+
|
|
871
|
+
##### `DownloadProgressNotification`
|
|
872
|
+
Уведомление о загрузке с прогрессом.
|
|
873
|
+
|
|
874
|
+
```javascript
|
|
875
|
+
const progress = new DownloadProgressNotification({ title: 'Файл', text: 'Загрузка...', type: 'image' });
|
|
876
|
+
progress.show();
|
|
877
|
+
progress.update('Файл', 'Половина', 50, 100); // title, text, value, max
|
|
878
|
+
progress.done();
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
Опции: `{title, text, type}` — `max` задаётся не в конструкторе, а в `update(title, text, value, max)`.
|
|
882
|
+
API: `show()`, `update(title, text, value, max)`, `done()`, `error()`.
|
|
883
|
+
|
|
884
|
+
##### `Calendar`
|
|
885
|
+
Сетка месяца (месяц нумеруется с 1).
|
|
886
|
+
|
|
887
|
+
```javascript
|
|
888
|
+
const calendar = new Calendar(2026, 9);
|
|
889
|
+
calendar.getCalendar();
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
Сигнатура: `new Calendar(year, month)`. API: `getCalendar()`, `getDateNow()`, `getMonthName()`,
|
|
893
|
+
`getDayName()`, `getDate()`, `getMonth()`, `getYear()`. Метода `set()` нет — это не компонент-элемент.
|
|
894
|
+
|
|
895
|
+
##### `SlideShow`
|
|
896
|
+
Слайдер; слайды описываются фабрикой `SLIDE`.
|
|
897
|
+
|
|
898
|
+
```javascript
|
|
899
|
+
new SlideShow({
|
|
900
|
+
width: Styles.SIZE.WEBKIT_FILL, height: '260px',
|
|
901
|
+
autoplay: { status: true, interval: 4000 },
|
|
902
|
+
slides: [SlideShow.SLIDE({
|
|
903
|
+
size: { width: Styles.SIZE.WEBKIT_FILL, height: Styles.SIZE.WEBKIT_FILL },
|
|
904
|
+
style: { align: Styles.ALIGN.CENTER, justify: Styles.JUSTIFY.CENTER },
|
|
905
|
+
components: [new H(3, 'Слайд 1'), new Paragraph('Текст')]
|
|
906
|
+
})]
|
|
907
|
+
});
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Опции: `width`, `height`, `autoplay {status, interval}`, `slides`.
|
|
911
|
+
Фабрика: `SlideShow.SLIDE({size: {width, height}, style: {direction, wrap, align, justify}, components: [...]})`
|
|
912
|
+
(класс `Slide` не экспортируется).
|
|
913
|
+
|
|
914
|
+
> Подпись `SLIDE` обещает `{width, height}`, но `Slide` читает `options.size.width` и
|
|
915
|
+
> `options.style.*`: вызов `SlideShow.SLIDE({components: [...]})` без `size` и `style` падает
|
|
916
|
+
> с `TypeError`. Передавайте оба объекта, как в примере выше.
|
|
917
|
+
|
|
918
|
+
##### `TextEditor`
|
|
919
|
+
WYSIWYG-редактор с панелью форматирования.
|
|
920
|
+
|
|
921
|
+
```javascript
|
|
922
|
+
const editor = new TextEditor('100%', {
|
|
923
|
+
title: 'Описание',
|
|
924
|
+
controls: { BOLD: true, ITALIC: true, INSERT_LINK: true }
|
|
925
|
+
});
|
|
926
|
+
editor.setValueAsHTML('<b>текст</b>');
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
Сигнатура: `new TextEditor(height, options {title, controls})`.
|
|
930
|
+
Ключи `controls` (20): `UNDO_REDO`, `BLOCK_FORMAT`, `FONT_SIZE`, `REMOVE_FORMAT`, `BOLD`, `ITALIC`,
|
|
931
|
+
`STRIKE_THROUGH`, `UNDERLINE`, `SUPERSCRIPT`, `SUBSCRIPT`, `JUSTIFY_LEFT`, `JUSTIFY_CENTER`,
|
|
932
|
+
`JUSTIFY_RIGHT`, `JUSTIFY_FULL`, `LISTS`, `CONTENT_CONTROLS`, `INSERT_LINK`, `INSERT_TABLE`,
|
|
933
|
+
`INSERT_IMAGE`, `LINE_BREAK`. Панель форматирования собирается классом `TextEditorPanel`,
|
|
934
|
+
который наружу не экспортируется.
|
|
935
|
+
API: `getValueAsHTML()`, `setValueAsHTML()`, `getValueAsText()`, `setValueAsText()`, `addInputListener()`, `set()`.
|
|
936
|
+
|
|
937
|
+
##### `Image`
|
|
938
|
+
Изображение из файла, base64 или по HTTP.
|
|
939
|
+
|
|
940
|
+
```javascript
|
|
941
|
+
new Image({ path: `${__dirname}/icon.png`, width: '64px', openPopup: true });
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
Опции: `path`, `base64`, `httpPath`, `width`, `height`, `openPopup`, `disableMargin`.
|
|
945
|
+
API: `setWidth()`, `setHeight()`, `set()`.
|
|
946
|
+
|
|
947
|
+
##### `Audio`
|
|
948
|
+
Аудиоплеер с плейлистом и эквалайзером.
|
|
949
|
+
|
|
950
|
+
```javascript
|
|
951
|
+
const player = new Audio({ autoplay: false, pin: Audio.PIN.TOP, playlist: true, width: '400px' });
|
|
952
|
+
player.setPlayList([{ title: 'Трек', artist: 'Исполнитель', mimetype: 'audio/mpeg' }]);
|
|
953
|
+
player.setActive(0);
|
|
954
|
+
player.play();
|
|
955
|
+
player.openFolder('/music');
|
|
956
|
+
player.restoreFX();
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
Опции: `autoplay`, `pin`, `playlist`, `width`, `height`.
|
|
960
|
+
API: `play()`, `next()`, `prev()`, `openFolder(path)`, `setPlayList(list)`, `setActive(index)`, `restoreFX()`, `set()`.
|
|
961
|
+
Константы: `Audio.PIN.{TOP, BOTTOM}` и `Audio.MIMETYPES` (`AU_SND`, `LINEAR_PCM`, `MID_RMI`, `MP3`,
|
|
962
|
+
`MP4`, `AIF_AIFC_AIFF`, `M3U`, `RA_RAM`, `OGG`, `VORBIS`, `WAV`; у `LINEAR_PCM` значение
|
|
963
|
+
`'auido/L24'` — опечатка уже в коде).
|
|
964
|
+
|
|
965
|
+
##### `Video`
|
|
966
|
+
Видеоплеер; умеет проигрывать `MediaStream` (например, веб-камеру).
|
|
967
|
+
|
|
968
|
+
```javascript
|
|
969
|
+
const video = new Video({ autoplay: false, width: Styles.SIZE.WEBKIT_FILL, height: 'auto' });
|
|
970
|
+
video.setStream(stream); // MediaStream из Page.getMediaStream({video: true})
|
|
971
|
+
video.setPlayList([{ title: 'Видео', mimetype: Video.MIMETYPES.MP4, videoPath: '/video.mp4' }]);
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
Опции: `autoplay`, `width`, `height`.
|
|
975
|
+
API: `setStream(stream)`, `setPlayList(list)`, `getPlayList()`, `set()`.
|
|
976
|
+
Константа: `Video.MIMETYPES.{MP4, WEBM, M4V, QUICK_TIME}`.
|
|
977
|
+
|
|
978
|
+
##### `WebView`
|
|
979
|
+
Встроенный тег `webview` со своим preload и партишеном.
|
|
980
|
+
|
|
981
|
+
```javascript
|
|
982
|
+
const web = new WebView('https://example.com', false);
|
|
983
|
+
web.setPartition('persist:app');
|
|
984
|
+
web.setPreload(`${__dirname}/preload.js`);
|
|
985
|
+
web.insertCustomRes({ cssPath: `${__dirname}/inject.css`, jsPath: `${__dirname}/inject.js` });
|
|
986
|
+
web.addFinishLoadEvent(() => {});
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
Сигнатура: `new WebView(url, dev)`. API: `setUrl()`, `setPartition()`, `setPreload()`,
|
|
990
|
+
`addStartLoadEvent()`, `addStopLoadEvent()`, `addFinishLoadEvent()`, `insertCustomRes({cssPath, jsPath})`,
|
|
991
|
+
`executeJavaScript(code)`, `send(channel, ...args)`, `getWebContentsId()`, `set()`.
|
|
992
|
+
|
|
993
|
+
##### `Console`
|
|
994
|
+
Встроенная консоль для вывода текста.
|
|
995
|
+
|
|
996
|
+
```javascript
|
|
997
|
+
const console_ = new Console({ width: '100%', height: '240px' });
|
|
998
|
+
console_.addText('$ npm run start');
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
Опции: `{width, height}`. API: `addText(text)`, `set()`.
|
|
1002
|
+
|
|
1003
|
+
##### `CustomElement`
|
|
1004
|
+
Обёртка над произвольным DOM-элементом.
|
|
1005
|
+
|
|
1006
|
+
```javascript
|
|
1007
|
+
const el = new CustomElement({ tag: 'div', id: 'my-block', className: 'css_class', pathToCSS: `${__dirname}/style.css` });
|
|
1008
|
+
el.innerHTML('<b>HTML</b>');
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
Опции: `tag`, `id`, `className`, `pathToCSS` (`tag` и `id` обязательны — иначе исключение).
|
|
1012
|
+
API: `innerText()`, `innerHTML()`, `addEventListener(type, listener)`, `set()`.
|
|
1013
|
+
|
|
1014
|
+
#### Константы и статические фабрики
|
|
1015
|
+
|
|
1016
|
+
Кроме компонентов из `require('chuijs')` достаются константы и фабрики:
|
|
1017
|
+
|
|
1018
|
+
- **Размеры и раскладка** — `Styles.WORD_BREAK.*`, `Styles.TEXT_ALIGN.*`, `Styles.ALIGN.*`,
|
|
1019
|
+
`Styles.JUSTIFY.*`, `Styles.DIRECTION.*`, `Styles.WRAP.*`, `Styles.SIZE.*`.
|
|
1020
|
+
- **Стили уведомлений и бейджей** — `Notification.STYLE.{ERROR, SUCCESS, WARNING}`,
|
|
1021
|
+
`Badge.STYLE.{ERROR, SUCCESS, WARNING}`.
|
|
1022
|
+
- **Размеры спиннера** — `Spinner.SIZE.{V_SMALL, SMALL, DEFAULT, BIG, V_BIG}`.
|
|
1023
|
+
- **Фильтры таблицы** — `Table.FILTER_TYPE.{CLEAR_MATCH, PARTIAL_MATCH}`.
|
|
1024
|
+
- **Метод формы** — `Form.METHOD.{GET, POST}`.
|
|
1025
|
+
- **Типы файлов** — `AcceptTypes.{ALL_IMAGE, PNG, GIF, JPEG, ALL_AUDIO, ALL_VIDEO}`.
|
|
1026
|
+
- **Медиа** — `Audio.PIN.{TOP, BOTTOM}`, `Audio.MIMETYPES`, `Video.MIMETYPES.{MP4, WEBM, M4V, QUICK_TIME}`.
|
|
1027
|
+
- **Элементы шапки** — `AppLayout.TABS({width, default, tabs})`, `AppLayout.BUTTON({title, icon, reverse, clickEvent})`,
|
|
1028
|
+
`AppLayout.USER_PROFILE({username, image, items})`, `AppLayout.USER_PROFILE_ITEM({title, icon, clickEvent})`,
|
|
1029
|
+
`AppLayout.DIALOG({title, icon, reverse, dialogOptions})`.
|
|
1030
|
+
- **Меню, дерево, слайды** — `MenuBar.DROPDOWN({title, items})`, `ContextMenu.Item/Separator/SubMenu`,
|
|
1031
|
+
`TreeView.Button({title, listener})`, `TreeView.ExpandButton({title, subButtons, components})`,
|
|
1032
|
+
`Form.SubmitButton(title)`, `SlideShow.SLIDE({size, style, components})`.
|
|
1033
|
+
|
|
1034
|
+
> Не экспортируются из пакета (доступны только через фабрики): `Slide`, `SubmitButton`,
|
|
1035
|
+
> `TextEditorPanel`, `Commands`, а также классы popup-окон (`PopupAlert`, `PopupConfirm`, `PopupPrompt`).
|
|
1036
|
+
> Из `chui_functions` наружу не отдаются `getDate()`, `style_parse(json, component)`,
|
|
1037
|
+
> `markdownToHtml(text)`, `htmlToMarkdown(html)`, `shouldOpenDropdownUp(dropdown, anchor)`,
|
|
1038
|
+
> `setStyles(pathToCSS, component)` — они доступны только внутри фреймворка.
|
|
1039
|
+
|
|
1040
|
+
#### Сценарии целиком
|
|
1041
|
+
|
|
1042
|
+
```javascript
|
|
1043
|
+
/** AppLayout + Page + шапка */
|
|
1044
|
+
const { AppLayout, Page, Icon, Icons, Button, TextInput, Notification, MenuBar, render, Log } = require('chuijs');
|
|
1045
|
+
|
|
1046
|
+
class MainPage extends Page {
|
|
1047
|
+
constructor() {
|
|
1048
|
+
super();
|
|
1049
|
+
this.setTitle('Главная');
|
|
1050
|
+
this.setIcon(Icons.ACTIONS.HOME);
|
|
1051
|
+
this.setMain(true); // домашний роут
|
|
1052
|
+
this.setFullWidth(); // растянуть по ширине окна
|
|
1053
|
+
const menuBar = new MenuBar({ test: true });
|
|
1054
|
+
menuBar.addMenuItems(
|
|
1055
|
+
new Button({ icon: Icons.NAVIGATION.MENU, clickEvent: () => {} }),
|
|
1056
|
+
MenuBar.DROPDOWN({ title: 'Ещё', items: [new Button({ title: 'О программе' })] })
|
|
1057
|
+
);
|
|
1058
|
+
this.setMenuBar(menuBar);
|
|
1059
|
+
const name = new TextInput({ title: 'Имя', placeholder: 'Имя' });
|
|
1060
|
+
this.add(name, new Button({ title: 'Поздороваться', clickEvent: () =>
|
|
1061
|
+
new Notification({ title: 'Привет!', text: name.getValue(), style: Notification.STYLE.SUCCESS, showTime: 4000 }).show()
|
|
1062
|
+
}));
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
class App extends AppLayout {
|
|
1067
|
+
constructor() {
|
|
1068
|
+
super();
|
|
1069
|
+
this.setRoute(new MainPage());
|
|
1070
|
+
this.addToHeaderLeft([AppLayout.BUTTON({ title: 'Назад', icon: Icons.NAVIGATION.ARROW_BACK, reverse: true, clickEvent: () => {} })]);
|
|
1071
|
+
this.addToHeaderRight([AppLayout.USER_PROFILE({
|
|
1072
|
+
username: 'user',
|
|
1073
|
+
items: [AppLayout.USER_PROFILE_ITEM({ title: 'Настройки', clickEvent: () => {} })]
|
|
1074
|
+
})]);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
render(() => new App()).catch(err => Log.error(err));
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
```javascript
|
|
1081
|
+
/** Page.setBackButton + переключение роутов */
|
|
1082
|
+
const page = new MainPage();
|
|
1083
|
+
page.setBackButton({ title: 'Назад', icon: Icons.NAVIGATION.ARROW_BACK, reverse: true, page: new SecondPage() });
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
```javascript
|
|
1087
|
+
/** Select, ComboBox, MultiComboBox, RadioGroup, Form */
|
|
1088
|
+
const select = new Select({ title: 'Select' });
|
|
1089
|
+
select.addOptions({ title: 'Пункт 1', value: '1' }, { title: 'Пункт 2', value: '2' });
|
|
1090
|
+
|
|
1091
|
+
const combo = new ComboBox({ title: 'ComboBox', width: '300px' });
|
|
1092
|
+
combo.addOptions({ title: 'Пункт', value: '1' });
|
|
1093
|
+
|
|
1094
|
+
const multi = new MultiComboBox({ title: 'MultiComboBox', width: '300px' });
|
|
1095
|
+
multi.addOptionsWithSections([{ title: 'Секция', options: [{ title: 'Пункт', value: '1' }] }]);
|
|
1096
|
+
|
|
1097
|
+
const group = new RadioGroup({ styles: { direction: Styles.DIRECTION.COLUMN } });
|
|
1098
|
+
group.addOptions([{ name: 'a', value: 'Вариант A' }, { name: 'b', value: 'Вариант B' }]);
|
|
1099
|
+
|
|
1100
|
+
try { group.getValue(); } catch (e) { Log.error(e); } // бросит, если ничего не выбрано
|
|
1101
|
+
|
|
1102
|
+
const form = new Form({
|
|
1103
|
+
method: Form.METHOD.POST,
|
|
1104
|
+
components: [
|
|
1105
|
+
new TextInput({ name: 'name', title: 'Имя', required: true }),
|
|
1106
|
+
new TextArea({ name: 'text', title: 'Сообщение', width: '300px', required: true }),
|
|
1107
|
+
Form.SubmitButton('Отправить')
|
|
1108
|
+
],
|
|
1109
|
+
submitEvent: (e) => { e.preventDefault(); new Notification({ title: 'Form', text: 'Отправлено', showTime: 3000 }).show(); }
|
|
1110
|
+
});
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
```javascript
|
|
1114
|
+
/** Table, TreeView, Tabs, ContextMenu */
|
|
1115
|
+
class Car { constructor(car, model, size) { this.car = car; this.model = model; this.size = size; } }
|
|
1116
|
+
|
|
1117
|
+
const table = new Table({ data: [new Car('Acura', 'NSX', 10), new Car('Ford', 'SHELBY', 34)], sorted: true });
|
|
1118
|
+
table.setFilterByProperty(Table.FILTER_TYPE.PARTIAL_MATCH, 'model', 'n');
|
|
1119
|
+
|
|
1120
|
+
const tree = new TreeView({
|
|
1121
|
+
width: Styles.SIZE.WEBKIT_FILL,
|
|
1122
|
+
components: [TreeView.ExpandButton({ title: 'Папка', subButtons: [], components: [new Paragraph('Внутри')] })]
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
const first = new Tab('Вкладка 1');
|
|
1126
|
+
first.addContent(new TextInput({ title: 'Поле 1', width: '300px' }));
|
|
1127
|
+
const tabs = new Tabs({ default: 0, width: Styles.SIZE.WEBKIT_FILL, tabs: [first, new Tab('Вкладка 2')] });
|
|
1128
|
+
|
|
1129
|
+
const menu = new ContextMenu({
|
|
1130
|
+
items: [
|
|
1131
|
+
ContextMenu.Item({ title: 'Открыть', icon: Icons.NAVIGATION.APPS, clickEvent: () => {} }),
|
|
1132
|
+
ContextMenu.Separator(),
|
|
1133
|
+
ContextMenu.SubMenu({ title: 'Ещё', items: [ContextMenu.Item({ title: 'Свойство', clickEvent: () => {} })] })
|
|
1134
|
+
]
|
|
1135
|
+
});
|
|
1136
|
+
menu.attach(table);
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
```javascript
|
|
1140
|
+
/** Dialog + Popup */
|
|
1141
|
+
const dialog = new Dialog({ width: '420px', height: '260px', closeOutSideClick: true });
|
|
1142
|
+
dialog.addToHeader(new Paragraph('Dialog — модальное окно'));
|
|
1143
|
+
dialog.addToBody(new Paragraph('Содержимое задаётся через addToBody().'));
|
|
1144
|
+
dialog.addToFooter(new Button({ title: 'Понятно', clickEvent: () => dialog.close() }));
|
|
1145
|
+
dialog.open();
|
|
1146
|
+
|
|
1147
|
+
const popup = new Popup();
|
|
1148
|
+
popup.alert({ title: 'Внимание', message: 'Текст сообщения' });
|
|
1149
|
+
const ok = await popup.confirm({ title: 'Удалить?', message: 'Действие необратимо', okText: 'Удалить', cancelText: 'Отмена' });
|
|
1150
|
+
const data = await popup.prompt({ title: 'Ввод', message: 'Заполните поля', inputs: { text: { placeholder: 'Логин', errorMessage: 'Обязательное поле' }, password: { placeholder: 'Пароль' } } });
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
```javascript
|
|
1154
|
+
/** TextEditor, SlideShow, графики, календарь */
|
|
1155
|
+
const editor = new TextEditor('100%', { title: 'TextEditor', controls: { BOLD: true, ITALIC: true, INSERT_LINK: true } });
|
|
1156
|
+
|
|
1157
|
+
const slideshow = new SlideShow({
|
|
1158
|
+
width: Styles.SIZE.WEBKIT_FILL, height: '260px',
|
|
1159
|
+
autoplay: { status: true, interval: 4000 },
|
|
1160
|
+
slides: [SlideShow.SLIDE({
|
|
1161
|
+
size: { width: Styles.SIZE.WEBKIT_FILL, height: Styles.SIZE.WEBKIT_FILL },
|
|
1162
|
+
style: { align: Styles.ALIGN.CENTER },
|
|
1163
|
+
components: [new H(3, 'Слайд 1'), new Paragraph('Текст')]
|
|
1164
|
+
})]
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
const bar = new BarGraph({ data: { 'Пн': 4, 'Вт': 7 }, colors: ['#0a84ff', '#34c759'] });
|
|
1168
|
+
const pie = new PieGraph({ data: { 'Да': 30, 'Нет': 70 }, colors: ['#34c759', '#ff3b30'], legend: true, doughnutHoleSize: 0.5 });
|
|
1169
|
+
|
|
1170
|
+
const calendar = new Calendar(new Date().getFullYear(), new Date().getMonth() + 1);
|
|
1171
|
+
const weeks = calendar.getCalendar();
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
## Модель безопасности renderer
|
|
1175
|
+
|
|
1176
|
+
Текущая конфигурация рассчитана на доверенный код приложения (всё, что грузится в окно,
|
|
1177
|
+
пишется вами), а не на загрузку сторонних страниц. Что включено сейчас:
|
|
1178
|
+
|
|
1179
|
+
- `process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = "true"` — глобально глушит предупреждения Electron о небезопасной конфигурации (`index.js`).
|
|
1180
|
+
- В `webPreferences` окна (`Main`): `nodeIntegration: true`, `contextIsolation: false`,
|
|
1181
|
+
`nodeIntegrationInSubFrames: true`, `webviewTag: true`, `enableRemoteModule: true`.
|
|
1182
|
+
- `preload` не является мостом: в него передаётся ваш renderer-энтрипоинт (`options.render`),
|
|
1183
|
+
то есть код страницы исполняется с полным доступом к Node.js.
|
|
1184
|
+
- `@electron/remote` инициализируется и включается для главного окна — renderer может
|
|
1185
|
+
напрямую обращаться к модулям main-процесса.
|
|
1186
|
+
- При `webSecurity: false` заголовки `Access-Control-Allow-Origin`, `Access-Control-Allow-Headers`
|
|
1187
|
+
и `X-Frame-Options` перезаписываются на `*` в `onBeforeSendHeaders` / `onHeadersReceived`.
|
|
1188
|
+
- Обработчик разрешений сессии автоматически одобряет только `media` (микрофон/камера),
|
|
1189
|
+
все остальные разрешения отклоняются.
|
|
1190
|
+
|
|
1191
|
+
Если приложение будет открывать внешний или недоверенный контент, безопасную схему нужно собрать так:
|
|
1192
|
+
|
|
1193
|
+
- `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true` (где возможно) — вместо `@electron/remote`.
|
|
1194
|
+
- Отдельный preload-скрипт с `contextBridge.exposeInMainWorld(...)` и узким списком методов;
|
|
1195
|
+
обмен с main-процессом через `ipcRenderer.invoke` / `ipcMain.handle` вместо `remote`.
|
|
1196
|
+
- `webviewTag` — только если он реально используется; иначе `false`.
|
|
1197
|
+
- `webSecurity` держать `true`, а внешние запросы проксировать через main-процесс,
|
|
1198
|
+
вместо перезаписи CORS-заголовков.
|
|
1199
|
+
- Ограничивать `loadURL`/`BrowserWindow`-навигацию через `will-navigate` и `setWindowOpenHandler`.
|
|
1200
|
+
|
|
1201
|
+
### В разработке
|
|
1202
|
+
|
|
1203
|
+
- Дочерние `BrowserWindow` — отдельные окна приложения пока не поддерживаются.
|