From 48ef812a635f70571b395f82ffdb2969ce99da9e Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 23 Jun 2022 06:51:33 +0000 Subject: [PATCH] [REF] web: kanban, list and form views in owl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces the new kanban, list and form views written in owl. Even though it contains the implementation of those 3 views, only the kanban view is activated for now (the list and form views aren't 100% ready yet, so they aren't added to the view registry). Alongside the views, the fields ( in archs) and widgets ( Co-authored-by: Bruno Boi Co-authored-by: Géry Debongnie Co-authored-by: Samuel Degueldre Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: Simon Genin (ges) Co-authored-by: Francois (fge) Co-authored-by: Michael Mattiello (mcm) Co-authored-by: Julien Mougenot Co-authored-by: Lucas Perais (lpe) Co-authored-by: Jorge Pinna Puissant Co-authored-by: luvi --- .../form_renderer/wowl_form_renderer.js | 34 + addons/web/__manifest__.py | 12 +- addons/web/static/src/core/dialog/dialog.js | 27 +- addons/web/static/src/core/dialog/dialog.xml | 16 +- .../static/src/core/dialog/dialog_service.js | 6 +- .../static/src/core/dialog/simple_dialog.xml | 29 + .../static/src/core/errors/error_dialogs.js | 2 +- addons/web/static/src/core/utils/hooks.js | 67 +- addons/web/static/src/core/utils/strings.js | 2 + addons/web/static/src/core/utils/xml.js | 118 +- addons/web/static/src/fields/fields.js | 113 - .../web/static/src/legacy/action_adapters.js | 37 +- addons/web/static/src/legacy/backend_utils.js | 10 +- addons/web/static/src/legacy/debug_manager.js | 33 +- .../src/legacy/js/components/action_menus.js | 2 +- .../src/legacy/js/fields/basic_fields.js | 4 +- .../src/legacy/js/fields/basic_fields_owl.js | 2 +- .../src/legacy/js/fields/relational_fields.js | 6 +- .../js/views/kanban/kanban_controller.js | 2 +- .../legacy/js/views/kanban/kanban_record.js | 2 +- .../legacy/js/views/kanban/kanban_renderer.js | 4 +- .../src/legacy/js/views/view_dialogs.js | 2 +- addons/web/static/src/legacy/legacy_fields.js | 314 + .../web/static/src/legacy/legacy_fields.scss | 3 + .../static/src/legacy/legacy_view_widgets.js | 101 + addons/web/static/src/legacy/legacy_views.js | 56 +- addons/web/static/src/legacy/scss/fields.scss | 21 +- .../web/static/src/legacy/scss/form_view.scss | 4 +- .../scss/kanban_column_progressbar.scss | 10 +- .../src/legacy/scss/kanban_dashboard.scss | 4 +- .../legacy/scss/kanban_examples_dialog.scss | 2 +- .../static/src/legacy/scss/kanban_view.scss | 6 +- .../web/static/src/legacy/scss/list_view.scss | 2 +- addons/web/static/src/legacy/scss/utils.scss | 6 - addons/web/static/src/legacy/scss/views.scss | 4 + addons/web/static/src/legacy/xml/base.xml | 6 +- addons/web/static/src/legacy/xml/fields.xml | 2 +- addons/web/static/src/legacy/xml/kanban.xml | 8 +- .../src/views/basic_relational_model.js | 1399 ++ .../static/src/views/fields/ace/ace_field.js | 110 + .../src/views/fields/ace/ace_field.scss | 3 + .../static/src/views/fields/ace/ace_field.xml | 11 + .../attachment_image_field.js | 15 + .../attachment_image_field.xml | 10 + .../src/views/fields/badge/badge_field.js | 36 + .../src/views/fields/badge/badge_field.scss | 13 + .../src/views/fields/badge/badge_field.xml | 8 + .../badge_selection/badge_selection_field.js | 93 + .../badge_selection/badge_selection_field.xml | 20 + .../src/views/fields/binary/binary_field.js | 107 + .../src/views/fields/binary/binary_field.xml | 46 + .../src/views/fields/boolean/boolean_field.js | 39 + .../views/fields/boolean/boolean_field.xml | 8 + .../boolean_favorite_field.js | 31 + .../boolean_favorite_field.xml | 19 + .../boolean_toggle/boolean_toggle_field.js | 28 + .../boolean_toggle/boolean_toggle_field.xml | 17 + .../list_boolean_toggle_field.js | 19 + .../list_boolean_toggle_field.xml | 10 + .../src/views/fields/char/char_field.js | 62 + .../src/views/fields/char/char_field.xml | 30 + .../src/views/fields/color/color_field.js | 27 + .../src/views/fields/color/color_field.xml | 10 + .../fields/color_picker/color_picker_field.js | 25 + .../color_picker/color_picker_field.scss} | 45 +- .../color_picker/color_picker_field.xml | 8 + .../fields/copy_clipboard/copy_button.js | 39 + .../fields/copy_clipboard/copy_button.xml | 20 + .../copy_clipboard/copy_clipboard_field.js | 47 + .../copy_clipboard/copy_clipboard_field.scss | 19 + .../copy_clipboard/copy_clipboard_field.xml | 11 + .../src/views/fields/date/date_field.js | 54 + .../src/views/fields/date/date_field.xml | 17 + .../views/fields/daterange/daterange_field.js | 155 + .../fields/daterange/daterange_field.xml | 13 + .../views/fields/datetime/datetime_field.js | 44 + .../views/fields/datetime/datetime_field.xml | 17 + .../src/views/fields/domain/domain_field.js | 129 + .../src/views/fields/domain/domain_field.xml | 49 + .../src/views/fields/email/email_field.js | 24 + .../src/views/fields/email/email_field.xml | 20 + addons/web/static/src/views/fields/field.js | 262 + .../static/src/views/fields/field_tooltip.js | 37 + .../static/src/views/fields/field_tooltip.xml | 73 + .../web/static/src/views/fields/fields.scss | 3 + .../static/src/views/fields/file_handler.js | 103 + .../static/src/views/fields/file_handler.xml | 28 + .../src/views/fields/float/float_field.js | 79 + .../src/views/fields/float/float_field.xml | 9 + .../fields/float_factor/float_factor_field.js | 47 + .../float_factor/float_factor_field.xml | 8 + .../fields/float_time/float_time_field.js | 47 + .../fields/float_time/float_time_field.xml | 9 + .../fields/float_toggle/float_toggle_field.js | 56 + .../float_toggle/float_toggle_field.xml | 9 + .../font_selection/font_selection_field.js | 50 + .../font_selection/font_selection_field.xml | 29 + .../src/{ => views}/fields/formatters.js | 189 +- .../src/views/fields/handle/handle_field.js | 19 + .../src/views/fields/handle/handle_field.xml | 8 + .../src/views/fields/html/html_field.js | 10 + .../src/views/fields/html/html_field.xml | 11 + .../src/views/fields/image/image_field.js | 121 + .../src/views/fields/image/image_field.scss | 58 + .../src/views/fields/image/image_field.xml | 53 + .../views/fields/image_url/image_url_field.js | 64 + .../fields/image_url/image_url_field.xml | 18 + .../src/views/fields/input_field_hook.js | 84 + .../src/views/fields/integer/integer_field.js | 57 + .../views/fields/integer/integer_field.xml | 9 + .../journal_dashboard_graph_field.js | 151 + .../journal_dashboard_graph_field.scss | 3 + .../journal_dashboard_graph_field.xml | 10 + .../label_selection/label_selection_field.js | 39 + .../label_selection/label_selection_field.xml | 8 + .../many2many_binary_field.js | 69 + .../many2many_binary_field.xml | 54 + .../many2many_checkboxes_field.js | 58 + .../many2many_checkboxes_field.xml | 20 + .../kanban_many2many_tags_field.js | 20 + .../kanban_many2many_tags_field.xml | 7 + .../many2many_tags/many2many_tags_field.js | 311 + .../many2many_tags/many2many_tags_field.scss | 15 + .../many2many_tags/many2many_tags_field.xml | 31 + .../views/fields/many2many_tags/tags_list.js | 33 + .../fields/many2many_tags/tags_list.scss | 49 + .../views/fields/many2many_tags/tags_list.xml | 24 + .../many2many_tags_avatar_field.js | 65 + .../many2many_tags_avatar_field.scss | 9 + .../many2many_tags_avatar_field.xml | 23 + .../views/fields/many2one/many2one_field.js | 242 + .../views/fields/many2one/many2one_field.xml | 80 + .../many2one_avatar/many2one_avatar_field.js | 38 + .../many2one_avatar/many2one_avatar_field.xml | 14 + .../views/fields/monetary/monetary_field.js | 93 + .../views/fields/monetary/monetary_field.xml | 13 + .../src/views/fields/numpad_decimal_hook.js | 44 + .../static/src/{ => views}/fields/parsers.js | 74 +- .../fields/pdf_viewer/pdf_viewer_field.js | 99 + .../fields/pdf_viewer/pdf_viewer_field.xml | 51 + .../fields/percent_pie/percent_pie_field.js | 35 + .../fields/percent_pie/percent_pie_field.xml | 17 + .../fields/percentage/percentage_field.js | 52 + .../fields/percentage/percentage_field.xml | 21 + .../src/views/fields/phone/phone_field.js | 31 + .../src/views/fields/phone/phone_field.xml | 19 + .../views/fields/priority/priority_field.js | 89 + .../views/fields/priority/priority_field.xml | 39 + .../fields/progress_bar/progress_bar_field.js | 123 + .../progress_bar/progress_bar_field.xml | 71 + .../src/views/fields/radio/radio_field.js | 90 + .../src/views/fields/radio/radio_field.scss | 29 + .../src/views/fields/radio/radio_field.xml | 29 + .../views/fields/reference/reference_field.js | 135 + .../fields/reference/reference_field.xml | 23 + .../src/views/fields/relational_utils.js | 700 + .../src/views/fields/relational_utils.xml | 59 + .../remaining_days/remaining_days_field.js | 56 + .../remaining_days/remaining_days_field.xml | 33 + .../views/fields/selection/selection_field.js | 93 + .../fields/selection/selection_field.xml | 28 + .../src/views/fields/standard_field_props.js | 13 + .../views/fields/stat_info/stat_info_field.js | 42 + .../fields/stat_info/stat_info_field.xml | 9 + .../state_selection/state_selection_field.js | 96 + .../state_selection/state_selection_field.xml | 31 + .../views/fields/statusbar/statusbar_field.js | 203 + .../fields/statusbar/statusbar_field.scss | 71 + .../fields/statusbar/statusbar_field.xml | 66 + .../src/views/fields/text/text_field.js | 120 + .../src/views/fields/text/text_field.scss | 7 + .../src/views/fields/text/text_field.xml | 29 + .../src/views/fields/translation_button.js | 43 + .../src/views/fields/translation_button.scss | 9 + .../src/views/fields/translation_button.xml | 12 + .../src/views/fields/translation_dialog.js | 107 + .../src/views/fields/translation_dialog.xml | 51 + .../static/src/views/fields/url/url_field.js | 48 + .../static/src/views/fields/url/url_field.xml | 20 + .../views/fields/x2many/list_x2many_field.js | 21 + .../views/fields/x2many/list_x2many_field.xml | 8 + .../src/views/fields/x2many/x2many_field.js | 264 + .../src/views/fields/x2many/x2many_field.xml | 24 + .../src/views/form/button_box/button_box.js | 34 + .../src/views/form/button_box/button_box.scss | 165 + .../src/views/form/button_box/button_box.xml | 22 + .../static/src/views/form/form_arch_parser.js | 71 + .../static/src/views/form/form_compiler.js | 519 + .../static/src/views/form/form_controller.js | 436 + .../src/views/form/form_controller.scss | 1074 ++ .../static/src/views/form/form_controller.xml | 67 + .../src/views/form/form_group/form_group.js | 93 + .../src/views/form/form_group/form_group.xml | 45 + .../web/static/src/views/form/form_label.js | 53 + .../static/src/views/form/form_renderer.js | 42 + addons/web/static/src/views/form/form_view.js | 39 + .../src/views/form/form_view_extra.scss | 131 + .../src/views/graph/graph_arch_parser.js | 4 +- .../src/views/graph/graph_controller.js | 9 +- .../src/views/graph/graph_controller.xml | 4 +- .../web/static/src/views/graph/graph_model.js | 4 +- .../static/src/views/graph/graph_renderer.js | 2 +- .../src/views/helpers/standard_view_props.js | 38 - addons/web/static/src/views/helpers/utils.js | 98 - .../views/kanban/kanban_animated_number.js | 49 + .../views/kanban/kanban_animated_number.xml | 12 + .../src/views/kanban/kanban_arch_parser.js | 155 + .../kanban/kanban_column_examples_dialog.js | 60 + .../kanban/kanban_column_examples_dialog.xml | 31 + .../kanban/kanban_column_progressbar.scss | 147 + .../kanban/kanban_column_quick_create.js | 73 + .../kanban/kanban_column_quick_create.xml | 41 + .../src/views/kanban/kanban_compiler.js | 289 + .../src/views/kanban/kanban_controller.js | 124 + .../src/views/kanban/kanban_controller.scss | 661 + .../src/views/kanban/kanban_controller.xml | 32 + .../views/kanban/kanban_cover_image_dialog.js | 60 + .../kanban/kanban_cover_image_dialog.xml | 39 + .../src/views/kanban/kanban_dashboard.scss | 210 + .../views/kanban/kanban_examples_dialog.scss | 99 + .../static/src/views/kanban/kanban_model.js | 543 + .../static/src/views/kanban/kanban_record.js | 399 + .../kanban/kanban_record_quick_create.js | 62 + .../kanban/kanban_record_quick_create.xml | 25 + .../src/views/kanban/kanban_renderer.js | 567 + .../src/views/kanban/kanban_renderer.xml | 196 + .../static/src/views/kanban/kanban_view.js | 42 + .../static/src/views/list/list_arch_parser.js | 182 + .../views/list/list_confirmation_dialog.js | 52 + .../views/list/list_confirmation_dialog.xml | 45 + .../static/src/views/list/list_controller.js | 428 + .../src/views/list/list_controller.scss | 372 + .../static/src/views/list/list_controller.xml | 90 + .../static/src/views/list/list_renderer.js | 1648 ++ .../static/src/views/list/list_renderer.xml | 226 + addons/web/static/src/views/list/list_view.js | 35 + .../src/views/list/list_view_extra.scss | 42 + .../static/src/views/{helpers => }/model.js | 39 +- .../{helpers => }/no_content_helpers.xml | 0 .../web/static/src/views/onboarding_banner.js | 2 +- .../src/views/pivot/pivot_arch_parser.js | 2 +- .../src/views/pivot/pivot_controller.js | 9 +- .../src/views/pivot/pivot_controller.xml | 4 +- .../web/static/src/views/pivot/pivot_model.js | 12 +- .../static/src/views/pivot/pivot_renderer.js | 6 +- addons/web/static/src/views/record.js | 53 + .../web/static/src/views/relational_model.js | 3287 ++++ .../src/views/{helpers => }/sample_server.js | 60 +- .../static/src/views/standard_view_props.js | 42 + addons/web/static/src/views/utils.js | 238 + addons/web/static/src/views/view.js | 141 +- addons/web/static/src/views/view.xml | 11 +- .../src/views/view_button/view_button.js | 134 + .../src/views/view_button/view_button.xml | 72 + .../src/views/view_button/view_button_hook.js | 106 + addons/web/static/src/views/view_compiler.js | 461 + .../views/view_dialogs/export_data_dialog.js | 352 + .../view_dialogs/export_data_dialog.scss | 42 + .../views/view_dialogs/export_data_dialog.xml | 127 + .../views/view_dialogs/form_view_dialog.js | 90 + .../views/view_dialogs/form_view_dialog.scss | 5 + .../views/view_dialogs/form_view_dialog.xml | 31 + .../view_dialogs/select_create_dialog.js | 82 + .../view_dialogs/select_create_dialog.xml | 25 + .../src/views/{helpers => }/view_hook.js | 31 +- addons/web/static/src/views/view_service.js | 13 +- .../src/views/widgets/week_days/week_days.js | 40 + .../src/views/widgets/week_days/week_days.xml | 32 + addons/web/static/src/views/widgets/widget.js | 45 + .../src/webclient/actions/action_dialog.js | 15 +- .../src/webclient/actions/action_dialog.xml | 4 +- .../src/webclient/actions/action_service.js | 87 +- .../static/tests/core/dialog_service_tests.js | 1 - addons/web/static/tests/helpers/mock_env.js | 3 + .../web/static/tests/helpers/mock_server.js | 90 +- .../web/static/tests/helpers/mock_services.js | 6 +- addons/web/static/tests/helpers/utils.js | 352 +- .../tests/legacy/components/pager_tests.js | 2 +- .../legacy/control_panel/search_bar_tests.js | 11 + .../tests/legacy/core/owl_dialog_tests.js | 12 +- .../tests/legacy/fields/basic_fields_tests.js | 12 +- .../field_many2many_tests.js | 12 +- .../relational_fields/field_many2one_tests.js | 11 +- .../relational_fields/field_one2many_tests.js | 62 +- .../legacy/fields/relational_fields_tests.js | 10 +- .../web/static/tests/legacy/legacy_setup.js | 9 + .../tests/legacy/owl_compatibility_tests.js | 9 +- .../legacy/views/abstract_controller_tests.js | 2 +- .../legacy/views/abstract_model_tests.js | 2 +- .../views/abstract_view_banner_tests.js | 2 +- .../tests/legacy/views/abstract_view_tests.js | 8 +- .../tests/legacy/views/basic_model_tests.js | 4 +- .../tests/legacy/views/basic_view_tests.js | 2 +- .../tests/legacy/views/calendar_tests.js | 8 +- .../static/tests/legacy/views/form_tests.js | 28 +- .../static/tests/legacy/views/graph_tests.js | 2 +- .../tests/legacy/views/kanban_model_tests.js | 4 +- .../static/tests/legacy/views/kanban_tests.js | 132 +- .../static/tests/legacy/views/list_tests.js | 31 +- .../static/tests/legacy/views/pivot_tests.js | 6 +- .../tests/legacy/views/search_panel_tests.js | 62 +- .../tests/legacy/views/state_mapping_tests.js | 2 +- .../tests/legacy/views/view_dialogs_tests.js | 4 +- .../legacy/widgets/domain_selector_tests.js | 6 + .../tests/mobile/mobile_list_view_tests.js | 235 + .../web/static/tests/search/control_panel.js | 2 +- addons/web/static/tests/search/helpers.js | 240 +- .../static/tests/search/search_panel_tests.js | 136 +- addons/web/static/tests/setup.js | 15 +- .../views/fields/ace_editor_field_tests.js | 60 + .../tests/views/fields/badge_field_tests.js | 130 + .../fields/badge_selection_field_tests.js | 151 + .../tests/views/fields/binary_field_tests.js | 327 + .../fields/boolean_favorite_field_tests.js | 198 + .../tests/views/fields/boolean_field_tests.js | 306 + .../fields/boolean_toggle_field_tests.js | 205 + .../tests/views/fields/char_field_tests.js | 1016 ++ .../tests/views/fields/color_field_tests.js | 88 + .../views/fields/color_picker_field_tests.js | 199 + .../fields/copy_clipboard_field_tests.js | 124 + .../tests/views/fields/date_field_tests.js | 716 + .../views/fields/daterange_field_tests.js | 563 + .../views/fields/datetime_field_tests.js | 610 + .../tests/views/fields/domain_field_tests.js | 809 + .../tests/views/fields/email_field_tests.js | 242 + .../views/fields/float_factor_field_tests.js | 70 + .../tests/views/fields/float_field_tests.js | 450 + .../views/fields/float_time_field_tests.js | 167 + .../views/fields/float_toggle_field_tests.js | 120 + .../fields/font_selection_field_tests.js | 68 + .../{ => views}/fields/formatters_tests.js | 94 +- .../tests/views/fields/handle_field_tests.js | 168 + .../tests/views/fields/html_field_tests.js | 71 + .../tests/views/fields/image_field_tests.js | 370 + .../views/fields/image_url_field_tests.js | 215 + .../tests/views/fields/integer_field_tests.js | 302 + .../journal_dashboard_graph_field_tests.js | 182 + .../fields/label_selection_field_tests.js | 267 + .../fields/many2many_binary_field_tests.js | 178 + .../many2many_checkboxes_field_tests.js | 447 + .../views/fields/many2many_field_tests.js | 1824 ++ .../many2many_tags_avatar_field_tests.js | 361 + .../fields/many2many_tags_field_tests.js | 1528 ++ .../fields/many2one_avatar_field_tests.js | 227 + .../views/fields/many2one_field_tests.js | 4037 +++++ .../views/fields/monetary_field_tests.js | 1115 ++ .../views/fields/numeric_fields_tests.js | 185 + .../views/fields/one2many_field_tests.js | 11915 +++++++++++++ .../tests/{ => views}/fields/parsers_tests.js | 34 +- .../views/fields/pdf_viewer_field_tests.js | 105 + .../views/fields/percent_pie_field_tests.js | 281 + .../views/fields/percentage_field_tests.js | 80 + .../tests/views/fields/phone_field_tests.js | 165 + .../views/fields/priority_field_tests.js | 582 + .../views/fields/progress_bar_field_tests.js | 466 + .../tests/views/fields/radio_field_tests.js | 350 + .../views/fields/reference_field_tests.js | 878 + .../fields/remaining_days_field_tests.js | 477 + .../views/fields/selection_field_tests.js | 398 + .../views/fields/stat_info_field_tests.js | 295 + .../fields/state_selection_field_tests.js | 471 + .../views/fields/statusbar_field_tests.js | 525 + .../tests/views/fields/text_field_tests.js | 554 + .../tests/views/fields/url_field_tests.js | 299 + .../tests/views/form/form_compiler_tests.js | 235 + .../tests/views/form/form_view_tests.js | 10805 ++++++++++++ .../static/tests/views/graph_view_tests.js | 13 +- addons/web/static/tests/views/helpers.js | 42 +- .../static/tests/views/kanban_view_tests.js | 9528 +++++++++++ .../web/static/tests/views/list_view_tests.js | 13922 ++++++++++++++++ .../static/tests/views/pivot_view_tests.js | 6 +- addons/web/static/tests/views/record_tests.js | 186 + .../view_dialogs/export_data_dialog_tests.js | 684 + .../view_dialogs/form_view_dialog_tests.js | 286 + .../select_create_dialog_tests.js | 477 + addons/web/static/tests/views/view_tests.js | 9 +- .../tests/views/widgets/week_days_tests.js | 178 + .../webclient/actions/close_action_tests.js | 84 +- .../webclient/actions/concurrency_tests.js | 309 +- .../tests/webclient/actions/legacy_tests.js | 196 + .../webclient/actions/load_state_tests.js | 29 +- .../tests/webclient/actions/misc_tests.js | 133 +- .../webclient/actions/push_state_tests.js | 19 +- .../webclient/actions/server_action_tests.js | 4 +- .../tests/webclient/actions/target_tests.js | 170 +- .../webclient/actions/window_action_tests.js | 671 +- addons/web/static/tests/webclient/helpers.js | 4 +- .../static/tests/webclient/navbar_tests.js | 12 +- .../static/tests/webclient/webclient_tests.js | 6 +- 389 files changed, 99597 insertions(+), 2046 deletions(-) create mode 100644 addons/mail/static/src/widgets/form_renderer/wowl_form_renderer.js create mode 100644 addons/web/static/src/core/dialog/simple_dialog.xml delete mode 100644 addons/web/static/src/fields/fields.js create mode 100644 addons/web/static/src/legacy/legacy_fields.js create mode 100644 addons/web/static/src/legacy/legacy_fields.scss create mode 100644 addons/web/static/src/legacy/legacy_view_widgets.js create mode 100644 addons/web/static/src/views/basic_relational_model.js create mode 100644 addons/web/static/src/views/fields/ace/ace_field.js create mode 100644 addons/web/static/src/views/fields/ace/ace_field.scss create mode 100644 addons/web/static/src/views/fields/ace/ace_field.xml create mode 100644 addons/web/static/src/views/fields/attachment_image/attachment_image_field.js create mode 100644 addons/web/static/src/views/fields/attachment_image/attachment_image_field.xml create mode 100644 addons/web/static/src/views/fields/badge/badge_field.js create mode 100644 addons/web/static/src/views/fields/badge/badge_field.scss create mode 100644 addons/web/static/src/views/fields/badge/badge_field.xml create mode 100644 addons/web/static/src/views/fields/badge_selection/badge_selection_field.js create mode 100644 addons/web/static/src/views/fields/badge_selection/badge_selection_field.xml create mode 100644 addons/web/static/src/views/fields/binary/binary_field.js create mode 100644 addons/web/static/src/views/fields/binary/binary_field.xml create mode 100644 addons/web/static/src/views/fields/boolean/boolean_field.js create mode 100644 addons/web/static/src/views/fields/boolean/boolean_field.xml create mode 100644 addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.js create mode 100644 addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.xml create mode 100644 addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.js create mode 100644 addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.xml create mode 100644 addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.js create mode 100644 addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.xml create mode 100644 addons/web/static/src/views/fields/char/char_field.js create mode 100644 addons/web/static/src/views/fields/char/char_field.xml create mode 100644 addons/web/static/src/views/fields/color/color_field.js create mode 100644 addons/web/static/src/views/fields/color/color_field.xml create mode 100644 addons/web/static/src/views/fields/color_picker/color_picker_field.js rename addons/web/static/src/{legacy/scss/color_picker.scss => views/fields/color_picker/color_picker_field.scss} (60%) create mode 100644 addons/web/static/src/views/fields/color_picker/color_picker_field.xml create mode 100644 addons/web/static/src/views/fields/copy_clipboard/copy_button.js create mode 100644 addons/web/static/src/views/fields/copy_clipboard/copy_button.xml create mode 100644 addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.js create mode 100644 addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.scss create mode 100644 addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.xml create mode 100644 addons/web/static/src/views/fields/date/date_field.js create mode 100644 addons/web/static/src/views/fields/date/date_field.xml create mode 100644 addons/web/static/src/views/fields/daterange/daterange_field.js create mode 100644 addons/web/static/src/views/fields/daterange/daterange_field.xml create mode 100644 addons/web/static/src/views/fields/datetime/datetime_field.js create mode 100644 addons/web/static/src/views/fields/datetime/datetime_field.xml create mode 100644 addons/web/static/src/views/fields/domain/domain_field.js create mode 100644 addons/web/static/src/views/fields/domain/domain_field.xml create mode 100644 addons/web/static/src/views/fields/email/email_field.js create mode 100644 addons/web/static/src/views/fields/email/email_field.xml create mode 100644 addons/web/static/src/views/fields/field.js create mode 100644 addons/web/static/src/views/fields/field_tooltip.js create mode 100644 addons/web/static/src/views/fields/field_tooltip.xml create mode 100644 addons/web/static/src/views/fields/fields.scss create mode 100644 addons/web/static/src/views/fields/file_handler.js create mode 100644 addons/web/static/src/views/fields/file_handler.xml create mode 100644 addons/web/static/src/views/fields/float/float_field.js create mode 100644 addons/web/static/src/views/fields/float/float_field.xml create mode 100644 addons/web/static/src/views/fields/float_factor/float_factor_field.js create mode 100644 addons/web/static/src/views/fields/float_factor/float_factor_field.xml create mode 100644 addons/web/static/src/views/fields/float_time/float_time_field.js create mode 100644 addons/web/static/src/views/fields/float_time/float_time_field.xml create mode 100644 addons/web/static/src/views/fields/float_toggle/float_toggle_field.js create mode 100644 addons/web/static/src/views/fields/float_toggle/float_toggle_field.xml create mode 100644 addons/web/static/src/views/fields/font_selection/font_selection_field.js create mode 100644 addons/web/static/src/views/fields/font_selection/font_selection_field.xml rename addons/web/static/src/{ => views}/fields/formatters.js (69%) create mode 100644 addons/web/static/src/views/fields/handle/handle_field.js create mode 100644 addons/web/static/src/views/fields/handle/handle_field.xml create mode 100644 addons/web/static/src/views/fields/html/html_field.js create mode 100644 addons/web/static/src/views/fields/html/html_field.xml create mode 100644 addons/web/static/src/views/fields/image/image_field.js create mode 100644 addons/web/static/src/views/fields/image/image_field.scss create mode 100644 addons/web/static/src/views/fields/image/image_field.xml create mode 100644 addons/web/static/src/views/fields/image_url/image_url_field.js create mode 100644 addons/web/static/src/views/fields/image_url/image_url_field.xml create mode 100644 addons/web/static/src/views/fields/input_field_hook.js create mode 100644 addons/web/static/src/views/fields/integer/integer_field.js create mode 100644 addons/web/static/src/views/fields/integer/integer_field.xml create mode 100644 addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.js create mode 100644 addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.scss create mode 100644 addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.xml create mode 100644 addons/web/static/src/views/fields/label_selection/label_selection_field.js create mode 100644 addons/web/static/src/views/fields/label_selection/label_selection_field.xml create mode 100644 addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.js create mode 100644 addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.xml create mode 100644 addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.js create mode 100644 addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.xml create mode 100644 addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.js create mode 100644 addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.xml create mode 100644 addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js create mode 100644 addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.scss create mode 100644 addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.xml create mode 100644 addons/web/static/src/views/fields/many2many_tags/tags_list.js create mode 100644 addons/web/static/src/views/fields/many2many_tags/tags_list.scss create mode 100644 addons/web/static/src/views/fields/many2many_tags/tags_list.xml create mode 100644 addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.js create mode 100644 addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss create mode 100644 addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.xml create mode 100644 addons/web/static/src/views/fields/many2one/many2one_field.js create mode 100644 addons/web/static/src/views/fields/many2one/many2one_field.xml create mode 100644 addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.js create mode 100644 addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.xml create mode 100644 addons/web/static/src/views/fields/monetary/monetary_field.js create mode 100644 addons/web/static/src/views/fields/monetary/monetary_field.xml create mode 100644 addons/web/static/src/views/fields/numpad_decimal_hook.js rename addons/web/static/src/{ => views}/fields/parsers.js (66%) create mode 100644 addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.js create mode 100644 addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.xml create mode 100644 addons/web/static/src/views/fields/percent_pie/percent_pie_field.js create mode 100644 addons/web/static/src/views/fields/percent_pie/percent_pie_field.xml create mode 100644 addons/web/static/src/views/fields/percentage/percentage_field.js create mode 100644 addons/web/static/src/views/fields/percentage/percentage_field.xml create mode 100644 addons/web/static/src/views/fields/phone/phone_field.js create mode 100644 addons/web/static/src/views/fields/phone/phone_field.xml create mode 100644 addons/web/static/src/views/fields/priority/priority_field.js create mode 100644 addons/web/static/src/views/fields/priority/priority_field.xml create mode 100644 addons/web/static/src/views/fields/progress_bar/progress_bar_field.js create mode 100644 addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml create mode 100644 addons/web/static/src/views/fields/radio/radio_field.js create mode 100644 addons/web/static/src/views/fields/radio/radio_field.scss create mode 100644 addons/web/static/src/views/fields/radio/radio_field.xml create mode 100644 addons/web/static/src/views/fields/reference/reference_field.js create mode 100644 addons/web/static/src/views/fields/reference/reference_field.xml create mode 100644 addons/web/static/src/views/fields/relational_utils.js create mode 100644 addons/web/static/src/views/fields/relational_utils.xml create mode 100644 addons/web/static/src/views/fields/remaining_days/remaining_days_field.js create mode 100644 addons/web/static/src/views/fields/remaining_days/remaining_days_field.xml create mode 100644 addons/web/static/src/views/fields/selection/selection_field.js create mode 100644 addons/web/static/src/views/fields/selection/selection_field.xml create mode 100644 addons/web/static/src/views/fields/standard_field_props.js create mode 100644 addons/web/static/src/views/fields/stat_info/stat_info_field.js create mode 100644 addons/web/static/src/views/fields/stat_info/stat_info_field.xml create mode 100644 addons/web/static/src/views/fields/state_selection/state_selection_field.js create mode 100644 addons/web/static/src/views/fields/state_selection/state_selection_field.xml create mode 100644 addons/web/static/src/views/fields/statusbar/statusbar_field.js create mode 100644 addons/web/static/src/views/fields/statusbar/statusbar_field.scss create mode 100644 addons/web/static/src/views/fields/statusbar/statusbar_field.xml create mode 100644 addons/web/static/src/views/fields/text/text_field.js create mode 100644 addons/web/static/src/views/fields/text/text_field.scss create mode 100644 addons/web/static/src/views/fields/text/text_field.xml create mode 100644 addons/web/static/src/views/fields/translation_button.js create mode 100644 addons/web/static/src/views/fields/translation_button.scss create mode 100644 addons/web/static/src/views/fields/translation_button.xml create mode 100644 addons/web/static/src/views/fields/translation_dialog.js create mode 100644 addons/web/static/src/views/fields/translation_dialog.xml create mode 100644 addons/web/static/src/views/fields/url/url_field.js create mode 100644 addons/web/static/src/views/fields/url/url_field.xml create mode 100644 addons/web/static/src/views/fields/x2many/list_x2many_field.js create mode 100644 addons/web/static/src/views/fields/x2many/list_x2many_field.xml create mode 100644 addons/web/static/src/views/fields/x2many/x2many_field.js create mode 100644 addons/web/static/src/views/fields/x2many/x2many_field.xml create mode 100644 addons/web/static/src/views/form/button_box/button_box.js create mode 100644 addons/web/static/src/views/form/button_box/button_box.scss create mode 100644 addons/web/static/src/views/form/button_box/button_box.xml create mode 100644 addons/web/static/src/views/form/form_arch_parser.js create mode 100644 addons/web/static/src/views/form/form_compiler.js create mode 100644 addons/web/static/src/views/form/form_controller.js create mode 100644 addons/web/static/src/views/form/form_controller.scss create mode 100644 addons/web/static/src/views/form/form_controller.xml create mode 100644 addons/web/static/src/views/form/form_group/form_group.js create mode 100644 addons/web/static/src/views/form/form_group/form_group.xml create mode 100644 addons/web/static/src/views/form/form_label.js create mode 100644 addons/web/static/src/views/form/form_renderer.js create mode 100644 addons/web/static/src/views/form/form_view.js create mode 100644 addons/web/static/src/views/form/form_view_extra.scss delete mode 100644 addons/web/static/src/views/helpers/standard_view_props.js delete mode 100644 addons/web/static/src/views/helpers/utils.js create mode 100644 addons/web/static/src/views/kanban/kanban_animated_number.js create mode 100644 addons/web/static/src/views/kanban/kanban_animated_number.xml create mode 100644 addons/web/static/src/views/kanban/kanban_arch_parser.js create mode 100644 addons/web/static/src/views/kanban/kanban_column_examples_dialog.js create mode 100644 addons/web/static/src/views/kanban/kanban_column_examples_dialog.xml create mode 100644 addons/web/static/src/views/kanban/kanban_column_progressbar.scss create mode 100644 addons/web/static/src/views/kanban/kanban_column_quick_create.js create mode 100644 addons/web/static/src/views/kanban/kanban_column_quick_create.xml create mode 100644 addons/web/static/src/views/kanban/kanban_compiler.js create mode 100644 addons/web/static/src/views/kanban/kanban_controller.js create mode 100644 addons/web/static/src/views/kanban/kanban_controller.scss create mode 100644 addons/web/static/src/views/kanban/kanban_controller.xml create mode 100644 addons/web/static/src/views/kanban/kanban_cover_image_dialog.js create mode 100644 addons/web/static/src/views/kanban/kanban_cover_image_dialog.xml create mode 100644 addons/web/static/src/views/kanban/kanban_dashboard.scss create mode 100644 addons/web/static/src/views/kanban/kanban_examples_dialog.scss create mode 100644 addons/web/static/src/views/kanban/kanban_model.js create mode 100644 addons/web/static/src/views/kanban/kanban_record.js create mode 100644 addons/web/static/src/views/kanban/kanban_record_quick_create.js create mode 100644 addons/web/static/src/views/kanban/kanban_record_quick_create.xml create mode 100644 addons/web/static/src/views/kanban/kanban_renderer.js create mode 100644 addons/web/static/src/views/kanban/kanban_renderer.xml create mode 100644 addons/web/static/src/views/kanban/kanban_view.js create mode 100644 addons/web/static/src/views/list/list_arch_parser.js create mode 100644 addons/web/static/src/views/list/list_confirmation_dialog.js create mode 100644 addons/web/static/src/views/list/list_confirmation_dialog.xml create mode 100644 addons/web/static/src/views/list/list_controller.js create mode 100644 addons/web/static/src/views/list/list_controller.scss create mode 100644 addons/web/static/src/views/list/list_controller.xml create mode 100644 addons/web/static/src/views/list/list_renderer.js create mode 100644 addons/web/static/src/views/list/list_renderer.xml create mode 100644 addons/web/static/src/views/list/list_view.js create mode 100644 addons/web/static/src/views/list/list_view_extra.scss rename addons/web/static/src/views/{helpers => }/model.js (74%) rename addons/web/static/src/views/{helpers => }/no_content_helpers.xml (100%) create mode 100644 addons/web/static/src/views/record.js create mode 100644 addons/web/static/src/views/relational_model.js rename addons/web/static/src/views/{helpers => }/sample_server.js (94%) create mode 100644 addons/web/static/src/views/standard_view_props.js create mode 100644 addons/web/static/src/views/utils.js create mode 100644 addons/web/static/src/views/view_button/view_button.js create mode 100644 addons/web/static/src/views/view_button/view_button.xml create mode 100644 addons/web/static/src/views/view_button/view_button_hook.js create mode 100644 addons/web/static/src/views/view_compiler.js create mode 100644 addons/web/static/src/views/view_dialogs/export_data_dialog.js create mode 100644 addons/web/static/src/views/view_dialogs/export_data_dialog.scss create mode 100644 addons/web/static/src/views/view_dialogs/export_data_dialog.xml create mode 100644 addons/web/static/src/views/view_dialogs/form_view_dialog.js create mode 100644 addons/web/static/src/views/view_dialogs/form_view_dialog.scss create mode 100644 addons/web/static/src/views/view_dialogs/form_view_dialog.xml create mode 100644 addons/web/static/src/views/view_dialogs/select_create_dialog.js create mode 100644 addons/web/static/src/views/view_dialogs/select_create_dialog.xml rename addons/web/static/src/views/{helpers => }/view_hook.js (81%) create mode 100644 addons/web/static/src/views/widgets/week_days/week_days.js create mode 100644 addons/web/static/src/views/widgets/week_days/week_days.xml create mode 100644 addons/web/static/src/views/widgets/widget.js create mode 100644 addons/web/static/tests/mobile/mobile_list_view_tests.js create mode 100644 addons/web/static/tests/views/fields/ace_editor_field_tests.js create mode 100644 addons/web/static/tests/views/fields/badge_field_tests.js create mode 100644 addons/web/static/tests/views/fields/badge_selection_field_tests.js create mode 100644 addons/web/static/tests/views/fields/binary_field_tests.js create mode 100644 addons/web/static/tests/views/fields/boolean_favorite_field_tests.js create mode 100644 addons/web/static/tests/views/fields/boolean_field_tests.js create mode 100644 addons/web/static/tests/views/fields/boolean_toggle_field_tests.js create mode 100644 addons/web/static/tests/views/fields/char_field_tests.js create mode 100644 addons/web/static/tests/views/fields/color_field_tests.js create mode 100644 addons/web/static/tests/views/fields/color_picker_field_tests.js create mode 100644 addons/web/static/tests/views/fields/copy_clipboard_field_tests.js create mode 100644 addons/web/static/tests/views/fields/date_field_tests.js create mode 100644 addons/web/static/tests/views/fields/daterange_field_tests.js create mode 100644 addons/web/static/tests/views/fields/datetime_field_tests.js create mode 100644 addons/web/static/tests/views/fields/domain_field_tests.js create mode 100644 addons/web/static/tests/views/fields/email_field_tests.js create mode 100644 addons/web/static/tests/views/fields/float_factor_field_tests.js create mode 100644 addons/web/static/tests/views/fields/float_field_tests.js create mode 100644 addons/web/static/tests/views/fields/float_time_field_tests.js create mode 100644 addons/web/static/tests/views/fields/float_toggle_field_tests.js create mode 100644 addons/web/static/tests/views/fields/font_selection_field_tests.js rename addons/web/static/tests/{ => views}/fields/formatters_tests.js (80%) create mode 100644 addons/web/static/tests/views/fields/handle_field_tests.js create mode 100644 addons/web/static/tests/views/fields/html_field_tests.js create mode 100644 addons/web/static/tests/views/fields/image_field_tests.js create mode 100644 addons/web/static/tests/views/fields/image_url_field_tests.js create mode 100644 addons/web/static/tests/views/fields/integer_field_tests.js create mode 100644 addons/web/static/tests/views/fields/journal_dashboard_graph_field_tests.js create mode 100644 addons/web/static/tests/views/fields/label_selection_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2many_binary_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2many_checkboxes_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2many_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2many_tags_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2one_avatar_field_tests.js create mode 100644 addons/web/static/tests/views/fields/many2one_field_tests.js create mode 100644 addons/web/static/tests/views/fields/monetary_field_tests.js create mode 100644 addons/web/static/tests/views/fields/numeric_fields_tests.js create mode 100644 addons/web/static/tests/views/fields/one2many_field_tests.js rename addons/web/static/tests/{ => views}/fields/parsers_tests.js (82%) create mode 100644 addons/web/static/tests/views/fields/pdf_viewer_field_tests.js create mode 100644 addons/web/static/tests/views/fields/percent_pie_field_tests.js create mode 100644 addons/web/static/tests/views/fields/percentage_field_tests.js create mode 100644 addons/web/static/tests/views/fields/phone_field_tests.js create mode 100644 addons/web/static/tests/views/fields/priority_field_tests.js create mode 100644 addons/web/static/tests/views/fields/progress_bar_field_tests.js create mode 100644 addons/web/static/tests/views/fields/radio_field_tests.js create mode 100644 addons/web/static/tests/views/fields/reference_field_tests.js create mode 100644 addons/web/static/tests/views/fields/remaining_days_field_tests.js create mode 100644 addons/web/static/tests/views/fields/selection_field_tests.js create mode 100644 addons/web/static/tests/views/fields/stat_info_field_tests.js create mode 100644 addons/web/static/tests/views/fields/state_selection_field_tests.js create mode 100644 addons/web/static/tests/views/fields/statusbar_field_tests.js create mode 100644 addons/web/static/tests/views/fields/text_field_tests.js create mode 100644 addons/web/static/tests/views/fields/url_field_tests.js create mode 100644 addons/web/static/tests/views/form/form_compiler_tests.js create mode 100644 addons/web/static/tests/views/form/form_view_tests.js create mode 100644 addons/web/static/tests/views/kanban_view_tests.js create mode 100644 addons/web/static/tests/views/list_view_tests.js create mode 100644 addons/web/static/tests/views/record_tests.js create mode 100644 addons/web/static/tests/views/view_dialogs/export_data_dialog_tests.js create mode 100644 addons/web/static/tests/views/view_dialogs/form_view_dialog_tests.js create mode 100644 addons/web/static/tests/views/view_dialogs/select_create_dialog_tests.js create mode 100644 addons/web/static/tests/views/widgets/week_days_tests.js diff --git a/addons/mail/static/src/widgets/form_renderer/wowl_form_renderer.js b/addons/mail/static/src/widgets/form_renderer/wowl_form_renderer.js new file mode 100644 index 00000000000..76bc7d2b7fd --- /dev/null +++ b/addons/mail/static/src/widgets/form_renderer/wowl_form_renderer.js @@ -0,0 +1,34 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { FormRenderer } from "@web/views/form/form_renderer"; +import { ChatterContainer } from "@mail/components/chatter_container/chatter_container"; +import { + createElement, +} from "@web/core/utils/xml"; +import { append } from "@web/views/view_compiler"; + +function compileChatter(node, params) { + node.classList.remove("oe_chatter"); + const container = createElement("div"); + container.classList.add("o_FormRenderer_chatterContainer"); + + const chatter = createElement("ChatterContainer"); + chatter.setAttribute("threadModel", "props.record.resModel"); + chatter.setAttribute("threadId", "props.record.resId"); + // TODO: pass chatterFields equivalent in props + + // chatter.setAttribute("record", "props.record"); // props.record.model.load() to reload the form + // in ChatterContainer setup: + // this.messagingService = useService("messaging"); + + append(container, chatter); + return container; +} + +registry.category("form_compilers").add("chatter_compiler", { + selector: "div.oe_chatter", + fn: compileChatter, +}); + +FormRenderer.components.ChatterContainer = ChatterContainer; diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index bd2bb28f119..96137e56561 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -96,9 +96,10 @@ This module provides the core of the Odoo Web Client. 'web/static/src/core/utils/transitions.scss', 'web/static/src/core/**/*', - 'web/static/src/fields/**/*', + 'web/static/src/legacy/legacy_fields.scss', 'web/static/src/search/**/*', 'web/static/src/views/**/*', + ('remove', 'web/static/src/views/form/button_box/*.scss'), 'web/static/src/webclient/**/*', ('remove', 'web/static/src/webclient/navbar/navbar.scss'), # already in _assets_common_styles ('remove', 'web/static/src/webclient/clickbot/clickbot.js'), # lazy loaded @@ -141,6 +142,9 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/scss/profiling_qweb_view.scss', 'base/static/src/scss/res_partner.scss', + # Form style should be computed before + 'web/static/src/views/form/button_box/*.scss', + 'web/static/src/legacy/action_adapters.js', 'web/static/src/legacy/debug_manager.js', 'web/static/src/legacy/legacy_service_provider.js', @@ -148,6 +152,8 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/legacy_dialog.js', 'web/static/src/legacy/legacy_load_views.js', 'web/static/src/legacy/legacy_views.js', + 'web/static/src/legacy/legacy_fields.js', + 'web/static/src/legacy/legacy_view_widgets.js', 'web/static/src/legacy/legacy_promise_error_handler.js', 'web/static/src/legacy/legacy_rpc_error_handler.js', 'web/static/src/legacy/root_widget.js', @@ -355,7 +361,6 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/scss/keyboard.scss', 'web/static/src/legacy/scss/name_and_signature.scss', 'web/static/src/legacy/scss/web.zoomodoo.scss', - 'web/static/src/legacy/scss/color_picker.scss', 'web/static/src/legacy/scss/fontawesome_overridden.scss', ], 'web._assets_common_scripts': [ @@ -490,6 +495,7 @@ This module provides the core of the Odoo Web Client. # 'web/static/tests/legacy/main_tests.js', 'web/static/tests/helpers/**/*.js', + 'web/static/tests/views/helpers.js', 'web/static/tests/search/helpers.js', 'web/static/tests/webclient/**/helpers.js', 'web/static/tests/qunit.js', @@ -511,10 +517,10 @@ This module provides the core of the Odoo Web Client. 'base/static/tests/base_settings_tests.js', 'web/static/tests/env_tests.js', 'web/static/tests/core/**/*.js', - 'web/static/tests/fields/**/*.js', 'web/static/tests/search/**/*.js', ('remove', 'web/static/tests/search/helpers.js'), 'web/static/tests/views/**/*.js', + ('remove', 'web/static/tests/views/helpers.js'), 'web/static/tests/webclient/**/*.js', ('remove', 'web/static/tests/webclient/**/helpers.js'), 'web/static/tests/legacy/**/*.js', diff --git a/addons/web/static/src/core/dialog/dialog.js b/addons/web/static/src/core/dialog/dialog.js index 2e4a5a3d34c..0f2d75a0a44 100644 --- a/addons/web/static/src/core/dialog/dialog.js +++ b/addons/web/static/src/core/dialog/dialog.js @@ -2,17 +2,19 @@ import { useHotkey } from "@web/core/hotkeys/hotkey_hook"; import { useActiveElement } from "../ui/ui_service"; +import { useForwardRefToParent } from "@web/core/utils/hooks"; -const { Component, useRef, useChildSubEnv, useState } = owl; +const { Component, useChildSubEnv, useState } = owl; export class Dialog extends Component { setup() { - this.modalRef = useRef("modal"); - useActiveElement("modal"); + this.modalRef = useForwardRefToParent("modalRef"); + useActiveElement("modalRef"); this.data = useState(this.env.dialogData); useHotkey("escape", () => { this.data.close(); }); - useChildSubEnv({ inDialog: true }); + this.id = `dialog_${this.data.id}`; + useChildSubEnv({ inDialog: true, dialogId: this.id }); } } Dialog.template = "web.Dialog"; @@ -24,6 +26,7 @@ Dialog.props = { size: { type: String, optional: true, validate: (s) => ["sm", "md", "lg", "xl"].includes(s) }, technical: { type: Boolean, optional: true }, title: { type: String, optional: true }, + modalRef: { type: Function, optional: true }, slots: { type: Object, shape: { @@ -41,3 +44,19 @@ Dialog.defaultProps = { technical: true, title: "Odoo", }; + +export class SimpleDialog extends Component { + setup() { + useActiveElement("modal"); + useHotkey("escape", () => { + this.props.close(); + }); + useChildSubEnv({ inDialog: true }); + } +} +SimpleDialog.template = "web.SimpleDialog"; +SimpleDialog.props = { + close: Function, + isActive: { optional: true }, + "*": true, +}; diff --git a/addons/web/static/src/core/dialog/dialog.xml b/addons/web/static/src/core/dialog/dialog.xml index 5cacc2f9d94..048a104399f 100644 --- a/addons/web/static/src/core/dialog/dialog.xml +++ b/addons/web/static/src/core/dialog/dialog.xml @@ -2,11 +2,11 @@ -
+
diff --git a/addons/web/static/src/core/dialog/dialog_service.js b/addons/web/static/src/core/dialog/dialog_service.js index 18be68c8aba..0d6cbc23e71 100644 --- a/addons/web/static/src/core/dialog/dialog_service.js +++ b/addons/web/static/src/core/dialog/dialog_service.js @@ -3,7 +3,7 @@ import { registry } from "../registry"; import { DialogContainer } from "./dialog_container"; -const { reactive } = owl; +const { markRaw, reactive } = owl; /** * @typedef {{ @@ -51,8 +51,8 @@ export const dialogService = { const dialog = { id, class: dialogClass, - props: { ...props, close }, - dialogData: { isActive: true, close }, + props: markRaw({ ...props, close }), + dialogData: { isActive: true, close, id }, }; dialogs[id] = dialog; diff --git a/addons/web/static/src/core/dialog/simple_dialog.xml b/addons/web/static/src/core/dialog/simple_dialog.xml new file mode 100644 index 00000000000..127b7d2da39 --- /dev/null +++ b/addons/web/static/src/core/dialog/simple_dialog.xml @@ -0,0 +1,29 @@ + + + +
+ +
+
+
diff --git a/addons/web/static/src/core/errors/error_dialogs.js b/addons/web/static/src/core/errors/error_dialogs.js index c8a3f1d569f..c5dcffd6fef 100644 --- a/addons/web/static/src/core/errors/error_dialogs.js +++ b/addons/web/static/src/core/errors/error_dialogs.js @@ -111,7 +111,7 @@ export class WarningDialog extends Component { if (this.props.exceptionName && odooExceptionTitleMap.has(this.props.exceptionName)) { return odooExceptionTitleMap.get(this.props.exceptionName).toString(); } - return this.env._t("Odoo Warning"); + return this.props.title || this.env._t("Odoo Warning"); } } WarningDialog.template = "web.WarningDialog"; diff --git a/addons/web/static/src/core/utils/hooks.js b/addons/web/static/src/core/utils/hooks.js index 48825e95ec8..381b80061cb 100644 --- a/addons/web/static/src/core/utils/hooks.js +++ b/addons/web/static/src/core/utils/hooks.js @@ -2,7 +2,7 @@ import { SERVICES_METADATA } from "@web/env"; -const { status, useComponent, useEffect, useRef } = owl; +const { status, useComponent, useEffect, useRef, onWillUnmount } = owl; /** * This file contains various custom hooks. @@ -31,7 +31,7 @@ const { status, useComponent, useEffect, useRef } = owl; */ export function useAutofocus() { const comp = useComponent(); - let ref = useRef("autofocus"); + const ref = useRef("autofocus"); // Prevent autofocus in mobile if (comp.env.isSmall) { return ref; @@ -110,7 +110,7 @@ export function useBus(bus, eventName, callback) { * * @param {string} eventName the name of the event * @param {string} [querySelector] a JS native selector for event delegation - * @param {function} handler the event handler (will be bound to the component) + * @param {function} [handler] the event handler (will be bound to the component) * @param {Object} [options] to be passed to addEventListener as options. * Useful for listening in the capture phase */ @@ -190,7 +190,7 @@ export function useService(serviceName) { } else { const methods = SERVICES_METADATA[serviceName]; const result = Object.create(service); - for (let method of methods) { + for (const method of methods) { result[method] = _protectMethod(component, service, service[method]); } return result; @@ -198,3 +198,62 @@ export function useService(serviceName) { } return service; } + +/** + * @typedef {Function} ForwardRef + * @property {HTMLElement | undefined} el + */ + +/** + * Use a ref that was forwarded by a child @see useForwardRefToParent + * + * @returns {ForwardRef} a ref that can be called to set its value to that of a + * child ref, but can otherwise be used as a normal ref object + */ +export function useChildRef() { + let defined = false; + return function ref(value) { + if (defined) { + return; + } + Object.defineProperty(ref, "el", { + get() { + return value.el; + }, + }); + defined = true; + }; +} +/** + * Forwards the given refName to the parent by calling the corresponding + * ForwardRef received as prop. @see useChildRef + * + * @param {string} refName name of the ref to forward + * @returns {{el: HTMLelement | null}} the same ref that is forwarded to the + * parent + */ +export function useForwardRefToParent(refName) { + const component = useComponent(); + const ref = useRef(refName); + if (component.props[refName]) { + component.props[refName](ref); + } + return ref; +} +/** + * Use the dialog service while also automatically closing the dialogs opened + * by the current component when it is unmounted. + */ +export function useOwnedDialogs() { + const dialogService = useService("dialog"); + const cbs = []; + onWillUnmount(() => { + cbs.forEach((cb) => cb()); + }); + const addDialog = (...args) => { + const close = dialogService.add(...args); + cbs.push(close); + return close; + }; + return addDialog; +} diff --git a/addons/web/static/src/core/utils/strings.js b/addons/web/static/src/core/utils/strings.js index 88b5185e3f9..a53307c68a9 100644 --- a/addons/web/static/src/core/utils/strings.js +++ b/addons/web/static/src/core/utils/strings.js @@ -1,5 +1,7 @@ /** @odoo-module **/ +export const nbsp = "\u00a0"; + /** * Escapes a string for HTML. * Note that it doesn't work for escaping node attributes. diff --git a/addons/web/static/src/core/utils/xml.js b/addons/web/static/src/core/utils/xml.js index 91bcccf8b62..01cd2a3b26b 100644 --- a/addons/web/static/src/core/utils/xml.js +++ b/addons/web/static/src/core/utils/xml.js @@ -1,5 +1,12 @@ /** @odoo-module **/ +/** + * XML document to create new elements from. The fact that this is a "text/xml" + * document ensures that tagNames and attribute names are case sensitive. + */ +const parser = new DOMParser(); +const xmlDocument = parser.parseFromString("", "text/xml"); + function hasParsingError(parsedDocument) { return parsedDocument.getElementsByTagName("parsererror").length > 0; } @@ -11,29 +18,37 @@ export class XMLParser { */ parse() {} + /** + * @param {Element | string} xml + * @param {(el: Element, visitChildren: () => any) => any} callback + */ visitXML(xml, callback) { - function visit(xml) { - if (xml) { + const visit = (el) => { + if (el) { let didVisitChildren = false; const visitChildren = () => { - for (let child of xml.children) { + for (const child of el.children) { visit(child); } didVisitChildren = true; }; - const shouldVisitChildren = callback(xml, visitChildren); + const shouldVisitChildren = callback(el, visitChildren); if (shouldVisitChildren !== false && !didVisitChildren) { visitChildren(); } } - } + }; const xmlDoc = typeof xml === "string" ? this.parseXML(xml) : xml; visit(xmlDoc); } + /** + * @param {string} arch + * @returns {Element} + */ parseXML(arch) { - const parser = new DOMParser(); - const xml = parser.parseFromString(arch, "text/xml"); + const cleanedArch = arch.replace(/ /g, ""); + const xml = parser.parseFromString(cleanedArch, "text/xml"); if (hasParsingError(xml)) { throw new Error( `An error occured while parsing ${arch}: ${xml.getElementsByTagName("parsererror")}` @@ -42,3 +57,92 @@ export class XMLParser { return xml.documentElement; } } + +/** + * @param {Element} parent + * @param {Node | Node[] | void} node + */ +export function append(parent, node) { + const nodes = Array.isArray(node) ? node : [node]; + parent.append(...nodes.filter(Boolean)); + return parent; +} + +/** + * Combines the existing value of a node attribute with new given parts. The glue + * is the string used to join the parts. + * + * @param {Element} el + * @param {string} attr + * @param {string | string[]} parts + * @param {string} [glue=" "] + */ +export function combineAttributes(el, attr, parts, glue = " ") { + const allValues = []; + if (el.hasAttribute(attr)) { + allValues.push(el.getAttribute(attr)); + } + allValues.push(...(Array.isArray(parts) ? parts : [parts])); + el.setAttribute(attr, allValues.join(glue)); +} + +/** + * XML equivalent of `document.createElement`. + * + * @param {string} tagName + * @param {...(Iterable | Record)} args + * @returns {Element} + */ +export function createElement(tagName, ...args) { + const el = xmlDocument.createElement(tagName); + for (const arg of args) { + if (!arg) { + continue; + } + if (Symbol.iterator in arg) { + // Children list + el.append(...arg); + } else if (typeof arg === "object") { + // Attributes + for (const name in arg) { + el.setAttribute(name, arg[name]); + } + } + } + return el; +} + +/** + * XML equivalent of `document.createTextNode`. + * + * @param {string} data + * @returns {Text} + */ +export function createTextNode(data) { + return xmlDocument.createTextNode(data); +} + +/** + * Removes the given attributes on the given element and returns them as a dictionnary. + * @param {Element} el + * @param {string[]} attributes + * @returns {Record} + */ +export function extractAttributes(el, attributes) { + const attrs = Object.create(null); + for (const attr of attributes) { + attrs[attr] = el.getAttribute(attr) || ""; + el.removeAttribute(attr); + } + return attrs; +} + +/** + * @param {Node} [node] + * @param {boolean} [lower=false] + * @returns {string} + */ +export function getTag(node, lower = false) { + const tag = (node && node.nodeName) || ""; + return lower ? tag.toLowerCase() : tag; +} diff --git a/addons/web/static/src/fields/fields.js b/addons/web/static/src/fields/fields.js deleted file mode 100644 index c046f062537..00000000000 --- a/addons/web/static/src/fields/fields.js +++ /dev/null @@ -1,113 +0,0 @@ -/** @odoo-module **/ -import { registry } from "@web/core/registry"; -import legacyFieldRegistry from "web.field_registry"; -import owlFieldRegistry from "web.field_registry_owl"; -import { ComponentAdapter } from "web.OwlCompatibility"; - -const { Component, useEffect, xml } = owl; - -const fieldRegistry = registry.category("fields"); - -function getFieldFromRegistry(registry, { fieldType, viewType, fieldName, fieldsDescription }) { - if (viewType && fieldType) { - const specificType = `${viewType}.${fieldType}`; - if (registry.contains(specificType)) { - return registry.get(specificType); - } - } - if (!registry.contains(fieldType)) { - const field = fieldsDescription[fieldName]; - fieldType = field && field.type; - } - return registry.get(fieldType, null); -} - -class Field extends Component { - static getTangibleField({ record, type, name }) { - const FieldClass = getFieldFromRegistry(fieldRegistry, { - fieldName: name, - fieldType: type, - viewType: record.viewtype, - fieldsDescription: record.fields, - }); - return { FieldClass }; - } - - setup() { - const { record, type, name } = this.props; - this.FieldComponent = Field.getTangibleField({ record, type, name }).FieldClass; - } -} - -Field.template = xml/* xml */ ` - -`; - -class FieldSupportsLegacy extends Field { - static getTangibleField({ record, type, fieldName }) { - let { FieldClass } = super.getTangibleField(...arguments); - if (!FieldClass) { - FieldClass = getFieldFromRegistry(owlFieldRegistry, { - fieldName, - fieldType: type, - viewType: record.viewType, - fieldsDescription: record.fields, - }); - if (FieldClass) { - return { FieldClass, isOwlLegacy: true }; - } else { - FieldClass = getFieldFromRegistry(legacyFieldRegistry, { - fieldName, - fieldType: type, - viewType: record.viewType, - fieldsDescription: record.fields, - }); - return { FieldClass, isLegacy: true }; - } - } - return {}; - } - - setup() { - super.setup(); - this.renderId = 0; - useEffect(() => { - this.renderId++; - }); - if (!this.FieldComponent) { - this.env = Component.env; - const { record, type, name } = this.props; - const { FieldClass, isOwlLegacy, isLegacy } = FieldSupportsLegacy.getTangibleField({ - record, - type, - name, - }); - this.FieldComponent = FieldClass; - this.isOwlLegacy = isOwlLegacy; - this.isLegacy = isLegacy; - } - } - - get legacyProps() { - const legacyRecord = Object.assign({}, this.props.model._legacyRecord_); - legacyRecord.data = Object.assign({}, legacyRecord.data, this.props.record.data); - return { - fieldName: this.props.name, - record: legacyRecord, - }; - } - - get widgetArgs() { - const { fieldName, record } = this.legacyProps; - return [fieldName, record]; - } -} -FieldSupportsLegacy.template = xml/* xml */ ` - - - - -`; -FieldSupportsLegacy.components = { ComponentAdapter }; - -export { FieldSupportsLegacy as Field }; diff --git a/addons/web/static/src/legacy/action_adapters.js b/addons/web/static/src/legacy/action_adapters.js index 9765d443cf2..1fdd239e091 100644 --- a/addons/web/static/src/legacy/action_adapters.js +++ b/addons/web/static/src/legacy/action_adapters.js @@ -7,10 +7,9 @@ import { objectToQuery } from "../core/browser/router_service"; import { useDebugCategory } from "../core/debug/debug_context"; import { Dialog } from "../core/dialog/dialog"; import { useService } from "@web/core/utils/hooks"; -import { ViewNotFoundError } from "../webclient/actions/action_service"; +import { ViewNotFoundError } from "@web/views/view"; import { cleanDomFromBootstrap, wrapSuccessOrFail, useLegacyRefs } from "./utils"; import { mapDoActionOptionAPI } from "./backend_utils"; -import { setScrollPosition } from "@web/webclient/actions/scrolling"; const { Component, @@ -94,7 +93,11 @@ class ActionAdapter extends ComponentAdapter { }); this.onScrollTo = (payload) => { - setScrollPosition(this.wowlEnv, { left: payload.left, top: payload.top }); + const contentEl = this.el.querySelector(".o_content"); + if (contentEl) { + contentEl.scrollLeft = payload.left || 0; + contentEl.scrollTop = payload.top || 0; + } }; } @@ -301,7 +304,7 @@ export class ViewAdapter extends ActionAdapter { useDebugCategory("action", debugContext); useDebugCategory("view", debugContext); if (this.props.viewInfo.type === "form") { - useDebugCategory("form", debugContext); + useDebugCategory("form_legacy", debugContext); } this.env = Component.env; } @@ -376,21 +379,21 @@ export class ViewAdapter extends ActionAdapter { async _trigger_up(ev) { const payload = ev.data; if (ev.name === "switch_view") { - try { - const props = {}; - if (payload.mode) { - props.mode = payload.mode; + if (payload.view_type === "form") { + if (payload.res_id) { + return this.props.selectRecord(payload.res_id, { mode: payload.mode }); + } else { + return this.props.createRecord(); } - // if (payload.res_id) { - // if make 'open a record, come back, and create a new record' crash - props.resId = payload.res_id; - // } - await this.actionService.switchView(payload.view_type, props); - } catch (e) { - if (e instanceof ViewNotFoundError) { - return; + } else { + try { + await this.actionService.switchView(payload.view_type); + } catch (e) { + if (typeof e === "object" && e instanceof ViewNotFoundError) { + return; + } + throw e; } - throw e; } } else if (ev.name === "execute_action") { const buttonContext = new Context(payload.action_data.context).eval(); diff --git a/addons/web/static/src/legacy/backend_utils.js b/addons/web/static/src/legacy/backend_utils.js index 6fcaad96ba1..1382cbfe9b9 100644 --- a/addons/web/static/src/legacy/backend_utils.js +++ b/addons/web/static/src/legacy/backend_utils.js @@ -91,6 +91,9 @@ function searchModelStateFromLegacy(state) { newState.sections = sections; //! Can be undefined. See search_model.__legacyParseSearchPanelArchAnyway newState.searchPanelInfo = searchPanelInfo; + if (newState.searchPanelInfo) { + newState.searchPanelInfo.loaded = true; + } } for (const [key, extension] of Object.entries(ActionModel.registry.entries())) { @@ -166,12 +169,15 @@ export function searchModelStateToLegacy(state) { filter.orderedBy = item.orderBy; delete filter.orderBy; break; - case "filter": + case "filter": { let context = item.context; try { context = makeContext([context]); - } catch (_e) {} + } catch (_e) { + // pass + } filter.context = context; + } } filters[item.id] = filter; } diff --git a/addons/web/static/src/legacy/debug_manager.js b/addons/web/static/src/legacy/debug_manager.js index ec823a43bc6..7492e3caaab 100644 --- a/addons/web/static/src/legacy/debug_manager.js +++ b/addons/web/static/src/legacy/debug_manager.js @@ -1,12 +1,11 @@ /** @odoo-module **/ -import { _lt } from "@web/core/l10n/translation"; import { Dialog } from "@web/core/dialog/dialog"; -import { FormViewDialog } from 'web.view_dialogs'; +import { FormViewDialog } from "web.view_dialogs"; import { formatDateTime, parseDateTime } from "@web/core/l10n/dates"; -import { formatMany2one } from "@web/fields/formatters"; +import { formatMany2one } from "@web/views/fields/formatters"; import { registry } from "@web/core/registry"; -import { standaloneAdapter } from 'web.OwlCompatibility'; +import { standaloneAdapter } from "web.OwlCompatibility"; const { Component, onWillStart, useState } = owl; @@ -14,7 +13,7 @@ const debugRegistry = registry.category("debug"); class GetMetadataDialog extends Component { setup() { - super.setup(); + this.title = this.env._t("View Metadata"); this.state = useState({}); onWillStart(this.onWillStart); } @@ -25,7 +24,7 @@ class GetMetadataDialog extends Component { async onClickCreateXmlid() { const context = Object.assign({}, this.context, { - default_module: '__custom__', + default_module: "__custom__", default_res_id: this.state.id, default_model: this.props.res_model, }); @@ -34,9 +33,9 @@ class GetMetadataDialog extends Component { context: context, on_saved: () => this.getMetadata(), disable_multiple_selection: true, - res_model: 'ir.model.data', + res_model: "ir.model.data", }); - dialog.on('dialog_form_loaded', this, () => { + dialog.on("dialog_form_loaded", this, () => { dialog.$el.find('[name="name"]').focus(); }); await dialog.open(); @@ -61,16 +60,20 @@ class GetMetadataDialog extends Component { this.state.creator = formatMany2one(metadata.create_uid); this.state.lastModifiedBy = formatMany2one(metadata.write_uid); this.state.noupdate = metadata.noupdate; - this.state.create_date = formatDateTime(parseDateTime(metadata.create_date), { timezone: true }); - this.state.write_date = formatDateTime(parseDateTime(metadata.write_date), { timezone: true }); + this.state.createDate = formatDateTime(parseDateTime(metadata.create_date), { + timezone: true, + }); + this.state.writeDate = formatDateTime(parseDateTime(metadata.write_date), { + timezone: true, + }); } } GetMetadataDialog.template = "web.DebugMenu.GetMetadataDialog"; GetMetadataDialog.components = { Dialog }; -GetMetadataDialog.title = _lt("View Metadata"); + class SetDefaultDialog extends Component { setup() { - super.setup(); + this.title = this.env._t("Set Defaults"); this.state = { fieldToSet: "", condition: "", @@ -212,11 +215,9 @@ class SetDefaultDialog extends Component { } SetDefaultDialog.template = "web.DebugMenu.SetDefaultDialog"; SetDefaultDialog.components = { Dialog }; -SetDefaultDialog.title = _lt("Set Default"); // Form view items - -export function setDefaults({ action, component, env }) { +function setDefaults({ action, component, env }) { return { type: "item", description: env._t("Set Defaults"), @@ -282,7 +283,7 @@ function manageAttachments({ action, component, env }) { } debugRegistry - .category("form") + .category("form_legacy") .add("setDefaults", setDefaults) .add("viewMetadata", viewMetadata) .add("manageAttachments", manageAttachments); diff --git a/addons/web/static/src/legacy/js/components/action_menus.js b/addons/web/static/src/legacy/js/components/action_menus.js index 221540d4ce8..0a07ab92a99 100644 --- a/addons/web/static/src/legacy/js/components/action_menus.js +++ b/addons/web/static/src/legacy/js/components/action_menus.js @@ -202,7 +202,7 @@ odoo.define('web.ActionMenus', function (require) { }, }, }; - ActionMenus.template = 'web.ActionMenus'; + ActionMenus.template = 'web.Legacy.ActionMenus'; return ActionMenus; }); diff --git a/addons/web/static/src/legacy/js/fields/basic_fields.js b/addons/web/static/src/legacy/js/fields/basic_fields.js index 44dea68bd42..bfafef604a9 100644 --- a/addons/web/static/src/legacy/js/fields/basic_fields.js +++ b/addons/web/static/src/legacy/js/fields/basic_fields.js @@ -1631,7 +1631,7 @@ var FieldText = InputField.extend(TranslatableFieldMixin, { //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- - + /** * @private * @override @@ -4155,7 +4155,7 @@ var FieldColorPicker = FieldInteger.extend({ if (!$colorpicker.length) { return; } - $colorpicker.html(qweb.render('KanbanColorPicker', { colors: this.RECORD_COLORS })); + $colorpicker.html(qweb.render('web.Legacy.KanbanColorPicker', { colors: this.RECORD_COLORS })); $colorpicker.on('click', 'a', this._onColorChanged.bind(this)); }, /** diff --git a/addons/web/static/src/legacy/js/fields/basic_fields_owl.js b/addons/web/static/src/legacy/js/fields/basic_fields_owl.js index c57b68d3a18..9f2fb02af85 100644 --- a/addons/web/static/src/legacy/js/fields/basic_fields_owl.js +++ b/addons/web/static/src/legacy/js/fields/basic_fields_owl.js @@ -152,7 +152,7 @@ odoo.define('web.basic_fields_owl', function (require) { FieldBoolean.components = { CustomCheckbox }; FieldBoolean.description = _lt("Checkbox"); FieldBoolean.supportedFieldTypes = ['boolean']; - FieldBoolean.template = 'web.FieldBoolean'; + FieldBoolean.template = 'web.LegacyFieldBoolean'; FieldBoolean.isQuickEditable = true; diff --git a/addons/web/static/src/legacy/js/fields/relational_fields.js b/addons/web/static/src/legacy/js/fields/relational_fields.js index 1fd63768b23..de9d2aee02b 100644 --- a/addons/web/static/src/legacy/js/fields/relational_fields.js +++ b/addons/web/static/src/legacy/js/fields/relational_fields.js @@ -1363,7 +1363,7 @@ var FieldX2Many = AbstractField.extend(WidgetAdapterMixin, { if (extraInfo.subFieldName) { parts.push(`[name="${extraInfo.subFieldName}"]`); } - + if (parts.length) { const el = this.el.querySelector(parts.join(' ')); if (el) { @@ -2041,7 +2041,7 @@ var FieldOne2Many = FieldX2Many.extend({ /** * @private - * @param {*} data + * @param {*} data */ _addCreateRecordRow(data) { const self = this; @@ -3308,7 +3308,7 @@ var FieldStatus = AbstractField.extend({ */ var FieldSelection = AbstractField.extend({ description: _lt("Selection"), - template: 'FieldSelection', + template: 'web.Legacy.FieldSelection', specialData: "_fetchSpecialRelation", supportedFieldTypes: ['selection'], events: _.extend({}, AbstractField.prototype.events, { diff --git a/addons/web/static/src/legacy/js/views/kanban/kanban_controller.js b/addons/web/static/src/legacy/js/views/kanban/kanban_controller.js index 223705f4b38..84aad47500d 100644 --- a/addons/web/static/src/legacy/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/legacy/js/views/kanban/kanban_controller.js @@ -238,7 +238,7 @@ var KanbanController = BasicController.extend({ const state = this.model.get(this.handle, {raw: true}); if (!state.count || state.isSample) { const classesList = [ - 'o_kanban_view', + 'o_legacy_kanban_view', 'o_kanban_group', 'o_kanban_header', 'o_column_quick_create', diff --git a/addons/web/static/src/legacy/js/views/kanban/kanban_record.js b/addons/web/static/src/legacy/js/views/kanban/kanban_record.js index a797371373c..a507ab98a8d 100644 --- a/addons/web/static/src/legacy/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/legacy/js/views/kanban/kanban_record.js @@ -614,7 +614,7 @@ var KanbanRecord = Widget.extend(WidgetAdapterMixin, { if (!$colorpicker.length) { return; } - $colorpicker.html(QWeb.render('KanbanColorPicker', { colors: KANBAN_RECORD_COLORS})); + $colorpicker.html(QWeb.render('web.Legacy.KanbanColorPicker', { colors: KANBAN_RECORD_COLORS})); $colorpicker.on('click', 'a', this._onColorChanged.bind(this)); }, /** diff --git a/addons/web/static/src/legacy/js/views/kanban/kanban_renderer.js b/addons/web/static/src/legacy/js/views/kanban/kanban_renderer.js index 5703e804ada..80f2a2a5128 100644 --- a/addons/web/static/src/legacy/js/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/legacy/js/views/kanban/kanban_renderer.js @@ -83,7 +83,7 @@ function transformQwebTemplate(node, fields) { } var KanbanRenderer = BasicRenderer.extend({ - className: 'o_kanban_view', + className: 'o_legacy_kanban_view', config: { // the KanbanRecord and KanbanColumn classes to use (may be overridden) KanbanColumn: KanbanColumn, KanbanRecord: KanbanRecord, @@ -99,7 +99,7 @@ var KanbanRenderer = BasicRenderer.extend({ 'keydown .o_kanban_record' : '_onRecordKeyDown' }), sampleDataTargets: [ - '.o_kanban_counter', + '.o_legacy_kanban_counter', '.o_kanban_record', '.o_kanban_toggle_fold', '.o_column_folded', diff --git a/addons/web/static/src/legacy/js/views/view_dialogs.js b/addons/web/static/src/legacy/js/views/view_dialogs.js index 669b09d8185..23b7b5fa502 100644 --- a/addons/web/static/src/legacy/js/views/view_dialogs.js +++ b/addons/web/static/src/legacy/js/views/view_dialogs.js @@ -400,7 +400,7 @@ var SelectCreateDialog = ViewDialog.extend({ var view = new ViewClass(fieldsViews[this.viewType], _.extend(viewOptions, { action: { controlPanelFieldsView: fieldsViews.search, - help: markup(_.str.sprintf("

%s

", _t("No records found!"))), + help: markup(_.str.sprintf("

%s

", _.escape(_t("No records found!")))), }, action_buttons: false, dynamicFilters: this.options.dynamicFilters, diff --git a/addons/web/static/src/legacy/legacy_fields.js b/addons/web/static/src/legacy/legacy_fields.js new file mode 100644 index 00000000000..8f086f22cb4 --- /dev/null +++ b/addons/web/static/src/legacy/legacy_fields.js @@ -0,0 +1,314 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { sprintf } from "@web/core/utils/strings"; +import { Domain } from "web.Domain"; +import legacyFieldRegistry from "web.field_registry"; +import { ComponentAdapter } from "web.OwlCompatibility"; +import viewUtils from "web.viewUtils"; + +const { Component, useEffect, xml } = owl; +const fieldRegistry = registry.category("fields"); + +const legacyFieldTemplate = xml` + `; + +// ----------------------------------------------------------------------------- +// FieldAdapter +// ----------------------------------------------------------------------------- + +class FieldAdapter extends ComponentAdapter { + setup() { + super.setup(); + this.wowlEnv = this.env; + this.env = Component.env; + useEffect(() => { + // if classNames are given to the field, we only want + // to add those classes to the legacy field without + // the parent element affecting the style of the field + const fieldClassnames = + this.props.fieldParams.options && + this.props.fieldParams.options.attrs && + this.props.fieldParams.options.attrs.class; + if (fieldClassnames) { + for (const className of fieldClassnames.split(" ")) { + this.widgetEl.parentElement.classList.remove(className); + } + this.widgetEl.parentElement.style.display = "contents"; + } + this.widgetEl.parentElement.classList.remove("o_text_overflow"); + this.widgetEl.classList.add("o_legacy_field_widget"); + }); + this.lastFieldChangedEvent = null; + } + + /** + * @override + */ + get widgetArgs() { + const { name, record, options } = this.props.fieldParams; + return [name, record, options]; + } + + updateWidget(nextProps) { + const { name, record, options } = nextProps.fieldParams; + if (this.widget.mode !== options.mode) { + // the mode changed, we need to instantiate a new FieldWidget + if (this.oldWidget) { + this.widget.destroy(); // we were already updating -> abort, and start over + } else { + this.oldWidget = this.widget; + } + this.widget = new this.props.Component(this, name, record, options); + return this.widget._widgetRenderAndInsert(() => {}); + } else { + // the mode is the same, simply reset the FieldWidget with the new record + return this.widget.reset(record, this.lastFieldChangedEvent); + } + } + + renderWidget() { + if (this.oldWidget) { + const parentEl = this.oldWidget.el.parentElement; + parentEl.replaceChild(this.widget.el, this.oldWidget.el); + this.widgetEl = this.widget.el; + if (this.widget.on_attach_callback) { + this.widget.on_attach_callback(); + } + this.oldWidget.destroy(); + this.oldWidget = null; + } + } + + async _trigger_up(ev) { + const evType = ev.name; + const payload = ev.data; + if (evType === "set_dirty") { + // TODO (not yet handled in the Record datapoint) + } else if (evType === "field_changed") { + const { name, record } = this.props.fieldParams; + const proms = []; + for (const fieldName in payload.changes) { + let value = payload.changes[fieldName]; + const fieldType = record.fields[name].type; + if (fieldType === "many2one" && value) { + value = [value.id, value.display_name]; + } else if (fieldType === "one2many" || fieldType === "many2many") { + // TODO map all operations + if (value.operation === "ADD_M2M") { + const valueIds = value.ids; // can be a lot of stuff + let newIds = []; + if (Array.isArray(valueIds)) { + if (typeof valueIds[0] === "number") { + newIds = valueIds; // not sure if it is a real case: a list of ids + } else if (valueIds.length && "id" in valueIds[0]) { + newIds = valueIds.map((r) => r.id); + } + } else if ("id" in valueIds) { + newIds = [valueIds.id]; + } + value = { + resIds: [...record.data[name].res_ids, ...newIds], + operation: "REPLACE_WITH", + }; + } + } else if (fieldType === "date" || fieldType === "datetime") { + // from moment to luxon + value = value ? luxon.DateTime.fromISO(value.toISOString()) : false; + } + proms.push(this.props.update(fieldName, value)); + } + this.lastFieldChangedEvent = ev; + await Promise.all(proms); + if (payload.onSuccess) { + payload.onSuccess(); + } + // TODO: handle onFailure? + } else if (evType === "reload") { + const record = this.props.record; + if (payload.db_id === record.id) { + await record.load(); + } else { + await record.model.root.load(); + } + record.model.notify(); + if (payload.onSuccess) { + payload.onSuccess(); + } + } else if (evType === "history_back") { + return this.wowlEnv.config.historyBack(); + } + super._trigger_up(...arguments); + } +} + +// ----------------------------------------------------------------------------- +// Helpers to map wowl datapoints to basic model datapoints +// ----------------------------------------------------------------------------- + +let nextId = 1; +function createMany2OneDatapoint([resId, displayName]) { + return { + id: `m2o_${nextId++}`, + data: { id: resId, display_name: displayName }, + fields: { + display_name: { type: "char" }, + id: { type: "integer" }, + }, + res_id: resId, + ref: resId, + }; +} + +function mapDatapoint(datapoint) { + return { + id: datapoint.id, + model: datapoint.resModel, + fields: datapoint.fields, + fieldsInfo: { + default: datapoint.activeFields, // TODO: how to handle viewTypes? + }, + getContext: () => [], // TODO: not yet handled in wowl + getDomain: () => [], // TODO: not yet handled in wowl + getFieldNames: () => datapoint.fieldNames, + }; +} + +function mapStaticListDatapoint(staticList) { + return { + ...mapDatapoint(staticList), + res_ids: staticList.currentIds, + data: staticList.records.map(mapRecordDatapoint), + groupedBy: [], + orderedBy: staticList.orderBy, + }; +} + +export function mapRecordDatapoint(record) { + const data = Object.assign({}, record.data); + for (const fieldName of record.fieldNames) { + const fieldType = record.fields[fieldName].type; + switch (fieldType) { + case "one2many": + case "many2many": + data[fieldName] = mapStaticListDatapoint(data[fieldName]); + break; + case "many2one": + data[fieldName] = data[fieldName] && createMany2OneDatapoint(data[fieldName]); + break; + case "date": + case "datetime": + // from luxon to moment + data[fieldName] = data[fieldName] ? moment(data[fieldName].toISO()) : false; + } + } + data.id = record.resId; + const basicModelRecord = { + ...mapDatapoint(record), + res_id: record.resId, + data, + }; + Object.defineProperty(basicModelRecord, "evalContext", { + get: () => record.evalContext, + }); + Object.defineProperty(basicModelRecord, "isDirty", { + get: () => record.isDirty, + }); + Object.defineProperty(basicModelRecord, "isNew", { + get: () => record.isDirty, + }); + basicModelRecord.evalModifiers = (modifiers) => { + let evalContext = null; + const evaluated = {}; + for (const k of ["invisible", "column_invisible", "readonly", "required"]) { + const mod = modifiers[k]; + if (mod === undefined || mod === false || mod === true) { + if (k in modifiers) { + evaluated[k] = !!mod; + } + continue; + } + try { + evalContext = evalContext || basicModelRecord.evalContext; + evaluated[k] = new Domain(mod, evalContext).compute(evalContext); + } catch (e) { + throw new Error(sprintf('for modifier "%s": %s', k, e.message)); + } + } + return evaluated; + }; + return basicModelRecord; +} + +// ----------------------------------------------------------------------------- +// Register legacy field widgets to the wowl field registry (wrapped in a Component) +// ----------------------------------------------------------------------------- + +function registerField(name, LegacyFieldWidget) { + class LegacyField extends Component { + setup() { + this.FieldWidget = LegacyFieldWidget; + } + + get fieldParams() { + const { name, record } = this.props; + if (record.model.__bm__) { + const legacyRecord = record.model.__bm__.get(record.__bm_handle__); + const options = { + viewType: legacyRecord.viewType, + hasReadonlyModifier: record.isReadonly(name), + mode: record.mode, + }; + return { name, record: legacyRecord, options }; + } else { + const legacyRecord = mapRecordDatapoint(record); + const fieldInfo = record.activeFields[name]; + const views = {}; + for (const viewType in fieldInfo.views || {}) { + let arch = fieldInfo.views[viewType].arch; + if (viewType === "list") { + const editable = fieldInfo.views.list.editable; + arch = ``; + } + // FIXME: need the legacy fieldInfo here + views[viewType] = { + ...fieldInfo.views[viewType], + arch: viewUtils.parseArch(arch), + }; + } + const options = { + attrs: { + ...fieldInfo.rawAttrs, + modifiers: JSON.parse(fieldInfo.rawAttrs.modifiers || "{}"), + options: fieldInfo.options, + widget: fieldInfo.widget, + views, + mode: fieldInfo.viewMode, + }, + mode: record.mode, + }; + return { name, record: legacyRecord, options }; + } + } + + update(fieldName, value) { + if (fieldName === this.props.name) { + return this.props.update(value); + } else { + return this.props.record.update({ [fieldName]: value }); + } + } + } + LegacyField.template = legacyFieldTemplate; + LegacyField.components = { FieldAdapter }; + LegacyField.fieldsToFetch = LegacyFieldWidget.prototype.fieldsToFetch || {}; + if (!fieldRegistry.contains(name)) { + fieldRegistry.add(name, LegacyField); + } +} + +// register fields already in the legacy registry, and listens to future registrations +for (const [name, FieldWidget] of Object.entries(legacyFieldRegistry.entries())) { + registerField(name, FieldWidget); +} +legacyFieldRegistry.onAdd(registerField); diff --git a/addons/web/static/src/legacy/legacy_fields.scss b/addons/web/static/src/legacy/legacy_fields.scss new file mode 100644 index 00000000000..33d9188634f --- /dev/null +++ b/addons/web/static/src/legacy/legacy_fields.scss @@ -0,0 +1,3 @@ +.o_legacy_field_widget > * { + background-color: rgba(255, 0, 0, 0.1); +} diff --git a/addons/web/static/src/legacy/legacy_view_widgets.js b/addons/web/static/src/legacy/legacy_view_widgets.js new file mode 100644 index 00000000000..89b6970c050 --- /dev/null +++ b/addons/web/static/src/legacy/legacy_view_widgets.js @@ -0,0 +1,101 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { ComponentAdapter } from "web.OwlCompatibility"; +import legacyWidgetRegistry from "web.widget_registry"; +import { mapRecordDatapoint } from "./legacy_fields"; + +const { Component, useEffect, xml } = owl; +const viewWidgetRegistry = registry.category("view_widgets"); + +const legacyWidgetTemplate = xml` + `; + +// ---------------------------------------------------------------------------- +// ViewWidgetAdapter +// ---------------------------------------------------------------------------- + +class ViewWidgetAdapter extends ComponentAdapter { + setup() { + super.setup(); + this.wowlEnv = this.env; + this.env = Component.env; + useEffect(() => { + this.widgetEl.parentElement.classList.remove("o_text_overflow"); + this.widgetEl.classList.add("o_legacy_widget"); + }); + } + + /** + * @override + */ + get widgetArgs() { + return this.getParams(this.props); + } + + getParams(props) { + const { record, node, options, readonly } = props.viewWidgetParams; + return [record, node, { mode: readonly ? "readonly" : "edit", ...options }]; + } + + updateWidget(nextProps) { + if (this.oldWidget) { + this.widget.destroy(); // we were already updating -> abort, and start over + } else { + this.oldWidget = this.widget; + } + this.widget = new this.props.Component(this, ...this.getParams(nextProps)); + return this.widget._widgetRenderAndInsert(() => {}); + } + + renderWidget() { + if (this.oldWidget) { + const parentEl = this.oldWidget.el.parentElement; + parentEl.replaceChild(this.widget.el, this.oldWidget.el); + this.widgetEl = this.widget.el; + if (this.widget.on_attach_callback) { + this.widget.on_attach_callback(); + } + this.oldWidget.destroy(); + this.oldWidget = null; + } + } +} + +// ---------------------------------------------------------------------------- +// Register legacy widgets to the wowl widget registry (wrapped in a Component) +// ---------------------------------------------------------------------------- + +function registerWidget(name, LegacyWidgetWidget) { + class LegacyViewWidget extends Component { + setup() { + this.ViewWidget = LegacyWidgetWidget; + } + get viewWidgetParams() { + const { record } = this.props; + let legacyRecord; + if (record.model.__bm__) { + legacyRecord = record.model.__bm__.get(record.__bm_handle__); + } else { + legacyRecord = mapRecordDatapoint(record); + } + const options = { + viewType: legacyRecord.viewType, + mode: record.mode, + }; + return { ...this.props, record: legacyRecord, options }; + } + } + LegacyViewWidget.template = legacyWidgetTemplate; + LegacyViewWidget.components = { ViewWidgetAdapter }; + if (!viewWidgetRegistry.contains(name)) { + console.log(`View widgets: using legacy ${name} Widget`); + viewWidgetRegistry.add(name, LegacyViewWidget); + } +} + +// register widgets already in the legacy registry, and listens to future registrations +for (const [name, Widget] of Object.entries(legacyWidgetRegistry.entries())) { + registerWidget(name, Widget); +} +legacyWidgetRegistry.onAdd(registerWidget); diff --git a/addons/web/static/src/legacy/legacy_views.js b/addons/web/static/src/legacy/legacy_views.js index b8c167c87e6..f326d98ebf2 100644 --- a/addons/web/static/src/legacy/legacy_views.js +++ b/addons/web/static/src/legacy/legacy_views.js @@ -27,11 +27,14 @@ function getJsClassWidget(fieldsInfo) { const legacyViewTemplate = xml` `; + widget="widget" onReverseBreadcrumb="onReverseBreadcrumb" selectRecord="props.selectRecord" createRecord="props.createRecord"/>`; // registers a view from the legacy view registry to the wowl one, but wrapped // into an Owl Component function registerView(name, LegacyView) { + if (viewRegistry.contains(name)) { + return; + } class Controller extends LegacyComponent { setup() { this.vm = useService("view"); @@ -51,11 +54,13 @@ function registerView(name, LegacyView) { // always add user context to the action context this.user = useService("user"); - const action = Object.assign({}, this.props.action, { - context: Object.assign({}, this.user.context, this.props.action.context), - }); + const action = Object.assign({}, this.props.action); + action.context = Object.assign({}, this.user.context, action.context); - const { actionFlags, breadcrumbs } = this.env.config; + const { actionFlags, breadcrumbs, noBreadcrumbs } = this.env.config; + if (noBreadcrumbs) { + action.context.no_breadcrumbs = true; + } this.viewParams = Object.assign({}, actionFlags, { action, // legacy views automatically add the last part of the breadcrumbs @@ -90,6 +95,11 @@ function registerView(name, LegacyView) { this.onReverseBreadcrumb = this.props.state && this.props.state.__on_reverse_breadcrumb__; useSetupAction({ + rootRef: { + get el() { + return legacyRefs.widget && legacyRefs.widget.el; + }, + }, beforeLeave: () => legacyRefs.widget.canBeRemoved(), getGlobalState: () => getGlobalState(legacyRefs.component.exportState()), getLocalState: () => getLocalState(legacyRefs.component.exportState()), @@ -100,7 +110,7 @@ function registerView(name, LegacyView) { async onWillStart() { const params = { resModel: this.props.resModel, - views: this.props.views, + views: this.props.views || this.env.config.views, context: this.props.context, }; const options = { @@ -110,13 +120,13 @@ function registerView(name, LegacyView) { }; const viewDescriptions = await this.vm.loadViews(params, options); const result = viewDescriptions.__legacy__; - const fieldsInfo = result.fields_views[this.props.type]; + const fieldsInfo = result.fields_views[this.props.type || this.env.config.viewType]; const jsClass = getJsClassWidget(fieldsInfo); this.View = jsClass || this.View; this.viewInfo = Object.assign({}, fieldsInfo, { fields: result.fields, viewFields: fieldsInfo.fields, - type: this.props.type, + type: this.props.type || this.env.config.viewType, }); let controlPanelFieldsView; if (result.fields_views.search) { @@ -127,7 +137,8 @@ function registerView(name, LegacyView) { }); } const { viewSwitcherEntries = [] } = this.env.config; - const views = this.viewParams.action.views + const _views = this.viewParams.action.views || this.env.config.views; + const views = _views .filter(([, vtype]) => vtype !== "search") .map(([vid, vtype]) => { const view = viewSwitcherEntries.find((v) => v.type === vtype); @@ -137,29 +148,32 @@ function registerView(name, LegacyView) { return { viewID: vid, type: vtype, - multiRecord: !this.constructor.multiRecord, + multiRecord: !legacyView.multiRecord, }; } }); this.viewParams.action = Object.assign({}, this.viewParams.action, { controlPanelFieldsView, - _views: this.viewParams.action.views, + _views, views, }); } } - Controller.template = legacyViewTemplate; + Controller.template = legacyViewTemplate; Controller.components = { ViewAdapter }; - Controller.display_name = LegacyView.prototype.display_name; - Controller.icon = LegacyView.prototype.icon; - Controller.isMobileFriendly = LegacyView.prototype.mobile_friendly; - Controller.multiRecord = LegacyView.prototype.multi_record; - Controller.type = LegacyView.prototype.viewType; - Controller.isLegacy = true; - if (!viewRegistry.contains(name)) { - viewRegistry.add(name, Controller); - } + Controller.isLegacy = true; // for action dialog + + const legacyView = { + type: LegacyView.prototype.viewType, + display_name: LegacyView.prototype.display_name, + icon: LegacyView.prototype.icon, + isMobileFriendly: LegacyView.prototype.mobile_friendly, + multiRecord: LegacyView.prototype.multi_record, + isLegacy: true, + Controller, + }; + viewRegistry.add(name, legacyView); } // register views already in the legacy registry, and listens to future registrations diff --git a/addons/web/static/src/legacy/scss/fields.scss b/addons/web/static/src/legacy/scss/fields.scss index 91d9c21a069..68a3f368efa 100644 --- a/addons/web/static/src/legacy/scss/fields.scss +++ b/addons/web/static/src/legacy/scss/fields.scss @@ -322,7 +322,7 @@ } // Stars - &.o_priority { + &.o_priority, & .o_priority { display: inline-block; padding: 0; margin: 0; @@ -349,7 +349,7 @@ } // Favorite - &.o_favorite { + &.o_favorite, & .o_favorite { i.fa { font-size: 16px; } @@ -367,7 +367,7 @@ } // Handle widget - &.o_row_handle { + .o_row_handle { @include o-grab-cursor; color: #adb5bd; text-align: center; @@ -477,7 +477,7 @@ } // Image - &.o_field_image { + &.o_field_image, &.o_field_image_url { position: relative; .o_form_image_controls { @@ -577,7 +577,6 @@ right: 0; } &.o_btn_char_copy { - padding-top: 2px; height: 100%; } } @@ -596,18 +595,6 @@ display: inline-block; } - &.o_field_badge { - border: 0; - font-size: 12px; - user-select: none; - background-color: rgba(lightgray, 0.5); - font-weight: 500; - @include o-text-overflow; - transition: none; // remove transition to prevent badges from flickering at reload - &.o_field_empty { - display: none !important; - } - } } span.o_field_copy:empty { diff --git a/addons/web/static/src/legacy/scss/form_view.scss b/addons/web/static/src/legacy/scss/form_view.scss index 24f0d23349f..162a0d1086a 100644 --- a/addons/web/static/src/legacy/scss/form_view.scss +++ b/addons/web/static/src/legacy/scss/form_view.scss @@ -886,7 +886,7 @@ $o-form-label-margin-right: 0px; } // One2Many Kanban views - .o_field_widget .o_kanban_view.o_kanban_ungrouped { + .o_field_widget .o_legacy_kanban_view.o_kanban_ungrouped { padding: 0; .o_kanban_record { box-shadow: none; @@ -1061,7 +1061,7 @@ $o-form-label-margin-right: 0px; } // Settings form views -.o_settings_container { +.o_legacy_settings_container { display: flex; flex: 0 1 auto; flex-flow: row wrap; diff --git a/addons/web/static/src/legacy/scss/kanban_column_progressbar.scss b/addons/web/static/src/legacy/scss/kanban_column_progressbar.scss index 36f3b508fe8..22ac8e1f4a8 100644 --- a/addons/web/static/src/legacy/scss/kanban_column_progressbar.scss +++ b/addons/web/static/src/legacy/scss/kanban_column_progressbar.scss @@ -41,20 +41,20 @@ } // If columns has the progressbar, adapt the layout -.o_kanban_view .o_kanban_group.o_kanban_has_progressbar { +.o_legacy_kanban_view .o_kanban_group.o_kanban_has_progressbar { > .o_kanban_header .o_kanban_header_title { height: $o-kanban-header-title-height*0.6; margin-top: 5px; } &.o_kanban_no_records { - .o_kanban_counter { + .o_legacy_kanban_counter { opacity: 0.3; } } } -.o_kanban_counter { +.o_legacy_kanban_counter { position: relative; display: flex; align-items: center; @@ -102,11 +102,11 @@ } } } -.o_column_folded .o_kanban_counter { +.o_column_folded .o_legacy_kanban_counter { display: none; } -.o_kanban_view .o_kanban_group { +.o_legacy_kanban_view .o_kanban_group { @include o-kanban-css-filter(success, theme-color('success')); @include o-kanban-css-filter(warning, theme-color('warning')); @include o-kanban-css-filter(danger, theme-color('danger')); diff --git a/addons/web/static/src/legacy/scss/kanban_dashboard.scss b/addons/web/static/src/legacy/scss/kanban_dashboard.scss index 40c2508f27c..587df3fa402 100644 --- a/addons/web/static/src/legacy/scss/kanban_dashboard.scss +++ b/addons/web/static/src/legacy/scss/kanban_dashboard.scss @@ -1,5 +1,5 @@ -.o_kanban_view.o_kanban_dashboard { +.o_legacy_kanban_view.o_kanban_dashboard { &:not(.o_kanban_grouped) { // correctly display the no_content_helper in dashboards flex-flow: row wrap; @@ -31,8 +31,6 @@ } .o_kanban_card_manage_pane { - @include o-kanban-dropdown-menu; - // Arbitrary value to place the dropdown-menu exactly below the // dropdown-toggle (height is forced so that it works on Firefox) top: $o-kanban-manage-toggle-height; diff --git a/addons/web/static/src/legacy/scss/kanban_examples_dialog.scss b/addons/web/static/src/legacy/scss/kanban_examples_dialog.scss index 503cf4f0acb..5c17f6c7a5d 100644 --- a/addons/web/static/src/legacy/scss/kanban_examples_dialog.scss +++ b/addons/web/static/src/legacy/scss/kanban_examples_dialog.scss @@ -1,5 +1,5 @@ // container ghost card in modal -.o_kanban_examples_dialog { +.o_legacy_kanban_examples_dialog{ display: flex; padding: 0px; background: theme-color('light'); diff --git a/addons/web/static/src/legacy/scss/kanban_view.scss b/addons/web/static/src/legacy/scss/kanban_view.scss index 399bcde90fe..e8097edcdf2 100644 --- a/addons/web/static/src/legacy/scss/kanban_view.scss +++ b/addons/web/static/src/legacy/scss/kanban_view.scss @@ -1,5 +1,5 @@ // ------- Kanban View Layout ------- -.o_kanban_view { +.o_legacy_kanban_view { display: flex; align-content: stretch; @@ -249,7 +249,6 @@ @include o-kanban-dropdown-open; } .dropdown-menu { - @include o-kanban-dropdown-menu; min-width: 11rem; } } @@ -619,6 +618,7 @@ &.o_kanban_ghost { height: 0; + border: none; min-height: 0!important; // to prevent view writers to override this height visibility: hidden; margin-top: 0; @@ -645,7 +645,7 @@ } // ----------------- Set Cover Dialog ----------------- -.modal .o_kanban_cover_container .o_kanban_cover_image { +.modal .o_legacy_kanban_cover_container .o_kanban_cover_image { border: 1px solid $border-color; height: 120px; width: 120px; diff --git a/addons/web/static/src/legacy/scss/list_view.scss b/addons/web/static/src/legacy/scss/list_view.scss index 0a6a4888232..ebebeb8e1f6 100644 --- a/addons/web/static/src/legacy/scss/list_view.scss +++ b/addons/web/static/src/legacy/scss/list_view.scss @@ -372,7 +372,7 @@ } // Buttons in ControlPanel -.o_list_buttons { +.o_legacy_list_buttons { .o_list_button_save, .o_list_button_discard { display: none; } diff --git a/addons/web/static/src/legacy/scss/utils.scss b/addons/web/static/src/legacy/scss/utils.scss index 3fc0e09322b..ba314b269ef 100644 --- a/addons/web/static/src/legacy/scss/utils.scss +++ b/addons/web/static/src/legacy/scss/utils.scss @@ -348,12 +348,6 @@ border-color: $border-color; z-index: $zindex-dropdown + 1; } -@mixin o-kanban-dropdown-menu { - @include o-position-absolute($right: -1px); - margin-top: -2px; - border-color: $border-color; - border-top-right-radius: 0; -} @mixin o-kanban-colorpicker { max-width: 150px; padding: 3px ($o-dropdown-hpadding - $o-kanban-inner-hmargin) 3px $o-dropdown-hpadding; diff --git a/addons/web/static/src/legacy/scss/views.scss b/addons/web/static/src/legacy/scss/views.scss index f5af47f0f1c..5cc5407cc75 100644 --- a/addons/web/static/src/legacy/scss/views.scss +++ b/addons/web/static/src/legacy/scss/views.scss @@ -3,6 +3,10 @@ * all renderers. For field default rules, see the fields.scss file. */ +.o_legacy_kanban_view { + border-top: 2px solid red; +} + // Invisible modifier (can be inside the view, the button area, ...) .o_invisible_modifier { display: none!important; diff --git a/addons/web/static/src/legacy/xml/base.xml b/addons/web/static/src/legacy/xml/base.xml index 40570509185..7417c09d2ef 100644 --- a/addons/web/static/src/legacy/xml/base.xml +++ b/addons/web/static/src/legacy/xml/base.xml @@ -396,7 +396,7 @@
- +
-