chuijs 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +597 -0
- package/README.md +43 -644
- package/framework/appLayout/default/chui_app_layout.js +602 -496
- package/framework/appLayout/default/global_style.css +288 -97
- package/framework/appLayout/default/main_theme_style.css +188 -101
- package/framework/components/chui_accordion/accordion.js +38 -35
- package/framework/components/chui_accordion/styles.css +13 -12
- package/framework/components/chui_action_sheet/action_sheet.js +93 -64
- package/framework/components/chui_action_sheet/styles.css +12 -5
- package/framework/components/chui_actions_bar/actions_bar.js +338 -0
- package/framework/components/chui_actions_bar/styles.css +139 -0
- package/framework/components/chui_affix/affix.js +16 -11
- package/framework/components/chui_affix/styles.css +5 -0
- package/framework/components/chui_alert/alert.js +34 -19
- package/framework/components/chui_alert/styles.css +7 -0
- package/framework/components/chui_avatar/avatar.js +36 -35
- package/framework/components/chui_avatar/styles.css +1 -1
- package/framework/components/chui_avatar_group/avatar_group.js +25 -23
- package/framework/components/chui_avatar_group/styles.css +1 -1
- package/framework/components/chui_backtop/backtop.js +60 -21
- package/framework/components/chui_backtop/styles.css +21 -0
- package/framework/components/chui_badge/badge.js +30 -28
- package/framework/components/chui_badge/styles.css +6 -6
- package/framework/components/chui_banner/banner.js +222 -0
- package/framework/components/chui_banner/styles.css +170 -0
- package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
- package/framework/components/chui_breadcrumb/styles.css +1 -1
- package/framework/components/chui_button/button.js +52 -36
- package/framework/components/chui_button/styles.css +7 -7
- package/framework/components/chui_calendar/calendar.js +18 -16
- package/framework/components/chui_card/card.js +52 -32
- package/framework/components/chui_card_scroll/card_scroll.js +169 -0
- package/framework/components/chui_card_scroll/styles.css +86 -0
- package/framework/components/chui_chat/chat.js +140 -79
- package/framework/components/chui_chat/styles.css +18 -7
- package/framework/components/chui_clipboard_history/clipboard_history.js +53 -39
- package/framework/components/chui_clipboard_history/styles.css +13 -9
- package/framework/components/chui_code/code.js +10 -10
- package/framework/components/chui_code/styles.css +4 -4
- package/framework/components/chui_code_editor/code_editor.js +73 -57
- package/framework/components/chui_code_editor/styles.css +6 -5
- package/framework/components/chui_command_palette/command_palette.js +87 -64
- package/framework/components/chui_command_palette/styles.css +5 -5
- package/framework/components/chui_console/console.js +13 -13
- package/framework/components/chui_console/styles.css +6 -3
- package/framework/components/chui_content_block/content_block.js +35 -21
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +171 -58
- package/framework/components/chui_context_menu/styles.css +82 -8
- package/framework/components/chui_custom_element/custom_element.js +25 -18
- package/framework/components/chui_data_grid/data_grid.js +633 -471
- package/framework/components/chui_data_grid/styles.css +24 -22
- package/framework/components/chui_descriptions/descriptions.js +34 -17
- package/framework/components/chui_descriptions/styles.css +1 -1
- package/framework/components/chui_details/details.js +42 -36
- package/framework/components/chui_details/styles.css +12 -11
- package/framework/components/chui_diff_viewer/diff_viewer.js +172 -147
- package/framework/components/chui_diff_viewer/styles.css +10 -10
- package/framework/components/chui_divider/divider.js +19 -17
- package/framework/components/chui_divider/styles.css +2 -2
- package/framework/components/chui_drawer/drawer.js +63 -38
- package/framework/components/chui_drawer/styles.css +18 -4
- package/framework/components/chui_drop_zone/drop_zone.js +37 -27
- package/framework/components/chui_drop_zone/styles.css +10 -6
- package/framework/components/chui_empty_state/empty_state.js +20 -15
- package/framework/components/chui_fieldset/fieldset.js +40 -23
- package/framework/components/chui_fieldset/styles.css +6 -6
- package/framework/components/chui_file_explorer/file_explorer.js +260 -189
- package/framework/components/chui_file_explorer/styles.css +8 -7
- package/framework/components/chui_form/form.js +40 -28
- package/framework/components/chui_form/styles.css +1 -1
- package/framework/components/chui_gantt/gantt.js +102 -78
- package/framework/components/chui_gantt/styles.css +5 -5
- package/framework/components/chui_graphs/graphs.js +376 -239
- package/framework/components/chui_graphs/styles.css +27 -6
- package/framework/components/chui_hero/hero.js +98 -37
- package/framework/components/chui_hero/styles.css +176 -7
- package/framework/components/chui_html_block/html_block.js +8 -8
- package/framework/components/chui_html_block/styles.css +3 -3
- package/framework/components/chui_hx/hx.js +3 -3
- package/framework/components/chui_hx/styles.css +13 -6
- package/framework/components/chui_icons/icons.js +2115 -2115
- package/framework/components/chui_icons/styles.css +4 -3
- package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
- package/framework/components/chui_inputs/chui_check_box/styles.css +30 -21
- package/framework/components/chui_inputs/chui_chips/chips.js +141 -70
- package/framework/components/chui_inputs/chui_chips/styles.css +32 -7
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +55 -38
- package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +127 -109
- package/framework/components/chui_inputs/chui_combo_box/styles.css +23 -21
- package/framework/components/chui_inputs/chui_date/date.js +161 -127
- package/framework/components/chui_inputs/chui_date/styles.css +20 -19
- package/framework/components/chui_inputs/chui_date_range/date_range.js +61 -50
- package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
- package/framework/components/chui_inputs/chui_datetime/datetime.js +47 -39
- package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
- package/framework/components/chui_inputs/chui_email/email.js +48 -36
- package/framework/components/chui_inputs/chui_email/styles.css +11 -11
- package/framework/components/chui_inputs/chui_file/file.js +71 -44
- package/framework/components/chui_inputs/chui_file/styles.css +23 -19
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +101 -76
- package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
- package/framework/components/chui_inputs/chui_masked/masked.js +83 -72
- package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
- package/framework/components/chui_inputs/chui_mention/mention.js +87 -40
- package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +242 -212
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +23 -15
- package/framework/components/chui_inputs/chui_number/number.js +84 -70
- package/framework/components/chui_inputs/chui_number/styles.css +13 -13
- package/framework/components/chui_inputs/chui_otp/otp.js +71 -62
- package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
- package/framework/components/chui_inputs/chui_password/password.js +72 -56
- package/framework/components/chui_inputs/chui_password/styles.css +15 -15
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
- package/framework/components/chui_inputs/chui_radio_button/styles.css +22 -22
- package/framework/components/chui_inputs/chui_rating/rating.js +54 -50
- package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
- package/framework/components/chui_inputs/chui_select_box/select_box.js +132 -108
- package/framework/components/chui_inputs/chui_select_box/styles.css +21 -21
- package/framework/components/chui_inputs/chui_slider/slider.js +41 -38
- package/framework/components/chui_inputs/chui_slider/styles.css +7 -5
- package/framework/components/chui_inputs/chui_text/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text/text.js +69 -52
- package/framework/components/chui_inputs/chui_text_area/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text_area/text_area.js +59 -36
- package/framework/components/chui_inputs/chui_time/styles.css +18 -20
- package/framework/components/chui_inputs/chui_time/time.js +71 -49
- package/framework/components/chui_inputs/chui_toggle/styles.css +13 -6
- package/framework/components/chui_inputs/chui_toggle/toggle.js +27 -27
- package/framework/components/chui_json_editor/json_editor.js +137 -127
- package/framework/components/chui_json_editor/styles.css +5 -5
- package/framework/components/chui_json_viewer/json_viewer.js +133 -114
- package/framework/components/chui_json_viewer/styles.css +9 -8
- package/framework/components/chui_kanban/kanban.js +78 -60
- package/framework/components/chui_kanban/styles.css +8 -4
- package/framework/components/chui_kbd/kbd.js +12 -10
- package/framework/components/chui_kbd/styles.css +1 -1
- package/framework/components/chui_label/label.js +31 -29
- package/framework/components/chui_label/styles.css +3 -3
- package/framework/components/chui_link/link.js +36 -29
- package/framework/components/chui_link/styles.css +11 -4
- package/framework/components/chui_list/list.js +42 -38
- package/framework/components/chui_list/styles.css +4 -3
- package/framework/components/chui_markdown_editor/markdown_editor.js +125 -109
- package/framework/components/chui_markdown_editor/styles.css +5 -5
- package/framework/components/chui_media/audio.js +457 -364
- package/framework/components/chui_media/audio_styles.css +74 -37
- package/framework/components/chui_media/image.js +99 -64
- package/framework/components/chui_media/image_compare.js +86 -55
- package/framework/components/chui_media/image_compare_styles.css +5 -5
- package/framework/components/chui_media/image_styles.css +4 -4
- package/framework/components/chui_media/video.js +264 -171
- package/framework/components/chui_media/video_styles.css +57 -24
- package/framework/components/chui_menu_bar/menu_bar.js +67 -65
- package/framework/components/chui_menu_bar/styles.css +14 -12
- package/framework/components/chui_message_bar/message_bar.js +551 -0
- package/framework/components/chui_message_bar/styles.css +248 -0
- package/framework/components/chui_modal/modal.js +81 -56
- package/framework/components/chui_modal/styles.css +3 -2
- package/framework/components/chui_notification/notification.js +107 -69
- package/framework/components/chui_notification/notification_download.js +28 -77
- package/framework/components/chui_notification/notification_download_card.js +115 -0
- package/framework/components/chui_notification/notification_download_progress.css +5 -4
- package/framework/components/chui_notification/notification_download_progress.js +73 -105
- package/framework/components/chui_notification/notification_update.js +44 -37
- package/framework/components/chui_notification/status_plate.css +3 -3
- package/framework/components/chui_notification/status_plate.js +2 -2
- package/framework/components/chui_notification/styles.css +6 -1
- package/framework/components/chui_notification/styles_download.css +2 -2
- package/framework/components/chui_notification/styles_update.css +3 -3
- package/framework/components/chui_org_chart/org_chart.js +101 -62
- package/framework/components/chui_org_chart/styles.css +2 -2
- package/framework/components/chui_pagination/pagination.js +65 -30
- package/framework/components/chui_pagination/styles.css +1 -1
- package/framework/components/chui_paragraph/paragraph.js +2 -2
- package/framework/components/chui_paragraph/styles.css +3 -3
- package/framework/components/chui_popover/popover.js +77 -50
- package/framework/components/chui_popover/styles.css +1 -1
- package/framework/components/chui_popups/popups.js +210 -181
- package/framework/components/chui_popups/styles.css +7 -7
- package/framework/components/chui_print_button/print_button.js +48 -33
- package/framework/components/chui_progress_bar/progress_bar.js +49 -19
- package/framework/components/chui_progress_bar/styles.css +6 -6
- package/framework/components/chui_progress_ring/progress_ring.js +32 -30
- package/framework/components/chui_progress_ring/styles.css +1 -1
- package/framework/components/chui_qrcode/qrcode.js +85 -41
- package/framework/components/chui_query_builder/query_builder.js +263 -170
- package/framework/components/chui_query_builder/styles.css +4 -4
- package/framework/components/chui_radio_group/radio_group.js +36 -26
- package/framework/components/chui_radio_group/styles.css +2 -2
- package/framework/components/chui_result/result.js +41 -31
- package/framework/components/chui_result/styles.css +3 -3
- package/framework/components/chui_scheduler/scheduler.js +126 -104
- package/framework/components/chui_scheduler/styles.css +11 -7
- package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
- package/framework/components/chui_screen_spinner/styles.css +49 -0
- package/framework/components/chui_segmented_control/segmented_control.js +90 -27
- package/framework/components/chui_segmented_control/styles.css +32 -6
- package/framework/components/chui_settings/settings.js +160 -87
- package/framework/components/chui_settings/styles.css +4 -4
- package/framework/components/chui_shortcut_help/shortcut_help.js +82 -61
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +132 -53
- package/framework/components/chui_sidebar_nav/styles.css +9 -3
- package/framework/components/chui_skeleton/skeleton.js +29 -24
- package/framework/components/chui_slideshow/slideshow.js +122 -103
- package/framework/components/chui_slideshow/styles.css +22 -16
- package/framework/components/chui_spinner/spinner.js +17 -17
- package/framework/components/chui_spinner/styles.css +0 -1
- package/framework/components/chui_split_button/split_button.js +53 -30
- package/framework/components/chui_split_button/styles.css +22 -6
- package/framework/components/chui_splitter/splitter.js +46 -39
- package/framework/components/chui_splitter/styles.css +2 -2
- package/framework/components/chui_statistic/statistic.js +111 -84
- package/framework/components/chui_status_bar/status_bar.js +11 -9
- package/framework/components/chui_status_bar/styles.css +2 -2
- package/framework/components/chui_stepper/stepper.js +230 -54
- package/framework/components/chui_stepper/styles.css +79 -14
- package/framework/components/chui_sticky_bar/sticky_bar.js +53 -17
- package/framework/components/chui_sticky_bar/styles.css +11 -7
- package/framework/components/chui_tabs/chui_tabs.js +100 -46
- package/framework/components/chui_tabs/styles.css +43 -15
- package/framework/components/chui_text_editor/styles.css +24 -23
- package/framework/components/chui_text_editor/text_editor.js +103 -79
- package/framework/components/chui_text_editor/text_editor_panel.js +428 -367
- package/framework/components/chui_theme_toggle/theme_toggle.js +46 -25
- package/framework/components/chui_timeline/styles.css +3 -1
- package/framework/components/chui_timeline/timeline.js +14 -12
- package/framework/components/chui_toast_queue/styles.css +11 -11
- package/framework/components/chui_toast_queue/toast_queue.js +82 -73
- package/framework/components/chui_toolbar/styles.css +2 -2
- package/framework/components/chui_toolbar/toolbar.js +20 -13
- package/framework/components/chui_tooltip/styles.css +2 -2
- package/framework/components/chui_tooltip/tooltip.js +40 -27
- package/framework/components/chui_tour/styles.css +12 -7
- package/framework/components/chui_tour/tour.js +132 -98
- package/framework/components/chui_transfer/styles.css +8 -7
- package/framework/components/chui_transfer/transfer.js +143 -123
- package/framework/components/chui_tree_table/styles.css +4 -4
- package/framework/components/chui_tree_table/tree_table.js +88 -74
- package/framework/components/chui_tree_view/styles.css +14 -10
- package/framework/components/chui_tree_view/tree_view.js +298 -253
- package/framework/components/chui_user_bar/styles.css +145 -0
- package/framework/components/chui_user_bar/user_bar.js +402 -0
- package/framework/components/chui_user_list_bar/styles.css +173 -0
- package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
- package/framework/components/chui_watermark/watermark.js +41 -33
- package/framework/components/chui_webview/styles.css +2 -2
- package/framework/components/chui_webview/webview.js +64 -52
- package/framework/components/chui_write_bar/styles.css +172 -0
- package/framework/components/chui_write_bar/write_bar.js +320 -0
- package/framework/main_window.js +1234 -0
- package/framework/modules/chui_a11y/chui_a11y.js +153 -123
- package/framework/modules/chui_animations/animations.js +105 -43
- package/framework/modules/chui_animations/motion.js +120 -0
- package/framework/modules/chui_animations/styles.css +37 -4
- package/framework/modules/chui_bridge/preload.js +36 -25
- package/framework/modules/chui_functions.js +69 -63
- package/framework/modules/chui_highlight/chui_highlight.js +25 -22
- package/framework/modules/chui_highlight/styles.css +13 -10
- package/framework/modules/chui_i18n/chui_i18n.js +12 -10
- 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 +65 -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 +12 -8
- package/framework/modules/chui_i18n/locales/validate.js +3 -3
- package/framework/modules/chui_json/chui_json.js +11 -11
- package/framework/modules/chui_layout/chui_layout.js +12 -10
- package/framework/modules/chui_logger/chui_logger.js +47 -28
- package/framework/modules/chui_page/page.js +39 -33
- package/framework/modules/chui_palette/palette.js +61 -0
- package/framework/modules/chui_qr/chui_qr.js +179 -88
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +79 -68
- package/framework/modules/chui_scrollbar/styles.css +4 -2
- package/framework/modules/chui_services/chui_channels.js +27 -13
- package/framework/modules/chui_services/chui_services.js +47 -39
- package/framework/modules/chui_services/desktop.js +108 -101
- package/framework/modules/chui_sortable/chui_sortable.js +33 -28
- package/framework/modules/chui_validate/chui_validate.js +10 -7
- package/framework/tray_bar/chui_menu_items.js +136 -46
- package/index.d.ts +5046 -0
- package/index.js +199 -1254
- package/package.json +81 -68
- package/framework/components/telegram_bot/chui_telegram_bot.js +0 -184
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
const {setStyles} = require("../../modules/chui_functions");
|
|
2
|
-
const {Validate} = require("../../modules/chui_validate/chui_validate");
|
|
3
|
-
const {SegmentedControl} = require("../chui_segmented_control/segmented_control");
|
|
4
|
-
const {Select} = require("../chui_inputs/chui_select_box/select_box");
|
|
5
|
-
const {TextInput} = require("../chui_inputs/chui_text/text");
|
|
6
|
-
const {NumberInput} = require("../chui_inputs/chui_number/number");
|
|
7
|
-
const {DateInput} = require("../chui_inputs/chui_date/date");
|
|
8
|
-
const {I18n} = require("../../modules/chui_i18n/chui_i18n");
|
|
9
|
-
const {Icon, Icons} = require("../chui_icons/icons");
|
|
1
|
+
const { setStyles } = require("../../modules/chui_functions");
|
|
2
|
+
const { Validate } = require("../../modules/chui_validate/chui_validate");
|
|
3
|
+
const { SegmentedControl } = require("../chui_segmented_control/segmented_control");
|
|
4
|
+
const { Select } = require("../chui_inputs/chui_select_box/select_box");
|
|
5
|
+
const { TextInput } = require("../chui_inputs/chui_text/text");
|
|
6
|
+
const { NumberInput } = require("../chui_inputs/chui_number/number");
|
|
7
|
+
const { DateInput } = require("../chui_inputs/chui_date/date");
|
|
8
|
+
const { I18n } = require("../../modules/chui_i18n/chui_i18n");
|
|
9
|
+
const { Icon, Icons } = require("../chui_icons/icons");
|
|
10
10
|
|
|
11
11
|
/** Соединение условий внутри группы */
|
|
12
|
-
const COMBINATOR = {AND: "and", OR: "or"};
|
|
12
|
+
const COMBINATOR = { AND: "and", OR: "or" };
|
|
13
13
|
/** Типы полей: от типа зависят и операторы, и вид поля значения */
|
|
14
|
-
const TYPE = {STRING: "string", NUMBER: "number", BOOLEAN: "boolean", DATE: "date", SELECT: "select"};
|
|
14
|
+
const TYPE = { STRING: "string", NUMBER: "number", BOOLEAN: "boolean", DATE: "date", SELECT: "select" };
|
|
15
15
|
const TYPES = [TYPE.STRING, TYPE.NUMBER, TYPE.BOOLEAN, TYPE.DATE, TYPE.SELECT];
|
|
16
16
|
/** Операторы сравнения */
|
|
17
17
|
const OPERATOR = {
|
|
@@ -26,7 +26,7 @@ const OPERATOR = {
|
|
|
26
26
|
LESS: "less",
|
|
27
27
|
LESS_OR_EQUAL: "less_or_equal",
|
|
28
28
|
IS_EMPTY: "is_empty",
|
|
29
|
-
IS_NOT_EMPTY: "is_not_empty"
|
|
29
|
+
IS_NOT_EMPTY: "is_not_empty",
|
|
30
30
|
};
|
|
31
31
|
/** Операторы словами — для подписей контролов и `toText()`: внутренние ключи словаря */
|
|
32
32
|
const OPERATOR_KEYS = {
|
|
@@ -41,23 +41,46 @@ const OPERATOR_KEYS = {
|
|
|
41
41
|
less: "queryBuilder.operator.less",
|
|
42
42
|
less_or_equal: "queryBuilder.operator.lessOrEqual",
|
|
43
43
|
is_empty: "queryBuilder.operator.isEmpty",
|
|
44
|
-
is_not_empty: "queryBuilder.operator.isNotEmpty"
|
|
44
|
+
is_not_empty: "queryBuilder.operator.isNotEmpty",
|
|
45
45
|
};
|
|
46
46
|
// Публичная таблица отдаёт уже переведённый текст: геттеры читают словарь в момент обращения
|
|
47
47
|
const OPERATOR_TITLES = {};
|
|
48
48
|
for (const key of Object.keys(OPERATOR_KEYS)) {
|
|
49
|
-
Object.defineProperty(OPERATOR_TITLES, key, {enumerable: true, get: () => I18n.t(OPERATOR_KEYS[key])});
|
|
49
|
+
Object.defineProperty(OPERATOR_TITLES, key, { enumerable: true, get: () => I18n.t(OPERATOR_KEYS[key]) });
|
|
50
50
|
}
|
|
51
51
|
/** Операторы по типу поля: «содержит» у числа и «больше» у логического смысла не имеют */
|
|
52
52
|
const OPERATORS_BY_TYPE = {
|
|
53
|
-
string: [
|
|
54
|
-
OPERATOR.
|
|
55
|
-
|
|
56
|
-
OPERATOR.
|
|
57
|
-
|
|
58
|
-
OPERATOR.
|
|
53
|
+
string: [
|
|
54
|
+
OPERATOR.EQUALS,
|
|
55
|
+
OPERATOR.NOT_EQUALS,
|
|
56
|
+
OPERATOR.CONTAINS,
|
|
57
|
+
OPERATOR.NOT_CONTAINS,
|
|
58
|
+
OPERATOR.STARTS_WITH,
|
|
59
|
+
OPERATOR.ENDS_WITH,
|
|
60
|
+
OPERATOR.IS_EMPTY,
|
|
61
|
+
OPERATOR.IS_NOT_EMPTY,
|
|
62
|
+
],
|
|
63
|
+
number: [
|
|
64
|
+
OPERATOR.EQUALS,
|
|
65
|
+
OPERATOR.NOT_EQUALS,
|
|
66
|
+
OPERATOR.GREATER,
|
|
67
|
+
OPERATOR.GREATER_OR_EQUAL,
|
|
68
|
+
OPERATOR.LESS,
|
|
69
|
+
OPERATOR.LESS_OR_EQUAL,
|
|
70
|
+
OPERATOR.IS_EMPTY,
|
|
71
|
+
OPERATOR.IS_NOT_EMPTY,
|
|
72
|
+
],
|
|
73
|
+
date: [
|
|
74
|
+
OPERATOR.EQUALS,
|
|
75
|
+
OPERATOR.GREATER,
|
|
76
|
+
OPERATOR.GREATER_OR_EQUAL,
|
|
77
|
+
OPERATOR.LESS,
|
|
78
|
+
OPERATOR.LESS_OR_EQUAL,
|
|
79
|
+
OPERATOR.IS_EMPTY,
|
|
80
|
+
OPERATOR.IS_NOT_EMPTY,
|
|
81
|
+
],
|
|
59
82
|
boolean: [OPERATOR.EQUALS, OPERATOR.NOT_EQUALS],
|
|
60
|
-
select: [OPERATOR.EQUALS, OPERATOR.NOT_EQUALS, OPERATOR.IS_EMPTY, OPERATOR.IS_NOT_EMPTY]
|
|
83
|
+
select: [OPERATOR.EQUALS, OPERATOR.NOT_EQUALS, OPERATOR.IS_EMPTY, OPERATOR.IS_NOT_EMPTY],
|
|
61
84
|
};
|
|
62
85
|
/** Операторы, которым значение не нужно: у них поле значения не рисуется */
|
|
63
86
|
const VALUELESS = new Set([OPERATOR.IS_EMPTY, OPERATOR.IS_NOT_EMPTY]);
|
|
@@ -128,47 +151,49 @@ class QueryBuilder {
|
|
|
128
151
|
#fields = [];
|
|
129
152
|
#value = undefined;
|
|
130
153
|
#listeners = [];
|
|
131
|
-
#
|
|
132
|
-
#
|
|
154
|
+
#onChange = undefined;
|
|
155
|
+
#maxDepth = QueryBuilder.MAX_DEPTH;
|
|
133
156
|
#disabled = false;
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
157
|
+
#errorShown = false;
|
|
158
|
+
#nodeCounter = 0;
|
|
159
|
+
#ruleCounter = 0;
|
|
137
160
|
// Переключатели «И/ИЛИ» живут по группе: при перерисовке пересобираются вместе с узлами
|
|
138
161
|
#combinators = new Map();
|
|
139
162
|
// Готовые поля условий: их нужно гасить, снимать у них слушатели окна и уметь закрыть
|
|
140
163
|
// раскрытый список при возврате фокуса после пересборки
|
|
141
164
|
#controls = [];
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
|
|
146
|
-
constructor(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
#titleText = undefined;
|
|
166
|
+
#labelText = undefined;
|
|
167
|
+
#emptyText = undefined;
|
|
168
|
+
|
|
169
|
+
constructor(
|
|
170
|
+
options = {
|
|
171
|
+
id: String(),
|
|
172
|
+
name: String(),
|
|
173
|
+
title: String(),
|
|
174
|
+
label: String(),
|
|
175
|
+
fields: Array(),
|
|
176
|
+
value: undefined,
|
|
177
|
+
maxDepth: Number(),
|
|
178
|
+
emptyText: String(),
|
|
179
|
+
required: Boolean(),
|
|
180
|
+
messages: Object(),
|
|
181
|
+
disabled: Boolean(),
|
|
182
|
+
changeListener: undefined,
|
|
183
|
+
onChange: undefined,
|
|
184
|
+
}
|
|
185
|
+
) {
|
|
161
186
|
setStyles(__dirname + "/styles.css", "chUiJS_QueryBuilder");
|
|
162
187
|
QueryBuilder.#counter += 1;
|
|
163
188
|
this.#id = options.id === undefined ? `chui_query_builder_${QueryBuilder.#counter}` : options.id;
|
|
164
189
|
this.#name = options.name;
|
|
165
190
|
this.#root.id = this.#id;
|
|
166
191
|
this.#root.setAttribute("role", "group");
|
|
167
|
-
this.#
|
|
168
|
-
this.#
|
|
169
|
-
if (options.emptyText !== undefined) this.#
|
|
170
|
-
else this.#
|
|
171
|
-
if (options.maxDepth !== undefined) this.#
|
|
192
|
+
this.#labelText = options.label;
|
|
193
|
+
this.#onChange = options.onChange;
|
|
194
|
+
if (options.emptyText !== undefined) this.#emptyText = String(options.emptyText);
|
|
195
|
+
else this.#emptyText = I18n.t("queryBuilder.empty");
|
|
196
|
+
if (options.maxDepth !== undefined) this.#maxDepth = Math.max(1, Number(options.maxDepth));
|
|
172
197
|
this.#title.className = "query_builder_title";
|
|
173
198
|
this.#title.id = `${this.#id}_title`;
|
|
174
199
|
this.#tree.className = "query_builder_tree";
|
|
@@ -182,19 +207,32 @@ class QueryBuilder {
|
|
|
182
207
|
this.#root.appendChild(this.#status);
|
|
183
208
|
if (options.title !== undefined) this.setTitle(options.title);
|
|
184
209
|
else this.#title.hidden = true;
|
|
185
|
-
this.setLabel(this.#
|
|
210
|
+
this.setLabel(this.#labelText === undefined ? I18n.t("queryBuilder.title") : this.#labelText);
|
|
186
211
|
this.setFields(options.fields === undefined ? [] : options.fields);
|
|
187
212
|
if (options.value === undefined) this.clear(true);
|
|
188
213
|
else this.setValue(options.value);
|
|
189
214
|
// Правила формы и поля читают пустой запрос как пустое значение: за него отвечает required
|
|
190
|
-
Validate.installField(
|
|
191
|
-
|
|
215
|
+
Validate.installField(
|
|
216
|
+
this,
|
|
217
|
+
options,
|
|
218
|
+
this.#root,
|
|
219
|
+
this.#error,
|
|
220
|
+
() => (this.isEmpty() ? "" : this.getText()),
|
|
221
|
+
this.#tree
|
|
222
|
+
);
|
|
223
|
+
const baseValidate = this.validate;
|
|
192
224
|
this.validate = () => {
|
|
193
|
-
const result =
|
|
225
|
+
const result = baseValidate();
|
|
194
226
|
if (result.valid === false) return result;
|
|
195
|
-
const empty = this.#rules().find(
|
|
227
|
+
const empty = this.#rules().find(
|
|
228
|
+
(rule) => this.#needsValue(rule.operator) && this.#isEmptyValue(rule.value)
|
|
229
|
+
);
|
|
196
230
|
if (empty === undefined) return result;
|
|
197
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
valid: false,
|
|
233
|
+
rule: "query",
|
|
234
|
+
message: I18n.t("queryBuilder.valueMissing", { index: this.#indexOfRule(empty.id) }),
|
|
235
|
+
};
|
|
198
236
|
};
|
|
199
237
|
this.setDisabled(options.disabled === true);
|
|
200
238
|
if (options.changeListener !== undefined) this.addChangeListener(options.changeListener);
|
|
@@ -206,11 +244,11 @@ class QueryBuilder {
|
|
|
206
244
|
}
|
|
207
245
|
|
|
208
246
|
getTitle() {
|
|
209
|
-
return this.#
|
|
247
|
+
return this.#titleText;
|
|
210
248
|
}
|
|
211
249
|
|
|
212
250
|
getLabel() {
|
|
213
|
-
return this.#
|
|
251
|
+
return this.#labelText;
|
|
214
252
|
}
|
|
215
253
|
|
|
216
254
|
/** Имя запроса для формы: с ним `Form.getValues()`/`setValues()` знают, куда класть дерево */
|
|
@@ -220,7 +258,9 @@ class QueryBuilder {
|
|
|
220
258
|
|
|
221
259
|
/** Описания полей: копии того, что задано в `setFields()` */
|
|
222
260
|
getFields() {
|
|
223
|
-
return this.#fields.map((field) =>
|
|
261
|
+
return this.#fields.map((field) =>
|
|
262
|
+
Object.assign({}, field, { options: field.options.map((option) => Object.assign({}, option)) })
|
|
263
|
+
);
|
|
224
264
|
}
|
|
225
265
|
|
|
226
266
|
/** Дерево запроса: `{id, combinator, children}`, у условий есть `id` */
|
|
@@ -235,7 +275,7 @@ class QueryBuilder {
|
|
|
235
275
|
|
|
236
276
|
/** Плоский список условий `[{field, operator, value}]` — в порядке обхода дерева */
|
|
237
277
|
getConditions() {
|
|
238
|
-
return this.#rules().map((rule) => ({field: rule.field, operator: rule.operator, value: rule.value}));
|
|
278
|
+
return this.#rules().map((rule) => ({ field: rule.field, operator: rule.operator, value: rule.value }));
|
|
239
279
|
}
|
|
240
280
|
|
|
241
281
|
/** Идентификаторы условий в порядке обхода — для `removeRule()` и `focus()` */
|
|
@@ -264,7 +304,7 @@ class QueryBuilder {
|
|
|
264
304
|
}
|
|
265
305
|
|
|
266
306
|
getMaxDepth() {
|
|
267
|
-
return this.#
|
|
307
|
+
return this.#maxDepth;
|
|
268
308
|
}
|
|
269
309
|
|
|
270
310
|
isDisabled() {
|
|
@@ -272,7 +312,7 @@ class QueryBuilder {
|
|
|
272
312
|
}
|
|
273
313
|
|
|
274
314
|
getEmptyText() {
|
|
275
|
-
return this.#
|
|
315
|
+
return this.#emptyText;
|
|
276
316
|
}
|
|
277
317
|
|
|
278
318
|
/** Запрос словами: «Статус равно «Оплачен» И Сумма больше 1000» */
|
|
@@ -291,7 +331,7 @@ class QueryBuilder {
|
|
|
291
331
|
|
|
292
332
|
/** Подпись над запросом; `undefined` её убирает (имя тогда даёт `label`) */
|
|
293
333
|
setTitle(text = undefined) {
|
|
294
|
-
this.#
|
|
334
|
+
this.#titleText = text;
|
|
295
335
|
if (text === undefined || text === "") {
|
|
296
336
|
this.#title.hidden = true;
|
|
297
337
|
this.#title.innerText = "";
|
|
@@ -306,7 +346,7 @@ class QueryBuilder {
|
|
|
306
346
|
|
|
307
347
|
/** Имя запроса, когда подписи в разметке нет */
|
|
308
348
|
setLabel(text = undefined) {
|
|
309
|
-
this.#
|
|
349
|
+
this.#labelText = text;
|
|
310
350
|
this.#syncLabel();
|
|
311
351
|
return this;
|
|
312
352
|
}
|
|
@@ -328,10 +368,12 @@ class QueryBuilder {
|
|
|
328
368
|
key: String(field.key),
|
|
329
369
|
label: field.label === undefined ? String(field.key) : String(field.label),
|
|
330
370
|
type: TYPES.includes(type) ? type : TYPE.STRING,
|
|
331
|
-
options: Array.isArray(field.options)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
371
|
+
options: Array.isArray(field.options)
|
|
372
|
+
? field.options.map((option) => ({
|
|
373
|
+
value: String(option.value),
|
|
374
|
+
title: option.title === undefined ? String(option.value) : String(option.title),
|
|
375
|
+
}))
|
|
376
|
+
: [],
|
|
335
377
|
};
|
|
336
378
|
});
|
|
337
379
|
for (const rule of this.#rules()) {
|
|
@@ -347,7 +389,7 @@ class QueryBuilder {
|
|
|
347
389
|
|
|
348
390
|
/** Дерево запроса; `silent` (как у полей формы) — молча, без уведомления слушателей */
|
|
349
391
|
setValue(value = Object(), silent = false) {
|
|
350
|
-
this.#
|
|
392
|
+
this.#nodeCounter = 0;
|
|
351
393
|
this.#value = this.#toGroup(value, 1);
|
|
352
394
|
this.#render();
|
|
353
395
|
if (silent === false) this.#notify();
|
|
@@ -355,13 +397,13 @@ class QueryBuilder {
|
|
|
355
397
|
}
|
|
356
398
|
|
|
357
399
|
setMaxDepth(depth = Number()) {
|
|
358
|
-
this.#
|
|
400
|
+
this.#maxDepth = Math.max(1, Number(depth));
|
|
359
401
|
this.#render();
|
|
360
402
|
return this;
|
|
361
403
|
}
|
|
362
404
|
|
|
363
405
|
setEmptyText(text = String()) {
|
|
364
|
-
this.#
|
|
406
|
+
this.#emptyText = text === undefined ? "" : String(text);
|
|
365
407
|
this.#render();
|
|
366
408
|
return this;
|
|
367
409
|
}
|
|
@@ -374,7 +416,9 @@ class QueryBuilder {
|
|
|
374
416
|
if (group.combinator === next) return this;
|
|
375
417
|
group.combinator = next;
|
|
376
418
|
this.#render();
|
|
377
|
-
this.#announce(
|
|
419
|
+
this.#announce(
|
|
420
|
+
I18n.t("queryBuilder.connectionChanged", { combinator: I18n.t(QueryBuilder.#combinatorTitle(next)) })
|
|
421
|
+
);
|
|
378
422
|
this.#notify();
|
|
379
423
|
return this;
|
|
380
424
|
}
|
|
@@ -391,7 +435,7 @@ class QueryBuilder {
|
|
|
391
435
|
const rule = this.#emptyRule(field);
|
|
392
436
|
group.children.push(rule);
|
|
393
437
|
this.#render();
|
|
394
|
-
this.#announce(I18n.t("queryBuilder.ruleAdded", {index: this.#indexOfRule(rule.id)}));
|
|
438
|
+
this.#announce(I18n.t("queryBuilder.ruleAdded", { index: this.#indexOfRule(rule.id) }));
|
|
395
439
|
this.#refreshErrors();
|
|
396
440
|
this.#notify();
|
|
397
441
|
return rule.id;
|
|
@@ -405,18 +449,20 @@ class QueryBuilder {
|
|
|
405
449
|
addGroup(groupId = undefined) {
|
|
406
450
|
const group = groupId === undefined ? this.#value : this.#group(groupId);
|
|
407
451
|
if (group === undefined) return undefined;
|
|
408
|
-
if (this.#depthOf(group) >= this.#
|
|
452
|
+
if (this.#depthOf(group) >= this.#maxDepth) return undefined;
|
|
409
453
|
const field = this.#fields[0];
|
|
410
454
|
if (field === undefined) return undefined;
|
|
411
455
|
const nested = {
|
|
412
456
|
kind: "group",
|
|
413
457
|
id: this.#nextId("group"),
|
|
414
458
|
combinator: COMBINATOR.AND,
|
|
415
|
-
children: [this.#emptyRule(field)]
|
|
459
|
+
children: [this.#emptyRule(field)],
|
|
416
460
|
};
|
|
417
461
|
group.children.push(nested);
|
|
418
462
|
this.#render();
|
|
419
|
-
this.#announce(
|
|
463
|
+
this.#announce(
|
|
464
|
+
I18n.t("queryBuilder.groupAdded", { index: this.#groups().findIndex((item) => item.id === nested.id) + 1 })
|
|
465
|
+
);
|
|
420
466
|
this.#refreshErrors();
|
|
421
467
|
this.#notify();
|
|
422
468
|
return nested.id;
|
|
@@ -428,7 +474,7 @@ class QueryBuilder {
|
|
|
428
474
|
if (index === 0) return this;
|
|
429
475
|
this.#removeNode(id);
|
|
430
476
|
this.#render();
|
|
431
|
-
this.#announce(I18n.t("queryBuilder.ruleRemoved", {index: index}));
|
|
477
|
+
this.#announce(I18n.t("queryBuilder.ruleRemoved", { index: index }));
|
|
432
478
|
this.#refreshErrors();
|
|
433
479
|
this.#notify();
|
|
434
480
|
return this;
|
|
@@ -441,7 +487,7 @@ class QueryBuilder {
|
|
|
441
487
|
if (index === -1) return this;
|
|
442
488
|
this.#removeNode(id);
|
|
443
489
|
this.#render();
|
|
444
|
-
this.#announce(I18n.t("queryBuilder.groupRemoved", {index: index + 1}));
|
|
490
|
+
this.#announce(I18n.t("queryBuilder.groupRemoved", { index: index + 1 }));
|
|
445
491
|
this.#refreshErrors();
|
|
446
492
|
this.#notify();
|
|
447
493
|
return this;
|
|
@@ -449,7 +495,7 @@ class QueryBuilder {
|
|
|
449
495
|
|
|
450
496
|
/** Убрать все условия и группы: остаётся пустой корень */
|
|
451
497
|
clear(silent = false) {
|
|
452
|
-
this.#value = {kind: "group", id: this.#nextId("group"), combinator: COMBINATOR.AND, children: []};
|
|
498
|
+
this.#value = { kind: "group", id: this.#nextId("group"), combinator: COMBINATOR.AND, children: [] };
|
|
453
499
|
this.#render();
|
|
454
500
|
if (silent === false) this.#notify();
|
|
455
501
|
return this;
|
|
@@ -472,9 +518,10 @@ class QueryBuilder {
|
|
|
472
518
|
focus(id = undefined) {
|
|
473
519
|
const rules = this.#rules();
|
|
474
520
|
const rule = id === undefined ? rules[0] : rules.find((item) => item.id === id);
|
|
475
|
-
const selector =
|
|
476
|
-
|
|
477
|
-
|
|
521
|
+
const selector =
|
|
522
|
+
rule === undefined
|
|
523
|
+
? "query_builder_rule .query_builder_field input"
|
|
524
|
+
: `query_builder_rule[data-id="${CSS.escape(rule.id)}"] .query_builder_field input`;
|
|
478
525
|
const node = this.#tree.querySelector(selector);
|
|
479
526
|
if (node !== null) node.focus();
|
|
480
527
|
return this;
|
|
@@ -484,12 +531,12 @@ class QueryBuilder {
|
|
|
484
531
|
check() {
|
|
485
532
|
const result = this.validate();
|
|
486
533
|
if (result.valid) {
|
|
487
|
-
this.#
|
|
534
|
+
this.#errorShown = false;
|
|
488
535
|
this.clearError();
|
|
489
536
|
this.#announce(this.isEmpty() ? I18n.t("queryBuilder.empty") : I18n.t("queryBuilder.valid"));
|
|
490
537
|
return this;
|
|
491
538
|
}
|
|
492
|
-
this.#
|
|
539
|
+
this.#errorShown = true;
|
|
493
540
|
this.setError(result.message);
|
|
494
541
|
this.#syncErrors();
|
|
495
542
|
this.#announce(result.message);
|
|
@@ -515,8 +562,8 @@ class QueryBuilder {
|
|
|
515
562
|
|
|
516
563
|
// === ВНУТРЕННЕЕ ===
|
|
517
564
|
#nextId(prefix = String()) {
|
|
518
|
-
this.#
|
|
519
|
-
return `${this.#id}_${prefix}_${this.#
|
|
565
|
+
this.#nodeCounter += 1;
|
|
566
|
+
return `${this.#id}_${prefix}_${this.#nodeCounter}`;
|
|
520
567
|
}
|
|
521
568
|
|
|
522
569
|
#emptyRule(field = Object()) {
|
|
@@ -525,7 +572,7 @@ class QueryBuilder {
|
|
|
525
572
|
id: this.#nextId("rule"),
|
|
526
573
|
field: field.key,
|
|
527
574
|
operator: OPERATORS_BY_TYPE[field.type][0],
|
|
528
|
-
value: undefined
|
|
575
|
+
value: undefined,
|
|
529
576
|
};
|
|
530
577
|
}
|
|
531
578
|
|
|
@@ -534,7 +581,7 @@ class QueryBuilder {
|
|
|
534
581
|
const found = this.#fields.find((field) => field.key === key);
|
|
535
582
|
if (found !== undefined) return found;
|
|
536
583
|
return this.#fields[0] === undefined
|
|
537
|
-
? {key: String(key), label: String(key), type: TYPE.STRING, options: []}
|
|
584
|
+
? { key: String(key), label: String(key), type: TYPE.STRING, options: [] }
|
|
538
585
|
: this.#fields[0];
|
|
539
586
|
}
|
|
540
587
|
|
|
@@ -545,13 +592,13 @@ class QueryBuilder {
|
|
|
545
592
|
kind: "group",
|
|
546
593
|
id: typeof source.id === "string" ? source.id : this.#nextId("group"),
|
|
547
594
|
combinator: source.combinator === COMBINATOR.OR ? COMBINATOR.OR : COMBINATOR.AND,
|
|
548
|
-
children: []
|
|
595
|
+
children: [],
|
|
549
596
|
};
|
|
550
597
|
const children = Array.isArray(source.children) ? source.children : [];
|
|
551
598
|
for (const child of children) {
|
|
552
599
|
if (child === undefined || child === null || typeof child !== "object") continue;
|
|
553
600
|
if (child.children !== undefined || child.combinator !== undefined) {
|
|
554
|
-
if (depth >= this.#
|
|
601
|
+
if (depth >= this.#maxDepth) continue;
|
|
555
602
|
group.children.push(this.#toGroup(child, depth + 1));
|
|
556
603
|
continue;
|
|
557
604
|
}
|
|
@@ -563,7 +610,7 @@ class QueryBuilder {
|
|
|
563
610
|
id: typeof child.id === "string" ? child.id : this.#nextId("rule"),
|
|
564
611
|
field: field.key,
|
|
565
612
|
operator: operators.includes(child.operator) ? child.operator : operators[0],
|
|
566
|
-
value: child.value
|
|
613
|
+
value: child.value,
|
|
567
614
|
});
|
|
568
615
|
}
|
|
569
616
|
return group;
|
|
@@ -573,9 +620,11 @@ class QueryBuilder {
|
|
|
573
620
|
return {
|
|
574
621
|
id: group.id,
|
|
575
622
|
combinator: group.combinator,
|
|
576
|
-
children: group.children.map((child) =>
|
|
577
|
-
|
|
578
|
-
|
|
623
|
+
children: group.children.map((child) =>
|
|
624
|
+
child.kind === "group"
|
|
625
|
+
? QueryBuilder.#describeGroup(child)
|
|
626
|
+
: { id: child.id, field: child.field, operator: child.operator, value: child.value }
|
|
627
|
+
),
|
|
579
628
|
};
|
|
580
629
|
}
|
|
581
630
|
|
|
@@ -630,7 +679,7 @@ class QueryBuilder {
|
|
|
630
679
|
}
|
|
631
680
|
return undefined;
|
|
632
681
|
};
|
|
633
|
-
return this.#value === undefined ? 1 : walk(this.#value, 1) ?? 1;
|
|
682
|
+
return this.#value === undefined ? 1 : (walk(this.#value, 1) ?? 1);
|
|
634
683
|
}
|
|
635
684
|
|
|
636
685
|
/** Номер условия в порядке обхода; 0 — условия с таким `id` нет */
|
|
@@ -653,27 +702,28 @@ class QueryBuilder {
|
|
|
653
702
|
let mark = undefined;
|
|
654
703
|
if (active !== null && active !== undefined && this.#tree.contains(active)) {
|
|
655
704
|
const owner = active.closest("query_builder_rule, query_builder_group");
|
|
656
|
-
if (owner !== null)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
705
|
+
if (owner !== null)
|
|
706
|
+
mark = {
|
|
707
|
+
owner: owner.dataset.id,
|
|
708
|
+
control: QueryBuilder.#controlMark(active),
|
|
709
|
+
// Раскрытый список должен остаться раскрытым, закрытый — закрытым
|
|
710
|
+
open: active.getAttribute("aria-expanded") === "true",
|
|
711
|
+
};
|
|
662
712
|
}
|
|
663
713
|
this.#destroyControls();
|
|
664
714
|
this.#tree.innerHTML = "";
|
|
665
715
|
this.#combinators.clear();
|
|
666
|
-
this.#
|
|
716
|
+
this.#ruleCounter = 0;
|
|
667
717
|
if (this.#value === undefined || this.#fields.length === 0) {
|
|
668
718
|
this.#tree.hidden = true;
|
|
669
719
|
this.#empty.hidden = false;
|
|
670
|
-
this.#empty.innerText = this.#fields.length === 0 ? I18n.t("queryBuilder.noFields") : this.#
|
|
720
|
+
this.#empty.innerText = this.#fields.length === 0 ? I18n.t("queryBuilder.noFields") : this.#emptyText;
|
|
671
721
|
return this;
|
|
672
722
|
}
|
|
673
723
|
this.#tree.appendChild(this.#groupNode(this.#value, 0));
|
|
674
724
|
this.#tree.hidden = false;
|
|
675
725
|
this.#empty.hidden = true;
|
|
676
|
-
this.#empty.innerText = this.#
|
|
726
|
+
this.#empty.innerText = this.#emptyText;
|
|
677
727
|
this.#syncErrors();
|
|
678
728
|
if (mark !== undefined) {
|
|
679
729
|
const node = this.#tree.querySelector(`[data-id="${CSS.escape(mark.owner)}"] ${mark.control}`);
|
|
@@ -705,13 +755,14 @@ class QueryBuilder {
|
|
|
705
755
|
* (три `Select` в строке), поэтому контрол ищется вместе с обёрткой условия.
|
|
706
756
|
*/
|
|
707
757
|
static #controlMark(node = Object()) {
|
|
708
|
-
if (node.tagName === "SEGMENTED_OPTION")
|
|
758
|
+
if (node.tagName === "SEGMENTED_OPTION")
|
|
759
|
+
return `segmented_option[data-value="${CSS.escape(node.dataset.value)}"]`;
|
|
709
760
|
const list = [...node.classList];
|
|
710
761
|
const own = list.length > 0 ? `.${CSS.escape(list[0])}` : node.tagName.toLowerCase();
|
|
711
762
|
const holder = node.closest(".query_builder_field, .query_builder_operator, .query_builder_value");
|
|
712
763
|
if (holder === null) return own;
|
|
713
|
-
const
|
|
714
|
-
return
|
|
764
|
+
const holderClass = [...holder.classList].find((name) => name.startsWith("query_builder_"));
|
|
765
|
+
return holderClass === undefined ? own : `.${CSS.escape(holderClass)} ${own}`;
|
|
715
766
|
}
|
|
716
767
|
|
|
717
768
|
#groupNode(group = Object(), depth = Number()) {
|
|
@@ -720,10 +771,16 @@ class QueryBuilder {
|
|
|
720
771
|
node.dataset.id = group.id;
|
|
721
772
|
node.style.setProperty("--query_builder_depth", String(depth));
|
|
722
773
|
node.setAttribute("role", "group");
|
|
723
|
-
node.setAttribute(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
774
|
+
node.setAttribute(
|
|
775
|
+
"aria-label",
|
|
776
|
+
I18n.t("queryBuilder.groupAria", {
|
|
777
|
+
name:
|
|
778
|
+
depth === 0
|
|
779
|
+
? I18n.t("queryBuilder.conditions")
|
|
780
|
+
: I18n.t("queryBuilder.nestedGroup", { depth: depth }),
|
|
781
|
+
combinator: I18n.t(QueryBuilder.#combinatorTitle(group.combinator)),
|
|
782
|
+
})
|
|
783
|
+
);
|
|
727
784
|
node.appendChild(this.#groupHead(group, depth));
|
|
728
785
|
node.appendChild(this.#groupChildren(group, depth));
|
|
729
786
|
node.appendChild(this.#groupFoot(group, depth));
|
|
@@ -735,14 +792,17 @@ class QueryBuilder {
|
|
|
735
792
|
head.className = "query_builder_group_head";
|
|
736
793
|
const combinator = new SegmentedControl({
|
|
737
794
|
id: `${group.id}_combinator`,
|
|
738
|
-
label:
|
|
795
|
+
label:
|
|
796
|
+
depth === 0
|
|
797
|
+
? I18n.t("queryBuilder.connection")
|
|
798
|
+
: I18n.t("queryBuilder.connectionOfGroup", { depth: depth }),
|
|
739
799
|
size: SegmentedControl.SIZE.SMALL,
|
|
740
800
|
options: [
|
|
741
|
-
{value: COMBINATOR.AND, title: I18n.t("queryBuilder.and")},
|
|
742
|
-
{value: COMBINATOR.OR, title: I18n.t("queryBuilder.or")}
|
|
801
|
+
{ value: COMBINATOR.AND, title: I18n.t("queryBuilder.and") },
|
|
802
|
+
{ value: COMBINATOR.OR, title: I18n.t("queryBuilder.or") },
|
|
743
803
|
],
|
|
744
804
|
value: group.combinator,
|
|
745
|
-
onChange: (value) => this.setCombinator(group.id, value)
|
|
805
|
+
onChange: (value) => this.setCombinator(group.id, value),
|
|
746
806
|
});
|
|
747
807
|
combinator.setStretch(false);
|
|
748
808
|
this.#combinators.set(group.id, combinator);
|
|
@@ -751,13 +811,13 @@ class QueryBuilder {
|
|
|
751
811
|
const summary = document.createElement("query_builder_summary");
|
|
752
812
|
summary.className = "query_builder_summary";
|
|
753
813
|
summary.setAttribute("aria-hidden", "true");
|
|
754
|
-
summary.innerText = I18n.t("queryBuilder.rulesCount", {count: count});
|
|
814
|
+
summary.innerText = I18n.t("queryBuilder.rulesCount", { count: count });
|
|
755
815
|
head.appendChild(summary);
|
|
756
816
|
if (depth > 0) {
|
|
757
817
|
const remove = document.createElement("button");
|
|
758
818
|
remove.type = "button";
|
|
759
819
|
remove.className = "query_builder_remove query_builder_remove_group";
|
|
760
|
-
remove.setAttribute("aria-label", I18n.t("queryBuilder.removeGroup", {depth: depth}));
|
|
820
|
+
remove.setAttribute("aria-label", I18n.t("queryBuilder.removeGroup", { depth: depth }));
|
|
761
821
|
const glyph = new Icon(Icons.CONTENT.REMOVE, "18px").set();
|
|
762
822
|
glyph.setAttribute("aria-hidden", "true");
|
|
763
823
|
remove.appendChild(glyph);
|
|
@@ -776,7 +836,7 @@ class QueryBuilder {
|
|
|
776
836
|
if (group.children.length === 0) {
|
|
777
837
|
const note = document.createElement("query_builder_group_empty");
|
|
778
838
|
note.className = "query_builder_group_empty";
|
|
779
|
-
note.innerText = this.#
|
|
839
|
+
note.innerText = this.#emptyText;
|
|
780
840
|
list.appendChild(note);
|
|
781
841
|
return list;
|
|
782
842
|
}
|
|
@@ -789,17 +849,27 @@ class QueryBuilder {
|
|
|
789
849
|
#groupFoot(group = Object(), depth = Number()) {
|
|
790
850
|
const foot = document.createElement("query_builder_group_foot");
|
|
791
851
|
foot.className = "query_builder_group_foot";
|
|
792
|
-
foot.appendChild(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
852
|
+
foot.appendChild(
|
|
853
|
+
this.#addButton({
|
|
854
|
+
title: I18n.t("queryBuilder.ruleShort"),
|
|
855
|
+
label:
|
|
856
|
+
depth === 0
|
|
857
|
+
? I18n.t("queryBuilder.addRule")
|
|
858
|
+
: I18n.t("queryBuilder.addRuleInGroup", { depth: depth }),
|
|
859
|
+
onClick: () => this.addRule(group.id),
|
|
860
|
+
})
|
|
861
|
+
);
|
|
862
|
+
if (this.#depthOf(group) < this.#maxDepth) {
|
|
863
|
+
foot.appendChild(
|
|
864
|
+
this.#addButton({
|
|
865
|
+
title: I18n.t("queryBuilder.groupShort"),
|
|
866
|
+
label:
|
|
867
|
+
depth === 0
|
|
868
|
+
? I18n.t("queryBuilder.addGroup")
|
|
869
|
+
: I18n.t("queryBuilder.addGroupInGroup", { depth: depth }),
|
|
870
|
+
onClick: () => this.addGroup(group.id),
|
|
871
|
+
})
|
|
872
|
+
);
|
|
803
873
|
}
|
|
804
874
|
return foot;
|
|
805
875
|
}
|
|
@@ -851,7 +921,7 @@ class QueryBuilder {
|
|
|
851
921
|
|
|
852
922
|
/** Запомнить готовое поле вместе с его узлом: по узлу его найдут после пересборки */
|
|
853
923
|
#track(component = Object(), node = new HTMLElement()) {
|
|
854
|
-
this.#controls.push({component: component, node: node});
|
|
924
|
+
this.#controls.push({ component: component, node: node });
|
|
855
925
|
return node;
|
|
856
926
|
}
|
|
857
927
|
|
|
@@ -864,33 +934,40 @@ class QueryBuilder {
|
|
|
864
934
|
}
|
|
865
935
|
|
|
866
936
|
#ruleNode(rule = Object()) {
|
|
867
|
-
this.#
|
|
868
|
-
const index = this.#
|
|
937
|
+
this.#ruleCounter += 1;
|
|
938
|
+
const index = this.#ruleCounter;
|
|
869
939
|
const field = this.#fieldOf(rule.field);
|
|
870
940
|
const node = document.createElement("query_builder_rule");
|
|
871
941
|
node.className = "query_builder_rule";
|
|
872
942
|
node.dataset.id = rule.id;
|
|
873
943
|
node.setAttribute("role", "group");
|
|
874
|
-
node.setAttribute("aria-label", I18n.t("queryBuilder.ruleLabel", {index: index}));
|
|
875
|
-
node.appendChild(
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
944
|
+
node.setAttribute("aria-label", I18n.t("queryBuilder.ruleLabel", { index: index }));
|
|
945
|
+
node.appendChild(
|
|
946
|
+
this.#selectControl({
|
|
947
|
+
className: "query_builder_field",
|
|
948
|
+
label: I18n.t("queryBuilder.fieldOfRule", { index: index }),
|
|
949
|
+
options: this.#fields.map((item) => ({ value: item.key, title: item.label })),
|
|
950
|
+
value: field.key,
|
|
951
|
+
onChange: (value) => this.#changeField(rule.id, value),
|
|
952
|
+
})
|
|
953
|
+
);
|
|
954
|
+
node.appendChild(
|
|
955
|
+
this.#selectControl({
|
|
956
|
+
className: "query_builder_operator",
|
|
957
|
+
label: I18n.t("queryBuilder.operatorOfRule", { index: index }),
|
|
958
|
+
options: OPERATORS_BY_TYPE[field.type].map((operator) => ({
|
|
959
|
+
value: operator,
|
|
960
|
+
title: I18n.t(OPERATOR_KEYS[operator]),
|
|
961
|
+
})),
|
|
962
|
+
value: rule.operator,
|
|
963
|
+
onChange: (value) => this.#changeOperator(rule.id, value),
|
|
964
|
+
})
|
|
965
|
+
);
|
|
889
966
|
if (this.#needsValue(rule.operator)) node.appendChild(this.#valueControl(rule, field, index));
|
|
890
967
|
const remove = document.createElement("button");
|
|
891
968
|
remove.type = "button";
|
|
892
969
|
remove.className = "query_builder_remove query_builder_remove_rule";
|
|
893
|
-
remove.setAttribute("aria-label", I18n.t("queryBuilder.removeRule", {index: index}));
|
|
970
|
+
remove.setAttribute("aria-label", I18n.t("queryBuilder.removeRule", { index: index }));
|
|
894
971
|
const glyph = new Icon(Icons.CONTENT.REMOVE, "18px").set();
|
|
895
972
|
glyph.setAttribute("aria-hidden", "true");
|
|
896
973
|
remove.appendChild(glyph);
|
|
@@ -907,19 +984,23 @@ class QueryBuilder {
|
|
|
907
984
|
#valueControl(rule = Object(), field = Object(), index = Number()) {
|
|
908
985
|
const unset = rule.value === undefined || rule.value === null;
|
|
909
986
|
if (field.type === TYPE.BOOLEAN || field.type === TYPE.SELECT) {
|
|
910
|
-
const options =
|
|
911
|
-
|
|
912
|
-
|
|
987
|
+
const options =
|
|
988
|
+
field.type === TYPE.BOOLEAN
|
|
989
|
+
? [
|
|
990
|
+
{ value: "true", title: I18n.t("queryBuilder.yes") },
|
|
991
|
+
{ value: "false", title: I18n.t("queryBuilder.no") },
|
|
992
|
+
]
|
|
993
|
+
: field.options;
|
|
913
994
|
// Пустой вариант показывает «значение не выбрано» честно: модель и вид не расходятся
|
|
914
995
|
return this.#selectControl({
|
|
915
996
|
className: "query_builder_value",
|
|
916
|
-
label: I18n.t("queryBuilder.valueOfRule", {index: index}),
|
|
917
|
-
options: [{value: "", title: I18n.t(UNSET_TITLE)}].concat(options),
|
|
997
|
+
label: I18n.t("queryBuilder.valueOfRule", { index: index }),
|
|
998
|
+
options: [{ value: "", title: I18n.t(UNSET_TITLE) }].concat(options),
|
|
918
999
|
value: unset ? "" : String(rule.value),
|
|
919
|
-
onChange: (value) => this.#changeValue(rule.id, value)
|
|
1000
|
+
onChange: (value) => this.#changeValue(rule.id, value),
|
|
920
1001
|
});
|
|
921
1002
|
}
|
|
922
|
-
const label = I18n.t("queryBuilder.valueOfRule", {index: index});
|
|
1003
|
+
const label = I18n.t("queryBuilder.valueOfRule", { index: index });
|
|
923
1004
|
// У числа значение живёт по подтверждению: недонабранное «1e» или «-» в модель не попадает
|
|
924
1005
|
if (field.type === TYPE.NUMBER) {
|
|
925
1006
|
const number = new NumberInput({});
|
|
@@ -940,7 +1021,7 @@ class QueryBuilder {
|
|
|
940
1021
|
return this.#bindValueControl(this.#track(date, date.set()), "query_builder_value", label);
|
|
941
1022
|
}
|
|
942
1023
|
// Строка: значение живёт по вводу, поэтому слушатель на input, а не на change
|
|
943
|
-
const text = new TextInput({placeholder: I18n.t("queryBuilder.value")});
|
|
1024
|
+
const text = new TextInput({ placeholder: I18n.t("queryBuilder.value") });
|
|
944
1025
|
text.setValue(unset ? "" : String(rule.value));
|
|
945
1026
|
text.addInputListener(() => this.#changeValue(rule.id, text.getValue()));
|
|
946
1027
|
return this.#bindValueControl(this.#track(text, text.set()), "query_builder_value", label);
|
|
@@ -964,7 +1045,7 @@ class QueryBuilder {
|
|
|
964
1045
|
}
|
|
965
1046
|
rule.value = undefined;
|
|
966
1047
|
this.#render();
|
|
967
|
-
this.#announce(I18n.t("queryBuilder.fieldChanged", {index: this.#indexOfRule(ruleId), label: field.label}));
|
|
1048
|
+
this.#announce(I18n.t("queryBuilder.fieldChanged", { index: this.#indexOfRule(ruleId), label: field.label }));
|
|
968
1049
|
this.#refreshErrors();
|
|
969
1050
|
this.#notify();
|
|
970
1051
|
return this;
|
|
@@ -976,7 +1057,12 @@ class QueryBuilder {
|
|
|
976
1057
|
rule.operator = operator;
|
|
977
1058
|
if (this.#needsValue(operator) === false) rule.value = undefined;
|
|
978
1059
|
this.#render();
|
|
979
|
-
this.#announce(
|
|
1060
|
+
this.#announce(
|
|
1061
|
+
I18n.t("queryBuilder.operatorChanged", {
|
|
1062
|
+
index: this.#indexOfRule(ruleId),
|
|
1063
|
+
operator: I18n.t(OPERATOR_KEYS[operator]),
|
|
1064
|
+
})
|
|
1065
|
+
);
|
|
980
1066
|
this.#refreshErrors();
|
|
981
1067
|
this.#notify();
|
|
982
1068
|
return this;
|
|
@@ -1028,15 +1114,19 @@ class QueryBuilder {
|
|
|
1028
1114
|
|
|
1029
1115
|
static #groupText(group = Object(), fields = Array()) {
|
|
1030
1116
|
if (group === undefined) return "";
|
|
1031
|
-
const parts = group.children.map((child) =>
|
|
1032
|
-
|
|
1033
|
-
|
|
1117
|
+
const parts = group.children.map((child) =>
|
|
1118
|
+
child.kind === "group"
|
|
1119
|
+
? `(${QueryBuilder.#groupText(child, fields)})`
|
|
1120
|
+
: QueryBuilder.#valueText(child, fields)
|
|
1121
|
+
);
|
|
1034
1122
|
if (parts.length === 0) return "";
|
|
1035
|
-
return parts.length === 1
|
|
1123
|
+
return parts.length === 1
|
|
1124
|
+
? parts[0]
|
|
1125
|
+
: parts.join(` ${I18n.t(QueryBuilder.#combinatorTitle(group.combinator))} `);
|
|
1036
1126
|
}
|
|
1037
1127
|
|
|
1038
1128
|
#syncLabel() {
|
|
1039
|
-
if (this.#
|
|
1129
|
+
if (this.#titleText !== undefined && this.#titleText !== "") {
|
|
1040
1130
|
this.#root.setAttribute("aria-labelledby", this.#title.id);
|
|
1041
1131
|
this.#root.removeAttribute("aria-label");
|
|
1042
1132
|
} else {
|
|
@@ -1047,8 +1137,8 @@ class QueryBuilder {
|
|
|
1047
1137
|
}
|
|
1048
1138
|
|
|
1049
1139
|
#accessibleName() {
|
|
1050
|
-
if (this.#
|
|
1051
|
-
if (this.#
|
|
1140
|
+
if (this.#titleText !== undefined && this.#titleText !== "") return this.#titleText;
|
|
1141
|
+
if (this.#labelText !== undefined && this.#labelText !== "") return this.#labelText;
|
|
1052
1142
|
return I18n.t("queryBuilder.title");
|
|
1053
1143
|
}
|
|
1054
1144
|
|
|
@@ -1057,17 +1147,20 @@ class QueryBuilder {
|
|
|
1057
1147
|
for (const rule of this.#rules()) {
|
|
1058
1148
|
const node = this.#tree.querySelector(`query_builder_rule[data-id="${CSS.escape(rule.id)}"]`);
|
|
1059
1149
|
if (node === null) continue;
|
|
1060
|
-
node.classList.toggle(
|
|
1150
|
+
node.classList.toggle(
|
|
1151
|
+
"query_builder_rule_empty",
|
|
1152
|
+
this.#needsValue(rule.operator) && this.#isEmptyValue(rule.value)
|
|
1153
|
+
);
|
|
1061
1154
|
}
|
|
1062
1155
|
return this;
|
|
1063
1156
|
}
|
|
1064
1157
|
|
|
1065
1158
|
/** После правки показанная ошибка пересчитывается: она не должна пережить своё условие */
|
|
1066
1159
|
#refreshErrors() {
|
|
1067
|
-
if (this.#
|
|
1160
|
+
if (this.#errorShown === false) return this;
|
|
1068
1161
|
const result = this.validate();
|
|
1069
1162
|
if (result.valid) {
|
|
1070
|
-
this.#
|
|
1163
|
+
this.#errorShown = false;
|
|
1071
1164
|
this.clearError();
|
|
1072
1165
|
return this;
|
|
1073
1166
|
}
|
|
@@ -1084,9 +1177,9 @@ class QueryBuilder {
|
|
|
1084
1177
|
#notify() {
|
|
1085
1178
|
const value = this.getValue();
|
|
1086
1179
|
for (const listener of [...this.#listeners]) listener(value, this);
|
|
1087
|
-
if (this.#
|
|
1180
|
+
if (this.#onChange !== undefined) this.#onChange(value, this);
|
|
1088
1181
|
return this;
|
|
1089
1182
|
}
|
|
1090
1183
|
}
|
|
1091
1184
|
|
|
1092
|
-
exports.QueryBuilder = QueryBuilder
|
|
1185
|
+
exports.QueryBuilder = QueryBuilder;
|