diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html
index fc74eb4e9..3cc116171 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html
+++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html
@@ -12,6 +12,16 @@
+
@@ -19,15 +29,18 @@
+
+ Click again to exclude items.
+
diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.scss b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.scss
index 40c93838f..e728ca16b 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.scss
+++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.scss
@@ -1,3 +1,5 @@
+@import "/src/theme";
+
.badge-corner {
position: absolute;
top: -8px;
@@ -12,3 +14,43 @@
overflow-y: scroll;
}
}
+
+.btn-group-xs {
+ > .btn {
+ padding: 0.2rem 0.25rem;
+ font-size: 0.675rem;
+ line-height: 1.2;
+ border-radius: 0.15rem;
+ }
+
+ > .btn:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ > .btn:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
+
+.btn-group > label.disabled {
+ filter: brightness(0.5);
+
+ &.active {
+ background-color: lighten($primary, 30%);
+ }
+}
+
+
+small > svg {
+ margin-top: -2px;
+}
+
+.list-group-item-note {
+ line-height: 1;
+
+ small {
+ font-size: 65%;
+ }
+}
diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
index 6c526faee..413f2fdb6 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
+++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
@@ -15,6 +15,8 @@ export class FilterableDropdownSelectionModel {
changed = new Subject()
multiple = false
+ private _logicalOperator = 'and'
+ temporaryLogicalOperator = this._logicalOperator
items: MatchingModel[] = []
@@ -43,6 +45,10 @@ export class FilterableDropdownSelectionModel {
return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Selected)
}
+ getExcludedItems() {
+ return this.items.filter(i => this.temporarySelectionStates.get(i.id) == ToggleableItemState.Excluded)
+ }
+
set(id: number, state: ToggleableItemState, fireEvent = true) {
if (state == ToggleableItemState.NotSelected) {
this.temporarySelectionStates.delete(id)
@@ -56,9 +62,9 @@ export class FilterableDropdownSelectionModel {
toggle(id: number, fireEvent = true) {
let state = this.temporarySelectionStates.get(id)
- if (state == null || state != ToggleableItemState.Selected) {
+ if (state == null || (state != ToggleableItemState.Selected && state != ToggleableItemState.Excluded)) {
this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
- } else if (state == ToggleableItemState.Selected) {
+ } else if (state == ToggleableItemState.Selected || state == ToggleableItemState.Excluded) {
this.temporarySelectionStates.delete(id)
}
@@ -83,13 +89,46 @@ export class FilterableDropdownSelectionModel {
if (fireEvent) {
this.changed.next(this)
}
+ }
+ exclude(id: number, fireEvent:boolean = true) {
+ let state = this.temporarySelectionStates.get(id)
+ if (state == null || state != ToggleableItemState.Excluded) {
+ this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
+ this.temporaryLogicalOperator = this._logicalOperator = 'and'
+ } else if (state == ToggleableItemState.Excluded) {
+ this.temporarySelectionStates.delete(id)
+ }
+
+ if (!this.multiple) {
+ for (let key of this.temporarySelectionStates.keys()) {
+ if (key != id) {
+ this.temporarySelectionStates.delete(key)
+ }
+ }
+ }
+
+ if (fireEvent) {
+ this.changed.next(this)
+ }
}
private getNonTemporary(id: number) {
return this.selectionStates.get(id) || ToggleableItemState.NotSelected
}
+ get logicalOperator(): string {
+ return this.temporaryLogicalOperator
+ }
+
+ set logicalOperator(operator: string) {
+ this.temporaryLogicalOperator = operator
+ }
+
+ toggleOperator() {
+ this.changed.next(this)
+ }
+
get(id: number) {
return this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
}
@@ -100,6 +139,7 @@ export class FilterableDropdownSelectionModel {
clear(fireEvent = true) {
this.temporarySelectionStates.clear()
+ this.temporaryLogicalOperator = this._logicalOperator = 'and'
if (fireEvent) {
this.changed.next(this)
}
@@ -110,6 +150,8 @@ export class FilterableDropdownSelectionModel {
return true
} else if (!Array.from(this.selectionStates.keys()).every(id => this.selectionStates.get(id) == this.temporarySelectionStates.get(id))) {
return true
+ } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
+ return true
} else {
return false
}
@@ -129,6 +171,7 @@ export class FilterableDropdownSelectionModel {
this.temporarySelectionStates.forEach((value, key) => {
this.selectionStates.set(key, value)
})
+ this._logicalOperator = this.temporaryLogicalOperator
}
reset() {
@@ -228,6 +271,10 @@ export class FilterableDropdownComponent {
@Output()
open = new EventEmitter()
+ get operatorToggleEnabled(): boolean {
+ return this.selectionModel.selectionSize() > 1 && this.selectionModel.getExcludedItems().length == 0
+ }
+
constructor(private filterPipe: FilterPipe) {
this.selectionModel = new FilterableDropdownSelectionModel()
}
@@ -269,4 +316,12 @@ export class FilterableDropdownComponent {
}
}
}
+
+ excludeClicked(itemID: number) {
+ if (this.editing) {
+ this.selectionModel.toggle(itemID)
+ } else {
+ this.selectionModel.exclude(itemID)
+ }
+ }
}
diff --git a/src-ui/src/app/components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component.html b/src-ui/src/app/components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component.html
index 8f74297d3..b020a3c34 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component.html
+++ b/src-ui/src/app/components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component.html
@@ -1,4 +1,4 @@
-