From dec94baf4ddffc8b09c06a5b01c60088e4a94d40 Mon Sep 17 00:00:00 2001 From: Dmitry Kremnev Date: Tue, 3 Mar 2026 18:03:57 +0400 Subject: [PATCH] Make operation list configurable in PropertyFilter jmix-framework/jmix_5087 --- .../kit/meta/component/StudioComponents.java | 6 +++ .../kit/meta/element/StudioElements.java | 10 ++++- .../propertyfilter/PropertyFilter.java | 42 +++++++++++++++++++ .../component/PropertyFilterLoader.java | 20 +++++++++ .../resources/io/jmix/flowui/view/layout.xsd | 31 ++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) diff --git a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/component/StudioComponents.java b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/component/StudioComponents.java index d06cbd252b..a2730b1434 100644 --- a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/component/StudioComponents.java +++ b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/component/StudioComponents.java @@ -3164,6 +3164,12 @@ interface StudioComponents { "STARTS_WITH", "ENDS_WITH", "IS_SET", "IN_LIST", "NOT_IN_LIST", "IN_INTERVAL", "DATE_EQUALS", "IS_COLLECTION_EMPTY", "MEMBER_OF_COLLECTION", "NOT_MEMBER_OF_COLLECTION"}, required = true), + @StudioProperty(xmlAttribute = "operationsList", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.VALUES_LIST, + options = {"EQUAL", "NOT_EQUAL", "GREATER", + "GREATER_OR_EQUAL", "LESS", "LESS_OR_EQUAL", "CONTAINS", "NOT_CONTAINS", + "STARTS_WITH", "ENDS_WITH", "IS_SET", "IN_LIST", "NOT_IN_LIST", "IN_INTERVAL", + "DATE_EQUALS", "IS_COLLECTION_EMPTY", "MEMBER_OF_COLLECTION", + "NOT_MEMBER_OF_COLLECTION"}), @StudioProperty(xmlAttribute = "operationEditable", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN, defaultValue = "false"), @StudioProperty(xmlAttribute = "operationTextVisible", category = StudioProperty.Category.LOOK_AND_FEEL, type = StudioPropertyType.BOOLEAN, diff --git a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioElements.java b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioElements.java index 1c266cba99..695e7b42a1 100644 --- a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioElements.java +++ b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioElements.java @@ -907,13 +907,19 @@ interface StudioElements { @StudioProperty(xmlAttribute = "label", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.LOCALIZED_STRING), @StudioProperty(xmlAttribute = "labelVisible", category = StudioProperty.Category.LOOK_AND_FEEL, type = StudioPropertyType.BOOLEAN, defaultValue = "true"), - @StudioProperty(xmlAttribute = "operation", type = StudioPropertyType.ENUMERATION, + @StudioProperty(xmlAttribute = "operation", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.ENUMERATION, classFqn = "io.jmix.flowui.component.propertyfilter.PropertyFilter$Operation", options = {"EQUAL", "NOT_EQUAL", "GREATER", "GREATER_OR_EQUAL", "LESS", "LESS_OR_EQUAL", "CONTAINS", "NOT_CONTAINS", "STARTS_WITH", "ENDS_WITH", "IS_SET", "IN_LIST", "NOT_IN_LIST", "IN_INTERVAL", - "IS_COLLECTION_EMPTY", "MEMBER_OF_COLLECTION", + "DATE_EQUALS", "IS_COLLECTION_EMPTY", "MEMBER_OF_COLLECTION", "NOT_MEMBER_OF_COLLECTION"}, required = true), + @StudioProperty(xmlAttribute = "operationsList", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.VALUES_LIST, + options = {"EQUAL", "NOT_EQUAL", "GREATER", + "GREATER_OR_EQUAL", "LESS", "LESS_OR_EQUAL", "CONTAINS", "NOT_CONTAINS", + "STARTS_WITH", "ENDS_WITH", "IS_SET", "IN_LIST", "NOT_IN_LIST", "IN_INTERVAL", + "DATE_EQUALS", "IS_COLLECTION_EMPTY", "MEMBER_OF_COLLECTION", + "NOT_MEMBER_OF_COLLECTION"}), @StudioProperty(xmlAttribute = "operationEditable", type = StudioPropertyType.BOOLEAN, defaultValue = "false"), @StudioProperty(xmlAttribute = "operationTextVisible", type = StudioPropertyType.BOOLEAN, diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/propertyfilter/PropertyFilter.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/propertyfilter/PropertyFilter.java index 1e10c3e724..093e9a1a83 100644 --- a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/propertyfilter/PropertyFilter.java +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/propertyfilter/PropertyFilter.java @@ -36,9 +36,12 @@ import io.micrometer.observation.Observation; import org.springframework.lang.Nullable; +import java.util.EnumSet; +import java.util.List; import java.util.Objects; import java.util.function.BiConsumer; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static io.jmix.core.common.util.Preconditions.checkNotNullArgument; @@ -61,6 +64,7 @@ public class PropertyFilter extends SingleFilterComponentBase { protected DropdownButton operationSelector; protected Operation operation; + protected List operationsList; protected boolean operationEditable = false; protected boolean operationTextVisible = true; @@ -103,6 +107,10 @@ && getProperty() != null) { MetaClass metaClass = dataLoader.getContainer().getEntityMetaClass(); for (Operation operation : propertyFilterSupport.getAvailableOperations(metaClass, getProperty())) { + if (operationsList != null && !operationsList.contains(operation)) { + continue; + } + OperationChangeAction action = new OperationChangeAction(operation, this::setOperationInternal); action.setText(getOperationText(operation)); operationSelector.addItem(operation.name(), action); @@ -160,6 +168,28 @@ public Operation getOperation() { return operation; } + /** + * @return a list of available operations + */ + @Nullable + public List getOperationsList() { + return operationsList; + } + + /** + * Sets a list of available operations. + * + * @param operationsList a list of available operations + */ + public void setOperationsList(@Nullable List operationsList) { + this.operationsList = operationsList; + + if (operationSelector != null) { + operationSelector.removeAll(); + initOperationSelectorActions(operationSelector); + } + } + /** * Sets a filtering operation. * @@ -176,6 +206,18 @@ protected void setOperationInternal(Operation operation, boolean fromClient) { return; } + if (dataLoader != null && getProperty() != null) { + MetaClass metaClass = dataLoader.getContainer().getEntityMetaClass(); + EnumSet availableOperations = propertyFilterSupport.getAvailableOperations(metaClass, getProperty()); + checkArgument(availableOperations.contains(operation), + "Operation '%s' is not available for property '%s'", operation.name(), getProperty()); + + if (operationsList != null) { + checkArgument(operationsList.contains(operation), + "Operation '%s' is not in operationsList", operation.name()); + } + } + getQueryCondition().setOperation(propertyFilterSupport.toPropertyConditionOperation(operation)); if (operationSelector != null) { diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/xml/layout/loader/component/PropertyFilterLoader.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/xml/layout/loader/component/PropertyFilterLoader.java index a7aabb3309..d1864b7e69 100644 --- a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/xml/layout/loader/component/PropertyFilterLoader.java +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/xml/layout/loader/component/PropertyFilterLoader.java @@ -16,6 +16,7 @@ package io.jmix.flowui.xml.layout.loader.component; +import com.google.common.base.Strings; import com.vaadin.flow.component.Component; import io.jmix.core.MetadataTools; import io.jmix.core.metamodel.model.MetaClass; @@ -26,6 +27,7 @@ import io.jmix.flowui.exception.GuiDevelopmentException; import org.dom4j.Element; +import java.util.Arrays; import java.util.List; import static io.jmix.core.querycondition.PropertyConditionUtils.generateParameterName; @@ -49,6 +51,7 @@ protected void loadAttributesBeforeValueComponent() { super.loadAttributesBeforeValueComponent(); loadString(element, "property", resultComponent::setProperty); + loadOperationsList(resultComponent, element); loadEnum(element, Operation.class, "operation", resultComponent::setOperation); loadBoolean(element, "operationEditable", resultComponent::setOperationEditable); loadBoolean(element, "operationTextVisible", resultComponent::setOperationTextVisible); @@ -73,6 +76,17 @@ protected Component generateValueComponent() { resultComponent.getProperty(), resultComponent.getOperation())); } + protected void loadOperationsList(PropertyFilter resultComponent, Element element) { + loadString(element, "operationsList") + .map(list -> + split(list) + .stream() + .map(Operation::valueOf) + .toList() + ) + .ifPresent(resultComponent::setOperationsList); + } + @SuppressWarnings({"unchecked", "rawtypes"}) protected void loadDefaultValue(PropertyFilter component, Element element) { loadString(element, "defaultValue") @@ -119,4 +133,10 @@ protected MetadataTools getMetadataTools() { protected boolean isValueComponent(Element subElement) { return !"tooltip".equals(subElement.getName()); } + + protected List split(String names) { + return Arrays.stream(names.split("[\\s,]+")) + .filter(split -> !Strings.isNullOrEmpty(split)) + .toList(); + } } diff --git a/jmix-flowui/flowui/src/main/resources/io/jmix/flowui/view/layout.xsd b/jmix-flowui/flowui/src/main/resources/io/jmix/flowui/view/layout.xsd index 6a0c9baac9..5607a20aab 100644 --- a/jmix-flowui/flowui/src/main/resources/io/jmix/flowui/view/layout.xsd +++ b/jmix-flowui/flowui/src/main/resources/io/jmix/flowui/view/layout.xsd @@ -1852,6 +1852,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5241,6 +5271,7 @@ +