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,
+});