diff --git a/projects/components/src/multi-select/multi-select.component.scss b/projects/components/src/multi-select/multi-select.component.scss index a3ccc2032..63e5ce5f7 100644 --- a/projects/components/src/multi-select/multi-select.component.scss +++ b/projects/components/src/multi-select/multi-select.component.scss @@ -35,7 +35,7 @@ } &.extra-small { - height: 30px; + height: 24px; } &.small { @@ -98,6 +98,10 @@ &:hover { background-color: $gray-1; } + + &.menu-with-background { + background-color: $gray-1; + } } } diff --git a/projects/components/src/multi-select/multi-select.component.ts b/projects/components/src/multi-select/multi-select.component.ts index 93b578604..fb6666335 100644 --- a/projects/components/src/multi-select/multi-select.component.ts +++ b/projects/components/src/multi-select/multi-select.component.ts @@ -20,6 +20,7 @@ import { IconSize } from '../icon/icon-size'; import { SearchBoxDisplayMode } from '../search-box/search-box.component'; import { SelectOptionComponent } from '../select/select-option.component'; import { SelectSize } from '../select/select-size'; +import { SelectTriggerDisplayMode } from '../select/select.component'; import { MultiSelectJustify } from './multi-select-justify'; @Component({ selector: 'ht-multi-select', @@ -57,7 +58,8 @@ import { MultiSelectJustify } from './multi-select-justify'; this.triggerLabelDisplayMode, this.popoverOpen ? 'open' : '', this.size, - this.disabled ? 'disabled' : '' + this.disabled ? 'disabled' : '', + this.triggerDisplayMode ]" #triggerContainer > @@ -171,6 +173,9 @@ export class MultiSelectComponent implements ControlValueAccessor, AfterConte @Input() public justify: MultiSelectJustify = MultiSelectJustify.Left; + @Input() + public triggerDisplayMode?: SelectTriggerDisplayMode = SelectTriggerDisplayMode.MenuWithBorder; + @Input() public triggerLabelDisplayMode: TriggerLabelDisplayMode = TriggerLabelDisplayMode.Selection; @@ -251,7 +256,7 @@ export class MultiSelectComponent implements ControlValueAccessor, AfterConte } public isSelectedItem(item: SelectOptionComponent): boolean { - return this.selected !== undefined && this.selected.filter(value => isEqual(value, item.value)).length > 0; + return Array.isArray(this.selected) && this.selected.filter(value => isEqual(value, item.value)).length > 0; } public preventClickDefault(event: Event): void { diff --git a/projects/components/src/select/select.component.scss b/projects/components/src/select/select.component.scss index 5d40321ca..56641f030 100644 --- a/projects/components/src/select/select.component.scss +++ b/projects/components/src/select/select.component.scss @@ -18,6 +18,10 @@ width: 100%; height: 36px; + &.extra-small { + height: 24px; + } + &.small { height: 32px; } @@ -64,7 +68,7 @@ .trigger-content { display: flex; align-items: center; - padding: 0 8px; + padding-left: 8px; height: 100%; &.menu-with-border { @@ -78,7 +82,7 @@ border-radius: 4px; .trigger-label { - @include body-1-medium($gray-9); + @include body-2-medium(); } .trigger-icon { @@ -154,6 +158,8 @@ .select-content { @include dropdown(); max-height: 204px; + display: flex; + flex-direction: column; .select-option { display: flex; @@ -201,3 +207,16 @@ } } } + +.search-bar { + display: flex; + height: 34px; + margin-top: 2px; + cursor: pointer; + font-size: 14px; + align-items: center; +} + +.divider { + padding: 0 16px; +} diff --git a/projects/components/src/select/select.component.ts b/projects/components/src/select/select.component.ts index db65d2a0e..6f48aa348 100644 --- a/projects/components/src/select/select.component.ts +++ b/projects/components/src/select/select.component.ts @@ -13,9 +13,11 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { IconType } from '@hypertrace/assets-library'; import { LoggerService, queryListAndChanges$, SubscriptionLifecycle, TypedSimpleChanges } from '@hypertrace/common'; +import { isEqual } from 'lodash-es'; import { EMPTY, merge, Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { IconSize } from '../icon/icon-size'; +import { SearchBoxDisplayMode } from '../search-box/search-box.component'; import { SelectControlOptionComponent, SelectControlOptionPosition } from './select-control-option.component'; import { SelectGroupPosition } from './select-group-position'; import { SelectJustify } from './select-justify'; @@ -115,12 +117,23 @@ import { SelectSize } from './select-size'; > - +
+ + + + + +
implements ControlValueAccessor, AfterContentIni @Input() public highlightSelected: boolean = true; + @Input() + public searchMode: SelectSearchMode = SelectSearchMode.Disabled; + @Output() public readonly selectedChange: EventEmitter = new EventEmitter(); + @Output() + public readonly searchValueChange: EventEmitter = new EventEmitter(); + @ContentChildren(SelectOptionComponent) public items?: QueryList>; @@ -258,7 +277,7 @@ export class SelectComponent implements ControlValueAccessor, AfterContentIni } public isSelectedItem(item: SelectOptionComponent): boolean { - return this.selected === item.value; + return isEqual(this.selected, item.value); } public updateGroupPosition(position: SelectGroupPosition): void { @@ -266,6 +285,14 @@ export class SelectComponent implements ControlValueAccessor, AfterContentIni this.changeDetector.markForCheck(); } + public searchOptions(searchText: string): void { + if (this.searchMode === SelectSearchMode.Disabled) { + return; + } + + this.searchValueChange.emit(searchText); + } + private buildObservableOfSelected(): Observable | undefined> { if (!this.items) { return EMPTY; @@ -339,3 +366,8 @@ export const enum SelectTriggerDisplayMode { MenuWithBackground = 'menu-with-background', Icon = 'icon' } + +export const enum SelectSearchMode { + Disabled = 'disabled', // Search is not available + EmitOnly = 'emit-only' // Current available values not filtered, but an emit still triggered +} diff --git a/projects/components/src/select/select.module.ts b/projects/components/src/select/select.module.ts index 90bea359c..cb0b8b984 100644 --- a/projects/components/src/select/select.module.ts +++ b/projects/components/src/select/select.module.ts @@ -3,10 +3,12 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MemoizeModule } from '@hypertrace/common'; import { DividerModule } from '../divider/divider.module'; +import { EventBlockerModule } from '../event-blocker/event-blocker.module'; import { IconModule } from '../icon/icon.module'; import { LabelModule } from '../label/label.module'; import { LetAsyncModule } from '../let-async/let-async.module'; import { PopoverModule } from '../popover/popover.module'; +import { TraceSearchBoxModule } from '../search-box/search-box.module'; import { TooltipModule } from '../tooltip/tooltip.module'; import { SelectOptionRendererDirective } from './directive/select-option-renderer.directive'; import { SelectControlOptionComponent } from './select-control-option.component'; @@ -24,7 +26,9 @@ import { SelectComponent } from './select.component'; PopoverModule, TooltipModule, DividerModule, - MemoizeModule + MemoizeModule, + TraceSearchBoxModule, + EventBlockerModule ], declarations: [ SelectComponent, diff --git a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts index 81ffdaced..0cdeee5f8 100644 --- a/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts +++ b/projects/observability/src/shared/services/filter-builder/graphql-filter-builder.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { assertUnreachable } from '@hypertrace/common'; -import { FieldFilter, FilterOperator, FilterValue, TableFilter } from '@hypertrace/components'; +import { FieldFilter, Filter, FilterOperator, FilterValue, TableFilter } from '@hypertrace/components'; import { GraphQlArgumentValue } from '@hypertrace/graphql-client'; import { GraphQlFieldFilter } from '../../graphql/model/schema/filter/field/graphql-field-filter'; import { GraphQlFilter, GraphQlOperatorType } from '../../graphql/model/schema/filter/graphql-filter'; @@ -20,7 +20,7 @@ export class GraphQlFilterBuilderService { })); } - public buildGraphQlFieldFilters(filters: FieldFilter[]): GraphQlFieldFilter[] { + public buildGraphQlFieldFilters(filters: (Filter | FieldFilter)[]): GraphQlFieldFilter[] { return filters.map( filter => new GraphQlFieldFilter(