From 9f4622492c5cd84847368d32bf02b0b538a6dd69 Mon Sep 17 00:00:00 2001 From: "Michael (mcm)" Date: Fri, 3 Feb 2023 11:20:18 +0000 Subject: [PATCH] [REF] *: register field descriptors instead of components Before this commit, the field's description was stored on the component and this component was then registered. Now, an object describing the field is used on registration the same way as it is done for views since https://github.com/odoo/odoo/commit/b828cfc72c587d0b73fcc5459695705640437671. This split the component's description (props, template, ...) of the field's description (displayName, supportedTypes, ...) and makes it clearer. closes odoo/odoo#112498 Task: 3171520 Related: odoo/enterprise#37105 Signed-off-by: Aaron Bohy (aab) --- .../account_payment_field.js | 8 +- .../account_resequence_field.js | 4 +- .../account_type_selection.js | 9 +- .../autosave_many2many_tags.js | 9 +- .../grouped_view_widget.js | 4 +- .../journal_dashboard_activity.js | 6 +- .../open_move_line_move_widget.js | 9 +- .../open_move_widget/open_move_widget.js | 4 +- .../section_and_note_fields_backend.js | 24 ++- .../src/components/tax_totals/tax_totals.js | 6 +- .../analytic_distribution.js | 20 ++- .../static/src/password_field.js | 10 +- .../static/src/barcode_handler_field.js | 6 +- .../static/src/float_scannable_field.js | 9 +- .../src/components/iban_widget/iban_widget.js | 9 +- .../src/views/fields/many2many_attendee.js | 16 +- .../src/components/mondialrelay_field.js | 6 +- .../icon_selection_field.js | 16 +- .../background_image/background_image.js | 9 +- .../work_permit_upload/work_permit_upload.js | 9 +- .../many2many_avatar_employee_field.js | 37 +++- .../many2one_avatar_employee_field.js | 28 ++- .../radio_image_field/radio_image_field.js | 7 +- .../static/src/fields/hr_org_chart.js | 9 +- .../src/applicant_char/applicant_char.js | 10 +- .../static/src/fields/resume_one2many.js | 10 +- .../static/src/fields/skills_one2many.js | 9 +- .../task_with_hours/task_with_hours.js | 7 +- .../components/timesheet_uom/timesheet_uom.js | 8 +- .../timesheet_uom_no_toggle.js | 9 +- .../im_livechat_chatbot_script_answers_m2m.js | 12 +- .../js/im_livechat_chatbot_steps_one2many.js | 9 +- .../filterable_selection_field.js | 18 +- .../src/js/loyalty_control_panel_widget.js | 9 +- .../activity_exception/activity_exception.js | 6 +- .../kanban_field_activity_view_container.js | 16 +- .../list_field_activity_view_container.js | 16 +- .../mail/static/src/js/onchange_on_keydown.js | 36 ++-- .../views/activity/activity_arch_parser.js | 2 +- .../emojis_char_field/emojis_char_field.js | 12 +- .../emojis_text_field/emojis_text_field.js | 12 +- .../many2many_avatar_user_field.js | 26 ++- .../many2many_tags_email.js | 18 +- .../many2one_avatar_user_field.js | 31 ++-- .../static/src/js/mailing_m2o_filter.js | 19 +- .../static/src/js/mass_mailing_html_field.js | 24 +-- .../src/views/fields/google_slides_viewer.js | 11 +- .../static/src/widgets/mrp_should_consume.js | 11 +- .../src/widgets/mrp_workorder_popover.js | 11 +- addons/mrp/static/src/widgets/timer.js | 8 +- .../src/js/partner_autocomplete_fieldchar.js | 9 +- .../src/js/partner_autocomplete_many2one.js | 9 +- .../fields/portal_wizard_user_one2many.js | 9 +- .../project_private_task_many2one_field.js | 9 +- .../project_state_selection.js | 12 +- ...oject_status_with_color_selection_field.js | 9 +- ...task_name_with_subtask_count_char_field.js | 7 +- .../project_task_priority_switch_field.js | 9 +- .../static/src/js/purchase_product_field.js | 9 +- .../purchase_order_alternatives_widget.js | 9 +- .../static/src/section_one2many_field.js | 7 +- .../static/src/js/product_discount_field.js | 11 +- .../sale/static/src/js/sale_product_field.js | 9 +- .../static/src/js/sale_progressbar_field.js | 12 +- .../static/src/js/sale_order_many2one.js | 9 +- .../components/so_line_field/so_line_field.js | 21 ++- .../src/components/phone_field/phone_field.js | 14 +- .../src/components/sms_button/sms_button.js | 1 + .../sms_widget/fields_sms_widget.js | 19 +- .../views/picking_form/stock_move_one2many.js | 9 +- .../src/widgets/counted_quantity_widget.js | 9 +- .../static/src/widgets/forecast_widget.js | 9 +- .../stock/static/src/widgets/json_widget.js | 21 ++- .../static/src/widgets/popover_widget.js | 9 +- .../src/widgets/stock_rescheduling_popover.js | 11 +- .../question_page/description_page_field.js | 7 +- .../question_page_one2many_field.js | 9 +- .../core/debug/profiling/profiling_qweb.js | 69 ++++--- .../src/views/basic_relational_model.js | 3 +- .../static/src/views/fields/ace/ace_field.js | 32 ++-- .../attachment_image_field.js | 15 +- .../src/views/fields/badge/badge_field.js | 17 +- .../badge_selection/badge_selection_field.js | 22 +-- .../src/views/fields/binary/binary_field.js | 40 ++--- .../src/views/fields/boolean/boolean_field.js | 22 +-- .../boolean_favorite_field.js | 38 ++-- .../fields/boolean_icon/boolean_icon_field.js | 25 +-- .../boolean_toggle/boolean_toggle_field.js | 14 +- .../list_boolean_toggle_field.js | 11 +- .../src/views/fields/char/char_field.js | 78 ++++---- .../src/views/fields/color/color_field.js | 19 +- .../fields/color_picker/color_picker_field.js | 24 +-- .../fields/copy_clipboard/copy_button.js | 17 +- .../copy_clipboard/copy_clipboard_field.js | 91 ++++++---- .../src/views/fields/date/date_field.js | 41 ++--- .../views/fields/daterange/daterange_field.js | 32 ++-- .../views/fields/datetime/datetime_field.js | 41 ++--- .../src/views/fields/domain/domain_field.js | 42 ++--- .../src/views/fields/email/email_field.js | 36 ++-- addons/web/static/src/views/fields/field.js | 50 +++--- addons/web/static/src/views/fields/field.xml | 2 +- .../static/src/views/fields/field_tooltip.js | 2 +- .../src/views/fields/float/float_field.js | 71 ++++---- .../fields/float_factor/float_factor_field.js | 43 +++-- .../fields/float_time/float_time_field.js | 28 +-- .../fields/float_toggle/float_toggle_field.js | 71 ++++---- .../font_selection/font_selection_field.js | 28 +-- .../src/views/fields/handle/handle_field.js | 20 ++- .../src/views/fields/html/html_field.js | 13 +- .../iframe_wrapper/iframe_wrapper_field.js | 18 +- .../src/views/fields/image/image_field.js | 57 +++--- .../views/fields/image_url/image_url_field.js | 34 ++-- .../src/views/fields/integer/integer_field.js | 38 ++-- .../journal_dashboard_graph_field.js | 24 +-- .../label_selection/label_selection_field.js | 32 ++-- .../many2many_binary_field.js | 43 ++--- .../many2many_checkboxes_field.js | 24 +-- .../kanban_many2many_tags_field.js | 11 +- .../many2many_tags/many2many_tags_field.js | 170 +++++++++--------- .../views/fields/many2many_tags/tags_list.js | 29 +-- .../many2many_tags_avatar_field.js | 29 ++- .../views/fields/many2one/many2one_field.js | 128 ++++++------- .../many2one_avatar/many2one_avatar_field.js | 27 +-- .../many2one_barcode_field.js | 34 ++-- .../many2one_reference_field.js | 4 +- .../views/fields/monetary/monetary_field.js | 42 ++--- .../fields/pdf_viewer/pdf_viewer_field.js | 34 ++-- .../fields/percent_pie/percent_pie_field.js | 28 +-- .../fields/percentage/percentage_field.js | 53 +++--- .../src/views/fields/phone/phone_field.js | 39 ++-- .../views/fields/priority/priority_field.js | 30 ++-- .../progress_bar/kanban_progress_bar_field.js | 9 +- .../fields/progress_bar/progress_bar_field.js | 38 ++-- .../properties/kanban_properties_field.js | 11 +- .../fields/properties/properties_field.js | 42 ++--- .../src/views/fields/radio/radio_field.js | 66 +++---- .../views/fields/reference/reference_field.js | 64 +++---- .../remaining_days/remaining_days_field.js | 17 +- .../views/fields/selection/selection_field.js | 30 ++-- .../views/fields/signature/signature_field.js | 33 ++-- .../views/fields/stat_info/stat_info_field.js | 59 +++--- .../state_selection/state_selection_field.js | 42 ++--- .../views/fields/statusbar/statusbar_field.js | 61 +++---- .../src/views/fields/text/text_field.js | 68 +++---- .../timezone_mismatch_field.js | 47 ++--- .../static/src/views/fields/url/url_field.js | 43 +++-- .../views/fields/x2many/list_x2many_field.js | 15 +- .../src/views/fields/x2many/x2many_field.js | 33 ++-- .../static/src/views/form/form_arch_parser.js | 2 +- .../static/src/views/form/form_controller.js | 2 +- .../web/static/src/views/form/form_label.js | 2 +- .../src/views/kanban/kanban_arch_parser.js | 2 +- addons/web/static/src/views/legacy_utils.js | 17 +- .../static/src/views/list/list_arch_parser.js | 10 +- .../web/static/src/views/relational_model.js | 3 +- .../fields/upgrade_boolean_field.js | 16 +- .../form_label_highlight_text.js | 4 +- .../highlight_text/settings_radio_field.js | 21 ++- .../views/calendar/calendar_view_tests.js | 4 +- .../tests/views/fields/html_field_tests.js | 4 +- .../fields/many2many_tags_field_tests.js | 7 +- .../views/fields/one2many_field_tests.js | 6 +- .../tests/views/form/form_compiler_tests.js | 22 ++- .../tests/views/form/form_view_tests.js | 50 +++--- .../static/tests/views/kanban_view_tests.js | 79 ++++---- .../web/static/tests/views/list_view_tests.js | 58 +++--- .../static/src/js/backend/html_field.js | 106 +++++------ .../static/src/gauge_field.js | 10 +- .../static/src/components/fields/fields.js | 30 ++-- .../src/components/fields/publish_button.js | 4 +- .../src/components/fields/redirect_field.js | 5 +- .../src/components/fields/widget_iframe.js | 6 +- .../js/website_sale_video_field_preview.js | 6 +- .../src/slide_category_one2many_field.js | 7 +- 174 files changed, 2365 insertions(+), 1693 deletions(-) diff --git a/addons/account/static/src/components/account_payment_field/account_payment_field.js b/addons/account/static/src/components/account_payment_field/account_payment_field.js index b85831ed525..2d123abafc4 100644 --- a/addons/account/static/src/components/account_payment_field/account_payment_field.js +++ b/addons/account/static/src/components/account_payment_field/account_payment_field.js @@ -88,6 +88,10 @@ export class AccountPaymentField extends Component { } } AccountPaymentField.template = "account.AccountPaymentField"; -AccountPaymentField.supportedTypes = ["char"]; -registry.category("fields").add("payment", AccountPaymentField); +export const accountPaymentField = { + component: AccountPaymentField, + supportedTypes: ["char"], +}; + +registry.category("fields").add("payment", accountPaymentField); diff --git a/addons/account/static/src/components/account_resequence/account_resequence_field.js b/addons/account/static/src/components/account_resequence/account_resequence_field.js index ecb9b3d173e..4f33b087dba 100644 --- a/addons/account/static/src/components/account_resequence/account_resequence_field.js +++ b/addons/account/static/src/components/account_resequence/account_resequence_field.js @@ -21,4 +21,6 @@ class ShowResequenceRenderer extends Component { ShowResequenceRenderer.template = "account.ResequenceRenderer"; ShowResequenceRenderer.components = { ChangeLine }; -registry.category("fields").add("account_resequence_widget", ShowResequenceRenderer); +registry.category("fields").add("account_resequence_widget", { + component: ShowResequenceRenderer, +}); diff --git a/addons/account/static/src/components/account_type_selection/account_type_selection.js b/addons/account/static/src/components/account_type_selection/account_type_selection.js index 6e43dff1935..a2b79d82c19 100644 --- a/addons/account/static/src/components/account_type_selection/account_type_selection.js +++ b/addons/account/static/src/components/account_type_selection/account_type_selection.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { SelectionField } from "@web/views/fields/selection/selection_field"; +import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field"; export class AccountTypeSelection extends SelectionField { get hierarchyOptions() { @@ -20,4 +20,9 @@ export class AccountTypeSelection extends SelectionField { } AccountTypeSelection.template = "account.AccountTypeSelection"; -registry.category("fields").add("account_type_selection", AccountTypeSelection); +export const accountTypeSelection = { + ...selectionField, + component: AccountTypeSelection, +}; + +registry.category("fields").add("account_type_selection", accountTypeSelection); diff --git a/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js b/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js index 6f368a34353..76f195efdf7 100644 --- a/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js +++ b/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { Many2ManyTagsField, many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; const { onWillUpdateProps } = owl; @@ -46,4 +46,9 @@ export class AutosaveMany2ManyTagsField extends Many2ManyTagsField { } } -registry.category("fields").add("autosave_many2many_tags", AutosaveMany2ManyTagsField); +export const autosaveMany2ManyTagsField = { + ...many2ManyTagsField, + component: AutosaveMany2ManyTagsField, +}; + +registry.category("fields").add("autosave_many2many_tags", autosaveMany2ManyTagsField); diff --git a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js index f8b90ab2dd7..6f06f801316 100644 --- a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js +++ b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js @@ -28,4 +28,6 @@ class ShowGroupedList extends Component { ShowGroupedList.template = "account.GroupedListTemplate"; ShowGroupedList.components = { ListGroup }; -registry.category("fields").add("grouped_view_widget", ShowGroupedList); +registry.category("fields").add("grouped_view_widget", { + component: ShowGroupedList, +}); diff --git a/addons/account/static/src/components/journal_dashboard_activity/journal_dashboard_activity.js b/addons/account/static/src/components/journal_dashboard_activity/journal_dashboard_activity.js index c8b9d5d069b..ace961bc683 100644 --- a/addons/account/static/src/components/journal_dashboard_activity/journal_dashboard_activity.js +++ b/addons/account/static/src/components/journal_dashboard_activity/journal_dashboard_activity.js @@ -45,4 +45,8 @@ export class JournalDashboardActivity extends Component { } JournalDashboardActivity.template = "account.JournalDashboardActivity"; -registry.category("fields").add("kanban_vat_activity", JournalDashboardActivity); +export const journalDashboardActivity = { + component: JournalDashboardActivity, +}; + +registry.category("fields").add("kanban_vat_activity", journalDashboardActivity); diff --git a/addons/account/static/src/components/open_move_line_move_widget/open_move_line_move_widget.js b/addons/account/static/src/components/open_move_line_move_widget/open_move_line_move_widget.js index 815607938b4..ba7ad0119de 100644 --- a/addons/account/static/src/components/open_move_line_move_widget/open_move_line_move_widget.js +++ b/addons/account/static/src/components/open_move_line_move_widget/open_move_line_move_widget.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2OneField } from "@web/views/fields/many2one/many2one_field"; +import { Many2OneField, many2OneField } from "@web/views/fields/many2one/many2one_field"; class LineOpenMoveWidget extends Many2OneField { async openAction() { @@ -10,4 +10,9 @@ class LineOpenMoveWidget extends Many2OneField { } } -registry.category("fields").add("line_open_move_widget", LineOpenMoveWidget); +export const lineOpenMoveWidget = { + ...many2OneField, + component: LineOpenMoveWidget, +}; + +registry.category("fields").add("line_open_move_widget", lineOpenMoveWidget); diff --git a/addons/account/static/src/components/open_move_widget/open_move_widget.js b/addons/account/static/src/components/open_move_widget/open_move_widget.js index d996876d065..b546b430e58 100644 --- a/addons/account/static/src/components/open_move_widget/open_move_widget.js +++ b/addons/account/static/src/components/open_move_widget/open_move_widget.js @@ -19,4 +19,6 @@ class OpenMoveWidget extends Component { } OpenMoveWidget.template = "account.OpenMoveWidget"; -registry.category("fields").add("open_move_widget", OpenMoveWidget); +registry.category("fields").add("open_move_widget", { + component: OpenMoveWidget, +}); diff --git a/addons/account/static/src/components/section_and_note_fields_backend/section_and_note_fields_backend.js b/addons/account/static/src/components/section_and_note_fields_backend/section_and_note_fields_backend.js index ec37200c5a6..77c8c99d4ca 100644 --- a/addons/account/static/src/components/section_and_note_fields_backend/section_and_note_fields_backend.js +++ b/addons/account/static/src/components/section_and_note_fields_backend/section_and_note_fields_backend.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; import { ListRenderer } from "@web/views/list/list_renderer"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; import { TextField, ListTextField } from "@web/views/fields/text/text_field"; import { CharField } from "@web/views/fields/char/char_field"; @@ -82,7 +82,6 @@ export class SectionAndNoteText extends Component { } } SectionAndNoteText.template = "account.SectionAndNoteText"; -SectionAndNoteText.additionalClasses = ["o_field_text"]; export class ListSectionAndNoteText extends SectionAndNoteText { get componentToUse() { @@ -92,6 +91,21 @@ export class ListSectionAndNoteText extends SectionAndNoteText { } } -registry.category("fields").add("section_and_note_one2many", SectionAndNoteFieldOne2Many); -registry.category("fields").add("section_and_note_text", SectionAndNoteText); -registry.category("fields").add("list.section_and_note_text", ListSectionAndNoteText); +export const sectionAndNoteFieldOne2Many = { + ...x2ManyField, + component: SectionAndNoteFieldOne2Many, +}; + +export const sectionAndNoteText = { + component: SectionAndNoteText, + additionalClasses: ["o_field_text"], +}; + +export const listSectionAndNoteText = { + ...sectionAndNoteText, + component: ListSectionAndNoteText, +}; + +registry.category("fields").add("section_and_note_one2many", sectionAndNoteFieldOne2Many); +registry.category("fields").add("section_and_note_text", sectionAndNoteText); +registry.category("fields").add("list.section_and_note_text", listSectionAndNoteText); diff --git a/addons/account/static/src/components/tax_totals/tax_totals.js b/addons/account/static/src/components/tax_totals/tax_totals.js index d8fa1f82523..556f89712f3 100644 --- a/addons/account/static/src/components/tax_totals/tax_totals.js +++ b/addons/account/static/src/components/tax_totals/tax_totals.js @@ -186,4 +186,8 @@ TaxTotalsComponent.props = { ...standardFieldProps, }; -registry.category("fields").add("account-tax-totals-field", TaxTotalsComponent); +export const taxTotalsComponent = { + component: TaxTotalsComponent, +}; + +registry.category("fields").add("account-tax-totals-field", taxTotalsComponent); diff --git a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js index a435740a6eb..357f472c651 100644 --- a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js +++ b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js @@ -573,15 +573,11 @@ export class AnalyticDistribution extends Component { } } AnalyticDistribution.template = "analytic.AnalyticDistribution"; -AnalyticDistribution.supportedTypes = ["char", "text"]; AnalyticDistribution.components = { AnalyticAutoComplete, TagsList, } -AnalyticDistribution.fieldDependencies = { - analytic_precision: { type: 'integer' }, -} AnalyticDistribution.props = { ...standardFieldProps, business_domain: { type: String, optional: true }, @@ -591,15 +587,21 @@ AnalyticDistribution.props = { force_applicability: { type: String, optional: true }, allow_save: { type: Boolean }, } -AnalyticDistribution.extractProps = ({ field, attrs }) => { - return { + +export const analyticDistribution = { + component: AnalyticDistribution, + supportedTypes: ["char", "text"], + fieldDependencies: { + analytic_precision: { type: "integer" }, + }, + extractProps: ({ attrs }) => ({ business_domain: attrs.options.business_domain, account_field: attrs.options.account_field, product_field: attrs.options.product_field, business_domain_compute: attrs.business_domain_compute, force_applicability: attrs.options.force_applicability, - allow_save: !Boolean(attrs.options.disable_save), - }; + allow_save: !attrs.options.disable_save, + }), }; -registry.category("fields").add("analytic_distribution", AnalyticDistribution); +registry.category("fields").add("analytic_distribution", analyticDistribution); diff --git a/addons/auth_password_policy/static/src/password_field.js b/addons/auth_password_policy/static/src/password_field.js index 87477d49c65..8e10f8ea8b2 100644 --- a/addons/auth_password_policy/static/src/password_field.js +++ b/addons/auth_password_policy/static/src/password_field.js @@ -30,8 +30,6 @@ export class PasswordField extends Component { this.recommendations = recommendations; } } -PasswordField.displayName = _lt("Password"); -PasswordField.supportedTypes = ["char"]; PasswordField.props = standardFieldProps; PasswordField.components = { Meter }; PasswordField.template = xml` @@ -46,4 +44,10 @@ PasswordField.template = xml` `; -registry.category("fields").add("password_meter", PasswordField); +export const passwordField = { + component: PasswordField, + displayName: _lt("Password"), + supportedTypes: ["char"], +}; + +registry.category("fields").add("password_meter", passwordField); diff --git a/addons/barcodes/static/src/barcode_handler_field.js b/addons/barcodes/static/src/barcode_handler_field.js index 4cf6cfe39a9..494c0ab7bb1 100644 --- a/addons/barcodes/static/src/barcode_handler_field.js +++ b/addons/barcodes/static/src/barcode_handler_field.js @@ -20,4 +20,8 @@ export class BarcodeHandlerField extends Component { BarcodeHandlerField.template = xml``; BarcodeHandlerField.props = { ...standardFieldProps }; -registry.category("fields").add("barcode_handler", BarcodeHandlerField); +export const barcodeHandlerField = { + component: BarcodeHandlerField, +}; + +registry.category("fields").add("barcode_handler", barcodeHandlerField); diff --git a/addons/barcodes/static/src/float_scannable_field.js b/addons/barcodes/static/src/float_scannable_field.js index 1efc750afd6..db46e9c6998 100644 --- a/addons/barcodes/static/src/float_scannable_field.js +++ b/addons/barcodes/static/src/float_scannable_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { FloatField } from "@web/views/fields/float/float_field"; +import { FloatField, floatField } from "@web/views/fields/float/float_field"; export class FloatScannableField extends FloatField { onBarcodeScanned() { @@ -10,4 +10,9 @@ export class FloatScannableField extends FloatField { } FloatScannableField.template = "barcodes.FloatScannableField"; -registry.category("fields").add("field_float_scannable", FloatScannableField); +export const floatScannableField = { + ...floatField, + component: FloatScannableField, +}; + +registry.category("fields").add("field_float_scannable", floatScannableField); diff --git a/addons/base_iban/static/src/components/iban_widget/iban_widget.js b/addons/base_iban/static/src/components/iban_widget/iban_widget.js index 991c237f5ff..d157c122251 100644 --- a/addons/base_iban/static/src/components/iban_widget/iban_widget.js +++ b/addons/base_iban/static/src/components/iban_widget/iban_widget.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; import { useDebounced } from "@web/core/utils/timing"; import { useService } from "@web/core/utils/hooks"; @@ -27,4 +27,9 @@ export class IbanWidget extends CharField { } IbanWidget.template = "base_iban.iban"; -registry.category("fields").add("iban", IbanWidget); +export const ibanWidget = { + ...charField, + component: IbanWidget, +}; + +registry.category("fields").add("iban", ibanWidget); diff --git a/addons/calendar/static/src/views/fields/many2many_attendee.js b/addons/calendar/static/src/views/fields/many2many_attendee.js index f6bb1192e45..7459a970fb1 100644 --- a/addons/calendar/static/src/views/fields/many2many_attendee.js +++ b/addons/calendar/static/src/views/fields/many2many_attendee.js @@ -1,7 +1,10 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2ManyTagsAvatarField } from "@web/views/fields/many2many_tags_avatar/many2many_tags_avatar_field"; +import { + Many2ManyTagsAvatarField, + many2ManyTagsAvatarField, +} from "@web/views/fields/many2many_tags_avatar/many2many_tags_avatar_field"; export class Many2ManyAttendee extends Many2ManyTagsAvatarField { get tags() { @@ -26,10 +29,15 @@ export class Many2ManyAttendee extends Many2ManyTagsAvatarField { return tags; } } -Many2ManyAttendee.additionalClasses = ["o_field_many2many_tags_avatar"]; -Many2ManyAttendee.legacySpecialData = "_fetchSpecialAttendeeStatus"; -registry.category("fields").add("many2manyattendee", Many2ManyAttendee); +export const many2ManyAttendee = { + ...many2ManyTagsAvatarField, + component: Many2ManyAttendee, + additionalClasses: ["o_field_many2many_tags_avatar"], + legacySpecialData: "_fetchSpecialAttendeeStatus", +}; + +registry.category("fields").add("many2manyattendee", many2ManyAttendee); export function preloadMany2ManyAttendee(orm, record, fieldName) { const context = record.getFieldContext(fieldName); diff --git a/addons/delivery_mondialrelay/static/src/components/mondialrelay_field.js b/addons/delivery_mondialrelay/static/src/components/mondialrelay_field.js index fe1cfba1c70..925896daf03 100644 --- a/addons/delivery_mondialrelay/static/src/components/mondialrelay_field.js +++ b/addons/delivery_mondialrelay/static/src/components/mondialrelay_field.js @@ -88,4 +88,8 @@ export class MondialRelayField extends Component { } MondialRelayField.template = xml`
`; -registry.category("fields").add("mondialrelay_relay", MondialRelayField); +export const mondialRelayField = { + component: MondialRelayField, +}; + +registry.category("fields").add("mondialrelay_relay", mondialRelayField); diff --git a/addons/event/static/src/icon_selection_field/icon_selection_field.js b/addons/event/static/src/icon_selection_field/icon_selection_field.js index ebb9daca39d..21c96537f1e 100644 --- a/addons/event/static/src/icon_selection_field/icon_selection_field.js +++ b/addons/event/static/src/icon_selection_field/icon_selection_field.js @@ -20,11 +20,13 @@ IconSelectionField.props = { icons: Object, }; -IconSelectionField.displayName = _lt("Icon Selection"); -IconSelectionField.supportedTypes = ["char", "text", "selection"]; +export const iconSelectionField = { + component: IconSelectionField, + displayName: _lt("Icon Selection"), + supportedTypes: ["char", "text", "selection"], + extractProps: ({ attrs }) => ({ + icons: attrs.options, + }), +}; -IconSelectionField.extractProps = ({ attrs }) => ({ - icons: attrs.options, -}); - -registry.category("fields").add("event_icon_selection", IconSelectionField); +registry.category("fields").add("event_icon_selection", iconSelectionField); diff --git a/addons/hr/static/src/components/background_image/background_image.js b/addons/hr/static/src/components/background_image/background_image.js index e691f9817fe..cc11ecc540a 100644 --- a/addons/hr/static/src/components/background_image/background_image.js +++ b/addons/hr/static/src/components/background_image/background_image.js @@ -2,9 +2,14 @@ import { registry } from '@web/core/registry'; -import { ImageField } from '@web/views/fields/image/image_field'; +import { ImageField, imageField } from '@web/views/fields/image/image_field'; export class BackgroundImageField extends ImageField {} BackgroundImageField.template = 'hr.BackgroundImage'; -registry.category("fields").add("background_image", BackgroundImageField); +export const backgroundImageField = { + ...imageField, + component: BackgroundImageField, +}; + +registry.category("fields").add("background_image", backgroundImageField); diff --git a/addons/hr/static/src/components/work_permit_upload/work_permit_upload.js b/addons/hr/static/src/components/work_permit_upload/work_permit_upload.js index 6176fd1ef1a..0e429c707ac 100644 --- a/addons/hr/static/src/components/work_permit_upload/work_permit_upload.js +++ b/addons/hr/static/src/components/work_permit_upload/work_permit_upload.js @@ -1,9 +1,14 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { BinaryField } from "@web/views/fields/binary/binary_field"; +import { BinaryField, binaryField } from "@web/views/fields/binary/binary_field"; export class WorkPermitUploadField extends BinaryField {} WorkPermitUploadField.template = "hr.WorkPermitUploadField"; -registry.category("fields").add("work_permit_upload", WorkPermitUploadField); +export const workPermitUploadField = { + ...binaryField, + component: WorkPermitUploadField, +}; + +registry.category("fields").add("work_permit_upload", workPermitUploadField); diff --git a/addons/hr/static/src/views/fields/many2many_avatar_employee_field/many2many_avatar_employee_field.js b/addons/hr/static/src/views/fields/many2many_avatar_employee_field/many2many_avatar_employee_field.js index 6e3c519bc08..434f711eead 100644 --- a/addons/hr/static/src/views/fields/many2many_avatar_employee_field/many2many_avatar_employee_field.js +++ b/addons/hr/static/src/views/fields/many2many_avatar_employee_field/many2many_avatar_employee_field.js @@ -1,15 +1,40 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2ManyTagsAvatarUserField, KanbanMany2ManyTagsAvatarUserField } from "@mail/views/fields/many2many_avatar_user_field/many2many_avatar_user_field"; +import { + Many2ManyTagsAvatarUserField, + KanbanMany2ManyTagsAvatarUserField, + many2ManyTagsAvatarUserField, + kanbanMany2ManyTagsAvatarUserField, +} from "@mail/views/fields/many2many_avatar_user_field/many2many_avatar_user_field"; export class Many2ManyTagsAvatarEmployeeField extends Many2ManyTagsAvatarUserField {} -Many2ManyTagsAvatarEmployeeField.additionalClasses = [...Many2ManyTagsAvatarUserField.additionalClasses, "o_field_many2many_avatar_user"]; -registry.category("fields").add("many2many_avatar_employee", Many2ManyTagsAvatarEmployeeField); +export const many2ManyTagsAvatarEmployeeField = { + ...many2ManyTagsAvatarUserField, + component: Many2ManyTagsAvatarEmployeeField, + additionalClasses: [ + ...many2ManyTagsAvatarUserField.additionalClasses, + "o_field_many2many_avatar_user", + ], +}; + +registry.category("fields").add("many2many_avatar_employee", many2ManyTagsAvatarEmployeeField); export class KanbanMany2ManyTagsAvatarEmployeeField extends KanbanMany2ManyTagsAvatarUserField {} -KanbanMany2ManyTagsAvatarEmployeeField.additionalClasses = [...KanbanMany2ManyTagsAvatarUserField.additionalClasses, "o_field_many2many_avatar_user"]; -registry.category("fields").add("kanban.many2many_avatar_employee", KanbanMany2ManyTagsAvatarEmployeeField); -registry.category("fields").add("list.many2many_avatar_employee", KanbanMany2ManyTagsAvatarEmployeeField); +export const kanbanMany2ManyTagsAvatarEmployeeField = { + ...kanbanMany2ManyTagsAvatarUserField, + component: KanbanMany2ManyTagsAvatarEmployeeField, + additionalClasses: [ + ...kanbanMany2ManyTagsAvatarUserField.additionalClasses, + "o_field_many2many_avatar_user", + ], +}; + +registry + .category("fields") + .add("kanban.many2many_avatar_employee", kanbanMany2ManyTagsAvatarEmployeeField); +registry + .category("fields") + .add("list.many2many_avatar_employee", kanbanMany2ManyTagsAvatarEmployeeField); diff --git a/addons/hr/static/src/views/fields/many2one_avatar_employee_field/many2one_avatar_employee_field.js b/addons/hr/static/src/views/fields/many2one_avatar_employee_field/many2one_avatar_employee_field.js index f05024ecbac..53abf16df1b 100644 --- a/addons/hr/static/src/views/fields/many2one_avatar_employee_field/many2one_avatar_employee_field.js +++ b/addons/hr/static/src/views/fields/many2one_avatar_employee_field/many2one_avatar_employee_field.js @@ -1,13 +1,33 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2OneAvatarUserField, KanbanMany2OneAvatarUserField } from "@mail/views/fields/many2one_avatar_user_field/many2one_avatar_user_field"; +import { + Many2OneAvatarUserField, + KanbanMany2OneAvatarUserField, + many2OneAvatarUserField, + kanbanMany2OneAvatarUserField, +} from "@mail/views/fields/many2one_avatar_user_field/many2one_avatar_user_field"; export class Many2OneAvatarEmployeeField extends Many2OneAvatarUserField {} -Many2OneAvatarEmployeeField.additionalClasses = [...Many2OneAvatarUserField.additionalClasses, "o_field_many2one_avatar_user"]; -registry.category("fields").add("many2one_avatar_employee", Many2OneAvatarEmployeeField); +export const many2OneAvatarEmployeeField = { + ...many2OneAvatarUserField, + component: Many2OneAvatarEmployeeField, + additionalClasses: [ + ...many2OneAvatarUserField.additionalClasses, + "o_field_many2one_avatar_user", + ], +}; + +registry.category("fields").add("many2one_avatar_employee", many2OneAvatarEmployeeField); export class KanbanMany2OneAvatarEmployeeField extends KanbanMany2OneAvatarUserField {} -registry.category("fields").add("kanban.many2one_avatar_employee", KanbanMany2OneAvatarEmployeeField); +export const kanbanMany2OneAvatarEmployeeField = { + ...kanbanMany2OneAvatarUserField, + component: KanbanMany2OneAvatarEmployeeField, +}; + +registry + .category("fields") + .add("kanban.many2one_avatar_employee", kanbanMany2OneAvatarEmployeeField); diff --git a/addons/hr_holidays/static/src/radio_image_field/radio_image_field.js b/addons/hr_holidays/static/src/radio_image_field/radio_image_field.js index ea2b4fbea45..c9944fe6275 100644 --- a/addons/hr_holidays/static/src/radio_image_field/radio_image_field.js +++ b/addons/hr_holidays/static/src/radio_image_field/radio_image_field.js @@ -1,12 +1,15 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { RadioField, preloadRadio } from "@web/views/fields/radio/radio_field"; +import { RadioField, preloadRadio, radioField } from "@web/views/fields/radio/radio_field"; class RadioImageField extends RadioField {} RadioImageField.template = "hr_holidays.RadioImageField"; -registry.category("fields").add("hr_holidays_radio_image", RadioImageField); +registry.category("fields").add("hr_holidays_radio_image", { + ...radioField, + component: RadioImageField, +}); registry.category("preloadedData").add("hr_holidays_radio_image", { loadOnTypes: ["many2one"], diff --git a/addons/hr_org_chart/static/src/fields/hr_org_chart.js b/addons/hr_org_chart/static/src/fields/hr_org_chart.js index 1d38e76412a..126c1c105ca 100644 --- a/addons/hr_org_chart/static/src/fields/hr_org_chart.js +++ b/addons/hr_org_chart/static/src/fields/hr_org_chart.js @@ -1,6 +1,5 @@ /** @odoo-module */ -import {Field} from '@web/views/fields/field'; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { usePopover } from "@web/core/popover/popover_hook"; @@ -49,7 +48,7 @@ class HrOrgChartPopover extends Component { } HrOrgChartPopover.template = 'hr_org_chart.hr_orgchart_emp_popover'; -export class HrOrgChart extends Field { +export class HrOrgChart extends Component { async setup() { super.setup(); @@ -143,4 +142,8 @@ HrOrgChart.components = { HrOrgChart.template = 'hr_org_chart.hr_org_chart'; -registry.category("fields").add("hr_org_chart", HrOrgChart); +export const hrOrgChart = { + component: HrOrgChart, +}; + +registry.category("fields").add("hr_org_chart", hrOrgChart); diff --git a/addons/hr_recruitment/static/src/applicant_char/applicant_char.js b/addons/hr_recruitment/static/src/applicant_char/applicant_char.js index caae452cc8b..17114b164b5 100644 --- a/addons/hr_recruitment/static/src/applicant_char/applicant_char.js +++ b/addons/hr_recruitment/static/src/applicant_char/applicant_char.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; @@ -27,4 +27,10 @@ export class ApplicantCharField extends CharField { } } ApplicantCharField.template = "hr_recruitment.ApplicantCharField"; -registry.category("fields").add("applicant_char", ApplicantCharField); + +export const applicantCharField = { + ...charField, + component: ApplicantCharField, +}; + +registry.category("fields").add("applicant_char", applicantCharField); diff --git a/addons/hr_skills/static/src/fields/resume_one2many.js b/addons/hr_skills/static/src/fields/resume_one2many.js index b6b627dabc3..040c40c5bc1 100644 --- a/addons/hr_skills/static/src/fields/resume_one2many.js +++ b/addons/hr_skills/static/src/fields/resume_one2many.js @@ -4,7 +4,7 @@ import { registry } from "@web/core/registry"; import { formatDate } from "@web/core/l10n/dates"; -import { SkillsX2ManyField } from "./skills_one2many"; +import { SkillsX2ManyField, skillsX2ManyField } from "./skills_one2many"; import { CommonSkillsListRenderer } from "../views/skills_list_renderer"; export class ResumeListRenderer extends CommonSkillsListRenderer { @@ -36,5 +36,9 @@ ResumeX2ManyField.components = { ListRenderer: ResumeListRenderer, }; -registry.category("fields") - .add("resume_one2many", ResumeX2ManyField); +export const resumeX2ManyField = { + ...skillsX2ManyField, + component: ResumeX2ManyField, +}; + +registry.category("fields").add("resume_one2many", resumeX2ManyField); diff --git a/addons/hr_skills/static/src/fields/skills_one2many.js b/addons/hr_skills/static/src/fields/skills_one2many.js index 9ff69e4d8ec..306be5e6d52 100644 --- a/addons/hr_skills/static/src/fields/skills_one2many.js +++ b/addons/hr_skills/static/src/fields/skills_one2many.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; import { registry } from "@web/core/registry"; import { CommonSkillsListRenderer } from "../views/skills_list_renderer"; @@ -41,4 +41,9 @@ SkillsX2ManyField.components = { ListRenderer: SkillsListRenderer, }; -registry.category("fields").add("skills_one2many", SkillsX2ManyField); +export const skillsX2ManyField = { + ...x2ManyField, + component: SkillsX2ManyField, +}; + +registry.category("fields").add("skills_one2many", skillsX2ManyField); diff --git a/addons/hr_timesheet/static/src/components/task_with_hours/task_with_hours.js b/addons/hr_timesheet/static/src/components/task_with_hours/task_with_hours.js index ba82247cede..9a21997aa9f 100644 --- a/addons/hr_timesheet/static/src/components/task_with_hours/task_with_hours.js +++ b/addons/hr_timesheet/static/src/components/task_with_hours/task_with_hours.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2OneField } from "@web/views/fields/many2one/many2one_field"; +import { Many2OneField, many2OneField } from "@web/views/fields/many2one/many2one_field"; class TaskWithHours extends Many2OneField { @@ -48,4 +48,7 @@ class TaskWithHours extends Many2OneField { } -registry.category("fields").add("task_with_hours", TaskWithHours); +registry.category("fields").add("task_with_hours", { + ...many2OneField, + component: TaskWithHours, +}); diff --git a/addons/hr_timesheet/static/src/components/timesheet_uom/timesheet_uom.js b/addons/hr_timesheet/static/src/components/timesheet_uom/timesheet_uom.js index 21c63d2f9ff..87b65e6bbb0 100644 --- a/addons/hr_timesheet/static/src/components/timesheet_uom/timesheet_uom.js +++ b/addons/hr_timesheet/static/src/components/timesheet_uom/timesheet_uom.js @@ -30,7 +30,7 @@ export class TimesheetUOM extends Component { } get timesheetComponent() { - return registry.category("fields").get(this.timesheetWidget, FloatFactorField); + return registry.category("fields").get(this.timesheetWidget, { component: FloatFactorField }).component; } get timesheetComponentProps() { @@ -53,4 +53,8 @@ TimesheetUOM.template = "hr_timesheet.TimesheetUOM"; TimesheetUOM.components = { FloatFactorField, FloatToggleField, FloatTimeField }; -registry.category("fields").add("timesheet_uom", TimesheetUOM); +export const timesheetUOM = { + component: TimesheetUOM, +}; + +registry.category("fields").add("timesheet_uom", timesheetUOM); diff --git a/addons/hr_timesheet/static/src/components/timesheet_uom_no_toggle/timesheet_uom_no_toggle.js b/addons/hr_timesheet/static/src/components/timesheet_uom_no_toggle/timesheet_uom_no_toggle.js index 91f35d701dd..275e391f14a 100644 --- a/addons/hr_timesheet/static/src/components/timesheet_uom_no_toggle/timesheet_uom_no_toggle.js +++ b/addons/hr_timesheet/static/src/components/timesheet_uom_no_toggle/timesheet_uom_no_toggle.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; -import { TimesheetUOM } from "../timesheet_uom/timesheet_uom"; +import { TimesheetUOM, timesheetUOM } from "../timesheet_uom/timesheet_uom"; export class TimesheetUOMNoToggle extends TimesheetUOM { @@ -17,4 +17,9 @@ export class TimesheetUOMNoToggle extends TimesheetUOM { // As FloatToggleField won't be used by TimesheetUOMNoToggle, we remove it from the components that we get from TimesheetUOM. delete TimesheetUOMNoToggle.components.FloatToggleField; -registry.category("fields").add("timesheet_uom_no_toggle", TimesheetUOMNoToggle); +export const timesheetUOMNoToggle = { + ...timesheetUOM, + component: TimesheetUOMNoToggle, +}; + +registry.category("fields").add("timesheet_uom_no_toggle", timesheetUOMNoToggle); diff --git a/addons/im_livechat/static/src/js/im_livechat_chatbot_script_answers_m2m.js b/addons/im_livechat/static/src/js/im_livechat_chatbot_script_answers_m2m.js index 058cac3f438..9a37c0b3543 100644 --- a/addons/im_livechat/static/src/js/im_livechat_chatbot_script_answers_m2m.js +++ b/addons/im_livechat/static/src/js/im_livechat_chatbot_script_answers_m2m.js @@ -2,7 +2,10 @@ import { registry } from "@web/core/registry"; -import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { + Many2ManyTagsField, + many2ManyTagsField, +} from "@web/views/fields/many2many_tags/many2many_tags_field"; const fieldRegistry = registry.category("fields"); @@ -22,4 +25,9 @@ export class ChatbotScriptTriggeringAnswersMany2Many extends Many2ManyTagsField } }; -fieldRegistry.add("chatbot_triggering_answers_widget", ChatbotScriptTriggeringAnswersMany2Many); +export const chatbotScriptTriggeringAnswersMany2Many = { + ...many2ManyTagsField, + component: ChatbotScriptTriggeringAnswersMany2Many, +}; + +fieldRegistry.add("chatbot_triggering_answers_widget", chatbotScriptTriggeringAnswersMany2Many); diff --git a/addons/im_livechat/static/src/js/im_livechat_chatbot_steps_one2many.js b/addons/im_livechat/static/src/js/im_livechat_chatbot_steps_one2many.js index 0a8e7b9a236..20c57e3a791 100644 --- a/addons/im_livechat/static/src/js/im_livechat_chatbot_steps_one2many.js +++ b/addons/im_livechat/static/src/js/im_livechat_chatbot_steps_one2many.js @@ -4,7 +4,7 @@ import { ListRenderer } from "@web/views/list/list_renderer"; import { registry } from "@web/core/registry"; import { patch } from '@web/core/utils/patch'; import { useX2ManyCrud, useOpenX2ManyRecord, X2ManyFieldDialog } from "@web/views/fields/relational_utils"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; const fieldRegistry = registry.category("fields"); @@ -96,7 +96,12 @@ export class ChatbotStepsOne2many extends X2ManyField { } }; -fieldRegistry.add("chatbot_steps_one2many", ChatbotStepsOne2many); +export const chatbotStepsOne2many = { + ...x2ManyField, + component: ChatbotStepsOne2many, +}; + +fieldRegistry.add("chatbot_steps_one2many", chatbotStepsOne2many); ChatbotStepsOne2many.components = { ...X2ManyField.components, diff --git a/addons/loyalty/static/src/js/filterable_selection_field/filterable_selection_field.js b/addons/loyalty/static/src/js/filterable_selection_field/filterable_selection_field.js index c925e5aec8d..b2c9b898f08 100644 --- a/addons/loyalty/static/src/js/filterable_selection_field/filterable_selection_field.js +++ b/addons/loyalty/static/src/js/filterable_selection_field/filterable_selection_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { SelectionField } from "@web/views/fields/selection/selection_field"; +import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field"; /** * The purpose of this field is to be able to define some values which should not be @@ -33,12 +33,14 @@ FilterableSelectionField.props = { blacklisted_values: { type: Array, optional: true }, }; -FilterableSelectionField.extractProps = ({ attrs }) => { - return { - ...SelectionField.extractProps({ attrs }), - whitelisted_values: attrs.options.whitelisted_values, - blacklisted_values: attrs.options.blacklisted_values, - }; +export const filterableSelectionField = { + ...selectionField, + component: FilterableSelectionField, + extractProps: (params) => ({ + ...selectionField.extractProps(params), + whitelisted_values: params.attrs.options.whitelisted_values, + blacklisted_values: params.attrs.options.blacklisted_values, + }), }; -registry.category("fields").add("filterable_selection", FilterableSelectionField); +registry.category("fields").add("filterable_selection", filterableSelectionField); diff --git a/addons/loyalty/static/src/js/loyalty_control_panel_widget.js b/addons/loyalty/static/src/js/loyalty_control_panel_widget.js index caf6b1e60a9..f372baa26c1 100644 --- a/addons/loyalty/static/src/js/loyalty_control_panel_widget.js +++ b/addons/loyalty/static/src/js/loyalty_control_panel_widget.js @@ -1,9 +1,14 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; export class LoyaltyX2ManyField extends X2ManyField {}; LoyaltyX2ManyField.template = "loyalty.LoyaltyX2ManyField"; -registry.category("fields").add("loyalty_one2many", LoyaltyX2ManyField); +export const loyaltyX2ManyField = { + ...x2ManyField, + component: LoyaltyX2ManyField, +}; + +registry.category("fields").add("loyalty_one2many", loyaltyX2ManyField); diff --git a/addons/mail/static/src/backend_components/activity_exception/activity_exception.js b/addons/mail/static/src/backend_components/activity_exception/activity_exception.js index 3778b0e3c8f..71a32b81650 100644 --- a/addons/mail/static/src/backend_components/activity_exception/activity_exception.js +++ b/addons/mail/static/src/backend_components/activity_exception/activity_exception.js @@ -19,10 +19,12 @@ class ActivityException extends Component { Object.assign(ActivityException, { props: standardFieldProps, template: "mail.ActivityException", +}); + +registry.category("fields").add("activity_exception", { + component: ActivityException, fieldDependencies: { activity_exception_icon: { type: "char" }, }, noLabel: true, }); - -registry.category("fields").add("activity_exception", ActivityException); diff --git a/addons/mail/static/src/backend_components/kanban_field_activity_view/kanban_field_activity_view_container.js b/addons/mail/static/src/backend_components/kanban_field_activity_view/kanban_field_activity_view_container.js index 1abcdb67139..58cb199de29 100644 --- a/addons/mail/static/src/backend_components/kanban_field_activity_view/kanban_field_activity_view_container.js +++ b/addons/mail/static/src/backend_components/kanban_field_activity_view/kanban_field_activity_view_container.js @@ -78,6 +78,14 @@ export class KanbanFieldActivityViewContainer extends Component { } Object.assign(KanbanFieldActivityViewContainer, { + props: { + ...standardFieldProps, + }, + template: "mail.KanbanFieldActivityViewContainer", +}); + +export const kanbanFieldActivityViewContainer = { + component: KanbanFieldActivityViewContainer, fieldDependencies: { activity_exception_decoration: { type: "selection" }, activity_exception_icon: { type: "char" }, @@ -86,10 +94,6 @@ Object.assign(KanbanFieldActivityViewContainer, { activity_type_icon: { type: "char" }, activity_type_id: { type: "many2one", relation: "mail.activity.type" }, }, - props: { - ...standardFieldProps, - }, - template: "mail.KanbanFieldActivityViewContainer", -}); +}; -registry.category("fields").add("kanban_activity", KanbanFieldActivityViewContainer); +registry.category("fields").add("kanban_activity", kanbanFieldActivityViewContainer); diff --git a/addons/mail/static/src/backend_components/list_field_activity_view/list_field_activity_view_container.js b/addons/mail/static/src/backend_components/list_field_activity_view/list_field_activity_view_container.js index 7e60dd9eed4..ad507248c3f 100644 --- a/addons/mail/static/src/backend_components/list_field_activity_view/list_field_activity_view_container.js +++ b/addons/mail/static/src/backend_components/list_field_activity_view/list_field_activity_view_container.js @@ -78,6 +78,14 @@ export class ListFieldActivityViewContainer extends Component { } Object.assign(ListFieldActivityViewContainer, { + props: { + ...standardFieldProps, + }, + template: "mail.ListFieldActivityViewContainer", +}); + +export const listFieldActivityViewContainer = { + component: ListFieldActivityViewContainer, fieldDependencies: { activity_exception_decoration: { type: "selection" }, activity_exception_icon: { type: "char" }, @@ -86,10 +94,6 @@ Object.assign(ListFieldActivityViewContainer, { activity_type_icon: { type: "char" }, activity_type_id: { type: "many2one", relation: "mail.activity.type" }, }, - props: { - ...standardFieldProps, - }, - template: "mail.ListFieldActivityViewContainer", -}); +}; -registry.category("fields").add("list_activity", ListFieldActivityViewContainer); +registry.category("fields").add("list_activity", listFieldActivityViewContainer); diff --git a/addons/mail/static/src/js/onchange_on_keydown.js b/addons/mail/static/src/js/onchange_on_keydown.js index a0dc6b94219..ca8229635dc 100644 --- a/addons/mail/static/src/js/onchange_on_keydown.js +++ b/addons/mail/static/src/js/onchange_on_keydown.js @@ -2,8 +2,8 @@ import { patch } from "@web/core/utils/patch"; import { debounce } from "@web/core/utils/timing"; -import { CharField } from "@web/views/fields/char/char_field"; -import { TextField } from '@web/views/fields/text/text_field'; +import { CharField, charField } from "@web/views/fields/char/char_field"; +import { TextField, textField } from "@web/views/fields/text/text_field"; import { archParseBoolean } from "@web/views/utils"; const { useEffect } = owl; @@ -24,7 +24,7 @@ const onchangeOnKeydownMixin = { const triggerOnChange = debounce(this.triggerOnChange, this.props.keydownDebounceDelay); useEffect(() => { if (input.el) { - input.el.addEventListener('keydown', triggerOnChange.bind(this)); + input.el.addEventListener("keydown", triggerOnChange.bind(this)); } }); } @@ -32,12 +32,12 @@ const onchangeOnKeydownMixin = { triggerOnChange() { const input = this.input || this.textareaRef; - input.el.dispatchEvent(new Event('change')); - } + input.el.dispatchEvent(new Event("change")); + }, }; -patch(CharField.prototype, 'char_field_onchange_on_keydown', onchangeOnKeydownMixin); -patch(TextField.prototype, 'text_field_onchange_on_keydown', onchangeOnKeydownMixin); +patch(CharField.prototype, "char_field_onchange_on_keydown", onchangeOnKeydownMixin); +patch(TextField.prototype, "text_field_onchange_on_keydown", onchangeOnKeydownMixin); CharField.props = { ...CharField.props, @@ -51,18 +51,22 @@ TextField.props = { keydownDebounceDelay: { type: Number, optional: true }, }; -const charExtractProps = CharField.extractProps; -CharField.extractProps = ({ attrs, field }) => { - return Object.assign(charExtractProps({ attrs, field }), { +const charExtractProps = charField.extractProps; +charField.extractProps = (attrs) => { + return Object.assign(charExtractProps(attrs), { onchangeOnKeydown: archParseBoolean(attrs.onchange_on_keydown), - keydownDebounceDelay: attrs.keydown_debounce_delay ? Number(attrs.keydown_debounce_delay) : 2000, + keydownDebounceDelay: attrs.keydown_debounce_delay + ? Number(attrs.keydown_debounce_delay) + : 2000, }); }; -const textExtractProps = TextField.extractProps; -TextField.extractProps = ({ attrs, field }) => { - return Object.assign(textExtractProps({ attrs, field }), { - onchangeOnKeydown: archParseBoolean(attrs.onchange_on_keydown), - keydownDebounceDelay: attrs.keydown_debounce_delay ? Number(attrs.keydown_debounce_delay) : 2000, +const textExtractProps = textField.extractProps; +textField.extractProps = (params) => { + return Object.assign(textExtractProps(params), { + onchangeOnKeydown: archParseBoolean(params.attrs.onchange_on_keydown), + keydownDebounceDelay: params.attrs.keydown_debounce_delay + ? Number(params.attrs.keydown_debounce_delay) + : 2000, }); }; diff --git a/addons/mail/static/src/views/activity/activity_arch_parser.js b/addons/mail/static/src/views/activity/activity_arch_parser.js index adbe2ffb418..f5d0b77ed7a 100644 --- a/addons/mail/static/src/views/activity/activity_arch_parser.js +++ b/addons/mail/static/src/views/activity/activity_arch_parser.js @@ -35,7 +35,7 @@ export class ActivityArchParser extends XMLParser { addFieldDependencies( activeFields, models[modelName], - fieldInfo.FieldComponent.fieldDependencies + fieldInfo.field.fieldDependencies ); } diff --git a/addons/mail/static/src/views/fields/emojis_char_field/emojis_char_field.js b/addons/mail/static/src/views/fields/emojis_char_field/emojis_char_field.js index 8b30660d195..c22b7dd7092 100644 --- a/addons/mail/static/src/views/fields/emojis_char_field/emojis_char_field.js +++ b/addons/mail/static/src/views/fields/emojis_char_field/emojis_char_field.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; import { patch } from "@web/core/utils/patch"; import MailEmojisMixin from "@mail/js/emojis_mixin"; import { EmojisFieldCommon } from "@mail/views/fields/emojis_field_common/emojis_field_common"; @@ -23,5 +23,11 @@ patch(EmojisCharField.prototype, "emojis_char_field_mail_mixin", MailEmojisMixin patch(EmojisCharField.prototype, "emojis_char_field_field_mixin", EmojisFieldCommon); EmojisCharField.template = "mail.EmojisCharField"; EmojisCharField.components = { ...CharField.components }; -EmojisCharField.additionalClasses = [...(CharField.additionalClasses || []), "o_field_text"]; -registry.category("fields").add("char_emojis", EmojisCharField); + +export const emojisCharField = { + ...charField, + component: EmojisCharField, + additionalClasses: [...(charField.additionalClasses || []), "o_field_text"], +}; + +registry.category("fields").add("char_emojis", emojisCharField); diff --git a/addons/mail/static/src/views/fields/emojis_text_field/emojis_text_field.js b/addons/mail/static/src/views/fields/emojis_text_field/emojis_text_field.js index c937405dc3d..ab33e106c75 100644 --- a/addons/mail/static/src/views/fields/emojis_text_field/emojis_text_field.js +++ b/addons/mail/static/src/views/fields/emojis_text_field/emojis_text_field.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { TextField } from "@web/views/fields/text/text_field"; +import { TextField, textField } from "@web/views/fields/text/text_field"; import { patch } from "@web/core/utils/patch"; import MailEmojisMixin from "@mail/js/emojis_mixin"; import { EmojisFieldCommon } from "@mail/views/fields/emojis_field_common/emojis_field_common"; @@ -21,5 +21,11 @@ patch(EmojisTextField.prototype, "emojis_char_field_mail_mixin", MailEmojisMixin patch(EmojisTextField.prototype, "emojis_text_field_field_mixin", EmojisFieldCommon); EmojisTextField.template = "mail.EmojisTextField"; EmojisTextField.components = { ...TextField.components }; -EmojisTextField.additionalClasses = [...(TextField.additionalClasses || []), "o_field_text"]; -registry.category("fields").add("text_emojis", EmojisTextField); + +export const emojisTextField = { + ...textField, + component: EmojisTextField, + additionalClasses: [...(textField.additionalClasses || []), "o_field_text"], +}; + +registry.category("fields").add("text_emojis", emojisTextField); diff --git a/addons/mail/static/src/views/fields/many2many_avatar_user_field/many2many_avatar_user_field.js b/addons/mail/static/src/views/fields/many2many_avatar_user_field/many2many_avatar_user_field.js index 9a659cb4308..7cd7c52fb1e 100644 --- a/addons/mail/static/src/views/fields/many2many_avatar_user_field/many2many_avatar_user_field.js +++ b/addons/mail/static/src/views/fields/many2many_avatar_user_field/many2many_avatar_user_field.js @@ -5,6 +5,8 @@ import { TagsList } from "@web/views/fields/many2many_tags/tags_list"; import { Many2ManyTagsAvatarField, ListKanbanMany2ManyTagsAvatarField, + many2ManyTagsAvatarField, + listKanbanMany2ManyTagsAvatarField, } from "@web/views/fields/many2many_tags_avatar/many2many_tags_avatar_field"; import { useOpenChat } from "@mail/views/open_chat_hook"; import { useAssignUserCommand } from "@mail/views/fields/assign_user_command_hook"; @@ -32,9 +34,14 @@ Many2ManyTagsAvatarUserField.components = { ...Many2ManyTagsAvatarField.components, TagsList: Many2ManyAvatarUserTagsList, }; -Many2ManyTagsAvatarUserField.additionalClasses = ["o_field_many2many_tags_avatar"]; -registry.category("fields").add("many2many_avatar_user", Many2ManyTagsAvatarUserField); +export const many2ManyTagsAvatarUserField = { + ...many2ManyTagsAvatarField, + component: Many2ManyTagsAvatarUserField, + additionalClasses: ["o_field_many2many_tags_avatar"], +}; + +registry.category("fields").add("many2many_avatar_user", many2ManyTagsAvatarUserField); export class KanbanMany2ManyTagsAvatarUserField extends ListKanbanMany2ManyTagsAvatarField { setup() { @@ -63,8 +70,15 @@ KanbanMany2ManyTagsAvatarUserField.components = { ...ListKanbanMany2ManyTagsAvatarField.components, TagsList: Many2ManyAvatarUserTagsList, }; -KanbanMany2ManyTagsAvatarUserField.additionalClasses = ["o_field_many2many_tags_avatar"]; -registry.category("fields").add("kanban.many2many_avatar_user", KanbanMany2ManyTagsAvatarUserField); -registry.category("fields").add("list.many2many_avatar_user", KanbanMany2ManyTagsAvatarUserField); -registry.category("fields").add("activity.many2many_avatar_user", KanbanMany2ManyTagsAvatarUserField); +export const kanbanMany2ManyTagsAvatarUserField = { + ...listKanbanMany2ManyTagsAvatarField, + component: ListKanbanMany2ManyTagsAvatarField, + additionalClasses: ["o_field_many2many_tags_avatar"], +}; + +registry.category("fields").add("kanban.many2many_avatar_user", kanbanMany2ManyTagsAvatarUserField); +registry.category("fields").add("list.many2many_avatar_user", kanbanMany2ManyTagsAvatarUserField); +registry + .category("fields") + .add("activity.many2many_avatar_user", kanbanMany2ManyTagsAvatarUserField); diff --git a/addons/mail/static/src/views/fields/many2many_tags_email/many2many_tags_email.js b/addons/mail/static/src/views/fields/many2many_tags_email/many2many_tags_email.js index 236ef9b3282..f1d69a5069e 100644 --- a/addons/mail/static/src/views/fields/many2many_tags_email/many2many_tags_email.js +++ b/addons/mail/static/src/views/fields/many2many_tags_email/many2many_tags_email.js @@ -4,7 +4,10 @@ import { registry } from "@web/core/registry"; import { useOpenMany2XRecord } from "@web/views/fields/relational_utils"; import { sprintf } from "@web/core/utils/strings"; -import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { + Many2ManyTagsField, + many2ManyTagsField, +} from "@web/views/fields/many2many_tags/many2many_tags_field"; import { TagsList } from "@web/views/fields/many2many_tags/tags_list"; import { onMounted, onWillUpdateProps } from "@odoo/owl"; @@ -87,10 +90,13 @@ FieldMany2ManyTagsEmail.components = { TagsList: FieldMany2ManyTagsEmailTagsList, }; -FieldMany2ManyTagsEmail.fieldsToFetch = (fieldInfo) => { - return [...Many2ManyTagsField.fieldsToFetch(fieldInfo), { name: "email", type: "char" }]; +export const fieldMany2ManyTagsEmail = { + ...many2ManyTagsField, + component: FieldMany2ManyTagsEmail, + fieldsToFetch: (fieldInfo) => { + return [...many2ManyTagsField.fieldsToFetch(fieldInfo), { name: "email", type: "char" }]; + }, + additionalClasses: ["o_field_many2many_tags"], }; -FieldMany2ManyTagsEmail.additionalClasses = ["o_field_many2many_tags"]; - -registry.category("fields").add("many2many_tags_email", FieldMany2ManyTagsEmail); +registry.category("fields").add("many2many_tags_email", fieldMany2ManyTagsEmail); diff --git a/addons/mail/static/src/views/fields/many2one_avatar_user_field/many2one_avatar_user_field.js b/addons/mail/static/src/views/fields/many2one_avatar_user_field/many2one_avatar_user_field.js index b7abd36d2e0..babdb3bbcc2 100644 --- a/addons/mail/static/src/views/fields/many2one_avatar_user_field/many2one_avatar_user_field.js +++ b/addons/mail/static/src/views/fields/many2one_avatar_user_field/many2one_avatar_user_field.js @@ -1,7 +1,10 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2OneAvatarField } from "@web/views/fields/many2one_avatar/many2one_avatar_field"; +import { + Many2OneAvatarField, + many2OneAvatarField, +} from "@web/views/fields/many2one_avatar/many2one_avatar_field"; import { useOpenChat } from "@mail/views/open_chat_hook"; import { useAssignUserCommand } from "@mail/views/fields/assign_user_command_hook"; @@ -17,9 +20,14 @@ export class Many2OneAvatarUserField extends Many2OneAvatarField { } } Many2OneAvatarUserField.template = "mail.Many2OneAvatarUserField"; -Many2OneAvatarUserField.additionalClasses = ["o_field_many2one_avatar"]; -registry.category("fields").add("many2one_avatar_user", Many2OneAvatarUserField); +export const many2OneAvatarUserField = { + ...many2OneAvatarField, + component: Many2OneAvatarUserField, + additionalClasses: ["o_field_many2one_avatar"], +}; + +registry.category("fields").add("many2one_avatar_user", many2OneAvatarUserField); export class KanbanMany2OneAvatarUserField extends Many2OneAvatarUserField { /** @@ -37,12 +45,15 @@ KanbanMany2OneAvatarUserField.props = { ...Many2OneAvatarUserField.props, displayAvatarName: { type: Boolean, optional: true }, }; -KanbanMany2OneAvatarUserField.extractProps = ({ attrs, field }) => { - return { - ...Many2OneAvatarUserField.extractProps({ attrs, field }), - displayAvatarName: attrs.options.display_avatar_name || false, - }; + +export const kanbanMany2OneAvatarUserField = { + ...many2OneAvatarUserField, + component: KanbanMany2OneAvatarUserField, + extractProps: (params) => ({ + ...many2OneAvatarUserField.extractProps(params), + displayAvatarName: params.attrs.options.display_avatar_name || false, + }), }; -registry.category("fields").add("kanban.many2one_avatar_user", KanbanMany2OneAvatarUserField); -registry.category("fields").add("activity.many2one_avatar_user", KanbanMany2OneAvatarUserField); +registry.category("fields").add("kanban.many2one_avatar_user", kanbanMany2OneAvatarUserField); +registry.category("fields").add("activity.many2one_avatar_user", kanbanMany2OneAvatarUserField); diff --git a/addons/mass_mailing/static/src/js/mailing_m2o_filter.js b/addons/mass_mailing/static/src/js/mailing_m2o_filter.js index d0893cfca84..9012d3888dc 100644 --- a/addons/mass_mailing/static/src/js/mailing_m2o_filter.js +++ b/addons/mass_mailing/static/src/js/mailing_m2o_filter.js @@ -3,7 +3,7 @@ import { registry } from '@web/core/registry'; import { Dropdown } from "@web/core/dropdown/dropdown"; import { useService } from "@web/core/utils/hooks"; -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; import Domain from 'web.Domain'; const { useState, useEffect } = owl; @@ -149,12 +149,15 @@ FieldMany2OneMailingFilter.defaultProps = { domain_field: "mailing_domain", model_field: "mailing_model_id", }; -FieldMany2OneMailingFilter.extractProps = ({ field, attrs }) => { - return { - ...Many2OneField.extractProps({ field, attrs }), - domain_field: attrs.options.domain_field, - model_field: attrs.options.model_field, - } + +export const fieldMany2OneMailingFilter = { + ...many2OneField, + component: FieldMany2OneMailingFilter, + extractProps: (params) => ({ + ...many2OneField.extractProps(params), + domain_field: params.attrs.options.domain_field, + model_field: params.attrs.options.model_field, + }), }; -registry.category('fields').add('mailing_filter', FieldMany2OneMailingFilter); +registry.category("fields").add("mailing_filter", fieldMany2OneMailingFilter); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js index 2653bda4ca1..488cb379b96 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js @@ -9,7 +9,7 @@ import { loadBundle, loadJS } from "@web/core/assets"; import { qweb } from 'web.core'; import { useService } from "@web/core/utils/hooks"; import { buildQuery } from "web.rpc"; -import { HtmlField } from "@web_editor/js/backend/html_field"; +import { HtmlField, htmlField } from "@web_editor/js/backend/html_field"; import { getWysiwygClass } from 'web_editor.loader'; import { device } from 'web.config'; import { MassMailingMobilePreviewDialog } from "./mass_mailing_mobile_preview"; @@ -584,16 +584,16 @@ MassMailingHtmlField.props = { iframeHtmlClass: { type: String, optional: true }, }; -MassMailingHtmlField.displayName = _lt("Email"); -MassMailingHtmlField.extractProps = (...args) => { - const [{ attrs }] = args; - const htmlProps = HtmlField.extractProps(...args); - return { - ...htmlProps, - filterTemplates: attrs.options.filterTemplates, - inlineField: attrs.options['inline-field'], - iframeHtmlClass: attrs['iframeHtmlClass'], - }; +export const massMailingHtmlField = { + ...htmlField, + component: MassMailingHtmlField, + displayName: _lt("Email"), + extractProps: (params) => ({ + ...htmlField.extractProps(params), + filterTemplates: params.attrs.options.filterTemplates, + inlineField: params.attrs.options['inline-field'], + iframeHtmlClass: params.attrs['iframeHtmlClass'], + }), }; -registry.category("fields").add("mass_mailing_html", MassMailingHtmlField); +registry.category("fields").add("mass_mailing_html", massMailingHtmlField); diff --git a/addons/mrp/static/src/views/fields/google_slides_viewer.js b/addons/mrp/static/src/views/fields/google_slides_viewer.js index a569407d52c..507a57cd5c4 100644 --- a/addons/mrp/static/src/views/fields/google_slides_viewer.js +++ b/addons/mrp/static/src/views/fields/google_slides_viewer.js @@ -3,7 +3,7 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; const { useState } = owl; @@ -48,6 +48,11 @@ export class SlidesViewer extends CharField { } SlidesViewer.template = "mrp.SlidesViewer"; -SlidesViewer.displayName = _lt("Google Slides Viewer"); -registry.category("fields").add("embed_viewer", SlidesViewer); +export const slidesViewer = { + ...charField, + component: SlidesViewer, + displayName: _lt("Google Slides Viewer"), +}; + +registry.category("fields").add("embed_viewer", slidesViewer); diff --git a/addons/mrp/static/src/widgets/mrp_should_consume.js b/addons/mrp/static/src/widgets/mrp_should_consume.js index 12d3148f31b..fea60f4e087 100644 --- a/addons/mrp/static/src/widgets/mrp_should_consume.js +++ b/addons/mrp/static/src/widgets/mrp_should_consume.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { FloatField } from "@web/views/fields/float/float_field"; +import { FloatField, floatField } from "@web/views/fields/float/float_field"; import { registry } from "@web/core/registry"; import { formatFloat } from "@web/views/fields/formatters"; @@ -44,6 +44,11 @@ export class MrpShouldConsumeOwl extends FloatField { } MrpShouldConsumeOwl.template = "mrp.ShouldConsume"; -MrpShouldConsumeOwl.displayName = "MRP Should Consume"; -registry.category("fields").add("mrp_should_consume", MrpShouldConsumeOwl); +export const mrpShouldConsumeOwl = { + ...floatField, + component: MrpShouldConsumeOwl, + displayName: "MRP Should Consume", +}; + +registry.category("fields").add("mrp_should_consume", mrpShouldConsumeOwl); diff --git a/addons/mrp/static/src/widgets/mrp_workorder_popover.js b/addons/mrp/static/src/widgets/mrp_workorder_popover.js index 45be7abc19b..0455bb6d189 100644 --- a/addons/mrp/static/src/widgets/mrp_workorder_popover.js +++ b/addons/mrp/static/src/widgets/mrp_workorder_popover.js @@ -1,7 +1,11 @@ /** @odoo-module */ import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; -import { PopoverComponent, PopoverWidgetField } from '@stock/widgets/popover_widget'; +import { + PopoverComponent, + PopoverWidgetField, + popoverWidgetField, +} from "@stock/widgets/popover_widget"; /** * Link to a Char field representing a JSON: @@ -36,4 +40,7 @@ WorkOrderPopoverField.components = { Popover: WorkOrderPopover }; -registry.category("fields").add("mrp_workorder_popover", WorkOrderPopoverField); \ No newline at end of file +registry.category("fields").add("mrp_workorder_popover", { + ...popoverWidgetField, + component: WorkOrderPopoverField, +}); \ No newline at end of file diff --git a/addons/mrp/static/src/widgets/timer.js b/addons/mrp/static/src/widgets/timer.js index feaedccb48b..f3d8dedc538 100644 --- a/addons/mrp/static/src/widgets/timer.js +++ b/addons/mrp/static/src/widgets/timer.js @@ -84,7 +84,6 @@ export class MrpTimer extends Component { } } -MrpTimer.supportedTypes = ["float"]; MrpTimer.props = { ...standardFieldProps, duration: { type: Number, optional: true }, @@ -93,5 +92,10 @@ MrpTimer.props = { }; MrpTimer.template = "mrp.MrpTimer"; -registry.category("fields").add("mrp_timer", MrpTimer); +export const mrpTimer = { + component: MrpTimer, + supportedTypes: ["float"], +}; + +registry.category("fields").add("mrp_timer", mrpTimer); registry.category("formatters").add("mrp_timer", formatMinutes); \ No newline at end of file diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js index 1896b7c143c..a8140107080 100644 --- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js +++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js @@ -5,7 +5,7 @@ import { AutoComplete } from "@web/core/autocomplete/autocomplete"; import { useChildRef } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; import { _t } from "@web/core/l10n/translation"; -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; import { useInputField } from "@web/views/fields/input_field_hook"; import { loadJS } from "@web/core/assets"; @@ -97,4 +97,9 @@ PartnerAutoCompleteCharField.components = { AutoComplete, }; -registry.category("fields").add("field_partner_autocomplete", PartnerAutoCompleteCharField); +export const partnerAutoCompleteCharField = { + ...charField, + component: PartnerAutoCompleteCharField, +}; + +registry.category("fields").add("field_partner_autocomplete", partnerAutoCompleteCharField); diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js index baadbe738ad..a8daeb68873 100644 --- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js +++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { Many2XAutocomplete } from '@web/views/fields/relational_utils'; -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; @@ -68,4 +68,9 @@ PartnerAutoCompleteMany2one.components = { Many2XAutocomplete: PartnerMany2XAutocomplete, } -registry.category("fields").add("res_partner_many2one", PartnerAutoCompleteMany2one); +export const partnerAutoCompleteMany2one = { + ...many2OneField, + component: PartnerAutoCompleteMany2one, +}; + +registry.category("fields").add("res_partner_many2one", partnerAutoCompleteMany2one); diff --git a/addons/portal/static/src/views/fields/portal_wizard_user_one2many.js b/addons/portal/static/src/views/fields/portal_wizard_user_one2many.js index 5b8773234d3..f491e60a1fe 100644 --- a/addons/portal/static/src/views/fields/portal_wizard_user_one2many.js +++ b/addons/portal/static/src/views/fields/portal_wizard_user_one2many.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { PortalWizardUserListController } from "../list/portal_wizard_user_list_controller"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; import { registry } from "@web/core/registry"; export class PortalUserX2ManyField extends X2ManyField {} @@ -10,4 +10,9 @@ PortalUserX2ManyField.components = { Controller: PortalWizardUserListController, }; -registry.category("fields").add("portal_wizard_user_one2many", PortalUserX2ManyField); +export const portalUserX2ManyField = { + ...x2ManyField, + component: PortalUserX2ManyField, +}; + +registry.category("fields").add("portal_wizard_user_one2many", portalUserX2ManyField); diff --git a/addons/project/static/src/components/project_private_task_many2one_field/project_private_task_many2one_field.js b/addons/project/static/src/components/project_private_task_many2one_field/project_private_task_many2one_field.js index 9dea8a35ca3..5c9487fc839 100644 --- a/addons/project/static/src/components/project_private_task_many2one_field/project_private_task_many2one_field.js +++ b/addons/project/static/src/components/project_private_task_many2one_field/project_private_task_many2one_field.js @@ -1,9 +1,14 @@ /** @odoo-module */ import { registry } from '@web/core/registry'; -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; export class ProjectPrivateTaskMany2OneField extends Many2OneField { } ProjectPrivateTaskMany2OneField.template = 'project.ProjectPrivateTaskMany2OneField'; -registry.category('fields').add('project_private_task', ProjectPrivateTaskMany2OneField); +export const projectPrivateTaskMany2OneField = { + ...many2OneField, + component: ProjectPrivateTaskMany2OneField, +}; + +registry.category("fields").add("project_private_task", projectPrivateTaskMany2OneField); diff --git a/addons/project/static/src/components/project_state_selection/project_state_selection.js b/addons/project/static/src/components/project_state_selection/project_state_selection.js index 8ce6f9d1a2f..0be3a7aef74 100644 --- a/addons/project/static/src/components/project_state_selection/project_state_selection.js +++ b/addons/project/static/src/components/project_state_selection/project_state_selection.js @@ -1,7 +1,10 @@ /** @odoo-module */ import { registry } from '@web/core/registry'; -import { StateSelectionField } from '@web/views/fields/state_selection/state_selection_field'; +import { + StateSelectionField, + stateSelectionField, +} from "@web/views/fields/state_selection/state_selection_field"; import { STATUS_COLORS, STATUS_COLOR_PREFIX } from '../../utils/project_utils'; @@ -27,4 +30,9 @@ export class ProjectStateSelectionField extends StateSelectionField { } } -registry.category('fields').add('kanban.project_state_selection', ProjectStateSelectionField); +export const projectStateSelectionField = { + ...stateSelectionField, + component: ProjectStateSelectionField, +}; + +registry.category("fields").add("kanban.project_state_selection", projectStateSelectionField); diff --git a/addons/project/static/src/components/project_status_with_color_selection/project_status_with_color_selection_field.js b/addons/project/static/src/components/project_status_with_color_selection/project_status_with_color_selection_field.js index 7a28e722805..e840c3802fb 100644 --- a/addons/project/static/src/components/project_status_with_color_selection/project_status_with_color_selection_field.js +++ b/addons/project/static/src/components/project_status_with_color_selection/project_status_with_color_selection_field.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { SelectionField } from '@web/views/fields/selection/selection_field'; +import { SelectionField, selectionField } from '@web/views/fields/selection/selection_field'; import { registry } from '@web/core/registry'; import { STATUS_COLORS, STATUS_COLOR_PREFIX } from '../../utils/project_utils'; @@ -22,4 +22,9 @@ export class ProjectStatusWithColorSelectionField extends SelectionField { } ProjectStatusWithColorSelectionField.template = 'project.ProjectStatusWithColorSelectionField'; -registry.category('fields').add('status_with_color', ProjectStatusWithColorSelectionField); +export const projectStatusWithColorSelectionField = { + ...selectionField, + component: ProjectStatusWithColorSelectionField, +}; + +registry.category("fields").add("status_with_color", projectStatusWithColorSelectionField); diff --git a/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.js b/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.js index 5c73a2961bb..0cc014f92c9 100644 --- a/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.js +++ b/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.js @@ -1,7 +1,7 @@ /** @odoo-module */ import { registry } from '@web/core/registry'; -import { CharField } from '@web/views/fields/char/char_field'; +import { CharField, charField } from '@web/views/fields/char/char_field'; import { formatChar } from '@web/views/fields/formatters'; class ProjectTaskNameWithSubtaskCountCharField extends CharField { @@ -12,4 +12,7 @@ class ProjectTaskNameWithSubtaskCountCharField extends CharField { ProjectTaskNameWithSubtaskCountCharField.template = 'project.ProjectTaskNameWithSubtaskCountCharField'; -registry.category('fields').add('name_with_subtask_count', ProjectTaskNameWithSubtaskCountCharField); +registry.category("fields").add("name_with_subtask_count", { + ...charField, + component: ProjectTaskNameWithSubtaskCountCharField, +}); diff --git a/addons/project/static/src/views/fields/project_task_priority_switch_field.js b/addons/project/static/src/views/fields/project_task_priority_switch_field.js index c6c7acaa694..f18494167a5 100644 --- a/addons/project/static/src/views/fields/project_task_priority_switch_field.js +++ b/addons/project/static/src/views/fields/project_task_priority_switch_field.js @@ -2,7 +2,7 @@ import { sprintf } from "@web/core/utils/strings"; import { registry } from "@web/core/registry"; -import { PriorityField } from "@web/views/fields/priority/priority_field"; +import { PriorityField, priorityField } from "@web/views/fields/priority/priority_field"; import { useCommand } from "@web/core/commands/command_hook"; import { useState } from "@odoo/owl"; @@ -29,4 +29,9 @@ export class PrioritySwitchField extends PriorityField { } } -registry.category("fields").add("priority_switch", PrioritySwitchField); +export const prioritySwitchField = { + ...priorityField, + component: PrioritySwitchField, +}; + +registry.category("fields").add("priority_switch", prioritySwitchField); diff --git a/addons/purchase_product_matrix/static/src/js/purchase_product_field.js b/addons/purchase_product_matrix/static/src/js/purchase_product_field.js index bf973084f28..e42daab6869 100644 --- a/addons/purchase_product_matrix/static/src/js/purchase_product_field.js +++ b/addons/purchase_product_matrix/static/src/js/purchase_product_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from '@web/core/registry'; -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; import { ProductMatrixDialog } from "@product_matrix/js/product_matrix_dialog"; import { useService } from "@web/core/utils/hooks"; @@ -107,4 +107,9 @@ export class PurchaseOrderLineProductField extends Many2OneField { PurchaseOrderLineProductField.template = "purchase.PurchaseProductField"; -registry.category("fields").add("pol_product_many2one", PurchaseOrderLineProductField); +export const purchaseOrderLineProductField = { + ...many2OneField, + component: PurchaseOrderLineProductField, +}; + +registry.category("fields").add("pol_product_many2one", purchaseOrderLineProductField); diff --git a/addons/purchase_requisition/static/src/widgets/purchase_order_alternatives_widget.js b/addons/purchase_requisition/static/src/widgets/purchase_order_alternatives_widget.js index 4ada4a25f4a..31afa394b5d 100644 --- a/addons/purchase_requisition/static/src/widgets/purchase_order_alternatives_widget.js +++ b/addons/purchase_requisition/static/src/widgets/purchase_order_alternatives_widget.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; import { ListRenderer } from "@web/views/list/list_renderer"; @@ -43,4 +43,9 @@ FieldMany2ManyAltPOs.components = { ListRenderer: FieldMany2ManyAltPOsRenderer, }; -registry.category("fields").add("many2many_alt_pos", FieldMany2ManyAltPOs); +export const fieldMany2ManyAltPOs = { + ...x2ManyField, + component: FieldMany2ManyAltPOs, +}; + +registry.category("fields").add("many2many_alt_pos", fieldMany2ManyAltPOs); diff --git a/addons/resource/static/src/section_one2many_field.js b/addons/resource/static/src/section_one2many_field.js index 54ab8dd10f1..e4937b0b74b 100644 --- a/addons/resource/static/src/section_one2many_field.js +++ b/addons/resource/static/src/section_one2many_field.js @@ -2,7 +2,7 @@ import { SectionListRenderer } from "./section_list_renderer"; import { registry } from "@web/core/registry"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; class SectionOneToManyField extends X2ManyField {} SectionOneToManyField.components = { @@ -14,4 +14,7 @@ SectionOneToManyField.defaultProps = { editable: "bottom", }; -registry.category("fields").add("section_one2many", SectionOneToManyField); +registry.category("fields").add("section_one2many", { + ...x2ManyField, + component: SectionOneToManyField, +}); diff --git a/addons/sale/static/src/js/product_discount_field.js b/addons/sale/static/src/js/product_discount_field.js index 72761573e5c..75821c68678 100644 --- a/addons/sale/static/src/js/product_discount_field.js +++ b/addons/sale/static/src/js/product_discount_field.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { FloatField } from "@web/views/fields/float/float_field"; +import { FloatField, floatField } from "@web/views/fields/float/float_field"; import { _lt } from "@web/core/l10n/translation"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; @@ -57,6 +57,11 @@ export function sameValue(orderLines) { ProductDiscountField.components = { ConfirmationDialog }; ProductDiscountField.template = "sale.ProductDiscountField"; -ProductDiscountField.displayName = _lt("Disc.%"); -registry.category("fields").add("sol_discount", ProductDiscountField) +export const productDiscountField = { + ...floatField, + component: ProductDiscountField, + displayName: _lt("Disc.%"), +}; + +registry.category("fields").add("sol_discount", productDiscountField) diff --git a/addons/sale/static/src/js/sale_product_field.js b/addons/sale/static/src/js/sale_product_field.js index 83c29acc0f8..da52c54e2a5 100644 --- a/addons/sale/static/src/js/sale_product_field.js +++ b/addons/sale/static/src/js/sale_product_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from '@web/core/registry'; -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; export class SaleOrderLineProductField extends Many2OneField { @@ -109,4 +109,9 @@ export class SaleOrderLineProductField extends Many2OneField { SaleOrderLineProductField.template = "sale.SaleProductField"; -registry.category("fields").add("sol_product_many2one", SaleOrderLineProductField); +export const saleOrderLineProductField = { + ...many2OneField, + component: SaleOrderLineProductField, +}; + +registry.category("fields").add("sol_product_many2one", saleOrderLineProductField); diff --git a/addons/sale/static/src/js/sale_progressbar_field.js b/addons/sale/static/src/js/sale_progressbar_field.js index 49491c3e4c6..03aef776944 100644 --- a/addons/sale/static/src/js/sale_progressbar_field.js +++ b/addons/sale/static/src/js/sale_progressbar_field.js @@ -2,7 +2,10 @@ import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { KanbanProgressBarField } from "@web/views/fields/progress_bar/kanban_progress_bar_field"; +import { + KanbanProgressBarField, + kanbanProgressBarField, +} from "@web/views/fields/progress_bar/kanban_progress_bar_field"; const { useEffect } = owl; @@ -42,4 +45,9 @@ export class SaleProgressBarField extends KanbanProgressBarField { */ SaleProgressBarField.template = "sale.SaleProgressBarField"; -registry.category("fields").add("sales_team_progressbar", SaleProgressBarField); +export const saleProgressBarField = { + ...kanbanProgressBarField, + component: SaleProgressBarField, +}; + +registry.category("fields").add("sales_team_progressbar", saleProgressBarField); diff --git a/addons/sale_expense/static/src/js/sale_order_many2one.js b/addons/sale_expense/static/src/js/sale_order_many2one.js index 38e0f99c58c..039bbf3eadf 100644 --- a/addons/sale_expense/static/src/js/sale_order_many2one.js +++ b/addons/sale_expense/static/src/js/sale_order_many2one.js @@ -1,6 +1,6 @@ /** @odoo-module alias=sale_expense.sale_order_many2one **/ -import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; +import { Many2OneField, many2OneField } from '@web/views/fields/many2one/many2one_field'; import { registry } from "@web/core/registry"; @@ -21,5 +21,10 @@ export class OrderField extends Many2OneField { } } -registry.category('fields').add('sale_order_many2one', OrderField) +export const orderField = { + ...many2OneField, + component: OrderField, +}; + +registry.category("fields").add("sale_order_many2one", orderField); registry.add('sale_order_many2one', OrderField); diff --git a/addons/sale_timesheet/static/src/components/so_line_field/so_line_field.js b/addons/sale_timesheet/static/src/components/so_line_field/so_line_field.js index 00c4101ad8b..9a3d2ab9599 100644 --- a/addons/sale_timesheet/static/src/components/so_line_field/so_line_field.js +++ b/addons/sale_timesheet/static/src/components/so_line_field/so_line_field.js @@ -1,8 +1,8 @@ /** @odoo-module */ import { registry } from "@web/core/registry"; -import { Many2OneField } from "@web/views/fields/many2one/many2one_field"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { Many2OneField, many2OneField } from "@web/views/fields/many2one/many2one_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; export class SoLineField extends Many2OneField { setup() { @@ -18,8 +18,17 @@ export class SoLineField extends Many2OneField { } } -export class TimesheetsOne2ManyField extends X2ManyField {} -TimesheetsOne2ManyField.additionalClasses = ['o_field_one2many']; -registry.category("fields").add('so_line_one2many', TimesheetsOne2ManyField); // TODO: Remove me when the gantt view is converted in OWL +export const soLineField = { + ...many2OneField, + component: SoLineField, +}; +registry.category("fields").add("so_line_field", soLineField); -registry.category("fields").add("so_line_field", SoLineField); +export class TimesheetsOne2ManyField extends X2ManyField {} +export const timesheetsOne2ManyField = { + ...x2ManyField, + component: TimesheetsOne2ManyField, + additionalClasses: ['o_field_one2many'], +}; + +registry.category("fields").add('so_line_one2many', timesheetsOne2ManyField); // TODO: Remove me when the gantt view is converted in OWL diff --git a/addons/sms/static/src/components/phone_field/phone_field.js b/addons/sms/static/src/components/phone_field/phone_field.js index 6441de17581..a83b41d15b1 100644 --- a/addons/sms/static/src/components/phone_field/phone_field.js +++ b/addons/sms/static/src/components/phone_field/phone_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { patch } from "@web/core/utils/patch"; -import { PhoneField } from "@web/views/fields/phone/phone_field"; +import { PhoneField, phoneField, formPhoneField } from "@web/views/fields/phone/phone_field"; import { SendSMSButton } from '@sms/components/sms_button/sms_button'; patch(PhoneField, "sms.PhoneField", { @@ -17,10 +17,16 @@ patch(PhoneField, "sms.PhoneField", { ...PhoneField.props, enableButton: { type: Boolean, optional: true }, }, - extractProps: ({ attrs }) => { +}); + +const patchDescr = { + extractProps({ attrs }) { return { + ...this._super({ attrs }), enableButton: attrs.options.enable_sms, - placeholder: attrs.placeholder, }; }, -}); +}; + +patch(phoneField, "sms.PhoneField", patchDescr); +patch(formPhoneField, "sms.PhoneField", patchDescr); diff --git a/addons/sms/static/src/components/sms_button/sms_button.js b/addons/sms/static/src/components/sms_button/sms_button.js index 884976b8456..b9337a88295 100644 --- a/addons/sms/static/src/components/sms_button/sms_button.js +++ b/addons/sms/static/src/components/sms_button/sms_button.js @@ -34,3 +34,4 @@ export class SendSMSButton extends Component { } }; SendSMSButton.template = "sms.SendSMSButton"; +SendSMSButton.props = ["*"]; diff --git a/addons/sms/static/src/components/sms_widget/fields_sms_widget.js b/addons/sms/static/src/components/sms_widget/fields_sms_widget.js index 6481dbff149..5ecf85bd8e1 100644 --- a/addons/sms/static/src/components/sms_widget/fields_sms_widget.js +++ b/addons/sms/static/src/components/sms_widget/fields_sms_widget.js @@ -1,6 +1,9 @@ /** @odoo-module **/ -import { EmojisTextField} from '@mail/views/fields/emojis_text_field/emojis_text_field'; +import { + EmojisTextField, + emojisTextField, +} from "@mail/views/fields/emojis_text_field/emojis_text_field"; import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; @@ -94,5 +97,15 @@ export class SmsWidget extends EmojisTextField { } } SmsWidget.template = 'sms.SmsWidget'; -SmsWidget.additionalClasses = [...(EmojisTextField.additionalClasses || []), 'o_field_text', 'o_field_text_emojis']; -registry.category("fields").add("sms_widget", SmsWidget); + +export const smsWidget = { + ...emojisTextField, + component: SmsWidget, + additionalClasses: [ + ...(emojisTextField.additionalClasses || []), + "o_field_text", + "o_field_text_emojis", + ], +}; + +registry.category("fields").add("sms_widget", smsWidget); diff --git a/addons/stock/static/src/views/picking_form/stock_move_one2many.js b/addons/stock/static/src/views/picking_form/stock_move_one2many.js index 1c4ca1b6a45..ba80a2e4b5d 100644 --- a/addons/stock/static/src/views/picking_form/stock_move_one2many.js +++ b/addons/stock/static/src/views/picking_form/stock_move_one2many.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; import { ListRenderer } from "@web/views/list/list_renderer"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; import { ViewButton } from "@web/views/view_button/view_button"; class MoveViewButton extends ViewButton { @@ -30,4 +30,9 @@ MovesListRenderer.components = { ...ListRenderer.components, ViewButton: MoveVie export class StockMoveX2ManyField extends X2ManyField {} StockMoveX2ManyField.components = { ...X2ManyField.components, ListRenderer: MovesListRenderer }; -registry.category("fields").add("stock_move_one2many", StockMoveX2ManyField); +export const stockMoveX2ManyField = { + ...x2ManyField, + component: StockMoveX2ManyField, +}; + +registry.category("fields").add("stock_move_one2many", stockMoveX2ManyField); diff --git a/addons/stock/static/src/widgets/counted_quantity_widget.js b/addons/stock/static/src/widgets/counted_quantity_widget.js index 2bd47ecaedd..a25421ca9a4 100644 --- a/addons/stock/static/src/widgets/counted_quantity_widget.js +++ b/addons/stock/static/src/widgets/counted_quantity_widget.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { FloatField } from "@web/views/fields/float/float_field"; +import { FloatField, floatField } from "@web/views/fields/float/float_field"; import { registry } from "@web/core/registry"; const { useEffect, useRef } = owl; @@ -41,4 +41,9 @@ export class CountedQuantityWidgetField extends FloatField { } } -registry.category("fields").add("counted_quantity_widget", CountedQuantityWidgetField); +export const countedQuantityWidgetField = { + ...floatField, + component: CountedQuantityWidgetField, +}; + +registry.category("fields").add("counted_quantity_widget", countedQuantityWidgetField); diff --git a/addons/stock/static/src/widgets/forecast_widget.js b/addons/stock/static/src/widgets/forecast_widget.js index 8477adb5612..379b8417856 100644 --- a/addons/stock/static/src/widgets/forecast_widget.js +++ b/addons/stock/static/src/widgets/forecast_widget.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { FloatField } from "@web/views/fields/float/float_field"; +import { FloatField, floatField } from "@web/views/fields/float/float_field"; import { formatDate } from "@web/core/l10n/dates"; import { formatFloat } from "@web/views/fields/formatters"; import { registry } from "@web/core/registry"; @@ -49,4 +49,9 @@ export class ForecastWidgetField extends FloatField { } ForecastWidgetField.template = "stock.ForecastWidget"; -registry.category("fields").add("forecast_widget", ForecastWidgetField); +export const forecastWidgetField = { + ...floatField, + component: ForecastWidgetField, +}; + +registry.category("fields").add("forecast_widget", forecastWidgetField); diff --git a/addons/stock/static/src/widgets/json_widget.js b/addons/stock/static/src/widgets/json_widget.js index 2fde6c285f4..d34ab220c08 100644 --- a/addons/stock/static/src/widgets/json_widget.js +++ b/addons/stock/static/src/widgets/json_widget.js @@ -16,8 +16,11 @@ export class JsonPopOver extends Component { } } -JsonPopOver.displayName = _lt("Json Popup"); -JsonPopOver.supportedTypes = ["char"]; +export const jsonPopOver = { + component: JsonPopOver, + displayName: _lt("Json Popup"), + supportedTypes: ["char"], +}; export class PopOverLeadDays extends JsonPopOver { setup() { @@ -50,8 +53,18 @@ export class PopOverLeadDays extends JsonPopOver { PopOverLeadDays.template = "stock.leadDays"; +export const popOverLeadDays = { + ...jsonPopOver, + component: PopOverLeadDays, +}; +registry.category("fields").add("lead_days_widget", popOverLeadDays); + export class ReplenishmentHistoryWidget extends JsonPopOver {} ReplenishmentHistoryWidget.template = "stock.replenishmentHistory"; -registry.category("fields").add("lead_days_widget", PopOverLeadDays); -registry.category("fields").add("replenishment_history_widget", ReplenishmentHistoryWidget); +export const replenishmentHistoryWidget = { + ...jsonPopOver, + component: ReplenishmentHistoryWidget, +}; + +registry.category("fields").add("replenishment_history_widget", replenishmentHistoryWidget); diff --git a/addons/stock/static/src/widgets/popover_widget.js b/addons/stock/static/src/widgets/popover_widget.js index c097c207f00..3ab687f6623 100644 --- a/addons/stock/static/src/widgets/popover_widget.js +++ b/addons/stock/static/src/widgets/popover_widget.js @@ -46,7 +46,12 @@ export class PopoverWidgetField extends Component { } } -PopoverWidgetField.supportedTypes = ['char']; PopoverWidgetField.template = 'stock.popoverButton'; PopoverWidgetField.components = { Popover: PopoverComponent } -registry.category("fields").add("popover_widget", PopoverWidgetField); + +export const popoverWidgetField = { + component: PopoverWidgetField, + supportedTypes: ['char'], +}; + +registry.category("fields").add("popover_widget", popoverWidgetField); diff --git a/addons/stock/static/src/widgets/stock_rescheduling_popover.js b/addons/stock/static/src/widgets/stock_rescheduling_popover.js index 9b2e221a75f..ccecdcb2b76 100644 --- a/addons/stock/static/src/widgets/stock_rescheduling_popover.js +++ b/addons/stock/static/src/widgets/stock_rescheduling_popover.js @@ -1,7 +1,11 @@ /** @odoo-module */ import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; -import { PopoverComponent, PopoverWidgetField } from '@stock/widgets/popover_widget'; +import { + PopoverComponent, + PopoverWidgetField, + popoverWidgetField, +} from "@stock/widgets/popover_widget"; class StockRescheculingPopoverComponent extends PopoverComponent { setup(){ @@ -37,4 +41,7 @@ StockRescheculingPopover.components = { Popover: StockRescheculingPopoverComponent } -registry.category("fields").add("stock_rescheduling_popover", StockRescheculingPopover); +registry.category("fields").add("stock_rescheduling_popover", { + ...popoverWidgetField, + component: StockRescheculingPopover, +}); diff --git a/addons/survey/static/src/question_page/description_page_field.js b/addons/survey/static/src/question_page/description_page_field.js index d3cd74d1609..4efe161386e 100644 --- a/addons/survey/static/src/question_page/description_page_field.js +++ b/addons/survey/static/src/question_page/description_page_field.js @@ -1,6 +1,6 @@ /** @odoo-module */ -import { CharField } from "@web/views/fields/char/char_field"; +import { CharField, charField } from "@web/views/fields/char/char_field"; import { registry } from "@web/core/registry"; const { useEffect, useRef } = owl; @@ -24,4 +24,7 @@ class DescriptionPageField extends CharField { } DescriptionPageField.template = "survey.DescriptionPageField"; -registry.category("fields").add("survey_description_page", DescriptionPageField); +registry.category("fields").add("survey_description_page", { + ...charField, + component: DescriptionPageField, +}); diff --git a/addons/survey/static/src/question_page/question_page_one2many_field.js b/addons/survey/static/src/question_page/question_page_one2many_field.js index 22a96f0ce3c..bfc910abd0e 100644 --- a/addons/survey/static/src/question_page/question_page_one2many_field.js +++ b/addons/survey/static/src/question_page/question_page_one2many_field.js @@ -5,7 +5,7 @@ import { registry } from "@web/core/registry"; import { useOpenX2ManyRecord, useX2ManyCrud, X2ManyFieldDialog } from "@web/views/fields/relational_utils"; import { patch } from '@web/core/utils/patch'; import { useService } from "@web/core/utils/hooks"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; const { useSubEnv } = owl; @@ -127,4 +127,9 @@ QuestionPageOneToManyField.defaultProps = { editable: "bottom", }; -registry.category("fields").add("question_page_one2many", QuestionPageOneToManyField); +export const questionPageOneToManyField = { + ...x2ManyField, + component: QuestionPageOneToManyField, +}; + +registry.category("fields").add("question_page_one2many", questionPageOneToManyField); diff --git a/addons/web/static/src/core/debug/profiling/profiling_qweb.js b/addons/web/static/src/core/debug/profiling/profiling_qweb.js index 08e6ffb272e..b3cac1eff66 100644 --- a/addons/web/static/src/core/debug/profiling/profiling_qweb.js +++ b/addons/web/static/src/core/debug/profiling/profiling_qweb.js @@ -8,14 +8,18 @@ import { useDebounced } from "@web/core/utils/timing"; import { Component, useState, useRef, onWillStart, onMounted, onWillUnmount } from "@odoo/owl"; -class MenuItem extends Component {} -MenuItem.template = "web.ProfilingQwebView.menuitem"; +class MenuItem extends Component { + static template = "web.ProfilingQwebView.menuitem"; +} /** * This widget is intended to be used on Text fields. It will provide Ace Editor * for display XML and Python profiling. */ export class ProfilingQwebView extends Component { + static template = "web.ProfilingQwebView"; + static components = { MenuItem }; + setup() { super.setup(); @@ -36,11 +40,11 @@ export class ProfilingQwebView extends Component { onWillStart(async () => { await loadBundle({ jsLibs: [ - '/web/static/lib/ace/ace.js', + "/web/static/lib/ace/ace.js", [ - '/web/static/lib/ace/mode-python.js', - '/web/static/lib/ace/mode-xml.js', - '/web/static/lib/ace/mode-qweb.js' + "/web/static/lib/ace/mode-python.js", + "/web/static/lib/ace/mode-xml.js", + "/web/static/lib/ace/mode-qweb.js", ], ], }); @@ -81,7 +85,7 @@ export class ProfilingQwebView extends Component { * @private * @returns {Promise} */ - async _fetchViewData () { + async _fetchViewData() { const viewIDs = Array.from(new Set(this.profile.data.map((line) => line.view_id))); const viewObjects = await this.orm.call("ir.ui.view", "search_read", [], { fields: ["id", "display_name", "key"], @@ -111,7 +115,7 @@ export class ProfilingQwebView extends Component { * @param {number} delay * @returns {string} */ - _formatDelay (delay) { + _formatDelay(delay) { return delay ? _.str.sprintf("%.1f", Math.ceil(delay * 10) / 10) : "."; } @@ -122,7 +126,7 @@ export class ProfilingQwebView extends Component { * @private * @param {Node} node - the DOM element the ace library must initialize on */ - _startAce (node) { + _startAce(node) { this.aceEditor = window.ace.edit(node); this.aceEditor.setOptions({ maxLines: Infinity, @@ -150,20 +154,22 @@ export class ProfilingQwebView extends Component { this.aceEditor.renderer.on("afterRender", this.renderProfilingInformation.bind(this)); } - renderProfilingInformation () { + renderProfilingInformation() { this._unmoutInfo(); - let flat = {}; - let arch = [{ xpath: "", children: [] }]; + const flat = {}; + const arch = [{ xpath: "", children: [] }]; const rows = this.ace.el.querySelectorAll(".ace_gutter .ace_gutter-cell"); - const elems = this.ace.el.querySelectorAll(".ace_tag-open, .ace_end-tag-close, .ace_end-tag-open, .ace_qweb"); - elems.forEach(node => { + const elems = this.ace.el.querySelectorAll( + ".ace_tag-open, .ace_end-tag-close, .ace_end-tag-open, .ace_qweb" + ); + elems.forEach((node) => { const parent = arch[arch.length - 1]; let xpath = parent.xpath; if (node.classList.contains("ace_end-tag-close")) { // Close tag. let previous = node; - while (previous = previous.previousElementSibling) { + while ((previous = previous.previousElementSibling)) { if (previous && previous.classList.contains("ace_tag-name")) { break; } @@ -220,7 +226,12 @@ export class ProfilingQwebView extends Component { i++; } xpath += "[" + i + "]"; - flat[xpath] = { xpath: xpath, tag: nodeTagName.textContent, children: [], directive: [] }; + flat[xpath] = { + xpath: xpath, + tag: nodeTagName.textContent, + children: [], + directive: [], + }; arch.push(flat[xpath]); parent.children.push(flat[xpath]); @@ -278,32 +289,32 @@ export class ProfilingQwebView extends Component { } _unmoutInfo() { if (this.hover) { - if (this.ace.el.querySelector('.o_ace_hover')) { - this.ace.el.querySelector('.o_ace_hover').remove(); + if (this.ace.el.querySelector(".o_ace_hover")) { + this.ace.el.querySelector(".o_ace_hover").remove(); } } if (this.info) { - if (this.ace.el.querySelector('.o_ace_info')) { - this.ace.el.querySelector('.o_ace_info').remove(); + if (this.ace.el.querySelector(".o_ace_info")) { + this.ace.el.querySelector(".o_ace_info").remove(); } } } _renderHover(delay, query, node) { - const xml = renderToString('web.ProfilingQwebView.hover', { + const xml = renderToString("web.ProfilingQwebView.hover", { delay: this._formatDelay(delay), query: query, }); - const div = new DOMParser().parseFromString(xml, "text/html").querySelector('div'); + const div = new DOMParser().parseFromString(xml, "text/html").querySelector("div"); node.insertBefore(div, node.firstChild); } _renderInfo(delays, querys, displayDetail, groups, node) { - const xml = renderToString('web.ProfilingQwebView.info', { + const xml = renderToString("web.ProfilingQwebView.info", { delay: this._formatDelay(delays.reduce((a, b) => a + b, 0)), query: querys.reduce((a, b) => a + b, 0) || ".", displayDetail: displayDetail, groups: groups, }); - const div = new DOMParser().parseFromString(xml, "text/html").querySelector('div'); + const div = new DOMParser().parseFromString(xml, "text/html").querySelector("div"); node.insertBefore(div, node.firstChild); } @@ -315,12 +326,14 @@ export class ProfilingQwebView extends Component { * @private * @param {MouseEvent} ev */ - _onSelectView (ev) { + _onSelectView(ev) { this.state.viewID = +ev.currentTarget.dataset.id; this._renderView(); } } -ProfilingQwebView.template = "web.ProfilingQwebView"; -ProfilingQwebView.components = { MenuItem }; -registry.category("fields").add("profiling_qweb_view", ProfilingQwebView); +export const profilingQwebView = { + component: ProfilingQwebView, +}; + +registry.category("fields").add("profiling_qweb_view", profilingQwebView); diff --git a/addons/web/static/src/views/basic_relational_model.js b/addons/web/static/src/views/basic_relational_model.js index 39b03347616..ba4c4ddafce 100644 --- a/addons/web/static/src/views/basic_relational_model.js +++ b/addons/web/static/src/views/basic_relational_model.js @@ -232,8 +232,7 @@ export class Record extends DataPoint { } } - const isSet = - activeField && activeField.FieldComponent && activeField.FieldComponent.isSet; + const isSet = activeField && activeField.field && activeField.field.isSet; if (this.isRequired(fieldName) && isSet && !isSet(this.data[fieldName])) { this.setInvalidField(fieldName); diff --git a/addons/web/static/src/views/fields/ace/ace_field.js b/addons/web/static/src/views/fields/ace/ace_field.js index 9b3afdd5544..7fa48f2f36b 100644 --- a/addons/web/static/src/views/fields/ace/ace_field.js +++ b/addons/web/static/src/views/fields/ace/ace_field.js @@ -11,6 +11,15 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, onWillStart, onWillUpdateProps, useEffect, useRef } from "@odoo/owl"; export class AceField extends Component { + static template = "web.AceField"; + static props = { + ...standardFieldProps, + mode: { type: String, optional: true }, + }; + static defaultProps = { + mode: "qweb", + }; + setup() { this.aceEditor = null; this.editorRef = useRef("editor"); @@ -110,22 +119,13 @@ export class AceField extends Component { } } -AceField.template = "web.AceField"; -AceField.props = { - ...standardFieldProps, - mode: { type: String, optional: true }, -}; -AceField.defaultProps = { - mode: "qweb", -}; - -AceField.displayName = _lt("Ace Editor"); -AceField.supportedTypes = ["text"]; - -AceField.extractProps = ({ attrs }) => { - return { +export const aceField = { + component: AceField, + displayName: _lt("Ace Editor"), + supportedTypes: ["text"], + extractProps: ({ attrs }) => ({ mode: attrs.options.mode, - }; + }), }; -registry.category("fields").add("ace", AceField); +registry.category("fields").add("ace", aceField); diff --git a/addons/web/static/src/views/fields/attachment_image/attachment_image_field.js b/addons/web/static/src/views/fields/attachment_image/attachment_image_field.js index 644b2f337e3..048912d58c4 100644 --- a/addons/web/static/src/views/fields/attachment_image/attachment_image_field.js +++ b/addons/web/static/src/views/fields/attachment_image/attachment_image_field.js @@ -5,11 +5,14 @@ import { registry } from "@web/core/registry"; import { Component } from "@odoo/owl"; -export class AttachmentImageField extends Component {} +export class AttachmentImageField extends Component { + static template = "web.AttachmentImageField"; +} -AttachmentImageField.template = "web.AttachmentImageField"; +export const attachmentImageField = { + component: AttachmentImageField, + displayName: _lt("Attachment Image"), + supportedTypes: ["many2one"], +}; -AttachmentImageField.displayName = _lt("Attachment Image"); -AttachmentImageField.supportedTypes = ["many2one"]; - -registry.category("fields").add("attachment_image", AttachmentImageField); +registry.category("fields").add("attachment_image", attachmentImageField); diff --git a/addons/web/static/src/views/fields/badge/badge_field.js b/addons/web/static/src/views/fields/badge/badge_field.js index 1f890cfdc64..4c569bdd3ec 100644 --- a/addons/web/static/src/views/fields/badge/badge_field.js +++ b/addons/web/static/src/views/fields/badge/badge_field.js @@ -8,6 +8,11 @@ import { Component } from "@odoo/owl"; const formatters = registry.category("formatters"); export class BadgeField extends Component { + static template = "web.BadgeField"; + static props = { + ...standardFieldProps, + }; + get formattedValue() { const formatter = formatters.get(this.props.type); return formatter(this.props.value, { @@ -25,12 +30,10 @@ export class BadgeField extends Component { } } -BadgeField.template = "web.BadgeField"; -BadgeField.props = { - ...standardFieldProps, +export const badgeField = { + component: BadgeField, + displayName: _lt("Badge"), + supportedTypes: ["selection", "many2one", "char"], }; -BadgeField.displayName = _lt("Badge"); -BadgeField.supportedTypes = ["selection", "many2one", "char"]; - -registry.category("fields").add("badge", BadgeField); +registry.category("fields").add("badge", badgeField); diff --git a/addons/web/static/src/views/fields/badge_selection/badge_selection_field.js b/addons/web/static/src/views/fields/badge_selection/badge_selection_field.js index 95c6ae0a7c8..7c52a932658 100644 --- a/addons/web/static/src/views/fields/badge_selection/badge_selection_field.js +++ b/addons/web/static/src/views/fields/badge_selection/badge_selection_field.js @@ -7,6 +7,11 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class BadgeSelectionField extends Component { + static template = "web.BadgeSelectionField"; + static props = { + ...standardFieldProps, + }; + get options() { switch (this.props.record.fields[this.props.name].type) { case "many2one": @@ -61,18 +66,15 @@ export class BadgeSelectionField extends Component { } } -BadgeSelectionField.template = "web.BadgeSelectionField"; -BadgeSelectionField.props = { - ...standardFieldProps, +export const badgeSelectionField = { + component: BadgeSelectionField, + displayName: _lt("Badges"), + supportedTypes: ["many2one", "selection"], + isEmpty: (record, fieldName) => record.data[fieldName] === false, + legacySpecialData: "_fetchSpecialMany2ones", }; -BadgeSelectionField.displayName = _lt("Badges"); -BadgeSelectionField.supportedTypes = ["many2one", "selection"]; -BadgeSelectionField.legacySpecialData = "_fetchSpecialMany2ones"; - -BadgeSelectionField.isEmpty = (record, fieldName) => record.data[fieldName] === false; - -registry.category("fields").add("selection_badge", BadgeSelectionField); +registry.category("fields").add("selection_badge", badgeSelectionField); export function preloadSelection(orm, record, fieldName) { const field = record.fields[fieldName]; diff --git a/addons/web/static/src/views/fields/binary/binary_field.js b/addons/web/static/src/views/fields/binary/binary_field.js index fb52d34180f..0eaccda2c0c 100644 --- a/addons/web/static/src/views/fields/binary/binary_field.js +++ b/addons/web/static/src/views/fields/binary/binary_field.js @@ -10,6 +10,19 @@ import { _lt } from "@web/core/l10n/translation"; import { Component, onWillUpdateProps, useState } from "@odoo/owl"; export class BinaryField extends Component { + static template = "web.BinaryField"; + static components = { + FileUploader, + }; + static props = { + ...standardFieldProps, + acceptedFileExtensions: { type: String, optional: true }, + fileNameField: { type: String, optional: true }, + }; + static defaultProps = { + acceptedFileExtensions: "*", + }; + setup() { this.notification = useService("notification"); this.state = useState({ @@ -52,27 +65,14 @@ export class BinaryField extends Component { } } -BinaryField.template = "web.BinaryField"; -BinaryField.components = { - FileUploader, -}; -BinaryField.props = { - ...standardFieldProps, - acceptedFileExtensions: { type: String, optional: true }, - fileNameField: { type: String, optional: true }, -}; -BinaryField.defaultProps = { - acceptedFileExtensions: "*", -}; - -BinaryField.displayName = _lt("File"); -BinaryField.supportedTypes = ["binary"]; - -BinaryField.extractProps = ({ attrs }) => { - return { +export const binaryField = { + component: BinaryField, + displayName: _lt("File"), + supportedTypes: ["binary"], + extractProps: ({ attrs }) => ({ acceptedFileExtensions: attrs.options.accepted_file_extensions, fileNameField: attrs.filename, - }; + }), }; -registry.category("fields").add("binary", BinaryField); +registry.category("fields").add("binary", binaryField); diff --git a/addons/web/static/src/views/fields/boolean/boolean_field.js b/addons/web/static/src/views/fields/boolean/boolean_field.js index c63ff84edda..c2eb677138e 100644 --- a/addons/web/static/src/views/fields/boolean/boolean_field.js +++ b/addons/web/static/src/views/fields/boolean/boolean_field.js @@ -8,6 +8,12 @@ import { CheckBox } from "@web/core/checkbox/checkbox"; import { Component } from "@odoo/owl"; export class BooleanField extends Component { + static template = "web.BooleanField"; + static components = { CheckBox }; + static props = { + ...standardFieldProps, + }; + get isReadonly() { return !(this.props.record.isInEdition && !this.props.record.isReadonly(this.props.name)); } @@ -20,15 +26,11 @@ export class BooleanField extends Component { } } -BooleanField.template = "web.BooleanField"; -BooleanField.components = { CheckBox }; -BooleanField.props = { - ...standardFieldProps, +export const booleanField = { + component: BooleanField, + displayName: _lt("Checkbox"), + supportedTypes: ["boolean"], + isEmpty: () => false, }; -BooleanField.displayName = _lt("Checkbox"); -BooleanField.supportedTypes = ["boolean"]; - -BooleanField.isEmpty = () => false; - -registry.category("fields").add("boolean", BooleanField); +registry.category("fields").add("boolean", booleanField); diff --git a/addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.js b/addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.js index 395b6e0bcc0..f969361e061 100644 --- a/addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.js +++ b/addons/web/static/src/views/fields/boolean_favorite/boolean_favorite_field.js @@ -7,25 +7,25 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; -export class BooleanFavoriteField extends Component {} - -BooleanFavoriteField.template = "web.BooleanFavoriteField"; -BooleanFavoriteField.props = { - ...standardFieldProps, - noLabel: { type: Boolean, optional: true }, -}; -BooleanFavoriteField.defaultProps = { - noLabel: false, -}; - -BooleanFavoriteField.displayName = _lt("Favorite"); -BooleanFavoriteField.supportedTypes = ["boolean"]; - -BooleanFavoriteField.isEmpty = () => false; -BooleanFavoriteField.extractProps = ({ attrs }) => { - return { - noLabel: archParseBoolean(attrs.nolabel), +export class BooleanFavoriteField extends Component { + static template = "web.BooleanFavoriteField"; + static props = { + ...standardFieldProps, + noLabel: { type: Boolean, optional: true }, }; + static defaultProps = { + noLabel: false, + }; +} + +export const booleanFavoriteField = { + component: BooleanFavoriteField, + displayName: _lt("Favorite"), + supportedTypes: ["boolean"], + isEmpty: () => false, + extractProps: ({ attrs }) => ({ + noLabel: archParseBoolean(attrs.nolabel), + }), }; -registry.category("fields").add("boolean_favorite", BooleanFavoriteField); +registry.category("fields").add("boolean_favorite", booleanFavoriteField); diff --git a/addons/web/static/src/views/fields/boolean_icon/boolean_icon_field.js b/addons/web/static/src/views/fields/boolean_icon/boolean_icon_field.js index d3614fed95b..8385e6f2e25 100644 --- a/addons/web/static/src/views/fields/boolean_icon/boolean_icon_field.js +++ b/addons/web/static/src/views/fields/boolean_icon/boolean_icon_field.js @@ -6,24 +6,27 @@ import { _lt } from "@web/core/l10n/translation"; import { standardFieldProps } from "../standard_field_props"; export class BooleanIconField extends Component { - static defaultProps = { - icon: "fa-check-square-o", - }; - static extractProps = ({ attrs }) => { - return { - icon: attrs.options.icon, - }; - }; static template = "web.BooleanIconField"; static props = { ...standardFieldProps, icon: { type: String, optional: true }, }; - static displayName = _lt("Boolean Icon"); - static supportedTypes = ["boolean"]; + static defaultProps = { + icon: "fa-check-square-o", + }; + get label() { return this.props.record.activeFields[this.props.name].string; } } -registry.category("fields").add("boolean_icon", BooleanIconField); +export const booleanIconField = { + component: BooleanIconField, + displayName: _lt("Boolean Icon"), + supportedTypes: ["boolean"], + extractProps: ({ attrs }) => ({ + icon: attrs.options.icon, + }), +}; + +registry.category("fields").add("boolean_icon", booleanIconField); diff --git a/addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.js b/addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.js index 0230876c2c9..d50a4057b96 100644 --- a/addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.js +++ b/addons/web/static/src/views/fields/boolean_toggle/boolean_toggle_field.js @@ -2,16 +2,20 @@ import { registry } from "@web/core/registry"; import { _lt } from "@web/core/l10n/translation"; -import { BooleanField } from "../boolean/boolean_field"; +import { booleanField, BooleanField } from "../boolean/boolean_field"; export class BooleanToggleField extends BooleanField { + static template = "web.BooleanToggleField"; + get isReadonly() { return this.props.record.isReadonly(this.props.name); } } -BooleanToggleField.template = "web.BooleanToggleField"; +export const booleanToggleField = { + ...booleanField, + component: BooleanToggleField, + displayName: _lt("Toggle"), +}; -BooleanToggleField.displayName = _lt("Toggle"); - -registry.category("fields").add("boolean_toggle", BooleanToggleField); +registry.category("fields").add("boolean_toggle", booleanToggleField); diff --git a/addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.js b/addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.js index 8fa48458ff5..fab772db4f5 100644 --- a/addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.js +++ b/addons/web/static/src/views/fields/boolean_toggle/list_boolean_toggle_field.js @@ -1,9 +1,11 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { BooleanToggleField } from "./boolean_toggle_field"; +import { booleanToggleField, BooleanToggleField } from "./boolean_toggle_field"; export class ListBooleanToggleField extends BooleanToggleField { + static template = "web.ListBooleanToggleField"; + onClick() { if (!this.props.readonly) { this.props.update(!this.props.value); @@ -11,6 +13,9 @@ export class ListBooleanToggleField extends BooleanToggleField { } } -ListBooleanToggleField.template = "web.ListBooleanToggleField"; +export const listBooleanToggleField = { + ...booleanToggleField, + component: ListBooleanToggleField, +}; -registry.category("fields").add("list.boolean_toggle", ListBooleanToggleField); +registry.category("fields").add("list.boolean_toggle", listBooleanToggleField); diff --git a/addons/web/static/src/views/fields/char/char_field.js b/addons/web/static/src/views/fields/char/char_field.js index 472858e8668..3daa54a1966 100644 --- a/addons/web/static/src/views/fields/char/char_field.js +++ b/addons/web/static/src/views/fields/char/char_field.js @@ -12,6 +12,22 @@ import { useDynamicPlaceholder } from "../dynamicplaceholder_hook"; import { Component, onMounted, onWillUnmount, useRef } from "@odoo/owl"; export class CharField extends Component { + static template = "web.CharField"; + static components = { + TranslationButton, + }; + static props = { + ...standardFieldProps, + autocomplete: { type: String, optional: true }, + isPassword: { type: Boolean, optional: true }, + placeholder: { type: String, optional: true }, + dynamicPlaceholder: { type: Boolean, optional: true }, + shouldTrim: { type: Boolean, optional: true }, + maxLength: { type: Number, optional: true }, + isTranslatable: { type: Boolean, optional: true }, + }; + static defaultProps = { dynamicPlaceholder: false }; + setup() { if (this.props.dynamicPlaceholder) { this.dynamicPlaceholder = useDynamicPlaceholder(); @@ -26,34 +42,33 @@ export class CharField extends Component { if (ev.key === this.dynamicPlaceholder.TRIGGER_KEY && ev.target === this.input.el) { const baseModel = this.props.record.data.mailing_model_real; if (baseModel) { - await this.dynamicPlaceholder.open( - this.input.el, - baseModel, - { - validateCallback: this.onDynamicPlaceholderValidate.bind(this), - closeCallback: this.onDynamicPlaceholderClose.bind(this) - } - ); + await this.dynamicPlaceholder.open(this.input.el, baseModel, { + validateCallback: this.onDynamicPlaceholderValidate.bind(this), + closeCallback: this.onDynamicPlaceholderClose.bind(this), + }); } } } onMounted() { if (this.props.dynamicPlaceholder) { this.keydownListenerCallback = this.onKeydownListener.bind(this); - document.addEventListener('keydown', this.keydownListenerCallback); + document.addEventListener("keydown", this.keydownListenerCallback); } } onWillUnmount() { if (this.props.dynamicPlaceholder) { - document.removeEventListener('keydown', this.keydownListenerCallback); + document.removeEventListener("keydown", this.keydownListenerCallback); } } onDynamicPlaceholderValidate(chain, defaultValue) { if (chain) { const triggerKeyReplaceRegex = new RegExp(`${this.dynamicPlaceholder.TRIGGER_KEY}$`); - let dynamicPlaceholder = "{{object." + chain.join('.'); - dynamicPlaceholder += defaultValue && defaultValue !== '' ? ` or '''${defaultValue}'''}}` : '}}'; - this.props.update(this.input.el.value.replace(triggerKeyReplaceRegex, '') + dynamicPlaceholder); + let dynamicPlaceholder = "{{object." + chain.join("."); + dynamicPlaceholder += + defaultValue && defaultValue !== "" ? ` or '''${defaultValue}'''}}` : "}}"; + this.props.update( + this.input.el.value.replace(triggerKeyReplaceRegex, "") + dynamicPlaceholder + ); } } onDynamicPlaceholderClose() { @@ -72,35 +87,20 @@ export class CharField extends Component { } } -CharField.template = "web.CharField"; -CharField.components = { - TranslationButton, -}; -CharField.defaultProps = { dynamicPlaceholder: false }; -CharField.props = { - ...standardFieldProps, - autocomplete: { type: String, optional: true }, - isPassword: { type: Boolean, optional: true }, - placeholder: { type: String, optional: true }, - dynamicPlaceholder: { type: Boolean, optional: true}, - shouldTrim: { type: Boolean, optional: true }, - maxLength: { type: Number, optional: true }, - isTranslatable: { type: Boolean, optional: true }, -}; +export const charField = { + component: CharField, + displayName: _lt("Text"), + supportedTypes: ["char"], + extractProps: ({ attrs, field }) => ({ + isPassword: archParseBoolean(attrs.password), + dynamicPlaceholder: attrs.options.dynamic_placeholder, + autocomplete: attrs.autocomplete, + placeholder: attrs.placeholder, -CharField.displayName = _lt("Text"); -CharField.supportedTypes = ["char"]; - -CharField.extractProps = ({ attrs, field }) => { - return { shouldTrim: field.trim && !archParseBoolean(attrs.password), // passwords shouldn't be trimmed maxLength: field.size, isTranslatable: field.translate, - dynamicPlaceholder: attrs.options.dynamic_placeholder, - autocomplete: attrs.autocomplete, - isPassword: archParseBoolean(attrs.password), - placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("char", CharField); +registry.category("fields").add("char", charField); diff --git a/addons/web/static/src/views/fields/color/color_field.js b/addons/web/static/src/views/fields/color/color_field.js index afd51f6de1e..57c68c42818 100644 --- a/addons/web/static/src/views/fields/color/color_field.js +++ b/addons/web/static/src/views/fields/color/color_field.js @@ -6,13 +6,18 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, useState, onWillUpdateProps } from "@odoo/owl"; export class ColorField extends Component { + static template = "web.ColorField"; + static props = { + ...standardFieldProps, + }; + setup() { this.state = useState({ - color: this.props.value || '', + color: this.props.value || "", }); onWillUpdateProps((nextProps) => { - this.state.color = nextProps.value || ''; + this.state.color = nextProps.value || ""; }); } @@ -21,11 +26,9 @@ export class ColorField extends Component { } } -ColorField.template = "web.ColorField"; -ColorField.props = { - ...standardFieldProps, +export const colorField = { + component: ColorField, + supportedTypes: ["char"], }; -ColorField.supportedTypes = ["char"]; - -registry.category("fields").add("color", ColorField); +registry.category("fields").add("color", colorField); diff --git a/addons/web/static/src/views/fields/color_picker/color_picker_field.js b/addons/web/static/src/views/fields/color_picker/color_picker_field.js index 853d2b0bf3c..d2b523f77b7 100644 --- a/addons/web/static/src/views/fields/color_picker/color_picker_field.js +++ b/addons/web/static/src/views/fields/color_picker/color_picker_field.js @@ -7,6 +7,16 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class ColorPickerField extends Component { + static template = "web.ColorPickerField"; + static components = { + ColorList, + }; + static props = { + ...standardFieldProps, + }; + + static RECORD_COLORS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + get canToggle() { return this.props.record.activeFields[this.props.name].viewType !== "list"; } @@ -20,15 +30,9 @@ export class ColorPickerField extends Component { } } -ColorPickerField.template = "web.ColorPickerField"; -ColorPickerField.components = { - ColorList, -}; -ColorPickerField.props = { - ...standardFieldProps, +export const colorPickerField = { + component: ColorPickerField, + supportedTypes: ["integer"], }; -ColorPickerField.supportedTypes = ["integer"]; -ColorPickerField.RECORD_COLORS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; - -registry.category("fields").add("color_picker", ColorPickerField); +registry.category("fields").add("color_picker", colorPickerField); diff --git a/addons/web/static/src/views/fields/copy_clipboard/copy_button.js b/addons/web/static/src/views/fields/copy_clipboard/copy_button.js index 7318da43fb2..a93ad6479c3 100644 --- a/addons/web/static/src/views/fields/copy_clipboard/copy_button.js +++ b/addons/web/static/src/views/fields/copy_clipboard/copy_button.js @@ -6,6 +6,15 @@ import { useService } from "@web/core/utils/hooks"; import { Component, useRef } from "@odoo/owl"; export class CopyButton extends Component { + static template = "web.CopyButton"; + static props = { + className: { type: String, optional: true }, + copyText: { type: String, optional: true }, + disabled: { type: Boolean, optional: true }, + successText: { type: String, optional: true }, + content: { type: [String, Object], optional: true }, + }; + setup() { this.button = useRef("button"); this.popover = useService("popover"); @@ -40,11 +49,3 @@ export class CopyButton extends Component { this.showTooltip(); } } -CopyButton.template = "web.CopyButton"; -CopyButton.props = { - className: { type: String, optional: true }, - copyText: { type: String, optional: true }, - disabled: { type: Boolean, optional: true }, - successText: { type: String, optional: true }, - content: { type: [String, Object], optional: true }, -}; diff --git a/addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.js b/addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.js index c2f77211a0e..1b885e43a9d 100644 --- a/addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.js +++ b/addons/web/static/src/views/fields/copy_clipboard/copy_clipboard_field.js @@ -15,10 +15,18 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; class CopyClipboardField extends Component { + static template = "web.CopyClipboardField"; + static props = { + ...standardFieldProps, + string: { type: String, optional: true }, + disabledExpr: { type: String, optional: true }, + }; + setup() { this.copyText = this.props.string || this.env._t("Copy"); this.successText = this.env._t("Copied"); } + get copyButtonClassName() { return `o_btn_${this.props.type}_copy btn-sm`; } @@ -34,47 +42,68 @@ class CopyClipboardField extends Component { return Boolean(evaluated); } } -CopyClipboardField.template = "web.CopyClipboardField"; -CopyClipboardField.props = { - ...standardFieldProps, - string: { type: String, optional: true }, - disabledExpr: { type: String, optional: true }, -}; -CopyClipboardField.extractProps = ({ attrs }) => { - return { - string: attrs.string, - disabledExpr: attrs.disabled, - }; -}; export class CopyClipboardButtonField extends CopyClipboardField { + static template = "web.CopyClipboardButtonField"; + static components = { CopyButton }; + get copyButtonClassName() { return `o_btn_${this.props.type}_copy rounded-2`; } } -CopyClipboardButtonField.template = "web.CopyClipboardButtonField"; -CopyClipboardButtonField.components = { CopyButton }; -CopyClipboardButtonField.displayName = _lt("Copy to Clipboard"); -registry.category("fields").add("CopyClipboardButton", CopyClipboardButtonField); +export class CopyClipboardCharField extends CopyClipboardField { + static components = { Field: CharField, CopyButton }; +} -export class CopyClipboardCharField extends CopyClipboardField {} -CopyClipboardCharField.components = { Field: CharField, CopyButton }; -CopyClipboardCharField.displayName = _lt("Copy Text to Clipboard"); -CopyClipboardCharField.supportedTypes = ["char"]; +export class CopyClipboardTextField extends CopyClipboardField { + static components = { Field: TextField, CopyButton }; +} -registry.category("fields").add("CopyClipboardChar", CopyClipboardCharField); +export class CopyClipboardURLField extends CopyClipboardField { + static components = { Field: UrlField, CopyButton }; +} -export class CopyClipboardTextField extends CopyClipboardField {} -CopyClipboardTextField.components = { Field: TextField, CopyButton }; -CopyClipboardTextField.displayName = _lt("Copy Multiline Text to Clipboard"); -CopyClipboardTextField.supportedTypes = ["text"]; +// ---------------------------------------------------------------------------- -registry.category("fields").add("CopyClipboardText", CopyClipboardTextField); +function extractProps({ attrs }) { + return { + string: attrs.string, + disabledExpr: attrs.disabled, + }; +} -export class CopyClipboardURLField extends CopyClipboardField {} -CopyClipboardURLField.components = { Field: UrlField, CopyButton }; -CopyClipboardURLField.displayName = _lt("Copy URL to Clipboard"); -CopyClipboardURLField.supportedTypes = ["char"]; +export const copyClipboardButtonField = { + component: CopyClipboardButtonField, + displayName: _lt("Copy to Clipboard"), + extractProps, +}; -registry.category("fields").add("CopyClipboardURL", CopyClipboardURLField); +registry.category("fields").add("CopyClipboardButton", copyClipboardButtonField); + +export const copyClipboardCharField = { + component: CopyClipboardCharField, + displayName: _lt("Copy Text to Clipboard"), + supportedTypes: ["char"], + extractProps, +}; + +registry.category("fields").add("CopyClipboardChar", copyClipboardCharField); + +export const copyClipboardTextField = { + component: CopyClipboardTextField, + displayName: _lt("Copy Multiline Text to Clipboard"), + supportedTypes: ["text"], + extractProps, +}; + +registry.category("fields").add("CopyClipboardText", copyClipboardTextField); + +export const copyClipboardURLField = { + component: CopyClipboardURLField, + displayName: _lt("Copy URL to Clipboard"), + supportedTypes: ["char"], + extractProps, +}; + +registry.category("fields").add("CopyClipboardURL", copyClipboardURLField); diff --git a/addons/web/static/src/views/fields/date/date_field.js b/addons/web/static/src/views/fields/date/date_field.js index 61957696dd4..fc1ed6d6f07 100644 --- a/addons/web/static/src/views/fields/date/date_field.js +++ b/addons/web/static/src/views/fields/date/date_field.js @@ -10,6 +10,19 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class DateField extends Component { + static template = "web.DateField"; + static components = { + DatePicker, + }; + static props = { + ...standardFieldProps, + pickerOptions: { type: Object, optional: true }, + placeholder: { type: String, optional: true }, + }; + static defaultProps = { + pickerOptions: {}, + }; + setup() { /** * The last value that has been commited to the model. @@ -17,6 +30,7 @@ export class DateField extends Component { */ this.lastSetValue = null; } + get isDateTime() { return this.props.record.fields[this.props.name].type === "datetime"; } @@ -44,27 +58,14 @@ export class DateField extends Component { } } -DateField.template = "web.DateField"; -DateField.components = { - DatePicker, -}; -DateField.props = { - ...standardFieldProps, - pickerOptions: { type: Object, optional: true }, - placeholder: { type: String, optional: true }, -}; -DateField.defaultProps = { - pickerOptions: {}, -}; - -DateField.displayName = _lt("Date"); -DateField.supportedTypes = ["date", "datetime"]; - -DateField.extractProps = ({ attrs }) => { - return { +export const dateField = { + component: DateField, + displayName: _lt("Date"), + supportedTypes: ["date", "datetime"], + extractProps: ({ attrs }) => ({ pickerOptions: attrs.options.datepicker, placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("date", DateField); +registry.category("fields").add("date", dateField); diff --git a/addons/web/static/src/views/fields/daterange/daterange_field.js b/addons/web/static/src/views/fields/daterange/daterange_field.js index 95cbd47c75d..09494c25a1e 100644 --- a/addons/web/static/src/views/fields/daterange/daterange_field.js +++ b/addons/web/static/src/views/fields/daterange/daterange_field.js @@ -11,6 +11,15 @@ const formatters = registry.category("formatters"); const parsers = registry.category("parsers"); export class DateRangeField extends Component { + static template = "web.DateRangeField"; + static props = { + ...standardFieldProps, + relatedEndDateField: { type: String, optional: true }, + relatedStartDateField: { type: String, optional: true }, + formatType: { type: String, optional: true }, + placeholder: { type: String, optional: true }, + }; + setup() { this.notification = useService("notification"); this.root = useRef("root"); @@ -138,24 +147,17 @@ export class DateRangeField extends Component { this.isPickerShown = false; } } -DateRangeField.template = "web.DateRangeField"; -DateRangeField.props = { - ...standardFieldProps, - relatedEndDateField: { type: String, optional: true }, - relatedStartDateField: { type: String, optional: true }, - formatType: { type: String, optional: true }, - placeholder: { type: String, optional: true }, -}; -DateRangeField.supportedTypes = ["date", "datetime"]; - -DateRangeField.extractProps = ({ attrs, field }) => { - return { +export const dateRangeField = { + component: DateRangeField, + supportedTypes: ["date", "datetime"], + extractProps: ({ attrs, field }) => ({ relatedEndDateField: attrs.options.related_end_date, relatedStartDateField: attrs.options.related_start_date, - formatType: attrs.options.format_type || field.type, placeholder: attrs.placeholder, - }; + + formatType: attrs.options.format_type || field.type, + }), }; -registry.category("fields").add("daterange", DateRangeField); +registry.category("fields").add("daterange", dateRangeField); diff --git a/addons/web/static/src/views/fields/datetime/datetime_field.js b/addons/web/static/src/views/fields/datetime/datetime_field.js index 9eaa36ba482..bbf1cce4e0a 100644 --- a/addons/web/static/src/views/fields/datetime/datetime_field.js +++ b/addons/web/static/src/views/fields/datetime/datetime_field.js @@ -9,6 +9,19 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class DateTimeField extends Component { + static template = "web.DateTimeField"; + static components = { + DateTimePicker, + }; + static props = { + ...standardFieldProps, + pickerOptions: { type: Object, optional: true }, + placeholder: { type: String, optional: true }, + }; + static defaultProps = { + pickerOptions: {}, + }; + setup() { /** * The last value that has been commited to the model. @@ -16,6 +29,7 @@ export class DateTimeField extends Component { */ this.lastSetValue = null; } + get formattedValue() { return formatDateTime(this.props.value); } @@ -34,27 +48,14 @@ export class DateTimeField extends Component { } } -DateTimeField.template = "web.DateTimeField"; -DateTimeField.components = { - DateTimePicker, -}; -DateTimeField.props = { - ...standardFieldProps, - pickerOptions: { type: Object, optional: true }, - placeholder: { type: String, optional: true }, -}; -DateTimeField.defaultProps = { - pickerOptions: {}, -}; - -DateTimeField.displayName = _lt("Date & Time"); -DateTimeField.supportedTypes = ["datetime"]; - -DateTimeField.extractProps = ({ attrs }) => { - return { +export const dateTimeField = { + component: DateTimeField, + displayName: _lt("Date & Time"), + supportedTypes: ["datetime"], + extractProps: ({ attrs }) => ({ pickerOptions: attrs.options.datepicker, placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("datetime", DateTimeField); +registry.category("fields").add("datetime", dateTimeField); diff --git a/addons/web/static/src/views/fields/domain/domain_field.js b/addons/web/static/src/views/fields/domain/domain_field.js index 3a5e7fd67aa..bd53da421d8 100644 --- a/addons/web/static/src/views/fields/domain/domain_field.js +++ b/addons/web/static/src/views/fields/domain/domain_field.js @@ -12,6 +12,19 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, onWillStart, onWillUpdateProps, useState } from "@odoo/owl"; export class DomainField extends Component { + static template = "web.DomainField"; + static components = { + DomainSelector, + }; + static props = { + ...standardFieldProps, + editInDialog: { type: Boolean, optional: true }, + resModel: { type: String, optional: true }, + }; + static defaultProps = { + editInDialog: false, + }; + setup() { this.orm = useService("orm"); this.state = useState({ @@ -126,28 +139,15 @@ export class DomainField extends Component { } } -DomainField.template = "web.DomainField"; -DomainField.components = { - DomainSelector, -}; -DomainField.props = { - ...standardFieldProps, - editInDialog: { type: Boolean, optional: true }, - resModel: { type: String, optional: true }, -}; -DomainField.defaultProps = { - editInDialog: false, -}; - -DomainField.displayName = _lt("Domain"); -DomainField.supportedTypes = ["char"]; - -DomainField.isEmpty = () => false; -DomainField.extractProps = ({ attrs }) => { - return { +export const domainField = { + component: DomainField, + displayName: _lt("Domain"), + supportedTypes: ["char"], + isEmpty: () => false, + extractProps: ({ attrs }) => ({ editInDialog: attrs.options.in_dialog, resModel: attrs.options.model, - }; + }), }; -registry.category("fields").add("domain", DomainField); +registry.category("fields").add("domain", domainField); diff --git a/addons/web/static/src/views/fields/email/email_field.js b/addons/web/static/src/views/fields/email/email_field.js index 96fd83282a2..630650e1b42 100644 --- a/addons/web/static/src/views/fields/email/email_field.js +++ b/addons/web/static/src/views/fields/email/email_field.js @@ -8,27 +8,35 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class EmailField extends Component { + static template = "web.EmailField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + }; + setup() { useInputField({ getValue: () => this.props.value || "" }); } } -EmailField.template = "web.EmailField"; -EmailField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, -}; -EmailField.extractProps = ({ attrs }) => { - return { +export const emailField = { + component: EmailField, + displayName: _lt("Email"), + supportedTypes: ["char"], + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder, - }; + }), }; -EmailField.displayName = _lt("Email"); -EmailField.supportedTypes = ["char"]; +registry.category("fields").add("email", emailField); -class FormEmailField extends EmailField {} -FormEmailField.template = "web.FormEmailField"; +class FormEmailField extends EmailField { + static template = "web.FormEmailField"; +} -registry.category("fields").add("email", EmailField); -registry.category("fields").add("form.email", FormEmailField); +export const formEmailField = { + ...emailField, + component: FormEmailField, +}; + +registry.category("fields").add("form.email", formEmailField); diff --git a/addons/web/static/src/views/fields/field.js b/addons/web/static/src/views/fields/field.js index 3d51d513319..235d89866b7 100644 --- a/addons/web/static/src/views/fields/field.js +++ b/addons/web/static/src/views/fields/field.js @@ -50,17 +50,17 @@ function getFieldClassFromRegistry(fieldType, widget, viewType, jsClass) { return fieldRegistry.get(fieldType); } - return DefaultField; + return { component: DefaultField }; } -export function fieldVisualFeedback(FieldComponent, record, fieldName, fieldInfo) { +export function fieldVisualFeedback(field, record, fieldName, fieldInfo) { const modifiers = fieldInfo.modifiers || {}; const readonly = evalDomain(modifiers.readonly, record.evalContext); const inEdit = record.isInEdition; let empty = !record.isVirtual; - if ("isEmpty" in FieldComponent) { - empty = empty && FieldComponent.isEmpty(record, fieldName); + if ("isEmpty" in field) { + empty = empty && field.isEmpty(record, fieldName); } else { empty = empty && !record.data[fieldName]; } @@ -75,17 +75,17 @@ export function fieldVisualFeedback(FieldComponent, record, fieldName, fieldInfo export class Field extends Component { setup() { - this.FieldComponent = this.props.fieldInfo.FieldComponent; - if (!this.FieldComponent) { + this.field = this.props.fieldInfo.field; + if (!this.field) { const fieldType = this.props.record.fields[this.props.name].type; - this.FieldComponent = getFieldClassFromRegistry(fieldType, this.props.type); + this.field = getFieldClassFromRegistry(fieldType, this.props.type); } } get classNames() { const { class: _class, fieldInfo, name, record } = this.props; const { readonly, required, invalid, empty } = fieldVisualFeedback( - this.FieldComponent, + this.field, record, name, fieldInfo @@ -99,8 +99,8 @@ export class Field extends Component { [`o_field_${this.type}`]: true, [_class]: Boolean(_class), }; - if (this.FieldComponent.additionalClasses) { - for (const cls of this.FieldComponent.additionalClasses) { + if (this.field.additionalClasses) { + for (const cls of this.field.additionalClasses) { classNames[cls] = true; } } @@ -139,10 +139,10 @@ export class Field extends Component { decorationMap[decoName] = value; } - let propsFromAttrs = fieldInfo.propsFromAttrs; + let propsFromAttrs = fieldInfo.propsFromAttrs || {}; if (this.props.attrs) { - const extractProps = this.FieldComponent.extractProps || (() => ({})); - propsFromAttrs = extractProps({ + const extractProps = this.field.extractProps || (() => ({})); + propsFromAttrs = extractProps.call(this.field, { field, attrs: { ...this.props.attrs, @@ -202,18 +202,18 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { const name = node.getAttribute("name"); const widget = node.getAttribute("widget"); const fields = models[modelName]; - const field = fields[name]; const modifiers = JSON.parse(node.getAttribute("modifiers") || "{}"); + const field = getFieldClassFromRegistry(fields[name].type, widget, viewType, jsClass); const fieldInfo = { name, viewType, context: node.getAttribute("context") || "{}", - string: node.getAttribute("string") || field.string, + string: node.getAttribute("string") || fields[name].string, help: node.getAttribute("help"), widget, modifiers, onChange: archParseBoolean(node.getAttribute("on_change")), - FieldComponent: getFieldClassFromRegistry(fields[name].type, widget, viewType, jsClass), + field, forceSave: archParseBoolean(node.getAttribute("force_save")), decorations: {}, // populated below noLabel: archParseBoolean(node.getAttribute("nolabel")), @@ -244,26 +244,26 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { if (viewType !== "kanban") { // FIXME WOWL: find a better solution - const extractProps = fieldInfo.FieldComponent.extractProps || (() => ({})); - fieldInfo.propsFromAttrs = extractProps({ - field, + const extractProps = field.extractProps || (() => ({})); + fieldInfo.propsFromAttrs = extractProps.call(field, { + field: fields[name], attrs: { ...fieldInfo.rawAttrs, options: fieldInfo.options }, }); } - if (X2M_TYPES.includes(field.type)) { + if (X2M_TYPES.includes(fields[name].type)) { const views = {}; for (const child of node.children) { const viewType = child.tagName === "tree" ? "list" : child.tagName; const { ArchParser } = viewRegistry.get(viewType); const xmlSerializer = new XMLSerializer(); const subArch = xmlSerializer.serializeToString(child); - const archInfo = new ArchParser().parse(subArch, models, field.relation); + const archInfo = new ArchParser().parse(subArch, models, fields[name].relation); views[viewType] = { ...archInfo, - fields: models[field.relation], + fields: models[fields[name].relation], }; - fieldInfo.relatedFields = models[field.relation]; + fieldInfo.relatedFields = models[fields[name].relation]; } let viewMode = node.getAttribute("mode"); @@ -279,10 +279,10 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { viewMode = viewMode.replace("tree", "list"); } fieldInfo.viewMode = viewMode; - fieldInfo.relation = field.relation; // not really necessary + fieldInfo.relation = fields[name].relation; // not really necessary fieldInfo.views = views; - let fieldsToFetch = fieldInfo.FieldComponent.fieldsToFetch; + let fieldsToFetch = field.fieldsToFetch; if (fieldsToFetch) { if (fieldsToFetch instanceof Function) { fieldsToFetch = fieldsToFetch(fieldInfo); diff --git a/addons/web/static/src/views/fields/field.xml b/addons/web/static/src/views/fields/field.xml index 40812cd7268..824b75d33d3 100644 --- a/addons/web/static/src/views/fields/field.xml +++ b/addons/web/static/src/views/fields/field.xml @@ -3,7 +3,7 @@
- +
diff --git a/addons/web/static/src/views/fields/field_tooltip.js b/addons/web/static/src/views/fields/field_tooltip.js index 5648cf542d0..9b92b79dec4 100644 --- a/addons/web/static/src/views/fields/field_tooltip.js +++ b/addons/web/static/src/views/fields/field_tooltip.js @@ -3,7 +3,7 @@ export function getTooltipInfo(params) { let widgetDescription = undefined; if (params.fieldInfo.widget) { - widgetDescription = params.fieldInfo.FieldComponent.displayName; + widgetDescription = params.fieldInfo.field.displayName; } const info = { diff --git a/addons/web/static/src/views/fields/float/float_field.js b/addons/web/static/src/views/fields/float/float_field.js index ade8dd2daf0..5e6066a9296 100644 --- a/addons/web/static/src/views/fields/float/float_field.js +++ b/addons/web/static/src/views/fields/float/float_field.js @@ -11,6 +11,18 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class FloatField extends Component { + static template = "web.FloatField"; + static props = { + ...standardFieldProps, + inputType: { type: String, optional: true }, + step: { type: Number, optional: true }, + digits: { type: Array, optional: true }, + placeholder: { type: String, optional: true }, + }; + static defaultProps = { + inputType: "text", + }; + setup() { this.inputRef = useInputField({ getValue: () => this.formattedValue, @@ -32,39 +44,30 @@ export class FloatField extends Component { } } -FloatField.template = "web.FloatField"; -FloatField.props = { - ...standardFieldProps, - inputType: { type: String, optional: true }, - step: { type: Number, optional: true }, - digits: { type: Array, optional: true }, - placeholder: { type: String, optional: true }, -}; -FloatField.defaultProps = { - inputType: "text", +export const floatField = { + component: FloatField, + displayName: _lt("Float"), + supportedTypes: ["float"], + isEmpty: () => false, + extractProps: ({ attrs, field }) => { + // Sadly, digits param was available as an option and an attr. + // The option version could be removed with some xml refactoring. + let digits; + if (attrs.digits) { + digits = JSON.parse(attrs.digits); + } else if (attrs.options.digits) { + digits = attrs.options.digits; + } else if (Array.isArray(field.digits)) { + digits = field.digits; + } + + return { + inputType: attrs.options.type, + step: attrs.options.step, + digits, + placeholder: attrs.placeholder, + }; + }, }; -FloatField.displayName = _lt("Float"); -FloatField.supportedTypes = ["float"]; - -FloatField.isEmpty = () => false; -FloatField.extractProps = ({ attrs, field }) => { - // Sadly, digits param was available as an option and an attr. - // The option version could be removed with some xml refactoring. - let digits; - if (attrs.digits) { - digits = JSON.parse(attrs.digits); - } else if (attrs.options.digits) { - digits = attrs.options.digits; - } else if (Array.isArray(field.digits)) { - digits = field.digits; - } - return { - inputType: attrs.options.type, - step: attrs.options.step, - digits, - placeholder: attrs.placeholder, - }; -}; - -registry.category("fields").add("float", FloatField); +registry.category("fields").add("float", floatField); diff --git a/addons/web/static/src/views/fields/float_factor/float_factor_field.js b/addons/web/static/src/views/fields/float_factor/float_factor_field.js index e35fe2eb521..946e6f350bd 100644 --- a/addons/web/static/src/views/fields/float_factor/float_factor_field.js +++ b/addons/web/static/src/views/fields/float_factor/float_factor_field.js @@ -1,10 +1,21 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { FloatField } from "../float/float_field"; - +import { floatField, FloatField } from "../float/float_field"; import { Component } from "@odoo/owl"; + export class FloatFactorField extends Component { + static template = "web.FloatFactorField"; + static components = { FloatField }; + static props = { + ...FloatField.props, + factor: { type: Number, optional: true }, + }; + static defaultProps = { + ...FloatField.defaultProps, + factor: 1, + }; + get factor() { return this.props.factor; } @@ -20,25 +31,13 @@ export class FloatFactorField extends Component { } } -FloatFactorField.template = "web.FloatFactorField"; -FloatFactorField.components = { FloatField }; -FloatFactorField.props = { - ...FloatField.props, - factor: { type: Number, optional: true }, -}; -FloatFactorField.defaultProps = { - ...FloatField.defaultProps, - factor: 1, +export const floatFactorField = { + ...floatField, + component: FloatFactorField, + extractProps: (params) => ({ + ...floatField.extractProps(params), + factor: params.attrs.options.factor, + }), }; -FloatFactorField.supportedTypes = ["float"]; - -FloatFactorField.isEmpty = () => false; -FloatFactorField.extractProps = ({ attrs, field }) => { - return { - ...FloatField.extractProps({ attrs, field }), - factor: attrs.options.factor, - }; -}; - -registry.category("fields").add("float_factor", FloatFactorField); +registry.category("fields").add("float_factor", floatFactorField); diff --git a/addons/web/static/src/views/fields/float_time/float_time_field.js b/addons/web/static/src/views/fields/float_time/float_time_field.js index dd5f0e0dd1c..2164e935c15 100644 --- a/addons/web/static/src/views/fields/float_time/float_time_field.js +++ b/addons/web/static/src/views/fields/float_time/float_time_field.js @@ -11,6 +11,12 @@ import { useNumpadDecimal } from "../numpad_decimal_hook"; import { Component } from "@odoo/owl"; export class FloatTimeField extends Component { + static template = "web.FloatTimeField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + }; + setup() { useInputField({ getValue: () => this.formattedValue, @@ -25,20 +31,14 @@ export class FloatTimeField extends Component { } } -FloatTimeField.template = "web.FloatTimeField"; -FloatTimeField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, -}; - -FloatTimeField.displayName = _lt("Time"); -FloatTimeField.supportedTypes = ["float"]; - -FloatTimeField.isEmpty = () => false; -FloatTimeField.extractProps = ({ attrs }) => { - return { +export const floatTimeField = { + component: FloatTimeField, + displayName: _lt("Time"), + supportedTypes: ["float"], + isEmpty: () => false, + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("float_time", FloatTimeField); +registry.category("fields").add("float_time", floatTimeField); diff --git a/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js b/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js index 4b5e15db9f0..60edbec9a1f 100644 --- a/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js +++ b/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js @@ -7,6 +7,20 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class FloatToggleField extends Component { + static template = "web.FloatToggleField"; + static props = { + ...standardFieldProps, + digits: { type: Array, optional: true }, + range: { type: Array, optional: true }, + factor: { type: Number, optional: true }, + disableReadOnly: { type: Boolean, optional: true }, + }; + static defaultProps = { + range: [0.0, 0.5, 1.0], + factor: 1, + disableReadOnly: false, + }; + // TODO perf issue (because of update round trip) // we probably want to have a state and a useEffect or onWillUpateProps onChange() { @@ -30,38 +44,29 @@ export class FloatToggleField extends Component { } } -FloatToggleField.template = "web.FloatToggleField"; -FloatToggleField.props = { - ...standardFieldProps, - digits: { type: Array, optional: true }, - range: { type: Array, optional: true }, - factor: { type: Number, optional: true }, - disableReadOnly: { type: Boolean, optional: true }, -}; -FloatToggleField.defaultProps = { - range: [0.0, 0.5, 1.0], - factor: 1, - disableReadOnly: false, +export const floatToggleField = { + component: FloatToggleField, + supportedTypes: ["float"], + isEmpty: () => false, + extractProps: ({ attrs, field }) => { + // Sadly, digits param was available as an option and an attr. + // The option version could be removed with some xml refactoring. + let digits; + if (attrs.digits) { + digits = JSON.parse(attrs.digits); + } else if (attrs.options.digits) { + digits = attrs.options.digits; + } else if (Array.isArray(field.digits)) { + digits = field.digits; + } + + return { + digits, + range: attrs.options.range, + factor: attrs.options.factor, + disableReadOnly: attrs.options.force_button || false, + }; + }, }; -FloatToggleField.supportedTypes = ["float"]; - -FloatToggleField.isEmpty = () => false; -FloatToggleField.extractProps = ({ attrs, field }) => { - let digits; - if (attrs.digits) { - digits = JSON.parse(attrs.digits); - } else if (attrs.options.digits) { - digits = attrs.options.digits; - } else if (Array.isArray(field.digits)) { - digits = field.digits; - } - return { - digits, - range: attrs.options.range, - factor: attrs.options.factor, - disableReadOnly: attrs.options.force_button || false, - }; -}; - -registry.category("fields").add("float_toggle", FloatToggleField); +registry.category("fields").add("float_toggle", floatToggleField); diff --git a/addons/web/static/src/views/fields/font_selection/font_selection_field.js b/addons/web/static/src/views/fields/font_selection/font_selection_field.js index f6cbc09bf25..d8f297c76cd 100644 --- a/addons/web/static/src/views/fields/font_selection/font_selection_field.js +++ b/addons/web/static/src/views/fields/font_selection/font_selection_field.js @@ -8,6 +8,12 @@ import { formatSelection } from "../formatters"; import { Component } from "@odoo/owl"; export class FontSelectionField extends Component { + static template = "web.FontSelectionField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + }; + get options() { return this.props.record.fields[this.props.name].selection.filter( (option) => option[0] !== false && option[1] !== "" @@ -33,20 +39,14 @@ export class FontSelectionField extends Component { } } -FontSelectionField.template = "web.FontSelectionField"; -FontSelectionField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, -}; - -FontSelectionField.displayName = _lt("Font Selection"); -FontSelectionField.supportedTypes = ["selection"]; -FontSelectionField.legacySpecialData = "_fetchSpecialRelation"; - -FontSelectionField.extractProps = ({ attrs }) => { - return { +export const fontSelectionField = { + component: FontSelectionField, + displayName: _lt("Font Selection"), + supportedTypes: ["selection"], + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder, - }; + }), + legacySpecialData: "_fetchSpecialRelation", }; -registry.category("fields").add("font", FontSelectionField); +registry.category("fields").add("font", fontSelectionField); diff --git a/addons/web/static/src/views/fields/handle/handle_field.js b/addons/web/static/src/views/fields/handle/handle_field.js index 0cca12a7d57..1342d033d30 100644 --- a/addons/web/static/src/views/fields/handle/handle_field.js +++ b/addons/web/static/src/views/fields/handle/handle_field.js @@ -6,14 +6,18 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; -export class HandleField extends Component {} +export class HandleField extends Component { + static template = "web.HandleField"; + static props = { + ...standardFieldProps, + }; +} -HandleField.template = "web.HandleField"; -HandleField.props = { - ...standardFieldProps, +export const handleField = { + component: HandleField, + displayName: _lt("Handle"), + supportedTypes: ["integer"], + isEmpty: () => false, }; -HandleField.displayName = _lt("Handle"); -HandleField.supportedTypes = ["integer"]; -HandleField.isEmpty = () => false; -registry.category("fields").add("handle", HandleField); +registry.category("fields").add("handle", handleField); diff --git a/addons/web/static/src/views/fields/html/html_field.js b/addons/web/static/src/views/fields/html/html_field.js index e4baee895b8..0fa28900707 100644 --- a/addons/web/static/src/views/fields/html/html_field.js +++ b/addons/web/static/src/views/fields/html/html_field.js @@ -1,10 +1,15 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { TextField } from "../text/text_field"; +import { TextField, textField } from "../text/text_field"; -export class HtmlField extends TextField {} +export class HtmlField extends TextField { + static template = "web.HtmlField"; +} -HtmlField.template = "web.HtmlField"; +export const htmlField = { + ...textField, + component: HtmlField, +}; -registry.category("fields").add("html", HtmlField); +registry.category("fields").add("html", htmlField); diff --git a/addons/web/static/src/views/fields/iframe_wrapper/iframe_wrapper_field.js b/addons/web/static/src/views/fields/iframe_wrapper/iframe_wrapper_field.js index 52ddbb50d68..fc1717d2b01 100644 --- a/addons/web/static/src/views/fields/iframe_wrapper/iframe_wrapper_field.js +++ b/addons/web/static/src/views/fields/iframe_wrapper/iframe_wrapper_field.js @@ -6,6 +6,11 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, useEffect, useRef } from "@odoo/owl"; export class IframeWrapperField extends Component { + static template = "web.IframeWrapperField"; + static props = { + ...standardFieldProps, + }; + setup() { this.iframeRef = useRef("iframe"); @@ -31,12 +36,11 @@ export class IframeWrapperField extends Component { } } -IframeWrapperField.template = "web.IframeWrapperField"; -IframeWrapperField.props = { - ...standardFieldProps, +export const iframeWrapperField = { + component: IframeWrapperField, + displayName: _lt("Wrap raw html within an iframe"), + // If HTML, don't forget to adjust the sanitize options to avoid stripping most of the metadata + supportedTypes: ["text", "html"], }; -IframeWrapperField.displayName = _lt("Wrap raw html within an iframe"); -// If HTML, don't forget to adjust the sanitize options to avoid stripping most of the metadata -IframeWrapperField.supportedTypes = ["text", "html"]; -registry.category("fields").add("iframe_wrapper", IframeWrapperField); +registry.category("fields").add("iframe_wrapper", iframeWrapperField); diff --git a/addons/web/static/src/views/fields/image/image_field.js b/addons/web/static/src/views/fields/image/image_field.js index 0c143654fd2..c42e9a29b84 100644 --- a/addons/web/static/src/views/fields/image/image_field.js +++ b/addons/web/static/src/views/fields/image/image_field.js @@ -36,6 +36,23 @@ export function imageCacheKey(value) { } export class ImageField extends Component { + static template = "web.ImageField"; + static components = { + FileUploader, + }; + static props = { + ...standardFieldProps, + enableZoom: { type: Boolean, optional: true }, + zoomDelay: { type: Number, optional: true }, + previewImage: { type: String, optional: true }, + acceptedFileExtensions: { type: String, optional: true }, + width: { type: Number, optional: true }, + height: { type: Number, optional: true }, + }; + static defaultProps = { + acceptedFileExtensions: "image/*", + }; + setup() { this.notification = useService("notification"); this.isMobile = isMobileOS(); @@ -111,32 +128,14 @@ export class ImageField extends Component { } } -ImageField.template = "web.ImageField"; -ImageField.components = { - FileUploader, -}; -ImageField.props = { - ...standardFieldProps, - enableZoom: { type: Boolean, optional: true }, - zoomDelay: { type: Number, optional: true }, - previewImage: { type: String, optional: true }, - acceptedFileExtensions: { type: String, optional: true }, - width: { type: Number, optional: true }, - height: { type: Number, optional: true }, -}; -ImageField.defaultProps = { - acceptedFileExtensions: "image/*", -}; - -ImageField.displayName = _lt("Image"); -ImageField.supportedTypes = ["binary"]; - -ImageField.fieldDependencies = { - write_date: { type: "datetime" }, -}; - -ImageField.extractProps = ({ attrs }) => { - return { +export const imageField = { + component: ImageField, + displayName: _lt("Image"), + supportedTypes: ["binary"], + fieldDependencies: { + write_date: { type: "datetime" }, + }, + extractProps: ({ attrs }) => ({ enableZoom: attrs.options.zoom, zoomDelay: attrs.options.zoom_delay, previewImage: attrs.options.preview_image, @@ -149,8 +148,8 @@ ImageField.extractProps = ({ attrs }) => { attrs.options.size && Boolean(attrs.options.size[1]) ? attrs.options.size[1] : attrs.height, - }; + }), }; -registry.category("fields").add("image", ImageField); -registry.category("fields").add("kanban.image", ImageField); // FIXME WOWL: s.t. we don't use the legacy one +registry.category("fields").add("image", imageField); +registry.category("fields").add("kanban.image", imageField); // FIXME WOWL: s.t. we don't use the legacy one diff --git a/addons/web/static/src/views/fields/image_url/image_url_field.js b/addons/web/static/src/views/fields/image_url/image_url_field.js index 05c92e00cb3..eab3bca39f1 100644 --- a/addons/web/static/src/views/fields/image_url/image_url_field.js +++ b/addons/web/static/src/views/fields/image_url/image_url_field.js @@ -8,6 +8,15 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, onWillUpdateProps, useState } from "@odoo/owl"; export class ImageUrlField extends Component { + static template = "web.ImageUrlField"; + static props = { + ...standardFieldProps, + width: { type: Number, optional: true }, + height: { type: Number, optional: true }, + }; + + static fallbackSrc = "/web/static/img/placeholder.png"; + setup() { this.notification = useService("notification"); this.state = useState({ @@ -40,25 +49,16 @@ export class ImageUrlField extends Component { } } -ImageUrlField.fallbackSrc = "/web/static/img/placeholder.png"; - -ImageUrlField.template = "web.ImageUrlField"; -ImageUrlField.props = { - ...standardFieldProps, - width: { type: Number, optional: true }, - height: { type: Number, optional: true }, -}; - -ImageUrlField.displayName = _lt("Image"); -ImageUrlField.supportedTypes = ["char"]; - -ImageUrlField.extractProps = ({ attrs }) => { - return { +export const imageUrlField = { + component: ImageUrlField, + displayName: _lt("Image"), + supportedTypes: ["char"], + extractProps: ({ attrs }) => ({ width: attrs.options.size ? attrs.options.size[0] : attrs.width, height: attrs.options.size ? attrs.options.size[1] : attrs.height, - }; + }), }; -registry.category("fields").add("image_url", ImageUrlField); +registry.category("fields").add("image_url", imageUrlField); // TODO WOWL: remove below when old registry is removed. -registry.category("fields").add("kanban.image_url", ImageUrlField); +registry.category("fields").add("kanban.image_url", imageUrlField); diff --git a/addons/web/static/src/views/fields/integer/integer_field.js b/addons/web/static/src/views/fields/integer/integer_field.js index dc875c03c9c..f8e7338bca9 100644 --- a/addons/web/static/src/views/fields/integer/integer_field.js +++ b/addons/web/static/src/views/fields/integer/integer_field.js @@ -11,6 +11,17 @@ import { useNumpadDecimal } from "../numpad_decimal_hook"; import { Component } from "@odoo/owl"; export class IntegerField extends Component { + static template = "web.IntegerField"; + static props = { + ...standardFieldProps, + inputType: { type: String, optional: true }, + step: { type: Number, optional: true }, + placeholder: { type: String, optional: true }, + }; + static defaultProps = { + inputType: "text", + }; + setup() { useInputField({ getValue: () => this.formattedValue, @@ -28,27 +39,16 @@ export class IntegerField extends Component { } } -IntegerField.template = "web.IntegerField"; -IntegerField.props = { - ...standardFieldProps, - inputType: { type: String, optional: true }, - step: { type: Number, optional: true }, - placeholder: { type: String, optional: true }, -}; -IntegerField.defaultProps = { - inputType: "text", -}; - -IntegerField.displayName = _lt("Integer"); -IntegerField.supportedTypes = ["integer"]; - -IntegerField.isEmpty = (record, fieldName) => (record.data[fieldName] === false ? true : false); -IntegerField.extractProps = ({ attrs }) => { - return { +export const integerField = { + component: IntegerField, + displayName: _lt("Integer"), + supportedTypes: ["integer"], + isEmpty: (record, fieldName) => record.data[fieldName] === false, + extractProps: ({ attrs }) => ({ inputType: attrs.options.type, step: attrs.options.step, placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("integer", IntegerField); +registry.category("fields").add("integer", integerField); diff --git a/addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.js b/addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.js index d14199b06fc..22f6de487bc 100644 --- a/addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.js +++ b/addons/web/static/src/views/fields/journal_dashboard_graph/journal_dashboard_graph_field.js @@ -9,6 +9,12 @@ import { useService } from "@web/core/utils/hooks"; import { Component, onWillStart, useEffect, useRef } from "@odoo/owl"; export class JournalDashboardGraphField extends Component { + static template = "web.JournalDashboardGraphField"; + static props = { + ...standardFieldProps, + graphType: String, + }; + setup() { this.chart = null; this.cookies = useService("cookie"); @@ -145,18 +151,12 @@ export class JournalDashboardGraphField extends Component { } } -JournalDashboardGraphField.template = "web.JournalDashboardGraphField"; -JournalDashboardGraphField.props = { - ...standardFieldProps, - graphType: String, -}; - -JournalDashboardGraphField.supportedTypes = ["text"]; - -JournalDashboardGraphField.extractProps = ({ attrs }) => { - return { +export const journalDashboardGraphField = { + component: JournalDashboardGraphField, + supportedTypes: ["text"], + extractProps: ({ attrs }) => ({ graphType: attrs.graph_type, - }; + }), }; -registry.category("fields").add("dashboard_graph", JournalDashboardGraphField); +registry.category("fields").add("dashboard_graph", journalDashboardGraphField); diff --git a/addons/web/static/src/views/fields/label_selection/label_selection_field.js b/addons/web/static/src/views/fields/label_selection/label_selection_field.js index 23f29516f9d..263bbd98457 100644 --- a/addons/web/static/src/views/fields/label_selection/label_selection_field.js +++ b/addons/web/static/src/views/fields/label_selection/label_selection_field.js @@ -8,6 +8,15 @@ import { formatSelection } from "../formatters"; import { Component } from "@odoo/owl"; export class LabelSelectionField extends Component { + static template = "web.LabelSelectionField"; + static props = { + ...standardFieldProps, + classesObj: { type: Object, optional: true }, + }; + static defaultProps = { + classesObj: {}, + }; + get className() { return this.props.classesObj[this.props.value] || "primary"; } @@ -18,22 +27,13 @@ export class LabelSelectionField extends Component { } } -LabelSelectionField.template = "web.LabelSelectionField"; -LabelSelectionField.props = { - ...standardFieldProps, - classesObj: { type: Object, optional: true }, -}; -LabelSelectionField.defaultProps = { - classesObj: {}, -}; - -LabelSelectionField.displayName = _lt("Label Selection"); -LabelSelectionField.supportedTypes = ["selection"]; - -LabelSelectionField.extractProps = ({ attrs }) => { - return { +export const labelSelectionField = { + component: LabelSelectionField, + displayName: _lt("Label Selection"), + supportedTypes: ["selection"], + extractProps: ({ attrs }) => ({ classesObj: attrs.options.classes, - }; + }), }; -registry.category("fields").add("label_selection", LabelSelectionField); +registry.category("fields").add("label_selection", labelSelectionField); diff --git a/addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.js b/addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.js index e7f26fcf5b0..f9605ece90c 100644 --- a/addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.js +++ b/addons/web/static/src/views/fields/many2many_binary/many2many_binary_field.js @@ -9,6 +9,17 @@ import { useX2ManyCrud } from "@web/views/fields/relational_utils"; import { Component } from "@odoo/owl"; export class Many2ManyBinaryField extends Component { + static template = "web.Many2ManyBinaryField"; + static components = { + FileInput, + }; + static props = { + ...standardFieldProps, + acceptedFileExtensions: { type: String, optional: true }, + className: { type: String, optional: true }, + uploadText: { type: String, optional: true }, + }; + setup() { this.orm = useService("orm"); this.notification = useService("notification"); @@ -45,29 +56,19 @@ export class Many2ManyBinaryField extends Component { } } -Many2ManyBinaryField.template = "web.Many2ManyBinaryField"; -Many2ManyBinaryField.components = { - FileInput, -}; -Many2ManyBinaryField.props = { - ...standardFieldProps, - acceptedFileExtensions: { type: String, optional: true }, - className: { type: String, optional: true }, - uploadText: { type: String, optional: true }, -}; -Many2ManyBinaryField.supportedTypes = ["many2many"]; -Many2ManyBinaryField.fieldsToFetch = [ - { name: "name", type: "char" }, - { name: "mimetype", type: "char" }, -]; - -Many2ManyBinaryField.isEmpty = () => false; -Many2ManyBinaryField.extractProps = ({ attrs, field }) => { - return { +export const many2ManyBinaryField = { + component: Many2ManyBinaryField, + supportedTypes: ["many2many"], + isEmpty: () => false, + fieldsToFetch: [ + { name: "name", type: "char" }, + { name: "mimetype", type: "char" }, + ], + extractProps: ({ attrs, field }) => ({ acceptedFileExtensions: attrs.options.accepted_file_extensions, className: attrs.class, uploadText: field.string, - }; + }), }; -registry.category("fields").add("many2many_binary", Many2ManyBinaryField); +registry.category("fields").add("many2many_binary", many2ManyBinaryField); diff --git a/addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.js b/addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.js index da1403434b9..76905792436 100644 --- a/addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.js +++ b/addons/web/static/src/views/fields/many2many_checkboxes/many2many_checkboxes_field.js @@ -8,6 +8,12 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class Many2ManyCheckboxesField extends Component { + static template = "web.Many2ManyCheckboxesField"; + static components = { CheckBox }; + static props = { + ...standardFieldProps, + }; + get items() { return this.props.record.preloadedData[this.props.name]; } @@ -30,19 +36,15 @@ export class Many2ManyCheckboxesField extends Component { } } -Many2ManyCheckboxesField.template = "web.Many2ManyCheckboxesField"; -Many2ManyCheckboxesField.components = { CheckBox }; -Many2ManyCheckboxesField.props = { - ...standardFieldProps, +export const many2ManyCheckboxesField = { + component: Many2ManyCheckboxesField, + displayName: _lt("Checkboxes"), + supportedTypes: ["many2many"], + isEmpty: () => false, + legacySpecialData: "_fetchSpecialRelation", }; -Many2ManyCheckboxesField.legacySpecialData = "_fetchSpecialRelation"; -Many2ManyCheckboxesField.displayName = _lt("Checkboxes"); -Many2ManyCheckboxesField.supportedTypes = ["many2many"]; - -Many2ManyCheckboxesField.isEmpty = () => false; - -registry.category("fields").add("many2many_checkboxes", Many2ManyCheckboxesField); +registry.category("fields").add("many2many_checkboxes", many2ManyCheckboxesField); export function preloadMany2ManyCheckboxes(orm, record, fieldName) { const field = record.fields[fieldName]; diff --git a/addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.js b/addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.js index 0ce81139da2..20c85005b79 100644 --- a/addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.js +++ b/addons/web/static/src/views/fields/many2many_tags/kanban_many2many_tags_field.js @@ -1,9 +1,11 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2ManyTagsField } from "./many2many_tags_field"; +import { Many2ManyTagsField, many2ManyTagsField } from "./many2many_tags_field"; export class KanbanMany2ManyTagsField extends Many2ManyTagsField { + static template = "web.KanbanMany2ManyTagsField"; + get tags() { return super.tags.reduce((kanbanTags, tag) => { if (tag.colorIndex !== 0) { @@ -15,6 +17,9 @@ export class KanbanMany2ManyTagsField extends Many2ManyTagsField { } } -KanbanMany2ManyTagsField.template = "web.KanbanMany2ManyTagsField"; +export const kanbanMany2ManyTagsField = { + ...many2ManyTagsField, + component: KanbanMany2ManyTagsField, +}; -registry.category("fields").add("kanban.many2many_tags", KanbanMany2ManyTagsField); +registry.category("fields").add("kanban.many2many_tags", kanbanMany2ManyTagsField); diff --git a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js index 5b02b501bd6..3648d01d2bc 100644 --- a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js +++ b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js @@ -18,14 +18,41 @@ import { useService } from "@web/core/utils/hooks"; import { Component, useRef } from "@odoo/owl"; -class Many2ManyTagsFieldColorListPopover extends Component {} -Many2ManyTagsFieldColorListPopover.template = "web.Many2ManyTagsFieldColorListPopover"; -Many2ManyTagsFieldColorListPopover.components = { - CheckBox, - ColorList, -}; +class Many2ManyTagsFieldColorListPopover extends Component { + static template = "web.Many2ManyTagsFieldColorListPopover"; + static components = { + CheckBox, + ColorList, + }; +} export class Many2ManyTagsField extends Component { + static template = "web.Many2ManyTagsField"; + static components = { + TagsList, + Many2XAutocomplete, + }; + static props = { + ...standardFieldProps, + canCreate: { type: Boolean, optional: true }, + canQuickCreate: { type: Boolean, optional: true }, + canCreateEdit: { type: Boolean, optional: true }, + colorField: { type: String, optional: true }, + createDomain: { type: [Array, Boolean], optional: true }, + placeholder: { type: String, optional: true }, + relation: { type: String }, + nameCreateField: { type: String, optional: true }, + }; + static defaultProps = { + canCreate: true, + canQuickCreate: true, + canCreateEdit: true, + nameCreateField: "name", + }; + + static RECORD_COLORS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + static SEARCH_MORE_LIMIT = 320; + setup() { this.orm = useService("orm"); this.previousColorsMap = {}; @@ -216,69 +243,59 @@ export class Many2ManyTagsField extends Component { } } -Many2ManyTagsField.RECORD_COLORS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; -Many2ManyTagsField.SEARCH_MORE_LIMIT = 320; +export const many2ManyTagsField = { + component: Many2ManyTagsField, + displayName: _lt("Tags"), + supportedTypes: ["many2many"], + isSet: (value) => value.count > 0, + fieldsToFetch: (fieldInfo) => { + const fieldsToFetch = [{ name: "display_name", type: "char" }]; + if (fieldInfo.options.color_field) { + fieldsToFetch.push({ name: fieldInfo.options.color_field, type: "integer" }); + } + return fieldsToFetch; + }, + extractProps: ({ attrs, field }) => { + const noCreate = Boolean(attrs.options.no_create); + const canCreate = attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !noCreate; + const noQuickCreate = Boolean(attrs.options.no_quick_create); + const noCreateEdit = Boolean(attrs.options.no_create_edit); + return { + colorField: attrs.options.color_field, + nameCreateField: attrs.options.create_name_field, + canCreate, + canQuickCreate: canCreate && !noQuickCreate, + canCreateEdit: canCreate && !noCreateEdit, + createDomain: attrs.options.create, + placeholder: attrs.placeholder, -Many2ManyTagsField.template = "web.Many2ManyTagsField"; -Many2ManyTagsField.components = { - TagsList, - Many2XAutocomplete, + relation: field.relation, + }; + }, }; -Many2ManyTagsField.props = { - ...standardFieldProps, - canCreate: { type: Boolean, optional: true }, - canQuickCreate: { type: Boolean, optional: true }, - canCreateEdit: { type: Boolean, optional: true }, - colorField: { type: String, optional: true }, - createDomain: { type: [Array, Boolean], optional: true }, - placeholder: { type: String, optional: true }, - relation: { type: String }, - nameCreateField: { type: String, optional: true }, -}; -Many2ManyTagsField.defaultProps = { - canCreate: true, - canQuickCreate: true, - canCreateEdit: true, - nameCreateField: "name", -}; - -Many2ManyTagsField.displayName = _lt("Tags"); -Many2ManyTagsField.supportedTypes = ["many2many"]; -Many2ManyTagsField.fieldsToFetch = (fieldInfo) => { - const fieldsToFetch = [{ name: "display_name", type: "char" }]; - if (fieldInfo.options.color_field) { - fieldsToFetch.push({ name: fieldInfo.options.color_field, type: "integer" }); - } - return fieldsToFetch; -}; -Many2ManyTagsField.isSet = (value) => value.count > 0; - -Many2ManyTagsField.extractProps = ({ attrs, field }) => { - const noCreate = Boolean(attrs.options.no_create); - const canCreate = attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !noCreate; - const noQuickCreate = Boolean(attrs.options.no_quick_create); - const noCreateEdit = Boolean(attrs.options.no_create_edit); - - return { - colorField: attrs.options.color_field, - nameCreateField: attrs.options.create_name_field, - relation: field.relation, - canCreate, - canQuickCreate: canCreate && !noQuickCreate, - canCreateEdit: canCreate && !noCreateEdit, - createDomain: attrs.options.create, - placeholder: attrs.placeholder, - }; -}; - -registry.category("fields").add("many2many_tags", Many2ManyTagsField); +registry.category("fields").add("many2many_tags", many2ManyTagsField); +registry.category("fields").add("calendar.one2many", many2ManyTagsField); +registry.category("fields").add("calendar.many2many", many2ManyTagsField); /** * A specialization that allows to edit the color with the colorpicker. * Used in form view. */ export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField { + static components = { + ...super.components, + Popover: Many2ManyTagsFieldColorListPopover, + }; + static props = { + ...super.props, + canEditColor: { type: Boolean, optional: true }, + }; + static defaultProps = { + ...super.defaultProps, + canEditColor: true, + }; + getTagProps(record) { const props = super.getTagProps(record); props.onClick = (ev) => this.onBadgeClick(ev, record); @@ -339,30 +356,13 @@ export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField { } } -Many2ManyTagsFieldColorEditable.components = { - ...Many2ManyTagsField.components, - Popover: Many2ManyTagsFieldColorListPopover, -}; -Many2ManyTagsFieldColorEditable.props = { - ...Many2ManyTagsField.props, - canEditColor: { type: Boolean, optional: true }, -}; -Many2ManyTagsFieldColorEditable.defaultProps = { - ...Many2ManyTagsField.defaultProps, - canEditColor: true, -}; -Many2ManyTagsFieldColorEditable.extractProps = (params) => { - const props = Many2ManyTagsField.extractProps(params); - const attrs = params.attrs; - const noEditColor = Boolean(attrs.options.no_edit_color); - const hasColorField = Boolean(attrs.options.color_field); - return { - ...props, - canEditColor: !noEditColor && hasColorField, - }; +export const many2ManyTagsFieldColorEditable = { + ...many2ManyTagsField, + component: Many2ManyTagsFieldColorEditable, + extractProps: (params) => ({ + ...many2ManyTagsField.extractProps(params), + canEditColor: !params.attrs.options.no_edit_color && !!params.attrs.options.color_field, + }), }; -registry.category("fields").add("form.many2many_tags", Many2ManyTagsFieldColorEditable); - -registry.category("fields").add("calendar.one2many", Many2ManyTagsField); -registry.category("fields").add("calendar.many2many", Many2ManyTagsField); +registry.category("fields").add("form.many2many_tags", many2ManyTagsFieldColorEditable); diff --git a/addons/web/static/src/views/fields/many2many_tags/tags_list.js b/addons/web/static/src/views/fields/many2many_tags/tags_list.js index 667fad5447c..0ba482aa122 100644 --- a/addons/web/static/src/views/fields/many2many_tags/tags_list.js +++ b/addons/web/static/src/views/fields/many2many_tags/tags_list.js @@ -3,6 +3,21 @@ import { Component } from "@odoo/owl"; export class TagsList extends Component { + static template = "web.TagsList"; + static defaultProps = { + className: "", + displayBadge: true, + displayText: true, + }; + static props = { + className: { type: String, optional: true }, + displayBadge: { type: Boolean, optional: true }, + displayText: { type: Boolean, optional: true }, + name: { type: String, optional: true }, + itemsVisible: { type: Number, optional: true }, + tags: { type: Object, optional: true }, + }; + get visibleTags() { if (this.props.itemsVisible && this.props.tags.length > this.props.itemsVisible) { return this.props.tags.slice(0, this.props.itemsVisible - 1); @@ -24,17 +39,3 @@ export class TagsList extends Component { }); } } -TagsList.template = "web.TagsList"; -TagsList.defaultProps = { - className: "", - displayBadge: true, - displayText: true, -}; -TagsList.props = { - className: { type: String, optional: true }, - displayBadge: { type: Boolean, optional: true }, - displayText: { type: Boolean, optional: true }, - name: { type: String, optional: true }, - itemsVisible: { type: Number, optional: true }, - tags: { type: Object, optional: true }, -}; diff --git a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.js b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.js index e0c638efab3..6b111137966 100644 --- a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.js +++ b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.js @@ -2,10 +2,19 @@ import { registry } from "@web/core/registry"; import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; -import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { + many2ManyTagsField, + Many2ManyTagsField, +} from "@web/views/fields/many2many_tags/many2many_tags_field"; import { TagsList } from "../many2many_tags/tags_list"; export class Many2ManyTagsAvatarField extends Many2ManyTagsField { + static template = "web.Many2ManyTagsAvatarField"; + static components = { + Many2XAutocomplete, + TagsList, + }; + get tags() { return super.tags.map((tag) => ({ ...tag, @@ -15,13 +24,12 @@ export class Many2ManyTagsAvatarField extends Many2ManyTagsField { } } -Many2ManyTagsAvatarField.template = "web.Many2ManyTagsAvatarField"; -Many2ManyTagsAvatarField.components = { - Many2XAutocomplete, - TagsList, +export const many2ManyTagsAvatarField = { + ...many2ManyTagsField, + component: Many2ManyTagsAvatarField, }; -registry.category("fields").add("many2many_tags_avatar", Many2ManyTagsAvatarField); +registry.category("fields").add("many2many_tags_avatar", many2ManyTagsAvatarField); export class ListKanbanMany2ManyTagsAvatarField extends Many2ManyTagsAvatarField { get itemsVisible() { @@ -36,5 +44,10 @@ export class ListKanbanMany2ManyTagsAvatarField extends Many2ManyTagsAvatarField } } -registry.category("fields").add("list.many2many_tags_avatar", ListKanbanMany2ManyTagsAvatarField); -registry.category("fields").add("kanban.many2many_tags_avatar", ListKanbanMany2ManyTagsAvatarField); +export const listKanbanMany2ManyTagsAvatarField = { + ...many2ManyTagsAvatarField, + component: ListKanbanMany2ManyTagsAvatarField, +}; + +registry.category("fields").add("list.many2many_tags_avatar", listKanbanMany2ManyTagsAvatarField); +registry.category("fields").add("kanban.many2many_tags_avatar", listKanbanMany2ManyTagsAvatarField); diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.js b/addons/web/static/src/views/fields/many2one/many2one_field.js index ffeb238a9cc..88e72b19afe 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.js +++ b/addons/web/static/src/views/fields/many2one/many2one_field.js @@ -14,6 +14,9 @@ import * as BarcodeScanner from "@web/webclient/barcode/barcode_scanner"; import { Component, onWillUpdateProps, useState } from "@odoo/owl"; class CreateConfirmationDialog extends Component { + static template = "web.Many2OneField.CreateConfirmationDialog"; + static components = { Dialog }; + get title() { return sprintf(this.env._t("New: %s"), this.props.name); } @@ -23,8 +26,6 @@ class CreateConfirmationDialog extends Component { this.props.close(); } } -CreateConfirmationDialog.components = { Dialog }; -CreateConfirmationDialog.template = "web.Many2OneField.CreateConfirmationDialog"; export function m2oTupleFromData(data) { const id = data.id; @@ -39,6 +40,44 @@ export function m2oTupleFromData(data) { } export class Many2OneField extends Component { + static template = "web.Many2OneField"; + static components = { + Many2XAutocomplete, + }; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + canOpen: { type: Boolean, optional: true }, + canCreate: { type: Boolean, optional: true }, + canWrite: { type: Boolean, optional: true }, + canQuickCreate: { type: Boolean, optional: true }, + canCreateEdit: { type: Boolean, optional: true }, + nameCreateField: { type: String, optional: true }, + searchLimit: { type: Number, optional: true }, + relation: { type: String, optional: true }, + string: { type: String, optional: true }, + canScanBarcode: { type: Boolean, optional: true }, + openTarget: { + type: String, + validate: (v) => ["current", "new"].includes(v), + optional: true, + }, + }; + static defaultProps = { + canOpen: true, + canCreate: true, + canWrite: true, + canQuickCreate: true, + canCreateEdit: true, + nameCreateField: "name", + searchLimit: 7, + string: "", + canScanBarcode: false, + openTarget: "current", + }; + + static SEARCH_MORE_LIMIT = 320; + setup() { this.orm = useService("orm"); this.action = useService("action"); @@ -243,68 +282,31 @@ export class Many2OneField extends Component { } } -Many2OneField.SEARCH_MORE_LIMIT = 320; +export const many2OneField = { + component: Many2OneField, + displayName: _lt("Many2one"), + supportedTypes: ["many2one"], + extractProps: ({ attrs, field }) => { + const canCreate = + attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !attrs.options.no_create; + return { + placeholder: attrs.placeholder, + canOpen: !attrs.options.no_open, + canCreate, + canWrite: attrs.can_write && Boolean(JSON.parse(attrs.can_write)), + canQuickCreate: canCreate && !attrs.options.no_quick_create, + canCreateEdit: canCreate && !attrs.options.no_create_edit, + nameCreateField: attrs.options.create_name_field, + canScanBarcode: !!attrs.options.can_scan_barcode, + openTarget: attrs.open_target, -Many2OneField.template = "web.Many2OneField"; -Many2OneField.components = { - Many2XAutocomplete, -}; -Many2OneField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, - canOpen: { type: Boolean, optional: true }, - canCreate: { type: Boolean, optional: true }, - canWrite: { type: Boolean, optional: true }, - canQuickCreate: { type: Boolean, optional: true }, - canCreateEdit: { type: Boolean, optional: true }, - nameCreateField: { type: String, optional: true }, - searchLimit: { type: Number, optional: true }, - relation: { type: String, optional: true }, - string: { type: String, optional: true }, - canScanBarcode: { type: Boolean, optional: true }, - openTarget: { type: String, validate: (v) => ["current", "new"].includes(v), optional: true }, -}; -Many2OneField.defaultProps = { - canOpen: true, - canCreate: true, - canWrite: true, - canQuickCreate: true, - canCreateEdit: true, - nameCreateField: "name", - searchLimit: 7, - string: "", - canScanBarcode: false, - openTarget: "current", + relation: field.relation, + string: attrs.string || field.string, + }; + }, }; -Many2OneField.displayName = _lt("Many2one"); -Many2OneField.supportedTypes = ["many2one"]; - -Many2OneField.extractProps = ({ attrs, field }) => { - const noOpen = Boolean(attrs.options.no_open); - const noCreate = Boolean(attrs.options.no_create); - const canCreate = attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !noCreate; - const canWrite = attrs.can_write && Boolean(JSON.parse(attrs.can_write)); - const noQuickCreate = Boolean(attrs.options.no_quick_create); - const noCreateEdit = Boolean(attrs.options.no_create_edit); - const canScanBarcode = Boolean(attrs.options.can_scan_barcode); - - return { - placeholder: attrs.placeholder, - canOpen: !noOpen, - canCreate, - canWrite, - canQuickCreate: canCreate && !noQuickCreate, - canCreateEdit: canCreate && !noCreateEdit, - relation: field.relation, - string: attrs.string || field.string, - nameCreateField: attrs.options.create_name_field, - canScanBarcode: canScanBarcode, - openTarget: attrs.open_target, - }; -}; - -registry.category("fields").add("many2one", Many2OneField); +registry.category("fields").add("many2one", many2OneField); // the two following lines are there to prevent the fallback on legacy widgets -registry.category("fields").add("list.many2one", Many2OneField); -registry.category("fields").add("kanban.many2one", Many2OneField); +registry.category("fields").add("list.many2one", many2OneField); +registry.category("fields").add("kanban.many2one", many2OneField); diff --git a/addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.js b/addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.js index 3ae3c1bef31..0c66776ca56 100644 --- a/addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.js +++ b/addons/web/static/src/views/fields/many2one_avatar/many2one_avatar_field.js @@ -1,22 +1,23 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { Many2OneField } from "../many2one/many2one_field"; +import { many2OneField, Many2OneField } from "../many2one/many2one_field"; import { Component } from "@odoo/owl"; -export class Many2OneAvatarField extends Component {} +export class Many2OneAvatarField extends Component { + static template = "web.Many2OneAvatarField"; + static components = { + Many2OneField, + }; + static props = { + ...Many2OneField.props, + }; +} -Many2OneAvatarField.template = "web.Many2OneAvatarField"; -Many2OneAvatarField.components = { - Many2OneField, -}; -Many2OneAvatarField.props = { - ...Many2OneField.props, +export const many2OneAvatarField = { + ...many2OneField, + component: Many2OneAvatarField, }; -Many2OneAvatarField.supportedTypes = ["many2one"]; - -Many2OneAvatarField.extractProps = Many2OneField.extractProps; - -registry.category("fields").add("many2one_avatar", Many2OneAvatarField); +registry.category("fields").add("many2one_avatar", many2OneAvatarField); diff --git a/addons/web/static/src/views/fields/many2one_barcode/many2one_barcode_field.js b/addons/web/static/src/views/fields/many2one_barcode/many2one_barcode_field.js index 7b77d8efa59..bff69c3b110 100644 --- a/addons/web/static/src/views/fields/many2one_barcode/many2one_barcode_field.js +++ b/addons/web/static/src/views/fields/many2one_barcode/many2one_barcode_field.js @@ -2,27 +2,23 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { Many2OneField } from "../many2one/many2one_field"; +import { many2OneField, Many2OneField } from "../many2one/many2one_field"; -export class Many2OneBarcodeField extends Many2OneField {} - -Many2OneBarcodeField.props = { - ...Many2OneField.props, -}; -Many2OneBarcodeField.defaultProps = { - ...Many2OneField.defaultProps, - canScanBarcode: true, -}; - -Many2OneBarcodeField.displayName = _lt("Many2OneBarcode"); -Many2OneBarcodeField.template = "web.Many2OneField"; -Many2OneBarcodeField.supportedTypes = ["many2one"]; - -Many2OneBarcodeField.extractProps = (args) => { - return { - ...Many2OneField.extractProps(args), +export class Many2OneBarcodeField extends Many2OneField { + static defaultProps = { + ...super.defaultProps, canScanBarcode: true, }; +} + +export const many2OneBarcodeField = { + ...many2OneField, + component: Many2OneBarcodeField, + displayName: _lt("Many2OneBarcode"), + extractProps: (params) => ({ + ...many2OneField.extractProps(params), + canScanBarcode: true, + }), }; -registry.category("fields").add("many2one_barcode", Many2OneBarcodeField); +registry.category("fields").add("many2one_barcode", many2OneBarcodeField); diff --git a/addons/web/static/src/views/fields/many2one_reference/many2one_reference_field.js b/addons/web/static/src/views/fields/many2one_reference/many2one_reference_field.js index d0822a2fba6..8539acee6f1 100644 --- a/addons/web/static/src/views/fields/many2one_reference/many2one_reference_field.js +++ b/addons/web/static/src/views/fields/many2one_reference/many2one_reference_field.js @@ -1,6 +1,6 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { IntegerField } from "../integer/integer_field"; +import { integerField } from "../integer/integer_field"; -registry.category("fields").add("many2one_reference", IntegerField); +registry.category("fields").add("many2one_reference", integerField); diff --git a/addons/web/static/src/views/fields/monetary/monetary_field.js b/addons/web/static/src/views/fields/monetary/monetary_field.js index a6199ed1167..22b2973b8a7 100644 --- a/addons/web/static/src/views/fields/monetary/monetary_field.js +++ b/addons/web/static/src/views/fields/monetary/monetary_field.js @@ -12,6 +12,20 @@ import { session } from "@web/session"; import { Component } from "@odoo/owl"; export class MonetaryField extends Component { + static template = "web.MonetaryField"; + static props = { + ...standardFieldProps, + currencyField: { type: String, optional: true }, + inputType: { type: String, optional: true }, + useFieldDigits: { type: Boolean, optional: true }, + hideSymbol: { type: Boolean, optional: true }, + placeholder: { type: String, optional: true }, + }; + static defaultProps = { + hideSymbol: false, + inputType: "text", + }; + setup() { useInputField({ getValue: () => this.formattedValue, @@ -62,31 +76,17 @@ export class MonetaryField extends Component { } } -MonetaryField.template = "web.MonetaryField"; -MonetaryField.props = { - ...standardFieldProps, - currencyField: { type: String, optional: true }, - inputType: { type: String, optional: true }, - useFieldDigits: { type: Boolean, optional: true }, - hideSymbol: { type: Boolean, optional: true }, - placeholder: { type: String, optional: true }, -}; -MonetaryField.defaultProps = { - hideSymbol: false, - inputType: "text", -}; - -MonetaryField.supportedTypes = ["monetary", "float"]; -MonetaryField.displayName = _lt("Monetary"); - -MonetaryField.extractProps = ({ attrs }) => { - return { +export const monetaryField = { + component: MonetaryField, + supportedTypes: ["monetary", "float"], + displayName: _lt("Monetary"), + extractProps: ({ attrs }) => ({ currencyField: attrs.options.currency_field, inputType: attrs.type, useFieldDigits: attrs.options.field_digits, hideSymbol: attrs.options.no_symbol, placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("monetary", MonetaryField); +registry.category("fields").add("monetary", monetaryField); diff --git a/addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.js b/addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.js index a8561b0c982..e629d5cad51 100644 --- a/addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.js +++ b/addons/web/static/src/views/fields/pdf_viewer/pdf_viewer_field.js @@ -10,6 +10,16 @@ import { FileUploader } from "../file_handler"; import { Component, onWillUpdateProps, useState } from "@odoo/owl"; export class PdfViewerField extends Component { + static template = "web.PdfViewerField"; + static components = { + FileUploader, + }; + static props = { + ...standardFieldProps, + fileNameField: { type: String, optional: true }, + previewImage: { type: String, optional: true }, + }; + setup() { this.notification = useService("notification"); this.state = useState({ @@ -75,24 +85,14 @@ export class PdfViewerField extends Component { } } -PdfViewerField.template = "web.PdfViewerField"; -PdfViewerField.components = { - FileUploader, -}; -PdfViewerField.props = { - ...standardFieldProps, - fileNameField: { type: String, optional: true }, - previewImage: { type: String, optional: true }, -}; - -PdfViewerField.displayName = _lt("PDF Viewer"); -PdfViewerField.supportedTypes = ["binary"]; - -PdfViewerField.extractProps = ({ attrs }) => { - return { +export const pdfViewerField = { + component: PdfViewerField, + displayName: _lt("PDF Viewer"), + supportedTypes: ["binary"], + extractProps: ({ attrs }) => ({ fileNameField: attrs.filename, previewImage: attrs.options.preview_image, - }; + }), }; -registry.category("fields").add("pdf_viewer", PdfViewerField); +registry.category("fields").add("pdf_viewer", pdfViewerField); diff --git a/addons/web/static/src/views/fields/percent_pie/percent_pie_field.js b/addons/web/static/src/views/fields/percent_pie/percent_pie_field.js index bf53f3cf50a..64c2de3bfae 100644 --- a/addons/web/static/src/views/fields/percent_pie/percent_pie_field.js +++ b/addons/web/static/src/views/fields/percent_pie/percent_pie_field.js @@ -7,6 +7,12 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class PercentPieField extends Component { + static template = "web.PercentPieField"; + static props = { + ...standardFieldProps, + string: { type: String, optional: true }, + }; + get transform() { const rotateDeg = (360 * this.props.value) / 100; return { @@ -17,20 +23,14 @@ export class PercentPieField extends Component { } } -PercentPieField.template = "web.PercentPieField"; -PercentPieField.props = { - ...standardFieldProps, - string: { type: String, optional: true }, -}; - -PercentPieField.displayName = _lt("PercentPie"); -PercentPieField.supportedTypes = ["float", "integer"]; - -PercentPieField.extractProps = ({ attrs }) => { - return { +export const percentPieField = { + component: PercentPieField, + displayName: _lt("PercentPie"), + supportedTypes: ["float", "integer"], + additionalClasses: ["o_field_percent_pie"], + extractProps: ({ attrs }) => ({ string: attrs.string, - }; + }), }; -PercentPieField.additionalClasses = ["o_field_percent_pie"]; -registry.category("fields").add("percentpie", PercentPieField); +registry.category("fields").add("percentpie", percentPieField); diff --git a/addons/web/static/src/views/fields/percentage/percentage_field.js b/addons/web/static/src/views/fields/percentage/percentage_field.js index 84649231e16..31eeb18c009 100644 --- a/addons/web/static/src/views/fields/percentage/percentage_field.js +++ b/addons/web/static/src/views/fields/percentage/percentage_field.js @@ -11,6 +11,13 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class PercentageField extends Component { + static template = "web.PercentageField"; + static props = { + ...standardFieldProps, + digits: { type: Array, optional: true }, + placeholder: { type: String, optional: true }, + }; + setup() { useInputField({ getValue: () => @@ -31,29 +38,27 @@ export class PercentageField extends Component { } } -PercentageField.template = "web.PercentageField"; -PercentageField.props = { - ...standardFieldProps, - digits: { type: Array, optional: true }, - placeholder: { type: String, optional: true }, +export const percentageField = { + component: PercentageField, + displayName: _lt("Percentage"), + supportedTypes: ["integer", "float"], + extractProps: ({ attrs, field }) => { + // Sadly, digits param was available as an option and an attr. + // The option version could be removed with some xml refactoring. + let digits; + if (attrs.digits) { + digits = JSON.parse(attrs.digits); + } else if (attrs.options.digits) { + digits = attrs.options.digits; + } else if (Array.isArray(field.digits)) { + digits = field.digits; + } + + return { + digits, + placeholder: attrs.placeholder, + }; + }, }; -PercentageField.displayName = _lt("Percentage"); -PercentageField.supportedTypes = ["integer", "float"]; - -PercentageField.extractProps = ({ attrs, field }) => { - let digits; - if (attrs.digits) { - digits = JSON.parse(attrs.digits); - } else if (attrs.options.digits) { - digits = attrs.options.digits; - } else if (Array.isArray(field.digits)) { - digits = field.digits; - } - return { - digits, - placeholder: attrs.placeholder, - }; -}; - -registry.category("fields").add("percentage", PercentageField); +registry.category("fields").add("percentage", percentageField); diff --git a/addons/web/static/src/views/fields/phone/phone_field.js b/addons/web/static/src/views/fields/phone/phone_field.js index 6ba836d5f32..b40dde5e190 100644 --- a/addons/web/static/src/views/fields/phone/phone_field.js +++ b/addons/web/static/src/views/fields/phone/phone_field.js @@ -8,28 +8,35 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class PhoneField extends Component { + static template = "web.PhoneField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + }; + setup() { useInputField({ getValue: () => this.props.value || "" }); } } -PhoneField.template = "web.PhoneField"; -PhoneField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, -}; - -PhoneField.displayName = _lt("Phone"); -PhoneField.supportedTypes = ["char"]; - -PhoneField.extractProps = ({ attrs }) => { - return { +export const phoneField = { + component: PhoneField, + displayName: _lt("Phone"), + supportedTypes: ["char"], + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder, - }; + }), }; -class FormPhoneField extends PhoneField {} -FormPhoneField.template = "web.FormPhoneField"; +registry.category("fields").add("phone", phoneField); -registry.category("fields").add("phone", PhoneField); -registry.category("fields").add("form.phone", FormPhoneField); +class FormPhoneField extends PhoneField { + static template = "web.FormPhoneField"; +} + +export const formPhoneField = { + ...phoneField, + component: FormPhoneField, +}; + +registry.category("fields").add("form.phone", formPhoneField); diff --git a/addons/web/static/src/views/fields/priority/priority_field.js b/addons/web/static/src/views/fields/priority/priority_field.js index b73edc079b6..31925f27153 100644 --- a/addons/web/static/src/views/fields/priority/priority_field.js +++ b/addons/web/static/src/views/fields/priority/priority_field.js @@ -8,6 +8,12 @@ import { standardFieldProps } from "../standard_field_props"; import { Component, useState } from "@odoo/owl"; export class PriorityField extends Component { + static template = "web.PriorityField"; + static props = { + ...standardFieldProps, + tooltipLabel: { type: String, optional: true }, + }; + setup() { this.state = useState({ index: -1, @@ -50,7 +56,9 @@ export class PriorityField extends Component { } getTooltip(value) { - return this.props.tooltipLabel && this.props.tooltipLabel !== value ? `${this.props.tooltipLabel}: ${value}` : value; + return this.props.tooltipLabel && this.props.tooltipLabel !== value + ? `${this.props.tooltipLabel}: ${value}` + : value; } /** * @param {string} value @@ -65,19 +73,13 @@ export class PriorityField extends Component { } } -PriorityField.template = "web.PriorityField"; -PriorityField.props = { - ...standardFieldProps, - tooltipLabel: { type: String, optional: true }, -}; - -PriorityField.displayName = _lt("Priority"); -PriorityField.supportedTypes = ["selection"]; - -PriorityField.extractProps = ({ field }) => { - return { +export const priorityField = { + component: PriorityField, + displayName: _lt("Priority"), + supportedTypes: ["selection"], + extractProps: ({ field }) => ({ tooltipLabel: field.string, - }; + }), }; -registry.category("fields").add("priority", PriorityField); +registry.category("fields").add("priority", priorityField); diff --git a/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js b/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js index 470d8bf67ff..18921386b25 100644 --- a/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js +++ b/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { ProgressBarField } from "./progress_bar_field"; +import { progressBarField, ProgressBarField } from "./progress_bar_field"; export class KanbanProgressBarField extends ProgressBarField { onClick() { @@ -11,4 +11,9 @@ export class KanbanProgressBarField extends ProgressBarField { } } -registry.category("fields").add("kanban.progressbar", KanbanProgressBarField); +export const kanbanProgressBarField = { + ...progressBarField, + component: KanbanProgressBarField, +}; + +registry.category("fields").add("kanban.progressbar", kanbanProgressBarField); diff --git a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js index 89a044ba53e..c9861843f01 100644 --- a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js +++ b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js @@ -12,6 +12,18 @@ const formatters = registry.category("formatters"); const parsers = registry.category("parsers"); export class ProgressBarField extends Component { + static template = "web.ProgressBarField"; + static props = { + ...standardFieldProps, + maxValueField: { type: [String, Number], optional: true }, + currentValueField: { type: String, optional: true }, + isEditable: { type: Boolean, optional: true }, + isEditableInReadonly: { type: Boolean, optional: true }, + isCurrentValueEditable: { type: Boolean, optional: true }, + isMaxValueEditable: { type: Boolean, optional: true }, + title: { type: String, optional: true }, + }; + setup() { useNumpadDecimal(); useAutofocus({ refName: "maxValue", selectAll: true }); @@ -139,23 +151,11 @@ export class ProgressBarField extends Component { } } -ProgressBarField.template = "web.ProgressBarField"; -ProgressBarField.props = { - ...standardFieldProps, - maxValueField: { type: [String, Number], optional: true }, - currentValueField: { type: String, optional: true }, - isEditable: { type: Boolean, optional: true }, - isEditableInReadonly: { type: Boolean, optional: true }, - isCurrentValueEditable: { type: Boolean, optional: true }, - isMaxValueEditable: { type: Boolean, optional: true }, - title: { type: String, optional: true }, -}; - -ProgressBarField.displayName = _lt("Progress Bar"); -ProgressBarField.supportedTypes = ["integer", "float"]; - -ProgressBarField.extractProps = ({ attrs }) => { - return { +export const progressBarField = { + component: ProgressBarField, + displayName: _lt("Progress Bar"), + supportedTypes: ["integer", "float"], + extractProps: ({ attrs }) => ({ maxValueField: attrs.options.max_value, currentValueField: attrs.options.current_value, isEditable: !attrs.options.readonly && attrs.options.editable, @@ -165,7 +165,7 @@ ProgressBarField.extractProps = ({ attrs }) => { (!attrs.options.edit_max_value || attrs.options.edit_current_value), isMaxValueEditable: attrs.options.editable && attrs.options.edit_max_value, title: attrs.title, - }; + }), }; -registry.category("fields").add("progressbar", ProgressBarField); +registry.category("fields").add("progressbar", progressBarField); diff --git a/addons/web/static/src/views/fields/properties/kanban_properties_field.js b/addons/web/static/src/views/fields/properties/kanban_properties_field.js index 9f1fc265d80..8748b890012 100644 --- a/addons/web/static/src/views/fields/properties/kanban_properties_field.js +++ b/addons/web/static/src/views/fields/properties/kanban_properties_field.js @@ -1,15 +1,20 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { PropertiesField } from "./properties_field"; +import { propertiesField, PropertiesField } from "./properties_field"; export class KanbanPropertiesField extends PropertiesField { + static template = "web.KanbanPropertiesField"; + async _checkDefinitionAccess() { // can not edit properties definition in the kanban view this.state.canChangeDefinition = false; } } -KanbanPropertiesField.template = "web.KanbanPropertiesField"; +export const kanbanPropertiesField = { + ...propertiesField, + component: KanbanPropertiesField, +}; -registry.category("fields").add("kanban.properties", KanbanPropertiesField); +registry.category("fields").add("kanban.properties", kanbanPropertiesField); diff --git a/addons/web/static/src/views/fields/properties/properties_field.js b/addons/web/static/src/views/fields/properties/properties_field.js index 6f13111ff20..2fffc5ee913 100644 --- a/addons/web/static/src/views/fields/properties/properties_field.js +++ b/addons/web/static/src/views/fields/properties/properties_field.js @@ -18,6 +18,19 @@ import { archParseBoolean } from "@web/views/utils"; import { Component, useRef, useState, useEffect, onWillStart } from "@odoo/owl"; export class PropertiesField extends Component { + static template = "web.PropertiesField"; + static components = { + Dropdown, + DropdownItem, + PropertyDefinition, + PropertyValue, + }; + static props = { + ...standardFieldProps, + columns: { type: Number, optional: true }, + hideKanbanOption: { type: Boolean, optional: true }, + }; + setup() { this.notification = useService("notification"); this.orm = useService("orm"); @@ -484,25 +497,14 @@ export class PropertiesField extends Component { } } -PropertiesField.template = "web.PropertiesField"; -PropertiesField.components = { - Dropdown, - DropdownItem, - PropertyDefinition, - PropertyValue, -}; -PropertiesField.props = { - ...standardFieldProps, - columns: { type: Number, optional: true }, - hideKanbanOption: { type: Boolean, optional: true }, -}; -PropertiesField.extractProps = ({ attrs, field }) => { - const columns = parseInt(attrs.columns || "1"); - const hideKanbanOption = archParseBoolean(attrs.hideKanbanOption); - return { columns, hideKanbanOption }; +export const propertiesField = { + component: PropertiesField, + displayName: _lt("Properties"), + supportedTypes: ["properties"], + extractProps: ({ attrs }) => ({ + columns: parseInt(attrs.columns || "1"), + hideKanbanOption: archParseBoolean(attrs.hideKanbanOption), + }), }; -PropertiesField.displayName = _lt("Properties"); -PropertiesField.supportedTypes = ["properties"]; - -registry.category("fields").add("properties", PropertiesField); +registry.category("fields").add("properties", propertiesField); diff --git a/addons/web/static/src/views/fields/radio/radio_field.js b/addons/web/static/src/views/fields/radio/radio_field.js index 7c52098a7e0..9f0630d73c2 100644 --- a/addons/web/static/src/views/fields/radio/radio_field.js +++ b/addons/web/static/src/views/fields/radio/radio_field.js @@ -7,6 +7,30 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class RadioField extends Component { + static template = "web.RadioField"; + static props = { + ...standardFieldProps, + orientation: { type: String, optional: true }, + }; + static defaultProps = { + orientation: "vertical", + }; + + static nextId = 0; + + static getItems(fieldName, record) { + switch (record.fields[fieldName].type) { + case "selection": + return record.fields[fieldName].selection; + case "many2one": { + const value = record.preloadedData[fieldName] || []; + return value.map((item) => [item.id, item.display_name]); + } + default: + return []; + } + } + setup() { this.id = `radio_field_${++RadioField.nextId}`; } @@ -43,42 +67,18 @@ export class RadioField extends Component { } } -RadioField.nextId = 0; - -RadioField.template = "web.RadioField"; -RadioField.props = { - ...standardFieldProps, - orientation: { type: String, optional: true }, -}; -RadioField.defaultProps = { - orientation: "vertical", -}; - -RadioField.displayName = _lt("Radio"); -RadioField.supportedTypes = ["many2one", "selection"]; -RadioField.legacySpecialData = "_fetchSpecialMany2ones"; - -RadioField.isEmpty = (record, fieldName) => record.data[fieldName] === false; -RadioField.extractProps = ({ attrs }) => { - return { +export const radioField = { + component: RadioField, + displayName: _lt("Radio"), + supportedTypes: ["many2one", "selection"], + isEmpty: (record, fieldName) => record.data[fieldName] === false, + extractProps: ({ attrs }) => ({ orientation: attrs.options.horizontal ? "horizontal" : "vertical", - }; + }), + legacySpecialData: "_fetchSpecialMany2ones", }; -RadioField.getItems = (fieldName, record) => { - switch (record.fields[fieldName].type) { - case "selection": - return record.fields[fieldName].selection; - case "many2one": { - const value = record.preloadedData[fieldName] || []; - return value.map((item) => [item.id, item.display_name]); - } - default: - return []; - } -}; - -registry.category("fields").add("radio", RadioField); +registry.category("fields").add("radio", radioField); export async function preloadRadio(orm, record, fieldName) { const field = record.fields[fieldName]; diff --git a/addons/web/static/src/views/fields/reference/reference_field.js b/addons/web/static/src/views/fields/reference/reference_field.js index 4f273212364..336e6a730d4 100644 --- a/addons/web/static/src/views/fields/reference/reference_field.js +++ b/addons/web/static/src/views/fields/reference/reference_field.js @@ -2,7 +2,7 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { Many2OneField } from "../many2one/many2one_field"; +import { many2OneField, Many2OneField } from "../many2one/many2one_field"; import { Component, onWillUpdateProps, useState } from "@odoo/owl"; @@ -11,6 +11,18 @@ function valuesEqual(a, b) { } export class ReferenceField extends Component { + static template = "web.ReferenceField"; + static components = { + Many2OneField, + }; + static props = { + ...Many2OneField.props, + hideModelSelector: { type: Boolean, optional: true }, + }; + static defaultProps = { + ...Many2OneField.defaultProps, + }; + setup() { this.state = useState({ resModel: this.relation, @@ -107,36 +119,24 @@ export class ReferenceField extends Component { } } -ReferenceField.template = "web.ReferenceField"; -ReferenceField.components = { - Many2OneField, -}; -ReferenceField.props = { - ...Many2OneField.props, - hideModelSelector: { type: Boolean, optional: true }, -}; -ReferenceField.defaultProps = { - ...Many2OneField.defaultProps, +export const referenceField = { + component: ReferenceField, + displayName: _lt("Reference"), + supportedTypes: ["reference", "char"], + legacySpecialData: "_fetchSpecialReference", + extractProps: (params) => ({ + /* + 1 - + 2 - + 3 - + 4 - + + We want to display the model selector only in the 4th case. + */ + ...many2OneField.extractProps(params), + hideModelSelector: + !!params.attrs.options["hide_model"] || !!params.attrs.options["model_field"], + }), }; -ReferenceField.displayName = _lt("Reference"); -ReferenceField.supportedTypes = ["reference", "char"]; -ReferenceField.legacySpecialData = "_fetchSpecialReference"; - -ReferenceField.extractProps = ({ attrs, field }) => { - /* - 1 - - 2 - - 3 - - 4 - - - We want to display the model selector only in the 4th case. - */ - const displayModelSelector = !attrs.options["hide_model"] && !attrs.options["model_field"]; - return { - ...Many2OneField.extractProps({ attrs, field }), - hideModelSelector: !displayModelSelector, - }; -}; - -registry.category("fields").add("reference", ReferenceField); +registry.category("fields").add("reference", referenceField); diff --git a/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js b/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js index 01d851abc35..c3f83258216 100644 --- a/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js +++ b/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js @@ -10,6 +10,11 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class RemainingDaysField extends Component { + static template = "web.RemainingDaysField"; + static props = { + ...standardFieldProps, + }; + get hasTime() { return this.props.type === "datetime"; } @@ -42,12 +47,10 @@ export class RemainingDaysField extends Component { } } -RemainingDaysField.template = "web.RemainingDaysField"; -RemainingDaysField.props = { - ...standardFieldProps, +export const remainingDaysField = { + component: RemainingDaysField, + displayName: _lt("Remaining Days"), + supportedTypes: ["date", "datetime"], }; -RemainingDaysField.displayName = _lt("Remaining Days"); -RemainingDaysField.supportedTypes = ["date", "datetime"]; - -registry.category("fields").add("remaining_days", RemainingDaysField); +registry.category("fields").add("remaining_days", remainingDaysField); diff --git a/addons/web/static/src/views/fields/selection/selection_field.js b/addons/web/static/src/views/fields/selection/selection_field.js index e13ee37ea6f..911bc7813cf 100644 --- a/addons/web/static/src/views/fields/selection/selection_field.js +++ b/addons/web/static/src/views/fields/selection/selection_field.js @@ -7,6 +7,12 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class SelectionField extends Component { + static template = "web.SelectionField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + }; + get options() { switch (this.props.record.fields[this.props.name].type) { case "many2one": @@ -63,24 +69,18 @@ export class SelectionField extends Component { } } -SelectionField.template = "web.SelectionField"; -SelectionField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, -}; - -SelectionField.displayName = _lt("Selection"); -SelectionField.supportedTypes = ["many2one", "selection"]; -SelectionField.legacySpecialData = "_fetchSpecialRelation"; - -SelectionField.isEmpty = (record, fieldName) => record.data[fieldName] === false; -SelectionField.extractProps = ({ attrs }) => { - return { +export const selectionField = { + component: SelectionField, + displayName: _lt("Selection"), + supportedTypes: ["many2one", "selection"], + legacySpecialData: "_fetchSpecialRelation", + isEmpty: (record, fieldName) => record.data[fieldName] === false, + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder, - }; + }), }; -registry.category("fields").add("selection", SelectionField); +registry.category("fields").add("selection", selectionField); export function preloadSelection(orm, record, fieldName) { const field = record.fields[fieldName]; diff --git a/addons/web/static/src/views/fields/signature/signature_field.js b/addons/web/static/src/views/fields/signature/signature_field.js index 49b8322e3f2..7740a74658b 100644 --- a/addons/web/static/src/views/fields/signature/signature_field.js +++ b/addons/web/static/src/views/fields/signature/signature_field.js @@ -13,6 +13,16 @@ import { Component, onWillUpdateProps, useState } from "@odoo/owl"; const placeholder = "/web/static/img/placeholder.png"; export class SignatureField extends Component { + static template = "web.SignatureField"; + static props = { + ...standardFieldProps, + defaultFont: { type: String }, + fullName: { type: String, optional: true }, + height: { type: Number, optional: true }, + previewImage: { type: String, optional: true }, + width: { type: Number, optional: true }, + }; + setup() { this.displaySignatureRatio = 3; @@ -124,24 +134,15 @@ export class SignatureField extends Component { } } -SignatureField.template = "web.SignatureField"; -SignatureField.props = { - ...standardFieldProps, - defaultFont: { type: String }, - fullName: { type: String, optional: true }, - height: { type: Number, optional: true }, - previewImage: { type: String, optional: true }, - width: { type: Number, optional: true }, -}; -SignatureField.extractProps = ({ attrs }) => { - const { options, width, height } = attrs; - return { +export const signatureField = { + component: SignatureField, + extractProps: ({ attrs }) => ({ defaultFont: attrs.options.default_font || "", fullName: attrs.options.full_name, - height: options.size ? options.size[1] || undefined : height, + height: attrs.options.size ? attrs.options.size[1] || undefined : attrs.height, previewImage: attrs.options.preview_image, - width: options.size ? options.size[0] || undefined : width, - }; + width: attrs.options.size ? attrs.options.size[0] || undefined : attrs.width, + }), }; -registry.category("fields").add("signature", SignatureField); +registry.category("fields").add("signature", signatureField); diff --git a/addons/web/static/src/views/fields/stat_info/stat_info_field.js b/addons/web/static/src/views/fields/stat_info/stat_info_field.js index bba74cc8a03..8ce27a91e22 100644 --- a/addons/web/static/src/views/fields/stat_info/stat_info_field.js +++ b/addons/web/static/src/views/fields/stat_info/stat_info_field.js @@ -9,6 +9,14 @@ import { Component } from "@odoo/owl"; const formatters = registry.category("formatters"); export class StatInfoField extends Component { + static template = "web.StatInfoField"; + static props = { + ...standardFieldProps, + labelField: { type: String, optional: true }, + noLabel: { type: Boolean, optional: true }, + digits: { type: Array, optional: true }, + }; + get formattedValue() { const formatter = formatters.get(this.props.type); return formatter(this.props.value || 0, { digits: this.props.digits }); @@ -20,32 +28,29 @@ export class StatInfoField extends Component { } } -StatInfoField.template = "web.StatInfoField"; -StatInfoField.props = { - ...standardFieldProps, - labelField: { type: String, optional: true }, - noLabel: { type: Boolean, optional: true }, - digits: { type: Array, optional: true }, +export const statInfoField = { + component: StatInfoField, + displayName: _lt("Stat Info"), + supportedTypes: ["float", "integer", "monetary"], + isEmpty: () => false, + extractProps: ({ attrs, field }) => { + // Sadly, digits param was available as an option and an attr. + // The option version could be removed with some xml refactoring. + let digits; + if (attrs.digits) { + digits = JSON.parse(attrs.digits); + } else if (attrs.options.digits) { + digits = attrs.options.digits; + } else if (Array.isArray(field.digits)) { + digits = field.digits; + } + + return { + digits, + labelField: attrs.options.label_field, + noLabel: archParseBoolean(attrs.nolabel), + }; + }, }; -StatInfoField.displayName = _lt("Stat Info"); -StatInfoField.supportedTypes = ["float", "integer", "monetary"]; - -StatInfoField.isEmpty = () => false; -StatInfoField.extractProps = ({ attrs, field }) => { - let digits; - if (attrs.digits) { - digits = JSON.parse(attrs.digits); - } else if (attrs.options.digits) { - digits = attrs.options.digits; - } else if (Array.isArray(field.digits)) { - digits = field.digits; - } - return { - labelField: attrs.options.label_field, - noLabel: archParseBoolean(attrs.nolabel), - digits, - }; -}; - -registry.category("fields").add("statinfo", StatInfoField); +registry.category("fields").add("statinfo", statInfoField); diff --git a/addons/web/static/src/views/fields/state_selection/state_selection_field.js b/addons/web/static/src/views/fields/state_selection/state_selection_field.js index fad3364c409..0779990c73c 100644 --- a/addons/web/static/src/views/fields/state_selection/state_selection_field.js +++ b/addons/web/static/src/views/fields/state_selection/state_selection_field.js @@ -11,6 +11,19 @@ import { formatSelection } from "../formatters"; import { Component } from "@odoo/owl"; export class StateSelectionField extends Component { + static template = "web.StateSelectionField"; + static components = { + Dropdown, + DropdownItem, + }; + static props = { + ...standardFieldProps, + hideLabel: { type: Boolean, optional: true }, + }; + static defaultProps = { + hideLabel: false, + }; + setup() { this.colorPrefix = "o_status_"; this.colors = { @@ -65,27 +78,14 @@ export class StateSelectionField extends Component { } } -StateSelectionField.template = "web.StateSelectionField"; -StateSelectionField.components = { - Dropdown, - DropdownItem, -}; -StateSelectionField.props = { - ...standardFieldProps, - hideLabel: { type: Boolean, optional: true }, -}; -StateSelectionField.defaultProps = { - hideLabel: false, -}; - -StateSelectionField.displayName = _lt("Label Selection"); -StateSelectionField.supportedTypes = ["selection"]; - -StateSelectionField.extractProps = ({ attrs }) => { - return { +export const stateSelectionField = { + component: StateSelectionField, + displayName: _lt("Label Selection"), + supportedTypes: ["selection"], + extractProps: ({ attrs }) => ({ hideLabel: !!attrs.options.hide_label, - }; + }), }; -registry.category("fields").add("state_selection", StateSelectionField); -registry.category("fields").add("list.state_selection", StateSelectionField); +registry.category("fields").add("state_selection", stateSelectionField); +registry.category("fields").add("list.state_selection", stateSelectionField); diff --git a/addons/web/static/src/views/fields/statusbar/statusbar_field.js b/addons/web/static/src/views/fields/statusbar/statusbar_field.js index 83fb685ec0b..4131c280a65 100644 --- a/addons/web/static/src/views/fields/statusbar/statusbar_field.js +++ b/addons/web/static/src/views/fields/statusbar/statusbar_field.js @@ -12,6 +12,23 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class StatusBarField extends Component { + static template = "web.StatusBarField"; + static components = { + Dropdown, + DropdownItem, + }; + static props = { + ...standardFieldProps, + canCreate: { type: Boolean, optional: true }, + canWrite: { type: Boolean, optional: true }, + displayName: { type: String, optional: true }, + isDisabled: { type: Boolean, optional: true }, + visibleSelection: { type: Array, optional: true }, + }; + static defaultProps = { + visibleSelection: [], + }; + setup() { if (this.props.record.activeFields[this.props.name].viewType === "form") { const commandName = sprintf( @@ -78,7 +95,9 @@ export class StatusBarField extends Component { get currentName() { switch (this.type) { case "many2one": { - const item = this.options.find((item) => this.props.value && item.id === this.props.value[0]); + const item = this.options.find( + (item) => this.props.value && item.id === this.props.value[0] + ); return item ? item.display_name : ""; } case "selection": { @@ -188,42 +207,24 @@ export class StatusBarField extends Component { } } -StatusBarField.template = "web.StatusBarField"; -StatusBarField.defaultProps = { - visibleSelection: [], -}; -StatusBarField.props = { - ...standardFieldProps, - canCreate: { type: Boolean, optional: true }, - canWrite: { type: Boolean, optional: true }, - displayName: { type: String, optional: true }, - isDisabled: { type: Boolean, optional: true }, - visibleSelection: { type: Array, optional: true }, -}; -StatusBarField.components = { - Dropdown, - DropdownItem, -}; - -StatusBarField.displayName = _lt("Status"); -StatusBarField.supportedTypes = ["many2one", "selection"]; -StatusBarField.legacySpecialData = "_fetchSpecialStatus"; - -StatusBarField.isEmpty = (record, fieldName) => { - return record.model.env.isSmall ? !record.data[fieldName] : false; -}; -StatusBarField.extractProps = ({ attrs, field }) => { - return { +export const statusBarField = { + component: StatusBarField, + displayName: _lt("Status"), + supportedTypes: ["many2one", "selection"], + isEmpty: (record, fieldName) => record.model.env.isSmall && !record.data[fieldName], + legacySpecialData: "_fetchSpecialStatus", + extractProps: ({ attrs, field }) => ({ canCreate: Boolean(attrs.can_create), canWrite: Boolean(attrs.can_write), - displayName: field.string, isDisabled: !attrs.options.clickable, visibleSelection: attrs.statusbar_visible && attrs.statusbar_visible.trim().split(/\s*,\s*/g), - }; + + displayName: field.string, + }), }; -registry.category("fields").add("statusbar", StatusBarField); +registry.category("fields").add("statusbar", statusBarField); export async function preloadStatusBar(orm, record, fieldName) { const fieldNames = ["id", "display_name"]; diff --git a/addons/web/static/src/views/fields/text/text_field.js b/addons/web/static/src/views/fields/text/text_field.js index 33d73accfac..f43817ce93b 100644 --- a/addons/web/static/src/views/fields/text/text_field.js +++ b/addons/web/static/src/views/fields/text/text_field.js @@ -12,6 +12,22 @@ import { TranslationButton } from "../translation_button"; import { Component, onMounted, onWillUnmount, useEffect, useRef } from "@odoo/owl"; export class TextField extends Component { + static template = "web.TextField"; + static components = { + TranslationButton, + }; + static props = { + ...standardFieldProps, + isTranslatable: { type: Boolean, optional: true }, + placeholder: { type: String, optional: true }, + dynamicPlaceholder: { type: Boolean, optional: true }, + rowCount: { type: Number, optional: true }, + }; + static defaultProps = { + dynamicPlaceholder: false, + rowCount: 2, + }; + setup() { if (this.props.dynamicPlaceholder) { this.dynamicPlaceholder = useDynamicPlaceholder(); @@ -105,40 +121,27 @@ export class TextField extends Component { } } -TextField.template = "web.TextField"; -TextField.components = { - TranslationButton, -}; -TextField.defaultProps = { - dynamicPlaceholder: false, - rowCount: 2, -}; -TextField.props = { - ...standardFieldProps, - isTranslatable: { type: Boolean, optional: true }, - placeholder: { type: String, optional: true }, - dynamicPlaceholder: { type: Boolean, optional: true }, - rowCount: { type: Number, optional: true }, -}; - -TextField.displayName = _lt("Multiline Text"); -TextField.supportedTypes = ["html", "text"]; - -TextField.extractProps = ({ attrs, field }) => { - const props = { - isTranslatable: field.translate, +export const textField = { + component: TextField, + displayName: _lt("Multiline Text"), + supportedTypes: ["html", "text"], + extractProps: ({ attrs, field }) => ({ placeholder: attrs.placeholder, dynamicPlaceholder: attrs.options.dynamic_placeholder, - }; - if (attrs.rows) { - props.rowCount = parseInteger(attrs.rows); - } - return props; + rowCount: attrs.rows && parseInteger(attrs.rows), + + isTranslatable: field.translate, + }), }; -registry.category("fields").add("text", TextField); +registry.category("fields").add("text", textField); export class ListTextField extends TextField { + static defaultProps = { + ...super.defaultProps, + rowCount: 1, + }; + get minimumHeight() { return 0; } @@ -146,9 +149,10 @@ export class ListTextField extends TextField { return this.props.rowCount; } } -ListTextField.defaultProps = { - ...TextField.defaultProps, - rowCount: 1, + +export const listTextField = { + ...textField, + component: ListTextField, }; -registry.category("fields").add("list.text", ListTextField); +registry.category("fields").add("list.text", listTextField); diff --git a/addons/web/static/src/views/fields/timezone_mismatch/timezone_mismatch_field.js b/addons/web/static/src/views/fields/timezone_mismatch/timezone_mismatch_field.js index 2f8b2f2e5d6..5fcd79d2273 100644 --- a/addons/web/static/src/views/fields/timezone_mismatch/timezone_mismatch_field.js +++ b/addons/web/static/src/views/fields/timezone_mismatch/timezone_mismatch_field.js @@ -3,11 +3,25 @@ import { formatDateTime } from "@web/core/l10n/dates"; import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { SelectionField } from "../selection/selection_field"; +import { selectionField, SelectionField } from "../selection/selection_field"; const { DateTime } = luxon; export class TimezoneMismatchField extends SelectionField { + static template = "web.TimezoneMismatchField"; + static props = { + ...super.props, + tzOffsetField: { type: String, optional: true }, + mismatchTitle: { type: String, optional: true }, + }; + static defaultProps = { + ...super.defaultProps, + tzOffsetField: "tz_offset", + mismatchTitle: _lt( + "Timezone Mismatch : This timezone is different from that of your browser.\nPlease, set the same timezone as your browser's to avoid time discrepancies in your system." + ), + }; + get mismatch() { const userOffset = this.props.record.data[this.props.tzOffsetField]; if (userOffset && this.props.value) { @@ -56,26 +70,15 @@ export class TimezoneMismatchField extends SelectionField { } } -TimezoneMismatchField.template = "web.TimezoneMismatchField"; -TimezoneMismatchField.additionalClasses = ["d-flex"]; -TimezoneMismatchField.props = { - ...SelectionField.props, - tzOffsetField: { type: String, optional: true }, - mismatchTitle: { type: String, optional: true }, -}; -TimezoneMismatchField.defaultProps = { - ...SelectionField.defaultProps, - tzOffsetField: "tz_offset", - mismatchTitle: _lt( - "Timezone Mismatch : This timezone is different from that of your browser.\nPlease, set the same timezone as your browser's to avoid time discrepancies in your system." - ), -}; -TimezoneMismatchField.extractProps = ({ attrs }) => { - return { - ...SelectionField.extractProps({ attrs }), - tzOffsetField: attrs.options.tz_offset_field, - mismatchTitle: attrs.options.mismatch_title, - }; +export const timezoneMismatchField = { + ...selectionField, + component: TimezoneMismatchField, + additionalClasses: ["d-flex"], + extractProps: (params) => ({ + ...selectionField.extractProps(params), + tzOffsetField: params.attrs.options.tz_offset_field, + mismatchTitle: params.attrs.options.mismatch_title, + }), }; -registry.category("fields").add("timezone_mismatch", TimezoneMismatchField); +registry.category("fields").add("timezone_mismatch", timezoneMismatchField); diff --git a/addons/web/static/src/views/fields/url/url_field.js b/addons/web/static/src/views/fields/url/url_field.js index 5de41383054..88194ad8c94 100644 --- a/addons/web/static/src/views/fields/url/url_field.js +++ b/addons/web/static/src/views/fields/url/url_field.js @@ -8,6 +8,14 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class UrlField extends Component { + static template = "web.UrlField"; + static props = { + ...standardFieldProps, + placeholder: { type: String, optional: true }, + text: { type: String, optional: true }, + websitePath: { type: Boolean, optional: true }, + }; + setup() { useInputField({ getValue: () => this.props.value || "" }); } @@ -26,27 +34,26 @@ export class UrlField extends Component { } } -UrlField.template = "web.UrlField"; -UrlField.props = { - ...standardFieldProps, - placeholder: { type: String, optional: true }, - text: { type: String, optional: true }, - websitePath: { type: Boolean, optional: true }, -}; - -UrlField.displayName = _lt("URL"); -UrlField.supportedTypes = ["char"]; - -UrlField.extractProps = ({ attrs }) => { - return { +export const urlField = { + component: UrlField, + displayName: _lt("URL"), + supportedTypes: ["char"], + extractProps: ({ attrs }) => ({ text: attrs.text, websitePath: attrs.options.website_path, placeholder: attrs.placeholder, - }; + }), }; -class FormUrlField extends UrlField {} -FormUrlField.template = "web.FormUrlField"; +registry.category("fields").add("url", urlField); -registry.category("fields").add("url", UrlField); -registry.category("fields").add("form.url", FormUrlField); +class FormUrlField extends UrlField { + static template = "web.FormUrlField"; +} + +export const formUrlField = { + ...urlField, + component: FormUrlField, +}; + +registry.category("fields").add("form.url", formUrlField); diff --git a/addons/web/static/src/views/fields/x2many/list_x2many_field.js b/addons/web/static/src/views/fields/x2many/list_x2many_field.js index 274a98fc6d2..b993954149a 100644 --- a/addons/web/static/src/views/fields/x2many/list_x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/list_x2many_field.js @@ -7,15 +7,18 @@ import { standardFieldProps } from "../standard_field_props"; import { Component } from "@odoo/owl"; export class ListX2ManyField extends Component { + static template = "web.ListX2ManyField"; + static props = { ...standardFieldProps }; + get formattedValue() { return formatX2many(this.props.value); } } -ListX2ManyField.template = "web.ListX2ManyField"; -ListX2ManyField.props = { ...standardFieldProps }; +export const listX2ManyField = { + component: ListX2ManyField, + useSubView: false, +}; -ListX2ManyField.useSubView = false; - -registry.category("fields").add("list.one2many", ListX2ManyField); -registry.category("fields").add("list.many2many", ListX2ManyField); +registry.category("fields").add("list.one2many", listX2ManyField); +registry.category("fields").add("list.many2many", listX2ManyField); diff --git a/addons/web/static/src/views/fields/x2many/x2many_field.js b/addons/web/static/src/views/fields/x2many/x2many_field.js index ae4a0d07f95..94525578ad2 100644 --- a/addons/web/static/src/views/fields/x2many/x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/x2many_field.js @@ -21,6 +21,14 @@ import { ViewButton } from "@web/views/view_button/view_button"; import { Component } from "@odoo/owl"; export class X2ManyField extends Component { + static template = "web.X2ManyField"; + static components = { Pager, KanbanRenderer, ListRenderer, ViewButton }; + static props = { + ...standardFieldProps, + addLabel: { type: String, optional: true }, + editable: { type: String, optional: true }, + }; + setup() { this.activeField = this.props.record.activeFields[this.props.name]; this.field = this.props.record.fields[this.props.name]; @@ -244,21 +252,16 @@ export class X2ManyField extends Component { } } } -X2ManyField.components = { Pager, KanbanRenderer, ListRenderer, ViewButton }; -X2ManyField.props = { - ...standardFieldProps, - addLabel: { type: String, optional: true }, - editable: { type: String, optional: true }, -}; -X2ManyField.supportedTypes = ["one2many", "many2many"]; -X2ManyField.displayName = _lt("Relational table"); -X2ManyField.template = "web.X2ManyField"; -X2ManyField.useSubView = true; -X2ManyField.extractProps = ({ attrs }) => { - return { + +export const x2ManyField = { + component: X2ManyField, + displayName: _lt("Relational table"), + supportedTypes: ["one2many", "many2many"], + useSubView: true, + extractProps: ({ attrs }) => ({ addLabel: attrs["add-label"], - }; + }), }; -registry.category("fields").add("one2many", X2ManyField); -registry.category("fields").add("many2many", X2ManyField); +registry.category("fields").add("one2many", x2ManyField); +registry.category("fields").add("many2many", x2ManyField); diff --git a/addons/web/static/src/views/form/form_arch_parser.js b/addons/web/static/src/views/form/form_arch_parser.js index 002d439d3bc..81779e07a76 100644 --- a/addons/web/static/src/views/form/form_arch_parser.js +++ b/addons/web/static/src/views/form/form_arch_parser.js @@ -32,7 +32,7 @@ export class FormArchParser extends XMLParser { addFieldDependencies( activeFields, models[modelName], - fieldInfo.FieldComponent.fieldDependencies + fieldInfo.field.fieldDependencies ); return false; } else if (node.tagName === "div" && node.classList.contains("oe_chatter")) { diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index d0397f1db47..211b04ab97e 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -42,7 +42,7 @@ export async function loadSubViews( continue; // no need to fetch the sub view if the field is always invisible } - if (!fieldInfo.FieldComponent.useSubView) { + if (!fieldInfo.field.useSubView) { continue; // the FieldComponent used to render the field doesn't need a sub view } diff --git a/addons/web/static/src/views/form/form_label.js b/addons/web/static/src/views/form/form_label.js index a3025f435b3..f8e8d6b92ba 100644 --- a/addons/web/static/src/views/form/form_label.js +++ b/addons/web/static/src/views/form/form_label.js @@ -9,7 +9,7 @@ import { Component } from "@odoo/owl"; export class FormLabel extends Component { get className() { const { invalid, empty, readonly } = fieldVisualFeedback( - this.props.fieldInfo.FieldComponent, + this.props.fieldInfo.field, this.props.record, this.props.fieldName, this.props.fieldInfo diff --git a/addons/web/static/src/views/kanban/kanban_arch_parser.js b/addons/web/static/src/views/kanban/kanban_arch_parser.js index b3b16ced60b..42d0e849358 100644 --- a/addons/web/static/src/views/kanban/kanban_arch_parser.js +++ b/addons/web/static/src/views/kanban/kanban_arch_parser.js @@ -120,7 +120,7 @@ export class KanbanArchParser extends XMLParser { addFieldDependencies( activeFields, models[modelName], - fieldInfo.FieldComponent.fieldDependencies + fieldInfo.field.fieldDependencies ); } if (node.tagName === "widget") { diff --git a/addons/web/static/src/views/legacy_utils.js b/addons/web/static/src/views/legacy_utils.js index 3eb8bfa48e1..27c69d45586 100644 --- a/addons/web/static/src/views/legacy_utils.js +++ b/addons/web/static/src/views/legacy_utils.js @@ -73,12 +73,14 @@ export function mapActiveFieldsToFieldsInfo(activeFields, fields, viewType, env) fieldsInfo[viewType] = {}; for (const [fieldName, fieldDescr] of Object.entries(activeFields)) { const views = mapViews(fieldDescr.views, env); - const field = fields[fieldName]; let Widget; if (fieldDescr.widget) { Widget = fieldRegistry.getAny([`${viewType}.${fieldDescr.widget}`, fieldDescr.widget]); } else { - Widget = fieldRegistry.getAny([`${viewType}.${field.type}`, field.type]); + Widget = fieldRegistry.getAny([ + `${viewType}.${fields[fieldName].type}`, + fields[fieldName].type, + ]); } Widget = Widget || fieldRegistry.get("abstract"); let domain; @@ -89,10 +91,9 @@ export function mapActiveFieldsToFieldsInfo(activeFields, fields, viewType, env) if (mode && mode.split(",").length !== 1) { mode = env.isSmall ? "kanban" : "list"; } - const FieldComponent = fieldDescr.FieldComponent; const fieldInfo = { Widget, // remove this when we no longer support legacy fields inside wowl views - specialData: FieldComponent && FieldComponent.legacySpecialData, + specialData: fieldDescr.field && fieldDescr.field.legacySpecialData, domain, context: fieldDescr.context, fieldDependencies: {}, // ?? @@ -106,16 +107,16 @@ export function mapActiveFieldsToFieldsInfo(activeFields, fields, viewType, env) __WOWL_FIELD_DESCR__: fieldDescr, }; - if (FieldComponent && FieldComponent.limit) { - fieldInfo.limit = FieldComponent.limit; + if (fieldDescr.field && fieldDescr.field.limit) { + fieldInfo.limit = fieldDescr.field.limit; } if (fieldDescr.modifiers && fieldDescr.modifiers.invisible === true) { fieldInfo.__no_fetch = true; } - if (!fieldInfo.__no_fetch && FieldComponent && FieldComponent.fieldsToFetch) { - let fieldsToFetch = FieldComponent.fieldsToFetch; + if (!fieldInfo.__no_fetch && fieldDescr.field && fieldDescr.field.fieldsToFetch) { + let fieldsToFetch = fieldDescr.field.fieldsToFetch; if (fieldsToFetch instanceof Function) { fieldsToFetch = fieldsToFetch(fieldInfo.__WOWL_FIELD_DESCR__); } diff --git a/addons/web/static/src/views/list/list_arch_parser.js b/addons/web/static/src/views/list/list_arch_parser.js index 9d19061ffba..7d9fbf6088f 100644 --- a/addons/web/static/src/views/list/list_arch_parser.js +++ b/addons/web/static/src/views/list/list_arch_parser.js @@ -106,17 +106,17 @@ export class ListArchParser extends XMLParser { addFieldDependencies( activeFields, models[modelName], - fieldInfo.FieldComponent.fieldDependencies + fieldInfo.field.fieldDependencies ); if (this.isColumnVisible(fieldInfo.modifiers.column_invisible)) { - const label = fieldInfo.FieldComponent.label; + const label = fieldInfo.field.label; columns.push({ ...fieldInfo, id: `column_${nextId++}`, className: node.getAttribute("class"), // for oe_edit_only and oe_read_only optional: node.getAttribute("optional") || false, type: "field", - hasLabel: !(fieldInfo.noLabel || fieldInfo.FieldComponent.noLabel), + hasLabel: !(fieldInfo.noLabel || fieldInfo.field.noLabel), label: (fieldInfo.widget && label && label.toString()) || fieldInfo.string, }); } @@ -210,7 +210,9 @@ export class ListArchParser extends XMLParser { treeAttr.decorations = getDecoration(xmlDoc); treeAttr.defaultGroupBy = xmlDoc.getAttribute("default_group_by"); - treeAttr.defaultOrder = stringToOrderBy(xmlDoc.getAttribute("default_order") || null); + treeAttr.defaultOrder = stringToOrderBy( + xmlDoc.getAttribute("default_order") || null + ); // custom open action when clicking on record row const action = xmlDoc.getAttribute("action"); diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index 001160e5ad6..3eba5582b62 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -604,8 +604,7 @@ export class Record extends DataPoint { continue; } - const isSet = - activeField && activeField.FieldComponent && activeField.FieldComponent.isSet; + const isSet = activeField && activeField.field && activeField.field.isSet; if (this.isRequired(fieldName) && isSet && !isSet(this.data[fieldName])) { this.setInvalidField(fieldName); diff --git a/addons/web/static/src/webclient/settings_form_view/fields/upgrade_boolean_field.js b/addons/web/static/src/webclient/settings_form_view/fields/upgrade_boolean_field.js index 385b6cd1c0b..ac3ade9fb70 100644 --- a/addons/web/static/src/webclient/settings_form_view/fields/upgrade_boolean_field.js +++ b/addons/web/static/src/webclient/settings_form_view/fields/upgrade_boolean_field.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { BooleanField } from "@web/views/fields/boolean/boolean_field"; +import { booleanField, BooleanField } from "@web/views/fields/boolean/boolean_field"; import { useService } from "@web/core/utils/hooks"; import { UpgradeDialog } from "./upgrade_dialog"; @@ -33,10 +33,12 @@ export class UpgradeBooleanField extends BooleanField { } } } -UpgradeBooleanField.isUpgradeField = true; -UpgradeBooleanField.additionalClasses = [ - ...UpgradeBooleanField.additionalClasses || [], - "o_field_boolean", -]; -registry.category("fields").add("upgrade_boolean", UpgradeBooleanField); +export const upgradeBooleanField = { + ...booleanField, + component: UpgradeBooleanField, + isUpgradeField: true, + additionalClasses: [...(booleanField.additionalClasses || []), "o_field_boolean"], +}; + +registry.category("fields").add("upgrade_boolean", upgradeBooleanField); diff --git a/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js b/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js index 49863a59849..8dcb737e9ef 100644 --- a/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js +++ b/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js @@ -8,8 +8,8 @@ export class FormLabelHighlightText extends FormLabel { const isEnterprise = odoo.info && odoo.info.isEnterprise; if ( this.props.fieldInfo && - this.props.fieldInfo.FieldComponent && - this.props.fieldInfo.FieldComponent.isUpgradeField && + this.props.fieldInfo.field && + this.props.fieldInfo.field.isUpgradeField && !isEnterprise ) { this.upgradeEnterprise = true; diff --git a/addons/web/static/src/webclient/settings_form_view/highlight_text/settings_radio_field.js b/addons/web/static/src/webclient/settings_form_view/highlight_text/settings_radio_field.js index 5741c2132cc..17aaf665d52 100644 --- a/addons/web/static/src/webclient/settings_form_view/highlight_text/settings_radio_field.js +++ b/addons/web/static/src/webclient/settings_form_view/highlight_text/settings_radio_field.js @@ -1,16 +1,21 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { RadioField } from "@web/views/fields/radio/radio_field"; +import { radioField, RadioField } from "@web/views/fields/radio/radio_field"; import { HighlightText } from "./highlight_text"; -export class SettingsRadioField extends RadioField {} +export class SettingsRadioField extends RadioField { + static template = "web.SettingsRadioField"; + static components = { ...super.components, HighlightText }; +} -SettingsRadioField.extractStringExpr = (fieldName, record) => { - const radioItems = SettingsRadioField.getItems(fieldName, record); - return radioItems.map((r) => r[1]); +export const settingsRadioField = { + ...radioField, + component: SettingsRadioField, + extractStringExpr(fieldName, record) { + const radioItems = SettingsRadioField.getItems(fieldName, record); + return radioItems.map((r) => r[1]); + }, }; -SettingsRadioField.template = "web.SettingsRadioField"; -SettingsRadioField.components = { ...RadioField.components, HighlightText }; -registry.category("fields").add("base_settings.radio", SettingsRadioField); +registry.category("fields").add("base_settings.radio", settingsRadioField); diff --git a/addons/web/static/tests/views/calendar/calendar_view_tests.js b/addons/web/static/tests/views/calendar/calendar_view_tests.js index b4d24006a3d..243320a993c 100644 --- a/addons/web/static/tests/views/calendar/calendar_view_tests.js +++ b/addons/web/static/tests/views/calendar/calendar_view_tests.js @@ -1421,7 +1421,7 @@ QUnit.module("Views", ({ beforeEach }) => { QUnit.test(`render popover with widget which has specialData attribute`, async (assert) => { assert.expect(3); - fieldRegistry.add("specialWidget", CharField); + fieldRegistry.add("specialWidget", { component: CharField }); preloadedDataRegistry.add("specialWidget", { loadOnTypes: ["char"], preload: () => { @@ -4484,7 +4484,7 @@ QUnit.module("Views", ({ beforeEach }) => { } } DeferredWidget.template = owl.xml``; - fieldRegistry.add("deferred_widget", DeferredWidget); + fieldRegistry.add("deferred_widget", { component: DeferredWidget }); registerCleanup(() => fieldRegistry.remove("deferred_widget")); await makeView({ diff --git a/addons/web/static/tests/views/fields/html_field_tests.js b/addons/web/static/tests/views/fields/html_field_tests.js index e8e6f19a1fc..2170f9633c3 100644 --- a/addons/web/static/tests/views/fields/html_field_tests.js +++ b/addons/web/static/tests/views/fields/html_field_tests.js @@ -3,7 +3,7 @@ import { click, editInput, getFixture, patchWithCleanup } from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; -import { HtmlField } from "@web/views/fields/html/html_field"; +import { htmlField } from "@web/views/fields/html/html_field"; import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; import { session } from "@web/session"; @@ -32,7 +32,7 @@ QUnit.module("Fields", ({ beforeEach }) => { setupViewRegistries(); // Explicitly removed by web_editor, we need to add it back - registry.category("fields").add("html", HtmlField, { force: true }); + registry.category("fields").add("html", htmlField, { force: true }); }); QUnit.module("HtmlField"); diff --git a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js index 44da436a973..a5cd81985c4 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js @@ -2,7 +2,7 @@ import { AutoComplete } from "@web/core/autocomplete/autocomplete"; import { browser } from "@web/core/browser/browser"; -import { Many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; +import { many2ManyTagsFieldColorEditable } from "@web/views/fields/many2many_tags/many2many_tags_field"; import { click, clickDiscard, @@ -683,7 +683,7 @@ QUnit.module("Fields", (hooks) => { QUnit.test( "Many2ManyTagsField loads records according to limit defined on widget prototype", async function (assert) { - patchWithCleanup(Many2ManyTagsField, { + patchWithCleanup(many2ManyTagsFieldColorEditable, { limit: 30, }); @@ -1616,8 +1616,7 @@ QUnit.module("Fields", (hooks) => { type: "form", resModel: "partner", serverData, - arch: - '
', + arch: '
', }); assert.strictEqual( diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js index 80246fa777a..534fdbe2ce2 100644 --- a/addons/web/static/tests/views/fields/one2many_field_tests.js +++ b/addons/web/static/tests/views/fields/one2many_field_tests.js @@ -4450,8 +4450,8 @@ QUnit.module("Fields", (hooks) => { QUnit.test("one2many with CREATE onchanges correctly refreshed", async function (assert) { let delta = 0; const fieldRegistry = registry.category("fields"); - for (const [name, Field] of fieldRegistry.getEntries()) { - class DeltaField extends Field { + for (const [name, field] of fieldRegistry.getEntries()) { + class DeltaField extends field.component { setup() { super.setup(); owl.onWillStart(() => { @@ -4462,7 +4462,7 @@ QUnit.module("Fields", (hooks) => { }); } } - fieldRegistry.add(name, DeltaField, { force: true }); + fieldRegistry.add(name, { ...field, component: DeltaField }, { force: true }); } let deactiveOnchange = true; diff --git a/addons/web/static/tests/views/form/form_compiler_tests.js b/addons/web/static/tests/views/form/form_compiler_tests.js index d0e467b26e3..5fe3b332746 100644 --- a/addons/web/static/tests/views/form/form_compiler_tests.js +++ b/addons/web/static/tests/views/form/form_compiler_tests.js @@ -417,15 +417,19 @@ QUnit.module("Form Renderer", (hooks) => { QUnit.test("render field with placeholder", async (assert) => { assert.expect(1); - class CharField extends owl.Component { - setup() { - assert.strictEqual(this.props.placeholder, "e.g. Contact's Name or //someinfo..."); - } - } - CharField.template = owl.xml`
`; - CharField.extractProps = ({ attrs }) => ({ placeholder: attrs.placeholder }); - - registry.category("fields").add("char", CharField, { force: true }); + const charField = { + component: class CharField extends owl.Component { + static template = owl.xml`
`; + setup() { + assert.strictEqual( + this.props.placeholder, + "e.g. Contact's Name or //someinfo..." + ); + } + }, + extractProps: ({ attrs }) => ({ placeholder: attrs.placeholder }), + }; + registry.category("fields").add("char", charField, { force: true }); serverData.views = { "partner,1,form": /*xml*/ ` diff --git a/addons/web/static/tests/views/form/form_view_tests.js b/addons/web/static/tests/views/form/form_view_tests.js index 4fb480c09f8..02c55c6b902 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -585,7 +585,7 @@ QUnit.module("Views", (hooks) => { return def; } } - fieldRegistry.add("asyncwidget", AsyncField); + fieldRegistry.add("asyncwidget", { component: AsyncField }); const viewProm = makeView({ type: "form", @@ -691,10 +691,10 @@ QUnit.module("Views", (hooks) => { // (int_field) in our case, isn't in the form view. This test ensures that we can open // the form view in this situation. class MyField extends CharField {} - MyField.fieldDependencies = { - int_field: { type: "integer" }, - }; - fieldRegistry.add("my_widget", MyField); + fieldRegistry.add("my_widget", { + component: MyField, + fieldDependencies: { int_field: { type: "integer" } }, + }); serverData.models.partner.records[1].p = [1]; await makeView({ type: "form", @@ -3135,7 +3135,7 @@ QUnit.module("Views", (hooks) => { }); } } - fieldRegistry.add("asyncwidget", AsyncField); + fieldRegistry.add("asyncwidget", { component: AsyncField }); await makeView({ type: "form", @@ -3563,12 +3563,14 @@ QUnit.module("Views", (hooks) => { assert.strictEqual(this.props.horizontal, true); } } - MyField.extractProps = function ({ attrs }) { - assert.deepEqual(attrs.options, { horizontal: true }); - return { horizontal: attrs.options.horizontal }; - }; MyField.template = owl.xml`
ok
`; - fieldRegistry.add("my_field", MyField); + fieldRegistry.add("my_field", { + component: MyField, + extractProps: function ({ attrs }) { + assert.deepEqual(attrs.options, { horizontal: true }); + return { horizontal: attrs.options.horizontal }; + }, + }); await makeView({ type: "form", @@ -12565,12 +12567,15 @@ QUnit.module("Views", (hooks) => { QUnit.test("fieldDependencies support for fields", async (assert) => { serverData.models.partner.records = [{ id: 1, int_field: 2 }]; - class CustomField extends Component {} - CustomField.fieldDependencies = { - int_field: { type: "integer" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + int_field: { type: "integer" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ type: "form", @@ -12592,12 +12597,15 @@ QUnit.module("Views", (hooks) => { async (assert) => { serverData.models.partner.records[0].product_id = 37; - class CustomField extends Component {} - CustomField.fieldDependencies = { - product_id: { type: "many2one", relation: "product" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + product_id: { type: "many2one", relation: "product" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ type: "form", diff --git a/addons/web/static/tests/views/kanban_view_tests.js b/addons/web/static/tests/views/kanban_view_tests.js index 44bd827e7bd..ac8db5e46dc 100644 --- a/addons/web/static/tests/views/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban_view_tests.js @@ -855,24 +855,26 @@ QUnit.module("Views", (hooks) => { QUnit.test("field with widget and attributes in kanban", async (assert) => { assert.expect(1); - class MyField extends Component { - setup() { - if (this.props.record.resId === 1) { - assert.deepEqual(this.props.attrs, { - name: "int_field", - widget: "my_field", - str: "some string", - bool: "true", - num: "4.5", - options: {}, - field_id: "int_field", - }); + const myField = { + component: class MyField extends Component { + static template = xml``; + setup() { + if (this.props.record.resId === 1) { + assert.deepEqual(this.props.attrs, { + name: "int_field", + widget: "my_field", + str: "some string", + bool: "true", + num: "4.5", + options: {}, + field_id: "int_field", + }); + } } - } - } - MyField.template = xml``; - MyField.extractProps = ({ attrs }) => ({ attrs }); - registry.category("fields").add("my_field", MyField); + }, + extractProps: ({ attrs }) => ({ attrs }), + }; + registry.category("fields").add("my_field", myField); await makeView({ type: "kanban", @@ -897,14 +899,17 @@ QUnit.module("Views", (hooks) => { }); QUnit.test("field with widget and dynamic attributes in kanban", async (assert) => { - class MyField extends Component {} - MyField.template = xml``; - MyField.extractProps = ({ attrs }) => { - assert.step( - `${attrs["dyn-bool"]}/${attrs["interp-str"]}/${attrs["interp-str2"]}/${attrs["interp-str3"]}` - ); + const myField = { + component: class MyField extends Component { + static template = xml``; + }, + extractProps: ({ attrs }) => { + assert.step( + `${attrs["dyn-bool"]}/${attrs["interp-str"]}/${attrs["interp-str2"]}/${attrs["interp-str3"]}` + ); + }, }; - registry.category("fields").add("my_field", MyField); + registry.category("fields").add("my_field", myField); await makeView({ type: "kanban", @@ -11875,12 +11880,15 @@ QUnit.module("Views", (hooks) => { }); QUnit.test("fieldDependencies support for fields", async (assert) => { - class CustomField extends Component {} - CustomField.fieldDependencies = { - int_field: { type: "integer" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + int_field: { type: "integer" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ resModel: "partner", @@ -11905,12 +11913,15 @@ QUnit.module("Views", (hooks) => { QUnit.test( "fieldDependencies support for fields: dependence on a relational field", async (assert) => { - class CustomField extends Component {} - CustomField.fieldDependencies = { - product_id: { type: "many2one", relation: "product" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + product_id: { type: "many2one", relation: "product" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ resModel: "partner", diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index c70625c2196..809eabf59ed 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -10,7 +10,7 @@ import { uiService } from "@web/core/ui/ui_service"; import { getNextTabableElement } from "@web/core/utils/ui"; import { session } from "@web/session"; import { FloatField } from "@web/views/fields/float/float_field"; -import { TextField } from "@web/views/fields/text/text_field"; +import { textField } from "@web/views/fields/text/text_field"; import { ListController } from "@web/views/list/list_controller"; import { DynamicRecordList, DynamicGroupList } from "@web/views/relational_model"; import { actionService } from "@web/webclient/actions/action_service"; @@ -1123,15 +1123,21 @@ QUnit.module("Views", (hooks) => { QUnit.test("column names (noLabel, label, string and default)", async function (assert) { const fieldRegistry = registry.category("fields"); - const CharField = fieldRegistry.get("char"); + const charField = fieldRegistry.get("char"); - class NoLabelCharField extends CharField {} - NoLabelCharField.noLabel = true; - fieldRegistry.add("nolabel_char", NoLabelCharField); + class NoLabelCharField extends charField.component {} + fieldRegistry.add("nolabel_char", { + ...charField, + component: NoLabelCharField, + noLabel: true, + }); - class LabelCharField extends CharField {} - LabelCharField.label = "Some static label"; - fieldRegistry.add("label_char", LabelCharField); + class LabelCharField extends charField.component {} + fieldRegistry.add("label_char", { + ...charField, + component: LabelCharField, + label: "Some static label", + }); await makeView({ type: "list", @@ -11711,7 +11717,7 @@ QUnit.module("Views", (hooks) => { "editable form alongside html field: click out to unselect the row", async function (assert) { // FIXME WOWL hack: add back the text field as html field removed by web_editor html_field file - registry.category("fields").add("html", TextField, { force: true }); + registry.category("fields").add("html", textField, { force: true }); await makeView({ type: "form", @@ -14732,9 +14738,9 @@ QUnit.module("Views", (hooks) => { const def = makeDeferred(); const fieldRegistry = registry.category("fields"); - const CharField = fieldRegistry.get("char"); + const charField = fieldRegistry.get("char"); - class AsyncCharField extends CharField { + class AsyncCharField extends charField.component { setup() { super.setup(); onWillStart(async () => { @@ -14743,7 +14749,7 @@ QUnit.module("Views", (hooks) => { }); } } - fieldRegistry.add("asyncwidget", AsyncCharField); + fieldRegistry.add("asyncwidget", { component: AsyncCharField }); await makeView({ type: "list", @@ -15916,7 +15922,7 @@ QUnit.module("Views", (hooks) => { "selecting a row after another one containing a table within an html field should be the correct one", async function (assert) { // FIXME WOWL hack: add back the text field as html field removed by web_editor html_field file - registry.category("fields").add("html", TextField, { force: true }); + registry.category("fields").add("html", textField, { force: true }); serverData.models.foo.fields.html = { string: "HTML field", type: "html" }; serverData.models.foo.records[0].html = ` @@ -16210,12 +16216,15 @@ QUnit.module("Views", (hooks) => { QUnit.test("fieldDependencies support for fields", async (assert) => { serverData.models.foo.records = [{ id: 1, int_field: 2 }]; - class CustomField extends Component {} - CustomField.fieldDependencies = { - int_field: { type: "integer" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + int_field: { type: "integer" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ resModel: "foo", @@ -16234,12 +16243,15 @@ QUnit.module("Views", (hooks) => { QUnit.test( "fieldDependencies support for fields: dependence on a relational field", async (assert) => { - class CustomField extends Component {} - CustomField.fieldDependencies = { - m2o: { type: "many2one", relation: "bar" }, + const customField = { + component: class CustomField extends Component { + static template = xml``; + }, + fieldDependencies: { + m2o: { type: "many2one", relation: "bar" }, + }, }; - CustomField.template = xml``; - registry.category("fields").add("custom_field", CustomField); + registry.category("fields").add("custom_field", customField); await makeView({ resModel: "foo", diff --git a/addons/web_editor/static/src/js/backend/html_field.js b/addons/web_editor/static/src/js/backend/html_field.js index ca3d5668611..e5dfc960685 100644 --- a/addons/web_editor/static/src/js/backend/html_field.js +++ b/addons/web_editor/static/src/js/backend/html_field.js @@ -616,62 +616,64 @@ HtmlField.props = { wysiwygOptions: { type: Object }, }; -HtmlField.displayName = _lt("Html"); -HtmlField.supportedTypes = ["html"]; +export const htmlField = { + component: HtmlField, + displayName: _lt("Html"), + supportedTypes: ["html"], + extractProps: ({ attrs, field }) => { + const wysiwygOptions = { + placeholder: attrs.placeholder, + noAttachment: attrs.options['no-attachment'], + inIframe: Boolean(attrs.options.cssEdit), + iframeCssAssets: attrs.options.cssEdit, + iframeHtmlClass: attrs.iframeHtmlClass, + snippets: attrs.options.snippets, + mediaModalParams: { + noVideos: 'noVideos' in attrs.options ? attrs.options.noVideos : true, + useMediaLibrary: true, + }, + linkForceNewWindow: true, + tabsize: 0, + height: attrs.options.height, + minHeight: attrs.options.minHeight, + maxHeight: attrs.options.maxHeight, + resizable: 'resizable' in attrs.options ? attrs.options.resizable : false, + editorPlugins: [QWebPlugin], + }; + if ('collaborative' in attrs.options) { + wysiwygOptions.collaborative = attrs.options.collaborative; + } + if ('allowCommandImage' in attrs.options) { + // Set the option only if it is explicitly set in the view so a default + // can be set elsewhere otherwise. + wysiwygOptions.allowCommandImage = Boolean(attrs.options.allowCommandImage); + } + if (field.sanitize_tags || (field.sanitize_tags === undefined && field.sanitize)) { + wysiwygOptions.allowCommandVideo = false; // Tag-sanitized fields remove videos. + } else if ('allowCommandVideo' in attrs.options) { + // Set the option only if it is explicitly set in the view so a default + // can be set elsewhere otherwise. + wysiwygOptions.allowCommandVideo = Boolean(attrs.options.allowCommandVideo); + } + return { + isTranslatable: field.translate, + fieldName: field.name, + codeview: Boolean(odoo.debug && attrs.options.codeview), + placeholder: attrs.placeholder, -HtmlField.extractProps = ({ attrs, field }) => { - const wysiwygOptions = { - placeholder: attrs.placeholder, - noAttachment: attrs.options['no-attachment'], - inIframe: Boolean(attrs.options.cssEdit), - iframeCssAssets: attrs.options.cssEdit, - iframeHtmlClass: attrs.iframeHtmlClass, - snippets: attrs.options.snippets, - mediaModalParams: { - noVideos: 'noVideos' in attrs.options ? attrs.options.noVideos : true, - useMediaLibrary: true, - }, - linkForceNewWindow: true, - tabsize: 0, - height: attrs.options.height, - minHeight: attrs.options.minHeight, - maxHeight: attrs.options.maxHeight, - resizable: 'resizable' in attrs.options ? attrs.options.resizable : false, - editorPlugins: [QWebPlugin], - }; - if ('collaborative' in attrs.options) { - wysiwygOptions.collaborative = attrs.options.collaborative; - } - if ('allowCommandImage' in attrs.options) { - // Set the option only if it is explicitly set in the view so a default - // can be set elsewhere otherwise. - wysiwygOptions.allowCommandImage = Boolean(attrs.options.allowCommandImage); - } - if (field.sanitize_tags || (field.sanitize_tags === undefined && field.sanitize)) { - wysiwygOptions.allowCommandVideo = false; // Tag-sanitized fields remove videos. - } else if ('allowCommandVideo' in attrs.options) { - // Set the option only if it is explicitly set in the view so a default - // can be set elsewhere otherwise. - wysiwygOptions.allowCommandVideo = Boolean(attrs.options.allowCommandVideo); - } - return { - isTranslatable: field.translate, - fieldName: field.name, - codeview: Boolean(odoo.debug && attrs.options.codeview), - placeholder: attrs.placeholder, + isCollaborative: attrs.options.collaborative, + cssReadonlyAssetId: attrs.options.cssReadonly, + dynamicPlaceholder: attrs.options.dynamic_placeholder, + cssEditAssetId: attrs.options.cssEdit, + isInlineStyle: attrs.options['style-inline'], + wrapper: attrs.options.wrapper, - isCollaborative: attrs.options.collaborative, - cssReadonlyAssetId: attrs.options.cssReadonly, - dynamicPlaceholder: attrs.options.dynamic_placeholder, - cssEditAssetId: attrs.options.cssEdit, - isInlineStyle: attrs.options['style-inline'], - wrapper: attrs.options.wrapper, - - wysiwygOptions, - }; + wysiwygOptions, + }; + }, }; -registry.category("fields").add("html", HtmlField, { force: true }); +registry.category("fields").add("html", htmlField, { force: true }); function stripHistoryIds(value) { return value && value.replace(/\sdata-last-history-steps="[^"]*?"/, '') || value; diff --git a/addons/web_kanban_gauge/static/src/gauge_field.js b/addons/web_kanban_gauge/static/src/gauge_field.js index 429a3cb37d4..c3eba165bdf 100644 --- a/addons/web_kanban_gauge/static/src/gauge_field.js +++ b/addons/web_kanban_gauge/static/src/gauge_field.js @@ -88,11 +88,13 @@ GaugeField.props = { title: { type: String }, }; -GaugeField.extractProps = ({ attrs, field }) => { - return { +export const gaugeField = { + component: GaugeField, + extractProps: ({ attrs, field }) => ({ maxValueField: attrs.options.max_field, + title: attrs.options.title || field.string, - }; + }), }; -registry.category("fields").add("gauge", GaugeField); +registry.category("fields").add("gauge", gaugeField); diff --git a/addons/website/static/src/components/fields/fields.js b/addons/website/static/src/components/fields/fields.js index 7c88740d5f4..3cd15eb92ca 100644 --- a/addons/website/static/src/components/fields/fields.js +++ b/addons/website/static/src/components/fields/fields.js @@ -48,20 +48,23 @@ class PageUrlField extends Component { this.props.update(this.state); } } + PageUrlField.components = {Switch, PageDependencies}; PageUrlField.template = 'website.PageUrlField'; PageUrlField.props = { ...standardFieldProps, placeholder: {type: String, optional: true}, }; -PageUrlField.extractProps = ({attrs}) => { - return { - placeholder: attrs.placeholder, - }; -}; -PageUrlField.supportedTypes = ['char']; -registry.category("fields").add("page_url", PageUrlField); +const pageUrlField = { + component: PageUrlField, + supportedTypes: ['char'], + extractProps: ({ attrs }) => ({ + placeholder: attrs.placeholder, + }), +}; + +registry.category("fields").add("page_url", pageUrlField); /** * Displays 'Selection' field's values as images to select. @@ -86,16 +89,19 @@ export class ImageRadioField extends Component { this.props.update(value); } } -ImageRadioField.supportedTypes = ['selection']; + ImageRadioField.template = 'website.FieldImageRadio'; ImageRadioField.props = { ...standardFieldProps, images: {type: Array, element: String}, }; -ImageRadioField.extractProps = ({attrs}) => { - return { + +export const imageRadioField = { + component: ImageRadioField, + supportedTypes: ['selection'], + extractProps: ({ attrs }) => ({ images: attrs.options.images, - }; + }), }; -registry.category("fields").add("image_radio", ImageRadioField); +registry.category("fields").add("image_radio", imageRadioField); diff --git a/addons/website/static/src/components/fields/publish_button.js b/addons/website/static/src/components/fields/publish_button.js index cc60352dc23..8a969f20cb2 100644 --- a/addons/website/static/src/components/fields/publish_button.js +++ b/addons/website/static/src/components/fields/publish_button.js @@ -7,4 +7,6 @@ const { Component } = owl; class PublishField extends Component {} PublishField.template = "website.PublishField"; -registry.category("fields").add("website_publish_button", PublishField); +registry.category("fields").add("website_publish_button", { + component: PublishField, +}); diff --git a/addons/website/static/src/components/fields/redirect_field.js b/addons/website/static/src/components/fields/redirect_field.js index cfda848b8fb..3d45914365c 100644 --- a/addons/website/static/src/components/fields/redirect_field.js +++ b/addons/website/static/src/components/fields/redirect_field.js @@ -23,4 +23,7 @@ class RedirectField extends Component { } RedirectField.template = "website.RedirectField"; -registry.category("fields").add("website_redirect_button", RedirectField); + +registry.category("fields").add("website_redirect_button", { + component: RedirectField, +}); diff --git a/addons/website/static/src/components/fields/widget_iframe.js b/addons/website/static/src/components/fields/widget_iframe.js index 3850d857f99..c806ae7351d 100644 --- a/addons/website/static/src/components/fields/widget_iframe.js +++ b/addons/website/static/src/components/fields/widget_iframe.js @@ -16,4 +16,8 @@ class FieldIframePreview extends Component { } FieldIframePreview.template = 'website.iframeWidget'; -registry.category('fields').add('iframe', FieldIframePreview); +export const fieldIframePreview = { + component: FieldIframePreview, +}; + +registry.category("fields").add("iframe", fieldIframePreview); diff --git a/addons/website_sale/static/src/js/website_sale_video_field_preview.js b/addons/website_sale/static/src/js/website_sale_video_field_preview.js index a1754726861..a1c38a5e2cc 100644 --- a/addons/website_sale/static/src/js/website_sale_video_field_preview.js +++ b/addons/website_sale/static/src/js/website_sale_video_field_preview.js @@ -7,4 +7,8 @@ const { Component } = owl; export class FieldVideoPreview extends Component {} FieldVideoPreview.template = 'website_sale.FieldVideoPreview'; -registry.category("fields").add("video_preview", FieldVideoPreview); +export const fieldVideoPreview = { + component: FieldVideoPreview, +}; + +registry.category("fields").add("video_preview", fieldVideoPreview); diff --git a/addons/website_slides/static/src/slide_category_one2many_field.js b/addons/website_slides/static/src/slide_category_one2many_field.js index eb4fc168916..3fe751b018c 100644 --- a/addons/website_slides/static/src/slide_category_one2many_field.js +++ b/addons/website_slides/static/src/slide_category_one2many_field.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; import { SlideCategoryListRenderer } from "./slide_category_list_renderer"; -import { X2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; class SlideCategoryOneToManyField extends X2ManyField { setup() { @@ -19,4 +19,7 @@ SlideCategoryOneToManyField.defaultProps = { editable: "bottom", }; -registry.category("fields").add("slide_category_one2many", SlideCategoryOneToManyField); +registry.category("fields").add("slide_category_one2many", { + ...x2ManyField, + component: SlideCategoryOneToManyField, +});