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 e81315fd89b..05b5109af28 100644 --- a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js +++ b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js @@ -592,13 +592,13 @@ export const analyticDistribution = { component: AnalyticDistribution, supportedTypes: ["char", "text"], fieldDependencies: [{ name:"analytic_precision", type: "integer" }], - extractProps: ({ attrs }) => ({ - business_domain: attrs.options.business_domain, - account_field: attrs.options.account_field, - product_field: attrs.options.product_field, + extractProps: ({ attrs, options }) => ({ + business_domain: options.business_domain, + account_field: options.account_field, + product_field: options.product_field, business_domain_compute: attrs.business_domain_compute, - force_applicability: attrs.options.force_applicability, - allow_save: !attrs.options.disable_save, + force_applicability: options.force_applicability, + allow_save: !options.disable_save, }), }; 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 21c96537f1e..2389f7b23fe 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 @@ -24,8 +24,8 @@ export const iconSelectionField = { component: IconSelectionField, displayName: _lt("Icon Selection"), supportedTypes: ["char", "text", "selection"], - extractProps: ({ attrs }) => ({ - icons: attrs.options, + extractProps: ({ options }) => ({ + icons: options, }), }; 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 b2c9b898f08..70b9c5a8197 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 @@ -36,10 +36,10 @@ FilterableSelectionField.props = { 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, + extractProps: (fieldInfo) => ({ + ...selectionField.extractProps(fieldInfo), + whitelisted_values: fieldInfo.options.whitelisted_values, + blacklisted_values: fieldInfo.options.blacklisted_values, }), }; diff --git a/addons/mail/static/src/js/onchange_on_keydown.js b/addons/mail/static/src/js/onchange_on_keydown.js index ca8229635dc..a0ab1953e82 100644 --- a/addons/mail/static/src/js/onchange_on_keydown.js +++ b/addons/mail/static/src/js/onchange_on_keydown.js @@ -52,21 +52,21 @@ TextField.props = { }; 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) +charField.extractProps = (fieldInfo) => { + return Object.assign(charExtractProps(fieldInfo), { + onchangeOnKeydown: archParseBoolean(fieldInfo.attrs.onchange_on_keydown), + keydownDebounceDelay: fieldInfo.attrs.keydown_debounce_delay + ? Number(fieldInfo.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) +textField.extractProps = (fieldInfo) => { + return Object.assign(textExtractProps(fieldInfo), { + onchangeOnKeydown: archParseBoolean(fieldInfo.attrs.onchange_on_keydown), + keydownDebounceDelay: fieldInfo.attrs.keydown_debounce_delay + ? Number(fieldInfo.attrs.keydown_debounce_delay) : 2000, }); }; 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 82bf6d80145..7ea2f78989e 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 @@ -50,9 +50,9 @@ KanbanMany2OneAvatarUserField.props = { export const kanbanMany2OneAvatarUserField = { ...many2OneAvatarUserField, component: KanbanMany2OneAvatarUserField, - extractProps: (params) => ({ - ...many2OneAvatarUserField.extractProps(params), - displayAvatarName: params.attrs.options.display_avatar_name || false, + extractProps: (fieldInfo) => ({ + ...many2OneAvatarUserField.extractProps(fieldInfo), + displayAvatarName: fieldInfo.options.display_avatar_name || false, }), }; 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 f4d442b8cbe..740100d5081 100644 --- a/addons/mass_mailing/static/src/js/mailing_m2o_filter.js +++ b/addons/mass_mailing/static/src/js/mailing_m2o_filter.js @@ -153,10 +153,10 @@ FieldMany2OneMailingFilter.defaultProps = { 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, + extractProps: (fieldInfo) => ({ + ...many2OneField.extractProps(fieldInfo), + domain_field: fieldInfo.options.domain_field, + model_field: fieldInfo.options.model_field, }), }; 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 6786d416c5e..6730c93a57a 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 @@ -599,11 +599,11 @@ 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'], + extractProps: (fieldInfo) => ({ + ...htmlField.extractProps(fieldInfo), + filterTemplates: fieldInfo.options.filterTemplates, + inlineField: fieldInfo.options['inline-field'], + iframeHtmlClass: fieldInfo.attrs.iframeHtmlClass, }), }; 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 a83b41d15b1..31b9a5a6148 100644 --- a/addons/sms/static/src/components/phone_field/phone_field.js +++ b/addons/sms/static/src/components/phone_field/phone_field.js @@ -20,10 +20,10 @@ patch(PhoneField, "sms.PhoneField", { }); const patchDescr = { - extractProps({ attrs }) { + extractProps(fieldInfo) { return { - ...this._super({ attrs }), - enableButton: attrs.options.enable_sms, + ...this._super(fieldInfo), + enableButton: fieldInfo.options.enable_sms, }; }, }; diff --git a/addons/web/static/src/views/calendar/calendar_common/calendar_common_popover.xml b/addons/web/static/src/views/calendar/calendar_common/calendar_common_popover.xml index 361a41d9484..0a169c4bd83 100644 --- a/addons/web/static/src/views/calendar/calendar_common/calendar_common_popover.xml +++ b/addons/web/static/src/views/calendar/calendar_common/calendar_common_popover.xml @@ -48,7 +48,7 @@ -
  • +
  • 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 894d797b48e..76c2d0cb575 100644 --- a/addons/web/static/src/views/fields/ace/ace_field.js +++ b/addons/web/static/src/views/fields/ace/ace_field.js @@ -123,8 +123,8 @@ export const aceField = { component: AceField, displayName: _lt("Ace Editor"), supportedTypes: ["text"], - extractProps: ({ attrs }) => ({ - mode: attrs.options.mode, + extractProps: ({ options }) => ({ + mode: options.mode, }), }; 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 473a52a0f35..1f5675c38d7 100644 --- a/addons/web/static/src/views/fields/badge/badge_field.js +++ b/addons/web/static/src/views/fields/badge/badge_field.js @@ -1,8 +1,9 @@ /** @odoo-module **/ import { _lt } from "@web/core/l10n/translation"; +import { evaluateExpr } from "@web/core/py_js/py"; import { registry } from "@web/core/registry"; -import { standardFieldProps } from "../standard_field_props"; +import { standardFieldProps } from "@web/views/fields/standard_field_props"; import { Component } from "@odoo/owl"; const formatters = registry.category("formatters"); @@ -11,6 +12,10 @@ export class BadgeField extends Component { static template = "web.BadgeField"; static props = { ...standardFieldProps, + decorations: { type: Object, optional: true }, + }; + static defaultProps = { + decorations: {}, }; get formattedValue() { @@ -21,8 +26,9 @@ export class BadgeField extends Component { } get classFromDecoration() { + const evalContext = this.props.record.evalContext; for (const decorationName in this.props.decorations) { - if (this.props.decorations[decorationName]) { + if (evaluateExpr(this.props.decorations[decorationName], evalContext)) { return `text-bg-${decorationName}`; } } @@ -34,6 +40,9 @@ export const badgeField = { component: BadgeField, displayName: _lt("Badge"), supportedTypes: ["selection", "many2one", "char"], + extractProps: ({ decorations }) => { + return { decorations }; + }, }; registry.category("fields").add("badge", badgeField); 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 0eaccda2c0c..b1f64b6718a 100644 --- a/addons/web/static/src/views/fields/binary/binary_field.js +++ b/addons/web/static/src/views/fields/binary/binary_field.js @@ -69,8 +69,8 @@ export const binaryField = { component: BinaryField, displayName: _lt("File"), supportedTypes: ["binary"], - extractProps: ({ attrs }) => ({ - acceptedFileExtensions: attrs.options.accepted_file_extensions, + extractProps: ({ attrs, options }) => ({ + acceptedFileExtensions: options.accepted_file_extensions, fileNameField: attrs.filename, }), }; 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 4d3e95c7286..338c688d994 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 @@ -28,8 +28,8 @@ export const booleanIconField = { component: BooleanIconField, displayName: _lt("Boolean Icon"), supportedTypes: ["boolean"], - extractProps: ({ attrs }) => ({ - icon: attrs.options.icon, + extractProps: ({ options }) => ({ + icon: options.icon, }), }; 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 44db4909596..a7c77a3fd71 100644 --- a/addons/web/static/src/views/fields/char/char_field.js +++ b/addons/web/static/src/views/fields/char/char_field.js @@ -98,9 +98,9 @@ export const charField = { component: CharField, displayName: _lt("Text"), supportedTypes: ["char"], - extractProps: ({ attrs }) => ({ + extractProps: ({ attrs, options }) => ({ isPassword: archParseBoolean(attrs.password), - dynamicPlaceholder: attrs.options.dynamic_placeholder, + dynamicPlaceholder: options.dynamic_placeholder, autocomplete: attrs.autocomplete, placeholder: attrs.placeholder, }), 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 3de21385ab9..b3028897d98 100644 --- a/addons/web/static/src/views/fields/date/date_field.js +++ b/addons/web/static/src/views/fields/date/date_field.js @@ -62,8 +62,8 @@ export const dateField = { component: DateField, displayName: _lt("Date"), supportedTypes: ["date", "datetime"], - extractProps: ({ attrs }) => ({ - pickerOptions: attrs.options.datepicker, + extractProps: ({ attrs, options }) => ({ + pickerOptions: options.datepicker, placeholder: attrs.placeholder, }), }; 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 2d050aba75b..d9a114feb3a 100644 --- a/addons/web/static/src/views/fields/daterange/daterange_field.js +++ b/addons/web/static/src/views/fields/daterange/daterange_field.js @@ -154,11 +154,11 @@ export class DateRangeField extends Component { export const dateRangeField = { component: DateRangeField, supportedTypes: ["date", "datetime"], - extractProps: ({ attrs }) => ({ - relatedEndDateField: attrs.options.related_end_date, - relatedStartDateField: attrs.options.related_start_date, + extractProps: ({ attrs, options }) => ({ + relatedEndDateField: options.related_end_date, + relatedStartDateField: options.related_start_date, + formatType: options.format_type, placeholder: attrs.placeholder, - formatType: attrs.options.format_type, }), }; 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 b333ec6b6d1..7d884274f73 100644 --- a/addons/web/static/src/views/fields/datetime/datetime_field.js +++ b/addons/web/static/src/views/fields/datetime/datetime_field.js @@ -52,8 +52,8 @@ export const dateTimeField = { component: DateTimeField, displayName: _lt("Date & Time"), supportedTypes: ["datetime"], - extractProps: ({ attrs }) => ({ - pickerOptions: attrs.options.datepicker, + extractProps: ({ attrs, options }) => ({ + pickerOptions: options.datepicker, placeholder: attrs.placeholder, }), }; 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 e49f076dcf1..a28a018566f 100644 --- a/addons/web/static/src/views/fields/domain/domain_field.js +++ b/addons/web/static/src/views/fields/domain/domain_field.js @@ -144,9 +144,9 @@ export const domainField = { displayName: _lt("Domain"), supportedTypes: ["char"], isEmpty: () => false, - extractProps: ({ attrs }) => ({ - editInDialog: attrs.options.in_dialog, - resModel: attrs.options.model, + extractProps: ({ options }) => ({ + editInDialog: options.in_dialog, + resModel: options.model, }), }; diff --git a/addons/web/static/src/views/fields/field.js b/addons/web/static/src/views/fields/field.js index df7622b0756..5e92fab5dbd 100644 --- a/addons/web/static/src/views/fields/field.js +++ b/addons/web/static/src/views/fields/field.js @@ -18,39 +18,24 @@ const fieldRegistry = registry.category("fields"); class DefaultField extends Component {} DefaultField.template = xml``; -function getFieldClassFromRegistry(fieldType, widget, viewType, jsClass) { - if (jsClass && widget) { - const name = `${jsClass}.${widget}`; - if (fieldRegistry.contains(name)) { - return fieldRegistry.get(name); +function getFieldFromRegistry(fieldType, widget, viewType, jsClass) { + const prefixes = jsClass ? [jsClass, viewType, ""] : [viewType, ""]; + const findInRegistry = (key) => { + for (const prefix of prefixes) { + const _key = prefix ? `${prefix}.${key}` : key; + if (fieldRegistry.contains(_key)) { + return fieldRegistry.get(_key); + } } - } - if (viewType && widget) { - const name = `${viewType}.${widget}`; - if (fieldRegistry.contains(name)) { - return fieldRegistry.get(name); - } - } - + }; if (widget) { - if (fieldRegistry.contains(widget)) { - return fieldRegistry.get(widget); + const field = findInRegistry(widget); + if (field) { + return field; } console.warn(`Missing widget: ${widget} for field of type ${fieldType}`); } - - if (viewType && fieldType) { - const name = `${viewType}.${fieldType}`; - if (fieldRegistry.contains(name)) { - return fieldRegistry.get(name); - } - } - - if (fieldRegistry.contains(fieldType)) { - return fieldRegistry.get(fieldType); - } - - return { component: DefaultField }; + return findInRegistry(fieldType) || { component: DefaultField }; } export function fieldVisualFeedback(field, record, fieldName, fieldInfo) { @@ -75,10 +60,11 @@ export function fieldVisualFeedback(field, record, fieldName, fieldInfo) { export class Field extends Component { setup() { - this.field = this.props.fieldInfo.field; - if (!this.field) { + if (this.props.fieldInfo) { + this.field = this.props.fieldInfo.field; + } else { const fieldType = this.props.record.fields[this.props.name].type; - this.field = getFieldClassFromRegistry(fieldType, this.props.type); + this.field = getFieldFromRegistry(fieldType, this.props.type); } } @@ -88,7 +74,7 @@ export class Field extends Component { this.field, record, name, - fieldInfo + fieldInfo || {} ); const classNames = { o_field_widget: true, @@ -108,11 +94,13 @@ export class Field extends Component { // generate field decorations classNames (only if field-specific decorations // have been defined in an attribute, e.g. decoration-danger="other_field = 5") // only handle the text-decoration. - const { decorations } = fieldInfo; - const evalContext = record.evalContext; - for (const decoName in decorations) { - const value = evaluateExpr(decorations[decoName], evalContext); - classNames[getClassNameFromDecoration(decoName)] = value; + if (fieldInfo && fieldInfo.decorations) { + const { decorations } = fieldInfo; + const evalContext = record.evalContext; + for (const decoName in decorations) { + const value = evaluateExpr(decorations[decoName], evalContext); + classNames[getClassNameFromDecoration(decoName)] = value; + } } return classNames; @@ -125,28 +113,22 @@ export class Field extends Component { get fieldComponentProps() { const record = this.props.record; const evalContext = record.evalContext; - const fieldInfo = this.props.fieldInfo; - const modifiers = fieldInfo.modifiers || {}; - const readonlyFromModifiers = evalDomain(modifiers.readonly, evalContext); + let readonlyFromModifiers = false; + let propsFromNode = {}; + if (this.props.fieldInfo) { + let fieldInfo = this.props.fieldInfo; - // Decoration props - const decorationMap = {}; - const { decorations } = fieldInfo; - for (const decoName in decorations) { - const value = evaluateExpr(decorations[decoName], evalContext); - decorationMap[decoName] = value; - } + const modifiers = fieldInfo.modifiers || {}; + readonlyFromModifiers = evalDomain(modifiers.readonly, evalContext); - let propsFromAttrs = fieldInfo.propsFromAttrs || {}; - if (this.props.attrs) { - const extractProps = this.field.extractProps || (() => ({})); - propsFromAttrs = extractProps.call(this.field, { - attrs: { - ...this.props.attrs, - options: evaluateExpr(this.props.attrs.options || "{}"), - }, - }); + if (this.props.attrs) { + fieldInfo = { + ...fieldInfo, + attrs: { ...fieldInfo.attrs, ...this.props.attrs }, + }; + } + propsFromNode = this.field.extractProps ? this.field.extractProps(fieldInfo) : {}; } const props = { ...this.props }; @@ -158,11 +140,9 @@ export class Field extends Component { delete props.type; return { - ...fieldInfo.props, value: this.props.record.data[this.props.name], - decorations: decorationMap, readonly: !record.isInEdition || readonlyFromModifiers || false, - ...propsFromAttrs, + ...propsFromNode, ...props, }; } @@ -171,7 +151,7 @@ export class Field extends Component { if (this.props.showTooltip) { const tooltip = getTooltipInfo({ field: this.props.record.fields[this.props.name], - fieldInfo: this.props.fieldInfo, + fieldInfo: this.props.fieldInfo || {}, }); if (Boolean(odoo.debug) || (tooltip && JSON.parse(tooltip).field.help)) { return tooltip; @@ -187,51 +167,35 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { const widget = node.getAttribute("widget"); const fields = models[modelName]; const modifiers = JSON.parse(node.getAttribute("modifiers") || "{}"); - const field = getFieldClassFromRegistry(fields[name].type, widget, viewType, jsClass); + const field = getFieldFromRegistry(fields[name].type, widget, viewType, jsClass); const fieldInfo = { name, viewType, + widget, + modifiers, + field, context: node.getAttribute("context") || "{}", string: node.getAttribute("string") || fields[name].string, help: node.getAttribute("help"), - widget, - modifiers, onChange: archParseBoolean(node.getAttribute("on_change")), - field, forceSave: archParseBoolean(node.getAttribute("force_save")), - decorations: {}, // populated below - noLabel: archParseBoolean(node.getAttribute("nolabel")), - props: {}, - rawAttrs: {}, options: evaluateExpr(node.getAttribute("options") || "{}"), alwaysInvisible: modifiers.invisible === true || modifiers.column_invisible === true, + decorations: {}, // populated below + attrs: {}, // populated below }; if (node.getAttribute("domain")) { + // TODO WOWl: remove with new model? fieldInfo.domain = node.getAttribute("domain"); } - for (const attribute of node.attributes) { - if (attribute.name in Field.forbiddenAttributeNames) { - throw new Error(Field.forbiddenAttributeNames[attribute.name]); + for (const { name, value } of node.attributes) { + if (name.startsWith("decoration-")) { + // prepare field decorations + fieldInfo.decorations[name.replace("decoration-", "")] = value; + } else if (!name.startsWith("t-att")) { + // all other (non dynamic) attributes + fieldInfo.attrs[name] = value; } - - // prepare field decorations - if (attribute.name.startsWith("decoration-")) { - const decorationName = attribute.name.replace("decoration-", ""); - fieldInfo.decorations[decorationName] = attribute.value; - continue; - } - - if (!attribute.name.startsWith("t-att")) { - fieldInfo.rawAttrs[attribute.name] = attribute.value; - } - } - - if (viewType !== "kanban") { - // FIXME WOWL: find a better solution - const extractProps = field.extractProps || (() => ({})); - fieldInfo.propsFromAttrs = extractProps.call(field, { - attrs: { ...fieldInfo.rawAttrs, options: fieldInfo.options }, - }); } if (X2M_TYPES.includes(fields[name].type)) { @@ -262,7 +226,6 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { viewMode = viewMode.replace("tree", "list"); } fieldInfo.viewMode = viewMode; - fieldInfo.relation = fields[name].relation; // not really necessary fieldInfo.views = views; let fieldsToFetch = field.fieldsToFetch; @@ -277,8 +240,5 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) { return fieldInfo; }; -Field.forbiddenAttributeNames = { - decorations: `You cannot use the "decorations" attribute name as it is used as generated prop name for the composite decoration- attributes.`, -}; Field.props = ["fieldInfo?", "*"]; -Field.defaultProps = { fieldInfo: {}, setDirty: () => {} }; +Field.defaultProps = { setDirty: () => {} }; 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 e7c5999e58f..5f0767b2095 100644 --- a/addons/web/static/src/views/fields/float/float_field.js +++ b/addons/web/static/src/views/fields/float/float_field.js @@ -57,19 +57,19 @@ export const floatField = { displayName: _lt("Float"), supportedTypes: ["float"], isEmpty: () => false, - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options }) => { // 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 (options.digits) { + digits = options.digits; } return { - inputType: attrs.options.type, - step: attrs.options.step, + inputType: options.type, + step: options.step, digits, placeholder: attrs.placeholder, }; 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 f95504e31f2..4e0b4c290c3 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 @@ -29,9 +29,9 @@ export class FloatFactorField extends FloatField { export const floatFactorField = { ...floatField, component: FloatFactorField, - extractProps: (params) => ({ - ...floatField.extractProps(params), - factor: params.attrs.options.factor, + extractProps: (fieldInfo) => ({ + ...floatField.extractProps(fieldInfo), + factor: fieldInfo.options.factor, }), }; 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 0a7212c64c7..9b6e7b0e874 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 @@ -54,21 +54,21 @@ export const floatToggleField = { component: FloatToggleField, supportedTypes: ["float"], isEmpty: () => false, - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options }) => { // 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 (options.digits) { + digits = options.digits; } return { digits, - range: attrs.options.range, - factor: attrs.options.factor, - disableReadOnly: attrs.options.force_button || false, + range: options.range, + factor: options.factor, + disableReadOnly: options.force_button || false, }; }, }; 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 b4b48923246..da63b46ef2a 100644 --- a/addons/web/static/src/views/fields/image/image_field.js +++ b/addons/web/static/src/views/fields/image/image_field.js @@ -133,21 +133,14 @@ export const imageField = { displayName: _lt("Image"), supportedTypes: ["binary"], fieldDependencies: [{ name: "write_date", type: "datetime" }], - extractProps: ({ attrs }) => ({ - enableZoom: attrs.options.zoom, - zoomDelay: attrs.options.zoom_delay, - previewImage: attrs.options.preview_image, - acceptedFileExtensions: attrs.options.accepted_file_extensions, - width: - attrs.options.size && Boolean(attrs.options.size[0]) - ? attrs.options.size[0] - : attrs.width, - height: - attrs.options.size && Boolean(attrs.options.size[1]) - ? attrs.options.size[1] - : attrs.height, + extractProps: ({ attrs, options }) => ({ + enableZoom: options.zoom, + zoomDelay: options.zoom_delay, + previewImage: options.preview_image, + acceptedFileExtensions: options.accepted_file_extensions, + width: options.size && Boolean(options.size[0]) ? options.size[0] : attrs.width, + height: options.size && Boolean(options.size[1]) ? 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 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 eab3bca39f1..c13e542e4d4 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 @@ -53,12 +53,10 @@ 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, + extractProps: ({ attrs, options }) => ({ + width: options.size ? options.size[0] : attrs.width, + height: options.size ? options.size[1] : attrs.height, }), }; registry.category("fields").add("image_url", imageUrlField); -// TODO WOWL: remove below when old registry is removed. -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 f8e7338bca9..ba47eb82586 100644 --- a/addons/web/static/src/views/fields/integer/integer_field.js +++ b/addons/web/static/src/views/fields/integer/integer_field.js @@ -44,9 +44,9 @@ export const integerField = { displayName: _lt("Integer"), supportedTypes: ["integer"], isEmpty: (record, fieldName) => record.data[fieldName] === false, - extractProps: ({ attrs }) => ({ - inputType: attrs.options.type, - step: attrs.options.step, + extractProps: ({ attrs, options }) => ({ + inputType: options.type, + step: options.step, placeholder: attrs.placeholder, }), }; 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 263bbd98457..59ab13b1106 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 @@ -31,8 +31,8 @@ export const labelSelectionField = { component: LabelSelectionField, displayName: _lt("Label Selection"), supportedTypes: ["selection"], - extractProps: ({ attrs }) => ({ - classesObj: attrs.options.classes, + extractProps: ({ options }) => ({ + classesObj: options.classes, }), }; 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 0d9a396cb98..6af81cde805 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 @@ -66,8 +66,8 @@ export const many2ManyBinaryField = { { name: "name", type: "char" }, { name: "mimetype", type: "char" }, ], - extractProps: ({ attrs }) => ({ - acceptedFileExtensions: attrs.options.accepted_file_extensions, + extractProps: ({ attrs, options }) => ({ + acceptedFileExtensions: options.accepted_file_extensions, className: attrs.class, }), }; 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 781864a3bb4..bd320a8e5f2 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 @@ -250,25 +250,25 @@ export const many2ManyTagsField = { displayName: _lt("Tags"), supportedTypes: ["many2many"], isSet: (value) => value.count > 0, - fieldsToFetch: (fieldInfo) => { + fieldsToFetch: ({ options }) => { const fieldsToFetch = [{ name: "display_name", type: "char" }]; - if (fieldInfo.options.color_field) { - fieldsToFetch.push({ name: fieldInfo.options.color_field, type: "integer" }); + if (options.color_field) { + fieldsToFetch.push({ name: options.color_field, type: "integer" }); } return fieldsToFetch; }, - extractProps: ({ attrs }) => { - const noCreate = Boolean(attrs.options.no_create); + extractProps: ({ attrs, options }) => { + const noCreate = Boolean(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); + const noQuickCreate = Boolean(options.no_quick_create); + const noCreateEdit = Boolean(options.no_create_edit); return { - colorField: attrs.options.color_field, - nameCreateField: attrs.options.create_name_field, + colorField: options.color_field, + nameCreateField: options.create_name_field, canCreate, canQuickCreate: canCreate && !noQuickCreate, canCreateEdit: canCreate && !noCreateEdit, - createDomain: attrs.options.create, + createDomain: options.create, placeholder: attrs.placeholder, }; }, @@ -359,9 +359,9 @@ export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField { export const many2ManyTagsFieldColorEditable = { ...many2ManyTagsField, component: Many2ManyTagsFieldColorEditable, - extractProps: (params) => ({ - ...many2ManyTagsField.extractProps(params), - canEditColor: !params.attrs.options.no_edit_color && !!params.attrs.options.color_field, + extractProps: (fieldInfo) => ({ + ...many2ManyTagsField.extractProps(fieldInfo), + canEditColor: !fieldInfo.options.no_edit_color && !!fieldInfo.options.color_field, }), }; 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 c92f62b3ecb..360034e22c2 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.js +++ b/addons/web/static/src/views/fields/many2one/many2one_field.js @@ -297,20 +297,20 @@ export const many2OneField = { component: Many2OneField, displayName: _lt("Many2one"), supportedTypes: ["many2one"], - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options, string }) => { const canCreate = - attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !attrs.options.no_create; + attrs.can_create && Boolean(JSON.parse(attrs.can_create)) && !options.no_create; return { placeholder: attrs.placeholder, - canOpen: !attrs.options.no_open, + canOpen: !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, + canQuickCreate: canCreate && !options.no_quick_create, + canCreateEdit: canCreate && !options.no_create_edit, + nameCreateField: options.create_name_field, + canScanBarcode: !!options.can_scan_barcode, openTarget: attrs.open_target, - string: attrs.string, + string, }; }, }; 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 bff69c3b110..aa0295e740b 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 @@ -15,8 +15,8 @@ export const many2OneBarcodeField = { ...many2OneField, component: Many2OneBarcodeField, displayName: _lt("Many2OneBarcode"), - extractProps: (params) => ({ - ...many2OneField.extractProps(params), + extractProps: (fieldInfo) => ({ + ...many2OneField.extractProps(fieldInfo), canScanBarcode: true, }), }; 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 22b2973b8a7..1dc75a37787 100644 --- a/addons/web/static/src/views/fields/monetary/monetary_field.js +++ b/addons/web/static/src/views/fields/monetary/monetary_field.js @@ -80,11 +80,11 @@ export const monetaryField = { component: MonetaryField, supportedTypes: ["monetary", "float"], displayName: _lt("Monetary"), - extractProps: ({ attrs }) => ({ - currencyField: attrs.options.currency_field, + extractProps: ({ attrs, options }) => ({ + currencyField: options.currency_field, inputType: attrs.type, - useFieldDigits: attrs.options.field_digits, - hideSymbol: attrs.options.no_symbol, + useFieldDigits: options.field_digits, + hideSymbol: options.no_symbol, placeholder: attrs.placeholder, }), }; 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 e629d5cad51..431bc2063bf 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 @@ -89,9 +89,9 @@ export const pdfViewerField = { component: PdfViewerField, displayName: _lt("PDF Viewer"), supportedTypes: ["binary"], - extractProps: ({ attrs }) => ({ + extractProps: ({ attrs, options }) => ({ fileNameField: attrs.filename, - previewImage: attrs.options.preview_image, + previewImage: options.preview_image, }), }; 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 64c2de3bfae..7d219ee8e2c 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 @@ -28,9 +28,7 @@ export const percentPieField = { displayName: _lt("PercentPie"), supportedTypes: ["float", "integer"], additionalClasses: ["o_field_percent_pie"], - extractProps: ({ attrs }) => ({ - string: attrs.string, - }), + extractProps: ({ string }) => ({ string }), }; 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 ff02f831576..481629c99dc 100644 --- a/addons/web/static/src/views/fields/percentage/percentage_field.js +++ b/addons/web/static/src/views/fields/percentage/percentage_field.js @@ -46,14 +46,14 @@ export const percentageField = { component: PercentageField, displayName: _lt("Percentage"), supportedTypes: ["integer", "float"], - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options }) => { // 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 (options.digits) { + digits = options.digits; } return { 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 c9861843f01..0b3dc0ed5c5 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 @@ -155,15 +155,14 @@ 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, - isEditableInReadonly: attrs.options.editable_readonly, + extractProps: ({ attrs, options }) => ({ + maxValueField: options.max_value, + currentValueField: options.current_value, + isEditable: !options.readonly && options.editable, + isEditableInReadonly: options.editable_readonly, isCurrentValueEditable: - attrs.options.editable && - (!attrs.options.edit_max_value || attrs.options.edit_current_value), - isMaxValueEditable: attrs.options.editable && attrs.options.edit_max_value, + options.editable && (!options.edit_max_value || options.edit_current_value), + isMaxValueEditable: options.editable && options.edit_max_value, title: attrs.title, }), }; 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 ac42fb9a6db..4edab220a11 100644 --- a/addons/web/static/src/views/fields/radio/radio_field.js +++ b/addons/web/static/src/views/fields/radio/radio_field.js @@ -72,8 +72,8 @@ export const radioField = { displayName: _lt("Radio"), supportedTypes: ["many2one", "selection"], isEmpty: (record, fieldName) => record.data[fieldName] === false, - extractProps: ({ attrs }) => ({ - orientation: attrs.options.horizontal ? "horizontal" : "vertical", + extractProps: ({ options }) => ({ + orientation: options.horizontal ? "horizontal" : "vertical", }), legacySpecialData: "_fetchSpecialMany2ones", }; 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 f88c073d7a7..6acee8fae91 100644 --- a/addons/web/static/src/views/fields/reference/reference_field.js +++ b/addons/web/static/src/views/fields/reference/reference_field.js @@ -128,7 +128,7 @@ export const referenceField = { displayName: _lt("Reference"), supportedTypes: ["reference", "char"], legacySpecialData: "_fetchSpecialReference", - extractProps: (params) => ({ + extractProps: (fieldInfo) => ({ /* 1 - 2 - @@ -137,9 +137,8 @@ export const referenceField = { 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"], + ...many2OneField.extractProps(fieldInfo), + hideModelSelector: !!fieldInfo.options.hide_model || !!fieldInfo.options.model_field, }), }; 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 06a37b80bac..1afad51c821 100644 --- a/addons/web/static/src/views/fields/signature/signature_field.js +++ b/addons/web/static/src/views/fields/signature/signature_field.js @@ -136,12 +136,12 @@ export class SignatureField extends Component { export const signatureField = { component: SignatureField, - extractProps: ({ attrs }) => ({ - defaultFont: attrs.options.default_font || "", - fullName: attrs.options.full_name, - height: attrs.options.size ? attrs.options.size[1] || undefined : attrs.height, - previewImage: attrs.options.preview_image, - width: attrs.options.size ? attrs.options.size[0] || undefined : attrs.width, + extractProps: ({ attrs, options }) => ({ + defaultFont: options.default_font || "", + fullName: options.full_name, + height: options.size ? options.size[1] || undefined : attrs.height, + previewImage: options.preview_image, + width: options.size ? options.size[0] || undefined : attrs.width, }), }; diff --git a/addons/web/static/src/views/fields/standard_field_props.js b/addons/web/static/src/views/fields/standard_field_props.js index 589ccc805ef..8343503d3e8 100644 --- a/addons/web/static/src/views/fields/standard_field_props.js +++ b/addons/web/static/src/views/fields/standard_field_props.js @@ -6,6 +6,5 @@ export const standardFieldProps = { readonly: { type: Boolean, optional: true }, record: { type: Object }, value: true, - decorations: { type: Object, optional: true }, setDirty: { type: Function, optional: true }, }; 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 02fc06d6607..7f350a08756 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 @@ -37,19 +37,19 @@ export const statInfoField = { displayName: _lt("Stat Info"), supportedTypes: ["float", "integer", "monetary"], isEmpty: () => false, - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options }) => { // 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 (options.digits) { + digits = options.digits; } return { digits, - labelField: attrs.options.label_field, + labelField: options.label_field, noLabel: archParseBoolean(attrs.nolabel), }; }, 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 acd97912c04..30827b56f24 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 @@ -91,8 +91,8 @@ export const stateSelectionField = { component: StateSelectionField, displayName: _lt("Label Selection"), supportedTypes: ["selection"], - extractProps: ({ attrs }) => ({ - hideLabel: !!attrs.options.hide_label, + extractProps: ({ options }) => ({ + hideLabel: !!options.hide_label, }), }; 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 dae47071984..31b0abf3b2f 100644 --- a/addons/web/static/src/views/fields/statusbar/statusbar_field.js +++ b/addons/web/static/src/views/fields/statusbar/statusbar_field.js @@ -225,10 +225,10 @@ export const statusBarField = { supportedTypes: ["many2one", "selection"], isEmpty: (record, fieldName) => record.model.env.isSmall && !record.data[fieldName], legacySpecialData: "_fetchSpecialStatus", - extractProps: ({ attrs }) => ({ + extractProps: ({ attrs, options }) => ({ canCreate: Boolean(attrs.can_create), canWrite: Boolean(attrs.can_write), - isDisabled: !attrs.options.clickable, + isDisabled: !options.clickable, visibleSelection: attrs.statusbar_visible && attrs.statusbar_visible.trim().split(/\s*,\s*/g), }), 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 7c400b82152..3290c464d86 100644 --- a/addons/web/static/src/views/fields/text/text_field.js +++ b/addons/web/static/src/views/fields/text/text_field.js @@ -129,9 +129,9 @@ export const textField = { component: TextField, displayName: _lt("Multiline Text"), supportedTypes: ["html", "text"], - extractProps: ({ attrs }) => ({ + extractProps: ({ attrs, options }) => ({ placeholder: attrs.placeholder, - dynamicPlaceholder: attrs.options.dynamic_placeholder, + dynamicPlaceholder: options.dynamic_placeholder, rowCount: attrs.rows && parseInteger(attrs.rows), }), }; 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 5fcd79d2273..6d5e506b211 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 @@ -74,10 +74,10 @@ 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, + extractProps: (fieldInfo) => ({ + ...selectionField.extractProps(fieldInfo), + tzOffsetField: fieldInfo.options.tz_offset_field, + mismatchTitle: fieldInfo.options.mismatch_title, }), }; 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 88194ad8c94..cdd8aa6edae 100644 --- a/addons/web/static/src/views/fields/url/url_field.js +++ b/addons/web/static/src/views/fields/url/url_field.js @@ -38,9 +38,9 @@ export const urlField = { component: UrlField, displayName: _lt("URL"), supportedTypes: ["char"], - extractProps: ({ attrs }) => ({ + extractProps: ({ attrs, options }) => ({ text: attrs.text, - websitePath: attrs.options.website_path, + websitePath: options.website_path, placeholder: attrs.placeholder, }), }; 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 4487d840697..c664dc2c818 100644 --- a/addons/web/static/src/views/list/list_arch_parser.js +++ b/addons/web/static/src/views/list/list_arch_parser.js @@ -116,7 +116,9 @@ export class ListArchParser extends XMLParser { className: node.getAttribute("class"), // for oe_edit_only and oe_read_only optional: node.getAttribute("optional") || false, type: "field", - hasLabel: !(fieldInfo.noLabel || fieldInfo.field.noLabel), + hasLabel: !( + archParseBoolean(fieldInfo.attrs.nolabel) || fieldInfo.field.noLabel + ), label: (fieldInfo.widget && label && label.toString()) || fieldInfo.string, }); } @@ -133,7 +135,7 @@ export class ListArchParser extends XMLParser { name: widgetInfo.name, // FIXME: this is dumb, we encode it into a weird object so that the widget // can decode it later... - node: encodeObjectForTemplate({ attrs: widgetInfo.rawAttrs }).slice(1, -1), + node: encodeObjectForTemplate({ attrs: widgetInfo.attrs }).slice(1, -1), className: node.getAttribute("class") || "", }; columns.push({ diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 2829c5906a7..51d982e3eb3 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -535,12 +535,12 @@ export class ListRenderer extends Component { if (type !== "integer" && type !== "float" && type !== "monetary") { continue; } - const { rawAttrs, widget } = column; + const { attrs, widget } = column; const func = - (rawAttrs.sum && "sum") || - (rawAttrs.avg && "avg") || - (rawAttrs.max && "max") || - (rawAttrs.min && "min"); + (attrs.sum && "sum") || + (attrs.avg && "avg") || + (attrs.max && "max") || + (attrs.min && "min"); if (func) { let aggregateValue = 0; if (func === "max") { @@ -556,11 +556,11 @@ export class ListRenderer extends Component { const formatter = formatters.get(widget, false) || formatters.get(type, false); const formatOptions = { - digits: rawAttrs.digits ? JSON.parse(rawAttrs.digits) : undefined, + digits: attrs.digits ? JSON.parse(attrs.digits) : undefined, escape: true, }; aggregates[fieldName] = { - help: rawAttrs[func], + help: attrs[func], value: formatter ? formatter(aggregateValue, formatOptions) : aggregateValue, }; } @@ -569,7 +569,7 @@ export class ListRenderer extends Component { } formatAggregateValue(group, column) { - const { widget, rawAttrs } = column; + const { widget, attrs } = column; const fieldType = this.props.list.fields[column.name].type; const aggregateValue = group.aggregates[column.name]; if (!(column.name in group.aggregates)) { @@ -577,7 +577,7 @@ export class ListRenderer extends Component { } const formatter = formatters.get(widget, false) || formatters.get(fieldType, false); const formatOptions = { - digits: rawAttrs.digits ? JSON.parse(rawAttrs.digits) : undefined, + digits: attrs.digits ? JSON.parse(attrs.digits) : undefined, escape: true, }; return formatter ? formatter(aggregateValue, formatOptions) : aggregateValue; @@ -677,12 +677,8 @@ export class ListRenderer extends Component { classNames.push("o_list_button"); } else if (column.type === "field") { classNames.push("o_field_cell"); - if ( - column.rawAttrs && - column.rawAttrs.class && - this.canUseFormatter(column, record) - ) { - classNames.push(column.rawAttrs.class); + if (column.attrs && column.attrs.class && this.canUseFormatter(column, record)) { + classNames.push(column.attrs.class); } const typeClass = FIELD_CLASSES[this.fields[column.name].type]; if (typeClass) { @@ -743,7 +739,7 @@ export class ListRenderer extends Component { } getFieldClass(column) { - return column.rawAttrs && column.rawAttrs.class; + return column.attrs && column.attrs.class; } getFormattedValue(column, record) { @@ -753,8 +749,8 @@ export class ListRenderer extends Component { const formatOptions = { escape: false, data: record.data, - isPassword: "password" in column.rawAttrs, - digits: column.rawAttrs.digits ? JSON.parse(column.rawAttrs.digits) : field.digits, + isPassword: "password" in column.attrs, + digits: column.attrs.digits ? JSON.parse(column.attrs.digits) : field.digits, field: record.fields[fieldName], }; return formatter(record.data[fieldName], formatOptions); @@ -1660,8 +1656,8 @@ export class ListRenderer extends Component { } calculateColumnWidth(column) { - if (column.options && column.rawAttrs.width) { - return { type: "absolute", value: column.rawAttrs.width }; + if (column.options && column.attrs.width) { + return { type: "absolute", value: column.attrs.width }; } if (column.type !== "field") { diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index 6170c951a5d..9aa5df223f2 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -69,7 +69,7 @@ const DEFAULT_QUICK_CREATE_VIEW = { export function isAllowedDateField(groupByField) { return ( ["date", "datetime"].includes(groupByField.type) && - archParseBoolean(groupByField.rawAttrs.allow_group_range_value) + archParseBoolean(groupByField.attrs.allow_group_range_value) ); } @@ -1483,7 +1483,7 @@ class DynamicList extends DataPoint { } const [groupByFieldName, granularity] = this.firstGroupBy.split(":"); return { - rawAttrs: {}, + attrs: {}, ...this.fields[groupByFieldName], ...this.activeFields[groupByFieldName], granularity: granularity, diff --git a/addons/web/static/src/views/utils.js b/addons/web/static/src/views/utils.js index 2e4d1e1865f..87a75bd15a4 100644 --- a/addons/web/static/src/views/utils.js +++ b/addons/web/static/src/views/utils.js @@ -28,7 +28,7 @@ export function addFieldDependencies(activeFields, fields, dependencies = []) { for (const dependency of dependencies) { const { name } = dependency; if (!(name in activeFields)) { - activeFields[name] = Object.assign({ name, rawAttrs: {} }, dependency, { + activeFields[name] = Object.assign({ name, attrs: {} }, dependency, { modifiers: { invisible: true }, }); } diff --git a/addons/web/static/src/views/widgets/standard_widget_props.js b/addons/web/static/src/views/widgets/standard_widget_props.js index b8f103bdc82..93ac34c18d7 100644 --- a/addons/web/static/src/views/widgets/standard_widget_props.js +++ b/addons/web/static/src/views/widgets/standard_widget_props.js @@ -5,6 +5,6 @@ export const standardWidgetProps = { record: { type: Object }, // TODO TO REMOVE - ListView - FGE options: { type: Object, optional: true }, - rawAttrs: { type: Object, optional: true }, + attrs: { type: Object, optional: true }, className: { type: String, optional: true }, }; diff --git a/addons/web/static/src/views/widgets/widget.js b/addons/web/static/src/views/widgets/widget.js index c82957ad1fd..108074c1e44 100644 --- a/addons/web/static/src/views/widgets/widget.js +++ b/addons/web/static/src/views/widgets/widget.js @@ -70,7 +70,7 @@ Widget.parseWidgetNode = function (node) { return { options: evaluateExpr(node.getAttribute("options") || "{}"), name, - rawAttrs: attrs, + attrs, widget, }; }; diff --git a/addons/web/static/tests/views/calendar/helpers.js b/addons/web/static/tests/views/calendar/helpers.js index 165e71f6d4c..c98f923216d 100644 --- a/addons/web/static/tests/views/calendar/helpers.js +++ b/addons/web/static/tests/views/calendar/helpers.js @@ -1,7 +1,9 @@ /** @odoo-module **/ import { uiService } from "@web/core/ui/ui_service"; +import { createElement } from "@web/core/utils/xml"; import { registry } from "@web/core/registry"; +import { Field } from "@web/views/fields/field"; import { clearRegistryWithCleanup, makeTestEnv } from "../../helpers/mock_env"; import { click, getFixture, mount, nextTick, triggerEvent } from "../../helpers/utils"; import { setupViewRegistries } from "@web/../tests/views/helpers"; @@ -206,6 +208,8 @@ export const FAKE_FIELDS = { }; function makeFakeModelState() { + const fakeFieldNode = createElement("field", { name: "name" }); + const fakeModels = { event: FAKE_FIELDS }; return { canCreate: true, canDelete: true, @@ -228,7 +232,7 @@ function makeFakeModelState() { hasEditDialog: false, hasQuickCreate: false, popoverFields: { - name: { rawAttrs: {}, options: {} }, + name: Field.parseFieldNode(fakeFieldNode, fakeModels, "event", "calendar"), }, rangeEnd: makeFakeDate().endOf("month"), rangeStart: makeFakeDate().startOf("month"), 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 b375089c625..0b9293bb8a0 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -3566,9 +3566,9 @@ QUnit.module("Views", (hooks) => { MyField.template = owl.xml`
    ok
    `; fieldRegistry.add("my_field", { component: MyField, - extractProps: function ({ attrs }) { - assert.deepEqual(attrs.options, { horizontal: true }); - return { horizontal: attrs.options.horizontal }; + extractProps: function ({ options }) { + assert.deepEqual(options, { horizontal: true }); + return { horizontal: options.horizontal }; }, }); diff --git a/addons/web/static/tests/views/kanban_view_tests.js b/addons/web/static/tests/views/kanban_view_tests.js index 67fa712e452..a3ccefc6929 100644 --- a/addons/web/static/tests/views/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban_view_tests.js @@ -866,7 +866,6 @@ QUnit.module("Views", (hooks) => { str: "some string", bool: "true", num: "4.5", - options: {}, field_id: "int_field", }); } 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 24a188c2d84..4a15cf30a0f 100644 --- a/addons/web_editor/static/src/js/backend/html_field.js +++ b/addons/web_editor/static/src/js/backend/html_field.js @@ -630,49 +630,49 @@ export const htmlField = { component: HtmlField, displayName: _lt("Html"), supportedTypes: ["html"], - extractProps: ({ attrs }) => { + extractProps: ({ attrs, options }) => { const wysiwygOptions = { placeholder: attrs.placeholder, - noAttachment: attrs.options['no-attachment'], - inIframe: Boolean(attrs.options.cssEdit), - iframeCssAssets: attrs.options.cssEdit, + noAttachment: options['no-attachment'], + inIframe: Boolean(options.cssEdit), + iframeCssAssets: options.cssEdit, iframeHtmlClass: attrs.iframeHtmlClass, - snippets: attrs.options.snippets, + snippets: options.snippets, mediaModalParams: { - noVideos: 'noVideos' in attrs.options ? attrs.options.noVideos : true, + noVideos: 'noVideos' in options ? 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, + height: options.height, + minHeight: options.minHeight, + maxHeight: options.maxHeight, + resizable: 'resizable' in options ? options.resizable : false, editorPlugins: [QWebPlugin], }; - if ('collaborative' in attrs.options) { - wysiwygOptions.collaborative = attrs.options.collaborative; + if ('collaborative' in options) { + wysiwygOptions.collaborative = options.collaborative; } - if ('allowCommandImage' in attrs.options) { + if ('allowCommandImage' in 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); + wysiwygOptions.allowCommandImage = Boolean(options.allowCommandImage); } - if ('allowCommandVideo' in attrs.options) { + if ('allowCommandVideo' in 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); + wysiwygOptions.allowCommandVideo = Boolean(options.allowCommandVideo); } return { - codeview: Boolean(odoo.debug && attrs.options.codeview), + codeview: Boolean(odoo.debug && 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: options.collaborative, + cssReadonlyAssetId: options.cssReadonly, + dynamicPlaceholder: options.dynamic_placeholder, + cssEditAssetId: options.cssEdit, + isInlineStyle: options['style-inline'], + wrapper: options.wrapper, wysiwygOptions, }; diff --git a/addons/web_kanban_gauge/static/src/gauge_field.js b/addons/web_kanban_gauge/static/src/gauge_field.js index eeca9aeb990..e7ee5ee6f46 100644 --- a/addons/web_kanban_gauge/static/src/gauge_field.js +++ b/addons/web_kanban_gauge/static/src/gauge_field.js @@ -94,9 +94,9 @@ GaugeField.props = { export const gaugeField = { component: GaugeField, - extractProps: ({ attrs }) => ({ - maxValueField: attrs.options.max_field, - title: attrs.options.title, + extractProps: ({ options }) => ({ + maxValueField: options.max_field, + title: options.title, }), }; diff --git a/addons/website/static/src/components/fields/fields.js b/addons/website/static/src/components/fields/fields.js index 3cd15eb92ca..8a6b3492ce9 100644 --- a/addons/website/static/src/components/fields/fields.js +++ b/addons/website/static/src/components/fields/fields.js @@ -99,8 +99,8 @@ ImageRadioField.props = { export const imageRadioField = { component: ImageRadioField, supportedTypes: ['selection'], - extractProps: ({ attrs }) => ({ - images: attrs.options.images, + extractProps: ({ options }) => ({ + images: options.images, }), };