carbon-components-angular
Version:
Next generation components
1 lines • 231 kB
Source Map (JSON)
{"version":3,"file":"carbon-components-angular-table.mjs","sources":["../../src/table/toolbar/table-toolbar.component.ts","../../src/table/toolbar/table-toolbar-actions.component.ts","../../src/table/toolbar/table-toolbar-search.component.ts","../../src/search/search.component.html","../../src/table/toolbar/table-toolbar-content.component.ts","../../src/table/header/table-header-description.directive.ts","../../src/table/header/table-header-title.directive.ts","../../src/table/table-header-item.class.ts","../../src/table/table-item.class.ts","../../src/table/table-model.class.ts","../../src/table/data-grid-interaction-model.class.ts","../../src/table/table-adapter.class.ts","../../src/table/table.directive.ts","../../src/table/head/table-head-cell-label.directive.ts","../../src/table/head/table-head-cell.component.ts","../../src/table/head/table-head-checkbox.component.ts","../../src/table/head/table-head-expand.component.ts","../../src/table/head/table-head.component.ts","../../src/table/expanded-row-hover.directive.ts","../../src/table/cell/table-data.component.ts","../../src/table/cell/table-checkbox.component.ts","../../src/table/cell/table-radio.component.ts","../../src/table/cell/table-expand-button.component.ts","../../src/table/body/table-row.component.ts","../../src/table/body/table-expanded-row.component.ts","../../src/table/body/table-body.component.ts","../../src/table/table.component.ts","../../src/table/table-container.component.ts","../../src/table/header/table-header.component.ts","../../src/table/table-row.class.ts","../../src/table/table.module.ts","../../src/table/index.ts","../../src/table/carbon-components-angular-table.ts"],"sourcesContent":["import { TableModel } from \"../table-model.class\";\nimport {\n\tComponent,\n\tEventEmitter,\n\tInput,\n\tOutput\n} from \"@angular/core\";\nimport { I18n, Overridable } from \"carbon-components-angular/i18n\";\nimport { TableRowSize } from \"../table.types\";\n\n/**\n * The table toolbar is reserved for global table actions such as table settings, complex filter, export, or editing table data.\n *\n * ## Basic usage\n *\n * ```html\n * <cds-table-toolbar [model]=\"model\">\n *\t\t<cds-table-toolbar-actions>\n *\t\t\t<button cdsButton=\"primary\">\n *\t\t\t\tDelete\n *\t\t\t\t<svg cdsIcon=\"trash-can\" size=\"16\" class=\"cds--btn__icon\"></svg>\n *\t\t\t</button>\n *\t\t\t<button cdsButton=\"primary\">\n *\t\t\t\tSave\n *\t\t\t\t<svg cdsIcon=\"save\" size=\"16\" class=\"cds--btn__icon\"></svg>\n *\t\t\t</button>\n *\t\t\t<button cdsButton=\"primary\">\n *\t\t\t\tDownload\n *\t\t\t\t<svg cdsIcon=\"download\" size=\"16\" class=\"cds--btn__icon\"></svg>\n *\t\t\t</button>\n *\t\t</cds-table-toolbar-actions>\n *\t\t\t<cds-table-toolbar-content>\n *\t\t\t<cds-table-toolbar-search [expandable]=\"true\"></cds-table-toolbar-search>\n *\t\t\t<button cdsButton=\"toolbar-action\">\n *\t\t\t\t<svg cdsIcon=\"settings\" size=\"16\" class=\"cds--toolbar-action__icon\"></svg>\n *\t\t\t</button>\n *\t\t\t<button cdsButton=\"primary\" size=\"sm\">\n *\t\t\t\tPrimary Button\n *\t\t\t\t<svg cdsIcon=\"add\" size=\"20\" class=\"cds--btn__icon\"></svg>\n *\t\t\t</button>\n *\t\t</cds-table-toolbar-content>\n *\t</cds-table-toolbar>\n * ```\n *\n */\n@Component({\n\tselector: \"cds-table-toolbar, ibm-table-toolbar\",\n\ttemplate: `\n\t<section\n\t\tclass=\"cds--table-toolbar\"\n\t\t[ngClass]=\"{'cds--table-toolbar--sm' : size === 'sm'}\"\n\t\t[attr.aria-label]=\"actionBarLabel.subject | async\">\n\t\t<div\n\t\t\t*ngIf=\"model\"\n\t\t\tclass=\"cds--batch-actions\"\n\t\t\t[ngClass]=\"{\n\t\t\t\t'cds--batch-actions--active': selected\n\t\t\t}\">\n\t\t\t<div class=\"cds--batch-summary\">\n\t\t\t\t<p class=\"cds--batch-summary__para\" *ngIf=\"count as n\">\n\t\t\t\t\t<ng-container *ngIf=\"_batchTextLegacy.subject | async as legacyText; else batchTextBlock\">\n\t\t\t\t\t\t<span>{{n}}</span> {{legacyText}}\n\t\t\t\t\t</ng-container>\n\t\t\t\t\t<ng-template #batchTextBlock>\n\t\t\t\t\t\t<span *ngIf=\"n === 1\">{{_batchTextSingle.subject | async}}</span>\n\t\t\t\t\t\t<span *ngIf=\"n !== 1\">{{_batchTextMultiple.subject | i18nReplace: {count: n} | async}}</span>\n\t\t\t\t\t</ng-template>\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<div class=\"cds--action-list\">\n\t\t\t\t<ng-content select=\"cds-table-toolbar-actions,ibm-table-toolbar-actions\"></ng-content>\n\t\t\t\t<button\n\t\t\t\t\tcdsButton=\"primary\"\n\t\t\t\t\tclass=\"cds--batch-summary__cancel\"\n\t\t\t\t\t[tabindex]=\"selected ? 0 : -1\"\n\t\t\t\t\t(click)=\"onCancel()\">\n\t\t\t\t\t{{_cancelText.subject | async}}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t\t<ng-content></ng-content>\n\t</section>\n\t`\n})\nexport class TableToolbar {\n\t@Input() model: TableModel;\n\n\t@Input() set batchText (value: string | { SINGLE: string, MULTIPLE: string }) {\n\t\tif (typeof value === \"object\") {\n\t\t\tthis._batchTextSingle.override(value.SINGLE);\n\t\t\tthis._batchTextMultiple.override(value.MULTIPLE);\n\t\t} else {\n\t\t\t// For compatibility with old code\n\t\t\tthis._batchTextLegacy.override(value);\n\t\t}\n\t}\n\t@Input() set ariaLabel (value: { ACTION_BAR: string }) {\n\t\tthis.actionBarLabel.override(value.ACTION_BAR);\n\t}\n\t@Input() set cancelText(value: { CANCEL: string }) {\n\t\tthis._cancelText.override(value.CANCEL);\n\t}\n\t@Input() size: TableRowSize = \"md\";\n\n\tget cancelText(): { CANCEL: string } {\n\t\treturn { CANCEL: this._cancelText.value as string };\n\t}\n\n\t@Output() cancel = new EventEmitter();\n\n\tactionBarLabel: Overridable = this.i18n.getOverridable(\"TABLE_TOOLBAR.ACTION_BAR\");\n\t_cancelText: Overridable = this.i18n.getOverridable(\"TABLE_TOOLBAR.CANCEL\");\n\t_batchTextLegacy: Overridable = this.i18n.getOverridable(\"TABLE_TOOLBAR.BATCH_TEXT\");\n\t_batchTextSingle: Overridable = this.i18n.getOverridable(\"TABLE_TOOLBAR.BATCH_TEXT_SINGLE\");\n\t_batchTextMultiple: Overridable = this.i18n.getOverridable(\"TABLE_TOOLBAR.BATCH_TEXT_MULTIPLE\");\n\n\tconstructor(protected i18n: I18n) {}\n\n\tget count() {\n\t\treturn this.model.totalDataLength > 0 ? this.model.rowsSelected.reduce((previous, current) => previous + (current ? 1 : 0), 0) : 0;\n\t}\n\tget selected() {\n\t\treturn this.model.totalDataLength > 0 ? this.model.rowsSelected.some(item => item) : false;\n\t}\n\n\tonCancel() {\n\t\tthis.model.selectAll(false);\n\t\tthis.cancel.emit();\n\t}\n}\n","import { Component, HostBinding } from \"@angular/core\";\n\n@Component({\n\tselector: \"cds-table-toolbar-actions, ibm-table-toolbar-actions\",\n\ttemplate: `<ng-content></ng-content>`\n})\nexport class TableToolbarActions {}\n","import { Search } from \"carbon-components-angular/search\";\nimport {\n\tComponent,\n\tHostBinding,\n\tInput,\n\tOnInit,\n\tAfterViewInit\n} from \"@angular/core\";\nimport { NG_VALUE_ACCESSOR } from \"@angular/forms\";\n\n@Component({\n\tselector: \"cds-table-toolbar-search, ibm-table-toolbar-search\",\n\ttemplateUrl: \"../../search/search.component.html\",\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: TableToolbarSearch,\n\t\t\tmulti: true\n\t\t}\n\t]\n})\nexport class TableToolbarSearch extends Search implements AfterViewInit {\n\ttableSearch = true;\n\n\tsize: \"sm\" | \"md\" | \"lg\" = \"lg\";\n\n\t@HostBinding(\"class.cds--toolbar-content\") hostClass = true;\n\n\tngAfterViewInit() {\n\t\tsetTimeout(() => {\n\t\t\tif (this.value) {\n\t\t\t\tthis.openSearch();\n\t\t\t}\n\t\t});\n\t}\n}\n","<div\n\tclass=\"cds--search\"\n\t[ngClass]=\"{\n\t\t'cds--search--sm': size === 'sm',\n\t\t'cds--search--md': size === 'md',\n\t\t'cds--search--lg': size === 'lg',\n\t\t'cds--search--light': theme === 'light',\n\t\t'cds--skeleton': skeleton,\n\t\t'cds--search--expandable': expandable && !tableSearch,\n\t\t'cds--search--expanded': expandable && !tableSearch && active,\n\t\t'cds--toolbar-search': toolbar && !expandable,\n\t\t'cds--toolbar-search--active': toolbar && !expandable && active,\n\t\t'cds--toolbar-search-container-persistent': tableSearch && !expandable,\n\t\t'cds--toolbar-search-container-expandable': tableSearch && expandable,\n\t\t'cds--toolbar-search-container-active': tableSearch && expandable && active\n\t}\"\n\trole=\"search\"\n\t[attr.aria-label]=\"ariaLabel\"\n\t(click)=\"openSearch()\">\n\t<label class=\"cds--label\" [for]=\"id\">{{label}}</label>\n\n\t<div *ngIf=\"skeleton; else enableInput\" class=\"cds--search-input\"></div>\n\t<ng-template #enableInput>\n\t\t<input\n\t\t\t#input\n\t\t\tclass=\"cds--search-input\"\n\t\t\t[type]=\"tableSearch || !toolbar ? 'text' : 'search'\"\n\t\t\t[id]=\"id\"\n\t\t\t[value]=\"value\"\n\t\t\t[autocomplete]=\"autocomplete\"\n\t\t\t[placeholder]=\"placeholder\"\n\t\t\t[disabled]=\"disabled\"\n\t\t\t[required]=\"required\"\n\t\t\t(input)=\"onSearch($event.target.value)\"\n\t\t\t(keyup.enter)=\"onEnter()\"/>\n\t\t<button\n\t\t\t*ngIf=\"!tableSearch && toolbar\"\n\t\t\tclass=\"cds--toolbar-search__btn\"\n\t\t\t(click)=\"openSearch()\"\n\t\t\taria-label=\"Open search\">\n\t\t\t<svg cdsIcon=\"search\" size=\"16\" class=\"cds--search-magnifier-icon\"></svg>\n\t\t</button>\n\t\t<svg\n\t\t\tcdsIcon=\"search\"\n\t\t\t*ngIf=\"tableSearch || !toolbar\"\n\t\t\tclass=\"cds--search-magnifier-icon\"\n\t\t\tsize=\"16\">\n\t\t</svg>\n\t</ng-template>\n\n\t<button\n\t\t*ngIf=\"tableSearch || !toolbar\"\n\t\tclass=\"cds--search-close\"\n\t\t[ngClass]=\"{\n\t\t\t'cds--search-close--hidden': !value || value.length === 0\n\t\t}\"\n\t\t[title]=\"clearButtonTitle\"\n\t\t(click)=\"clearSearch()\">\n\t\t<span class=\"cds--visually-hidden\">{{ clearButtonTitle }}</span>\n\t\t<svg cdsIcon=\"close\" size=\"16\"></svg>\n\t</button>\n</div>\n","import { Component, HostBinding } from \"@angular/core\";\n\n@Component({\n\tselector: \"cds-table-toolbar-content, ibm-table-toolbar-content\",\n\ttemplate: `<ng-content></ng-content>`\n})\nexport class TableToolbarContent {\n\t@HostBinding(\"class.cds--toolbar-content\") class = true;\n}\n","import { Directive, HostBinding, Input } from \"@angular/core\";\n\n@Directive({\n\tselector: \"[cdsTableHeaderDescription], [ibmTableHeaderDescription]\"\n})\nexport class TableHeaderDescription {\n\tstatic counter = 0;\n\n\t@HostBinding(\"attr.id\") @Input() id = `table-description-${TableHeaderDescription.counter++}`;\n\t@HostBinding(\"class.cds--data-table-header__description\") descriptionClass = true;\n}\n","import {\n\tDirective,\n\tHostBinding,\n\tInput\n} from \"@angular/core\";\n\n@Directive({\n\tselector: \"[cdsTableHeaderTitle], [ibmTableHeaderTitle]\"\n})\nexport class TableHeaderTitle {\n\tstatic counter = 0;\n\n\t@HostBinding(\"attr.id\") @Input() id = `table-title-${TableHeaderTitle.counter++}`;\n\t@HostBinding(\"class.cds--data-table-header__title\") titleClass = true;\n}\n","import { TableItem } from \"./table-item.class\";\nimport { TemplateRef } from \"@angular/core\";\n\nexport type SortType = \"ASCENDING\" | \"DESCENDING\" | \"NONE\";\nexport class TableHeaderItem {\n\t/**\n\t * If true, sort is set to ascending, if false descending will be true.\n\t *\n\t */\n\tset ascending(asc: boolean) {\n\t\tthis.sortDirection = asc ? \"ASCENDING\" : \"DESCENDING\";\n\t}\n\tget ascending() {\n\t\treturn this.sortDirection === \"ASCENDING\";\n\t}\n\n\t/**\n\t * If true, sort is set to descending, if false ascending will be true.\n\t *\n\t */\n\tset descending(desc: boolean) {\n\t\tthis.sortDirection = desc ? \"DESCENDING\" : \"ASCENDING\";\n\t}\n\tget descending() {\n\t\treturn this.sortDirection === \"DESCENDING\";\n\t}\n\n\tget title() {\n\t\tif (this._title) {\n\t\t\treturn this._title;\n\t\t}\n\n\t\tif (!this.data) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tif (typeof this.data === \"string\") {\n\t\t\treturn this.data;\n\t\t}\n\n\t\tif (\n\t\t\tthis.data.toString &&\n\t\t\tthis.data.constructor !== ({}).constructor\n\t\t) {\n\t\t\treturn this.data.toString();\n\t\t}\n\n\t\t// data can’t be reasonably converted to an end user readable string\n\t\treturn \"\";\n\t}\n\n\tset title(title) {\n\t\tthis._title = title;\n\t}\n\t/**\n\t * Defines if column under this TableHeaderItem should be displayed.\n\t *\n\t */\n\tvisible = true;\n\n\t/**\n\t * Disables sorting by default.\n\t *\n\t */\n\tsorted = false;\n\n\t/**\n\t * Enables sorting on click by default.\n\t * If false then this column won't show a sorting arrow at all.\n\t *\n\t */\n\tsortable = true;\n\n\t/**\n\t * Number of applied filters.\n\t *\n\t * `filter()` should set it to appropriate number.\n\t *\n\t */\n\tfilterCount = 0;\n\n\t/**\n\t * The number of rows to span\n\t * **NOTE:** not supported by the default carbon table\n\t */\n\trowSpan = 1;\n\n\t/**\n\t * The number of columns to span\n\t */\n\tcolSpan = 1;\n\n\t/**\n\t * Attach a class to the column, both the header and column cells.\n\t *\n\t */\n\tclassName: string;\n\n\t/**\n\t * Style for the column, applied to every element in the column.\n\t *\n\t * ngStyle-like format\n\t *\n\t */\n\tstyle = {};\n\n\t/**\n\t * Data for the header item.\n\t */\n\tdata: any;\n\n\t/**\n\t * Data for the header item for general usage in the controller.\n\t * For example, which `field` is this column related to.\n\t */\n\tmetadata: any;\n\n\t/**\n\t * Used to display data in a desired way.\n\t *\n\t * If not provided, displays data as a simple string.\n\t *\n\t * Usage:\n\t *\n\t * In a component where you're using the table create a template like:\n\t *\n\t * ```html\n\t * <ng-template #customHeaderTemplate let-data=\"data\">\n\t * \t\t<i><a [routerLink]=\"data.link\">{{data.name}}</a></i>\n\t * </ng-template>\n\t * ```\n\t * where we assume your data contains `link` and `name`. `let-data=\"data\"` is\n\t * necessary for you to be able to access item's data in the template.\n\t *\n\t * Create `ViewChild` property with:\n\t *\n\t * ```typescript\n\t * (at)ViewChild(\"customHeaderTemplate\")\n\t * protected customHeaderTemplate: TemplateRef<any>;\n\t * ```\n\t *\n\t * Set the template to the header item, for example:\n\t *\n\t * ```typescript\n\t * this.model.header = [\n\t * \t\tnew TableHeaderItem({data: {name: \"Custom header\", link: \"/table\"}, template: this.customHeaderTemplate})\n\t * ];\n\t * ```\n\t */\n\ttemplate: TemplateRef<any>;\n\n\t/**\n\t * The label for the sort button\n\t */\n\tariaSortLabel: string;\n\n\t/**\n\t * A callback function to format the sort label. Will be heavily called.\n\t */\n\tformatSortLabel: (label: string, staticLabel?: string) => string;\n\n\t/**\n\t * Used as a template for popover filter.\n\t *\n\t * `let-popover=\"popover\"` will give you access to popover component and allow you to\n\t * manipulate it if needed.\n\t *\n\t * `let-filter=\"filter\"` will give you access to the \"filter\". The main takeaway is\n\t * store the data you need to `filter.data`. You will be able to access it as\n\t * `this.filterData.data` from your `filter()` function when you extend `TableHeaderItem`\n\t *\n\t * Example:\n\t * ```html\n\t * <ng-template #filter let-popover=\"popover\">\n\t * \t<cds-label class=\"first-label\">\n\t * \t\tValue\n\t * \t\t<input type=\"text\" [(ngModel)]=\"filter1\" class=\"input-field\">\n\t * \t</cds-label>\n\t * </ng-template>\n\t *\n\t * <ng-template #filterFooter let-popover=\"popover\" let-filter=\"data\">\n\t * \t<button class=\"btn--primary\" (click)=\"filter.data = filter1; popover.onClose()\">Apply</button>\n\t * \t<button class=\"btn--secondary\" (click)=\"popover.onClose()\">Cancel</button>\n\t * </ng-template>\n\t * ```\n\t *\n\t * Set the template with, for example:\n\t * ```typescript\n\t * new FilterableHeaderItem({\n\t * \tfilterTemplate: this.filter,\n\t *\tfilterFooter: this.filterFooter\n\t * })\n\t * ```\n\t *\n\t * ```typescript\n\t * class FilterableHeaderItem extends TableHeaderItem {\n\t * \t// custom filter function\n\t * \tfilter(item: TableItem): boolean {\n\t * \t\tif (typeof item.data === \"string\" && item.data.indexOf(this.filterData.data) >= 0) {\n\t * \t\t\tthis.filterCount = 1;\n\t * \t\t\treturn false;\n\t * \t\t}\n\t * \t\tthis.filterCount = 0;\n\t * \t\treturn true;\n\t * \t}\n\t * }\n\t * ```\n\t */\n\tfilterTemplate: TemplateRef<any>;\n\n\t/**\n\t * Used along with `filterTemplate` to construct the filter popover\n\t */\n\tfilterFooter: TemplateRef<any>;\n\n\t/**\n\t * This is where you store your data when applying filter.\n\t *\n\t * It is the actual object you have access to with `let-filter=\"data\"` in your template.\n\t *\n\t * Make sure to store data in `filter.data` in your template, and you will have it\n\t * available in `filterData.data` in your extension of `TableHeaderItem`.\n\t *\n\t * Because angular and object references.\n\t */\n\tfilterData: any;\n\n\tsortDirection: SortType = \"NONE\";\n\n\n\n\tprivate _title: string;\n\n\t/**\n\t * Creates an instance of TableHeaderItem.\n\t */\n\tconstructor(rawData?: any) {\n\t\t// defaults so we dont leave things empty\n\t\tconst defaults = {\n\t\t\tdata: \"\",\n\t\t\tvisible: this.visible,\n\t\t\tstyle: this.style,\n\t\t\tfilterCount: this.filterCount,\n\t\t\tfilterData: {data: \"\"}\n\t\t};\n\t\t// fill our object with provided props, and fallback to defaults\n\t\tconst data = Object.assign({}, defaults, rawData);\n\t\tfor (let property of Object.getOwnPropertyNames(data)) {\n\t\t\tif (data.hasOwnProperty(property)) {\n\t\t\t\tthis[property] = data[property];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Used for sorting rows of the table.\n\t *\n\t * Override to enable different sorting.\n\t *\n\t * < 0 if `one` should go before `two`\n\t * > 0 if `one` should go after `two`\n\t * 0 if it doesn't matter (they are the same)\n\t */\n\tcompare(one: TableItem, two: TableItem): number {\n\t\tif (!one || !two) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (typeof one.data === \"string\") {\n\t\t\treturn one.data.localeCompare(two.data);\n\t\t}\n\n\t\tif (one.data < two.data) {\n\t\t\treturn -1;\n\t\t} else if (one.data > two.data) {\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Used to filter rows in the table.\n\t *\n\t * Override to make a custom filter.\n\t *\n\t * Even though there is just one filter function, there can be multiple filters.\n\t * When implementing filter, set `filterCount` before returning.\n\t *\n\t * `true` to hide the row\n\t * `false` to show the row\n\t */\n\tfilter(item: TableItem): boolean {\n\t\tthis.filterCount = 0;\n\t\treturn false;\n\t}\n}\n","import {\n\tTemplateRef\n} from \"@angular/core\";\n\nexport class TableItem {\n\t/**\n\t * Data for the table item.\n\t */\n\tdata: any;\n\n\t/**\n\t * Data for the expanded part of the row.\n\t *\n\t * You only need to set it for the first item in the row.\n\t *\n\t * See `expandAsTable` documentation if you need to the table to expand to additional\n\t * table rows.\n\t */\n\texpandedData: any;\n\n\t/**\n\t * Used to display data in a desired way.\n\t *\n\t * If not provided, displays data as a simple string.\n\t *\n\t * Usage:\n\t *\n\t * In a component where you're using the table create a template like:\n\t *\n\t * ```html\n\t * <ng-template #customItemTemplate let-data=\"data\">\n\t * \t<i><a [routerLink]=\"data.link\">{{data.name}}</a></i>\n\t * </ng-template>\n\t * ```\n\t * where we assume your data contains `link` and `name`. `let-data=\"data\"` is\n\t * necessary for you to be able to access item's data in the template.\n\t *\n\t * Create `ViewChild` property with:\n\t *\n\t * ```typescript\n\t * (at)ViewChild(\"customItemTemplate\")\n\t * protected customItemTemplate: TemplateRef<any>;\n\t * ```\n\t *\n\t * Set the template to the table item, for example:\n\t *\n\t * ```typescript\n\t * this.model.data = [\n\t * \t[new TableItem({data: {name: \"Custom item\", link: \"/table\"}, template: this.customItemTemplate})]\n\t * ];\n\t * ```\n\t */\n\ttemplate: TemplateRef<any>;\n\n\t/**\n\t * Template for rendering `expandedData`\n\t *\n\t * You only need to set it for the first item in the row.\n\t *\n\t */\n\texpandedTemplate: TemplateRef<any>;\n\n\t/**\n\t * Setting this to `true` makes table interpret `expandedData` as additional rows to insert in place\n\t * for expanded data. `expandedTemplate` is then ignored.\n\t *\n\t * You can apply the template for individual cells as usual.\n\t *\n\t * Example model data:\n\t *\n\t * ```typescript\n\t * this.model.data = [\n\t * \t[new TableItem({ data: \"Name 4\" }), new TableItem({ data: \"twer\" })], // regular row\n\t * \t[\n\t * \t\tnew TableItem({\n\t * \t\t\tdata: \"Name 3.1\",\n\t * \t\t\t// `expandedData` mimics the format of the rest of the table\n\t * \t\t\texpandedData: [\n\t * \t\t\t\t[\n\t * \t\t\t\t\tnew TableItem({ data: \"More names\", expandedData: \"No template\" }),\n\t * \t\t\t\t\tnew TableItem({ data: { name: \"Morey\", link: \"#\" }, template: this.customTableItemTemplate })\n\t * \t\t\t\t],\n\t * \t\t\t\t[\n\t * \t\t\t\t\tnew TableItem({ data: \"Core names\", expandedData: \"No template\" }),\n\t * \t\t\t\t\tnew TableItem({ data: { name: \"Corey\", link: \"#\" }, template: this.customTableItemTemplate })\n\t * \t\t\t\t]\n\t * \t\t\t],\n\t * \t\t\t// `expandAsTable` tells the table to interpret `expandedData` as table data\n\t * \t\t\texpandAsTable: true\n\t * \t\t}),\n\t * \t\tnew TableItem({ data: \"swer\" })\n\t * \t],\n\t * \t[new TableItem({ data: \"Name 7\" }), new TableItem({data: \"twer\"})] // regular row\n\t * ];\n\t * ```\n\t */\n\texpandAsTable: false;\n\n\t/**\n\t * The number of rows to span\n\t */\n\trowSpan = 1;\n\n\t/**\n\t * The number of columns to span\n\t */\n\tcolSpan = 1;\n\n\tget title() {\n\t\tif (typeof this._title === \"string\") {\n\t\t\treturn this._title;\n\t\t}\n\n\t\tif (!this.data) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tif (typeof this.data === \"string\") {\n\t\t\treturn this.data;\n\t\t}\n\n\t\tif (\n\t\t\tthis.data.toString &&\n\t\t\tthis.data.constructor !== ({}).constructor\n\t\t) {\n\t\t\treturn this.data.toString();\n\t\t}\n\n\t\t// data can’t be reasonably converted to an end user readable string\n\t\treturn \"\";\n\t}\n\n\tset title(title) {\n\t\tthis._title = title;\n\t}\n\n\tprivate _title: string;\n\n\t/**\n\t * Creates an instance of TableItem.\n\t */\n\tconstructor(rawData?: any) {\n\t\t// defaults so we dont leave things empty\n\t\tconst defaults = {\n\t\t\tdata: \"\"\n\t\t};\n\t\t// fill our object with provided props, and fallback to defaults\n\t\tconst data = Object.assign({}, defaults, rawData);\n\t\tfor (const property of Object.getOwnPropertyNames(data)) {\n\t\t\tif (data.hasOwnProperty(property)) {\n\t\t\t\tthis[property] = data[property];\n\t\t\t}\n\t\t}\n\t}\n}\n","import { EventEmitter } from \"@angular/core\";\n\nimport { PaginationModel } from \"carbon-components-angular/pagination\";\nimport { TableHeaderItem } from \"./table-header-item.class\";\nimport { TableItem } from \"./table-item.class\";\nimport { TableRow } from \"./table-row.class\";\nimport { Subject } from \"rxjs\";\n\nexport type HeaderType = number | \"select\" | \"expand\";\n\n/**\n * TableModel represents a data model for two-dimensional data. It's used for all things table\n * (table component, table toolbar, pagination, etc)\n *\n * TableModel manages its internal data integrity very well if you use the provided helper\n * functions for modifying rows and columns and assigning header and data in that order.\n *\n * It also provides direct access to the data so you can read and modify it.\n * If you change the structure of the data (by directly pushing into the arrays or otherwise),\n * keep in mind to keep the data structure intact.\n *\n * Header length and length of every line in the data should be equal.\n *\n * If they are not consistent, unexpected things will happen.\n *\n * Use the provided functions when in doubt.\n */\nexport class TableModel implements PaginationModel {\n\t/**\n\t * The number of models instantiated, used for (among other things) unique id generation\n\t */\n\tprotected static COUNT = 0;\n\n\t/**\n\t * Sets data of the table.\n\t *\n\t * Make sure all rows are the same length to keep the column count accurate.\n\t */\n\tset data(newData: TableItem[][]) {\n\t\tif (!newData || (Array.isArray(newData) && newData.length === 0)) {\n\t\t\tnewData = [[]];\n\t\t}\n\n\t\tthis._data = newData;\n\n\t\t// init rowsSelected\n\t\tthis.rowsSelected = new Array<boolean>(this._data.length).fill(false);\n\t\tthis.rowsExpanded = new Array<boolean>(this._data.length).fill(false);\n\t\t// init rows indices\n\t\tthis.rowsIndices = [...Array(this._data.length).keys()];\n\t\t// init rowsContext\n\t\tthis.rowsContext = new Array<string>(this._data.length);\n\n\t\t// init rowsClass\n\t\tthis.rowsClass = new Array<string>(this._data.length);\n\n\t\t// only create a fresh header if necessary (header doesn't exist or differs in length)\n\t\tif (this.header == null || (this.header.length !== this._data[0].length && this._data[0].length > 0)) {\n\t\t\tlet header = new Array<TableHeaderItem>();\n\t\t\tfor (let i = 0; i < this._data[0].length; i++) {\n\t\t\t\theader.push(new TableHeaderItem());\n\t\t\t}\n\t\t\tthis.header = header;\n\t\t}\n\n\t\tthis.dataChange.emit();\n\t}\n\n\tdataChange = new EventEmitter();\n\trowsSelectedChange = new EventEmitter<number>();\n\trowsExpandedChange = new EventEmitter<number>();\n\t/**\n\t * Gets emitted when `selectAll` is called. Emits false if all rows are deselected and true if\n\t * all rows are selected.\n\t */\n\tselectAllChange = new Subject<boolean>();\n\n\t/**\n\t * Gets the full data.\n\t *\n\t * You can use it to alter individual `TableItem`s but if you need to change\n\t * table structure, use `addRow()` and/or `addColumn()`\n\t */\n\tget data() {\n\t\treturn this._data;\n\t}\n\n\t/**\n\t * Contains information about selection state of rows in the table.\n\t */\n\trowsSelected: boolean[] = [];\n\n\t/**\n\t * Contains information about expanded state of rows in the table.\n\t */\n\trowsExpanded: boolean[] = [];\n\n\t/**\n\t * Contains information about initial index of rows in the table\n\t */\n\trowsIndices: number[] = [];\n\n\t/**\n\t * Contains information about the context of the row.\n\t *\n\t * It affects styling of the row to reflect the context.\n\t *\n\t * string can be one of `\"success\" | \"warning\" | \"info\" | \"error\" | \"\"` and it's\n\t * empty or undefined by default\n\t */\n\trowsContext: string[] = [];\n\n\t/**\n\t * Contains class name(s) of the row.\n\t *\n\t * It affects styling of the row to reflect the appended class name(s).\n\t *\n\t * It's empty or undefined by default\n\t */\n\trowsClass: string[] = [];\n\n\t/**\n\t * Contains information about the header cells of the table.\n\t */\n\theader: TableHeaderItem[] = [];\n\n\t/**\n\t * Tracks the current page.\n\t */\n\tcurrentPage = 1;\n\n\t/**\n\t * Length of page.\n\t */\n\tpageLength = 10;\n\n\t/**\n\t * Set to true when there is no more data to load in the table\n\t */\n\tisEnd = false;\n\n\t/**\n\t * Set to true when lazy loading to show loading indicator\n\t */\n\tisLoading = false;\n\n\t/**\n\t * Absolute total number of rows of the table.\n\t */\n\tprotected _totalDataLength: number;\n\n\t/**\n\t * Manually set data length in case the data in the table doesn't\n\t * correctly reflect all the data that table is to display.\n\t *\n\t * Example: if you have multiple pages of data that table will display\n\t * but you're loading one at a time.\n\t *\n\t * Set to `null` to reset to default behavior.\n\t */\n\tset totalDataLength(length: number) {\n\t\t// if this function is called without a parameter we need to set to null to avoid having undefined != null\n\t\tthis._totalDataLength = isNaN(length) ? null : length;\n\t}\n\n\t/**\n\t * Total length of data that table has access to, or the amount manually set\n\t */\n\tget totalDataLength() {\n\t\t// if manually set data length\n\t\tif (this._totalDataLength !== null && this._totalDataLength >= 0) {\n\t\t\treturn this._totalDataLength;\n\t\t}\n\n\t\t// if empty dataset\n\t\tif (this.data && this.data.length === 1 && this.data[0].length === 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn this.data.length;\n\t}\n\n\t/**\n\t * Used in `data`\n\t */\n\tprotected _data: TableItem[][] = [[]];\n\n\t/**\n\t * The number of models instantiated, this is to make sure each table has a different\n\t * model count for unique id generation.\n\t */\n\tprotected tableModelCount = 0;\n\n\tconstructor() {\n\t\tthis.tableModelCount = TableModel.COUNT++;\n\t}\n\n\t/**\n\t * Returns an id for the given column\n\t *\n\t * @param column the column to generate an id for\n\t * @param row the row of the header to generate an id for\n\t */\n\tgetId(column: HeaderType, row = 0): string {\n\t\treturn `table-header-${row}-${column}-${this.tableModelCount}`;\n\t}\n\n\t/**\n\t * Returns the id of the header. Used to link the cells with headers (or headers with headers)\n\t *\n\t * @param column the column to start getting headers for\n\t * @param colSpan the number of columns to get headers for (defaults to 1)\n\t */\n\tgetHeaderId(column: HeaderType, colSpan = 1): string {\n\t\tif (column === \"select\" || column === \"expand\") {\n\t\t\treturn this.getId(column);\n\t\t}\n\n\t\tlet ids = [];\n\t\tfor (let i = column; i >= 0; i--) {\n\t\t\tif (this.header[i]) {\n\t\t\t\tfor (let j = 0; j < colSpan; j++) {\n\t\t\t\t\tids.push(this.getId(i + j));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn ids.join(\" \");\n\t}\n\n\t/**\n\t * Finds closest header by trying the `column` and then working its way to the left\n\t *\n\t * @param column the target column\n\t */\n\tgetHeader(column: number): TableHeaderItem {\n\t\tif (!this.header) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfor (let i = column; i >= 0; i--) {\n\t\t\tconst headerCell = this.header[i];\n\t\t\tif (headerCell) {\n\t\t\t\treturn headerCell;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns how many rows is currently selected\n\t */\n\tselectedRowsCount(): number {\n\t\tlet count = 0;\n\t\tif (this.rowsSelected) {\n\t\t\tthis.rowsSelected.forEach(rowSelected => {\n\t\t\t\tif (rowSelected) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn count;\n\t}\n\n\t/**\n\t * Returns how many rows is currently expanded\n\t */\n\texpandedRowsCount(): number {\n\t\tlet count = 0;\n\t\tif (this.rowsExpanded) {\n\t\t\tthis.rowsExpanded.forEach(rowExpanded => {\n\t\t\t\tif (rowExpanded) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn count;\n\t}\n\n\t/**\n\t * Returns `index`th row of the table.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param index\n\t */\n\trow(index: number): TableItem[] {\n\t\treturn this.data[this.realRowIndex(index)];\n\t}\n\n\t/**\n\t * Adds a row to the `index`th row or appends to table if index not provided.\n\t *\n\t * If row is shorter than other rows or not provided, it will be padded with\n\t * empty `TableItem` elements.\n\t *\n\t * If row is longer than other rows, others will be extended to match so no data is lost.\n\t *\n\t * If called on an empty table with no parameters, it creates a 1x1 table.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param [row]\n\t * @param [index]\n\t */\n\taddRow(row?: TableItem[], index?: number) {\n\t\t// if table empty create table with row\n\t\tif (!this.data || this.data.length === 0 || this.data[0].length === 0) {\n\t\t\tlet newData = new Array<Array<TableItem>>();\n\t\t\tnewData.push(row ? row : [new TableItem()]); // row or one empty one column row\n\t\t\tthis.data = newData;\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet realRow = row;\n\t\tconst columnCount = this.data[0].length;\n\n\t\tif (row == null) {\n\t\t\trealRow = new Array<TableItem>();\n\t\t\tfor (let i = 0; i < columnCount; i++) {\n\t\t\t\trealRow.push(new TableItem());\n\t\t\t}\n\t\t}\n\n\t\tif (realRow.length < columnCount) {\n\t\t\t// extend the length of realRow\n\t\t\tconst difference = columnCount - realRow.length;\n\t\t\tfor (let i = 0; i < difference; i++) {\n\t\t\t\trealRow.push(new TableItem());\n\t\t\t}\n\t\t} else if (realRow.length > columnCount) {\n\t\t\t// extend the length of header\n\t\t\tlet difference = realRow.length - this.header.length;\n\t\t\tfor (let j = 0; j < difference; j++) {\n\t\t\t\tthis.header.push(new TableHeaderItem());\n\t\t\t}\n\t\t\t// extend the length of every other row\n\t\t\tfor (let i = 0; i < this.data.length; i++) {\n\t\t\t\tlet currentRow = this.data[i];\n\t\t\t\tdifference = realRow.length - currentRow.length;\n\t\t\t\tfor (let j = 0; j < difference; j++) {\n\t\t\t\t\tcurrentRow.push(new TableItem());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (index == null) {\n\t\t\tthis.data.push(realRow);\n\n\t\t\t// update rowsSelected property for length\n\t\t\tthis.rowsSelected.push(false);\n\n\t\t\t// update rowsExpanded property for length\n\t\t\tthis.rowsExpanded.push(false);\n\n\t\t\t// update rowsContext property for length\n\t\t\tthis.rowsContext.push(undefined);\n\n\t\t\t// update rowsClass property for length\n\t\t\tthis.rowsClass.push(undefined);\n\n\t\t\t// update rowsIndices property for length\n\t\t\tthis.rowsIndices.push(this.data.length - 1);\n\t\t} else {\n\t\t\tconst ri = this.realRowIndex(index);\n\t\t\tthis.data.splice(ri, 0, realRow);\n\n\t\t\t// update rowsSelected property for length\n\t\t\tthis.rowsSelected.splice(ri, 0, false);\n\n\t\t\t// update rowsExpanded property for length\n\t\t\tthis.rowsExpanded.splice(ri, 0, false);\n\n\t\t\t// update rowsContext property for length\n\t\t\tthis.rowsContext.splice(ri, 0, undefined);\n\n\t\t\t// update rowsClass property for length\n\t\t\tthis.rowsClass.splice(ri, 0, undefined);\n\n\t\t\t// update rowsIndices property for length\n\t\t\tthis.rowsIndices.splice(ri, 0, this.data.length - 1);\n\t\t}\n\n\t\tthis.dataChange.emit();\n\t}\n\n\t/**\n\t * Deletes `index`th row.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param index\n\t */\n\tdeleteRow(index: number) {\n\t\tconst rri = this.realRowIndex(index);\n\t\tthis.data.splice(rri, 1);\n\t\tthis.rowsSelected.splice(rri, 1);\n\t\tthis.rowsExpanded.splice(rri, 1);\n\t\tthis.rowsContext.splice(rri, 1);\n\t\tthis.rowsClass.splice(rri, 1);\n\n\t\tconst rowIndex = this.rowsIndices[rri];\n\t\tthis.rowsIndices.splice(rri, 1);\n\t\tthis.rowsIndices = this.rowsIndices.map((value) => (value > rowIndex) ? --value : value);\n\n\t\tthis.dataChange.emit();\n\t}\n\n\thasExpandableRows() {\n\t\treturn this.data.some(data => data.some(d => d && d.expandedData)); // checking for some in 2D array\n\t}\n\n\tisRowExpandable(index: number) {\n\t\treturn this.data[index].some(d => d && d.expandedData);\n\t}\n\n\tisRowExpanded(index: number) {\n\t\treturn this.rowsExpanded[index];\n\t}\n\n\tgetRowContext(index: number) {\n\t\treturn this.rowsContext[index];\n\t}\n\n\t/**\n\t * Returns `index`th column of the table.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param index\n\t */\n\tcolumn(index: number): TableItem[] {\n\t\tlet column = new Array<TableItem>();\n\t\tconst ri = this.realColumnIndex(index);\n\t\tconst rc = this.data.length;\n\n\t\tfor (let i = 0; i < rc; i++) {\n\t\t\tconst row = this.data[i];\n\t\t\tcolumn.push(row[ri]);\n\t\t}\n\n\t\treturn column;\n\t}\n\n\t/**\n\t * Adds a column to the `index`th column or appends to table if index not provided.\n\t *\n\t * If column is shorter than other columns or not provided, it will be padded with\n\t * empty `TableItem` elements.\n\t *\n\t * If column is longer than other columns, others will be extended to match so no data is lost.\n\t *\n\t * If called on an empty table with no parameters, it creates a 1x1 table.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param [column]\n\t * @param [index]\n\t */\n\taddColumn(column?: TableItem[], index?: number) {\n\t\t// if table empty create table with row\n\t\tif (!this.data || this.data.length === 0 || this.data[0].length === 0) {\n\t\t\tlet newData = new Array<Array<TableItem>>();\n\t\t\tif (column == null) {\n\t\t\t\tnewData.push([new TableItem()]);\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < column.length; i++) {\n\t\t\t\t\tlet item = column[i];\n\t\t\t\t\tnewData.push([item]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.data = newData;\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet rc = this.data.length; // row count\n\t\tlet ci = this.realColumnIndex(index);\n\n\t\t// append missing rows\n\t\tfor (let i = 0; column != null && i < column.length - rc; i++) {\n\t\t\tthis.addRow();\n\t\t}\n\t\trc = this.data.length;\n\t\tif (index == null) {\n\t\t\t// append to end\n\t\t\tfor (let i = 0; i < rc; i++) {\n\t\t\t\tlet row = this.data[i];\n\t\t\t\trow.push(column == null || column[i] == null ? new TableItem() : column[i]);\n\t\t\t}\n\t\t\t// update header if not already set by user\n\t\t\tif (this.header.length < this.data[0].length) {\n\t\t\t\tthis.header.push(new TableHeaderItem());\n\t\t\t}\n\t\t} else {\n\t\t\tif (index >= this.data[0].length) {\n\t\t\t\t// if trying to append\n\t\t\t\tci++;\n\t\t\t}\n\t\t\t// insert\n\t\t\tfor (let i = 0; i < rc; i++) {\n\t\t\t\tlet row = this.data[i];\n\t\t\t\trow.splice(ci, 0, column == null || column[i] == null ? new TableItem() : column[i]);\n\t\t\t}\n\t\t\t// update header if not already set by user\n\t\t\tif (this.header.length < this.data[0].length) {\n\t\t\t\tthis.header.splice(ci, 0, new TableHeaderItem());\n\t\t\t}\n\t\t}\n\n\t\tthis.dataChange.emit();\n\t}\n\n\t/**\n\t * Deletes `index`th column.\n\t *\n\t * Negative index starts from the end. -1 being the last element.\n\t *\n\t * @param index\n\t */\n\tdeleteColumn(index: number) {\n\t\tconst rci = this.realColumnIndex(index);\n\t\tconst rowCount = this.data.length;\n\t\tfor (let i = 0; i < rowCount; i++) {\n\t\t\tthis.data[i].splice(rci, 1);\n\t\t}\n\t\t// update header if not already set by user\n\t\tif (this.header.length > this.data[0].length) {\n\t\t\tthis.header.splice(rci, 1);\n\t\t}\n\n\t\tthis.dataChange.emit();\n\t}\n\n\tmoveColumn(indexFrom: number, indexTo: number) {\n\t\tconst headerFrom = this.header[indexFrom];\n\n\t\tthis.addColumn(this.column(indexFrom), indexTo);\n\t\tthis.deleteColumn(indexFrom + (indexTo < indexFrom ? 1 : 0));\n\n\t\tthis.header[indexTo + (indexTo > indexFrom ? -1 : 0)] = headerFrom;\n\t}\n\n\t/**\n\t * cycle through the three sort states\n\t * @param index\n\t */\n\tcycleSortState(index: number) {\n\t\t// no sort provided so do the simple sort\n\t\tswitch (this.header[index].sortDirection) {\n\t\t\tcase \"ASCENDING\":\n\t\t\t\tthis.header[index].sortDirection = \"DESCENDING\";\n\t\t\t\tbreak;\n\t\t\tcase \"DESCENDING\":\n\t\t\t\tthis.header[index].sortDirection = \"NONE\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthis.header[index].sortDirection = \"ASCENDING\";\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Sorts the data currently present in the model based on `compare()`\n\t *\n\t * Direction is set by `ascending` and `descending` properties of `TableHeaderItem`\n\t * in `index`th column.\n\t *\n\t * @param index The column based on which it's sorting\n\t */\n\tsort(index: number) {\n\t\tthis.pushRowStateToModelData();\n\t\tconst headerSorted = this.header[index].sorted;\n\t\t// We only allow sorting by a single column, so reset sort state for all columns before specifying new sort state\n\t\tthis.header.forEach(column => column.sorted = false);\n\t\tif (this.header[index].sortDirection === \"NONE\" && headerSorted) {\n\t\t\t// Restore initial order of rows\n\t\t\tconst oldData = this._data;\n\t\t\tthis._data = [];\n\t\t\tfor (let i = 0; i < this.rowsIndices.length; i++) {\n\t\t\t\tconst ri = this.rowsIndices[i];\n\t\t\t\tthis._data[ri] = oldData[i];\n\t\t\t}\n\t\t} else {\n\t\t\tconst descending = this.header[index].sortDirection === \"DESCENDING\" ? -1 : 1;\n\t\t\tthis.data.sort((a, b) => {\n\t\t\t\treturn descending * this.header[index].compare(a[index], b[index]);\n\t\t\t});\n\t\t\tthis.header[index].sorted = true;\n\t\t}\n\t\tthis.popRowStateFromModelData();\n\t}\n\n\t/**\n\t * Appends `rowsSelected` and `rowsExpanded` info to model data.\n\t *\n\t * When sorting rows, do this first so information about row selection\n\t * gets sorted with the other row info.\n\t *\n\t * Call `popRowSelectionFromModelData()` after sorting to make everything\n\t * right with the world again.\n\t */\n\tpushRowStateToModelData() {\n\t\tfor (let i = 0; i < this.data.length; i++) {\n\t\t\tconst rowSelectedMark = new TableItem();\n\t\t\trowSelectedMark.data = this.rowsSelected[i];\n\t\t\tthis.data[i].push(rowSelectedMark);\n\n\t\t\tconst rowExpandedMark = new TableItem();\n\t\t\trowExpandedMark.data = this.rowsExpanded[i];\n\t\t\tthis.data[i].push(rowExpandedMark);\n\n\t\t\tconst rowContext = new TableItem();\n\t\t\trowContext.data = this.rowsContext[i];\n\t\t\tthis.data[i].push(rowContext);\n\n\t\t\tconst rowClass = new TableItem();\n\t\t\trowClass.data = this.rowsClass[i];\n\t\t\tthis.data[i].push(rowClass);\n\n\t\t\tconst rowIndex = new TableItem();\n\t\t\trowIndex.data = this.rowsIndices[i];\n\t\t\tthis.data[i].push(rowIndex);\n\t\t}\n\t}\n\n\t/**\n\t * Restores `rowsSelected` from data pushed by `pushRowSelectionToModelData()`\n\t *\n\t * Call after sorting data (if you previously pushed to maintain selection order)\n\t * to make everything right with the world again.\n\t */\n\tpopRowStateFromModelData() {\n\t\tfor (let i = 0; i < this.data.length; i++) {\n\t\t\tthis.rowsIndices[i] = this.data[i].pop().data;\n\t\t\tthis.rowsClass[i] = this.data[i].pop().data;\n\t\t\tthis.rowsContext[i] = this.data[i].pop().data;\n\t\t\tthis.rowsExpanded[i] = !!this.data[i].pop().data;\n\t\t\tthis.rowsSelected[i] = !!this.data[i].pop().data;\n\t\t}\n\t}\n\n\t/**\n\t * Checks if row is filtered out.\n\t *\n\t * @param index\n\t * @returns true if any of the filters in header filters out the `index`th row\n\t */\n\tisRowFiltered(index: number): boolean {\n\t\tconst realIndex = this.realRowIndex(index);\n\t\treturn this.header.some((item, i) => item && item.filter(this.row(realIndex)[i]));\n\t}\n\n\t/**\n\t * Select/deselect `index`th row based on value\n\t *\n\t * @param index index of the row to select\n\t * @param value state to set the row to. Defaults to `true`\n\t */\n\tselectRow(index: number, value = true) {\n\t\tif (this.isRowDisabled(index)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.rowsSelected[index] = value;\n\t\tthis.rowsSelectedChange.emit(index);\n\t}\n\n\t/**\n\t * Selects or deselects all rows in the model\n\t *\n\t * @param value state to set all rows to. Defaults to `true`\n\t */\n\tselectAll(value = true) {\n\t\tif (this.data.length >= 1 && this.data[0].length >= 1) {\n\t\t\tfor (let i = 0; i < this.rowsSelected.length; i++) {\n\t\t\t\tthis.selectRow(i, value);\n\t\t\t}\n\t\t}\n\t\tthis.selectAllChange.next(value);\n\t}\n\n\tisRowSelected(index: number) {\n\t\treturn this.rowsSelected[index];\n\t}\n\n\t/**\n\t * Checks if row is disabled or not.\n\t */\n\tisRowDisabled(index: number) {\n\t\tconst row = this.data[index] as TableRow;\n\t\treturn !!row.disabled;\n\t}\n\n\t/**\n\t * Expands/Collapses `index`th row based on value\n\t *\n\t * @param index index of the row to expand or collapse\n\t * @param value expanded state of the row. `true` is expanded and `false` is collapsed\n\t */\n\texpandRow(index: number, value = true) {\n\t\tthis.rowsExpanded[index] = value;\n\t\tthis.rowsExpandedChange.emit(index);\n\t}\n\n\t/**\n\t * Gets the true index of a row based on it's relative position.\n\t * Like in Python, positive numbers start from the top and\n\t * negative numbers start from the bottom.\n\t *\n\t * @param index\n\t */\n\tprotected realRowIndex(index: number): number {\n\t\treturn this.realIndex(index, this.data.length);\n\t}\n\n\t/**\n\t * Gets the true index of a column based on it's relative position.\n\t * Like in Python, positive numbers start from the top and\n\t * negative numbers start from the bottom.\n\t *\n\t * @param index\n\t */\n\tprotected realColumnIndex(index: number): number {\n\t\treturn this.realIndex(index, this.data[0].length);\n\t}\n\n\t/**\n\t * Generic function to calculate the real index of something.\n\t * Used by `realRowIndex()` and `realColumnIndex()`\n\t *\n\t * @param index\n\t * @param length\n\t */\n\tprotected realIndex(index: number, length: number): number {\n\t\tif (index == null) {\n\t\t\treturn length - 1;\n\t\t} else if (index >= 0) {\n\t\t\treturn index >= length ? length - 1 : index;\n\t\t} else {\n\t\t\treturn -index >= length ? 0 : length + index;\n\t\t}\n\t}\n}\n","import {\n\tBehaviorSubject,\n\tObservable,\n\tcombineLatest\n} from \"rxjs\";\nimport { map } from \"rxjs/operators\";\nimport { TableAdapter } from \"./table-adapter.class\";\nimport { tabbableSelectorIgnoreTabIndex, getFocusElementList } from \"carbon-components-angular/common\";\n\n/**\n * The current and previous position in the grid.\n *\n * `current` and `previous` are tuples that follow the `[row, column]` convention.\n */\nexport interface DataGridPosition {\n\tcurrent: [number, number];\n\tprevious: [number, number];\n}\n\n/**\n * `DataGridInteractionModel` provides centralized control over arbitrary 2d grids, following the w3 specs.\n *\n * Refs:\n * - https://www.w3.org/TR/wai-aria-practices/examples/grid/dataGrids.html\n * - https://www.w3.org/TR/wai-aria-practices/#grid\n *\n * Example usage (taken from `table.component`):\n```typescript\n// a standard HTML table\nconst table = this.elementRef.nativeElement.querySelector(\"table\") as HTMLTableElement;\n\n// `TableDomAdapter` implements `TableAdapter` and provides a consistent interface to query rows and columns in a table\nconst tableAdapter = new TableDomAdapter(table);\n\n// the keydown events that we'll use for keyboard navigation of the table\nconst keydownEventStream = fromEvent<KeyboardEvent>(table, \"keydown\");\n\n// the click events we'll use to ensure focus is updated correctly on click\nconst clickEventStream = fromEvent<MouseEvent>(table, \"click\");\n\n// the `DataGridInteractionModel` instance!\nthis.interactionModel = new DataGridInteractionModel(keydownEventStream, clickEventStream, tableAdapter);\n\n// subscribe to the combined position updates\nthis.interactionModel.position.subscribe(event => {\n\tconst [currentRow, currentColumn] = event.current;\n\tconst [previousRow, previousColumn] = event.previous;\n\n\t// query the TableAdapter for the cell at the current row and column ...\n\tconst currentElement = tableAdapter.getCell(currentRow, currentColumn);\n\t// ... and make it focusable it\n\tTable.setTabIndex(currentElement, 0);\n\n\t// if the model has just initialized don't focus or reset anything\n\tif (previousRow === -1 || previousColumn === -1) { return; }\n\n\t// query the TableAdapter for the cell at the previous row and column ...\n\tconst previousElement = tableAdapter.getCell(previousRow, previousColumn);\n\t// ... and make it unfocusable (now there is only a single focusable cell)\n\tTable.setTabIndex(previousElement, -1);\n\n\t// finally, focus the current cell (skipped during initilzation)\n\tTable.focus(currentElement);\n});\n```\n */\nexport class DataGridInteractionModel {\n\t/**\n\t * An Observable that provides an aggregated view of the `rowIndex` and `columnIndex` Observables\n\t */\n\treadonly position: Observable<DataGridPosition>;\n\t/**\n\t * An Observable that provides the current and previous row indexes.\n\t */\n\treadonly rowIndex: Observable<{ current: number, previous: number }>;\n\t/**\n\t * An Observable that provides the current and previous column indexes.\n\t */\n\treadonly columnIndex: Observable<{ current: number, previous: number }>;\n\n\t/**\n\t * Internal subject to handle changes in row\n\t */\n\tprotected rowSubject = new BehaviorSubject({ current: 0, previous: -1 });\n\t/**\n\t * Internal subject to handle changes in column\n\t */\n\tprotected columnSubject = new BehaviorSubject({ current: 0, previous: -1 });\n\n\t/**\n\t * The latest value emitted by the rowSubject\n\t */\n\tprotected get currentRow() {\n\t\treturn this.rowSubject.getValue().current;\n\t}\n\n\t/**\n\t * The latest value emitted by the columnSubject\n\t */\n\tprotected get currentColumn() {\n\t\treturn this.columnSubject.getValue().current;\n\t}\n\n\t/**\n\t * The last column as reported by the adapter\n\t */\n\tprotected get lastColumn() {\n\t\treturn this.tableAdapter.lastColumnIndex;\n\t}\n\n\t/**\n\t * The last row as reported by the adapter\n\t */\n\tprotected get lastRow() {\n\t\treturn this.tableAdapter.lastRowIndex;\n\t}\n\n\t/**\n\t * `DataGridInteractionModel` requires knowledge of events, and a representation of your table/grid to be useful.\n\t *\n\t * @param keyboardEventStream an Observable of KeyboardEvents. Should be scoped to the table container.\n\t * @param clickEventStream an Observable of ClickEvents. should only include clicks that take action on items known by the TableAdapter\n\t * @param tableAdapter an instance of a concrete class that implements TableAdapter. The standard carbon table uses TableDomAdapter\n\t */\n\tconstructor(\n\t\tprotected keyboardEventStream: Observable<KeyboardEvent>,\n\t\tprotected clickEventStream: Observable<MouseEvent>,\n\t\tprotected tableAdapter: TableAdapter\n\t) {\n\t\tthis.rowIndex = this.rowSubject.asObservable();\n\t\tthis.columnIndex = this.columnSubject.asObservable();\n\t\tthis.position = combineLatest(this.rowIndex, this.columnIndex).pipe(map(positions => {\n\t\t\tconst [row, column] = positions;\n\t\t\treturn {\n\t\t\t\tcurrent: [row.current, column.current],\n\t\t\t\tprevious: [row.previous, column.previous]\n\t\t\t};\n\t\t})) as Observable<DataGridPosition>;\n\t\tthis.keyboardEventStream.subscribe(this.handleKeyboardEvent.bind(this));\n\t\tthis.clickEventStream.subscribe(this.handleClickEvent.bind(this));\n\t}\n\n\t/**\n\t * Handles moving the position according to the w3 datagrid navigation specs\n\t *\n\t * Refs:\n\t * - https://www.w3.org/TR/wai-aria-practices/examples/grid/dataGrids.html\n\t * - https://www.w3.org/TR/wai-aria-practices/#grid\n\t *\n\t * @param event the KeyboardEvent to handle\n\t */\n\thandleKeyboa