diff --git a/angular.json b/angular.json index 68aea8c3909..c8f921d34a5 100644 --- a/angular.json +++ b/angular.json @@ -5159,6 +5159,47 @@ "tags": ["fdp"], "implicitDependencies": ["core"] }, + "platform-smart-filter-bar": { + "projectType": "library", + "root": "libs/platform/src/lib/smart-filter-bar", + "sourceRoot": "libs/platform/src/lib/smart-filter-bar", + "prefix": "fdp", + "architect": { + "build": { + "builder": "@nrwl/angular:package", + "outputs": ["dist/libs/platform/smart-filter-bar"], + "options": { + "tsConfig": "libs/platform/src/lib/smart-filter-bar/tsconfig.lib.json", + "project": "libs/platform/src/lib/smart-filter-bar/ng-package.json", + "updateBuildableProjectDepsInPackageJson": false + }, + "configurations": { + "production": { + "tsConfig": "libs/platform/src/lib/smart-filter-bar/tsconfig.lib.prod.json" + } + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "libs/platform/src/lib/smart-filter-bar/test.ts", + "tsConfig": "libs/platform/src/lib/smart-filter-bar/tsconfig.spec.json", + "karmaConfig": "libs/platform/src/lib/smart-filter-bar/karma.conf.js" + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "libs/platform/src/lib/smart-filter-bar/**/*.ts", + "libs/platform/src/lib/smart-filter-bar/**/*.html" + ] + } + } + }, + "tags": ["fdp"], + "implicitDependencies": ["core"] + }, "platform-split-menu-button": { "projectType": "library", "root": "libs/platform/src/lib/split-menu-button", diff --git a/apps/docs/src/app/platform/api-files.ts b/apps/docs/src/app/platform/api-files.ts index ebf17a3d2b6..56961cf69bc 100644 --- a/apps/docs/src/app/platform/api-files.ts +++ b/apps/docs/src/app/platform/api-files.ts @@ -98,5 +98,15 @@ export const API_FILES = { 'IconTabBarFilterTypeComponent', 'IconTabBarPopoverComponent', 'TextTypePopoverComponent' + ], + smartFilterBar: [ + 'SmartFilterBarComponent', + 'SmartFilterBarConditionFieldComponent', + 'SmartFilterBarConditionsDialogComponent', + 'SmartFilterBarFieldDefinitionDirective', + 'SmartFilterBarService', + 'SmartFilterBarSubjectDirective', + 'SmartFilterBarOptionsDataProvider', + 'SmartFilterBarSettingsDialogComponent' ] }; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.html new file mode 100644 index 00000000000..bfc1cd4efdd --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.html @@ -0,0 +1,107 @@ +Simple example + +

The Smart Filter Bar component analyzes provided data source with the help of two directives:

+ +

+ Default set of selected filters can be defined by using [defaultSelected]="true" input properties + for each fdp-smart-filter-bar-field-definition directive. +

+

+ By default, conditions for one field are combined with or operator. This can be changed by providing + conditionStrategy="and" input property for + fdp-smart-filter-bar-field-definition directive +

+
+ + + + + + + +Custom data provider example + +

+ Smart Filter Bar supports custom data providers that are based on TableDataProvider or + ObservableTableDataProvider. +

+

+ These base classes already implement filtering and searching, yet, developers can override those methods to + achieve desired behaviour. +

+
+ + + + + + + + + Custom filters example + + +

Developers can extend Smart Filter Bar's set of filters by providing their custom components.

+

+ Smart filter bar uses Form Generator component for rendering the forms, so the process of creating custom + filters will be similar to the creation of the custom controls for Form Generator. +

+

Developers can define following custom properties of the filter:

+ +
+ + + + + + + + + Smart filter bar with custom labels + + +

+ Developers can easily change Condition operator and Filter Visibility category labels by providing appropriate + input properties. +

+
+ + + + + + + + + Smart filter bar in Dynamic Page + + +

This example shows how to embed Smart Filter Bar into Dynamic Page component.

+
+ + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.ts new file mode 100644 index 00000000000..46c45d2c516 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-docs.component.ts @@ -0,0 +1,93 @@ +import { Component } from '@angular/core'; +import { ExampleFile } from '../../../documentation/core-helpers/code-example/example-file'; + +import platformBasicSfbSrc from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.html?raw'; +import platformBasicSfbTsCode from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.ts?raw'; + +import platformSfbCustomDataSourceSrc from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.html?raw'; +import platformSfbCustomDataSourceTsCode from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.ts?raw'; + +import platformSfbCustomFiltersSrc from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.html?raw'; +import platformSfbCustomFiltersTsCode from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.ts?raw'; + +import platformSfbCustomLabelsSrc from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.html?raw'; +import platformSfbCustomLabelsTsCode from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.ts?raw'; + +import platformSfbDynamicPageSrc from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.html?raw'; +import platformSfbDynamicPageTsCode from '!./platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.ts?raw'; + +@Component({ + selector: 'app-smart-filter-bar', + templateUrl: './platform-smart-filter-bar-docs.component.html' +}) +export class PlatformSmartFilterBarDocsComponent { + sfbBasic: ExampleFile[] = [ + { + language: 'html', + code: platformBasicSfbSrc, + fileName: 'platform-smart-filter-bar-basic-example' + }, + { + language: 'typescript', + code: platformBasicSfbTsCode, + fileName: 'platform-smart-filter-bar-basic-example', + component: 'PlatformSmartFilterBarBasicExampleComponent' + } + ]; + + sfbCustomDataSource: ExampleFile[] = [ + { + language: 'html', + code: platformSfbCustomDataSourceSrc, + fileName: 'platform-smart-filter-bar-observable-example' + }, + { + language: 'typescript', + code: platformSfbCustomDataSourceTsCode, + fileName: 'platform-smart-filter-bar-observable-example', + component: 'PlatformSmartFilterBarObservableExampleComponent' + } + ]; + + sfbCustomFilters: ExampleFile[] = [ + { + language: 'html', + code: platformSfbCustomFiltersSrc, + fileName: 'platform-smart-filter-bar-custom-filter-example' + }, + { + language: 'typescript', + code: platformSfbCustomFiltersTsCode, + fileName: 'platform-smart-filter-bar-custom-filter-example', + component: 'PlatformSmartFilterBarCustomFilterExampleComponent' + } + ]; + + sfbCustomLabels: ExampleFile[] = [ + { + language: 'html', + code: platformSfbCustomLabelsSrc, + fileName: 'platform-smart-filter-bar-custom-labels-example' + }, + { + language: 'typescript', + code: platformSfbCustomLabelsTsCode, + fileName: 'platform-smart-filter-bar-custom-labels-example', + component: 'PlatformSmartFilterBarCustomLabelsExampleComponent' + } + ]; + + sfbDynamicPage: ExampleFile[] = [ + { + language: 'html', + code: platformSfbDynamicPageSrc, + fileName: 'platform-smart-filter-bar-dynamic-page-example' + }, + { + language: 'typescript', + code: platformSfbDynamicPageTsCode, + fileName: 'platform-smart-filter-bar-dynamic-page-example', + component: 'PlatformSmartFilterBarDynamicPageExampleComponent' + } + ]; +} diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.html new file mode 100644 index 00000000000..adeb551bea4 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.html @@ -0,0 +1,88 @@ + +
+

Line Items

