diff --git a/src/lib/listing/index.ts b/src/lib/listing/index.ts index 5fa0f36..2c3e281 100644 --- a/src/lib/listing/index.ts +++ b/src/lib/listing/index.ts @@ -12,3 +12,6 @@ export * from './sync-width.directive'; export * from './listing.module'; export * from './listing-component.directive'; + +export * from './page-header/page-header.component'; +export * from './page-header/models'; diff --git a/src/lib/listing/listing.module.ts b/src/lib/listing/listing.module.ts index 3b347db..1a60ca1 100644 --- a/src/lib/listing/listing.module.ts +++ b/src/lib/listing/listing.module.ts @@ -16,15 +16,25 @@ import { RouterModule } from '@angular/router'; import { IqserEmptyStatesModule } from '../empty-states'; import { WorkflowComponent } from './workflow/workflow.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { PageHeaderComponent } from './page-header/page-header.component'; +import { IqserButtonsModule } from '../buttons'; const matModules = [MatTooltipModule]; -const components = [TableHeaderComponent, TableComponent, WorkflowComponent, TableColumnNameComponent, ScrollButtonComponent]; +const components = [ + TableHeaderComponent, + TableComponent, + WorkflowComponent, + TableColumnNameComponent, + ScrollButtonComponent, + PageHeaderComponent +]; const modules = [ DragDropModule, TranslateModule, IqserFiltersModule, IqserInputsModule, IqserIconsModule, + IqserButtonsModule, IqserScrollbarModule, IqserEmptyStatesModule, ScrollingModule, diff --git a/src/lib/listing/page-header/models/action-config.model.ts b/src/lib/listing/page-header/models/action-config.model.ts new file mode 100644 index 0000000..311e3fc --- /dev/null +++ b/src/lib/listing/page-header/models/action-config.model.ts @@ -0,0 +1,5 @@ +import { BaseHeaderConfig } from './base-config.model'; + +export interface ActionConfig extends BaseHeaderConfig { + readonly action: ($event: MouseEvent) => void; +} diff --git a/src/lib/listing/page-header/models/base-config.model.ts b/src/lib/listing/page-header/models/base-config.model.ts new file mode 100644 index 0000000..970891c --- /dev/null +++ b/src/lib/listing/page-header/models/base-config.model.ts @@ -0,0 +1,5 @@ +export interface BaseHeaderConfig { + readonly label: string; + readonly icon?: string; + readonly hide?: boolean; +} diff --git a/src/lib/listing/page-header/models/button-config.model.ts b/src/lib/listing/page-header/models/button-config.model.ts new file mode 100644 index 0000000..43ff455 --- /dev/null +++ b/src/lib/listing/page-header/models/button-config.model.ts @@ -0,0 +1,6 @@ +import { IconButtonType } from '@iqser/common-ui'; +import { ActionConfig } from './action-config.model'; + +export interface ButtonConfig extends ActionConfig { + readonly type?: IconButtonType; +} diff --git a/src/lib/listing/page-header/models/index.ts b/src/lib/listing/page-header/models/index.ts new file mode 100644 index 0000000..817ec0f --- /dev/null +++ b/src/lib/listing/page-header/models/index.ts @@ -0,0 +1,4 @@ +export * from './action-config.model'; +export * from './base-config.model'; +export * from './button-config.model'; +export * from './search-positions.type'; diff --git a/src/lib/listing/page-header/models/search-positions.type.ts b/src/lib/listing/page-header/models/search-positions.type.ts new file mode 100644 index 0000000..2b6314c --- /dev/null +++ b/src/lib/listing/page-header/models/search-positions.type.ts @@ -0,0 +1,6 @@ +export const SearchPositions = { + beforeFilters: 'beforeFilters', + afterFilters: 'afterFilters' +} as const; + +export type SearchPosition = keyof typeof SearchPositions; diff --git a/src/lib/listing/page-header/page-header.component.html b/src/lib/listing/page-header/page-header.component.html new file mode 100644 index 0000000..e2e0a75 --- /dev/null +++ b/src/lib/listing/page-header/page-header.component.html @@ -0,0 +1,63 @@ + + + + + diff --git a/src/lib/listing/page-header/page-header.component.scss b/src/lib/listing/page-header/page-header.component.scss new file mode 100644 index 0000000..acbdfdf --- /dev/null +++ b/src/lib/listing/page-header/page-header.component.scss @@ -0,0 +1,3 @@ +.ml-6 { + margin-left: 6px; +} diff --git a/src/lib/listing/page-header/page-header.component.ts b/src/lib/listing/page-header/page-header.component.ts new file mode 100644 index 0000000..18e4437 --- /dev/null +++ b/src/lib/listing/page-header/page-header.component.ts @@ -0,0 +1,54 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Optional, Output, TemplateRef } from '@angular/core'; +import { ActionConfig, ButtonConfig, SearchPosition, SearchPositions } from './models'; +import { distinctUntilChanged, map } from 'rxjs/operators'; +import { combineLatest, Observable, of } from 'rxjs'; +import { IListable } from '../models'; +import { IconButtonTypes } from '../../buttons'; +import { SearchService } from '../../search'; +import { FilterService } from '../../filtering'; + +@Component({ + selector: 'iqser-page-header', + templateUrl: './page-header.component.html', + styleUrls: ['./page-header.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PageHeaderComponent { + readonly searchPositions = SearchPositions; + readonly iconButtonTypes = IconButtonTypes; + + @Input() pageLabel?: string; + @Input() showCloseButton: boolean = false; + @Input() actionConfigs?: readonly ActionConfig[]; + @Input() buttonConfigs?: readonly ButtonConfig[]; + @Input() viewModeSelection?: TemplateRef; + @Input() searchPlaceholder?: string; + @Input() searchWidth?: number | 'full'; + @Input() searchPosition: SearchPosition = SearchPositions.afterFilters; + @Output() readonly closeAction = new EventEmitter(); + + readonly filters$ = this.filterService?.filterGroups$.pipe(map(all => all.filter(f => f.icon))); + readonly showResetFilters$ = this._showResetFilters$; + + constructor(@Optional() readonly filterService: FilterService, @Optional() readonly searchService: SearchService) {} + + get _showResetFilters$(): Observable { + if (!this.filterService) { + return of(false); + } + + return combineLatest([this.filterService.showResetFilters$, this.searchService.valueChanges$]).pipe( + map(([showResetFilters, searchValue]) => showResetFilters || !!searchValue), + distinctUntilChanged() + ); + } + + resetFilters(): void { + this.filterService.reset(); + this.searchService.reset(); + } + + trackByLabel(index: number, item: K): string | undefined { + return item.label; + } +}