diff --git a/.storybook/preview.js b/.storybook/preview.js index 1542b2d..b3e0107 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -29,7 +29,8 @@ export const parameters = { "Basic", "Sorting", "Nested data", - "Row click & hover", + "Row click & active state", + "Row selection", "Pagination", ["Setup", "Client-side", "Server-side"], "Custom templates", diff --git a/documentation.json b/documentation.json index bad1cfb..3bd6f18 100644 --- a/documentation.json +++ b/documentation.json @@ -260,6 +260,124 @@ "ngname": "highlight", "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'highlight',\n standalone: true,\n})\nexport class HighlightPipe implements PipeTransform {\n transform(text: any, searchTerm: string | null): string {\n if (!searchTerm) {\n return text;\n }\n const haystackAlwaysString = text + '';\n let highlightedText = haystackAlwaysString; // fallback\n\n let searchPattern;\n try {\n searchPattern = new RegExp(\n '(' +\n // @ts-ignore\n searchTerm\n .toLowerCase()\n .match(/\".*?\"|[^ ]+/g) // extract words\n .map(\n (needle) => needle.replace(/\"(.*?)\"/, '$1') // strip away '\"'\n )\n .join('|') + // combine words\n ')',\n 'ig'\n );\n } catch (error) {\n return highlightedText;\n }\n\n const containsTagPattern = /(<.*?>)(.*)(<\\/.*?>)/gi;\n const containsTagMatches = containsTagPattern.exec(haystackAlwaysString);\n\n if (containsTagMatches) {\n // tag exists in haystack\n highlightedText =\n containsTagMatches[1] +\n containsTagMatches[2].replace(\n searchPattern,\n '$1'\n ) +\n containsTagMatches[3];\n } else {\n highlightedText = haystackAlwaysString.replace(\n searchPattern,\n '$1'\n );\n }\n\n return highlightedText;\n }\n}\n" }, + { + "name": "RowSelectionPipe", + "id": "pipe-RowSelectionPipe-d56e24fac0c739a507ae0a67e419a21abda16fef09d1fd488cd1e5b746a5675d9b41c6c5412a2fa86c183630fe883c0888a6d1353d0a53f32f01b37fb63f5e06", + "file": "projects/core/src/lib/pipes/row-selection.pipe.ts", + "type": "pipe", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "rawdescription": "\n", + "properties": [], + "methods": [ + { + "name": "transform", + "args": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "comparator", + "type": "function", + "deprecated": false, + "deprecationMessage": "", + "function": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "" + } + ] + }, + { + "name": "className", + "type": "string", + "deprecated": false, + "deprecationMessage": "", + "optional": true + } + ], + "optional": false, + "returnType": "string", + "typeParameters": [], + "line": 9, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "comparator", + "type": "function", + "deprecated": false, + "deprecationMessage": "", + "function": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "" + } + ], + "tagName": { + "text": "param" + } + }, + { + "name": "className", + "type": "string", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } + } + ] + } + ], + "ngname": "rowSelection", + "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\nimport { TableRow } from '../models/table-row.interface';\n\n@Pipe({\n name: 'rowSelection',\n standalone: true,\n})\nexport class RowSelectionPipe implements PipeTransform {\n transform(\n row: TableRow,\n selection: any,\n comparator: (row: TableRow, selection: any) => boolean,\n className?: string\n ): string {\n return className && comparator(row, selection) ? className : '';\n }\n}\n" + }, { "name": "SortClassPipe", "id": "pipe-SortClassPipe-863827bcec157b80e5ad58b4ff87c5f6565a17557ffd0599b89ab1b91376e17b2adf6498974035e450cd05d0d86f4b953c02b4de5860733e761f4615c2e3b3e9", @@ -389,15 +507,15 @@ }, { "name": "CalcFunc", - "id": "interface-CalcFunc-9d7d31de0e62a8c41a418f3df63c9524673816d262c4cfcdf8a253c65a17aa4944e83c465e6eda12511b1d9fdc68afd78f3813c272611cc45c82ddcecde20d18", + "id": "interface-CalcFunc-a056644fdde68f4dadffd51d84324a6a3f2f5c5703bb1e4cd6e4c9d7f843a429f1dc80a81e8f220d59a2976a9e0ac93b92fb890d2aacf2769262f336f6f3feaa", "file": "projects/core/src/lib/models/table-config.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n rowHover?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", + "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n /** Toggle row active state on mouse enter/leave (hover)

**Default:** `false`

*/\n activateRowOnHover?: boolean;\n /** Toggle row active state on keyboard navigation

**Default:** `false`

*/\n activateRowOnKeyboardNavigation?: boolean;\n deactivateRowOnLostFocus?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", "properties": [ { - "id": "call-declaration-9d7d31de0e62a8c41a418f3df63c9524673816d262c4cfcdf8a253c65a17aa4944e83c465e6eda12511b1d9fdc68afd78f3813c272611cc45c82ddcecde20d18", + "id": "call-declaration-a056644fdde68f4dadffd51d84324a6a3f2f5c5703bb1e4cd6e4c9d7f843a429f1dc80a81e8f220d59a2976a9e0ac93b92fb890d2aacf2769262f336f6f3feaa", "args": [ { "name": "data", @@ -413,7 +531,7 @@ } ], "returnType": "number | string", - "line": 43, + "line": 46, "deprecated": false, "deprecationMessage": "" } @@ -515,12 +633,12 @@ }, { "name": "GtPageChangeEvent", - "id": "interface-GtPageChangeEvent-09ae9ef79f3f3c2930c5af69a9e746354e83f59c6576b164d505a68c5ba41df5c2e353dac6577d7ef7d1862829105aa59ed39bd469d4aa4877a37acc5ab2ff21", + "id": "interface-GtPageChangeEvent-d83279f9fe73cc6d6d1e7e259619f31f4a9007af673e4ae13f3fab2303aee9598ae0a131ae03eb8db0d14d394585cf674a760da87a8eb444236f77f50ff1aec3", "file": "projects/core/src/lib/models/table-events.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowHoverEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", + "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowActiveEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent | KeyboardEvent;\n}\n\nexport interface GtRowSelectEvent {\n row: R | null;\n index: number | null;\n event?: KeyboardEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", "properties": [ { "name": "index", @@ -529,7 +647,7 @@ "type": "number", "optional": false, "description": "", - "line": 25 + "line": 31 } ], "indexSignatures": [], @@ -686,14 +804,55 @@ "kind": 165, "methods": [] }, + { + "name": "GtRowActiveEvent", + "id": "interface-GtRowActiveEvent-d83279f9fe73cc6d6d1e7e259619f31f4a9007af673e4ae13f3fab2303aee9598ae0a131ae03eb8db0d14d394585cf674a760da87a8eb444236f77f50ff1aec3", + "file": "projects/core/src/lib/models/table-events.interface.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowActiveEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent | KeyboardEvent;\n}\n\nexport interface GtRowSelectEvent {\n row: R | null;\n index: number | null;\n event?: KeyboardEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", + "properties": [ + { + "name": "event", + "deprecated": false, + "deprecationMessage": "", + "type": "MouseEvent | KeyboardEvent", + "optional": true, + "description": "", + "line": 13 + }, + { + "name": "index", + "deprecated": false, + "deprecationMessage": "", + "type": "number | null", + "optional": false, + "description": "", + "line": 12 + }, + { + "name": "row", + "deprecated": false, + "deprecationMessage": "", + "type": "R | null", + "optional": false, + "description": "", + "line": 11 + } + ], + "indexSignatures": [], + "kind": 165, + "methods": [] + }, { "name": "GtRowClickEvent", - "id": "interface-GtRowClickEvent-09ae9ef79f3f3c2930c5af69a9e746354e83f59c6576b164d505a68c5ba41df5c2e353dac6577d7ef7d1862829105aa59ed39bd469d4aa4877a37acc5ab2ff21", + "id": "interface-GtRowClickEvent-d83279f9fe73cc6d6d1e7e259619f31f4a9007af673e4ae13f3fab2303aee9598ae0a131ae03eb8db0d14d394585cf674a760da87a8eb444236f77f50ff1aec3", "file": "projects/core/src/lib/models/table-events.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowHoverEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", + "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowActiveEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent | KeyboardEvent;\n}\n\nexport interface GtRowSelectEvent {\n row: R | null;\n index: number | null;\n event?: KeyboardEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", "properties": [ { "name": "event", @@ -728,22 +887,22 @@ "methods": [] }, { - "name": "GtRowHoverEvent", - "id": "interface-GtRowHoverEvent-09ae9ef79f3f3c2930c5af69a9e746354e83f59c6576b164d505a68c5ba41df5c2e353dac6577d7ef7d1862829105aa59ed39bd469d4aa4877a37acc5ab2ff21", + "name": "GtRowSelectEvent", + "id": "interface-GtRowSelectEvent-d83279f9fe73cc6d6d1e7e259619f31f4a9007af673e4ae13f3fab2303aee9598ae0a131ae03eb8db0d14d394585cf674a760da87a8eb444236f77f50ff1aec3", "file": "projects/core/src/lib/models/table-events.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowHoverEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", + "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowActiveEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent | KeyboardEvent;\n}\n\nexport interface GtRowSelectEvent {\n row: R | null;\n index: number | null;\n event?: KeyboardEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", "properties": [ { "name": "event", "deprecated": false, "deprecationMessage": "", - "type": "MouseEvent", + "type": "KeyboardEvent", "optional": true, "description": "", - "line": 13 + "line": 19 }, { "name": "index", @@ -752,7 +911,7 @@ "type": "number | null", "optional": false, "description": "", - "line": 12 + "line": 18 }, { "name": "row", @@ -761,7 +920,7 @@ "type": "R | null", "optional": false, "description": "", - "line": 11 + "line": 17 } ], "indexSignatures": [], @@ -802,12 +961,12 @@ }, { "name": "GtSortEvent", - "id": "interface-GtSortEvent-09ae9ef79f3f3c2930c5af69a9e746354e83f59c6576b164d505a68c5ba41df5c2e353dac6577d7ef7d1862829105aa59ed39bd469d4aa4877a37acc5ab2ff21", + "id": "interface-GtSortEvent-d83279f9fe73cc6d6d1e7e259619f31f4a9007af673e4ae13f3fab2303aee9598ae0a131ae03eb8db0d14d394585cf674a760da87a8eb444236f77f50ff1aec3", "file": "projects/core/src/lib/models/table-events.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowHoverEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", + "sourceCode": "import { TableRow } from './table-row.interface';\nimport { GtOrder, GtSortOrder } from './table-sort.interface';\n\nexport interface GtRowClickEvent {\n row: R;\n index: number;\n event: MouseEvent;\n}\n\nexport interface GtRowActiveEvent {\n row: R | null;\n index: number | null;\n event?: MouseEvent | KeyboardEvent;\n}\n\nexport interface GtRowSelectEvent {\n row: R | null;\n index: number | null;\n event?: KeyboardEvent;\n}\n\nexport interface GtSortEvent {\n key: keyof R;\n order: GtOrder;\n currentSortOrder: GtSortOrder;\n event?: MouseEvent;\n addSortKey: boolean /** Add additional key to sort on multiple properties? True if the user is holding shift while sorting */;\n}\n\nexport interface GtPageChangeEvent {\n index: number;\n}\n", "properties": [ { "name": "addSortKey", @@ -816,7 +975,7 @@ "type": "boolean", "optional": false, "description": "", - "line": 21 + "line": 27 }, { "name": "currentSortOrder", @@ -825,7 +984,7 @@ "type": "GtSortOrder", "optional": false, "description": "", - "line": 19 + "line": 25 }, { "name": "event", @@ -834,7 +993,7 @@ "type": "MouseEvent", "optional": true, "description": "", - "line": 20 + "line": 26 }, { "name": "key", @@ -843,7 +1002,7 @@ "type": "", "optional": false, "description": "", - "line": 17 + "line": 23 }, { "name": "order", @@ -852,7 +1011,7 @@ "type": "GtOrder", "optional": false, "description": "", - "line": 18 + "line": 24 } ], "indexSignatures": [], @@ -861,12 +1020,12 @@ }, { "name": "LazyLoadingData", - "id": "interface-LazyLoadingData-cb0f40783d1b367a19972a3b81e3d412ca580af909300bf9ee947fdcb514868d51bd4bbe662c4629e06be3b886763c1e2b2cb9ad5960daf2a9bee4c93c234cff", + "id": "interface-LazyLoadingData-84632bae79cd4899d6deb72214bfa38d32b216248b69830bb0ca440dec337ac8231dd42726cf0c8ab2fb2873ad182bf71c3f00fe899de8e6ebebf0d1ca5dd422", "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n PaginationComponent,\n TableConfig,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { GtPaginationInfo } from '../../../../../core/src/lib/models/gt-pagination';\nimport { sortOrderToParams } from '../../../../../core/src/lib/utilities/utilities';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const Pagination: Story = (\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n});\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n GtPaginationInfo,\n sortOrderToParams,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const ServerSidePagination: Story = (\n args: ServerSidePaginationComponent\n) => ({\n props: args,\n component: ServerSidePaginationComponent,\n});\n", "properties": [ { "name": "birthday", @@ -875,7 +1034,7 @@ "type": "string", "optional": false, "description": "", - "line": 25 + "line": 24 }, { "name": "email", @@ -884,7 +1043,7 @@ "type": "string", "optional": false, "description": "", - "line": 26 + "line": 25 }, { "name": "favorite_color", @@ -893,7 +1052,7 @@ "type": "string", "optional": false, "description": "", - "line": 27 + "line": 26 }, { "name": "first_name", @@ -902,7 +1061,7 @@ "type": "string", "optional": false, "description": "", - "line": 28 + "line": 27 }, { "name": "gender", @@ -911,7 +1070,7 @@ "type": "string", "optional": false, "description": "", - "line": 29 + "line": 28 }, { "name": "id", @@ -920,7 +1079,7 @@ "type": "number", "optional": false, "description": "", - "line": 30 + "line": 29 }, { "name": "last_name", @@ -929,7 +1088,7 @@ "type": "string", "optional": false, "description": "", - "line": 31 + "line": 30 } ], "indexSignatures": [], @@ -938,12 +1097,12 @@ }, { "name": "LazyLoadingResponse", - "id": "interface-LazyLoadingResponse-cb0f40783d1b367a19972a3b81e3d412ca580af909300bf9ee947fdcb514868d51bd4bbe662c4629e06be3b886763c1e2b2cb9ad5960daf2a9bee4c93c234cff", + "id": "interface-LazyLoadingResponse-84632bae79cd4899d6deb72214bfa38d32b216248b69830bb0ca440dec337ac8231dd42726cf0c8ab2fb2873ad182bf71c3f00fe899de8e6ebebf0d1ca5dd422", "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n PaginationComponent,\n TableConfig,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { GtPaginationInfo } from '../../../../../core/src/lib/models/gt-pagination';\nimport { sortOrderToParams } from '../../../../../core/src/lib/utilities/utilities';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const Pagination: Story = (\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n});\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n GtPaginationInfo,\n sortOrderToParams,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const ServerSidePagination: Story = (\n args: ServerSidePaginationComponent\n) => ({\n props: args,\n component: ServerSidePaginationComponent,\n});\n", "properties": [ { "name": "data", @@ -952,7 +1111,7 @@ "type": "Array", "optional": false, "description": "", - "line": 35 + "line": 34 }, { "name": "error", @@ -961,7 +1120,7 @@ "type": "any", "optional": false, "description": "", - "line": 44 + "line": 43 }, { "name": "filters", @@ -970,7 +1129,7 @@ "type": "[]", "optional": false, "description": "", - "line": 38 + "line": 37 }, { "name": "paging", @@ -979,7 +1138,7 @@ "type": "GtPaginationInfo", "optional": false, "description": "", - "line": 36 + "line": 35 }, { "name": "request", @@ -988,7 +1147,7 @@ "type": "any", "optional": false, "description": "", - "line": 43 + "line": 42 }, { "name": "sorting", @@ -997,7 +1156,7 @@ "type": "GtSortOrder", "optional": false, "description": "", - "line": 37 + "line": 36 } ], "indexSignatures": [], @@ -1068,6 +1227,83 @@ "kind": 165, "methods": [] }, + { + "name": "RowData", + "id": "interface-RowData-615810d13a0e0518a5f3de6e1a5cbbb78f9461cd1591bfeb19c86775816e1e82832c0c4c5b7382797b539938151879c9408c0bef4674b2ca686b908494385b02", + "file": "projects/docs/src/app/examples/row-select/row-select.component.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';\nimport {\n GtRowSelectEvent,\n GtRowClickEvent,\n TableRow,\n GtRowActiveEvent,\n} from '@angular-generic-table/core';\nimport { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';\nimport {\n map,\n pluck,\n shareReplay,\n startWith,\n take,\n takeUntil,\n tap,\n} from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { FormControl } from '@angular/forms';\ninterface RowData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: 'Female' | 'Male';\n id: number;\n last_name: string;\n}\n@Component({\n templateUrl: './row-select.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n :host ::ng-deep .table > tbody > tr {\n cursor: pointer;\n }\n `,\n ],\n})\nexport class RowSelectComponent implements OnDestroy {\n constructor(private http: HttpClient) {}\n\n get activateOnRowHover$(): Observable {\n return this._activateOnRowHover$.asObservable();\n }\n get activateOnRowHover(): boolean {\n return this._activateOnRowHover$.getValue();\n }\n toggleRowHover() {\n this._activateOnRowHover$.next(!this.activateOnRowHover);\n }\n private _activateOnRowHover$ = new BehaviorSubject(true);\n\n get activateOnNavigation$(): Observable {\n return this._activateOnNavigation$.asObservable();\n }\n get activateOnNavigation(): boolean {\n return this._activateOnNavigation$.getValue();\n }\n toggleRowNavigation() {\n this._activateOnNavigation$.next(!this.activateOnNavigation);\n }\n private _activateOnNavigation$ = new BehaviorSubject(true);\n\n private unsubscribe$ = new Subject();\n loading$ = new BehaviorSubject(true);\n customClassNames = {\n selectedRow: 'table-active',\n };\n lengthCtrl = new FormControl(15);\n data$ = this.http\n .get<{ data: TableRow[] }>(\n 'https://private-730c61-generictable.apiary-mock.com/data'\n )\n .pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false)),\n takeUntil(this.unsubscribe$),\n shareReplay(1)\n );\n config$ = combineLatest([\n this.lengthCtrl.valueChanges.pipe(\n startWith(this.lengthCtrl.value),\n map((length) =>\n length ? { pagination: { length: length < 0 ? 0 : length } } : {}\n )\n ),\n this.activateOnRowHover$,\n this.activateOnNavigation$,\n ]).pipe(\n map(\n ([pagination, activateRowOnHover, activateRowOnKeyboardNavigation]) => ({\n columns: {\n id: {\n sortable: true,\n },\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n email: {\n sortable: true,\n },\n },\n ...pagination,\n rowClick: true,\n activateRowOnHover,\n activateRowOnKeyboardNavigation,\n rowSelect: true,\n })\n )\n );\n\n setActiveRow(event: GtRowActiveEvent) {\n console.log(event);\n this._activeRow$.next(event.row);\n }\n get activeRow$() {\n return this._activeRow$.asObservable();\n }\n private _activeRow$ = new BehaviorSubject(null);\n isSelected(row: RowData, selection: typeof this.selection) {\n return selection[row.id];\n }\n\n selection: { [key: string]: boolean } = {};\n\n selectRow(event: GtRowClickEvent | GtRowSelectEvent) {\n const selection = { ...this.selection };\n const row = event.row;\n if (!row) return;\n if (!selection[row.id]) {\n selection[row.id] = true;\n } else {\n delete selection[row.id];\n }\n // update the selection\n this.selection = selection;\n }\n\n get isAllSelected() {\n return Object.keys(this.selection).length > 0;\n }\n toggleAll() {\n if (this.isAllSelected) {\n this.selection = {};\n return;\n }\n const selection = { ...this.selection };\n this.data$.pipe(take(1), takeUntil(this.unsubscribe$)).subscribe((data) => {\n data.forEach((row, index) => {\n selection[index] = true;\n });\n });\n this.selection = selection;\n }\n\n ngOnDestroy() {\n this.unsubscribe$.next();\n this.unsubscribe$.complete();\n }\n}\n", + "properties": [ + { + "name": "birthday", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": false, + "description": "", + "line": 21 + }, + { + "name": "email", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": false, + "description": "", + "line": 22 + }, + { + "name": "favorite_color", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": false, + "description": "", + "line": 23 + }, + { + "name": "first_name", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": false, + "description": "", + "line": 24 + }, + { + "name": "gender", + "deprecated": false, + "deprecationMessage": "", + "type": "\"Female\" | \"Male\"", + "optional": false, + "description": "", + "line": 25 + }, + { + "name": "id", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "optional": false, + "description": "", + "line": 26 + }, + { + "name": "last_name", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": false, + "description": "", + "line": 27 + } + ], + "indexSignatures": [], + "kind": 165, + "methods": [] + }, { "name": "SortingData", "id": "interface-SortingData-4ef62adeec6897b8e534c6091a292d9ae7fd533c6079340b6b9edf96420c885a8d8a92be14944773a784a876cca1d3299a6e8762760799e0958f49349cb5bd9a", @@ -1233,13 +1469,33 @@ }, { "name": "TableConfig", - "id": "interface-TableConfig-9d7d31de0e62a8c41a418f3df63c9524673816d262c4cfcdf8a253c65a17aa4944e83c465e6eda12511b1d9fdc68afd78f3813c272611cc45c82ddcecde20d18", + "id": "interface-TableConfig-a056644fdde68f4dadffd51d84324a6a3f2f5c5703bb1e4cd6e4c9d7f843a429f1dc80a81e8f220d59a2976a9e0ac93b92fb890d2aacf2769262f336f6f3feaa", "file": "projects/core/src/lib/models/table-config.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n rowHover?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", + "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n /** Toggle row active state on mouse enter/leave (hover)

**Default:** `false`

*/\n activateRowOnHover?: boolean;\n /** Toggle row active state on keyboard navigation

**Default:** `false`

*/\n activateRowOnKeyboardNavigation?: boolean;\n deactivateRowOnLostFocus?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", "properties": [ + { + "name": "activateRowOnHover", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": true, + "description": "

Toggle row active state on mouse enter/leave (hover)

Default: false

\n", + "line": 27, + "rawdescription": "\nToggle row active state on mouse enter/leave (hover)

**Default:** `false`

" + }, + { + "name": "activateRowOnKeyboardNavigation", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": true, + "description": "

Toggle row active state on keyboard navigation

Default: false

\n", + "line": 29, + "rawdescription": "\nToggle row active state on keyboard navigation

**Default:** `false`

" + }, { "name": "class", "deprecated": false, @@ -1247,7 +1503,7 @@ "type": "string", "optional": true, "description": "

Add one or more CSS classes to the table element e.g. table table-striped table-bordered.

Default: table

\n", - "line": 16, + "line": 15, "rawdescription": "\nAdd one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

" }, { @@ -1257,18 +1513,27 @@ "type": "", "optional": true, "description": "", - "line": 20 + "line": 19 }, { - "name": "disableTableSort", + "name": "deactivateRowOnLostFocus", "deprecated": false, "deprecationMessage": "", "type": "boolean", "optional": true, - "description": "

Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

Default: false

\n", - "line": 6, - "rawdescription": "\nDisable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

" - }, + "description": "", + "line": 30 + }, + { + "name": "disableTableSort", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": true, + "description": "

Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

Default: false

\n", + "line": 6, + "rawdescription": "\nDisable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

" + }, { "name": "footer", "deprecated": false, @@ -1276,7 +1541,7 @@ "type": "literal type", "optional": true, "description": "", - "line": 28 + "line": 31 }, { "name": "mobileLayout", @@ -1295,7 +1560,7 @@ "type": "literal type", "optional": true, "description": "", - "line": 23 + "line": 22 }, { "name": "rowClick", @@ -1304,16 +1569,7 @@ "type": "boolean", "optional": true, "description": "", - "line": 26 - }, - { - "name": "rowHover", - "deprecated": false, - "deprecationMessage": "", - "type": "boolean", - "optional": true, - "description": "", - "line": 27 + "line": 25 }, { "name": "rows", @@ -1322,7 +1578,7 @@ "type": "", "optional": true, "description": "", - "line": 17 + "line": 16 }, { "name": "stickyHeaders", @@ -1341,16 +1597,16 @@ }, { "name": "TableFooterColumn", - "id": "interface-TableFooterColumn-9d7d31de0e62a8c41a418f3df63c9524673816d262c4cfcdf8a253c65a17aa4944e83c465e6eda12511b1d9fdc68afd78f3813c272611cc45c82ddcecde20d18", + "id": "interface-TableFooterColumn-a056644fdde68f4dadffd51d84324a6a3f2f5c5703bb1e4cd6e4c9d7f843a429f1dc80a81e8f220d59a2976a9e0ac93b92fb890d2aacf2769262f336f6f3feaa", "file": "projects/core/src/lib/models/table-config.interface.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n rowHover?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", + "sourceCode": "import { TableColumn } from './table-column.interface';\nimport { TableRow } from './table-row.interface';\n\nexport interface TableConfig {\n /** Disable sorting of table data, useful when sorting is handled externally e.g. server-side pagination. Table will still use sortOrder to visually show how the data is sorted.

**Default:** `false`

*/\n disableTableSort?: boolean;\n /** Allows for a more mobile friendly layout by displaying columns as rows instead. This is accomplished with css by adding `table-mobile` class to table element and adding column headers as data labels to each row.

**Default:** `false`

*/\n mobileLayout?: boolean;\n /** Make row and/or column headers sticky by adding `gt-sticky-row-header` and `gt-sticky-column-header` to table to alter table behaviour using css. Please note that in order for headers to be sticky, table can't be placed inside element with overflow set to auto.

**Default:** `{row:false, column:false}`

*/\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/\n class?: string;\n rows?: {\n [Property in keyof R]: TableColumn;\n };\n columns?: {\n [Property in keyof R]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n rowClick?: boolean;\n /** Toggle row active state on mouse enter/leave (hover)

**Default:** `false`

*/\n activateRowOnHover?: boolean;\n /** Toggle row active state on keyboard navigation

**Default:** `false`

*/\n activateRowOnKeyboardNavigation?: boolean;\n deactivateRowOnLostFocus?: boolean;\n footer?: {\n headers?: {\n [key: FooterCalculation | string]: string | boolean;\n };\n columns?: {\n [Property in keyof R]: Partial>;\n };\n rowOrder?: Array;\n emptyContent?: string;\n };\n}\n\ninterface TableFooterColumn {\n [key: FooterCalculation | string]: boolean | number | string | CalcFunc;\n}\ninterface CalcFunc {\n (data: Array, key: keyof R): number | string;\n}\ntype FooterCalculation = 'sum' | 'avg' | 'count' | 'max' | 'min';\n", "properties": [], "indexSignatures": [ { - "id": "index-declaration-9d7d31de0e62a8c41a418f3df63c9524673816d262c4cfcdf8a253c65a17aa4944e83c465e6eda12511b1d9fdc68afd78f3813c272611cc45c82ddcecde20d18", + "id": "index-declaration-a056644fdde68f4dadffd51d84324a6a3f2f5c5703bb1e4cd6e4c9d7f843a429f1dc80a81e8f220d59a2976a9e0ac93b92fb890d2aacf2769262f336f6f3feaa", "args": [ { "name": "key", @@ -1360,7 +1616,7 @@ } ], "returnType": "boolean | number | string | CalcFunc", - "line": 40, + "line": 43, "deprecated": false, "deprecationMessage": "" } @@ -2360,7 +2616,7 @@ }, { "name": "CoreComponent", - "id": "component-CoreComponent-133594ca43b9430b5e9b887c0c48710604c4288035fd14f53551666d0baf6d0d9c1a25053fa595151a9452976a0e8a6d4c2fa4d2282824dbdb49208b57553d32", + "id": "component-CoreComponent-92f79bc9f3bdc89edef57fe938e9af41c50d33ac2269619c82872b26d38301853a92d9bb98afadec987a5d41ff1f615ba579567700688d6e344b9a6d150dd3bb", "file": "projects/core/src/lib/core.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [], @@ -2380,113 +2636,728 @@ "name": "config", "deprecated": false, "deprecationMessage": "", - "line": 114, + "line": 231, "type": "Observable | TableConfig", "decorators": [] }, { - "name": "data", + "name": "customClasses", "deprecated": false, "deprecationMessage": "", - "line": 119, - "type": "Observable | Array", + "jsdoctags": [ + { + "pos": 3864, + "end": 3956, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 3865, + "end": 3876, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

An object that contains custom classes for various elements in the table.

\n" + }, + { + "pos": 3956, + "end": 4071, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 3957, + "end": 3961, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • { selectedRow: string, activeRow: string } - default classes are 'gt-selected' and 'gt-active'
  • \n
\n", + "typeExpression": { + "pos": 3962, + "end": 3970, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 3963, + "end": 3969, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 147 + } + } + } + ], + "rawdescription": "\ncustomClasses\n", + "description": "

customClasses

\n", + "line": 161, + "type": "Partial<>", "decorators": [] }, { - "name": "loading", + "name": "data", "deprecated": false, "deprecationMessage": "", - "line": 88, - "type": "Observable | boolean", + "line": 238, + "type": "Observable | Array | null", "decorators": [] }, { - "name": "paginationIndex", + "name": "generateRowId", + "defaultValue": "true", "deprecated": false, "deprecationMessage": "", - "line": 92, - "type": "number", + "jsdoctags": [ + { + "pos": 5421, + "end": 5525, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 5422, + "end": 5433, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

Whether or not to generate a unique id for each row in the table. Defaults to true.

\n" + }, + { + "pos": 5525, + "end": 5544, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 5526, + "end": 5530, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "", + "typeExpression": { + "pos": 5531, + "end": 5540, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 5532, + "end": 5539, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 133 + } + } + } + ], + "rawdescription": "\ngenerateRowId\n", + "description": "

generateRowId

\n", + "line": 205, + "type": "boolean", "decorators": [] }, { - "name": "pagingInfo", + "name": "isRowSelectedFn", "deprecated": false, "deprecationMessage": "", - "line": 96, - "type": "GtPaginationInfo | null", + "jsdoctags": [ + { + "pos": 4433, + "end": 4500, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 4434, + "end": 4445, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

Function to determine if row is selected or not.

\n" + }, + { + "pos": 4500, + "end": 4664, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 4501, + "end": 4505, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "

A function that receives a row object and optional state for current selection that can be used to determine if row should be marked as selected or not.

\n", + "typeExpression": { + "pos": 4506, + "end": 4510, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 4507, + "end": 4509, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 4507, + "end": 4509, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "fn" + } + } + } + } + ], + "rawdescription": "\nisRowSelectedFn", + "description": "

isRowSelectedFn

\n", + "line": 177, + "type": "boolean", "decorators": [] }, { - "name": "search", + "name": "loading", "deprecated": false, "deprecationMessage": "", - "line": 109, - "type": "Observable | string | null", + "line": 133, + "type": "Observable | boolean", "decorators": [] }, { - "name": "sortOrder", + "name": "navigationKeys", "deprecated": false, "deprecationMessage": "", - "line": 123, - "type": "any", + "jsdoctags": [ + { + "pos": 2223, + "end": 2402, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2224, + "end": 2235, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

An array of keyboard keys that will trigger navigation and active row, currently only supports arrow keys, home and end (omit key name from array to disable it)

\n" + }, + { + "pos": 2402, + "end": 2424, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 2403, + "end": 2407, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "", + "typeExpression": { + "pos": 2408, + "end": 2418, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 2409, + "end": 2417, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 182, + "elementType": { + "pos": 2409, + "end": 2415, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 149 + } + } + } + }, + { + "pos": 2424, + "end": 2503, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2425, + "end": 2432, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 88, + "escapedText": "default" + }, + "comment": "

['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']

\n" + } + ], + "rawdescription": "\nnavigationKeys\n", + "description": "

navigationKeys

\n", + "line": 103, + "type": "", "decorators": [] - } - ], - "outputsClass": [ + }, { - "name": "columnSort", - "defaultValue": "new EventEmitter()", + "name": "paginationIndex", "deprecated": false, "deprecationMessage": "", - "line": 139, - "type": "EventEmitter" + "line": 137, + "type": "number", + "decorators": [] }, { - "name": "pageChange", - "defaultValue": "new EventEmitter()", + "name": "pagingInfo", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\npage change event - emitted when current page/index changes for pagination", - "description": "

page change event - emitted when current page/index changes for pagination

\n", - "line": 141, - "type": "EventEmitter" + "line": 145, + "type": "GtPaginationInfo | null", + "decorators": [] }, { - "name": "rowClick", - "defaultValue": "new EventEmitter()", + "name": "rowIdKey", "deprecated": false, "deprecationMessage": "", - "line": 130, - "type": "EventEmitter" + "jsdoctags": [ + { + "pos": 5164, + "end": 5333, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 5165, + "end": 5176, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

row key to use as unique id for table row. If passed, table won't generate unique ids for each row but instead use key to retrieve unique id from row.

\n" + }, + { + "pos": 5333, + "end": 5351, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 5334, + "end": 5338, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "", + "typeExpression": { + "pos": 5339, + "end": 5347, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 5340, + "end": 5346, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 149 + } + } + } + ], + "rawdescription": "\nrowIdKey\n", + "description": "

rowIdKey

\n", + "line": 199, + "type": "string | undefined", + "decorators": [] }, { - "name": "rowHover", - "defaultValue": "new EventEmitter()", + "name": "search", "deprecated": false, "deprecationMessage": "", - "line": 138, - "type": "EventEmitter" + "line": 226, + "type": "Observable | string | null", + "decorators": [] }, { - "name": "sortOrderChange", - "defaultValue": "new EventEmitter>()", + "name": "selection", + "defaultValue": "{}", "deprecated": false, "deprecationMessage": "", - "line": 131, - "type": "EventEmitter" - } - ], - "propertiesClass": [ - { - "name": "_currentPaginationIndex$", - "defaultValue": "new BehaviorSubject(0)", + "jsdoctags": [ + { + "pos": 4925, + "end": 5093, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 4926, + "end": 4937, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

An object that contains the currently selected row(s) in the table. It's passed to the selection function to determine which rows should be selected.

\n" + }, + { + "pos": 5093, + "end": 5108, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 5094, + "end": 5098, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "", + "typeExpression": { + "pos": 5099, + "end": 5104, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 5100, + "end": 5103, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 130 + } + } + } + ], + "rawdescription": "\nselection\n", + "description": "

selection

\n", + "line": 193, + "type": "any", + "decorators": [] + }, + { + "name": "selectKeys", + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "pos": 2825, + "end": 2942, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2826, + "end": 2837, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

An array of keyboard keys that will trigger row selection (omit key name from array to disable it)

\n" + }, + { + "pos": 2942, + "end": 2964, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 2943, + "end": 2947, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "", + "typeExpression": { + "pos": 2948, + "end": 2958, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 2949, + "end": 2957, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 182, + "elementType": { + "pos": 2949, + "end": 2955, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 149 + } + } + } + }, + { + "pos": 2964, + "end": 2991, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2965, + "end": 2972, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 88, + "escapedText": "default" + }, + "comment": "

['Enter', ' ']

\n" + } + ], + "rawdescription": "\nselectKeys\n", + "description": "

selectKeys

\n", + "line": 124, + "type": "string[]", + "decorators": [] + }, + { + "name": "sortOrder", + "deprecated": false, + "deprecationMessage": "", + "line": 244, + "type": "GtSortOrder | any", + "decorators": [] + }, + { + "name": "trackRowByFn", + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "pos": 5614, + "end": 5755, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 5615, + "end": 5626, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

A function that returns a unique identifier for each row in the table to optimize rendering when data is added or removed.

\n" + }, + { + "pos": 5755, + "end": 5874, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 5756, + "end": 5760, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • TrackByFunction to retrieve unique id based on index and/or row. Defaults to using row[this.rowIdKey].
  • \n
\n", + "typeExpression": { + "pos": 5761, + "end": 5763, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 5761, + "end": 5763, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 5761, + "end": 5763, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "fn" + } + } + } + } + ], + "rawdescription": "\ntrackRowByFn\n", + "description": "

trackRowByFn

\n", + "line": 211, + "type": "TrackByFunction", + "decorators": [] + } + ], + "outputsClass": [ + { + "name": "columnSort", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "line": 265, + "type": "EventEmitter" + }, + { + "name": "pageChange", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\npage change event - emitted when current page/index changes for pagination", + "description": "

page change event - emitted when current page/index changes for pagination

\n", + "line": 267, + "type": "EventEmitter" + }, + { + "name": "rowActive", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "line": 264, + "type": "EventEmitter" + }, + { + "name": "rowClick", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "line": 251, + "type": "EventEmitter" + }, + { + "name": "rowSelect", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "line": 252, + "type": "EventEmitter" + }, + { + "name": "sortOrderChange", + "defaultValue": "new EventEmitter>()", + "deprecated": false, + "deprecationMessage": "", + "line": 253, + "type": "EventEmitter" + } + ], + "propertiesClass": [ + { + "name": "_currentPaginationIndex$", + "defaultValue": "new BehaviorSubject(0)", "deprecated": false, "deprecationMessage": "", "type": "BehaviorSubject", "optional": false, "description": "", - "line": 344, + "line": 494, + "modifierKind": [ + 121 + ] + }, + { + "name": "_customClasses", + "defaultValue": "{\n selectedRow: 'gt-selected',\n activeRow: 'gt-active',\n }", + "deprecated": false, + "deprecationMessage": "", + "type": "object", + "optional": false, + "description": "", + "line": 169, "modifierKind": [ 121 ] @@ -2499,7 +3370,32 @@ "type": "ReplaySubject", "optional": false, "description": "", - "line": 217, + "line": 359, + "modifierKind": [ + 121 + ] + }, + { + "name": "_isRowSelectedFn", + "deprecated": false, + "deprecationMessage": "", + "type": "any", + "optional": false, + "description": "", + "line": 187, + "modifierKind": [ + 121 + ] + }, + { + "name": "_keyboardArrowEvent$", + "defaultValue": "fromEvent(\n document,\n 'keydown'\n ).pipe(\n filter(\n (event) =>\n [...this._navigationKeys, ...this._selectKeys].indexOf(event.key) > -1\n )\n )", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 638, "modifierKind": [ 121 ] @@ -2512,7 +3408,20 @@ "type": "ReplaySubject", "optional": false, "description": "", - "line": 183, + "line": 324, + "modifierKind": [ + 121 + ] + }, + { + "name": "_navigationKeys", + "defaultValue": "[\n 'ArrowDown',\n 'ArrowUp',\n 'ArrowLeft',\n 'ArrowRight',\n 'Home',\n 'End',\n ]", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "optional": false, + "description": "", + "line": 107, "modifierKind": [ 121 ] @@ -2525,20 +3434,20 @@ "type": "", "optional": false, "description": "", - "line": 196, + "line": 337, "modifierKind": [ 121 ] }, { - "name": "_rowHover$", - "defaultValue": "new ReplaySubject(1)", + "name": "_rowActive$", + "defaultValue": "new ReplaySubject(1)", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, "description": "", - "line": 137, + "line": 263, "modifierKind": [ 121 ] @@ -2551,7 +3460,20 @@ "type": "ReplaySubject", "optional": false, "description": "", - "line": 187, + "line": 328, + "modifierKind": [ + 121 + ] + }, + { + "name": "_selectKeys", + "defaultValue": "['Enter', ' ']", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "optional": false, + "description": "", + "line": 128, "modifierKind": [ 121 ] @@ -2564,7 +3486,19 @@ "type": "BehaviorSubject", "optional": false, "description": "", - "line": 185, + "line": 326, + "modifierKind": [ + 121 + ] + }, + { + "name": "_tableConfig", + "deprecated": false, + "deprecationMessage": "", + "type": "TableConfig | undefined", + "optional": false, + "description": "", + "line": 235, "modifierKind": [ 121 ] @@ -2577,7 +3511,7 @@ "type": "BehaviorSubject", "optional": false, "description": "", - "line": 208, + "line": 349, "modifierKind": [ 121 ] @@ -2590,11 +3524,34 @@ "type": "", "optional": false, "description": "", - "line": 342, + "line": 492, "modifierKind": [ 121 ] }, + { + "name": "_unsubscribeFromKeyboardEvents$", + "defaultValue": "new Subject()", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 637, + "modifierKind": [ + 121 + ] + }, + { + "name": "activeRowIndex", + "defaultValue": "null", + "deprecated": false, + "deprecationMessage": "", + "type": "number | null", + "optional": false, + "description": "", + "line": 284 + }, { "name": "calculations$", "defaultValue": "combineLatest([this.data$, this.tableConfig$]).pipe(\n map(([data, config]) => calculate(data, config)),\n shareReplay(1)\n )", @@ -2603,7 +3560,7 @@ "type": "", "optional": false, "description": "", - "line": 273 + "line": 422 }, { "name": "colspan$", @@ -2613,7 +3570,7 @@ "type": "", "optional": false, "description": "", - "line": 366 + "line": 516 }, { "name": "columnOrder", @@ -2623,7 +3580,7 @@ "type": "", "optional": false, "description": "", - "line": 466 + "line": 616 }, { "name": "currentPaginationIndex$", @@ -2633,17 +3590,17 @@ "type": "", "optional": false, "description": "", - "line": 346 + "line": 496 }, { "name": "data$", - "defaultValue": "this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n const newData: TableRow[] = [];\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, [key, value]) => ({\n ...previousValue,\n [key]: this.nestedValue(\n row,\n value.mapTo!.path,\n value.mapTo?.missingValue\n ),\n }),\n {}\n );\n newData[i] = { ...row, ...newKeys };\n }\n data = newData;\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([of(obs), this.sortOrder$, this.searchBy$])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy?.length || table.config?.disableTableSort\n ? searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)?.sort(\n sortOnMultipleKeys(sortBy)\n )\n : table.data?.sort(sortOnMultipleKeys(sortBy));\n }),\n shareReplay(1)\n )", + "defaultValue": "this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n const newData: TableRow[] = [];\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, [key, value]) => ({\n ...previousValue,\n [key]: this.nestedValue(\n row,\n value.mapTo!.path,\n value.mapTo?.missingValue\n ),\n }),\n {}\n );\n newData[i] = { ...row, ...newKeys };\n }\n data = newData;\n }\n if (this.generateRowId && !this.rowIdKey && data.length > 0) {\n const dataWithId = [];\n for (let i = 0; i < data.length; i++) {\n dataWithId[i] = { ...data[i], _id: i };\n }\n data = dataWithId;\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([of(obs), this.sortOrder$, this.searchBy$])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy?.length || table.config?.disableTableSort\n ? searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)?.sort(\n sortOnMultipleKeys(sortBy)\n )\n : table.data?.sort(sortOnMultipleKeys(sortBy));\n }),\n shareReplay(1)\n )", "deprecated": false, "deprecationMessage": "", "type": "Observable>", "optional": false, "description": "", - "line": 219 + "line": 361 }, { "name": "footerColspan$", @@ -2653,17 +3610,17 @@ "type": "", "optional": false, "description": "", - "line": 379 + "line": 529 }, { - "name": "rowHover$", - "defaultValue": "this._rowHover$.asObservable().pipe(\n debounceTime(50),\n distinctUntilChanged((p, q) => p.index === q.index),\n tap((event) => this.rowHover.emit(event)),\n shareReplay(1)\n )", + "name": "rowActive$", + "defaultValue": "this._rowActive$.asObservable().pipe(\n debounceTime(50),\n distinctUntilChanged((p, q) => {\n if (this.rowIdKey && p.row && q.row) {\n return p.row[this.rowIdKey] === q.row[this.rowIdKey];\n } else if (this.generateRowId && p.row && q.row) {\n return p.row._id === q.row._id;\n } else {\n return p.index === q.index;\n }\n }),\n tap((event) => (this.activeRowIndex = event.index)),\n tap((event) => this.rowActive.emit(event)),\n shareReplay(1)\n )", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, "description": "", - "line": 142 + "line": 268 }, { "name": "searchBy$", @@ -2673,32 +3630,42 @@ "type": "Observable", "optional": false, "description": "", - "line": 189 + "line": 330 }, { "name": "table$", - "defaultValue": "combineLatest([\n this.data$,\n this.tableConfig$,\n this._pagingInfo$.pipe(tap((res) => console.log(res))),\n ]).pipe(\n map(([sorted, config, pagingInfo]) => {\n if (\n pagingInfo.pageCurrent !== null &&\n pagingInfo.numberOfRecords !== null &&\n pagingInfo.pageSize !== null\n ) {\n return {\n data: [sorted],\n config,\n info: {\n lazyLoaded: true,\n numberOfRecords: pagingInfo.numberOfRecords,\n pageSize: pagingInfo.pageSize,\n pageTotal:\n pagingInfo.pageTotal ??\n Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),\n },\n };\n }\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { numberOfRecords: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n numberOfRecords: sorted.length,\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n tap((meta) => this._tableInfo$.next(meta.info)),\n shareReplay(1)\n )", + "defaultValue": "combineLatest([\n this.data$,\n this.tableConfig$,\n this._pagingInfo$,\n ]).pipe(\n map(([sorted, config, pagingInfo]) => {\n if (\n pagingInfo.pageCurrent !== null &&\n pagingInfo.numberOfRecords !== null &&\n pagingInfo.pageSize !== null\n ) {\n return {\n data: [sorted],\n config,\n info: {\n lazyLoaded: true,\n numberOfRecords: pagingInfo.numberOfRecords,\n pageSize: pagingInfo.pageSize,\n pageTotal:\n pagingInfo.pageTotal ??\n Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),\n },\n };\n }\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { numberOfRecords: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n numberOfRecords: sorted.length,\n pageSize: +(config.pagination.length || 0),\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n tap((meta) => this._tableInfo$.next(meta.info)),\n shareReplay(1)\n )", "deprecated": false, "deprecationMessage": "", "type": "Observable", "optional": false, "description": "", - "line": 278 + "line": 427 }, { "name": "tableConfig$", - "defaultValue": "this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n )", + "defaultValue": "this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n tap((config) => (this._tableConfig = config)),\n shareReplay(1)\n )", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 352 + }, + { + "name": "unsubscribe$", + "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, "description": "", - "line": 211 + "line": 93 } ], "methodsClass": [ { - "name": "_hoverRow", + "name": "_activateRow", "args": [ { "name": "row", @@ -2708,7 +3675,204 @@ }, { "name": "index", - "type": "number | null", + "type": "number | null", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 307, + "deprecated": false, + "deprecationMessage": "", + "modifierKind": [ + 122 + ], + "jsdoctags": [ + { + "name": "row", + "type": "TableRow | null", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "index", + "type": "number | null", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_handleNavigationEvent", + "args": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "rows", + "type": "any[]", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "currentPage", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "tableInfo", + "type": "any", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 693, + "deprecated": false, + "deprecationMessage": "", + "modifierKind": [ + 121 + ], + "jsdoctags": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "rows", + "type": "any[]", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "currentPage", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "tableInfo", + "type": "any", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_rowActive", + "args": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "index", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 259, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "index", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_rowClick", + "args": [ + { + "name": "row", + "type": "TableRow", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" }, @@ -2716,20 +3880,19 @@ "name": "event", "type": "MouseEvent", "deprecated": false, - "deprecationMessage": "", - "optional": true + "deprecationMessage": "" } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 166, + "line": 255, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ { "name": "row", - "type": "TableRow | null", + "type": "TableRow", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -2738,7 +3901,7 @@ }, { "name": "index", - "type": "number | null", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -2750,7 +3913,6 @@ "type": "MouseEvent", "deprecated": false, "deprecationMessage": "", - "optional": true, "tagName": { "text": "param" } @@ -2758,14 +3920,8 @@ ] }, { - "name": "_rowClick", + "name": "_trackRowByFn", "args": [ - { - "name": "row", - "type": "TableRow", - "deprecated": false, - "deprecationMessage": "" - }, { "name": "index", "type": "number", @@ -2773,28 +3929,22 @@ "deprecationMessage": "" }, { - "name": "event", - "type": "MouseEvent", + "name": "row", + "type": "TableRow", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "TrackByFunction", "typeParameters": [], - "line": 133, + "line": 218, "deprecated": false, "deprecationMessage": "", + "modifierKind": [ + 121 + ], "jsdoctags": [ - { - "name": "row", - "type": "TableRow", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, { "name": "index", "type": "number", @@ -2805,8 +3955,8 @@ } }, { - "name": "event", - "type": "MouseEvent", + "name": "row", + "type": "TableRow", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -2816,19 +3966,26 @@ ] }, { - "name": "hoverRow", + "name": "activateRow", "args": [ { "name": "id", "type": "string", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 149, + "line": 285, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -2840,23 +3997,40 @@ "tagName": { "text": "param" } + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } } ] }, { - "name": "hoverRow", + "name": "activateRow", "args": [ { "name": "index", "type": "number", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 150, + "line": 286, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -2868,23 +4042,40 @@ "tagName": { "text": "param" } + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } } ] }, { - "name": "hoverRow", + "name": "activateRow", "args": [ { "name": "none", "type": "", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 151, + "line": 287, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -2896,23 +4087,40 @@ "tagName": { "text": "param" } + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } } ] }, { - "name": "hoverRow", + "name": "activateRow", "args": [ { "name": "arg", "type": "string | number | null", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 152, + "line": 288, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -2924,9 +4132,32 @@ "tagName": { "text": "param" } + }, + { + "name": "event", + "type": "MouseEvent | KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } } ] }, + { + "name": "listenToKeyboardEvents", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 648, + "deprecated": false, + "deprecationMessage": "", + "modifierKind": [ + 122 + ] + }, { "name": "nestedValue", "args": [ @@ -2953,7 +4184,7 @@ "optional": false, "returnType": "", "typeParameters": [], - "line": 473, + "line": 623, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -2987,6 +4218,16 @@ } ] }, + { + "name": "ngOnDestroy", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 784, + "deprecated": false, + "deprecationMessage": "" + }, { "name": "sortByKey", "args": [ @@ -3007,7 +4248,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 399, + "line": 549, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nsortByKey - Sort by key in table row\n", @@ -3015,8 +4256,8 @@ "jsdoctags": [ { "name": { - "pos": 11485, - "end": 11488, + "pos": 16462, + "end": 16465, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3027,8 +4268,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 11479, - "end": 11484, + "pos": 16456, + "end": 16461, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3039,8 +4280,8 @@ }, { "name": { - "pos": 11534, - "end": 11540, + "pos": 16511, + "end": 16517, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3052,8 +4293,8 @@ "deprecationMessage": "", "optional": true, "tagName": { - "pos": 11512, - "end": 11517, + "pos": 16489, + "end": 16494, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3062,22 +4303,22 @@ }, "comment": "
    \n
  • Mouse event triggering sort, if shift key is pressed sort key will be added to already present sort keys
  • \n
\n", "typeExpression": { - "pos": 11518, - "end": 11532, + "pos": 16495, + "end": 16509, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 307, "type": { - "pos": 11519, - "end": 11530, + "pos": 16496, + "end": 16507, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 1, "kind": 177, "typeName": { - "pos": 11519, - "end": 11530, + "pos": 16496, + "end": 16507, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3088,6 +4329,34 @@ } } ] + }, + { + "name": "unsubscribeFromKeyboardEvents", + "args": [ + { + "name": "tableRef", + "type": "HTMLTableElement", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 679, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "tableRef", + "type": "HTMLTableElement", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] } ], "deprecated": false, @@ -3097,41 +4366,186 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport {\n BehaviorSubject,\n combineLatest,\n isObservable,\n Observable,\n of,\n ReplaySubject,\n} from 'rxjs';\nimport { TableConfig } from './models/table-config.interface';\nimport {\n AsyncPipe,\n KeyValue,\n KeyValuePipe,\n NgClass,\n NgForOf,\n NgIf,\n NgTemplateOutlet,\n SlicePipe,\n} from '@angular/common';\nimport {\n debounceTime,\n distinctUntilChanged,\n filter,\n map,\n shareReplay,\n startWith,\n switchMap,\n take,\n tap,\n withLatestFrom,\n} from 'rxjs/operators';\nimport { TableColumn } from './models/table-column.interface';\nimport {\n calculate,\n chunk,\n search,\n sortOnMultipleKeys,\n} from './utilities/utilities';\nimport { TableRow } from './models/table-row.interface';\nimport { GtOrder, GtSortOrder } from './models/table-sort.interface';\nimport { TableMeta } from './models/table-meta.interface';\nimport {\n GtPageChangeEvent,\n GtRowClickEvent,\n GtRowHoverEvent,\n GtSortEvent,\n} from './models/table-events.interface';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { GtPaginationInfo } from './models/gt-pagination';\nimport { TableInfo } from './models/table-info.interface';\n\n@Component({\n selector: 'angular-generic-table',\n templateUrl: './core.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n CapitalCasePipe,\n KeyValuePipe,\n SortClassPipe,\n DashCasePipe,\n AsyncPipe,\n NgTemplateOutlet,\n SlicePipe,\n DynamicPipe,\n HighlightPipe,\n NgClass,\n NgIf,\n NgForOf,\n ],\n})\nexport class CoreComponent {\n get sortOrder$(): Observable {\n return this._sortOrder$.asObservable();\n }\n\n @Input() set loading(isLoading: Observable | boolean) {\n this._loading$.next(isLoading);\n }\n @Input()\n set paginationIndex(pageIndex: number) {\n this._currentPaginationIndex$.next(pageIndex);\n }\n\n @Input() set pagingInfo(value: GtPaginationInfo | null) {\n if (value) {\n this._pagingInfo$.next(value);\n if (\n value.pageCurrent !== this._currentPaginationIndex$.getValue() + 1 &&\n value.pageCurrent !== null\n ) {\n this.paginationIndex = value.pageCurrent - 1;\n }\n }\n }\n\n @Input()\n set search(string: Observable | string | null) {\n this._searchBy$.next(string);\n }\n\n @Input()\n set config(config: Observable> | TableConfig) {\n this._tableConfig$.next(config);\n }\n\n @Input()\n set data(data: Observable> | Array) {\n this._data$.next(data);\n }\n\n @Input() set sortOrder(sortConfig: any) {\n if (JSON.stringify(sortConfig) !== JSON.stringify(this._sortOrder$.value)) {\n this.sortOrderChange.emit(sortConfig);\n this._sortOrder$.next(sortConfig);\n }\n }\n\n @Output() rowClick = new EventEmitter();\n @Output() sortOrderChange = new EventEmitter>();\n\n _rowClick(row: TableRow, index: number, event: MouseEvent): void {\n this.rowClick.emit({ row, index, event });\n }\n\n private _rowHover$ = new ReplaySubject(1);\n @Output() rowHover = new EventEmitter();\n @Output() columnSort = new EventEmitter();\n /** page change event - emitted when current page/index changes for pagination */\n @Output() pageChange = new EventEmitter();\n rowHover$ = this._rowHover$.asObservable().pipe(\n debounceTime(50),\n distinctUntilChanged((p, q) => p.index === q.index),\n tap((event) => this.rowHover.emit(event)),\n shareReplay(1)\n );\n\n hoverRow(id: string): void;\n hoverRow(index: number): void;\n hoverRow(none: null): void;\n hoverRow(arg: string | number | null): void {\n if (typeof arg === 'number') {\n this.data$\n .pipe(\n map((data) => data[arg]),\n take(1)\n )\n .subscribe((row) => this._hoverRow(row, arg));\n } else if (typeof arg === 'string') {\n // TODO: implement hover by id\n } else {\n this._hoverRow(null, null);\n }\n }\n _hoverRow(\n row: TableRow | null,\n index: number | null,\n event?: MouseEvent\n ): void {\n this._rowHover$.next({ row, index, event });\n }\n\n get loading$(): Observable {\n return this._loading$.pipe(\n startWith(false),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n }\n\n private _loading$: ReplaySubject | boolean> =\n new ReplaySubject(1);\n private _sortOrder$: BehaviorSubject =\n new BehaviorSubject([]);\n private _searchBy$: ReplaySubject | string | null> =\n new ReplaySubject(1);\n searchBy$: Observable = this._searchBy$.pipe(\n startWith(''),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n private _pagingInfo$ = new BehaviorSubject({\n pageCurrent: null,\n pageNext: null,\n pagePrevious: null,\n pageSize: null,\n numberOfRecords: null,\n //recordsAfterFilter: null,\n //recordsAfterSearch: null,\n //recordsAll: null,\n });\n\n // tslint:disable-next-line:variable-name\n private _tableConfig$: BehaviorSubject<\n TableConfig | Observable>\n > = new BehaviorSubject({});\n tableConfig$ = this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n private _data$: ReplaySubject | Observable>> =\n new ReplaySubject(1);\n data$: Observable> = this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n const newData: TableRow[] = [];\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, [key, value]) => ({\n ...previousValue,\n [key]: this.nestedValue(\n row,\n value.mapTo!.path,\n value.mapTo?.missingValue\n ),\n }),\n {}\n );\n newData[i] = { ...row, ...newKeys };\n }\n data = newData;\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([of(obs), this.sortOrder$, this.searchBy$])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy?.length || table.config?.disableTableSort\n ? searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)?.sort(\n sortOnMultipleKeys(sortBy)\n )\n : table.data?.sort(sortOnMultipleKeys(sortBy));\n }),\n shareReplay(1)\n );\n\n calculations$ = combineLatest([this.data$, this.tableConfig$]).pipe(\n map(([data, config]) => calculate(data, config)),\n shareReplay(1)\n );\n\n table$: Observable = combineLatest([\n this.data$,\n this.tableConfig$,\n this._pagingInfo$.pipe(tap((res) => console.log(res))),\n ]).pipe(\n map(([sorted, config, pagingInfo]) => {\n if (\n pagingInfo.pageCurrent !== null &&\n pagingInfo.numberOfRecords !== null &&\n pagingInfo.pageSize !== null\n ) {\n return {\n data: [sorted],\n config,\n info: {\n lazyLoaded: true,\n numberOfRecords: pagingInfo.numberOfRecords,\n pageSize: pagingInfo.pageSize,\n pageTotal:\n pagingInfo.pageTotal ??\n Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),\n },\n };\n }\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { numberOfRecords: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n numberOfRecords: sorted.length,\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n tap((meta) => this._tableInfo$.next(meta.info)),\n shareReplay(1)\n );\n\n /** tableInfo$ - returns observable for table info\n * @return Observable */\n get tableInfo$(): Observable {\n return this._tableInfo$.asObservable().pipe(\n filter((info) => !!info),\n shareReplay(1)\n );\n }\n\n /** tableInfo - returns the current table info\n * @return TableInfo */\n get tableInfo(): TableInfo | undefined {\n return this._tableInfo$.getValue();\n }\n\n private _tableInfo$ = new BehaviorSubject(undefined);\n\n private _currentPaginationIndex$: BehaviorSubject =\n new BehaviorSubject(0);\n currentPaginationIndex$ = combineLatest([\n this._currentPaginationIndex$,\n this.table$,\n ]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage =\n Math.ceil(\n table.info.records /\n (table.info.recordLength ??\n (table.config?.pagination?.length || table.info.records))\n ) - 1;\n // determine min/max position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n distinctUntilChanged(),\n tap((index) => this.pageChange.emit({ index })),\n shareReplay(1)\n );\n\n colspan$ = this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(\n Object.values(config.columns || config.rows || {}).filter(\n (value) => value.hidden !== true\n ).length\n )\n : this.data$.pipe(map((data) => data.length + 1))\n ),\n shareReplay(1)\n );\n\n footerColspan$ = this.tableConfig$.pipe(\n map((config) => {\n let colspan = 0;\n Object.values(config?.footer?.columns || {}).forEach((calculations) => {\n if (\n Object.values(calculations).filter((value) => value !== false)\n .length >= 0\n ) {\n colspan += 1;\n }\n }, {});\n return colspan;\n }),\n shareReplay(1)\n );\n\n /** sortByKey - Sort by key in table row\n * @param key - key to sort by\n * @param { MouseEvent } [$event] - Mouse event triggering sort, if shift key is pressed sort key will be added to already present sort keys\n */\n sortByKey(key: keyof TableRow, $event?: MouseEvent): void {\n const shiftKey = $event?.shiftKey === true;\n const currentOrder = this._sortOrder$.value;\n let sortOrder: GtOrder = 'asc';\n let newOrder: GtSortOrder = [];\n // if shift key is pressed while sorting...\n if (shiftKey) {\n // ...check if key is already sorted\n const existingSortPosition = currentOrder.findIndex(\n (value) => value.key === key\n );\n if (existingSortPosition === -1) {\n // ...if key is not sorted, add it to the end of the sort order\n newOrder = [...currentOrder, { key, order: 'asc' }];\n } else {\n // ...if key is already sorted, toggle sort order\n sortOrder = currentOrder[existingSortPosition].order;\n const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';\n newOrder = [...currentOrder];\n newOrder[existingSortPosition] = {\n ...newOrder[existingSortPosition],\n order: newSortOrder,\n };\n }\n } else {\n // ...else if shift key is not pressed...\n if (currentOrder.length > 0) {\n // ...check if key is already sorted\n const existingSortPosition = currentOrder.findIndex(\n (value) => value.key === key\n );\n // ...if key is already sorted, toggle sort order\n if (existingSortPosition === -1) {\n newOrder = [{ key, order: 'asc' }];\n } else {\n sortOrder = currentOrder[existingSortPosition].order;\n const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';\n newOrder = [{ key, order: newSortOrder }];\n }\n } else {\n // ...if key is not sorted set sort order for key to ascending\n newOrder = [{ key, order: sortOrder }];\n }\n }\n // create sort event\n const sortEvent: GtSortEvent = {\n key,\n order: sortOrder,\n currentSortOrder: newOrder,\n addSortKey: shiftKey,\n };\n\n // if event is passed to sort function...\n if ($event) {\n // ...emit it as well\n sortEvent.event = $event;\n }\n // emit sort event\n this.columnSort.emit(sortEvent);\n\n // if table is not lazy loaded (sorting is then handled server-side)...\n if (!this.tableInfo?.lazyLoaded) {\n // ...update sort order\n this.sortOrder = newOrder;\n }\n }\n\n columnOrder = (\n a: KeyValue,\n b: KeyValue\n ): number => {\n return (a.value.order || 0) - (b.value.order || 0);\n };\n\n nestedValue(\n object: any,\n mapTo: string,\n missingValue: string | number | null = null\n ): unknown {\n const levels = mapTo.split('.');\n return levels.reduce(\n (previousValue, currentValue, index) =>\n previousValue[currentValue] ||\n (index === levels.length - 1 ? missingValue : {}),\n object\n );\n }\n}\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n TrackByFunction,\n} from '@angular/core';\nimport {\n BehaviorSubject,\n combineLatest,\n fromEvent,\n isObservable,\n Observable,\n of,\n ReplaySubject,\n Subject,\n} from 'rxjs';\nimport { TableConfig } from './models/table-config.interface';\nimport {\n AsyncPipe,\n KeyValue,\n KeyValuePipe,\n NgClass,\n NgForOf,\n NgIf,\n NgTemplateOutlet,\n SlicePipe,\n} from '@angular/common';\nimport {\n debounceTime,\n distinctUntilChanged,\n filter,\n map,\n pluck,\n shareReplay,\n startWith,\n switchMap,\n take,\n takeUntil,\n tap,\n withLatestFrom,\n} from 'rxjs/operators';\nimport { TableColumn } from './models/table-column.interface';\nimport {\n calculate,\n chunk,\n search,\n sortOnMultipleKeys,\n} from './utilities/utilities';\nimport { TableRow } from './models/table-row.interface';\nimport { GtOrder, GtSortOrder } from './models/table-sort.interface';\nimport { TableMeta } from './models/table-meta.interface';\nimport {\n GtPageChangeEvent,\n GtRowSelectEvent,\n GtRowClickEvent,\n GtRowActiveEvent,\n GtSortEvent,\n} from './models/table-events.interface';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { RowSelectionPipe } from './pipes/row-selection.pipe';\nimport { GtPaginationInfo } from './models/gt-pagination';\nimport { TableInfo } from './models/table-info.interface';\n\n@Component({\n selector: 'angular-generic-table',\n templateUrl: './core.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n CapitalCasePipe,\n KeyValuePipe,\n SortClassPipe,\n DashCasePipe,\n RowSelectionPipe,\n AsyncPipe,\n NgTemplateOutlet,\n SlicePipe,\n DynamicPipe,\n HighlightPipe,\n NgClass,\n NgIf,\n NgForOf,\n ],\n})\nexport class CoreComponent implements OnDestroy {\n unsubscribe$ = new Subject();\n get navigationKeys(): typeof this._navigationKeys {\n return this._navigationKeys;\n }\n\n /** navigationKeys\n * @description An array of keyboard keys that will trigger navigation and active row, currently only supports arrow keys, home and end (omit key name from array to disable it)\n * @type {string[]}\n * @default ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']\n */\n @Input() set navigationKeys(value: typeof this._navigationKeys) {\n this._navigationKeys = value;\n }\n\n private _navigationKeys = [\n 'ArrowDown',\n 'ArrowUp',\n 'ArrowLeft',\n 'ArrowRight',\n 'Home',\n 'End',\n ];\n get selectKeys(): string[] {\n return this._selectKeys;\n }\n\n /** selectKeys\n * @description An array of keyboard keys that will trigger row selection (omit key name from array to disable it)\n * @type {string[]}\n * @default ['Enter', ' ']\n */\n @Input() set selectKeys(value: string[]) {\n this._selectKeys = value;\n }\n\n private _selectKeys = ['Enter', ' '];\n get sortOrder$(): Observable {\n return this._sortOrder$.asObservable();\n }\n\n @Input() set loading(isLoading: Observable | boolean) {\n this._loading$.next(isLoading);\n }\n @Input()\n set paginationIndex(pageIndex: number) {\n this._currentPaginationIndex$.next(pageIndex);\n }\n\n get paginationIndex(): number {\n return this._currentPaginationIndex$.getValue();\n }\n\n @Input() set pagingInfo(value: GtPaginationInfo | null) {\n if (value) {\n this._pagingInfo$.next(value);\n if (\n value.pageCurrent !== this._currentPaginationIndex$.getValue() + 1 &&\n value.pageCurrent !== null\n ) {\n this.paginationIndex = value.pageCurrent - 1;\n }\n }\n }\n\n /** customClasses\n * @description An object that contains custom classes for various elements in the table.\n * @type {object} - { selectedRow: string, activeRow: string } - default classes are 'gt-selected' and 'gt-active'\n */\n @Input() set customClasses(classes: Partial) {\n this._customClasses = { ...this._customClasses, ...classes };\n }\n\n get customClasses(): typeof this._customClasses {\n return this._customClasses;\n }\n\n private _customClasses = {\n selectedRow: 'gt-selected',\n activeRow: 'gt-active',\n };\n\n /** isRowSelectedFn\n * @description Function to determine if row is selected or not.\n * @type {fn} A function that receives a row object and optional state for current selection that can be used to determine if row should be marked as selected or not. */\n @Input() set isRowSelectedFn(\n fn: (row: TableRow | any, selection?: any) => boolean\n ) {\n this._isRowSelectedFn = fn;\n }\n\n get isRowSelectedFn(): any {\n return this._isRowSelectedFn;\n }\n\n private _isRowSelectedFn: any;\n\n /** selection\n * @description An object that contains the currently selected row(s) in the table. It's passed to the selection function to determine which rows should be selected.\n * @type {any}\n */\n @Input() selection: any = {};\n\n /** rowIdKey\n * @description row key to use as unique id for table row. If passed, table won't generate unique ids for each row but instead use key to retrieve unique id from row.\n * @type {string}\n */\n @Input() rowIdKey: string | undefined;\n\n /** generateRowId\n * @description Whether or not to generate a unique id for each row in the table. Defaults to `true`.\n * @type {boolean}\n */\n @Input() generateRowId: boolean = true;\n\n /** trackRowByFn\n * @description A function that returns a unique identifier for each row in the table to optimize rendering when data is added or removed.\n * @type fn - TrackByFunction to retrieve unique id based on index and/or row. Defaults to using `row[this.rowIdKey]`.\n */\n @Input() set trackRowByFn(fn: TrackByFunction) {\n this._trackRowByFn = fn;\n }\n get trackRowByFn(): TrackByFunction {\n return this._trackRowByFn;\n }\n\n private _trackRowByFn(\n index: number,\n row: TableRow\n ): TrackByFunction {\n return this.rowIdKey ? row[this.rowIdKey] : row?._id;\n }\n\n @Input()\n set search(string: Observable | string | null) {\n this._searchBy$.next(string);\n }\n\n @Input()\n set config(config: Observable> | TableConfig) {\n this._tableConfig$.next(config);\n }\n\n private _tableConfig: TableConfig | undefined;\n\n @Input()\n set data(data: Observable> | Array | null) {\n if (data) {\n this._data$.next(data);\n }\n }\n\n @Input() set sortOrder(sortConfig: GtSortOrder | any) {\n if (JSON.stringify(sortConfig) !== JSON.stringify(this._sortOrder$.value)) {\n this.sortOrderChange.emit(sortConfig);\n this._sortOrder$.next(sortConfig);\n }\n }\n\n @Output() rowClick = new EventEmitter();\n @Output() rowSelect = new EventEmitter();\n @Output() sortOrderChange = new EventEmitter>();\n\n _rowClick(row: TableRow, index: number, event: MouseEvent): void {\n this.rowClick.emit({ row, index, event });\n }\n\n _rowActive(row: TableRow, index: number, event: KeyboardEvent): void {\n this.rowSelect.emit({ row, index, event });\n }\n\n private _rowActive$ = new ReplaySubject(1);\n @Output() rowActive = new EventEmitter();\n @Output() columnSort = new EventEmitter();\n /** page change event - emitted when current page/index changes for pagination */\n @Output() pageChange = new EventEmitter();\n rowActive$ = this._rowActive$.asObservable().pipe(\n debounceTime(50),\n distinctUntilChanged((p, q) => {\n if (this.rowIdKey && p.row && q.row) {\n return p.row[this.rowIdKey] === q.row[this.rowIdKey];\n } else if (this.generateRowId && p.row && q.row) {\n return p.row._id === q.row._id;\n } else {\n return p.index === q.index;\n }\n }),\n tap((event) => (this.activeRowIndex = event.index)),\n tap((event) => this.rowActive.emit(event)),\n shareReplay(1)\n );\n\n activeRowIndex: number | null = null;\n activateRow(id: string, event?: MouseEvent | KeyboardEvent): void;\n activateRow(index: number, event?: MouseEvent | KeyboardEvent): void;\n activateRow(none: null, event?: MouseEvent | KeyboardEvent): void;\n activateRow(\n arg: string | number | null,\n event?: MouseEvent | KeyboardEvent\n ): void {\n if (typeof arg === 'number') {\n this.table$\n .pipe(\n pluck('data'),\n map((data) => data[this.paginationIndex][arg]),\n take(1),\n takeUntil(this.unsubscribe$)\n )\n .subscribe((row) => this._activateRow(row, arg, event));\n } else if (typeof arg === 'string') {\n // TODO: implement hover by id\n } else {\n this._activateRow(null, null);\n }\n }\n protected _activateRow(\n row: TableRow | null,\n index: number | null,\n event?: MouseEvent | KeyboardEvent\n ): void {\n this._rowActive$.next({ row, index, event });\n }\n\n get loading$(): Observable {\n return this._loading$.pipe(\n startWith(false),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n }\n\n private _loading$: ReplaySubject | boolean> =\n new ReplaySubject(1);\n private _sortOrder$: BehaviorSubject =\n new BehaviorSubject([]);\n private _searchBy$: ReplaySubject | string | null> =\n new ReplaySubject(1);\n searchBy$: Observable = this._searchBy$.pipe(\n startWith(''),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n private _pagingInfo$ = new BehaviorSubject({\n pageCurrent: null,\n pageNext: null,\n pagePrevious: null,\n pageSize: null,\n numberOfRecords: null,\n //recordsAfterFilter: null,\n //recordsAfterSearch: null,\n //recordsAll: null,\n });\n\n // tslint:disable-next-line:variable-name\n private _tableConfig$: BehaviorSubject<\n TableConfig | Observable>\n > = new BehaviorSubject({});\n tableConfig$ = this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n tap((config) => (this._tableConfig = config)),\n shareReplay(1)\n );\n\n private _data$: ReplaySubject | Observable>> =\n new ReplaySubject(1);\n data$: Observable> = this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n const newData: TableRow[] = [];\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, [key, value]) => ({\n ...previousValue,\n [key]: this.nestedValue(\n row,\n value.mapTo!.path,\n value.mapTo?.missingValue\n ),\n }),\n {}\n );\n newData[i] = { ...row, ...newKeys };\n }\n data = newData;\n }\n if (this.generateRowId && !this.rowIdKey && data.length > 0) {\n const dataWithId = [];\n for (let i = 0; i < data.length; i++) {\n dataWithId[i] = { ...data[i], _id: i };\n }\n data = dataWithId;\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([of(obs), this.sortOrder$, this.searchBy$])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy?.length || table.config?.disableTableSort\n ? searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : searchBy && !this.tableInfo?.lazyLoaded\n ? search(searchBy, false, table.data, table.config)?.sort(\n sortOnMultipleKeys(sortBy)\n )\n : table.data?.sort(sortOnMultipleKeys(sortBy));\n }),\n shareReplay(1)\n );\n\n calculations$ = combineLatest([this.data$, this.tableConfig$]).pipe(\n map(([data, config]) => calculate(data, config)),\n shareReplay(1)\n );\n\n table$: Observable = combineLatest([\n this.data$,\n this.tableConfig$,\n this._pagingInfo$,\n ]).pipe(\n map(([sorted, config, pagingInfo]) => {\n if (\n pagingInfo.pageCurrent !== null &&\n pagingInfo.numberOfRecords !== null &&\n pagingInfo.pageSize !== null\n ) {\n return {\n data: [sorted],\n config,\n info: {\n lazyLoaded: true,\n numberOfRecords: pagingInfo.numberOfRecords,\n pageSize: pagingInfo.pageSize,\n pageTotal:\n pagingInfo.pageTotal ??\n Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),\n },\n };\n }\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { numberOfRecords: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n numberOfRecords: sorted.length,\n pageSize: +(config.pagination.length || 0),\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n tap((meta) => this._tableInfo$.next(meta.info)),\n shareReplay(1)\n );\n\n /** tableInfo$ - returns observable for table info\n * @return Observable */\n get tableInfo$(): Observable {\n return this._tableInfo$.asObservable().pipe(\n filter((info) => !!info),\n shareReplay(1)\n );\n }\n\n /** tableInfo - returns the current table info\n * @return TableInfo */\n get tableInfo(): TableInfo | undefined {\n return this._tableInfo$.getValue();\n }\n\n private _tableInfo$ = new BehaviorSubject(undefined);\n\n private _currentPaginationIndex$: BehaviorSubject =\n new BehaviorSubject(0);\n currentPaginationIndex$ = combineLatest([\n this._currentPaginationIndex$,\n this.table$,\n ]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage =\n Math.ceil(\n table.info.records /\n (table.info.recordLength ??\n (table.config?.pagination?.length || table.info.records))\n ) - 1;\n // determine min/max position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n distinctUntilChanged(),\n tap((index) => this.pageChange.emit({ index })),\n shareReplay(1)\n );\n\n colspan$ = this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(\n Object.values(config.columns || config.rows || {}).filter(\n (value) => value.hidden !== true\n ).length\n )\n : this.data$.pipe(map((data) => data.length + 1))\n ),\n shareReplay(1)\n );\n\n footerColspan$ = this.tableConfig$.pipe(\n map((config) => {\n let colspan = 0;\n Object.values(config?.footer?.columns || {}).forEach((calculations) => {\n if (\n Object.values(calculations).filter((value) => value !== false)\n .length >= 0\n ) {\n colspan += 1;\n }\n }, {});\n return colspan;\n }),\n shareReplay(1)\n );\n\n /** sortByKey - Sort by key in table row\n * @param key - key to sort by\n * @param { MouseEvent } [$event] - Mouse event triggering sort, if shift key is pressed sort key will be added to already present sort keys\n */\n sortByKey(key: keyof TableRow, $event?: MouseEvent): void {\n const shiftKey = $event?.shiftKey === true;\n const currentOrder = this._sortOrder$.value;\n let sortOrder: GtOrder = 'asc';\n let newOrder: GtSortOrder = [];\n // if shift key is pressed while sorting...\n if (shiftKey) {\n // ...check if key is already sorted\n const existingSortPosition = currentOrder.findIndex(\n (value) => value.key === key\n );\n if (existingSortPosition === -1) {\n // ...if key is not sorted, add it to the end of the sort order\n newOrder = [...currentOrder, { key, order: 'asc' }];\n } else {\n // ...if key is already sorted, toggle sort order\n sortOrder = currentOrder[existingSortPosition].order;\n const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';\n newOrder = [...currentOrder];\n newOrder[existingSortPosition] = {\n ...newOrder[existingSortPosition],\n order: newSortOrder,\n };\n }\n } else {\n // ...else if shift key is not pressed...\n if (currentOrder.length > 0) {\n // ...check if key is already sorted\n const existingSortPosition = currentOrder.findIndex(\n (value) => value.key === key\n );\n // ...if key is already sorted, toggle sort order\n if (existingSortPosition === -1) {\n newOrder = [{ key, order: 'asc' }];\n } else {\n sortOrder = currentOrder[existingSortPosition].order;\n const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';\n newOrder = [{ key, order: newSortOrder }];\n }\n } else {\n // ...if key is not sorted set sort order for key to ascending\n newOrder = [{ key, order: sortOrder }];\n }\n }\n // create sort event\n const sortEvent: GtSortEvent = {\n key,\n order: sortOrder,\n currentSortOrder: newOrder,\n addSortKey: shiftKey,\n };\n\n // if event is passed to sort function...\n if ($event) {\n // ...emit it as well\n sortEvent.event = $event;\n }\n // emit sort event\n this.columnSort.emit(sortEvent);\n\n // if table is not lazy loaded (sorting is then handled server-side)...\n if (!this.tableInfo?.lazyLoaded) {\n // ...update sort order\n this.sortOrder = newOrder;\n }\n }\n\n columnOrder = (\n a: KeyValue,\n b: KeyValue\n ): number => {\n return (a.value.order || 0) - (b.value.order || 0);\n };\n\n nestedValue(\n object: any,\n mapTo: string,\n missingValue: string | number | null = null\n ): unknown {\n const levels = mapTo.split('.');\n return levels.reduce(\n (previousValue, currentValue, index) =>\n previousValue[currentValue] ||\n (index === levels.length - 1 ? missingValue : {}),\n object\n );\n }\n\n private _unsubscribeFromKeyboardEvents$ = new Subject();\n private _keyboardArrowEvent$ = fromEvent(\n document,\n 'keydown'\n ).pipe(\n filter(\n (event) =>\n [...this._navigationKeys, ...this._selectKeys].indexOf(event.key) > -1\n )\n );\n\n protected listenToKeyboardEvents(): void {\n if (!this._tableConfig?.activateRowOnKeyboardNavigation) {\n return;\n }\n\n this._unsubscribeFromKeyboardEvents$.next(true);\n this._keyboardArrowEvent$\n .pipe(\n withLatestFrom(\n this.data$,\n this.currentPaginationIndex$,\n this.tableInfo$\n ),\n takeUntil(this._unsubscribeFromKeyboardEvents$),\n takeUntil(this.unsubscribe$)\n )\n .subscribe(([event, rows, currentPage, tableInfo]) => {\n const selectEvent = this._selectKeys.includes(event.key);\n if (selectEvent && this.activeRowIndex !== null) {\n const rowIndex =\n this.activeRowIndex + currentPage * (tableInfo?.pageSize ?? 0);\n this._rowActive(rows[rowIndex], rowIndex, event);\n return;\n }\n\n const navigationEvent = this._navigationKeys.includes(event.key);\n if (navigationEvent) {\n this._handleNavigationEvent(event, rows, currentPage, tableInfo);\n }\n });\n }\n unsubscribeFromKeyboardEvents(tableRef: HTMLTableElement): void {\n if (!this._tableConfig?.activateRowOnKeyboardNavigation) {\n return;\n }\n // only unsubscribe if table is not focused\n if (tableRef !== document.activeElement) {\n if (this._tableConfig?.activateRowOnHover) {\n // unset active row\n this.activateRow(null);\n }\n this._unsubscribeFromKeyboardEvents$.next(true);\n }\n }\n\n private _handleNavigationEvent(\n event: KeyboardEvent,\n rows: any[],\n currentPage: number,\n tableInfo: any\n ): void {\n const hasPagination = (tableInfo?.pageTotal || 0) > 1 && tableInfo;\n const lastRowIndex = rows.length - 1;\n let newIndex = this.activeRowIndex;\n let indexModifier = 0;\n\n if (event.key === 'Home') {\n this.paginationIndex = 0;\n this.activateRow(0, event);\n return;\n }\n\n if (event.key === 'End') {\n const indexOfLastRecord = hasPagination\n ? rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1\n : lastRowIndex;\n if (tableInfo?.pageTotal) {\n this.paginationIndex = tableInfo.pageTotal - 1;\n }\n this.activateRow(indexOfLastRecord, event);\n return;\n }\n\n if (event.key === 'ArrowDown') {\n indexModifier = 1;\n } else if (event.key === 'ArrowUp') {\n indexModifier = -1;\n }\n\n if (newIndex === null) {\n newIndex = 0;\n } else if (\n newIndex + indexModifier >= 0 &&\n newIndex + indexModifier <= lastRowIndex\n ) {\n newIndex = newIndex + indexModifier;\n }\n\n if (hasPagination && tableInfo?.pageSize) {\n const isNotLastPage = currentPage + 1 < tableInfo.pageTotal;\n const recordsOnLastPage =\n rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1;\n const maxIndex = isNotLastPage\n ? tableInfo?.pageSize - 1\n : recordsOnLastPage;\n\n if (event.key === 'ArrowLeft' && currentPage > 0) {\n this.paginationIndex = currentPage - 1;\n this.activateRow(newIndex, event);\n return;\n } else if (event.key === 'ArrowRight' && isNotLastPage) {\n if (\n currentPage + 1 === tableInfo.pageTotal - 1 &&\n newIndex > recordsOnLastPage\n ) {\n this.activateRow(recordsOnLastPage, event);\n }\n this.paginationIndex = currentPage + 1;\n this.activateRow(newIndex, event);\n return;\n }\n\n if (\n currentPage > 0 &&\n indexModifier < 0 &&\n newIndex + indexModifier <= lastRowIndex &&\n (this.activeRowIndex || 0) + indexModifier < 0\n ) {\n // set last row of previous page as active\n this.activateRow(tableInfo?.pageSize - 1, event);\n this.paginationIndex = currentPage - 1;\n return;\n }\n\n const pageIndex = newIndex % tableInfo?.pageSize;\n\n if (newIndex > maxIndex && currentPage + 1 < tableInfo.pageTotal) {\n this.paginationIndex = currentPage + 1;\n }\n this.activateRow(pageIndex > maxIndex ? maxIndex : pageIndex, event);\n return;\n }\n\n this.activateRow(newIndex, event);\n }\n\n ngOnDestroy() {\n this.unsubscribe$.next(true);\n this.unsubscribe$.complete();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", + "implements": [ + "OnDestroy" + ], "accessors": { + "navigationKeys": { + "name": "navigationKeys", + "setSignature": { + "name": "navigationKeys", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "value", + "type": "", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 103, + "rawdescription": "\nnavigationKeys\n", + "description": "

navigationKeys

\n", + "jsdoctags": [ + { + "name": "value", + "type": "", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + "getSignature": { + "name": "navigationKeys", + "type": "", + "returnType": "", + "line": 94 + } + }, + "selectKeys": { + "name": "selectKeys", + "setSignature": { + "name": "selectKeys", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "value", + "type": "string[]", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 124, + "rawdescription": "\nselectKeys\n", + "description": "

selectKeys

\n", + "jsdoctags": [ + { + "name": "value", + "type": "string[]", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + "getSignature": { + "name": "selectKeys", + "type": "[]", + "returnType": "string[]", + "line": 115 + } + }, "sortOrder$": { "name": "sortOrder$", "getSignature": { - "name": "sortOrder$", - "type": "", - "returnType": "Observable", - "line": 84 + "name": "sortOrder$", + "type": "", + "returnType": "Observable", + "line": 129 + } + }, + "loading": { + "name": "loading", + "setSignature": { + "name": "loading", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "isLoading", + "type": "Observable | boolean", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 133, + "jsdoctags": [ + { + "name": "isLoading", + "type": "Observable | boolean", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + }, + "paginationIndex": { + "name": "paginationIndex", + "setSignature": { + "name": "paginationIndex", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "pageIndex", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 137, + "jsdoctags": [ + { + "name": "pageIndex", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + "getSignature": { + "name": "paginationIndex", + "type": "number", + "returnType": "number", + "line": 141 } }, - "loading": { - "name": "loading", + "pagingInfo": { + "name": "pagingInfo", "setSignature": { - "name": "loading", + "name": "pagingInfo", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "isLoading", - "type": "Observable | boolean", + "name": "value", + "type": "GtPaginationInfo | null", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 88, + "line": 145, "jsdoctags": [ { - "name": "isLoading", - "type": "Observable | boolean", + "name": "value", + "type": "GtPaginationInfo | null", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -3141,27 +4555,29 @@ ] } }, - "paginationIndex": { - "name": "paginationIndex", + "customClasses": { + "name": "customClasses", "setSignature": { - "name": "paginationIndex", + "name": "customClasses", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "pageIndex", - "type": "number", + "name": "classes", + "type": "Partial<>", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 92, + "line": 161, + "rawdescription": "\ncustomClasses\n", + "description": "

customClasses

\n", "jsdoctags": [ { - "name": "pageIndex", - "type": "number", + "name": "classes", + "type": "Partial<>", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -3169,29 +4585,105 @@ } } ] + }, + "getSignature": { + "name": "customClasses", + "type": "", + "returnType": "", + "line": 165 } }, - "pagingInfo": { - "name": "pagingInfo", + "isRowSelectedFn": { + "name": "isRowSelectedFn", "setSignature": { - "name": "pagingInfo", + "name": "isRowSelectedFn", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "value", - "type": "GtPaginationInfo | null", + "name": "fn", + "type": "function", + "deprecated": false, + "deprecationMessage": "", + "function": [ + { + "name": "row", + "type": "TableRow | any", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "", + "optional": true + } + ] + } + ], + "returnType": "void", + "line": 177, + "rawdescription": "\nisRowSelectedFn", + "description": "

isRowSelectedFn

\n", + "jsdoctags": [ + { + "name": "fn", + "type": "function", + "deprecated": false, + "deprecationMessage": "", + "function": [ + { + "name": "row", + "type": "TableRow | any", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "any", + "deprecated": false, + "deprecationMessage": "", + "optional": true + } + ], + "tagName": { + "text": "param" + } + } + ] + }, + "getSignature": { + "name": "isRowSelectedFn", + "type": "any", + "returnType": "any", + "line": 183 + } + }, + "trackRowByFn": { + "name": "trackRowByFn", + "setSignature": { + "name": "trackRowByFn", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "fn", + "type": "TrackByFunction", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 96, + "line": 211, + "rawdescription": "\ntrackRowByFn\n", + "description": "

trackRowByFn

\n", "jsdoctags": [ { - "name": "value", - "type": "GtPaginationInfo | null", + "name": "fn", + "type": "TrackByFunction", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -3199,6 +4691,12 @@ } } ] + }, + "getSignature": { + "name": "trackRowByFn", + "type": "", + "returnType": "TrackByFunction", + "line": 214 } }, "search": { @@ -3217,7 +4715,7 @@ } ], "returnType": "void", - "line": 109, + "line": 226, "jsdoctags": [ { "name": "string", @@ -3247,7 +4745,7 @@ } ], "returnType": "void", - "line": 114, + "line": 231, "jsdoctags": [ { "name": "config", @@ -3271,17 +4769,17 @@ "args": [ { "name": "data", - "type": "Observable> | Array", + "type": "Observable> | Array | null", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 119, + "line": 238, "jsdoctags": [ { "name": "data", - "type": "Observable> | Array", + "type": "Observable> | Array | null", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -3301,17 +4799,17 @@ "args": [ { "name": "sortConfig", - "type": "any", + "type": "GtSortOrder | any", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 123, + "line": 244, "jsdoctags": [ { "name": "sortConfig", - "type": "any", + "type": "GtSortOrder | any", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -3327,7 +4825,7 @@ "name": "loading$", "type": "", "returnType": "Observable", - "line": 174 + "line": 315 } }, "tableInfo$": { @@ -3336,20 +4834,20 @@ "name": "tableInfo$", "type": "", "returnType": "Observable", - "line": 329, + "line": 479, "rawdescription": "\ntableInfo$ - returns observable for table info", "description": "

tableInfo$ - returns observable for table info

\n", "jsdoctags": [ { - "pos": 9576, - "end": 9606, + "pos": 14553, + "end": 14583, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 339, "tagName": { - "pos": 9577, - "end": 9583, + "pos": 14554, + "end": 14560, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3368,20 +4866,20 @@ "name": "tableInfo", "type": "", "returnType": "TableInfo | undefined", - "line": 338, + "line": 488, "rawdescription": "\ntableInfo - returns the current table info", "description": "

tableInfo - returns the current table info

\n", "jsdoctags": [ { - "pos": 9833, - "end": 9851, + "pos": 14810, + "end": 14828, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 339, "tagName": { - "pos": 9834, - "end": 9840, + "pos": 14811, + "end": 14817, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -3395,7 +4893,7 @@ } } }, - "templateData": "\n \n \n \n \n \n {{\n column.value.header || column.key | capitalCase\n }}\n \n {{ column.value.header || column.key | capitalCase }}\n \n \n \n \n {{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 0 && !(loading$ | async)\">\n \n \n \n \n {{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n 0; else noData\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n {{ row.value.header || row.key | capitalCase }}\n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n\n\n {{ row[column.key] }}\n\n\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n\n\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n\n\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n\n\n \n\n" + "templateData": "\n \n \n \n \n \n {{\n column.value.header || column.key | capitalCase\n }}\n \n {{ column.value.header || column.key | capitalCase }}\n \n \n \n \n {{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 0 && !(loading$ | async)\">\n \n \n \n \n {{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n 0; else noData\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n {{ row.value.header || row.key | capitalCase }}\n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n\n\n {{ row[column.key] }}\n\n\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n\n\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n\n\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n\n\n \n\n" }, { "name": "CustomTemplatesComponent", @@ -4370,7 +5868,7 @@ }, { "name": "PaginationComponent", - "id": "component-PaginationComponent-5262b8292406289e9e0333027c11b22bd6a7d428ad89362bd9198308602f3bab85d6da2d365101ea9d84a9e8552d8a644e52de81d7c8094829accf4ce2914443", + "id": "component-PaginationComponent-f41dcb9eb9b3d9479c0ad637906cb4f93d17730ea480dfacf5af6dd2068485a68d6cce0c42050f6e9bb4fd325d0a31a143a14cb4f85a00b68005404936f72379", "file": "projects/core/src/lib/pagination/pagination.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [], @@ -4390,7 +5888,73 @@ "name": "ariaLabels", "deprecated": false, "deprecationMessage": "", - "line": 53, + "jsdoctags": [ + { + "pos": 2112, + "end": 2177, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2113, + "end": 2124, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

aria labels that describe pagination component

\n" + }, + { + "pos": 2177, + "end": 2315, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 2178, + "end": 2182, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • aria labels for pagination. Defaults to: { nav: 'Table pagination', button: 'Go to page ', }
  • \n
\n", + "typeExpression": { + "pos": 2183, + "end": 2189, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 2183, + "end": 2189, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 2183, + "end": 2189, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "labels" + } + } + } + } + ], + "rawdescription": "\nariaLabels\n nav: 'Table pagination',\n button: 'Go to page ',\n }`\n", + "description": "

ariaLabels\n nav: 'Table pagination',\n button: 'Go to page ',\n }`

\n", + "line": 76, "type": "GtPaginationAriaLabels", "decorators": [] }, @@ -4398,7 +5962,73 @@ "name": "classes", "deprecated": false, "deprecationMessage": "", - "line": 46, + "jsdoctags": [ + { + "pos": 1675, + "end": 1772, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 1676, + "end": 1687, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

classes that should be used within pagination component for different elements

\n" + }, + { + "pos": 1772, + "end": 1920, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 1773, + "end": 1777, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • classes to be used. Defaults to: { ul: 'pagination', li: 'page-item', button: 'page-link', }
  • \n
\n", + "typeExpression": { + "pos": 1778, + "end": 1785, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 1778, + "end": 1785, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 1778, + "end": 1785, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "classes" + } + } + } + } + ], + "rawdescription": "\nclasses\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n }`\n", + "description": "

classes\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n }`

\n", + "line": 62, "type": "GtPaginationClasses", "decorators": [] }, @@ -4406,7 +6036,73 @@ "name": "paginationLength", "deprecated": false, "deprecationMessage": "", - "line": 39, + "jsdoctags": [ + { + "pos": 1371, + "end": 1429, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 1372, + "end": 1383, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

number of buttons to show in pagination

\n" + }, + { + "pos": 1429, + "end": 1491, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 1430, + "end": 1434, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • number of buttons to show. Defaults to: 5
  • \n
\n", + "typeExpression": { + "pos": 1435, + "end": 1441, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 1435, + "end": 1441, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 1435, + "end": 1441, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "length" + } + } + } + } + ], + "rawdescription": "\npaginationLength\n", + "description": "

paginationLength

\n", + "line": 47, "type": "number", "decorators": [] }, @@ -4414,7 +6110,73 @@ "name": "pagingInfo", "deprecated": false, "deprecationMessage": "", - "line": 32, + "jsdoctags": [ + { + "pos": 932, + "end": 1130, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 933, + "end": 944, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

when provided, pagination component will use this information to render pagination instead of data from table. Use this option when pagination is handled by backend (server side).

\n" + }, + { + "pos": 1130, + "end": 1180, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 1131, + "end": 1135, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • metadata for pagination component
  • \n
\n", + "typeExpression": { + "pos": 1136, + "end": 1140, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 1136, + "end": 1140, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 1136, + "end": 1140, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "info" + } + } + } + } + ], + "rawdescription": "\npagingInfo\n", + "description": "

pagingInfo

\n", + "line": 36, "type": "GtPaginationInfo", "decorators": [] }, @@ -4422,7 +6184,73 @@ "name": "table", "deprecated": false, "deprecationMessage": "", - "line": 59, + "jsdoctags": [ + { + "pos": 2505, + "end": 2571, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 2506, + "end": 2517, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "description" + }, + "comment": "

table component to which pagination is attached

\n" + }, + { + "pos": 2571, + "end": 2607, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 341, + "tagName": { + "pos": 2572, + "end": 2576, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "originalKeywordKind": 151, + "escapedText": "type" + }, + "comment": "
    \n
  • table component
  • \n
\n", + "typeExpression": { + "pos": 2577, + "end": 2585, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 307, + "type": { + "pos": 2577, + "end": 2585, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 1, + "kind": 177, + "typeName": { + "pos": 2577, + "end": 2585, + "flags": 4194304, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "tableRef" + } + } + } + } + ], + "rawdescription": "\ntable\n", + "description": "

table

\n", + "line": 87, "type": "CoreComponent", "decorators": [] } @@ -4437,7 +6265,7 @@ "type": "GtPaginationAriaLabels", "optional": false, "description": "", - "line": 67, + "line": 95, "modifierKind": [ 121 ] @@ -4450,7 +6278,7 @@ "type": "GtPaginationClasses", "optional": false, "description": "", - "line": 71, + "line": 99, "modifierKind": [ 121 ] @@ -4463,7 +6291,7 @@ "type": "number", "optional": false, "description": "", - "line": 76, + "line": 104, "modifierKind": [ 121 ] @@ -4475,7 +6303,7 @@ "type": "GtPaginationInfo | undefined", "optional": false, "description": "", - "line": 64, + "line": 92, "modifierKind": [ 121 ] @@ -4487,7 +6315,7 @@ "type": "CoreComponent | undefined", "optional": false, "description": "", - "line": 66, + "line": 94, "modifierKind": [ 121 ] @@ -4500,7 +6328,7 @@ "type": "ReplaySubject", "optional": false, "description": "", - "line": 65, + "line": 93, "modifierKind": [ 121 ] @@ -4513,7 +6341,7 @@ "type": "", "optional": false, "description": "

paginationListItems$ - observable for page numbers to show based on number of pages and current position

\n", - "line": 79, + "line": 107, "rawdescription": "\npaginationListItems$ - observable for page numbers to show based on number of pages and current position" } ], @@ -4537,7 +6365,7 @@ "optional": false, "returnType": "Array", "typeParameters": [], - "line": 97, + "line": 125, "deprecated": false, "deprecationMessage": "", "rawdescription": "\ngenerate list - generate an array with page numbers to show based on number of pages and current position\n", @@ -4548,8 +6376,8 @@ "jsdoctags": [ { "name": { - "pos": 2716, - "end": 2729, + "pos": 3709, + "end": 3722, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4560,8 +6388,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 2710, - "end": 2715, + "pos": 3703, + "end": 3708, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4572,8 +6400,8 @@ }, { "name": { - "pos": 2766, - "end": 2781, + "pos": 3759, + "end": 3774, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4584,8 +6412,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 2760, - "end": 2765, + "pos": 3753, + "end": 3758, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4596,8 +6424,8 @@ }, { "tagName": { - "pos": 2839, - "end": 2846, + "pos": 3832, + "end": 3839, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4621,7 +6449,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 143, + "line": 171, "deprecated": false, "deprecationMessage": "", "rawdescription": "\ngo to page\n", @@ -4629,8 +6457,8 @@ "jsdoctags": [ { "name": { - "pos": 4157, - "end": 4162, + "pos": 5150, + "end": 5155, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4641,8 +6469,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 4151, - "end": 4156, + "pos": 5144, + "end": 5149, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -4661,7 +6489,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { combineLatest, Observable, ReplaySubject } from 'rxjs';\nimport { map, pluck, shareReplay, switchMap } from 'rxjs/operators';\nimport { CoreComponent } from '../core.component';\nimport {\n GtPaginationAriaLabels,\n GtPaginationClasses,\n GtPaginationInfo,\n} from '../models/gt-pagination';\nimport { AsyncPipe, CommonModule, NgForOf, NgIf } from '@angular/common';\n\n@Component({\n selector: 'angular-generic-table-pagination',\n templateUrl: './pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [AsyncPipe, NgIf, NgForOf, CommonModule],\n})\nexport class PaginationComponent {\n get pagingInfo(): GtPaginationInfo {\n return (\n this._pagingInfo || {\n pageNext: null,\n pageCurrent: null,\n pagePrevious: null,\n pageSize: null,\n numberOfRecords: null,\n pageTotal: null,\n }\n );\n }\n @Input() set pagingInfo(value: GtPaginationInfo) {\n this._pagingInfo = value;\n }\n get paginationLength(): number {\n return this._paginationLength;\n }\n\n @Input() set paginationLength(value: number) {\n this._paginationLength = +value;\n }\n get classes(): GtPaginationClasses {\n return this._classes;\n }\n\n @Input() set classes(value: GtPaginationClasses) {\n this._classes = value;\n }\n get ariaLabels(): GtPaginationAriaLabels {\n return this._ariaLabels;\n }\n\n @Input() set ariaLabels(value: GtPaginationAriaLabels) {\n this._ariaLabels = value;\n }\n get table(): CoreComponent {\n return this._table;\n }\n @Input() set table(value: CoreComponent) {\n this._table = value;\n this._table$.next(value);\n }\n\n private _pagingInfo: GtPaginationInfo | undefined;\n private _table$: ReplaySubject = new ReplaySubject(1);\n private _table: CoreComponent | undefined;\n private _ariaLabels: GtPaginationAriaLabels = {\n nav: 'Table pagination',\n button: 'Go to page ',\n };\n private _classes: GtPaginationClasses = {\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n };\n private _paginationLength: number = 5;\n\n /** paginationListItems$ - observable for page numbers to show based on number of pages and current position */\n paginationListItems$ = this._table$.pipe(\n switchMap((core) =>\n combineLatest([\n core?.table$.pipe(pluck('info')),\n core?.currentPaginationIndex$,\n ])\n ),\n map(([info, currentPage]) =>\n this._generateList(info.pageTotal, currentPage)\n ),\n shareReplay(1)\n );\n\n /** generate list - generate an array with page numbers to show based on number of pages and current position\n * @param numberOfPages number of pages to show\n * @param currentPosition current position (page index) being shown in table\n * @returns Array array of page numbers to show\n */\n private _generateList(\n numberOfPages: number,\n currentPosition: number\n ): Array {\n const middle = Math.floor(this.paginationLength / 2);\n const length =\n numberOfPages < this.paginationLength\n ? numberOfPages\n : this.paginationLength;\n\n return Array.from({ length }, (_, i) => {\n if (i === 0) {\n return 1;\n } else if (numberOfPages < this.paginationLength) {\n return i + 1;\n } else if (i + 1 === length) {\n return numberOfPages;\n } else if (\n currentPosition > middle &&\n currentPosition < numberOfPages - middle\n ) {\n return i + currentPosition - (middle - 1);\n } else if (\n currentPosition > middle &&\n currentPosition < numberOfPages - (middle - 1)\n ) {\n return i + currentPosition - middle;\n } else if (\n currentPosition > middle &&\n currentPosition === numberOfPages - (middle - 1)\n ) {\n return i + currentPosition - (middle + 1);\n } else if (\n currentPosition > middle &&\n currentPosition === numberOfPages - 1\n ) {\n return i + currentPosition - (middle + 2);\n } else {\n return i + 1;\n }\n });\n }\n\n /** go to page\n * @param index - page index to go to\n */\n goToPage(index: number): void {\n if (this.table) {\n this.table.paginationIndex = index - 1;\n }\n }\n}\n", + "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { combineLatest, ReplaySubject } from 'rxjs';\nimport { map, pluck, shareReplay, switchMap } from 'rxjs/operators';\nimport { CoreComponent } from '../core.component';\nimport {\n GtPaginationAriaLabels,\n GtPaginationClasses,\n GtPaginationInfo,\n} from '../models/gt-pagination';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'angular-generic-table-pagination',\n templateUrl: './pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [CommonModule],\n})\nexport class PaginationComponent {\n get pagingInfo(): GtPaginationInfo {\n return (\n this._pagingInfo || {\n pageNext: null,\n pageCurrent: null,\n pagePrevious: null,\n pageSize: null,\n numberOfRecords: null,\n pageTotal: null,\n }\n );\n }\n /** pagingInfo\n * @description when provided, pagination component will use this information to render pagination instead of data from table. Use this option when pagination is handled by backend (server side).\n * @type info - metadata for pagination component\n */\n @Input() set pagingInfo(info: GtPaginationInfo) {\n this._pagingInfo = info;\n }\n get paginationLength(): number {\n return this._paginationLength;\n }\n\n /** paginationLength\n * @description number of buttons to show in pagination\n * @type length - number of buttons to show. Defaults to: `5`\n */\n @Input() set paginationLength(length: number) {\n this._paginationLength = +length;\n }\n get classes(): GtPaginationClasses {\n return this._classes;\n }\n\n /** classes\n * @description classes that should be used within pagination component for different elements\n * @type classes - classes to be used. Defaults to: `{\n * ul: 'pagination',\n * li: 'page-item',\n * button: 'page-link',\n * }`\n */\n @Input() set classes(classes: GtPaginationClasses) {\n this._classes = classes;\n }\n get ariaLabels(): GtPaginationAriaLabels {\n return this._ariaLabels;\n }\n\n /** ariaLabels\n * @description aria labels that describe pagination component\n * @type labels - aria labels for pagination. Defaults to: `{\n * nav: 'Table pagination',\n * button: 'Go to page ',\n * }`\n */\n @Input() set ariaLabels(labels: GtPaginationAriaLabels) {\n this._ariaLabels = labels;\n }\n get table(): CoreComponent {\n return this._table;\n }\n\n /** table\n * @description table component to which pagination is attached\n * @type tableRef - table component\n */\n @Input() set table(tableRef: CoreComponent) {\n this._table = tableRef;\n this._table$.next(tableRef);\n }\n\n private _pagingInfo: GtPaginationInfo | undefined;\n private _table$: ReplaySubject = new ReplaySubject(1);\n private _table: CoreComponent | undefined;\n private _ariaLabels: GtPaginationAriaLabels = {\n nav: 'Table pagination',\n button: 'Go to page ',\n };\n private _classes: GtPaginationClasses = {\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n };\n private _paginationLength: number = 5;\n\n /** paginationListItems$ - observable for page numbers to show based on number of pages and current position */\n paginationListItems$ = this._table$.pipe(\n switchMap((core) =>\n combineLatest([\n core?.table$.pipe(pluck('info')),\n core?.currentPaginationIndex$,\n ])\n ),\n map(([info, currentPage]) =>\n this._generateList(info.pageTotal, currentPage)\n ),\n shareReplay(1)\n );\n\n /** generate list - generate an array with page numbers to show based on number of pages and current position\n * @param numberOfPages number of pages to show\n * @param currentPosition current position (page index) being shown in table\n * @returns Array array of page numbers to show\n */\n private _generateList(\n numberOfPages: number,\n currentPosition: number\n ): Array {\n const middle = Math.floor(this.paginationLength / 2);\n const length =\n numberOfPages < this.paginationLength\n ? numberOfPages\n : this.paginationLength;\n\n return Array.from({ length }, (_, i) => {\n if (i === 0) {\n return 1;\n } else if (numberOfPages < this.paginationLength) {\n return i + 1;\n } else if (i + 1 === length) {\n return numberOfPages;\n } else if (\n currentPosition > middle &&\n currentPosition < numberOfPages - middle\n ) {\n return i + currentPosition - (middle - 1);\n } else if (\n currentPosition > middle &&\n currentPosition < numberOfPages - (middle - 1)\n ) {\n return i + currentPosition - middle;\n } else if (\n currentPosition > middle &&\n currentPosition === numberOfPages - (middle - 1)\n ) {\n return i + currentPosition - (middle + 1);\n } else if (\n currentPosition > middle &&\n currentPosition === numberOfPages - 1\n ) {\n return i + currentPosition - (middle + 2);\n } else {\n return i + 1;\n }\n });\n }\n\n /** go to page\n * @param index - page index to go to\n */\n goToPage(index: number): void {\n if (this.table) {\n this.table.paginationIndex = index - 1;\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -4675,17 +6503,19 @@ "deprecationMessage": "", "args": [ { - "name": "value", + "name": "info", "type": "GtPaginationInfo", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 32, + "line": 36, + "rawdescription": "\npagingInfo\n", + "description": "

pagingInfo

\n", "jsdoctags": [ { - "name": "value", + "name": "info", "type": "GtPaginationInfo", "deprecated": false, "deprecationMessage": "", @@ -4711,17 +6541,19 @@ "deprecationMessage": "", "args": [ { - "name": "value", + "name": "length", "type": "number", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 39, + "line": 47, + "rawdescription": "\npaginationLength\n", + "description": "

paginationLength

\n", "jsdoctags": [ { - "name": "value", + "name": "length", "type": "number", "deprecated": false, "deprecationMessage": "", @@ -4735,7 +6567,7 @@ "name": "paginationLength", "type": "number", "returnType": "number", - "line": 35 + "line": 39 } }, "classes": { @@ -4747,17 +6579,19 @@ "deprecationMessage": "", "args": [ { - "name": "value", + "name": "classes", "type": "GtPaginationClasses", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 46, + "line": 62, + "rawdescription": "\nclasses\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n }`\n", + "description": "

classes\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n }`

\n", "jsdoctags": [ { - "name": "value", + "name": "classes", "type": "GtPaginationClasses", "deprecated": false, "deprecationMessage": "", @@ -4771,7 +6605,7 @@ "name": "classes", "type": "", "returnType": "GtPaginationClasses", - "line": 42 + "line": 50 } }, "ariaLabels": { @@ -4783,17 +6617,19 @@ "deprecationMessage": "", "args": [ { - "name": "value", + "name": "labels", "type": "GtPaginationAriaLabels", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 53, + "line": 76, + "rawdescription": "\nariaLabels\n nav: 'Table pagination',\n button: 'Go to page ',\n }`\n", + "description": "

ariaLabels\n nav: 'Table pagination',\n button: 'Go to page ',\n }`

\n", "jsdoctags": [ { - "name": "value", + "name": "labels", "type": "GtPaginationAriaLabels", "deprecated": false, "deprecationMessage": "", @@ -4807,7 +6643,7 @@ "name": "ariaLabels", "type": "", "returnType": "GtPaginationAriaLabels", - "line": 49 + "line": 65 } }, "table": { @@ -4819,17 +6655,19 @@ "deprecationMessage": "", "args": [ { - "name": "value", + "name": "tableRef", "type": "CoreComponent", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 59, + "line": 87, + "rawdescription": "\ntable\n", + "description": "

table

\n", "jsdoctags": [ { - "name": "value", + "name": "tableRef", "type": "CoreComponent", "deprecated": false, "deprecationMessage": "", @@ -4843,7 +6681,7 @@ "name": "table", "type": "", "returnType": "CoreComponent", - "line": 56 + "line": 79 } } }, @@ -5029,11 +6867,11 @@ "isDuplicate": true, "duplicateId": 1, "duplicateName": "PaginationComponent-1", - "templateData": "
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n
\n\n\n\n" + "templateData": "
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n
\n\n\n\n" }, { "name": "RowHoverClickComponent", - "id": "component-RowHoverClickComponent-beb9b8dec41975ff90d50f380c6e17362cfd5b4552fec0fdee29c3ecc06b680d6faa8ff75047aa7b54a2c504620ff01361bc68adfc590672a41ae31490e7f48d", + "id": "component-RowHoverClickComponent-37e56e13c3c24cbffb33eee0e72b605b89eab6d016b713351884e712ba40741eb988d7a69cde6ffc05bdb16f6fa4283a1306e8b61cb96b361d7d63d9a241b28b", "file": "projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts", "encapsulation": [], "entryComponents": [], @@ -5043,9 +6881,9 @@ "selector": "docs-row-hover-click", "styleUrls": [], "styles": [ - "\n :host ::ng-deep .gt-hover {\n background-color: var(--bs-highlight-bg);\n }\n " + "\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n " ], - "template": "
\n \n \n \n
\n{{ clicked }}\n\n", + "template": "
\n \n \n \n
\n{{ clicked }}\n\n", "templateUrl": [], "viewProviders": [], "inputsClass": [], @@ -5059,17 +6897,17 @@ "type": "string", "optional": false, "description": "", - "line": 36 + "line": 40 }, { "name": "config", - "defaultValue": "{\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n rowHover: true,\n }", + "defaultValue": "{\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n activateRowOnHover: true,\n }", "deprecated": false, "deprecationMessage": "", - "type": "object", + "type": "TableConfig", "optional": false, "description": "", - "line": 51 + "line": 55 }, { "name": "data", @@ -5079,7 +6917,7 @@ "type": "[]", "optional": false, "description": "", - "line": 37 + "line": 41 }, { "name": "SNIPPETS", @@ -5088,17 +6926,274 @@ "deprecationMessage": "", "type": "", "optional": false, - "description": "", - "line": 70 - } - ], - "methodsClass": [ + "description": "", + "line": 74 + } + ], + "methodsClass": [ + { + "name": "onRowClick", + "args": [ + { + "name": "event", + "type": "GtRowClickEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 66, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "event", + "type": "GtRowClickEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "onRowHover", + "args": [ + { + "name": "event", + "type": "GtRowActiveEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 70, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "event", + "type": "GtRowActiveEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "\n", + "type": "component", + "sourceCode": "import { Component } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ROW_HOVER_CLICK_SNIPPETS } from './row-hower-click.snippets';\nimport {\n GtRowClickEvent,\n GtRowActiveEvent,\n TableConfig,\n} from '@angular-generic-table/core';\n\n@Component({\n selector: 'docs-row-hover-click',\n template: `\n
\n \n \n \n
\n {{ clicked }}\n \n `,\n styles: [\n `\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n `,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config: TableConfig = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n activateRowOnHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = `clicked row number: ${event.index}`;\n }\n onRowHover(event: GtRowActiveEvent) {\n console.log('row hovered', event);\n }\n\n SNIPPETS = ROW_HOVER_CLICK_SNIPPETS;\n}\n\nexport const RowHoverClick: Story = (\n args: RowHoverClickComponent\n) => ({\n props: args,\n component: RowHoverClickComponent,\n});\n", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n \n" + }, + { + "name": "RowSelectComponent", + "id": "component-RowSelectComponent-615810d13a0e0518a5f3de6e1a5cbbb78f9461cd1591bfeb19c86775816e1e82832c0c4c5b7382797b539938151879c9408c0bef4674b2ca686b908494385b02", + "file": "projects/docs/src/app/examples/row-select/row-select.component.ts", + "changeDetection": "ChangeDetectionStrategy.OnPush", + "encapsulation": [], + "entryComponents": [], + "inputs": [], + "outputs": [], + "providers": [], + "styleUrls": [], + "styles": [ + "\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n :host ::ng-deep .table > tbody > tr {\n cursor: pointer;\n }\n " + ], + "templateUrl": [ + "./row-select.component.html" + ], + "viewProviders": [], + "inputsClass": [], + "outputsClass": [], + "propertiesClass": [ + { + "name": "_activateOnNavigation$", + "defaultValue": "new BehaviorSubject(true)", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 66, + "modifierKind": [ + 121 + ] + }, + { + "name": "_activateOnRowHover$", + "defaultValue": "new BehaviorSubject(true)", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 55, + "modifierKind": [ + 121 + ] + }, + { + "name": "_activeRow$", + "defaultValue": "new BehaviorSubject(null)", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 129, + "modifierKind": [ + 121 + ] + }, + { + "name": "config$", + "defaultValue": "combineLatest([\n this.lengthCtrl.valueChanges.pipe(\n startWith(this.lengthCtrl.value),\n map((length) =>\n length ? { pagination: { length: length < 0 ? 0 : length } } : {}\n )\n ),\n this.activateOnRowHover$,\n this.activateOnNavigation$,\n ]).pipe(\n map(\n ([pagination, activateRowOnHover, activateRowOnKeyboardNavigation]) => ({\n columns: {\n id: {\n sortable: true,\n },\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n email: {\n sortable: true,\n },\n },\n ...pagination,\n rowClick: true,\n activateRowOnHover,\n activateRowOnKeyboardNavigation,\n rowSelect: true,\n })\n )\n )", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 84 + }, + { + "name": "customClassNames", + "defaultValue": "{\n selectedRow: 'table-active',\n }", + "deprecated": false, + "deprecationMessage": "", + "type": "object", + "optional": false, + "description": "", + "line": 70 + }, + { + "name": "data$", + "defaultValue": "this.http\n .get<{ data: TableRow[] }>(\n 'https://private-730c61-generictable.apiary-mock.com/data'\n )\n .pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false)),\n takeUntil(this.unsubscribe$),\n shareReplay(1)\n )", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 74 + }, + { + "name": "lengthCtrl", + "defaultValue": "new FormControl(15)", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 73 + }, + { + "name": "loading$", + "defaultValue": "new BehaviorSubject(true)", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 69 + }, + { + "name": "selection", + "defaultValue": "{}", + "deprecated": false, + "deprecationMessage": "", + "type": "literal type", + "optional": false, + "description": "", + "line": 134 + }, + { + "name": "unsubscribe$", + "defaultValue": "new Subject()", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 68, + "modifierKind": [ + 121 + ] + } + ], + "methodsClass": [ + { + "name": "isSelected", + "args": [ + { + "name": "row", + "type": "RowData", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "selection", + "type": "", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "boolean", + "typeParameters": [], + "line": 130, + "deprecated": false, + "deprecationMessage": "", + "jsdoctags": [ + { + "name": "row", + "type": "RowData", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "selection", + "type": "", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "ngOnDestroy", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 166, + "deprecated": false, + "deprecationMessage": "" + }, { - "name": "onRowClick", + "name": "selectRow", "args": [ { "name": "event", - "type": "GtRowClickEvent", + "type": "GtRowClickEvent | GtRowSelectEvent", "deprecated": false, "deprecationMessage": "" } @@ -5106,13 +7201,13 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 62, + "line": 136, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ { "name": "event", - "type": "GtRowClickEvent", + "type": "GtRowClickEvent | GtRowSelectEvent", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -5122,11 +7217,11 @@ ] }, { - "name": "onRowHover", + "name": "setActiveRow", "args": [ { "name": "event", - "type": "GtRowHoverEvent", + "type": "GtRowActiveEvent", "deprecated": false, "deprecationMessage": "" } @@ -5134,13 +7229,13 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 66, + "line": 122, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ { "name": "event", - "type": "GtRowHoverEvent", + "type": "GtRowActiveEvent", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -5148,6 +7243,36 @@ } } ] + }, + { + "name": "toggleAll", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 152, + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "toggleRowHover", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 52, + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "toggleRowNavigation", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 63, + "deprecated": false, + "deprecationMessage": "" } ], "deprecated": false, @@ -5157,14 +7282,100 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Component } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ROW_HOVER_CLICK_SNIPPETS } from './row-hower-click.snippets';\nimport { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core';\n\n@Component({\n selector: 'docs-row-hover-click',\n template: `\n
\n \n \n \n
\n {{ clicked }}\n \n `,\n styles: [\n `\n :host ::ng-deep .gt-hover {\n background-color: var(--bs-highlight-bg);\n }\n `,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n rowHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = `clicked row number: ${event.index}`;\n }\n onRowHover(event: GtRowHoverEvent) {\n console.log('row hovered', event);\n }\n\n SNIPPETS = ROW_HOVER_CLICK_SNIPPETS;\n}\n\nexport const RowHoverClick: Story = (\n args: RowHoverClickComponent\n) => ({\n props: args,\n component: RowHoverClickComponent,\n});\n", + "sourceCode": "import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';\nimport {\n GtRowSelectEvent,\n GtRowClickEvent,\n TableRow,\n GtRowActiveEvent,\n} from '@angular-generic-table/core';\nimport { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';\nimport {\n map,\n pluck,\n shareReplay,\n startWith,\n take,\n takeUntil,\n tap,\n} from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { FormControl } from '@angular/forms';\ninterface RowData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: 'Female' | 'Male';\n id: number;\n last_name: string;\n}\n@Component({\n templateUrl: './row-select.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n :host ::ng-deep .table > tbody > tr {\n cursor: pointer;\n }\n `,\n ],\n})\nexport class RowSelectComponent implements OnDestroy {\n constructor(private http: HttpClient) {}\n\n get activateOnRowHover$(): Observable {\n return this._activateOnRowHover$.asObservable();\n }\n get activateOnRowHover(): boolean {\n return this._activateOnRowHover$.getValue();\n }\n toggleRowHover() {\n this._activateOnRowHover$.next(!this.activateOnRowHover);\n }\n private _activateOnRowHover$ = new BehaviorSubject(true);\n\n get activateOnNavigation$(): Observable {\n return this._activateOnNavigation$.asObservable();\n }\n get activateOnNavigation(): boolean {\n return this._activateOnNavigation$.getValue();\n }\n toggleRowNavigation() {\n this._activateOnNavigation$.next(!this.activateOnNavigation);\n }\n private _activateOnNavigation$ = new BehaviorSubject(true);\n\n private unsubscribe$ = new Subject();\n loading$ = new BehaviorSubject(true);\n customClassNames = {\n selectedRow: 'table-active',\n };\n lengthCtrl = new FormControl(15);\n data$ = this.http\n .get<{ data: TableRow[] }>(\n 'https://private-730c61-generictable.apiary-mock.com/data'\n )\n .pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false)),\n takeUntil(this.unsubscribe$),\n shareReplay(1)\n );\n config$ = combineLatest([\n this.lengthCtrl.valueChanges.pipe(\n startWith(this.lengthCtrl.value),\n map((length) =>\n length ? { pagination: { length: length < 0 ? 0 : length } } : {}\n )\n ),\n this.activateOnRowHover$,\n this.activateOnNavigation$,\n ]).pipe(\n map(\n ([pagination, activateRowOnHover, activateRowOnKeyboardNavigation]) => ({\n columns: {\n id: {\n sortable: true,\n },\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n email: {\n sortable: true,\n },\n },\n ...pagination,\n rowClick: true,\n activateRowOnHover,\n activateRowOnKeyboardNavigation,\n rowSelect: true,\n })\n )\n );\n\n setActiveRow(event: GtRowActiveEvent) {\n console.log(event);\n this._activeRow$.next(event.row);\n }\n get activeRow$() {\n return this._activeRow$.asObservable();\n }\n private _activeRow$ = new BehaviorSubject(null);\n isSelected(row: RowData, selection: typeof this.selection) {\n return selection[row.id];\n }\n\n selection: { [key: string]: boolean } = {};\n\n selectRow(event: GtRowClickEvent | GtRowSelectEvent) {\n const selection = { ...this.selection };\n const row = event.row;\n if (!row) return;\n if (!selection[row.id]) {\n selection[row.id] = true;\n } else {\n delete selection[row.id];\n }\n // update the selection\n this.selection = selection;\n }\n\n get isAllSelected() {\n return Object.keys(this.selection).length > 0;\n }\n toggleAll() {\n if (this.isAllSelected) {\n this.selection = {};\n return;\n }\n const selection = { ...this.selection };\n this.data$.pipe(take(1), takeUntil(this.unsubscribe$)).subscribe((data) => {\n data.forEach((row, index) => {\n selection[index] = true;\n });\n });\n this.selection = selection;\n }\n\n ngOnDestroy() {\n this.unsubscribe$.next();\n this.unsubscribe$.complete();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", - "stylesData": "\n :host ::ng-deep .gt-hover {\n background-color: var(--bs-highlight-bg);\n }\n \n" + "stylesData": "\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n :host ::ng-deep .table > tbody > tr {\n cursor: pointer;\n }\n \n", + "constructorObj": { + "name": "constructor", + "description": "", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "http", + "type": "HttpClient", + "deprecated": false, + "deprecationMessage": "" + } + ], + "line": 43, + "jsdoctags": [ + { + "name": "http", + "type": "HttpClient", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + "implements": [ + "OnDestroy" + ], + "accessors": { + "activateOnRowHover$": { + "name": "activateOnRowHover$", + "getSignature": { + "name": "activateOnRowHover$", + "type": "", + "returnType": "Observable", + "line": 46 + } + }, + "activateOnRowHover": { + "name": "activateOnRowHover", + "getSignature": { + "name": "activateOnRowHover", + "type": "boolean", + "returnType": "boolean", + "line": 49 + } + }, + "activateOnNavigation$": { + "name": "activateOnNavigation$", + "getSignature": { + "name": "activateOnNavigation$", + "type": "", + "returnType": "Observable", + "line": 57 + } + }, + "activateOnNavigation": { + "name": "activateOnNavigation", + "getSignature": { + "name": "activateOnNavigation", + "type": "boolean", + "returnType": "boolean", + "line": 60 + } + }, + "activeRow$": { + "name": "activeRow$", + "getSignature": { + "name": "activeRow$", + "type": "", + "returnType": "", + "line": 126 + } + }, + "isAllSelected": { + "name": "isAllSelected", + "getSignature": { + "name": "isAllSelected", + "type": "", + "returnType": "", + "line": 149 + } + } + }, + "templateData": "
\n
\n \n {{ activateOnRowHover ? \"Disable on hover\" : \"Enable on hover\" }}\n \n
\n
\n \n {{\n activateOnNavigation\n ? \"Disable on keyboard navigation\"\n : \"Enable on keyboard navigation\"\n }}\n \n
\n
\n \n {{ isAllSelected ? \"Deselect all\" : \"Select all\" }}\n \n
\n
\n
\n
\n \n \n
\n
\n Selected rows: {{ (selection | keyvalue).length }}\n
\n
\n Active row id: {{ (activeRow$ | async)?.id ?? \"none\" }}\n
\n
\n
\n \n
\n
Table is empty
\n \n \n \n
\n" }, { "name": "ServerSidePaginationComponent", - "id": "component-ServerSidePaginationComponent-cb0f40783d1b367a19972a3b81e3d412ca580af909300bf9ee947fdcb514868d51bd4bbe662c4629e06be3b886763c1e2b2cb9ad5960daf2a9bee4c93c234cff", + "id": "component-ServerSidePaginationComponent-84632bae79cd4899d6deb72214bfa38d32b216248b69830bb0ca440dec337ac8231dd42726cf0c8ab2fb2873ad182bf71c3f00fe899de8e6ebebf0d1ca5dd422", "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [], @@ -5189,7 +7400,7 @@ "type": "TemplateRef | undefined", "optional": false, "description": "", - "line": 53, + "line": 52, "decorators": [ { "name": "ViewChild", @@ -5204,7 +7415,7 @@ "type": "TemplateRef | undefined", "optional": false, "description": "", - "line": 54, + "line": 53, "decorators": [ { "name": "ViewChild", @@ -5220,7 +7431,7 @@ "type": "Observable>", "optional": false, "description": "", - "line": 82 + "line": 81 }, { "name": "loading$", @@ -5230,7 +7441,7 @@ "type": "", "optional": false, "description": "", - "line": 66 + "line": 65 }, { "name": "paginationForm", @@ -5240,7 +7451,7 @@ "type": "", "optional": false, "description": "", - "line": 55 + "line": 54 }, { "name": "paging$", @@ -5250,7 +7461,7 @@ "type": "Observable", "optional": false, "description": "", - "line": 86 + "line": 85 }, { "name": "request$", @@ -5260,7 +7471,7 @@ "type": "", "optional": false, "description": "", - "line": 68 + "line": 67 }, { "name": "requestParams$", @@ -5270,7 +7481,7 @@ "type": "", "optional": false, "description": "", - "line": 58 + "line": 57 }, { "name": "search$", @@ -5280,7 +7491,7 @@ "type": "", "optional": false, "description": "", - "line": 63 + "line": 62 }, { "name": "SNIPPETS", @@ -5290,7 +7501,7 @@ "type": "", "optional": false, "description": "", - "line": 152 + "line": 151 }, { "name": "sorting$", @@ -5300,7 +7511,7 @@ "type": "", "optional": false, "description": "", - "line": 91 + "line": 90 }, { "name": "tableConfig$", @@ -5310,7 +7521,7 @@ "type": "ReplaySubject>", "optional": false, "description": "", - "line": 115 + "line": 114 } ], "methodsClass": [ @@ -5320,7 +7531,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 118, + "line": 117, "deprecated": false, "deprecationMessage": "" }, @@ -5337,7 +7548,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 93, + "line": 92, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -5365,7 +7576,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 103, + "line": 102, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -5388,7 +7599,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n PaginationComponent,\n TableConfig,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { GtPaginationInfo } from '../../../../../core/src/lib/models/gt-pagination';\nimport { sortOrderToParams } from '../../../../../core/src/lib/utilities/utilities';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const Pagination: Story = (\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n});\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n GtPaginationInfo,\n sortOrderToParams,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { LAZY_LOADING_DOCS } from './server-side-pagination.snippets';\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n@Component({\n selector: 'docs-lazy-loading',\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n search: [''],\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(pluck('sorting'), shareReplay(1));\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n SNIPPETS = LAZY_LOADING_DOCS;\n}\n\nexport const ServerSidePagination: Story = (\n args: ServerSidePaginationComponent\n) => ({\n props: args,\n component: ServerSidePaginationComponent,\n});\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -5411,7 +7622,7 @@ "deprecationMessage": "" } ], - "line": 51, + "line": 50, "jsdoctags": [ { "name": "fb", @@ -6023,13 +8234,13 @@ "modules": [ { "name": "AppModule", - "id": "module-AppModule-df0c26e16dbd97eda40213d28568d96390d2c08cba6f9c16c17e04b6c7c949096d5908b766b6783de532cb4eaa331c14d81b18a6bea40b9229308e4a9399c8c9", + "id": "module-AppModule-05692c0296806f846ffa8a66e3675b6488ec6d03b4a87aac5053ac5f4885ea1c7a0941029a901631e9ff0cc809c5d617716c6f9001a4bd7bd448e4e2b9fd8384", "description": "", "deprecationMessage": "", "deprecated": false, "file": "projects/docs/src/app/app.module.ts", "methods": [], - "sourceCode": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport {\n GenericTableCoreModule,\n GenericTablePaginationModule,\n} from '@angular-generic-table/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { TabsComponent } from './components/tabs/tabs.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { CommonModule } from '@angular/common';\nimport { HttpClientModule } from '@angular/common/http';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport { RowHoverClickComponent } from './examples/row-hover-click/row-hover-click.component';\nimport { FooterComponent } from './examples/footer/footer.component';\nimport { SortingComponent } from './examples/sorting/sorting.component';\nimport { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component';\n\n@NgModule({\n declarations: [\n AppComponent,\n AdvancedComponent,\n SimpleComponent,\n HorizontalTableComponent,\n PaginationComponent,\n TabsComponent,\n CustomTemplatesComponent,\n MobileLayoutComponent,\n NestedDataComponent,\n TransposeComponent,\n RowHoverClickComponent,\n FooterComponent,\n SortingComponent,\n ServerSidePaginationComponent,\n ],\n imports: [\n BrowserModule,\n CommonModule,\n AppRoutingModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule,\n ],\n providers: [],\n bootstrap: [AppComponent],\n})\nexport class AppModule {}\n", + "sourceCode": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport {\n GenericTableCoreModule,\n GenericTablePaginationModule,\n} from '@angular-generic-table/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { TabsComponent } from './components/tabs/tabs.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { CommonModule } from '@angular/common';\nimport { HttpClientModule } from '@angular/common/http';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport { RowHoverClickComponent } from './examples/row-hover-click/row-hover-click.component';\nimport { FooterComponent } from './examples/footer/footer.component';\nimport { SortingComponent } from './examples/sorting/sorting.component';\nimport { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component';\nimport { RowSelectComponent } from './examples/row-select/row-select.component';\n\n@NgModule({\n declarations: [\n AppComponent,\n AdvancedComponent,\n SimpleComponent,\n HorizontalTableComponent,\n PaginationComponent,\n TabsComponent,\n CustomTemplatesComponent,\n MobileLayoutComponent,\n NestedDataComponent,\n TransposeComponent,\n RowHoverClickComponent,\n FooterComponent,\n SortingComponent,\n ServerSidePaginationComponent,\n RowSelectComponent,\n ],\n imports: [\n BrowserModule,\n CommonModule,\n AppRoutingModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule,\n ],\n providers: [],\n bootstrap: [AppComponent],\n})\nexport class AppModule {}\n", "children": [ { "type": "providers", @@ -6062,6 +8273,9 @@ { "name": "RowHoverClickComponent" }, + { + "name": "RowSelectComponent" + }, { "name": "ServerSidePaginationComponent" }, @@ -6113,13 +8327,13 @@ }, { "name": "AppRoutingModule", - "id": "module-AppRoutingModule-056ce6fa5285bcf62ac389e5990646ed12c3def5f4e71d855dc966b7fe096e8c7dd52ee76dbf954de3d5e586895691fe8a14b318de131dfece1940037face036", + "id": "module-AppRoutingModule-6b7030a561f0fa8129816edc1dc22bd910bb531ee345106dca4ebb6a32d04ebff6a7c412c1b227408a0ae294507cb5a3e410c94f24c7fca823b81b474d8e7483", "description": "", "deprecationMessage": "", "deprecated": false, "file": "projects/docs/src/app/app-routing.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport { RowHoverClickComponent } from './examples/row-hover-click/row-hover-click.component';\nimport { FooterComponent } from './examples/footer/footer.component';\nimport { SortingComponent } from './examples/sorting/sorting.component';\nimport { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component';\n\nconst routes: Routes = [\n {\n path: 'advanced',\n component: AdvancedComponent,\n },\n {\n path: 'pagination',\n component: PaginationComponent,\n },\n {\n path: 'lazy-loading',\n component: ServerSidePaginationComponent,\n },\n {\n path: 'simple',\n component: SimpleComponent,\n },\n {\n path: 'sorting',\n component: SortingComponent,\n },\n {\n path: 'row-hover-click',\n component: RowHoverClickComponent,\n },\n {\n path: 'horizontal-table',\n component: HorizontalTableComponent,\n },\n {\n path: 'custom-templates',\n component: CustomTemplatesComponent,\n },\n {\n path: 'mobile-layout',\n component: MobileLayoutComponent,\n },\n {\n path: 'nested',\n component: NestedDataComponent,\n },\n {\n path: 'transpose',\n component: TransposeComponent,\n },\n {\n path: 'footer',\n component: FooterComponent,\n },\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport { RowHoverClickComponent } from './examples/row-hover-click/row-hover-click.component';\nimport { FooterComponent } from './examples/footer/footer.component';\nimport { SortingComponent } from './examples/sorting/sorting.component';\nimport { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component';\nimport { RowSelectComponent } from './examples/row-select/row-select.component';\n\nconst routes: Routes = [\n {\n path: 'advanced',\n component: AdvancedComponent,\n },\n {\n path: 'pagination',\n component: PaginationComponent,\n },\n {\n path: 'lazy-loading',\n component: ServerSidePaginationComponent,\n },\n {\n path: 'simple',\n component: SimpleComponent,\n },\n {\n path: 'sorting',\n component: SortingComponent,\n },\n {\n path: 'row-hover-click',\n component: RowHoverClickComponent,\n },\n {\n path: 'row-select',\n component: RowSelectComponent,\n },\n {\n path: 'horizontal-table',\n component: HorizontalTableComponent,\n },\n {\n path: 'custom-templates',\n component: CustomTemplatesComponent,\n },\n {\n path: 'mobile-layout',\n component: MobileLayoutComponent,\n },\n {\n path: 'nested',\n component: NestedDataComponent,\n },\n {\n path: 'transpose',\n component: TransposeComponent,\n },\n {\n path: 'footer',\n component: FooterComponent,\n },\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n", "children": [ { "type": "providers", @@ -6149,13 +8363,13 @@ }, { "name": "GenericTableCoreModule", - "id": "module-GenericTableCoreModule-5e6f26e77833c9b6ffb8891bf22611853d3ccf26b6e6ee93cda5d153827062eb2978ba993150a911f3250163a3355a4aa0cd09c64c0550915cde75217981a475", + "id": "module-GenericTableCoreModule-d87313b0130c5f25c4e803a90e6d5e8b4516792f002d431d94ef3abbcccc10cfb128b7ff354582cb0673d7e600b8347d0bcd63db2a4b93a0a0a3d49e34009486", "description": "", "deprecationMessage": "", "deprecated": false, "file": "projects/core/src/lib/core.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\nimport { CoreComponent } from './core.component';\nimport { CommonModule } from '@angular/common';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\nimport { GtDeltaComponent } from './gt-delta/gt-delta.component';\n\n@NgModule({\n imports: [\n CommonModule,\n CoreComponent,\n SortClassPipe,\n DashCasePipe,\n HighlightPipe,\n CapitalCasePipe,\n CapitalCasePipe,\n DynamicPipe,\n GtDeltaComponent,\n ],\n exports: [CoreComponent, GtDeltaComponent],\n})\nexport class GenericTableCoreModule {}\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CoreComponent } from './core.component';\nimport { CommonModule } from '@angular/common';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\nimport { GtDeltaComponent } from './gt-delta/gt-delta.component';\nimport { RowSelectionPipe } from './pipes/row-selection.pipe';\n\n@NgModule({\n imports: [\n CommonModule,\n CoreComponent,\n SortClassPipe,\n DashCasePipe,\n HighlightPipe,\n RowSelectionPipe,\n CapitalCasePipe,\n CapitalCasePipe,\n DynamicPipe,\n GtDeltaComponent,\n ],\n exports: [CoreComponent, GtDeltaComponent],\n declarations: [],\n})\nexport class GenericTableCoreModule {}\n", "children": [ { "type": "providers", @@ -6189,6 +8403,9 @@ { "name": "HighlightPipe" }, + { + "name": "RowSelectionPipe" + }, { "name": "SortClassPipe" } @@ -6334,7 +8551,7 @@ "name": "context", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/core/src/test.ts", + "file": "projects/docs/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "", @@ -6344,7 +8561,7 @@ "name": "context", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/test.ts", + "file": "projects/core/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "", @@ -6383,21 +8600,21 @@ "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/environments/environment.prod.ts", + "file": "projects/docs/src/environments/environment.ts", "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\n production: true\n}" + "defaultValue": "{\n production: false\n}" }, { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/environments/environment.ts", + "file": "projects/docs/src/environments/environment.prod.ts", "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\n production: false\n}" + "defaultValue": "{\n production: true\n}" }, { "name": "Footer", @@ -6437,7 +8654,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\n {\n name: 'server-side-pagination.component.html',\n code: `
\n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n\n\n`,\n language: 'xml',\n },\n {\n name: 'server-side-pagination.component.ts',\n code: `import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { GtPaginationInfo } from '../../../../../core/src/lib/models/gt-pagination';\nimport { sortOrderToParams } from '../../../../../core/src/lib/utilities/utilities';\n\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n\n@Component({\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n\n paginationForm = this.fb.group({\n search: ['']\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(\n pluck('sorting'),\n shareReplay(1)\n );\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { ServerSidePaginationComponent } from './server-side-pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [ServerSidePaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [ServerSidePaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + "defaultValue": "[\n {\n name: 'server-side-pagination.component.html',\n code: `
\n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n\n\n`,\n language: 'xml',\n },\n {\n name: 'server-side-pagination.component.ts',\n code: `import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n GtPaginationInfo,\n sortOrderToParams\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\n\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n\n@Component({\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n\n paginationForm = this.fb.group({\n search: ['']\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(\n pluck('sorting'),\n shareReplay(1)\n );\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { ServerSidePaginationComponent } from './server-side-pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [ServerSidePaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [ServerSidePaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" }, { "name": "Mobile", @@ -6489,16 +8706,6 @@ "type": "Story", "defaultValue": "(\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n})" }, - { - "name": "Pagination", - "ctype": "miscellaneous", - "subtype": "variable", - "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "Story", - "defaultValue": "(\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n})" - }, { "name": "parseSortOrderParams", "ctype": "miscellaneous", @@ -6515,7 +8722,7 @@ "name": "require", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/core/src/test.ts", + "file": "projects/docs/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "literal type" @@ -6524,7 +8731,7 @@ "name": "require", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/test.ts", + "file": "projects/core/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "literal type" @@ -6537,7 +8744,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\n {\n name: 'row-hover-click.component.ts',\n code: `import { Component } from '@angular/core';\nimport { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core';\n\n\n@Component({\n selector: 'row-hover-click-table',\n template: '
\n \n \n \n {{ clicked }}\n
',\n styles: [\n \\`\n :host ::ng-deep .gt-hover {\n background-color: var(--bs-highlight-bg);\n }\n \\`,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n rowHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = \\`clicked row number: \\${event.index}\\`;\n }\n onRowHover(event: GtRowHoverEvent) {\n console.log('row hovered', event);\n }\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { RowHoverClickTableComponent } from './events-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [RowHoverClickTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [RowHoverClickTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + "defaultValue": "[\n {\n name: 'row-hover-click.component.ts',\n code: `import { Component } from '@angular/core';\nimport { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core';\n\n\n@Component({\n selector: 'row-hover-click-table',\n template: '
\n \n \n \n {{ clicked }}\n
',\n styles: [\n \\`\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n \\`,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n activateRowOnHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = \\`clicked row number: \\${event.index}\\`;\n }\n onRowHover(event: GtRowHoverEvent) {\n console.log('row hovered', event);\n }\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { RowHoverClickTableComponent } from './events-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [RowHoverClickTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [RowHoverClickTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" }, { "name": "RowHoverClick", @@ -6558,6 +8765,16 @@ "deprecationMessage": "", "type": "TableRow[]" }, + { + "name": "ServerSidePagination", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "(\n args: ServerSidePaginationComponent\n) => ({\n props: args,\n component: ServerSidePaginationComponent,\n})" + }, { "name": "Simple", "ctype": "miscellaneous", @@ -6578,6 +8795,16 @@ "type": "[]", "defaultValue": "[\n {\n name: 'footer-table.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableConfig, TableRow } from '@angular-generic-table/core';\nimport { DecimalPipe } from '@angular/common';\n\n@Component({\n template: \\`\n
\n \n \n
\n \n {{ height }} m\n \n \n \\`,\n})\nexport class FooterComponent implements OnInit {\n @ViewChild('heightTmplRef', { static: true }) heightTmplRef:\n | TemplateRef\n | undefined;\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n age: 27,\n weight: 85.457,\n height: 1.85,\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n age: 25,\n weight: 60.123,\n height: 1.65,\n },\n ];\n config: TableConfig = {};\n ngOnInit() {\n this.config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {\n class: 'text-end flex-end',\n },\n age: {\n class: 'text-end',\n },\n weight: {\n class: 'text-end',\n transform: {\n pipe: DecimalPipe,\n args: ['1.0-2'],\n },\n },\n height: {\n class: 'text-end',\n templateRef: this.heightTmplRef,\n },\n },\n footer: {\n headers: {\n sum: 'Total',\n numberOfWomen: 'Number of women',\n numberOfMen: 'Number of men',\n min: true,\n max: true,\n avg: true,\n count: true,\n static: true,\n first: true,\n },\n rowOrder: [\n 'first',\n 'numberOfWomen',\n 'numberOfMen',\n 'min',\n 'max',\n 'sum',\n 'avg',\n 'count',\n ],\n columns: {\n firstName: {},\n lastName: {\n static: 'n/a',\n },\n gender: {\n numberOfWomen: (data: Array, key) => {\n let count = 0;\n for (let i = 0; i < data.length; i++) {\n if (data[i][key] === 'female') {\n count++;\n }\n }\n return count;\n },\n numberOfMen: (data: Array, key) => {\n let count = 0;\n for (let i = 0; i < data.length; i++) {\n if (data[i][key] === 'male') {\n count++;\n }\n }\n return count;\n },\n },\n favoriteFood: {\n first: (data: Array, key) => data[0][key],\n },\n age: {\n sum: true,\n avg: true,\n count: true,\n max: true,\n min: true,\n },\n weight: {\n sum: true,\n avg: true,\n min: true,\n },\n height: {\n avg: true,\n min: true,\n max: true,\n },\n },\n },\n };\n }\n\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { BasicTableComponent } from './basic-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [BasicTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [BasicTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" }, + { + "name": "SIMPLE_SNIPPETS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/docs/src/app/examples/row-select/row-select.snippets.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "[\n {\n name: 'basic-table.component.ts',\n code: `import { Component } from '@angular/core';\nimport { TableConfig } from '@angular-generic-table/core';\n\ninterface BasicData {\n firstName: string;\n lastName: string;\n gender: 'male' | 'female';\n favoriteFood: string;\n}\n\n@Component({\n template: ''\n})\nexport class SimpleComponent {\n data: Array = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config: TableConfig = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n };\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { BasicTableComponent } from './basic-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [BasicTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [BasicTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + }, { "name": "SIMPLE_SNIPPETS", "ctype": "miscellaneous", @@ -6868,12 +9095,12 @@ "description": "

sortOrderToParams

\n" } ], - "projects/core/src/test.ts": [ + "projects/docs/src/test.ts": [ { "name": "context", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/core/src/test.ts", + "file": "projects/docs/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "", @@ -6883,18 +9110,18 @@ "name": "require", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/core/src/test.ts", + "file": "projects/docs/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "literal type" } ], - "projects/docs/src/test.ts": [ + "projects/core/src/test.ts": [ { "name": "context", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/test.ts", + "file": "projects/core/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "", @@ -6904,7 +9131,7 @@ "name": "require", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/test.ts", + "file": "projects/core/src/test.ts", "deprecated": false, "deprecationMessage": "", "type": "literal type" @@ -6934,28 +9161,28 @@ "defaultValue": "(\n args: CustomTemplatesComponent\n) => ({\n props: args,\n component: CustomTemplatesComponent,\n})" } ], - "projects/docs/src/environments/environment.prod.ts": [ + "projects/docs/src/environments/environment.ts": [ { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/environments/environment.prod.ts", + "file": "projects/docs/src/environments/environment.ts", "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\n production: true\n}" + "defaultValue": "{\n production: false\n}" } ], - "projects/docs/src/environments/environment.ts": [ + "projects/docs/src/environments/environment.prod.ts": [ { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "projects/docs/src/environments/environment.ts", + "file": "projects/docs/src/environments/environment.prod.ts", "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\n production: false\n}" + "defaultValue": "{\n production: true\n}" } ], "projects/docs/src/app/examples/footer/footer.component.ts": [ @@ -7003,7 +9230,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\n {\n name: 'server-side-pagination.component.html',\n code: `
\n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n\n\n`,\n language: 'xml',\n },\n {\n name: 'server-side-pagination.component.ts',\n code: `import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\nimport { GtPaginationInfo } from '../../../../../core/src/lib/models/gt-pagination';\nimport { sortOrderToParams } from '../../../../../core/src/lib/utilities/utilities';\n\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n\n@Component({\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n\n paginationForm = this.fb.group({\n search: ['']\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(\n pluck('sorting'),\n shareReplay(1)\n );\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { ServerSidePaginationComponent } from './server-side-pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [ServerSidePaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [ServerSidePaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + "defaultValue": "[\n {\n name: 'server-side-pagination.component.html',\n code: `
\n
\n
\n \n \n
\n
\n
\n
\n \n
\n
Table is empty
\n \n\n\n`,\n language: 'xml',\n },\n {\n name: 'server-side-pagination.component.ts',\n code: `import {\n ChangeDetectionStrategy,\n Component,\n OnInit,\n TemplateRef,\n ViewChild,\n} from '@angular/core';\nimport { DatePipe, formatDate } from '@angular/common';\nimport {\n GtPageChangeEvent,\n GtSortEvent,\n GtSortOrder,\n TableConfig,\n GtPaginationInfo,\n sortOrderToParams\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { pluck, shareReplay, switchMap, tap } from 'rxjs/operators';\n\ninterface LazyLoadingData {\n birthday: string;\n email: string;\n favorite_color: string;\n first_name: string;\n gender: string;\n id: number;\n last_name: string;\n}\n\ninterface LazyLoadingResponse {\n data: Array;\n paging: GtPaginationInfo;\n sorting: GtSortOrder;\n filters: [\n {\n [Property in keyof LazyLoadingResponse]: { [key: string]: any };\n }\n ];\n request: any;\n error: any;\n}\n\n@Component({\n templateUrl: './server-side-pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ServerSidePaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n\n paginationForm = this.fb.group({\n search: ['']\n });\n requestParams$ = new BehaviorSubject({\n page: 1,\n page_size: 10,\n sort_by: '+id',\n });\n search$ = this.paginationForm.controls.search.valueChanges.pipe(\n shareReplay(1)\n );\n loading$ = new BehaviorSubject(true);\n\n request$ = this.requestParams$.pipe(\n tap((_) => this.loading$.next(true)), // show loading indicator whenever requests are changed (remove to disable loading indicator)\n switchMap((params) =>\n this.http.get(\n 'https://private-a6da3-generictableapi.apiary-mock.com/data',\n {\n params,\n }\n )\n ),\n tap((_) => this.loading$.next(false)), // hide loading indicator\n shareReplay(1)\n );\n\n data$: Observable> = this.request$.pipe(\n pluck('data'),\n shareReplay(1)\n );\n\n paging$: Observable = this.request$.pipe(\n pluck('paging'),\n shareReplay(1)\n );\n\n sorting$ = this.request$.pipe(\n pluck('sorting'),\n shareReplay(1)\n );\n\n onPageChange(event: GtPageChangeEvent): void {\n const current = { ...this.requestParams$.value };\n if (current.page != event.index + 1) {\n this.requestParams$.next({\n ...current,\n page: event.index + 1,\n });\n }\n }\n\n onSortOrderChange(event: GtSortEvent): void {\n let sort_by = sortOrderToParams([...event.currentSortOrder]);\n const current = { ...this.requestParams$.value };\n if (current.sort_by !== sort_by) {\n this.requestParams$.next({\n ...current,\n page: 1, // reset page to 1 when sorting changes\n sort_by,\n });\n }\n }\n\n tableConfig$: ReplaySubject> = new ReplaySubject(\n 1\n );\n\n ngOnInit(): void {\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n id: {\n sortable: true,\n },\n first_name: {},\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n class: 'text-end justify-content-end',\n search: (row, column) => formatDate(row[column], 'longDate', 'en'),\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n favorite_color: {\n hidden: true,\n },\n email: {\n hidden: true,\n },\n },\n pagination: {\n length: 10,\n },\n });\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { ServerSidePaginationComponent } from './server-side-pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [ServerSidePaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [ServerSidePaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" } ], "projects/docs/src/app/examples/mobile-layout/mobile-layout.component.ts": [ @@ -7066,18 +9293,6 @@ "defaultValue": "(\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n})" } ], - "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts": [ - { - "name": "Pagination", - "ctype": "miscellaneous", - "subtype": "variable", - "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "Story", - "defaultValue": "(\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n})" - } - ], "projects/docs/src/app/examples/row-hover-click/row-hower-click.snippets.ts": [ { "name": "ROW_HOVER_CLICK_SNIPPETS", @@ -7087,7 +9302,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\n {\n name: 'row-hover-click.component.ts',\n code: `import { Component } from '@angular/core';\nimport { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core';\n\n\n@Component({\n selector: 'row-hover-click-table',\n template: '
\n \n \n \n {{ clicked }}\n
',\n styles: [\n \\`\n :host ::ng-deep .gt-hover {\n background-color: var(--bs-highlight-bg);\n }\n \\`,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n rowHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = \\`clicked row number: \\${event.index}\\`;\n }\n onRowHover(event: GtRowHoverEvent) {\n console.log('row hovered', event);\n }\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { RowHoverClickTableComponent } from './events-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [RowHoverClickTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [RowHoverClickTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + "defaultValue": "[\n {\n name: 'row-hover-click.component.ts',\n code: `import { Component } from '@angular/core';\nimport { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core';\n\n\n@Component({\n selector: 'row-hover-click-table',\n template: '
\n \n \n \n {{ clicked }}\n
',\n styles: [\n \\`\n :host ::ng-deep .gt-active {\n background-color: var(--bs-highlight-bg);\n }\n \\`,\n ],\n})\nexport class RowHoverClickComponent {\n clicked = '';\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n rowClick: true,\n activateRowOnHover: true,\n };\n\n onRowClick(event: GtRowClickEvent) {\n console.log('row clicked', event);\n this.clicked = \\`clicked row number: \\${event.index}\\`;\n }\n onRowHover(event: GtRowHoverEvent) {\n console.log('row hovered', event);\n }\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { RowHoverClickTableComponent } from './events-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [RowHoverClickTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [RowHoverClickTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" } ], "projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts": [ @@ -7102,6 +9317,18 @@ "defaultValue": "(\n args: RowHoverClickComponent\n) => ({\n props: args,\n component: RowHoverClickComponent,\n})" } ], + "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts": [ + { + "name": "ServerSidePagination", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "(\n args: ServerSidePaginationComponent\n) => ({\n props: args,\n component: ServerSidePaginationComponent,\n})" + } + ], "projects/docs/src/app/examples/simple/simple.component.ts": [ { "name": "Simple", @@ -7126,6 +9353,18 @@ "defaultValue": "[\n {\n name: 'footer-table.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableConfig, TableRow } from '@angular-generic-table/core';\nimport { DecimalPipe } from '@angular/common';\n\n@Component({\n template: \\`\n
\n \n \n
\n \n {{ height }} m\n \n \n \\`,\n})\nexport class FooterComponent implements OnInit {\n @ViewChild('heightTmplRef', { static: true }) heightTmplRef:\n | TemplateRef\n | undefined;\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n age: 27,\n weight: 85.457,\n height: 1.85,\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n age: 25,\n weight: 60.123,\n height: 1.65,\n },\n ];\n config: TableConfig = {};\n ngOnInit() {\n this.config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {\n class: 'text-end flex-end',\n },\n age: {\n class: 'text-end',\n },\n weight: {\n class: 'text-end',\n transform: {\n pipe: DecimalPipe,\n args: ['1.0-2'],\n },\n },\n height: {\n class: 'text-end',\n templateRef: this.heightTmplRef,\n },\n },\n footer: {\n headers: {\n sum: 'Total',\n numberOfWomen: 'Number of women',\n numberOfMen: 'Number of men',\n min: true,\n max: true,\n avg: true,\n count: true,\n static: true,\n first: true,\n },\n rowOrder: [\n 'first',\n 'numberOfWomen',\n 'numberOfMen',\n 'min',\n 'max',\n 'sum',\n 'avg',\n 'count',\n ],\n columns: {\n firstName: {},\n lastName: {\n static: 'n/a',\n },\n gender: {\n numberOfWomen: (data: Array, key) => {\n let count = 0;\n for (let i = 0; i < data.length; i++) {\n if (data[i][key] === 'female') {\n count++;\n }\n }\n return count;\n },\n numberOfMen: (data: Array, key) => {\n let count = 0;\n for (let i = 0; i < data.length; i++) {\n if (data[i][key] === 'male') {\n count++;\n }\n }\n return count;\n },\n },\n favoriteFood: {\n first: (data: Array, key) => data[0][key],\n },\n age: {\n sum: true,\n avg: true,\n count: true,\n max: true,\n min: true,\n },\n weight: {\n sum: true,\n avg: true,\n min: true,\n },\n height: {\n avg: true,\n min: true,\n max: true,\n },\n },\n },\n };\n }\n\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { BasicTableComponent } from './basic-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [BasicTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [BasicTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" } ], + "projects/docs/src/app/examples/row-select/row-select.snippets.ts": [ + { + "name": "SIMPLE_SNIPPETS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/docs/src/app/examples/row-select/row-select.snippets.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "[\n {\n name: 'basic-table.component.ts',\n code: `import { Component } from '@angular/core';\nimport { TableConfig } from '@angular-generic-table/core';\n\ninterface BasicData {\n firstName: string;\n lastName: string;\n gender: 'male' | 'female';\n favoriteFood: string;\n}\n\n@Component({\n template: ''\n})\nexport class SimpleComponent {\n data: Array = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config: TableConfig = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n };\n`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { BasicTableComponent } from './basic-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [BasicTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [BasicTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]" + } + ], "projects/docs/src/app/examples/sorting/sorting.snippets.ts": [ { "name": "SIMPLE_SNIPPETS", @@ -7288,6 +9527,10 @@ "path": "row-hover-click", "component": "RowHoverClickComponent" }, + { + "path": "row-select", + "component": "RowSelectComponent" + }, { "path": "horizontal-table", "component": "HorizontalTableComponent" @@ -7326,8 +9569,8 @@ "type": "component", "linktype": "component", "name": "CoreComponent", - "coveragePercent": 5, - "coverageCount": "2/40", + "coveragePercent": 15, + "coverageCount": "10/64", "status": "low" }, { @@ -7407,8 +9650,8 @@ "type": "interface", "linktype": "interface", "name": "TableConfig", - "coveragePercent": 36, - "coverageCount": "4/11", + "coveragePercent": 46, + "coverageCount": "6/13", "status": "medium" }, { @@ -7429,6 +9672,15 @@ "coverageCount": "0/2", "status": "low" }, + { + "filePath": "projects/core/src/lib/models/table-events.interface.ts", + "type": "interface", + "linktype": "interface", + "name": "GtRowActiveEvent", + "coveragePercent": 0, + "coverageCount": "0/4", + "status": "low" + }, { "filePath": "projects/core/src/lib/models/table-events.interface.ts", "type": "interface", @@ -7442,7 +9694,7 @@ "filePath": "projects/core/src/lib/models/table-events.interface.ts", "type": "interface", "linktype": "interface", - "name": "GtRowHoverEvent", + "name": "GtRowSelectEvent", "coveragePercent": 0, "coverageCount": "0/4", "status": "low" @@ -7497,9 +9749,9 @@ "type": "component", "linktype": "component", "name": "PaginationComponent", - "coveragePercent": 20, - "coverageCount": "3/15", - "status": "low" + "coveragePercent": 53, + "coverageCount": "8/15", + "status": "good" }, { "filePath": "projects/core/src/lib/pipes/capital-case.pipe.ts", @@ -7537,6 +9789,15 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "projects/core/src/lib/pipes/row-selection.pipe.ts", + "type": "pipe", + "linktype": "pipe", + "name": "RowSelectionPipe", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "projects/core/src/lib/pipes/sort-class.pipe.ts", "type": "pipe", @@ -7953,6 +10214,34 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "projects/docs/src/app/examples/row-select/row-select.component.ts", + "type": "component", + "linktype": "component", + "name": "RowSelectComponent", + "coveragePercent": 0, + "coverageCount": "0/19", + "status": "low" + }, + { + "filePath": "projects/docs/src/app/examples/row-select/row-select.component.ts", + "type": "interface", + "linktype": "interface", + "name": "RowData", + "coveragePercent": 0, + "coverageCount": "0/8", + "status": "low" + }, + { + "filePath": "projects/docs/src/app/examples/row-select/row-select.snippets.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "SIMPLE_SNIPPETS", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "projects/docs/src/app/examples/server-side-pagination/server-side-pagination.component.ts", "type": "component", @@ -7985,7 +10274,7 @@ "type": "variable", "linktype": "miscellaneous", "linksubtype": "variable", - "name": "Pagination", + "name": "ServerSidePagination", "coveragePercent": 0, "coverageCount": "0/1", "status": "low" diff --git a/projects/core/src/lib/core.component.html b/projects/core/src/lib/core.component.html index 8ee8d3d..b95098d 100644 --- a/projects/core/src/lib/core.component.html +++ b/projects/core/src/lib/core.component.html @@ -10,6 +10,12 @@ (tableConfig$ | async)?.stickyHeaders?.column " [attr.aria-busy]="(loading$ | async) === true ? true : null" + [tabindex]="(tableConfig$ | async)?.activateRowOnKeyboardNavigation ? 0 : -1" + #tableRef + (focus)="listenToKeyboardEvents()" + (focusout)="unsubscribeFromKeyboardEvents(tableRef)" + (mouseenter)="listenToKeyboardEvents()" + (mouseleave)="unsubscribeFromKeyboardEvents(tableRef)" > {{ row.value.header || row.key | capitalCase }} diff --git a/projects/core/src/lib/core.component.ts b/projects/core/src/lib/core.component.ts index 7118532..007bda3 100644 --- a/projects/core/src/lib/core.component.ts +++ b/projects/core/src/lib/core.component.ts @@ -3,15 +3,19 @@ import { Component, EventEmitter, Input, + OnDestroy, Output, + TrackByFunction, } from '@angular/core'; import { BehaviorSubject, combineLatest, + fromEvent, isObservable, Observable, of, ReplaySubject, + Subject, } from 'rxjs'; import { TableConfig } from './models/table-config.interface'; import { @@ -29,10 +33,12 @@ import { distinctUntilChanged, filter, map, + pluck, shareReplay, startWith, switchMap, take, + takeUntil, tap, withLatestFrom, } from 'rxjs/operators'; @@ -48,8 +54,9 @@ import { GtOrder, GtSortOrder } from './models/table-sort.interface'; import { TableMeta } from './models/table-meta.interface'; import { GtPageChangeEvent, + GtRowSelectEvent, GtRowClickEvent, - GtRowHoverEvent, + GtRowActiveEvent, GtSortEvent, } from './models/table-events.interface'; import { CapitalCasePipe } from './pipes/capital-case.pipe'; @@ -57,6 +64,7 @@ import { SortClassPipe } from './pipes/sort-class.pipe'; import { DashCasePipe } from './pipes/dash-case.pipe'; import { DynamicPipe } from './pipes/dynamic.pipe'; import { HighlightPipe } from './pipes/highlight.pipe'; +import { RowSelectionPipe } from './pipes/row-selection.pipe'; import { GtPaginationInfo } from './models/gt-pagination'; import { TableInfo } from './models/table-info.interface'; @@ -70,6 +78,7 @@ import { TableInfo } from './models/table-info.interface'; KeyValuePipe, SortClassPipe, DashCasePipe, + RowSelectionPipe, AsyncPipe, NgTemplateOutlet, SlicePipe, @@ -80,7 +89,43 @@ import { TableInfo } from './models/table-info.interface'; NgForOf, ], }) -export class CoreComponent { +export class CoreComponent implements OnDestroy { + unsubscribe$ = new Subject(); + get navigationKeys(): typeof this._navigationKeys { + return this._navigationKeys; + } + + /** navigationKeys + * @description An array of keyboard keys that will trigger navigation and active row, currently only supports arrow keys, home and end (omit key name from array to disable it) + * @type {string[]} + * @default ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End'] + */ + @Input() set navigationKeys(value: typeof this._navigationKeys) { + this._navigationKeys = value; + } + + private _navigationKeys = [ + 'ArrowDown', + 'ArrowUp', + 'ArrowLeft', + 'ArrowRight', + 'Home', + 'End', + ]; + get selectKeys(): string[] { + return this._selectKeys; + } + + /** selectKeys + * @description An array of keyboard keys that will trigger row selection (omit key name from array to disable it) + * @type {string[]} + * @default ['Enter', ' '] + */ + @Input() set selectKeys(value: string[]) { + this._selectKeys = value; + } + + private _selectKeys = ['Enter', ' ']; get sortOrder$(): Observable { return this._sortOrder$.asObservable(); } @@ -93,6 +138,10 @@ export class CoreComponent { this._currentPaginationIndex$.next(pageIndex); } + get paginationIndex(): number { + return this._currentPaginationIndex$.getValue(); + } + @Input() set pagingInfo(value: GtPaginationInfo | null) { if (value) { this._pagingInfo$.next(value); @@ -105,6 +154,74 @@ export class CoreComponent { } } + /** customClasses + * @description An object that contains custom classes for various elements in the table. + * @type {object} - { selectedRow: string, activeRow: string } - default classes are 'gt-selected' and 'gt-active' + */ + @Input() set customClasses(classes: Partial) { + this._customClasses = { ...this._customClasses, ...classes }; + } + + get customClasses(): typeof this._customClasses { + return this._customClasses; + } + + private _customClasses = { + selectedRow: 'gt-selected', + activeRow: 'gt-active', + }; + + /** isRowSelectedFn + * @description Function to determine if row is selected or not. + * @type {fn} A function that receives a row object and optional state for current selection that can be used to determine if row should be marked as selected or not. */ + @Input() set isRowSelectedFn( + fn: (row: TableRow | any, selection?: any) => boolean + ) { + this._isRowSelectedFn = fn; + } + + get isRowSelectedFn(): any { + return this._isRowSelectedFn; + } + + private _isRowSelectedFn: any; + + /** selection + * @description An object that contains the currently selected row(s) in the table. It's passed to the selection function to determine which rows should be selected. + * @type {any} + */ + @Input() selection: any = {}; + + /** rowIdKey + * @description row key to use as unique id for table row. If passed, table won't generate unique ids for each row but instead use key to retrieve unique id from row. + * @type {string} + */ + @Input() rowIdKey: string | undefined; + + /** generateRowId + * @description Whether or not to generate a unique id for each row in the table. Defaults to `true`. + * @type {boolean} + */ + @Input() generateRowId: boolean = true; + + /** trackRowByFn + * @description A function that returns a unique identifier for each row in the table to optimize rendering when data is added or removed. + * @type fn - TrackByFunction to retrieve unique id based on index and/or row. Defaults to using `row[this.rowIdKey]`. + */ + @Input() set trackRowByFn(fn: TrackByFunction) { + this._trackRowByFn = fn; + } + get trackRowByFn(): TrackByFunction { + return this._trackRowByFn; + } + + private _trackRowByFn( + index: number, + row: TableRow + ): TrackByFunction { + return this.rowIdKey ? row[this.rowIdKey] : row?._id; + } + @Input() set search(string: Observable | string | null) { this._searchBy$.next(string); @@ -115,9 +232,13 @@ export class CoreComponent { this._tableConfig$.next(config); } + private _tableConfig: TableConfig | undefined; + @Input() - set data(data: Observable> | Array) { - this._data$.next(data); + set data(data: Observable> | Array | null) { + if (data) { + this._data$.next(data); + } } @Input() set sortOrder(sortConfig: GtSortOrder | any) { @@ -128,47 +249,66 @@ export class CoreComponent { } @Output() rowClick = new EventEmitter(); + @Output() rowSelect = new EventEmitter(); @Output() sortOrderChange = new EventEmitter>(); _rowClick(row: TableRow, index: number, event: MouseEvent): void { this.rowClick.emit({ row, index, event }); } - private _rowHover$ = new ReplaySubject(1); - @Output() rowHover = new EventEmitter(); + _rowActive(row: TableRow, index: number, event: KeyboardEvent): void { + this.rowSelect.emit({ row, index, event }); + } + + private _rowActive$ = new ReplaySubject(1); + @Output() rowActive = new EventEmitter(); @Output() columnSort = new EventEmitter(); /** page change event - emitted when current page/index changes for pagination */ @Output() pageChange = new EventEmitter(); - rowHover$ = this._rowHover$.asObservable().pipe( - debounceTime(50), - distinctUntilChanged((p, q) => p.index === q.index), - tap((event) => this.rowHover.emit(event)), + rowActive$ = this._rowActive$.asObservable().pipe( + distinctUntilChanged((p, q) => { + if (this.rowIdKey && p.row && q.row) { + return p.row[this.rowIdKey] === q.row[this.rowIdKey]; + } else if (this.generateRowId && p.row && q.row) { + return p.row._id === q.row._id; + } else { + return p.index === q.index; + } + }), + tap((event) => (this.activeRowIndex = event.index)), + tap((event) => this.rowActive.emit(event)), shareReplay(1) ); - hoverRow(id: string): void; - hoverRow(index: number): void; - hoverRow(none: null): void; - hoverRow(arg: string | number | null): void { + activeRowIndex: number | null = null; + activateRow(id: string, event?: MouseEvent | KeyboardEvent): void; + activateRow(index: number, event?: MouseEvent | KeyboardEvent): void; + activateRow(none: null, event?: MouseEvent | KeyboardEvent): void; + activateRow( + arg: string | number | null, + event?: MouseEvent | KeyboardEvent + ): void { if (typeof arg === 'number') { - this.data$ + this.table$ .pipe( - map((data) => data[arg]), - take(1) + pluck('data'), + map((data) => data[this.paginationIndex][arg]), + take(1), + takeUntil(this.unsubscribe$) ) - .subscribe((row) => this._hoverRow(row, arg)); + .subscribe((row) => this._activateRow(row, arg, event)); } else if (typeof arg === 'string') { // TODO: implement hover by id } else { - this._hoverRow(null, null); + this._activateRow(null, null); } } - _hoverRow( + protected _activateRow( row: TableRow | null, index: number | null, - event?: MouseEvent + event?: MouseEvent | KeyboardEvent ): void { - this._rowHover$.next({ row, index, event }); + this._rowActive$.next({ row, index, event }); } get loading$(): Observable { @@ -211,6 +351,7 @@ export class CoreComponent { tableConfig$ = this._tableConfig$.pipe( map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), + tap((config) => (this._tableConfig = config)), shareReplay(1) ); @@ -249,6 +390,13 @@ export class CoreComponent { } data = newData; } + if (this.generateRowId && !this.rowIdKey && data.length > 0) { + const dataWithId = []; + for (let i = 0; i < data.length; i++) { + dataWithId[i] = { ...data[i], _id: i }; + } + data = dataWithId; + } return { data, config }; }), switchMap((obs) => @@ -314,6 +462,7 @@ export class CoreComponent { config, info: { numberOfRecords: sorted.length, + pageSize: +(config.pagination.length || 0), pageTotal: Math.ceil( sorted.length / +(config.pagination.length || 0) ), @@ -483,4 +632,156 @@ export class CoreComponent { object ); } + + private _unsubscribeFromKeyboardEvents$ = new Subject(); + private _keyboardArrowEvent$ = fromEvent( + document, + 'keydown' + ).pipe( + filter( + (event) => + [...this._navigationKeys, ...this._selectKeys].indexOf(event.key) > -1 + ) + ); + + protected listenToKeyboardEvents(): void { + if (!this._tableConfig?.activateRowOnKeyboardNavigation) { + return; + } + + this._unsubscribeFromKeyboardEvents$.next(true); + this._keyboardArrowEvent$ + .pipe( + withLatestFrom( + this.data$, + this.currentPaginationIndex$, + this.tableInfo$ + ), + takeUntil(this._unsubscribeFromKeyboardEvents$), + takeUntil(this.unsubscribe$) + ) + .subscribe(([event, rows, currentPage, tableInfo]) => { + const selectEvent = this._selectKeys.includes(event.key); + if (selectEvent && this.activeRowIndex !== null) { + const rowIndex = + this.activeRowIndex + currentPage * (tableInfo?.pageSize ?? 0); + this._rowActive(rows[rowIndex], rowIndex, event); + return; + } + + const navigationEvent = this._navigationKeys.includes(event.key); + if (navigationEvent) { + this._handleNavigationEvent(event, rows, currentPage, tableInfo); + } + }); + } + unsubscribeFromKeyboardEvents(tableRef: HTMLTableElement): void { + if (!this._tableConfig?.activateRowOnKeyboardNavigation) { + return; + } + // only unsubscribe if table is not focused + if (tableRef !== document.activeElement) { + if (this._tableConfig?.activateRowOnHover) { + // unset active row + this.activateRow(null); + } + this._unsubscribeFromKeyboardEvents$.next(true); + } + } + + private _handleNavigationEvent( + event: KeyboardEvent, + rows: any[], + currentPage: number, + tableInfo: any + ): void { + const hasPagination = (tableInfo?.pageTotal || 0) > 1 && tableInfo; + const lastRowIndex = rows.length - 1; + let newIndex = this.activeRowIndex; + let indexModifier = 0; + + if (event.key === 'Home') { + this.paginationIndex = 0; + this.activateRow(0, event); + return; + } + + if (event.key === 'End') { + const indexOfLastRecord = hasPagination + ? rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1 + : lastRowIndex; + if (tableInfo?.pageTotal) { + this.paginationIndex = tableInfo.pageTotal - 1; + } + this.activateRow(indexOfLastRecord, event); + return; + } + + if (event.key === 'ArrowDown') { + indexModifier = 1; + } else if (event.key === 'ArrowUp') { + indexModifier = -1; + } + + if (newIndex === null) { + newIndex = 0; + } else if ( + newIndex + indexModifier >= 0 && + newIndex + indexModifier <= lastRowIndex + ) { + newIndex = newIndex + indexModifier; + } + + if (hasPagination && tableInfo?.pageSize) { + const isNotLastPage = currentPage + 1 < tableInfo.pageTotal; + const recordsOnLastPage = + rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1; + const maxIndex = isNotLastPage + ? tableInfo?.pageSize - 1 + : recordsOnLastPage; + + if (event.key === 'ArrowLeft' && currentPage > 0) { + this.paginationIndex = currentPage - 1; + this.activateRow(newIndex, event); + return; + } else if (event.key === 'ArrowRight' && isNotLastPage) { + if ( + currentPage + 1 === tableInfo.pageTotal - 1 && + newIndex > recordsOnLastPage + ) { + this.activateRow(recordsOnLastPage, event); + } + this.paginationIndex = currentPage + 1; + this.activateRow(newIndex, event); + return; + } + + if ( + currentPage > 0 && + indexModifier < 0 && + newIndex + indexModifier <= lastRowIndex && + (this.activeRowIndex || 0) + indexModifier < 0 + ) { + // set last row of previous page as active + this.activateRow(tableInfo?.pageSize - 1, event); + this.paginationIndex = currentPage - 1; + return; + } + + const pageIndex = newIndex % tableInfo?.pageSize; + + if (newIndex > maxIndex && currentPage + 1 < tableInfo.pageTotal) { + this.paginationIndex = currentPage + 1; + } + this.activateRow(pageIndex > maxIndex ? maxIndex : pageIndex, event); + return; + } + + this.activateRow(newIndex, event); + } + + ngOnDestroy() { + this.unsubscribe$.next(true); + this.unsubscribe$.complete(); + } } diff --git a/projects/core/src/lib/core.module.ts b/projects/core/src/lib/core.module.ts index fe0a3ab..84f9902 100644 --- a/projects/core/src/lib/core.module.ts +++ b/projects/core/src/lib/core.module.ts @@ -7,6 +7,7 @@ import { HighlightPipe } from './pipes/highlight.pipe'; import { CapitalCasePipe } from './pipes/capital-case.pipe'; import { DynamicPipe } from './pipes/dynamic.pipe'; import { GtDeltaComponent } from './gt-delta/gt-delta.component'; +import { RowSelectionPipe } from './pipes/row-selection.pipe'; @NgModule({ imports: [ @@ -15,11 +16,13 @@ import { GtDeltaComponent } from './gt-delta/gt-delta.component'; SortClassPipe, DashCasePipe, HighlightPipe, + RowSelectionPipe, CapitalCasePipe, CapitalCasePipe, DynamicPipe, GtDeltaComponent, ], exports: [CoreComponent, GtDeltaComponent], + declarations: [], }) export class GenericTableCoreModule {} diff --git a/projects/core/src/lib/models/table-config.interface.ts b/projects/core/src/lib/models/table-config.interface.ts index ff14268..5bfc1ff 100644 --- a/projects/core/src/lib/models/table-config.interface.ts +++ b/projects/core/src/lib/models/table-config.interface.ts @@ -12,7 +12,6 @@ export interface TableConfig { column?: boolean; }; /** Add one or more CSS classes to the table element e.g. `table table-striped table-bordered`.

**Default:** `table`

*/ - class?: string; rows?: { [Property in keyof R]: TableColumn; @@ -24,7 +23,11 @@ export interface TableConfig { length?: number; }; rowClick?: boolean; - rowHover?: boolean; + /** Toggle row active state on mouse enter/leave (hover)

**Default:** `false`

*/ + activateRowOnHover?: boolean; + /** Toggle row active state on keyboard navigation

**Default:** `false`

*/ + activateRowOnKeyboardNavigation?: boolean; + deactivateRowOnLostFocus?: boolean; footer?: { headers?: { [key: FooterCalculation | string]: string | boolean; diff --git a/projects/core/src/lib/models/table-events.interface.ts b/projects/core/src/lib/models/table-events.interface.ts index 4e1bac5..8acbdcd 100644 --- a/projects/core/src/lib/models/table-events.interface.ts +++ b/projects/core/src/lib/models/table-events.interface.ts @@ -7,10 +7,16 @@ export interface GtRowClickEvent { event: MouseEvent; } -export interface GtRowHoverEvent { +export interface GtRowActiveEvent { row: R | null; index: number | null; - event?: MouseEvent; + event?: MouseEvent | KeyboardEvent; +} + +export interface GtRowSelectEvent { + row: R | null; + index: number | null; + event?: KeyboardEvent; } export interface GtSortEvent { diff --git a/projects/core/src/lib/pipes/row-selection.pipe.spec.ts b/projects/core/src/lib/pipes/row-selection.pipe.spec.ts new file mode 100644 index 0000000..b5cfa68 --- /dev/null +++ b/projects/core/src/lib/pipes/row-selection.pipe.spec.ts @@ -0,0 +1,8 @@ +import { RowSelectionPipe } from './row-selection.pipe'; + +describe('RowSelectionPipe', () => { + it('create an instance', () => { + const pipe = new RowSelectionPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/projects/core/src/lib/pipes/row-selection.pipe.ts b/projects/core/src/lib/pipes/row-selection.pipe.ts new file mode 100644 index 0000000..c7c4c94 --- /dev/null +++ b/projects/core/src/lib/pipes/row-selection.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { TableRow } from '../models/table-row.interface'; + +@Pipe({ + name: 'rowSelection', + standalone: true, +}) +export class RowSelectionPipe implements PipeTransform { + transform( + row: TableRow, + selection: any, + comparator: (row: TableRow, selection: any) => boolean, + className?: string + ): string { + return className && comparator(row, selection) ? className : ''; + } +} diff --git a/projects/docs/src/app/app-routing.module.ts b/projects/docs/src/app/app-routing.module.ts index 12c8032..159160a 100644 --- a/projects/docs/src/app/app-routing.module.ts +++ b/projects/docs/src/app/app-routing.module.ts @@ -12,6 +12,7 @@ import { RowHoverClickComponent } from './examples/row-hover-click/row-hover-cli import { FooterComponent } from './examples/footer/footer.component'; import { SortingComponent } from './examples/sorting/sorting.component'; import { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component'; +import { RowSelectComponent } from './examples/row-select/row-select.component'; const routes: Routes = [ { @@ -38,6 +39,10 @@ const routes: Routes = [ path: 'row-hover-click', component: RowHoverClickComponent, }, + { + path: 'row-select', + component: RowSelectComponent, + }, { path: 'horizontal-table', component: HorizontalTableComponent, diff --git a/projects/docs/src/app/app.module.ts b/projects/docs/src/app/app.module.ts index 12cc2bf..f1c1bc3 100644 --- a/projects/docs/src/app/app.module.ts +++ b/projects/docs/src/app/app.module.ts @@ -23,6 +23,7 @@ import { RowHoverClickComponent } from './examples/row-hover-click/row-hover-cli import { FooterComponent } from './examples/footer/footer.component'; import { SortingComponent } from './examples/sorting/sorting.component'; import { ServerSidePaginationComponent } from './examples/server-side-pagination/server-side-pagination.component'; +import { RowSelectComponent } from './examples/row-select/row-select.component'; @NgModule({ declarations: [ @@ -40,6 +41,7 @@ import { ServerSidePaginationComponent } from './examples/server-side-pagination FooterComponent, SortingComponent, ServerSidePaginationComponent, + RowSelectComponent, ], imports: [ BrowserModule, diff --git a/projects/docs/src/app/examples/pagination/pagination.component.html b/projects/docs/src/app/examples/pagination/pagination.component.html index 950c153..2c6c69c 100644 --- a/projects/docs/src/app/examples/pagination/pagination.component.html +++ b/projects/docs/src/app/examples/pagination/pagination.component.html @@ -7,6 +7,7 @@ formControlName="length" type="number" class="form-control" + min="0" />
diff --git a/projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts b/projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts index a7ab9d0..3574d84 100644 --- a/projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts +++ b/projects/docs/src/app/examples/row-hover-click/row-hover-click.component.ts @@ -1,23 +1,33 @@ import { Component } from '@angular/core'; import { Story } from '@storybook/angular/types-6-0'; import { ROW_HOVER_CLICK_SNIPPETS } from './row-hower-click.snippets'; -import { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core'; +import { + GtRowClickEvent, + GtRowActiveEvent, + TableConfig, +} from '@angular-generic-table/core'; @Component({ selector: 'docs-row-hover-click', template: `
- -
@@ -26,7 +36,7 @@ import { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core'; `, styles: [ ` - :host ::ng-deep .gt-hover { + :host ::ng-deep .gt-active { background-color: var(--bs-highlight-bg); } `, @@ -48,7 +58,7 @@ export class RowHoverClickComponent { favoriteFood: 'Pizza', }, ]; - config = { + config: TableConfig = { columns: { firstName: {}, lastName: {}, @@ -56,14 +66,14 @@ export class RowHoverClickComponent { favoriteFood: {}, }, rowClick: true, - rowHover: true, + activateRowOnHover: true, }; onRowClick(event: GtRowClickEvent) { console.log('row clicked', event); this.clicked = `clicked row number: ${event.index}`; } - onRowHover(event: GtRowHoverEvent) { + onRowHover(event: GtRowActiveEvent) { console.log('row hovered', event); } diff --git a/projects/docs/src/app/examples/row-hover-click/row-hower-click.snippets.ts b/projects/docs/src/app/examples/row-hover-click/row-hower-click.snippets.ts index c3cab1e..0db409b 100644 --- a/projects/docs/src/app/examples/row-hover-click/row-hower-click.snippets.ts +++ b/projects/docs/src/app/examples/row-hover-click/row-hower-click.snippets.ts @@ -8,24 +8,24 @@ import { GtRowClickEvent, GtRowHoverEvent } from '@angular-generic-table/core'; @Component({ selector: 'row-hover-click-table', template: '
- - {{ clicked }}
', styles: [ \` - :host ::ng-deep .gt-hover { + :host ::ng-deep .gt-active { background-color: var(--bs-highlight-bg); } \`, @@ -55,7 +55,7 @@ export class RowHoverClickComponent { favoriteFood: {}, }, rowClick: true, - rowHover: true, + activateRowOnHover: true, }; onRowClick(event: GtRowClickEvent) { diff --git a/projects/docs/src/app/examples/row-select/row-select.component.html b/projects/docs/src/app/examples/row-select/row-select.component.html new file mode 100644 index 0000000..b9ac92d --- /dev/null +++ b/projects/docs/src/app/examples/row-select/row-select.component.html @@ -0,0 +1,74 @@ +
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+ Selected rows: {{ (selection | keyvalue).length }} +
+
+ Active row id: {{ (activeRow$ | async)?.id ?? "none" }} +
+
+
+ +
+
Table is empty
+
+ + +
+ diff --git a/projects/docs/src/app/examples/row-select/row-select.component.ts b/projects/docs/src/app/examples/row-select/row-select.component.ts new file mode 100644 index 0000000..8b9ecf4 --- /dev/null +++ b/projects/docs/src/app/examples/row-select/row-select.component.ts @@ -0,0 +1,185 @@ +import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; +import { + GtRowSelectEvent, + GtRowClickEvent, + TableRow, + GtRowActiveEvent, +} from '@angular-generic-table/core'; +import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs'; +import { + map, + pluck, + shareReplay, + startWith, + take, + takeUntil, + tap, +} from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { FormControl } from '@angular/forms'; +import { Story } from '@storybook/angular/types-6-0'; +import { CUSTOM_TEMPLATES_DOCS } from '../custom-templates/custom-templates.snippets'; +import { ROW_SELECT_SNIPPETS } from './row-select.snippets'; +interface RowData { + birthday: string; + email: string; + favorite_color: string; + first_name: string; + gender: 'Female' | 'Male'; + id: number; + last_name: string; +} +@Component({ + templateUrl: './row-select.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styles: [ + ` + :host ::ng-deep .gt-active { + background-color: var(--bs-highlight-bg); + } + :host ::ng-deep .table > tbody > tr { + cursor: pointer; + } + `, + ], +}) +export class RowSelectComponent implements OnDestroy { + constructor(private http: HttpClient) {} + + get activateOnRowHover$(): Observable { + return this._activateOnRowHover$.asObservable(); + } + get activateOnRowHover(): boolean { + return this._activateOnRowHover$.getValue(); + } + toggleRowHover() { + this._activateOnRowHover$.next(!this.activateOnRowHover); + } + private _activateOnRowHover$ = new BehaviorSubject(true); + + get activateOnNavigation$(): Observable { + return this._activateOnNavigation$.asObservable(); + } + get activateOnNavigation(): boolean { + return this._activateOnNavigation$.getValue(); + } + toggleRowNavigation() { + this._activateOnNavigation$.next(!this.activateOnNavigation); + } + private _activateOnNavigation$ = new BehaviorSubject(true); + + private unsubscribe$ = new Subject(); + loading$ = new BehaviorSubject(true); + customClassNames = { + selectedRow: 'table-active', + }; + lengthCtrl = new FormControl(15); + data$ = this.http + .get<{ data: TableRow[] }>( + 'https://private-730c61-generictable.apiary-mock.com/data' + ) + .pipe( + pluck('data'), + tap((_) => this.loading$.next(false)), + takeUntil(this.unsubscribe$), + shareReplay(1) + ); + config$ = combineLatest([ + this.lengthCtrl.valueChanges.pipe( + startWith(this.lengthCtrl.value), + map((length) => + length ? { pagination: { length: length < 0 ? 0 : length } } : {} + ) + ), + this.activateOnRowHover$, + this.activateOnNavigation$, + ]).pipe( + map( + ([pagination, activateRowOnHover, activateRowOnKeyboardNavigation]) => ({ + columns: { + id: { + sortable: true, + }, + first_name: { + sortable: true, + }, + last_name: { + sortable: true, + }, + gender: { + sortable: true, + }, + email: { + sortable: true, + }, + }, + ...pagination, + rowClick: true, + activateRowOnHover, + activateRowOnKeyboardNavigation, + rowSelect: true, + }) + ) + ); + + setActiveRow(event: GtRowActiveEvent) { + console.log(event); + if (event.event && event.event.type === 'keydown') { + // stop the event from propagating, otherwise the page will scroll + event.event.preventDefault(); + } + this._activeRow$.next(event.row); + } + get activeRow$() { + return this._activeRow$.asObservable(); + } + private _activeRow$ = new BehaviorSubject(null); + isSelected(row: RowData, selection: typeof this.selection) { + return selection[row.id]; + } + + selection: { [key: string]: boolean } = {}; + + selectRow(event: GtRowClickEvent | GtRowSelectEvent) { + const selection = { ...this.selection }; + const row = event.row; + if (!row) return; + if (!selection[row.id]) { + selection[row.id] = true; + } else { + delete selection[row.id]; + } + // update the selection + this.selection = selection; + } + + get isAllSelected() { + return Object.keys(this.selection).length > 0; + } + toggleAll() { + if (this.isAllSelected) { + this.selection = {}; + return; + } + const selection = { ...this.selection }; + this.data$.pipe(take(1), takeUntil(this.unsubscribe$)).subscribe((data) => { + data.forEach((row, index) => { + selection[index] = true; + }); + }); + this.selection = selection; + } + + ngOnDestroy() { + this.unsubscribe$.next(); + this.unsubscribe$.complete(); + } + SNIPPETS = ROW_SELECT_SNIPPETS; +} + +export const RowSelect: Story = ( + args: RowSelectComponent +) => ({ + props: args, + component: RowSelectComponent, +}); diff --git a/projects/docs/src/app/examples/row-select/row-select.snippets.ts b/projects/docs/src/app/examples/row-select/row-select.snippets.ts new file mode 100644 index 0000000..0ac4cf7 --- /dev/null +++ b/projects/docs/src/app/examples/row-select/row-select.snippets.ts @@ -0,0 +1,274 @@ +export const ROW_SELECT_SNIPPETS = [ + { + name: 'row-select.component.ts', + code: `import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; +import { + GtRowSelectEvent, + GtRowClickEvent, + TableRow, + GtRowActiveEvent, +} from '@angular-generic-table/core'; +import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs'; +import { + map, + pluck, + shareReplay, + startWith, + take, + takeUntil, + tap, +} from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { FormControl } from '@angular/forms'; +interface RowData { + birthday: string; + email: string; + favorite_color: string; + first_name: string; + gender: 'Female' | 'Male'; + id: number; + last_name: string; +} +@Component({ + templateUrl: './row-select.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styles: [ + \` + :host ::ng-deep .gt-active { + background-color: var(--bs-highlight-bg); + } + :host ::ng-deep .table > tbody > tr { + cursor: pointer; + } + \`, + ], +}) +export class RowSelectComponent implements OnDestroy { + constructor(private http: HttpClient) {} + + get activateOnRowHover$(): Observable { + return this._activateOnRowHover$.asObservable(); + } + get activateOnRowHover(): boolean { + return this._activateOnRowHover$.getValue(); + } + toggleRowHover() { + this._activateOnRowHover$.next(!this.activateOnRowHover); + } + private _activateOnRowHover$ = new BehaviorSubject(true); + + get activateOnNavigation$(): Observable { + return this._activateOnNavigation$.asObservable(); + } + get activateOnNavigation(): boolean { + return this._activateOnNavigation$.getValue(); + } + toggleRowNavigation() { + this._activateOnNavigation$.next(!this.activateOnNavigation); + } + private _activateOnNavigation$ = new BehaviorSubject(true); + + private unsubscribe$ = new Subject(); + loading$ = new BehaviorSubject(true); + customClassNames = { + selectedRow: 'table-active', + }; + lengthCtrl = new FormControl(15); + data$ = this.http + .get<{ data: TableRow[] }>( + 'https://private-730c61-generictable.apiary-mock.com/data' + ) + .pipe( + pluck('data'), + tap((_) => this.loading$.next(false)), + takeUntil(this.unsubscribe$), + shareReplay(1) + ); + config$ = combineLatest([ + this.lengthCtrl.valueChanges.pipe( + startWith(this.lengthCtrl.value), + map((length) => + length ? { pagination: { length: length < 0 ? 0 : length } } : {} + ) + ), + this.activateOnRowHover$, + this.activateOnNavigation$, + ]).pipe( + map( + ([pagination, activateRowOnHover, activateRowOnKeyboardNavigation]) => ({ + columns: { + id: { + sortable: true, + }, + first_name: { + sortable: true, + }, + last_name: { + sortable: true, + }, + gender: { + sortable: true, + }, + email: { + sortable: true, + }, + }, + ...pagination, + rowClick: true, + activateRowOnHover, + activateRowOnKeyboardNavigation, + rowSelect: true, + }) + ) + ); + + setActiveRow(event: GtRowActiveEvent) { + console.log(event); + if (event.event && event.event.type === 'keydown') { + // stop the event from propagating, otherwise the page will scroll + event.event.preventDefault(); + } + this._activeRow$.next(event.row); + } + get activeRow$() { + return this._activeRow$.asObservable(); + } + private _activeRow$ = new BehaviorSubject(null); + isSelected(row: RowData, selection: typeof this.selection) { + return selection[row.id]; + } + + selection: { [key: string]: boolean } = {}; + + selectRow(event: GtRowClickEvent | GtRowSelectEvent) { + const selection = { ...this.selection }; + const row = event.row; + if (!row) return; + if (!selection[row.id]) { + selection[row.id] = true; + } else { + delete selection[row.id]; + } + // update the selection + this.selection = selection; + } + + get isAllSelected() { + return Object.keys(this.selection).length > 0; + } + toggleAll() { + if (this.isAllSelected) { + this.selection = {}; + return; + } + const selection = { ...this.selection }; + this.data$.pipe(take(1), takeUntil(this.unsubscribe$)).subscribe((data) => { + data.forEach((row, index) => { + selection[index] = true; + }); + }); + this.selection = selection; + } + + ngOnDestroy() { + this.unsubscribe$.next(); + this.unsubscribe$.complete(); + } +}; +`, + language: 'typescript', + }, + { + name: 'row-select.component.html', + code: `
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+ Selected rows: {{ (selection | keyvalue).length }} +
+
+ Active row id: {{ (activeRow$ | async)?.id ?? "none" }} +
+
+
+ +
+
Table is empty
+
+ + +
`, + language: 'xml', + }, + { + name: 'app.module.ts', + code: `import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; + +import { RowSelectComponent } from './row-select.component'; +import { GenericTableCoreModule } from '@angular-generic-table/core'; + +@NgModule({ + declarations: [RowSelectComponent], + imports: [BrowserModule, GenericTableCoreModule], + bootstrap: [RowSelectComponent] +}) +export class AppModule {}`, + language: 'typescript', + }, +]; diff --git a/projects/docs/src/stories/examples/Row-events.stories.mdx b/projects/docs/src/stories/examples/Row-events.stories.mdx index 4829e0b..a1514bd 100644 --- a/projects/docs/src/stories/examples/Row-events.stories.mdx +++ b/projects/docs/src/stories/examples/Row-events.stories.mdx @@ -10,20 +10,18 @@ import {HttpClientModule} from "@angular/common/http"; import {GenericTablePaginationModule} from "../../../../core/src/lib/pagination/pagination.module"; import { RowHoverClick, RowHoverClickComponent } from '../../app/examples/row-hover-click/row-hover-click.component'; - -# Row click and hover +# Row click and active state -Set `rowClick` and `rowHover` to `true` in `config` to enable row click and hover events which can be used to trigger actions when rows are clicked or hovered. These events are emitted and declared using `@Output rowClick` and `@Output() rowHover`. It's also possible to set hover state on a row using `rowHover` method on the table component (currently it takes a row index as argument), pass null to remove hover state. The later might come in handy when you want to highlight something in the table based on an external source like a chart etc. Note that you should use css if you're only interested in styling the row on row hover, no need to listen to events or set hover state to do that. +Set `rowClick` and `activateRowOnHover` to `true` in `config` to enable row click and row active events which can be used to trigger actions when rows are clicked or activated on hover (mouseenter/leave). These events are emitted and declared using `@Output rowClick` and `@Output() rowActive`. It's also possible to toggle active state on a row using `activateRow` method on the table component (currently it takes a row index as argument), pass null to remove active state. The later might come in handy when you want to highlight something in the table based on an external source like a chart etc. Note that you should use css if you're only interested in styling the row on row hover, no need to listen to events or set active state to do that. ### Example -Resize screen to view mobile version - - + {RowHoverClick.bind({})} diff --git a/projects/docs/src/stories/examples/Row-selection.stories.mdx b/projects/docs/src/stories/examples/Row-selection.stories.mdx new file mode 100644 index 0000000..a0747c4 --- /dev/null +++ b/projects/docs/src/stories/examples/Row-selection.stories.mdx @@ -0,0 +1,31 @@ +import { Meta, Story } from '@storybook/addon-docs'; + +import {moduleMetadata} from "@storybook/angular"; +import {BrowserModule} from "@angular/platform-browser"; +import {CommonModule} from "@angular/common"; +import {GenericTableCoreModule} from "../../../../core/src/lib/core.module"; +import {ReactiveFormsModule} from "@angular/forms"; +import {TabsComponent} from "../../app/components/tabs/tabs.component"; +import {HttpClientModule} from "@angular/common/http"; +import {GenericTablePaginationModule} from "../../../../core/src/lib/pagination/pagination.module"; +import { RowSelect, RowSelectComponent } from '../../app/examples/row-select/row-select.component'; + + + +# Row selection + +##### Determining if row is selected or not +Pass a function using `isRowSelectedFn` input to determine if row is selected or not, e.g. `isRowSelectedFn: (row: any) => boolean = (row) => row.selected;` if row data contains information about selected state or `isRowSelectedFn: (row: any, selection: any) => boolean = (row) => selection[row.id]` if state is not known by row but stored separately. The active selection can be passed to the table using `selection` input. It's also possible to listen to row select events using `rowSelect` output. + +##### Keyboard navigation and row selection +The example below shows one of multiple ways to implement row selection. It also shows how setting config options `activateRowOnHover` and `activateRowOnKeyboardNavigation` to true allows users to activate rows by hovering rows or using keyboard navigation (arrow keys and home and end) while table has focus. While navigating with a keyboard, rows can be selected using selection keys that can be passed using `selectKeys` input (defaults to `['Enter', ' ']`). In the example below it's also possible to select rows by clicking them. +### Example + + + + {RowSelect.bind({})} +