diff --git a/backend/src/baserow/contrib/builder/api/elements/serializers.py b/backend/src/baserow/contrib/builder/api/elements/serializers.py index 9925fbf7c0..ca4c9e37fb 100644 --- a/backend/src/baserow/contrib/builder/api/elements/serializers.py +++ b/backend/src/baserow/contrib/builder/api/elements/serializers.py @@ -32,6 +32,7 @@ ) from baserow.core.exceptions import InstanceTypeDoesNotExist from baserow.core.formula.serializers import FormulaSerializerField +from baserow.core.formula.types import BASEROW_FORMULA_MODE_RAW class ElementSerializer(serializers.ModelSerializer): @@ -482,3 +483,40 @@ def get_menu_items(self, obj): return MenuItemSerializer( root_items, many=True, context={"all_items": menu_items} ).data + + +@extend_schema_field(OpenApiTypes.STR) +class CollectionFieldOptionalFormulaSerializerField(FormulaSerializerField): + """ + This field can be used to store a formula which mode depends on a field aside. If + `is_formula_field_name` property is `True`, + then the value will be treated as a simple formula otherwise, the value + will be treated as raw formula. + """ + + def __init__(self, *args, is_formula_field_name=None, **kwargs): + self.is_formula_field_name = is_formula_field_name + super().__init__(*args, **kwargs) + + def to_representation(self, value): + value = super().to_representation(value) + + is_formula = getattr(self.parent.instance, "config", {}).get( + self.is_formula_field_name, False + ) + + if not is_formula: + # We force the type to raw as it's not a formula + # For compat with unmigrated values. + value["mode"] = "raw" + + return value + + def to_internal_value(self, data): + data = super().to_internal_value(data) + + is_formula = self.parent.data.get(self.is_formula_field_name, False) + if not is_formula: + data["mode"] = BASEROW_FORMULA_MODE_RAW + + return data diff --git a/backend/src/baserow/contrib/builder/elements/collection_field_types.py b/backend/src/baserow/contrib/builder/elements/collection_field_types.py index 3fcf251e4e..1d372c78ee 100644 --- a/backend/src/baserow/contrib/builder/elements/collection_field_types.py +++ b/backend/src/baserow/contrib/builder/elements/collection_field_types.py @@ -9,10 +9,7 @@ from baserow.contrib.builder.elements.registries import CollectionFieldType from baserow.contrib.builder.workflow_actions.models import BuilderWorkflowAction from baserow.core.constants import RatingStyleChoices -from baserow.core.formula.serializers import ( - FormulaSerializerField, - OptionalFormulaSerializerField, -) +from baserow.core.formula.serializers import FormulaSerializerField from baserow.core.formula.types import BaserowFormulaObject from baserow.core.registry import Instance @@ -236,12 +233,16 @@ class SerializedDict(TypedDict): @property def serializer_field_overrides(self): + from baserow.contrib.builder.api.elements.serializers import ( + CollectionFieldOptionalFormulaSerializerField, + ) + return { "values": FormulaSerializerField( help_text="The formula for the tags values", required=False, ), - "colors": OptionalFormulaSerializerField( + "colors": CollectionFieldOptionalFormulaSerializerField( help_text="The formula or value for the tags colors", required=False, is_formula_field_name="colors_is_formula", diff --git a/backend/src/baserow/core/formula/field.py b/backend/src/baserow/core/formula/field.py index 988f41cc4d..0b97ddd102 100644 --- a/backend/src/baserow/core/formula/field.py +++ b/backend/src/baserow/core/formula/field.py @@ -269,7 +269,7 @@ def _transform_db_property( :return: A `BaserowFormulaObject`. """ - if isinstance(value, str): + if not isinstance(value, dict): return BaserowFormulaObject( mode=BASEROW_FORMULA_MODE_SIMPLE, version=BASEROW_FORMULA_VERSION_INITIAL, @@ -390,7 +390,7 @@ def _transform_python_property( :return: A `BaserowFormulaMinified`. """ - if isinstance(value, str): + if not isinstance(value, dict): return BaserowFormulaMinified( m=BASEROW_FORMULA_MODE_SIMPLE, v=BASEROW_FORMULA_VERSION_INITIAL, diff --git a/backend/src/baserow/core/formula/serializers.py b/backend/src/baserow/core/formula/serializers.py index 4695fc207c..cdf3729e1c 100644 --- a/backend/src/baserow/core/formula/serializers.py +++ b/backend/src/baserow/core/formula/serializers.py @@ -109,7 +109,7 @@ def to_internal_value(self, data: Union[str, Dict[str, str]]): mode=BASEROW_FORMULA_MODE_SIMPLE, ) - if not data["formula"]: + if not data["formula"] or data["mode"] == BASEROW_FORMULA_MODE_RAW: return data try: diff --git a/web-frontend/modules/builder/collectionFieldTypes.js b/web-frontend/modules/builder/collectionFieldTypes.js index dfe0e33f35..451c641dc3 100644 --- a/web-frontend/modules/builder/collectionFieldTypes.js +++ b/web-frontend/modules/builder/collectionFieldTypes.js @@ -247,9 +247,9 @@ export class TagsCollectionFieldType extends CollectionFieldType { getProps(field, { resolveFormula, applicationContext }) { const values = ensureArray(resolveFormula(field.values)) - const colors = field.colors_is_formula - ? ensureArray(resolveFormula(field.colors)) - : [field.colors] + + const colors = ensureArray(resolveFormula(field.colors)) + const tags = values.map((value, index) => ({ value, color: colors[index % colors.length], diff --git a/web-frontend/modules/builder/components/elements/components/collectionField/form/TagsFieldForm.vue b/web-frontend/modules/builder/components/elements/components/collectionField/form/TagsFieldForm.vue index 21b2c72427..65f2c69142 100644 --- a/web-frontend/modules/builder/components/elements/components/collectionField/form/TagsFieldForm.vue +++ b/web-frontend/modules/builder/components/elements/components/collectionField/form/TagsFieldForm.vue @@ -52,7 +52,7 @@ class="margin-bottom-2" > @@ -82,20 +82,43 @@ export default { allowedValues: ['values', 'colors', 'colors_is_formula', 'styles'], values: { values: {}, - colors: '#acc8f8', + colors: { + formula: '#acc8f8', + mode: 'raw', + }, colors_is_formula: false, styles: {}, }, } }, + computed: { + rawFormula: { + get() { + return this.values.colors.formula + }, + set(newValue) { + this.values.colors = { + ...this.values.colors, + formula: newValue, + mode: 'raw', + } + }, + }, + }, methods: { setColorsToFormula() { this.values.colors_is_formula = true - this.values.colors = `'${this.values.colors}'` + this.values.colors = { + formula: `'${this.values.colors.formula}'`, + mode: 'simple', + } }, setColorsToPicker() { this.values.colors_is_formula = false - this.values.colors = '#acc8f8' + this.values.colors = { + formula: '#acc8f8', + mode: 'raw', + } }, }, } diff --git a/web-frontend/modules/core/formula/index.js b/web-frontend/modules/core/formula/index.js index 8a0cdfdd17..5dc6450a66 100644 --- a/web-frontend/modules/core/formula/index.js +++ b/web-frontend/modules/core/formula/index.js @@ -19,6 +19,11 @@ export const resolveFormula = ( return '' } + if (formulaCtx.mode === 'raw') { + // We don't need to resolve the formula for raw mode. + return formulaCtx.formula + } + try { const tree = parseBaserowFormula(formulaCtx.formula) return new JavascriptExecutor(functions, RuntimeFormulaContext).visit(tree)