diff --git a/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector+Controller.swift b/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector+Controller.swift index fc941f2f..486432a9 100644 --- a/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector+Controller.swift +++ b/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector+Controller.swift @@ -14,19 +14,22 @@ public extension DynamicFacetListConnector { - filterState: FilterState that holds your filters - interactor: External dynamic facet list interactor - filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - controller: Controller presenting the ordered list of facets and handling the user interaction - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the `defaultFilterGroupType` group with the facet attribute name. */ convenience init(searcher: Searcher, filterState: FilterState = .init(), interactor: DynamicFacetListInteractor = .init(), filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], + defaultFilterGroupType: RefinementOperator = .and, controller: Controller) { self.init(searcher: searcher, filterState: filterState, interactor: interactor, - filterGroupForAttribute: filterGroupForAttribute) + filterGroupForAttribute: filterGroupForAttribute, + defaultFilterGroupType: defaultFilterGroupType) connectController(controller) } @@ -36,26 +39,32 @@ public extension DynamicFacetListConnector { - filterState: FilterState that holds your filters - orderedFacets: Ordered list of attributed facets - selections: Mapping between a facet attribute and a set of selected facet values. - - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is .single. + - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is `defaultSelectionMode`. + - defaultSelectionMode: Selection mode to apply for a facet list. Default value is `single`. - filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - controller: Controller presenting the ordered list of facets and handling the user interaction - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the `defaultFilterGroupType` group with the facet attribute name. */ convenience init(searcher: Searcher, filterState: FilterState = .init(), orderedFacets: [AttributedFacets] = [], selections: [Attribute: Set] = [:], selectionModeForAttribute: [Attribute: SelectionMode] = [:], + defaultSelectionMode: SelectionMode = .single, filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], + defaultFilterGroupType: RefinementOperator = .and, controller: Controller) { let interactor = DynamicFacetListInteractor(orderedFacets: orderedFacets, selections: selections, - selectionModeForAttribute: selectionModeForAttribute) + selectionModeForAttribute: selectionModeForAttribute, + defaultSelectionMode: defaultSelectionMode) self.init(searcher: searcher, filterState: filterState, interactor: interactor, - filterGroupForAttribute: filterGroupForAttribute) + filterGroupForAttribute: filterGroupForAttribute, + defaultFilterGroupType: defaultFilterGroupType) connectController(controller) } diff --git a/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector.swift b/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector.swift index f207fed8..6235257c 100644 --- a/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector.swift +++ b/Sources/InstantSearchCore/DynamicFacets/Connector/DynamicFacetListConnector.swift @@ -34,20 +34,23 @@ public class DynamicFacetListConnector where S - filterState: FilterState that holds your filters - interactor: External dynamic facet list interactor - filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the group of `defaultFilterGroupType` with the facet attribute name. */ public init(searcher: Searcher, filterState: FilterState = .init(), interactor: DynamicFacetListInteractor, - filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:]) { + filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], + defaultFilterGroupType: RefinementOperator = .and) { self.searcher = searcher self.filterState = filterState self.interactor = interactor controllerConnections = [] searcherConnection = interactor.connectSearcher(searcher) filterStateConnection = interactor.connectFilterState(filterState, - filterGroupForAttribute: filterGroupForAttribute) + filterGroupForAttribute: filterGroupForAttribute, + defaultFilterGroupType: defaultFilterGroupType) Telemetry.shared.traceConnector(type: .dynamicFacets, parameters: [ filterGroupForAttribute.isEmpty ? .none : .filterGroupForAttribute @@ -60,24 +63,30 @@ public class DynamicFacetListConnector where S - filterState: FilterState that holds your filters - orderedFacets: Ordered list of attributed facets - selections: Mapping between a facet attribute and a set of selected facet values - - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is .single. + - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is `defaultSelectionMode`. + - defaultSelectionMode: Selection mode to apply for a facet list. Default value is `single`. - filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the `defaultFilterGroupType` group with the facet attribute name. */ public convenience init(searcher: Searcher, filterState: FilterState = .init(), orderedFacets: [AttributedFacets] = [], selections: [Attribute: Set] = [:], selectionModeForAttribute: [Attribute: SelectionMode] = [:], - filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:]) { + defaultSelectionMode: SelectionMode = .single, + filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], + defaultFilterGroupType: RefinementOperator = .and) { let interactor = DynamicFacetListInteractor(orderedFacets: orderedFacets, selections: selections, - selectionModeForAttribute: selectionModeForAttribute) + selectionModeForAttribute: selectionModeForAttribute, + defaultSelectionMode: defaultSelectionMode) self.init(searcher: searcher, filterState: filterState, interactor: interactor, - filterGroupForAttribute: filterGroupForAttribute) + filterGroupForAttribute: filterGroupForAttribute, + defaultFilterGroupType: defaultFilterGroupType) Telemetry.shared.traceConnector(type: .dynamicFacets, parameters: [ orderedFacets.isEmpty ? .none : .orderedFacets, diff --git a/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor+FilterState.swift b/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor+FilterState.swift index 09220c30..ff4aaea6 100644 --- a/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor+FilterState.swift +++ b/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor+FilterState.swift @@ -21,20 +21,26 @@ public extension DynamicFacetListInteractor { /// If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. public let filterGroupForAttribute: [Attribute: FilterGroupDescriptor] + /// Type of filter group created by default for a facet attribute. Default value is `and` + public let defaultFilterGroupType: RefinementOperator + /** - parameters: - interactor: Dynamic facet list business logic - filterState: FilterState that holds your filters - filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name`. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the group of `defaultFilterGroupType` with the facet attribute name. */ public init(interactor: DynamicFacetListInteractor, filterState: FilterState, - filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:]) { + filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], + defaultFilterGroupType: RefinementOperator = .and) { self.interactor = interactor self.filterState = filterState self.filterGroupForAttribute = filterGroupForAttribute + self.defaultFilterGroupType = defaultFilterGroupType } public func connect() { @@ -48,7 +54,7 @@ public extension DynamicFacetListInteractor { } private func groupID(for attribute: Attribute) -> FilterGroup.ID { - let (groupName, refinementOperator) = filterGroupForAttribute[attribute] ?? (attribute.rawValue, .and) + let (groupName, refinementOperator) = filterGroupForAttribute[attribute] ?? (attribute.rawValue, defaultFilterGroupType) switch refinementOperator { case .or: return .or(name: groupName, filterType: .facet) @@ -91,14 +97,16 @@ public extension DynamicFacetListInteractor { Establishes connection with a FilterState - parameter filterState: filter state to connect - parameter filterGroupForAttribute: Mapping between a facet attribute and a descriptor of a filter group where the corresponding facet filters stored in the filter state. + - parameter defaultFilterGroupType: Type of filter group created by default for a facet attribute. Default value is `and`. - If no filter group descriptor provided, the filters for attribute will be automatically stored in the conjunctive (`and`) group with the facet attribute name. + If no filter group descriptor provided, the filters for attribute will be automatically stored in the group of `defaultFilterGroupType` with the facet attribute name. */ @discardableResult func connectFilterState(_ filterState: FilterState, - filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:]) -> FilterStateConnection { + filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:], defaultFilterGroupType: RefinementOperator = .and) -> FilterStateConnection { let connection = FilterStateConnection(interactor: self, filterState: filterState, - filterGroupForAttribute: filterGroupForAttribute) + filterGroupForAttribute: filterGroupForAttribute, + defaultFilterGroupType: defaultFilterGroupType) connection.connect() return connection } diff --git a/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor.swift b/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor.swift index 2f2b781a..81084185 100644 --- a/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor.swift +++ b/Sources/InstantSearchCore/DynamicFacets/DynamicFacetListInteractor.swift @@ -43,8 +43,11 @@ public class DynamicFacetListInteractor { /// Event triggered when the facets values selection changed by the business logic public let onSelectionsComputed: Observer + /// Selection mode to apply for a facet list. Default value is `single`. + public var defaultSelectionMode: SelectionMode + /// Mapping between a facet attribute and a facet values selection mode - /// If not provided, the default selection mode is `.single` + /// If not provided, the default selection mode is `defaultSelectionMode` public let selectionModeForAttribute: [Attribute: SelectionMode] /// Storage for selectable facet list logic per attribute @@ -54,11 +57,14 @@ public class DynamicFacetListInteractor { - Parameters: - orderedFacets: Ordered list of attributed facets - selections: Mapping between a facet attribute and a set of selected facet values - - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is .single. + - selectionModeForAttribute: Mapping between a facet attribute and a facet values selection mode. If not provided, the default selection mode is `defaultSelectionMode`. + - defaultSelectionMode: Selection mode to apply for a facet list. Default value is `single`. + */ public init(orderedFacets: [AttributedFacets] = [], selections: [Attribute: Set] = [:], - selectionModeForAttribute: [Attribute: SelectionMode] = [:]) { + selectionModeForAttribute: [Attribute: SelectionMode] = [:], + defaultSelectionMode: SelectionMode = .single) { self.orderedFacets = orderedFacets self.selections = selections onFacetOrderChanged = .init() @@ -66,6 +72,7 @@ public class DynamicFacetListInteractor { onSelectionsComputed = .init() self.selectionModeForAttribute = selectionModeForAttribute facetListPerAttribute = [:] + self.defaultSelectionMode = defaultSelectionMode onFacetOrderChanged.fire(orderedFacets) onSelectionsChanged.fire(selections) updateInteractors() @@ -118,7 +125,7 @@ public class DynamicFacetListInteractor { } private func createFacetList(for attribute: Attribute) -> SelectableListInteractor { - let selectionMode = selectionModeForAttribute[attribute] ?? .single + let selectionMode = selectionModeForAttribute[attribute] ?? defaultSelectionMode let facetList = SelectableListInteractor(selectionMode: selectionMode) facetList.onSelectionsComputed.subscribe(with: self) { interactor, selections in