+
+ + + + + + + + + + + + + + + + + + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.ts new file mode 100644 index 00000000000..f2f2cbe9f71 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component.ts @@ -0,0 +1,238 @@ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { FdDate } from '@fundamental-ngx/core/datetime'; + +@Component({ + selector: 'fdp-platform-smart-filter-bar-basic-example', + templateUrl: './platform-smart-filter-bar-basic-example.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PlatformSmartFilterBarBasicExampleComponent { + source: ExampleItem[] = ITEMS; + + trackBy(_: number, item: ExampleItem): number { + return item.id; + } +} + +export interface ExampleItem { + id: number; + name: string; + description: string; + price: { + value: number; + currency: string; + }; + status: string; + statusColor?: string; + date: FdDate; + verified: boolean; +} + +// Example items +const ITEMS: ExampleItem[] = [ + { + id: 1, + name: '10 Portable DVD player', + description: 'diam neque vestibulum eget vulputate', + price: { + value: 66.04, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 7), + verified: true + }, + { + id: 2, + name: 'Astro Laptop 1516', + description: 'pede malesuada', + price: { + value: 489.01, + currency: 'EUR' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 2, 5), + verified: true + }, + { + id: 3, + name: 'Astro Phone 6', + description: 'penatibus et magnis', + price: { + value: 154.1, + currency: 'IDR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 12), + verified: true + }, + { + id: 4, + name: 'Beam Breaker B-1', + description: 'fermentum donec ut', + price: { + value: 36.56, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 11, 24), + verified: false + }, + { + id: 5, + name: 'Beam Breaker B-2', + description: 'sapien in sapien iaculis congue', + price: { + value: 332.57, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 10, 23), + verified: true + }, + { + id: 6, + name: 'Benda Laptop 1408', + description: 'suspendisse potenti cras in', + price: { + value: 243.49, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 9, 22), + verified: true + }, + { + id: 7, + name: 'Bending Screen 21HD', + description: 'nunc nisl duis bibendum', + price: { + value: 66.46, + currency: 'EUR' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 14), + verified: false + }, + { + id: 8, + name: 'Blaster Extreme', + description: 'quisque ut', + price: { + value: 436.88, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 15), + verified: true + }, + { + id: 9, + name: 'Broad Screen 22HD', + description: 'ultrices posuere', + price: { + value: 458.18, + currency: 'CNY' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 4), + verified: true + }, + { + id: 10, + name: 'Camcorder View', + description: 'integer ac leo pellentesque', + price: { + value: 300.52, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 5), + verified: true + }, + { + id: 11, + name: 'Cepat Tablet 10.5', + description: 'rutrum rutrum neque aenean auctor', + price: { + value: 365.12, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 5, 6), + verified: true + }, + { + id: 12, + name: 'Ergo Mousepad', + description: 'tortor duis mattis egestas', + price: { + value: 354.46, + currency: 'EUR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 5, 7), + verified: true + }, + { + id: 13, + name: 'Ergo Screen E-I', + description: 'massa quis augue luctus tincidunt', + price: { + value: 387.23, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 3, 23), + verified: true + }, + { + id: 14, + name: 'Ergo Screen E-II', + description: 'orci eget', + price: { + value: 75.86, + currency: 'EUR' + }, + status: 'No info', + date: new FdDate(2020, 3, 20), + verified: false + }, + { + id: 15, + name: 'Gaming Monster', + description: 'cubilia curae', + price: { + value: 152.95, + currency: 'EGP' + }, + status: 'No info', + date: new FdDate(2020, 9, 20), + verified: false + }, + { + id: 16, + name: 'Gaming Monster Pro', + description: 'pharetra magna vestibulum aliquet', + price: { + value: 213.47, + currency: 'MZN' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 4, 17), + verified: false + } +]; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.html new file mode 100644 index 00000000000..240e9857f38 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.html @@ -0,0 +1,92 @@ + +
+

Line Items

+
+ + + + + + + + + + + + + + + + + + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.ts new file mode 100644 index 00000000000..823fcef0e80 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component.ts @@ -0,0 +1,735 @@ +import { Component } from '@angular/core'; +import { DatetimeAdapter, FdDate } from '@fundamental-ngx/core/datetime'; +import { isSelectItem, SelectItem } from '@fundamental-ngx/platform/shared'; +import { + CollectionBooleanFilter, + CollectionDateFilter, + CollectionFilter, + CollectionFilterAndGroup, + CollectionFilterGroup, + CollectionNumberFilter, + CollectionSelectFilter, + CollectionStringFilter, + isCollectionFilter, + TableDataProvider, + TableDataSource, + TableState +} from '@fundamental-ngx/platform/table'; + +import { + dynamicFormFieldProvider, + dynamicFormGroupChildProvider, + BaseDynamicFormGeneratorControl +} from '@fundamental-ngx/platform/form'; + +import { BehaviorSubject, Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import get from 'lodash-es/get'; +import { + BaseSmartFilterBarConditionField, + SmartFilterBarCondition, + SmartFilterBarCustomFilterConfig, + SmartFilterBarService, + smartFilterBarProvider, + SmartFilterBar +} from '@fundamental-ngx/platform/smart-filter-bar'; +import { DialogService } from '@fundamental-ngx/core/dialog'; + +@Component({ + selector: 'fdp-smart-filter-bar-slider-example', + template: ` + + + + + + `, + viewProviders: [dynamicFormFieldProvider, dynamicFormGroupChildProvider, smartFilterBarProvider] +}) +export class PlatformSmartFilterBarSliderComponent extends BaseDynamicFormGeneratorControl { + constructor() { + super(); + } +} + +@Component({ + selector: 'fdp-smart-filter-bar-date-renderer-example', + template: ` + + + + + + `, + viewProviders: [dynamicFormFieldProvider, dynamicFormGroupChildProvider, smartFilterBarProvider] +}) +export class PlatformSmartFilterBarDateRendererComponent extends BaseSmartFilterBarConditionField { + constructor(dialogService: DialogService, smartFilterBar: SmartFilterBar) { + super(dialogService, smartFilterBar); + } +} + +@Component({ + selector: 'fdp-platform-smart-filter-bar-custom-filter-example', + templateUrl: './platform-smart-filter-bar-custom-filter-example.component.html' +}) +export class PlatformSmartFilterBarCustomFilterExampleComponent { + source: TableDataSource; + + constructor(datetimeAdapter: DatetimeAdapter, private _smartFilterBarService: SmartFilterBarService) { + const sliderConfig: SmartFilterBarCustomFilterConfig = { + conditionComponent: PlatformSmartFilterBarSliderComponent, + types: ['price-slider'], + filterStrategies: [ + 'equalTo', + 'greaterThan', + 'greaterThanOrEqualTo', + 'lessThan', + 'lessThanOrEqualTo', + 'between' + ], + valueTransformer: (filters: SelectItem[]) => { + const transformedFilters = filters?.map((f) => f.value); + transformedFilters?.forEach((f) => { + f.value2 = isSelectItem(f.value2) ? this.getSelectItemValue(f.value2) : f.value2; + f.value = isSelectItem(f.value) ? this.getSelectItemValue(f.value) : f.value; + }); + return transformedFilters; + }, + valueRenderer: (condition: SmartFilterBarCondition) => { + const value1 = condition.value?.value; + const value2 = condition.value2?.value; + + switch (condition.operator) { + case 'equalTo': + return `=${value1}`; + case 'contains': + return `*${value1}*`; + case 'between': + return `${value1}...${value2}`; + case 'beginsWith': + return `${value1}*`; + case 'endsWith': + return `*${value1}`; + case 'greaterThan': + return `>${value1}`; + case 'greaterThanOrEqualTo': + return `>=${value1}`; + case 'lessThan': + return `<${value1}`; + case 'lessThanOrEqualTo': + return `<=${value1}`; + case 'after': + return `>${value1}`; + case 'onOrAfter': + return `>=${value1}`; + case 'before': + return `<${value1}`; + case 'beforeOrOn': + return `<=${value1}`; + default: + return `${value1}`; + } + } + }; + + const dateConfig: SmartFilterBarCustomFilterConfig = { + rendererComponent: PlatformSmartFilterBarDateRendererComponent, + types: ['date-renderer'], + valueTransformer: (value: FdDate) => + !value + ? undefined + : ([ + { + value, + operator: 'equalTo' + } + ] as SmartFilterBarCondition[]) + }; + + this._smartFilterBarService.addCustomFilter(sliderConfig); + this._smartFilterBarService.addCustomFilter(dateConfig); + this.source = new TableDataSource(new TableDataProviderExample(ITEMS, datetimeAdapter)); + } + + trackBy(_: number, item: ExampleItem): number { + return item.id; + } + + getSelectItemValue(item: SelectItem): any { + return item.value; + } +} + +export interface ExampleItem { + id: number; + name: string; + description: string; + price: { + value: number; + currency: string; + }; + status: string; + statusColor?: string; + date: FdDate; + verified: boolean; +} + +/** + * Table Data Provider Example + * + */ +export class TableDataProviderExample extends TableDataProvider { + /** @hidden */ + protected itemsSubject = new BehaviorSubject([]); + protected items$ = this.itemsSubject.asObservable(); + + /** @hidden */ + constructor(items: ExampleItem[], dateTimeAdapter?: DatetimeAdapter) { + super(); + this.items = items; + this.totalItems = this.items.length; + this.itemsSubject.next(this.items); + this.dateTimeAdapter = dateTimeAdapter; + } + + /** + * Method for retrieving the data. + * @param tableState @see TableState Set of table parameters. + * @returns Observable with data. + */ + fetch(state?: TableState): Observable { + return this.items$.pipe( + map((items) => { + if (state?.searchInput) { + items = this.search(items, state); + } + + if (state?.filterBy) { + items = this.applyFiltering(items, state.filterBy); + } + + return items; + }) + ); + } + + /** + * Method for filtering the data. + * @param items Array of data source items. + * @param filters Set of column filters. + * @returns Array of filtered items. + */ + applyFiltering(items: ExampleItem[], filters: CollectionFilterAndGroup[]): ExampleItem[] { + items = items.filter((i) => this.getFilteringStrategy(i, filters)); + return items; + } + + /** + * Method for getting all possible options for particular field. + * Used for creating select options. + * Developers can override this method to extend the filtering functionality. + * @param field key of the data item. + * @returns Observable with select items. + */ + getFieldOptions(field: string): Observable { + return this.fetch().pipe( + take(1), + map((data) => { + const options: SelectItem[] = data + .filter((item) => get(item, field) !== undefined) + .map((item) => ({ + label: get(item, field), + value: get(item, field) + })); + + return options; + }) + ); + } + + /** + * Method which selects appropriate filtering strategy of the field depending on the filter type. + * Developers can override this method to extend the filtering functionality. + * @param item item of the data source. + * @param filters Set of column filters. + * @returns Whether or not item should be included in data array. + */ + getFilteringStrategy(item: ExampleItem, filters: CollectionFilterAndGroup[]): boolean { + return filters + .filter((condition) => condition.field) + .every((filter) => { + if (isCollectionFilter(filter)) { + return this.collectionFilterStrategy(item, filter); + } + + return this.collectionFilterGroupStrategy(item, filter); + }); + } + + /** + * Method which filters item depending on applied condition result. + * Developers can override this method to extend its functionality + * @param item Item to apply conditions to. + * @param filter Column filter. + * @returns {boolean} Whether this item should be present in filtered array of items. + */ + collectionFilterStrategy(item: ExampleItem, filter: CollectionFilter): boolean { + let result: boolean; + + switch (filter.type) { + case 'boolean': + result = this._filterBoolean(item, filter as CollectionBooleanFilter); + break; + case 'number': + result = this._filterNumber(item, filter as CollectionNumberFilter); + break; + case 'date': + result = this._filterDate(item, filter as CollectionDateFilter, this.dateTimeAdapter); + break; + case 'string': + default: + result = Array.isArray(filter.value) + ? this._filterArray(item, filter as CollectionSelectFilter) + : this._filterString(item, filter as CollectionStringFilter); + break; + } + + return result; + } + + /** + * Method which applies group filtering conditions for the item. + * @param item Item to apply conditions to. + * @param filter Filter group. + * @returns {boolean} Whether this item should be present in filtered items array. + */ + collectionFilterGroupStrategy(item: ExampleItem, filter: CollectionFilterGroup): boolean { + return filter.strategy === 'and' + ? filter.filters.every((f) => this.collectionFilterStrategy(item, f)) + : filter.filters.some((f) => this.collectionFilterStrategy(item, f)); + } + + /** + * Applies search of the search term for visible table columns. + * Developers can override this method to extend the filtering functionality. + * @param items data source items array. + * @param tableState @see TableState Set of table parameters. + * @returns filtered data source items array. + */ + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { + const searchText = searchInput?.text || ''; + const keysToSearchBy = columns; + + if (searchText.trim() === '' || keysToSearchBy.length === 0) { + return items; + } + + items = items.filter((item) => { + const valuesForSearch = keysToSearchBy.map((key) => get(item, key)); + return valuesForSearch + .filter((value) => !!value) + .map((value): string => value.toString()) + .some((value) => value.toLocaleLowerCase().includes(searchText.toLocaleLowerCase())); + }); + + return items; + } + + private _getSelectItemValue(item: any): any { + return isSelectItem(item) ? item.value : item; + } + + /** + * @hidden + * String filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterString(item: ExampleItem, filter: CollectionStringFilter): boolean { + const filterValue = filter.value && filter.value.toLocaleLowerCase(); + const filterValue2 = (filter.value2 && filter.value2.toLocaleLowerCase()) || ''; + let itemValue = get(item, filter.field); + + itemValue = itemValue ? itemValue.toLocaleLowerCase() : itemValue; + + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + result = itemValue === filterValue; + break; + case 'greaterThan': + result = itemValue > filterValue; + break; + case 'greaterThanOrEqualTo': + result = itemValue >= filterValue; + break; + case 'lessThan': + result = itemValue < filterValue; + break; + case 'lessThanOrEqualTo': + result = itemValue <= filterValue; + break; + case 'between': + result = itemValue >= filterValue && itemValue <= filterValue2; + break; + case 'beginsWith': + result = itemValue.startsWith(filterValue); + break; + case 'endsWith': + result = itemValue.endsWith(filterValue); + break; + case 'contains': + default: + result = itemValue ? itemValue.includes(filterValue) : itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Number filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterNumber(item: ExampleItem, filter: CollectionNumberFilter): boolean { + const filterValue = Number.parseFloat(filter.value as unknown as string); + const filterValue2 = Number.parseFloat(filter.value2 as unknown as string) || 0; + const itemValue = Number.parseFloat(get(item, filter.field)); + let result: boolean; + + switch (filter.strategy) { + case 'greaterThan': + result = itemValue > filterValue; + break; + case 'greaterThanOrEqualTo': + result = itemValue >= filterValue; + break; + case 'lessThan': + result = itemValue < filterValue; + break; + case 'lessThanOrEqualTo': + result = itemValue <= filterValue; + break; + case 'between': + result = itemValue >= filterValue && itemValue <= filterValue2; + break; + case 'equalTo': + default: + result = itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Date filtering strategy + * @param item data source array item. + * @param filter filter object + * @param adapter + * @returns whether or not item should be included in data source array. + */ + private _filterDate( + item: ExampleItem, + filter: CollectionDateFilter, + adapter: DatetimeAdapter + ): boolean { + const filterValue = filter.value; + const filterValue2 = filter.value2; + const itemValue = get(item, filter.field); + const diff = adapter.compareDate(itemValue, filterValue); + let result: boolean; + + switch (filter.strategy) { + case 'after': + result = diff > 0; + break; + case 'onOrAfter': + result = diff >= 0; + break; + case 'before': + result = diff < 0; + break; + case 'beforeOrOn': + result = diff <= 0; + break; + case 'between': + result = adapter.isBetween(itemValue, filterValue, filterValue2); + break; + + case 'equalTo': + default: + result = adapter.dateTimesEqual(itemValue, filterValue); + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Boolean filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterBoolean(item: ExampleItem, filter: CollectionBooleanFilter): boolean { + const filterValue = filter.value; + const itemValue = get(item, filter.field); + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + default: + result = itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Array filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterArray(item: ExampleItem, filter: CollectionSelectFilter): boolean { + const filterValues = filter.value.map((v) => this._getSelectItemValue(v)); + const itemValue = get(item, filter.field); + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + default: + result = filterValues.includes(itemValue); + } + + return !filterValues.length || filter.exclude ? !result : result; + } +} + +// Example items +const ITEMS: ExampleItem[] = [ + { + id: 1, + name: '10 Portable DVD player', + description: 'diam neque vestibulum eget vulputate', + price: { + value: 66.04, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 7), + verified: true + }, + { + id: 2, + name: 'Astro Laptop 1516', + description: 'pede malesuada', + price: { + value: 489.01, + currency: 'EUR' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 2, 5), + verified: true + }, + { + id: 3, + name: 'Astro Phone 6', + description: 'penatibus et magnis', + price: { + value: 154.1, + currency: 'IDR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 12), + verified: true + }, + { + id: 4, + name: 'Beam Breaker B-1', + description: 'fermentum donec ut', + price: { + value: 36.56, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 11, 24), + verified: false + }, + { + id: 5, + name: 'Beam Breaker B-2', + description: 'sapien in sapien iaculis congue', + price: { + value: 332.57, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 10, 23), + verified: true + }, + { + id: 6, + name: 'Benda Laptop 1408', + description: 'suspendisse potenti cras in', + price: { + value: 243.49, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 9, 22), + verified: true + }, + { + id: 7, + name: 'Bending Screen 21HD', + description: 'nunc nisl duis bibendum', + price: { + value: 66.46, + currency: 'EUR' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 14), + verified: false + }, + { + id: 8, + name: 'Blaster Extreme', + description: 'quisque ut', + price: { + value: 436.88, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 15), + verified: true + }, + { + id: 9, + name: 'Broad Screen 22HD', + description: 'ultrices posuere', + price: { + value: 458.18, + currency: 'CNY' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 4), + verified: true + }, + { + id: 10, + name: 'Camcorder View', + description: 'integer ac leo pellentesque', + price: { + value: 300.52, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 5), + verified: true + }, + { + id: 11, + name: 'Cepat Tablet 10.5', + description: 'rutrum rutrum neque aenean auctor', + price: { + value: 365.12, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 5, 6), + verified: true + }, + { + id: 12, + name: 'Ergo Mousepad', + description: 'tortor duis mattis egestas', + price: { + value: 354.46, + currency: 'EUR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 5, 7), + verified: true + }, + { + id: 13, + name: 'Ergo Screen E-I', + description: 'massa quis augue luctus tincidunt', + price: { + value: 387.23, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 3, 23), + verified: true + }, + { + id: 14, + name: 'Ergo Screen E-II', + description: 'orci eget', + price: { + value: 75.86, + currency: 'EUR' + }, + status: 'No info', + date: new FdDate(2020, 3, 20), + verified: false + }, + { + id: 15, + name: 'Gaming Monster', + description: 'cubilia curae', + price: { + value: 152.95, + currency: 'EGP' + }, + status: 'No info', + date: new FdDate(2020, 9, 20), + verified: false + }, + { + id: 16, + name: 'Gaming Monster Pro', + description: 'pharetra magna vestibulum aliquet', + price: { + value: 213.47, + currency: 'MZN' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 4, 17), + verified: false + } +]; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.html new file mode 100644 index 00000000000..069ca513905 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.html @@ -0,0 +1,92 @@ + +
+

Line Items

+
+ + + + + + + + + + + + + + + + + + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.ts new file mode 100644 index 00000000000..a629bea363c --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component.ts @@ -0,0 +1,268 @@ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { FdDate } from '@fundamental-ngx/core/datetime'; +import { + SmartFilterBarStrategyLabels, + SmartFilterBarVisibilityCategoryLabels +} from '@fundamental-ngx/platform/smart-filter-bar'; + +@Component({ + selector: 'fdp-platform-smart-filter-bar-custom-labels-example', + templateUrl: './platform-smart-filter-bar-custom-labels-example.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PlatformSmartFilterBarCustomLabelsExampleComponent { + source: ExampleItem[] = ITEMS; + + defineStrategyLabels: SmartFilterBarStrategyLabels = { + contains: 'custom "contains" label', + equalTo: 'custom "equal to" label', + between: 'custom "between" label', + beginsWith: 'custom "starts with" label', + endsWith: 'custom "ends with" label', + lessThan: 'custom "less than" label', + lessThanOrEqualTo: 'custom "less than or equal to" label', + greaterThan: 'custom "greater than" label', + greaterThanOrEqualTo: 'custom "greater than or equal to" label', + after: 'custom "after" label', + onOrAfter: 'custom "on or after" label', + before: 'custom "before" label', + beforeOrOn: 'custom "before or on" label' + }; + + filtersVisibilityCategoryLabels: SmartFilterBarVisibilityCategoryLabels = { + all: 'Custom "All" label', + visible: 'Custom "Visible" label', + active: 'Custom "Active" label', + visibleAndActive: 'Custom "Visible and active" label', + mandatory: 'Custom "Mandatory" label' + }; + + constructor() {} + + trackBy(_: number, item: ExampleItem): number { + return item.id; + } +} + +export interface ExampleItem { + id: number; + name: string; + description: string; + price: { + value: number; + currency: string; + }; + status: string; + statusColor?: string; + date: FdDate; + verified: boolean; +} + +// Example items +const ITEMS: ExampleItem[] = [ + { + id: 1, + name: '10 Portable DVD player', + description: 'diam neque vestibulum eget vulputate', + price: { + value: 66.04, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 7), + verified: true + }, + { + id: 2, + name: 'Astro Laptop 1516', + description: 'pede malesuada', + price: { + value: 489.01, + currency: 'EUR' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 2, 5), + verified: true + }, + { + id: 3, + name: 'Astro Phone 6', + description: 'penatibus et magnis', + price: { + value: 154.1, + currency: 'IDR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 12), + verified: true + }, + { + id: 4, + name: 'Beam Breaker B-1', + description: 'fermentum donec ut', + price: { + value: 36.56, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 11, 24), + verified: false + }, + { + id: 5, + name: 'Beam Breaker B-2', + description: 'sapien in sapien iaculis congue', + price: { + value: 332.57, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 10, 23), + verified: true + }, + { + id: 6, + name: 'Benda Laptop 1408', + description: 'suspendisse potenti cras in', + price: { + value: 243.49, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 9, 22), + verified: true + }, + { + id: 7, + name: 'Bending Screen 21HD', + description: 'nunc nisl duis bibendum', + price: { + value: 66.46, + currency: 'EUR' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 14), + verified: false + }, + { + id: 8, + name: 'Blaster Extreme', + description: 'quisque ut', + price: { + value: 436.88, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 15), + verified: true + }, + { + id: 9, + name: 'Broad Screen 22HD', + description: 'ultrices posuere', + price: { + value: 458.18, + currency: 'CNY' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 4), + verified: true + }, + { + id: 10, + name: 'Camcorder View', + description: 'integer ac leo pellentesque', + price: { + value: 300.52, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 5), + verified: true + }, + { + id: 11, + name: 'Cepat Tablet 10.5', + description: 'rutrum rutrum neque aenean auctor', + price: { + value: 365.12, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 5, 6), + verified: true + }, + { + id: 12, + name: 'Ergo Mousepad', + description: 'tortor duis mattis egestas', + price: { + value: 354.46, + currency: 'EUR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 5, 7), + verified: true + }, + { + id: 13, + name: 'Ergo Screen E-I', + description: 'massa quis augue luctus tincidunt', + price: { + value: 387.23, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 3, 23), + verified: true + }, + { + id: 14, + name: 'Ergo Screen E-II', + description: 'orci eget', + price: { + value: 75.86, + currency: 'EUR' + }, + status: 'No info', + date: new FdDate(2020, 3, 20), + verified: false + }, + { + id: 15, + name: 'Gaming Monster', + description: 'cubilia curae', + price: { + value: 152.95, + currency: 'EGP' + }, + status: 'No info', + date: new FdDate(2020, 9, 20), + verified: false + }, + { + id: 16, + name: 'Gaming Monster Pro', + description: 'pharetra magna vestibulum aliquet', + price: { + value: 213.47, + currency: 'MZN' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 4, 17), + verified: false + } +]; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.html new file mode 100644 index 00000000000..c95efdea2a0 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.html @@ -0,0 +1,141 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+
+
diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.ts new file mode 100644 index 00000000000..b02d74a4615 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component.ts @@ -0,0 +1,263 @@ +import { Component } from '@angular/core'; +import { FdDate } from '@fundamental-ngx/core/datetime'; + +@Component({ + selector: 'fdp-platform-smart-filter-bar-dynamic-page-example', + templateUrl: './platform-smart-filter-bar-dynamic-page-example.component.html', + styles: [ + ` + .overlay { + height: 100%; + width: 100%; + position: fixed; + z-index: 10; + top: 0; + left: 0; + background-color: rgb(255, 255, 255); + } + ` + ] +}) +export class PlatformSmartFilterBarDynamicPageExampleComponent { + visible = false; + + source: ExampleItem[] = ITEMS; + + onCollapseChange(): void { + console.log('collapse changed'); + } + + openPage(): void { + this.visible = true; + } + + closePage(): void { + this.visible = false; + } + + trackBy(_: number, item: ExampleItem): number { + return item.id; + } +} + +export interface ExampleItem { + id: number; + name: string; + description: string; + price: { + value: number; + currency: string; + }; + status: string; + statusColor?: string; + date: FdDate; + verified: boolean; +} + +// Example items +const ITEMS: ExampleItem[] = [ + { + id: 1, + name: '10 Portable DVD player', + description: 'diam neque vestibulum eget vulputate', + price: { + value: 66.04, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 7), + verified: true + }, + { + id: 2, + name: 'Astro Laptop 1516', + description: 'pede malesuada', + price: { + value: 489.01, + currency: 'EUR' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 2, 5), + verified: true + }, + { + id: 3, + name: 'Astro Phone 6', + description: 'penatibus et magnis', + price: { + value: 154.1, + currency: 'IDR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 12), + verified: true + }, + { + id: 4, + name: 'Beam Breaker B-1', + description: 'fermentum donec ut', + price: { + value: 36.56, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 11, 24), + verified: false + }, + { + id: 5, + name: 'Beam Breaker B-2', + description: 'sapien in sapien iaculis congue', + price: { + value: 332.57, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 10, 23), + verified: true + }, + { + id: 6, + name: 'Benda Laptop 1408', + description: 'suspendisse potenti cras in', + price: { + value: 243.49, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 9, 22), + verified: true + }, + { + id: 7, + name: 'Bending Screen 21HD', + description: 'nunc nisl duis bibendum', + price: { + value: 66.46, + currency: 'EUR' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 14), + verified: false + }, + { + id: 8, + name: 'Blaster Extreme', + description: 'quisque ut', + price: { + value: 436.88, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 15), + verified: true + }, + { + id: 9, + name: 'Broad Screen 22HD', + description: 'ultrices posuere', + price: { + value: 458.18, + currency: 'CNY' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 4), + verified: true + }, + { + id: 10, + name: 'Camcorder View', + description: 'integer ac leo pellentesque', + price: { + value: 300.52, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 5), + verified: true + }, + { + id: 11, + name: 'Cepat Tablet 10.5', + description: 'rutrum rutrum neque aenean auctor', + price: { + value: 365.12, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 5, 6), + verified: true + }, + { + id: 12, + name: 'Ergo Mousepad', + description: 'tortor duis mattis egestas', + price: { + value: 354.46, + currency: 'EUR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 5, 7), + verified: true + }, + { + id: 13, + name: 'Ergo Screen E-I', + description: 'massa quis augue luctus tincidunt', + price: { + value: 387.23, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 3, 23), + verified: true + }, + { + id: 14, + name: 'Ergo Screen E-II', + description: 'orci eget', + price: { + value: 75.86, + currency: 'EUR' + }, + status: 'No info', + date: new FdDate(2020, 3, 20), + verified: false + }, + { + id: 15, + name: 'Gaming Monster', + description: 'cubilia curae', + price: { + value: 152.95, + currency: 'EGP' + }, + status: 'No info', + date: new FdDate(2020, 9, 20), + verified: false + }, + { + id: 16, + name: 'Gaming Monster Pro', + description: 'pharetra magna vestibulum aliquet', + price: { + value: 213.47, + currency: 'MZN' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 4, 17), + verified: false + } +]; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.html new file mode 100644 index 00000000000..c6cfc91106b --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.html @@ -0,0 +1,88 @@ + +
+

Line Items

+
+ + + + + + + + + + + + + + + + + + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.ts new file mode 100644 index 00000000000..cb4fb26c05a --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component.ts @@ -0,0 +1,596 @@ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { DatetimeAdapter, FdDate } from '@fundamental-ngx/core/datetime'; +import { isSelectItem, SelectItem } from '@fundamental-ngx/platform/shared'; +import { + CollectionBooleanFilter, + CollectionDateFilter, + CollectionFilter, + CollectionFilterAndGroup, + CollectionFilterGroup, + CollectionNumberFilter, + CollectionSelectFilter, + CollectionStringFilter, + isCollectionFilter, + ObservableTableDataProvider, + TableDataSource, + TableState +} from '@fundamental-ngx/platform/table'; +import { Observable, of } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import get from 'lodash-es/get'; + +@Component({ + selector: 'fdp-smart-filter-bar-observable-example', + templateUrl: './platform-smart-filter-bar-observable-example.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PlatformSmartFilterBarObservableExampleComponent { + source: TableDataSource; + + constructor(datetimeAdapter: DatetimeAdapter) { + this.source = new TableDataSource(new ObservableTableDataProviderExample(ITEMS, datetimeAdapter)); + } + + trackBy(_: number, item: ExampleItem): number { + return item.id; + } + + alert(message: string): void { + alert(message); + } +} + +export interface ExampleItem { + id: number; + name: string; + description: string; + price: { + value: number; + currency: string; + }; + status: string; + statusColor?: string; + date: FdDate; + verified: boolean; +} + +/** + * Table Data Provider Example + * + */ +export class ObservableTableDataProviderExample extends ObservableTableDataProvider { + /** @hidden */ + constructor(items: ExampleItem[], dateTimeAdapter?: DatetimeAdapter) { + super(of(items), dateTimeAdapter); + } + + /** + * Method for retrieving the data. + * @param state @see TableState Set of table parameters. + * @returns Observable with data. + */ + fetch(state?: TableState): Observable { + return this.items$.pipe( + map((items) => { + if (state?.searchInput) { + items = this.search(items, state); + } + + if (state?.filterBy) { + items = this.applyFiltering(items, state.filterBy); + } + + this.totalItems = items.length; + + return items; + }) + ); + } + + /** + * Method for filtering the data. + * @param items Array of data source items. + * @param filters Set of column filters. + * @returns Array of filtered items. + */ + applyFiltering(items: ExampleItem[], filters: CollectionFilterAndGroup[]): ExampleItem[] { + items = items.filter((i) => this.getFilteringStrategy(i, filters)); + return items; + } + + /** + * Method for getting all possible options for particular field. + * Used for creating select options. + * Developers can override this method to extend the filtering functionality. + * @param field key of the data item. + * @returns Observable with select items. + */ + getFieldOptions(field: string): Observable { + return this.fetch().pipe( + take(1), + map((data) => { + const options: SelectItem[] = data + .filter((item) => get(item, field) !== undefined) + .map((item) => ({ + label: get(item, field), + value: get(item, field) + })); + + return options; + }) + ); + } + + /** + * Method which selects appropriate filtering strategy of the field depending on the filter type. + * Developers can override this method to extend the filtering functionality. + * @param item item of the data source. + * @param filters Set of column filters. + * @returns Whether or not item should be included in data array. + */ + getFilteringStrategy(item: ExampleItem, filters: CollectionFilterAndGroup[]): boolean { + return filters + .filter((condition) => condition.field) + .every((filter) => { + if (isCollectionFilter(filter)) { + return this.collectionFilterStrategy(item, filter); + } + + return this.collectionFilterGroupStrategy(item, filter); + }); + } + + /** + * Method which filters item depending on applied condition result. + * Developers can override this method to extend its functionality + * @param item Item to apply conditions to. + * @param filter Column filter. + * @returns {boolean} Whether this item should be present in filtered array of items. + */ + collectionFilterStrategy(item: ExampleItem, filter: CollectionFilter): boolean { + let result: boolean; + + switch (filter.type) { + case 'boolean': + result = this._filterBoolean(item, filter as CollectionBooleanFilter); + break; + case 'number': + result = this._filterNumber(item, filter as CollectionNumberFilter); + break; + case 'date': + result = this._filterDate(item, filter as CollectionDateFilter, this.dateTimeAdapter); + break; + case 'string': + default: + result = Array.isArray(filter.value) + ? this._filterArray(item, filter as CollectionSelectFilter) + : this._filterString(item, filter as CollectionStringFilter); + break; + } + + return result; + } + + /** + * Method which applies group filtering conditions for the item. + * @param item Item to apply conditions to. + * @param filter Filter group. + * @returns {boolean} Whether this item should be present in filtered items array. + */ + collectionFilterGroupStrategy(item: ExampleItem, filter: CollectionFilterGroup): boolean { + return filter.strategy === 'and' + ? filter.filters.every((f) => this.collectionFilterStrategy(item, f)) + : filter.filters.some((f) => this.collectionFilterStrategy(item, f)); + } + + /** + * Applies search of the search term for visible table columns. + * Developers can override this method to extend the filtering functionality. + * @param items data source items array. + * @param tableState @see TableState Set of table parameters. + * @returns filtered data source items array. + */ + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { + const searchText = searchInput?.text || ''; + const keysToSearchBy = columns; + + if (searchText.trim() === '' || keysToSearchBy.length === 0) { + return items; + } + + items = items.filter((item) => { + const valuesForSearch = keysToSearchBy.map((key) => get(item, key)); + return valuesForSearch + .filter((value) => !!value) + .map((value): string => value.toString()) + .some((value) => value.toLocaleLowerCase().includes(searchText.toLocaleLowerCase())); + }); + + return items; + } + + private _getSelectItemValue(item: any): any { + return isSelectItem(item) ? item.value : item; + } + + /** + * @hidden + * String filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterString(item: ExampleItem, filter: CollectionStringFilter): boolean { + const filterValue = filter.value && filter.value.toLocaleLowerCase(); + const filterValue2 = (filter.value2 && filter.value2.toLocaleLowerCase()) || ''; + let itemValue = get(item, filter.field); + + itemValue = itemValue ? itemValue.toLocaleLowerCase() : itemValue; + + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + result = itemValue === filterValue; + break; + case 'greaterThan': + result = itemValue > filterValue; + break; + case 'greaterThanOrEqualTo': + result = itemValue >= filterValue; + break; + case 'lessThan': + result = itemValue < filterValue; + break; + case 'lessThanOrEqualTo': + result = itemValue <= filterValue; + break; + case 'between': + result = itemValue >= filterValue && itemValue <= filterValue2; + break; + case 'beginsWith': + result = itemValue.startsWith(filterValue); + break; + case 'endsWith': + result = itemValue.endsWith(filterValue); + break; + case 'contains': + default: + result = itemValue ? itemValue.includes(filterValue) : itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Number filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterNumber(item: ExampleItem, filter: CollectionNumberFilter): boolean { + const filterValue = Number.parseFloat(filter.value as unknown as string); + const filterValue2 = Number.parseFloat(filter.value2 as unknown as string) || 0; + const itemValue = Number.parseFloat(get(item, filter.field)); + let result: boolean; + + switch (filter.strategy) { + case 'greaterThan': + result = itemValue > filterValue; + break; + case 'greaterThanOrEqualTo': + result = itemValue >= filterValue; + break; + case 'lessThan': + result = itemValue < filterValue; + break; + case 'lessThanOrEqualTo': + result = itemValue <= filterValue; + break; + case 'between': + result = itemValue >= filterValue && itemValue <= filterValue2; + break; + case 'equalTo': + default: + result = itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Date filtering strategy + * @param item data source array item. + * @param filter filter object + * @param adapter + * @returns whether or not item should be included in data source array. + */ + private _filterDate( + item: ExampleItem, + filter: CollectionDateFilter, + adapter: DatetimeAdapter + ): boolean { + const filterValue = filter.value; + const filterValue2 = filter.value2; + const itemValue = get(item, filter.field); + const diff = adapter.compareDate(itemValue, filterValue); + let result: boolean; + + switch (filter.strategy) { + case 'after': + result = diff > 0; + break; + case 'onOrAfter': + result = diff >= 0; + break; + case 'before': + result = diff < 0; + break; + case 'beforeOrOn': + result = diff <= 0; + break; + case 'between': + result = adapter.isBetween(itemValue, filterValue, filterValue2); + break; + + case 'equalTo': + default: + result = adapter.dateTimesEqual(itemValue, filterValue); + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Boolean filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterBoolean(item: ExampleItem, filter: CollectionBooleanFilter): boolean { + const filterValue = filter.value; + const itemValue = get(item, filter.field); + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + default: + result = itemValue === filterValue; + } + + return filter.exclude ? !result : result; + } + + /** + * @hidden + * Array filtering strategy + * @param item data source array item. + * @param filter filter object + * @returns whether or not item should be included in data source array. + */ + private _filterArray(item: ExampleItem, filter: CollectionSelectFilter): boolean { + const filterValues = filter.value.map((v) => this._getSelectItemValue(v)); + const itemValue = get(item, filter.field); + let result: boolean; + + switch (filter.strategy) { + case 'equalTo': + default: + result = filterValues.includes(itemValue); + } + + return !filterValues.length || filter.exclude ? !result : result; + } +} + +// Example items +const ITEMS: ExampleItem[] = [ + { + id: 1, + name: '10 Portable DVD player', + description: 'diam neque vestibulum eget vulputate', + price: { + value: 66.04, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 7), + verified: true + }, + { + id: 2, + name: 'Astro Laptop 1516', + description: 'pede malesuada', + price: { + value: 489.01, + currency: 'EUR' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 2, 5), + verified: true + }, + { + id: 3, + name: 'Astro Phone 6', + description: 'penatibus et magnis', + price: { + value: 154.1, + currency: 'IDR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 1, 12), + verified: true + }, + { + id: 4, + name: 'Beam Breaker B-1', + description: 'fermentum donec ut', + price: { + value: 36.56, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 11, 24), + verified: false + }, + { + id: 5, + name: 'Beam Breaker B-2', + description: 'sapien in sapien iaculis congue', + price: { + value: 332.57, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 10, 23), + verified: true + }, + { + id: 6, + name: 'Benda Laptop 1408', + description: 'suspendisse potenti cras in', + price: { + value: 243.49, + currency: 'CNY' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 9, 22), + verified: true + }, + { + id: 7, + name: 'Bending Screen 21HD', + description: 'nunc nisl duis bibendum', + price: { + value: 66.46, + currency: 'EUR' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 14), + verified: false + }, + { + id: 8, + name: 'Blaster Extreme', + description: 'quisque ut', + price: { + value: 436.88, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 8, 15), + verified: true + }, + { + id: 9, + name: 'Broad Screen 22HD', + description: 'ultrices posuere', + price: { + value: 458.18, + currency: 'CNY' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 4), + verified: true + }, + { + id: 10, + name: 'Camcorder View', + description: 'integer ac leo pellentesque', + price: { + value: 300.52, + currency: 'USD' + }, + status: 'Available', + statusColor: 'positive', + date: new FdDate(2020, 5, 5), + verified: true + }, + { + id: 11, + name: 'Cepat Tablet 10.5', + description: 'rutrum rutrum neque aenean auctor', + price: { + value: 365.12, + currency: 'NZD' + }, + status: 'No info', + date: new FdDate(2020, 5, 6), + verified: true + }, + { + id: 12, + name: 'Ergo Mousepad', + description: 'tortor duis mattis egestas', + price: { + value: 354.46, + currency: 'EUR' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 5, 7), + verified: true + }, + { + id: 13, + name: 'Ergo Screen E-I', + description: 'massa quis augue luctus tincidunt', + price: { + value: 387.23, + currency: 'NZD' + }, + status: 'Stocked on demand', + statusColor: 'informative', + date: new FdDate(2020, 3, 23), + verified: true + }, + { + id: 14, + name: 'Ergo Screen E-II', + description: 'orci eget', + price: { + value: 75.86, + currency: 'EUR' + }, + status: 'No info', + date: new FdDate(2020, 3, 20), + verified: false + }, + { + id: 15, + name: 'Gaming Monster', + description: 'cubilia curae', + price: { + value: 152.95, + currency: 'EGP' + }, + status: 'No info', + date: new FdDate(2020, 9, 20), + verified: false + }, + { + id: 16, + name: 'Gaming Monster Pro', + description: 'pharetra magna vestibulum aliquet', + price: { + value: 213.47, + currency: 'MZN' + }, + status: 'Out of stock', + statusColor: 'negative', + date: new FdDate(2020, 4, 17), + verified: false + } +]; diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.html b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.html new file mode 100644 index 00000000000..9ca0ab441a8 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.html @@ -0,0 +1,19 @@ +
Smart filter bar
+ +

+ The Smart Filter Bar component uses TableDataProvider data source in order to create a + Filter Bar. +

+

Smart filter bar supports:

+
    +
  • Automatic field condition generation based on provided configuration;
  • +
  • Automatic filtering of the data based on generated conditions;
  • +
  • Custom, user-defined filters;
  • +
  • Custom, user-defined filtering logic;
  • +
+

Smart filter bar can work with Observables and plain arrays.

+
+ + + + diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.ts new file mode 100644 index 00000000000..e963209eb96 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar-header/platform-smart-filter-bar-header.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-smart-filter-bar-header', + templateUrl: './platform-smart-filter-bar-header.component.html' +}) +export class PlatformSmartFilterBarHeaderComponent {} diff --git a/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar.module.ts b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar.module.ts new file mode 100644 index 00000000000..1e179f03814 --- /dev/null +++ b/apps/docs/src/app/platform/component-docs/platform-smart-filter-bar/platform-smart-filter-bar.module.ts @@ -0,0 +1,75 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { PlatformSmartFilterBarModule } from '@fundamental-ngx/platform/smart-filter-bar'; +import { PlatformTableModule } from '@fundamental-ngx/platform/table'; +import { PlatformButtonModule } from '@fundamental-ngx/platform/button'; +import { FdDatetimeModule } from '@fundamental-ngx/core/datetime'; +import { DynamicPageModule } from '@fundamental-ngx/core/dynamic-page'; +import { PlatformSliderModule } from '@fundamental-ngx/platform/slider'; +import { PlatformDatePickerModule } from '@fundamental-ngx/platform/form'; +import { BreadcrumbModule } from '@fundamental-ngx/core/breadcrumb'; +import { ToolbarModule } from '@fundamental-ngx/core/toolbar'; +import { BarModule } from '@fundamental-ngx/core/bar'; +import { TitleModule } from '@fundamental-ngx/core/title'; + +import { ApiComponent } from '../../../documentation/core-helpers/api/api.component'; +import { API_FILES } from '../../api-files'; +import { SharedDocumentationPageModule } from '../../../documentation/shared-documentation-page.module'; + +import { PlatformSmartFilterBarHeaderComponent } from './platform-smart-filter-bar-header/platform-smart-filter-bar-header.component'; +import { PlatformSmartFilterBarDocsComponent } from './platform-smart-filter-bar-docs.component'; +import { PlatformSmartFilterBarBasicExampleComponent } from './platform-smart-filter-bar-examples/platform-smart-filter-bar-basic-example.component'; +import { + PlatformSmartFilterBarCustomFilterExampleComponent, + PlatformSmartFilterBarSliderComponent, + PlatformSmartFilterBarDateRendererComponent +} from './platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-filter-example.component'; +import { PlatformSmartFilterBarObservableExampleComponent } from './platform-smart-filter-bar-examples/platform-smart-filter-bar-observable-example.component'; +import { PlatformSmartFilterBarCustomLabelsExampleComponent } from './platform-smart-filter-bar-examples/platform-smart-filter-bar-custom-labels-example.component'; +import { PlatformSmartFilterBarDynamicPageExampleComponent } from './platform-smart-filter-bar-examples/platform-smart-filter-bar-dynamic-page-example.component'; + +const routes: Routes = [ + { + path: '', + component: PlatformSmartFilterBarHeaderComponent, + children: [ + { path: '', component: PlatformSmartFilterBarDocsComponent }, + { path: 'api', component: ApiComponent, data: { content: API_FILES.smartFilterBar } } + ] + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes), + SharedDocumentationPageModule, + FormsModule, + ReactiveFormsModule, + PlatformTableModule, + PlatformButtonModule, + PlatformSmartFilterBarModule, + FdDatetimeModule, + PlatformSliderModule, + PlatformDatePickerModule, + DynamicPageModule, + BreadcrumbModule, + ToolbarModule, + BarModule, + TitleModule + ], + exports: [RouterModule], + declarations: [ + PlatformSmartFilterBarDocsComponent, + PlatformSmartFilterBarHeaderComponent, + PlatformSmartFilterBarBasicExampleComponent, + PlatformSmartFilterBarCustomFilterExampleComponent, + PlatformSmartFilterBarSliderComponent, + PlatformSmartFilterBarDateRendererComponent, + PlatformSmartFilterBarObservableExampleComponent, + PlatformSmartFilterBarCustomLabelsExampleComponent, + PlatformSmartFilterBarDynamicPageExampleComponent + ] +}) +export class PlatformSmartFilterBarDocsModule {} diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-activable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-activable-example.component.ts index ad810b99f76..0d20fc4c7b8 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-activable-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-activable-example.component.ts @@ -45,12 +45,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -58,8 +58,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-column-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-column-example.component.ts index 6a9abc6cc9b..bbbc02065df 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-column-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-column-example.component.ts @@ -46,12 +46,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -59,8 +59,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-width-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-width-example.component.ts index f4000bfc9d4..29ace43c785 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-width-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-custom-width-example.component.ts @@ -41,12 +41,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -54,8 +54,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-data-provider-example.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-data-provider-example.ts index 20d95461a86..546c555f782 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-data-provider-example.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-data-provider-example.ts @@ -24,27 +24,27 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = []; totalItems = 0; - constructor(private dateTimeAdapter: DatetimeAdapter) { + constructor(public dateTimeAdapter: DatetimeAdapter) { super(); } - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply filtering - if (tableState.filterBy) { + if (tableState?.filterBy) { this.items = this.filter(tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } // apply grouping - if (tableState.groupBy) { + if (tableState?.groupBy) { this.items = this.group(tableState); } @@ -147,8 +147,7 @@ export class TableDataProviderExample extends TableDataProvider { ); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-default-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-default-example.component.ts index ca0bfe4bfba..6b8250a3935 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-default-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-default-example.component.ts @@ -46,12 +46,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -59,8 +59,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-filterable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-filterable-example.component.ts index ff23553a413..e9000d84a16 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-filterable-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-filterable-example.component.ts @@ -71,23 +71,23 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = []; totalItems = 0; - constructor(private dateTimeAdapter: DatetimeAdapter) { + constructor(public dateTimeAdapter: DatetimeAdapter) { super(); } - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply filtering - if (tableState.filterBy) { + if (tableState?.filterBy) { this.items = this.filter(tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } @@ -118,6 +118,8 @@ export class TableDataProviderExample extends TableDataProvider { private filter({ filterBy }: TableState): ExampleItem[] { let items = this.items; + console.log(filterBy); + filterBy .filter(({ field }) => !!field) .forEach((rule) => { @@ -171,8 +173,7 @@ export class TableDataProviderExample extends TableDataProvider { return filterByString(item, rule as CollectionStringFilter); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-freezable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-freezable-example.component.ts index 650a1fa28b7..e498552c03a 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-freezable-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-freezable-example.component.ts @@ -41,12 +41,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -54,8 +54,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-groupable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-groupable-example.component.ts index 78a1c3881cf..d33cd9ea81a 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-groupable-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-groupable-example.component.ts @@ -51,12 +51,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -64,8 +64,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-initial-state-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-initial-state-example.component.ts index 4864507d6f1..8c1108a98fc 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-initial-state-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-initial-state-example.component.ts @@ -54,23 +54,23 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = []; totalItems = 0; - constructor(private dateTimeAdapter: DatetimeAdapter) { + constructor(public dateTimeAdapter: DatetimeAdapter) { super(); } - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply filtering - if (tableState.filterBy) { + if (tableState?.filterBy) { this.items = this.filter(tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } @@ -154,8 +154,7 @@ export class TableDataProviderExample extends TableDataProvider { return filterByString(item, rule as CollectionStringFilter); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-loading-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-loading-example.component.ts index efdb4f70e94..527494bde64 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-loading-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-loading-example.component.ts @@ -49,12 +49,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -62,8 +62,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-multiple-row-selection-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-multiple-row-selection-example.component.ts index a89c7f52256..9635fbc1600 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-multiple-row-selection-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-multiple-row-selection-example.component.ts @@ -46,12 +46,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = ITEMS; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = ITEMS; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -59,8 +59,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-navigatable-row-indicator-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-navigatable-row-indicator-example.component.ts index f53355cc35c..8e82a0431f6 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-navigatable-row-indicator-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-navigatable-row-indicator-example.component.ts @@ -59,12 +59,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -72,8 +72,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-no-outer-borders-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-no-outer-borders-example.component.ts index 2c8ee6e7f0e..35b504da098 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-no-outer-borders-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-no-outer-borders-example.component.ts @@ -41,12 +41,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -54,8 +54,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-columns-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-columns-example.component.ts index 32cd68b5457..0d2d2f0e8eb 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-columns-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-columns-example.component.ts @@ -37,12 +37,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -50,8 +50,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-filter-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-filter-example.component.ts index d48115ffad3..ca6be5b9061 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-filter-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-filter-example.component.ts @@ -53,23 +53,23 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = []; totalItems = 0; - constructor(private dateTimeAdapter: DatetimeAdapter) { + constructor(public dateTimeAdapter: DatetimeAdapter) { super(); } - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply filtering - if (tableState.filterBy) { + if (tableState?.filterBy) { this.items = this.filter(tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } @@ -125,8 +125,7 @@ export class TableDataProviderExample extends TableDataProvider { return items; } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-group-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-group-example.component.ts index 34797c7fa5d..2a9f48170ab 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-group-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-group-example.component.ts @@ -37,12 +37,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -50,8 +50,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-sort-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-sort-example.component.ts index 9e3010d8d00..e85ffd66d90 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-sort-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-p13-sort-example.component.ts @@ -37,15 +37,15 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } @@ -54,8 +54,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-page-scrolling-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-page-scrolling-example.component.ts index ddb984aeaa1..bcfead991d7 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-page-scrolling-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-page-scrolling-example.component.ts @@ -57,14 +57,14 @@ export class TableDataProviderExample extends TableDataProvider { totalItems = 0; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { const { currentPage, pageSize } = tableState.page; this.items = [...this.ALL_ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -83,8 +83,7 @@ export class TableDataProviderExample extends TableDataProvider { ); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-row-class-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-row-class-example.component.ts index bb356a21ad4..9356e143fb3 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-row-class-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-row-class-example.component.ts @@ -67,12 +67,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -80,8 +80,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-semantic-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-semantic-example.component.ts index 55c7955aa43..aac5a245daf 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-semantic-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-semantic-example.component.ts @@ -38,12 +38,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -51,8 +51,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-single-row-selection-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-single-row-selection-example.component.ts index 02842526243..21a50424a2c 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-single-row-selection-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-single-row-selection-example.component.ts @@ -55,12 +55,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -68,8 +68,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-sortable-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-sortable-example.component.ts index 75c6d5e7ca6..740994b8f6e 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-sortable-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-sortable-example.component.ts @@ -53,19 +53,19 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = []; totalItems = 0; - constructor(private dateTimeAdapter: DatetimeAdapter) { + constructor(public dateTimeAdapter: DatetimeAdapter) { super(); } - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } // apply sorting - if (tableState.sortBy) { + if (tableState?.sortBy) { this.items = this.sort(tableState); } @@ -93,8 +93,7 @@ export class TableDataProviderExample extends TableDataProvider { ); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-tree-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-tree-example.component.ts index bba28497cb9..4c4dc058ffa 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-tree-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-tree-example.component.ts @@ -56,12 +56,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -69,8 +69,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-wrap-example.component.ts b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-wrap-example.component.ts index f1c56d2612f..9a0a744460e 100644 --- a/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-wrap-example.component.ts +++ b/apps/docs/src/app/platform/component-docs/platform-table/platform-table-examples/platform-table-wrap-example.component.ts @@ -46,12 +46,12 @@ export class TableDataProviderExample extends TableDataProvider { items: ExampleItem[] = [...ITEMS]; totalItems = ITEMS.length; - fetch(tableState: TableState): Observable { + fetch(tableState?: TableState): Observable { this.items = [...ITEMS]; // apply searching - if (tableState.searchInput) { - this.items = this.search(tableState); + if (tableState?.searchInput) { + this.items = this.search(this.items, tableState); } this.totalItems = this.items.length; @@ -59,8 +59,7 @@ export class TableDataProviderExample extends TableDataProvider { return of(this.items); } - private search({ searchInput, columns }: TableState): ExampleItem[] { - const items = this.items; + search(items: ExampleItem[], { searchInput, columns }: TableState): ExampleItem[] { const searchText = searchInput?.text || ''; const keysToSearchBy = columns; diff --git a/apps/docs/src/app/platform/documentation/platform-documentation-data.ts b/apps/docs/src/app/platform/documentation/platform-documentation-data.ts index ecb35cb460c..6aebee10fe6 100644 --- a/apps/docs/src/app/platform/documentation/platform-documentation-data.ts +++ b/apps/docs/src/app/platform/documentation/platform-documentation-data.ts @@ -32,6 +32,7 @@ export const components: SectionInterfaceContent[] = [ { url: 'platform/object-attribute', name: 'Object Attribute' }, { url: 'platform/radio-group', name: 'Radio Button Group' }, { url: 'platform/search-field', name: 'Search Field' }, + { url: 'platform/smart-filter-bar', name: 'Smart Filter Bar' }, { url: 'platform/split-menu-button', name: 'Split Menu Button' }, { url: 'platform/table', name: 'Table' }, { url: 'platform/textarea', name: 'Textarea' }, diff --git a/apps/docs/src/app/platform/platform-documentation.routes.ts b/apps/docs/src/app/platform/platform-documentation.routes.ts index 07b9788ec70..409a6664ce0 100755 --- a/apps/docs/src/app/platform/platform-documentation.routes.ts +++ b/apps/docs/src/app/platform/platform-documentation.routes.ts @@ -330,6 +330,13 @@ export const ROUTES: Routes = [ import('./component-docs/platform-icon-tab-bar/platform-icon-tab-bar-docs.module').then( (m) => m.PlatformIconTabBarDocsModule ) + }, + { + path: 'smart-filter-bar', + loadChildren: () => + import('./component-docs/platform-smart-filter-bar/platform-smart-filter-bar.module').then( + (m) => m.PlatformSmartFilterBarDocsModule + ) } ] } diff --git a/libs/core/src/lib/date-picker/date-picker.component.ts b/libs/core/src/lib/date-picker/date-picker.component.ts index 0abc49eeb6a..fe326e6f7c8 100644 --- a/libs/core/src/lib/date-picker/date-picker.component.ts +++ b/libs/core/src/lib/date-picker/date-picker.component.ts @@ -55,7 +55,7 @@ let datePickerCounter = 0; host: { '(blur)': 'onTouched()', '[class.fd-date-picker]': 'true', - '[class.fd-date-picker-custom]': 'true' + '[class.fd-date-picker-custom]': 'inline' }, providers: [ { @@ -272,6 +272,10 @@ export class DatePickerComponent implements OnInit, OnDestroy, AfterViewInit, @Input() isOpen = false; + /** Should date picker be inlined. */ + @Input() + inline = true; + /** Event emitted when the state of the isOpen property changes. */ @Output() readonly isOpenChange = new EventEmitter(); diff --git a/libs/core/src/lib/dialog/base/dialog-ref-base.class.ts b/libs/core/src/lib/dialog/base/dialog-ref-base.class.ts index f6f3960a2b0..15eb2313bbd 100644 --- a/libs/core/src/lib/dialog/base/dialog-ref-base.class.ts +++ b/libs/core/src/lib/dialog/base/dialog-ref-base.class.ts @@ -2,7 +2,7 @@ import { Observable, Subject } from 'rxjs'; export class DialogRefBase { /** @hidden */ - protected readonly _afterClosed = new Subject(); + protected readonly _afterClosed = new Subject

(); /** @hidden */ protected readonly _afterLoaded = new Subject(); @@ -23,7 +23,7 @@ export class DialogRefBase { * Closes the dialog and passes the argument to the afterClosed observable. * @param result Value passed back to the observable as a result. */ - close(result?: any): void { + close(result?: P): void { this._afterClosed.next(result); this._afterClosed.complete(); } diff --git a/libs/core/src/lib/dialog/dialog-service/dialog.service.ts b/libs/core/src/lib/dialog/dialog-service/dialog.service.ts index b5c7b7244af..8954140d182 100644 --- a/libs/core/src/lib/dialog/dialog-service/dialog.service.ts +++ b/libs/core/src/lib/dialog/dialog-service/dialog.service.ts @@ -21,7 +21,7 @@ export class DialogService extends DialogBaseService { } /** - * Opens a dialog component with with provided content. + * Opens a dialog component with provided content. * @param content Content of the dialog component. * @param dialogConfig Configuration of the dialog component. */ diff --git a/libs/core/src/lib/popover/popover.component.ts b/libs/core/src/lib/popover/popover.component.ts index 165d8658e76..e95082ce885 100644 --- a/libs/core/src/lib/popover/popover.component.ts +++ b/libs/core/src/lib/popover/popover.component.ts @@ -281,8 +281,6 @@ export class PopoverComponent injector ); - console.log(111111, this._mobileModeComponentRef); - this._listenOnTriggerRefClicks(); } diff --git a/libs/core/src/lib/select/select.component.ts b/libs/core/src/lib/select/select.component.ts index 9920a4bef1e..7e4e3ccf575 100644 --- a/libs/core/src/lib/select/select.component.ts +++ b/libs/core/src/lib/select/select.component.ts @@ -70,7 +70,7 @@ export class FdSelectChange { encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { - '[class.fd-select-custom-class]': 'true', + '[class.fd-select-custom-class]': 'inline', '[class.fd-select-custom-class--mobile]': 'mobile' }, providers: [ @@ -192,6 +192,10 @@ export class SelectComponent @Input() mobileConfig: MobileModeConfig = { hasCloseButton: true }; + /** Should select be inlined. */ + @Input() + inline = true; + /** Event emitted when the popover open state changes. */ @Output() readonly isOpenChange: EventEmitter = new EventEmitter(); diff --git a/libs/core/src/lib/token/tokenizer.component.ts b/libs/core/src/lib/token/tokenizer.component.ts index d71c255d5ec..3ec77e6facb 100644 --- a/libs/core/src/lib/token/tokenizer.component.ts +++ b/libs/core/src/lib/token/tokenizer.component.ts @@ -178,6 +178,8 @@ export class TokenizerComponent } this.tokenListChangesSubscription = this.tokenList.changes.subscribe(() => { this._cdRef.detectChanges(); + this.moreTokensLeft = []; + this.moreTokensRight = []; this.previousTokenCount > this.tokenList.length ? this._expandTokens() : this._collapseTokens(); this.previousTokenCount = this.tokenList.length; this.handleTokenClickSubscriptions(); @@ -249,7 +251,7 @@ export class TokenizerComponent return [this.class]; } - elementRef(): ElementRef { + elementRef(): ElementRef { return this._elementRef; } @@ -456,7 +458,7 @@ export class TokenizerComponent let combinedTokenWidth = this.getCombinedTokenWidth(); // the combined width of all tokens, the "____ more" text, and the input let i = 0; /* - When resizing, we want to collapse the tokens on the left first. However when the user is navigating through a + When resizing, we want to collapse the tokens on the left first. However when the user is navigating through a group of overflowing tokens using the arrow left key, we may need to hide tokens on the right. So if this function has been called with the param 'right' it will collapse tokens from the right side of the list rather than the (default) left side. @@ -468,6 +470,7 @@ export class TokenizerComponent // loop through the tokens and hide them until the combinedTokenWidth fits in the elementWidth const token = this.tokenList.find((item, index) => index === i); const moreTokens = side === 'right' ? this.moreTokensRight : this.moreTokensLeft; + if (moreTokens.indexOf(token) === -1) { moreTokens.push(token); } @@ -625,11 +628,8 @@ export class TokenizerComponent } this.tokenList.forEach((token, indexOfToken) => { - if (indexOfToken >= this._firstElementInSelection && indexOfToken <= this._lastElementInSelection) { - token.selected = true; - } else { - token.selected = false; - } + token.selected = + indexOfToken >= this._firstElementInSelection && indexOfToken <= this._lastElementInSelection; }); this._ctrlPrevious = false; } diff --git a/libs/platform/src/lib/form/date-picker/date-picker.component.html b/libs/platform/src/lib/form/date-picker/date-picker.component.html index 4785291ca76..99cf50d70ba 100644 --- a/libs/platform/src/lib/form/date-picker/date-picker.component.html +++ b/libs/platform/src/lib/form/date-picker/date-picker.component.html @@ -31,6 +31,7 @@ [disableFunction]="disableFunction" [disableRangeStartFunction]="disableRangeStartFunction" [disableRangeEndFunction]="disableRangeEndFunction" + [inline]="inline" [(ngModel)]="value" (ngModelChange)="handleDateChange($event)" (isOpenChange)="handleOpenChange($event)" diff --git a/libs/platform/src/lib/form/date-picker/date-picker.component.ts b/libs/platform/src/lib/form/date-picker/date-picker.component.ts index 262675c65e2..08e69c966c0 100644 --- a/libs/platform/src/lib/form/date-picker/date-picker.component.ts +++ b/libs/platform/src/lib/form/date-picker/date-picker.component.ts @@ -204,6 +204,10 @@ export class PlatformDatePickerComponent extends BaseInput { @Input() isOpen = false; + /** Should date picker be inlined. */ + @Input() + inline = true; + /** Event emitted when the state of the isOpen property changes. */ @Output() readonly isOpenChange = new EventEmitter(); diff --git a/libs/platform/src/lib/form/form-generator/base-dynamic-form-generator-control.ts b/libs/platform/src/lib/form/form-generator/base-dynamic-form-generator-control.ts index d51c049c82b..bcaf225294d 100644 --- a/libs/platform/src/lib/form/form-generator/base-dynamic-form-generator-control.ts +++ b/libs/platform/src/lib/form/form-generator/base-dynamic-form-generator-control.ts @@ -39,5 +39,6 @@ export abstract class BaseDynamicFormGeneratorControl implements BaseDynamicForm */ @Input() formField: FormField; + /** @description Inner form group name */ @Input() formGroupName: string; } diff --git a/libs/platform/src/lib/form/form-generator/config/default-components-list.ts b/libs/platform/src/lib/form/form-generator/config/default-components-list.ts index 7727aa36c35..992ab2ba08d 100644 --- a/libs/platform/src/lib/form/form-generator/config/default-components-list.ts +++ b/libs/platform/src/lib/form/form-generator/config/default-components-list.ts @@ -6,6 +6,7 @@ import { DynamicFormGeneratorSelectComponent } from '../dynamic-form-generator-s import { FormComponentDefinition } from '../interfaces/form-component-definition'; import { DynamicFormGeneratorDatepickerComponent } from '../dynamic-form-generator-datepicker/dynamic-form-generator-datepicker.component'; import { DynamicFormGeneratorSwitchComponent } from '../dynamic-form-generator-switch/dynamic-form-generator-switch.component'; +import { DynamicFormGeneratorMultiInputComponent } from '../dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component'; export const DEFAULT_COMPONENTS_LIST: FormComponentDefinition[] = [ { @@ -35,5 +36,9 @@ export const DEFAULT_COMPONENTS_LIST: FormComponentDefinition[] = [ { types: ['switch'], component: DynamicFormGeneratorSwitchComponent + }, + { + types: ['multi-input'], + component: DynamicFormGeneratorMultiInputComponent } ]; diff --git a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-datepicker/dynamic-form-generator-datepicker.component.html b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-datepicker/dynamic-form-generator-datepicker.component.html index 06e56872927..f9840f97d53 100644 --- a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-datepicker/dynamic-form-generator-datepicker.component.html +++ b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-datepicker/dynamic-form-generator-datepicker.component.html @@ -5,6 +5,7 @@ [placeholder]="formItem.placeholder || formItem.message" [name]="name" [formControlName]="name" + [inline]="formItem.guiOptions?.inline !== false" [allowNull]="false" > diff --git a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.html b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.html new file mode 100644 index 00000000000..aae3b42d259 --- /dev/null +++ b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.html @@ -0,0 +1,16 @@ + + + + + + diff --git a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.ts b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.ts new file mode 100644 index 00000000000..0635c85e4f0 --- /dev/null +++ b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-multi-input/dynamic-form-generator-multi-input.component.ts @@ -0,0 +1,16 @@ +import { Component, ViewEncapsulation } from '@angular/core'; + +import { BaseDynamicFormGeneratorControl } from '../base-dynamic-form-generator-control'; +import { dynamicFormFieldProvider, dynamicFormGroupChildProvider } from '../providers/providers'; + +@Component({ + selector: 'fdp-dynamic-form-generator-multi-input', + templateUrl: './dynamic-form-generator-multi-input.component.html', + encapsulation: ViewEncapsulation.None, + viewProviders: [dynamicFormFieldProvider, dynamicFormGroupChildProvider] +}) +export class DynamicFormGeneratorMultiInputComponent extends BaseDynamicFormGeneratorControl { + constructor() { + super(); + } +} diff --git a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-select/dynamic-form-generator-select.component.html b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-select/dynamic-form-generator-select.component.html index f65ebbb420d..53b85d8fcb2 100644 --- a/libs/platform/src/lib/form/form-generator/dynamic-form-generator-select/dynamic-form-generator-select.component.html +++ b/libs/platform/src/lib/form/form-generator/dynamic-form-generator-select/dynamic-form-generator-select.component.html @@ -3,6 +3,7 @@ { - const obj = formItem.onchange(value, this.forms); + const obj = formItem.onchange(value, this.forms, formControl); await this._getFunctionValue(obj); }); @@ -217,6 +217,7 @@ export class FormGeneratorService implements OnDestroy { if (formItem.transformer) { const obj = formItem.transformer(formValue[i], formValue, formItem); + formValue[i] = await this._getFunctionValue(obj); } @@ -253,13 +254,10 @@ export class FormGeneratorService implements OnDestroy { c.types.filter((t) => types.includes(t)) ); - for (const existingComponent of existingComponents) { + existingComponents.forEach((existingComponent, index) => { existingComponent.types = existingComponent.types.filter((t) => !types.includes(t)); - - const index = this._formComponentDefinitions.findIndex((c) => c.component === existingComponent.component); - this._formComponentDefinitions[index] = existingComponent; - } + }); this._formComponentDefinitions.push({ types, diff --git a/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.html b/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.html index 6570570ca76..466314dc4d6 100644 --- a/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.html +++ b/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.html @@ -17,6 +17,7 @@ [fieldColumnLayout]="fieldColumnLayout" [gapColumnLayout]="gapColumnLayout" [unifiedLayout]="unifiedLayout" + [inlineColumnLayout]="inlineColumnLayout" > @@ -75,7 +76,7 @@ - + diff --git a/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.ts b/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.ts index 9500a6387ac..02b57b22061 100644 --- a/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.ts +++ b/libs/platform/src/lib/form/form-generator/form-generator/form-generator.component.ts @@ -140,10 +140,21 @@ export class FormGeneratorComponent implements OnDestroy { @Input() gapColumnLayout: ColumnLayout = DefaultGapLayout; - /** Whether or not all form items should have identical layout provided for form group. */ + /** Whether all form items should have identical layout provided for form group. */ @Input() unifiedLayout = true; + @Input() + inlineColumnLayout: ColumnLayout = DefaultVerticalFieldLayout; + + /** + * @hidden + * Removes extra empty row. + * TODO: remove after #7533 has been fixed. + */ + @Input() + noAdditionalContent = false; + /** * @description Event which notifies parent component that the form has been successfuly created * and all controls are in place. @@ -218,6 +229,14 @@ export class FormGeneratorComponent implements OnDestroy { this._onDestroy$.complete(); } + /** + * Refreshes form items visibility with 'when' condition. + */ + async refreshStepsVisibility(): Promise { + this.shouldShowFields = await this._fgService.checkVisibleFormItems(this.form); + this._cd.markForCheck(); + } + /** * @hidden */ diff --git a/libs/platform/src/lib/form/form-generator/interfaces/dynamic-form-item.ts b/libs/platform/src/lib/form/form-generator/interfaces/dynamic-form-item.ts index af7cdb44c30..acf6a62d65b 100644 --- a/libs/platform/src/lib/form/form-generator/interfaces/dynamic-form-item.ts +++ b/libs/platform/src/lib/form/form-generator/interfaces/dynamic-form-item.ts @@ -5,6 +5,7 @@ import { ContentDensity } from '@fundamental-ngx/core/utils'; import { InlineLayout, ColumnLayout, HintPlacement, LabelLayout, SelectItem } from '@fundamental-ngx/platform/shared'; import { InputType } from '../../input/input.component'; import { DynamicFormGroup } from './dynamic-form-group'; +import { DynamicFormControl } from '../dynamic-form-control'; export type DynamicFormItemChoices = number | string | SelectItem; export type DynamicFormItemValidationResult = null | boolean | string; @@ -169,14 +170,18 @@ export interface DynamicFormFieldItem { * @param formValue the form value hash. * @returns Boolean */ - when?: (formValue?: DynamicFormValue) => boolean | Promise | Observable; + when?: (formValue: DynamicFormValue) => boolean | Promise | Observable; /** * @description Callback function that is triggered after field value has been changed. * @param fieldValue Field value. * @param formGeneratorService Form generator service instance. */ - onchange?: (fieldValue?: any, forms?: Map) => void | Promise | Observable; + onchange?: ( + fieldValue: any, + forms: Map, + control: DynamicFormControl + ) => void | Promise | Observable; /** * @hidden diff --git a/libs/platform/src/lib/form/form-group/form-field/form-field.component.html b/libs/platform/src/lib/form/form-group/form-field/form-field.component.html index 53762a0544e..42e380bfdd7 100644 --- a/libs/platform/src/lib/form/form-group/form-field/form-field.component.html +++ b/libs/platform/src/lib/form/form-group/form-field/form-field.component.html @@ -1,6 +1,6 @@

-
+