{"_id":"@terminus/ui-table","_rev":"61-4f41f360b53b3d888e6a31c824b043f6","name":"@terminus/ui-table","dist-tags":{"latest":"4.0.2","next":"4.0.7"},"versions":{"0.0.1":{"name":"@terminus/ui-table","version":"0.0.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^1.0.10","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"*","@terminus/ui-sort":"*","@terminus/ui-utilities":"*","rxjs":"^6.5.0"},"main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@0.0.1","_nodeVersion":"12.10.0","_npmVersion":"6.10.3","dist":{"integrity":"sha512-lflsx1YJGP3auv7atTb/lKkHtQbMG6sVRCWW+4+uSYpfLFdIypca8tP9Cf1ophh7GDxt1ag39pQ/DvGy1z5GTg==","shasum":"354bb5411235744b3085d985111e98bbf777d94f","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-0.0.1.tgz","fileCount":87,"unpackedSize":964302,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe1dvVCRA9TVsSAnZWagAA4VEP/jvbTd7T+R+CIhLuk1j5\n56a7BWAkpQ5Kh24E6cziYSZIFUeA4kvvAMZzRwne6Ln+GMJpGPPAi8ZYOeB2\nf7AvmUybY44eCk6Yg2v5In1VGraaG/H1ImPREvPpAZGrbJdYsRfipWNthnz2\nrGGcEHluIHnqpI/U81BJVaz7VUIkPDPQf0Gs4RMK0CREHWeWdJeJCey7HR4k\nZRzuiBCJxiU+5k457kmTHNBE0dAAlFUAtlySm7iwY6Gbv15Guw9XvZlCi8BK\nX4t2qGj8MIpIYCIUbOhXbxAdzJqiwED6cwr3Qid+RyGco8vIXNQIRmcXGm2z\nbvzURx7+DFodJhRxBkjub6amkeQFkrFGO0fBV3+kChIJtWiwZbdSa+84Wi75\nZcBVemIgLgfJeDYnZDAsYk/gkFB3RJY7q5MXd3fyBfgnErxS2AcssYFnArDb\ng2AbWKZr7XoIOBg0qrXHLhA38V3nwaR1/2xNJ7Nr2QZmOqQ/PKUs6VALLVF5\n3p1nxJGpioU3AZqp5tSjzeODPs1fM6eUc4Vx9wZ8gM02uXKfp1Exa8o2tvtS\nHQU3SA/Oco/fWFTZCD/fC9T/DhjHmZADrBPxksio9uogOiaJhxO1wdTfV/LK\n1Hp6UbGmGqpyKbP5nUYqyzktbqBEj1Rqoq2A3pc9W10fILhWWF9JNpo97ZsL\n1lRM\r\n=vzrH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLbfpvX27VLsG4iNQwdceEWiaDKFIwB1zWI9jiNE0RIgIhAMDvQMHsLYd87JdzF/B+gp2RUrDERIYSC5DeqPSq/JXr"}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"benjamincharity","email":"ben@benjamincharity.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_0.0.1_1591073749147_0.6601132007041142"},"_hasShrinkwrap":false},"1.0.0":{"name":"@terminus/ui-table","version":"1.0.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^1.0.10","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^0.0.1","@terminus/ui-sort":"^0.0.1","@terminus/ui-utilities":"^0.0.1","rxjs":"^6.5.0","tslib":"^1.10.0"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Github release][gh-release-badge]][gh-releases] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by adding container around your content with `white-space: normal`.\n\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n \nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->    \n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n####  Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/master/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/master/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[gh-release-badge]:    https://img.shields.io/github/release/GetTerminus/terminus-oss.svg\n[gh-releases]:         https://github.com/GetTerminus/terminus-ui/releases/\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/CI%20Release/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.0","_nodeVersion":"12.16.3","_npmVersion":"6.14.5","dist":{"integrity":"sha512-NzNlTfX5XzqQdu7/C++w7EHRoG4khD8XqxswXarXCwGNNwATsaRAD7EgyqLKJjykRll82oHDrSuduCSj2ab0iA==","shasum":"5758c762754f51a1b8b27556852e4cd5bcdb361b","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.0.tgz","fileCount":26,"unpackedSize":108572,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2UHUCRA9TVsSAnZWagAAHpIQAIdRRHLT9NwWrR1PSWv7\ngvwW3dnRfrimIn/YsaKEpwTz24fQW+YrhH/4qJFzUvBaFaNIlH2k0JqGYSoh\nGGx5XqxCtc81bg/aDK6f5R8YClvPNb6YwIIymmalyJgaS/6LPVug19LnB5mK\nDQnKTQgOxtvnMJ8zMIV3rb+jibVw1jmYdz3UURZF/VUwrc+dqAvzSiUWNslA\nLMuqOeSQO/IB5oMJO7TWwdWgo0RwXRCSKBaI1vVSY1SDpDDTE8ajIbMY8cGj\nBQQKI9FyDZRe3TxTbDsgGc3Ytd1mrdq9DPTr4YSnDYPVCH5zJdTW6sOhAa69\nvgxtVyzdHo8kXoq4o3IB1qTNwEQ+sMiNG5X+IIihGtiNqeN4XLIZtGUKXcdr\niv3VtN57oiqB6wVAKsVj3Ak5y/Ju+5k7uzRqXPnBfHuReeKnXi+y+Cx7FQYk\nJ8vzsmOvXhzdOD72FQ5eT3UHAPaxglOYGH90/G37mblHP82gs5EdFOb+7DPU\nsKBA9Jtvzzz548Jetqyb3OU2ibhqPq8FeWhP8GH8GZGsQE1j5/MwjYg6gSXz\nG1iJp0zveCre94ddHZGaiy21NbQQUgcAz04W4P4IBz2re9MGIfUU4loX747j\n2YrTgsuBlMUZJpEOlsFsXhYhLxiXD4LFVVcxzFWwTwvSVkn332WmgZZdcz9t\nPLkX\r\n=fl8T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCYu61ehMqpHbKlivB7mktEUT0fMWfEKY1nRs9HVJ+YKAIhAIa4cRDdZoih12LOoKIE0ZYajNfj4hQ9I55KTrgr1q6X"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.0_1591296468223_0.6344423615238999"},"_hasShrinkwrap":false},"1.0.1":{"name":"@terminus/ui-table","version":"1.0.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^1.0.10","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^0.0.1","@terminus/ui-sort":"^0.0.1","@terminus/ui-utilities":"^0.0.1","rxjs":"^6.5.0","tslib":"^1.10.0"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.1","_nodeVersion":"12.16.3","_npmVersion":"6.14.5","dist":{"integrity":"sha512-ke0TqnwMn8cI7atUA/iHuU3t8djbH3O96Gr00XQejZUQ8YtWycg8/7iK7Zj9rH3snRZQRcYuoXCGJ/Ce1O3LAg==","shasum":"6bf9c7aa8483a89946be7c8feb2eaa7d61db44d2","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.1.tgz","fileCount":27,"unpackedSize":108626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe2a1xCRA9TVsSAnZWagAAp+IP/AsfpwRmHVocxLwEGUwJ\nr81pM5S6CE+TV8T4di3MMnoT5mmwCQBn2D6y6BXGSHCVD82gsMQ3BfV1cqdY\nHMXVqQGnMxywfzR85ovZZmI/N6XMrM8OS40zpB150il/aa9yLs6+naxhkEMn\nvadP7mhPy9UKC6OSLaNiODFRPXXZ6vs34zsCTnNiJ5U+nUoixJ+2CRaJyjgx\nX2bygQ0EnOSIrfzdtrJXVrJh64QqHCHngS/FLb8PV7Q6L00A1E9A3hVITPR4\nd+xc+EwvLVEpVi1TO3BP9hPwQun6FdTmixCZpU0CZ4reoV1b/IXx8htPwJcr\nv9m5DLQjJ+wo5w7+1AtptGut97olss/Fmp+G/aeFlwIUYlJ0epS+zDrciXcr\nFPZUYtOZEPLwXhQTFJxU4ljiVYeXz2DLxLSsd2S++7jT1fbX/FjPUqVi/pBb\nnMt5he9zOfgQIgQISf+WNtPEsDoRW0YbX2Omlm7aUqfPP44xjRa9YgUKBtgQ\n+UNVx7bWlhJCgQEZ6Dd+1r/1TeDspuN0atj0LQeqK0pJ6ZjGvzxX+cz/7Aum\nCgamSTvTdfamk8BWAFbZezzysLpoZR57DO2tEV1pMJrUR0fbl+3xutY2hcg9\n1rH80IxDcnpbKEPngyPr/586249SbYBVR2lmlnz9NyJBFBpxeZUJkUP1MO8s\np/Bi\r\n=4liK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCy2NVMcPal6oCOVuqpG1l8s6PnLeh4KU+hT8vxXh/zzAIhAJ532smu630SFr1dLNVa5c4llp2wWL83Oefvlt/Toxa9"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.1_1591324017144_0.07504474681955431"},"_hasShrinkwrap":false},"1.0.2":{"name":"@terminus/ui-table","version":"1.0.2","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.0.2","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Github release][gh-release-badge]][gh-releases] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by adding container around your content with `white-space: normal`.\n\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n \nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->    \n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n####  Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/master/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/master/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[gh-release-badge]:    https://img.shields.io/github/release/GetTerminus/terminus-oss.svg\n[gh-releases]:         https://github.com/GetTerminus/terminus-ui/releases/\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.2","_nodeVersion":"12.10.0","_npmVersion":"6.10.3","dist":{"integrity":"sha512-DJb8eipG910qjE9YNTecFdL2E2/+J2U13dLn9jlyDBow05z6nwBPbEoAskCdvmHdxBPLoHftMYAl2vrmQoV8Eg==","shasum":"4de7889a58d66a6a4cdeda1333635fb159dc5836","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.2.tgz","fileCount":88,"unpackedSize":964764,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4TLlCRA9TVsSAnZWagAAKKgQAI0fzum94AFGPjirExoq\nXspJebdi+Qi/axtX0q8uymCMVjnULOuS0Yj3lpEQXcE8IywnaIZvhTJFKmZB\n+0OMpMvwB46zeV23ZDyty7sHCXpWLwNDpO5UiMhDoon/6/2XxFUAXIPSIqQf\naWQDUOYLiuYaVLm+5Xe1NOuaUeQ0ghAxYykeNYwxnAeYvRtnGka3mNyYCBfO\nOdpK6aCN2ICjSWn7DPCCq5ZnFbqJ2W/LeAWeshSMe67D6yoXAAySo095emEQ\nz4j2jsYjgv6mt8h6EebK3mbU9wVIqpMex78mmIbLjBIncglUiyrhf6Ndin65\n4qAIaGF433sApRHaTl7jytrenbQE1zomUBCCEsy9C4FXrnVXTbkLCmQltKig\n2gVDujV0ACGco3cgbcHXn+hguvmU7QCeup4chUGhIp3+cWoUTtfQjEpMZ8Ai\n3+wrVunm36V7fqX56rf4pW06oAK48Fw9zdVxxBm+B7J0QONKTT2Q6H5DmDBp\nGjNG8IufWFnbsv8QhkfTvUMsI2FZEC/exaVf5kt72GxnhrTnQKUFz9moOZq0\nl+U1ubHccwJ3Q0ekNx6D1skwDJ6YLca/jCDn+xC/ntT9+Hp4fljuD+DgZU0g\niZj2oeNtF+Y41ppoSM+Cx5p+bFcxhz51X4mC2wQBq+OPvX5/d8gGGm2s/Tyo\nF2bT\r\n=+ZPF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9Nfx6IXfsxo9B0t9+LVF4Sj7nY7TBIosYSv7/WvMBBAiAYrkipahOCUURpUSY0bkODVeiL89hhA2OXqhyxuCr3Aw=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"benjamincharity","email":"ben@benjamincharity.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.2_1591816932711_0.856461592614542"},"_hasShrinkwrap":false},"1.0.3":{"name":"@terminus/ui-table","version":"1.0.3","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.0.2","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.0","@terminus/ui-selection-list":"1.0.5","@terminus/ui-chip":"1.0.4","@terminus/ui-option":"1.0.3","@terminus/ui-checkbox":"1.0.3","@angular/material":"9.2.3","@terminus/ui-icon":"1.0.3","@terminus/ui-validation-messages":"1.0.5","@terminus/ui-pipes":"1.0.3","date-fns":"2.14.0","@terminus/ui-form-field":"1.0.5","@terminus/ui-spacing":"1.0.3","@angular/flex-layout":"9.0.0-beta.29","@terminus/ui-input":"1.0.5","@terminus/ui-validators":"1.0.3","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-button":"1.0.8","@terminus/ui-menu":"1.0.3","@terminus/ui-tooltip":"1.0.3","@angular/animations":"9.1.0"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.3","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.1/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-HWaEBPm/+JGMUTEKva92SjPdx4PDZcQDWx71QinzocjtuaBjZf+dkW48IcXiqdm7tHqzhKnCgUf58RqEdZ31Sw==","shasum":"997fa876fb384802cd6fa4d9fb150428aa29d1a0","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.3.tgz","fileCount":89,"unpackedSize":966795,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe4j1+CRA9TVsSAnZWagAAhRQP/jOBsBNhnvkB4r+HYoRg\nEh4C0fED6LsRlU9KB0VtaxKEtiZKyBzBbnkwwuwBBX2pJhFiQEQaMF+k1Uss\njhzsGE0WsANhANXMRXxt6vNgmtpryQj57fyEC3UZjPZALhIeZnYc4CgsDANf\nz3qxdATpg0xhh/wCBSVz2GlB5B7pDN7oJEgFoRNxgWH7ptGqFeX0z/vJO6AK\nNOiBRuNeSX0KIfzbqvw8Lw/MC5DMfm/reeS3B7sUcENBxtqx/3KSSfmqrxds\n9ghQ859YfKLDD63SMCvg2Mg3U/SheGuqetkozjC1oU38v2tun+AXyTWnbKBk\nZ7e7HaYWjzkfjh7o8zaXuhFPgAgNUXpKHvA6zeJVdVbqyG0Qkd1N4xM6YkWW\nqAQE2Dve0EzsvSwLpfmi+GKM08EM06g2dHX+71m4c+sDsSON1g5JV5/Pj5ix\nW6sbVpEkaPzXPAGOzLvkiCEaTVfdWIfmrFpRHskorXZyfWP96MxYUi8y23fG\nlxRB2rxl2ZQ2nfEAPwAhqPP0ASkmzpm9EOW86UoKD46XBK71ifyxkOr3WA9o\nILIQ3f1padZzAnl6IOImb0ZPo8MAL+H7SAAnIFz6uoFfNTZL4b1lkMDbsiBL\nYs5ALa2btw3+2ftCB5CZu9a16gvFFBMa1NPT8f9FY147SiqHkOI/uaLCAlhJ\nR42W\r\n=ddJv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDd4mIV7DCN4l7mlql3HoBIvX3g2S+oY+5n8qJwmt/dxAiEAhP5sUdeiJtCqjabXj5oJNI1omj/4DHpgQGBMA5tfASk="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.3_1591885182146_0.3202494952167272"},"_hasShrinkwrap":false},"1.0.4":{"name":"@terminus/ui-table","version":"1.0.4","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.0.2","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.4","_nodeVersion":"12.18.0","_npmVersion":"lerna/3.22.1/node@v12.18.0+x64 (linux)","dist":{"integrity":"sha512-yR+OiUhneCI2t+MqMMSprOY18AIki6vo3/5BhE5g3/CmwaTNGUpnfKdbdHyxTttMc5+2bESvmcfzTGRXAXTwpw==","shasum":"008f00af3c6d60062ce57b10eda6b7c77d4b6b04","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.4.tgz","fileCount":89,"unpackedSize":966117,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8iFUCRA9TVsSAnZWagAAWqsP/iXwZJ3zkYTCa3nRY9tA\neWHBBsR0s/NSikfyKctXJq8znGRrONOpoo++aD8yPNRiCdIKvkN3wPDiJx3M\n0HzjJp/1C8pTvdvhGIqt5yjeBHsEQTcHIuooAOQBzUZmCuATXl/d+jGSebWi\nEEBZgeTREi8ipkOuT3GAhlYoaDVHip4l25WCN2FPiebRYrvNkyzw6HhDO75O\nha8IyS5ROyuEFINogr378jGsWDidppRkQOBJXSjlHr1L8iB5sKvKb6p2lqZk\nMwHIYKsZgui+bHlkUMil6NJgXTQdVX2G9PtKOkip7PYECP+NIpw3VAeP669w\nqhHD6MrWu+djncsdWzK0xr3cEKdNfdoETj3KsoClEK+Yv8BiBks3aO3fDfpf\nhotl9C2zkpHf7aFRMZ6Q2aZvmL/yce20Scbz81dJv1AlmiJ4oLQ9UgTFJWDd\nrwpyaDiDdVoDUMOEQ0ne5NHVb94uSBh+l8n5AIXf3gAN9jyf1goBrIa1K1Su\nL2Q8Zz8o6R9duWPa6HCgTrG3i2RhFeH2rFNC5H8+4Mu1/E1177dMBNZAAZWc\nAT/u2p7qAx/Ay+caXOtMdIiFVrZym8tS+ihWN4iHQZVryom0wFp00kN1Hu1U\nM6w3eXj373yc5b0Tg73N4viK2sJQTkGuBeOO6juxemM2EBMmZ2j5OYmXuYoI\njEdT\r\n=4qWm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8uuiGHn2CPCNQWQrpsa9T+ugB3hsk1Ax4lcO43lLn4gIhAJEgHzVK1x0W1lbuwNiqfNxzlwaRSpABbx9RspCRWBBT"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.4_1592926548129_0.6642775507742562"},"_hasShrinkwrap":false},"1.0.5":{"name":"@terminus/ui-table","version":"1.0.5","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.0.2","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.5","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-DJldh2Sbra9Id1jm9BvZLsQAD/v/+1YmQdKqnVjoKlJhM9FrSMpDke2ED0cuzpPRWyQ7Zt8jJo+GbEqqz/nqNQ==","shasum":"41cde57e0f7c6ca60076ac18edd2dcf71f013725","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.5.tgz","fileCount":89,"unpackedSize":966313,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfBJdQCRA9TVsSAnZWagAAHbIQAIcpPfV7a03uu153lb6N\nqQCh1MEPPxd96KaOBoRwAa9kAJ8VdYXTJFrjcA/G7Vsqav83MlRiTzUET0zn\n5sashMkCP1ODsOOVGmzRf825I5pyg7Ine6ttH/GwR54omrcLl3Y2otoz8+Ct\njt5MLYuxYaIfMypaMqU6Pz9PKUCBM8qzG0ebJbLkOAZcmqf2Rb3CEhRcSnck\n6oGM/9g0zs8E5zSAcezRLW/f9rYNw8gw8iFexWpmkYadLXK9bXY4HH2vN+dk\n38SAjZ9ghrbsGRjO8AQ8vp75M8mLOFsEhFC98AwwdAEtyvy6UR/fHpo1iFsl\nDmtREY9BUOgsnfagzniQedmitJnfzyayNHi5gZYHGWFpkrf7AWscguoY96Ie\n4fglbpL+pnYsRLAwiuuvhobbBeQCc8iKkqPR4FSFLCYKdX59RZHn+LkSWdUQ\n05oIbe3EiwHGfQnHOg+VC17D2YO47BkP8RMIeMguiWE2endlgP2+ga5pfWaP\nIpKJj9NLv8k9iEaDa6sLgosD9m0j2jdeT7kx+VbW9PS+o3A4KXTmq8UV2QMk\nM7LPAu+Sija99lwg/jLRDkb7AWJlIkTPF/nbmZmQ+UScG/4f5pRrXyIPK2f8\naXvymNOsm2KtgWIdrQb4kkHcQL4nJ8y0lejVMoGsxTFahQDuo5pFqy7V6fzc\nrNW2\r\n=t761\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDP1c2dCGgoKyYFbEhIcsZyc1VR2VCezZfyNMX9B34O2AiEAmMAd/ewGH550AVvXLtYeZJW46p7dCmw0J1hu3GbW99o="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.5_1594136400178_0.03591877332933335"},"_hasShrinkwrap":false},"1.0.6":{"name":"@terminus/ui-table","version":"1.0.6","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.0.2","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","@terminus/ui-selection-list":"1.0.9","@terminus/ui-chip":"1.0.8","@terminus/ui-icon":"1.0.6","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/material":"9.2.4","@terminus/ui-form-field":"1.0.8","@terminus/ui-spacing":"1.0.7","@terminus/ui-validation-messages":"1.0.9","@terminus/ui-pipes":"1.0.6","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.7","@terminus/ui-checkbox":"1.0.6","@terminus/ui-input":"1.0.8","@terminus/ui-validators":"1.0.5","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-button":"1.0.11","@terminus/ui-menu":"1.0.7","@terminus/ui-tooltip":"1.0.8","@angular/animations":"9.1.11"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Github release][gh-release-badge]][gh-releases] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [Styles to be added](#styles-to-be-added)\n  - [Links to be added](#links-to-be-added)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n  * @angular/cdk\n  * @angular/common\n  * @angular/core\n  * @angular/flex-layout\n  * @angular/forms\n  * @angular/material\n  * @angular/platform-browser\n  * @terminus/design-tokens\n  * @terminus/ngx-tools\n  * @terminus/ui-button\n  * @terminus/ui-chip\n  * @terminus/ui-checkbox\n  * @terminus/ui-form-field\n  * @terminus/ui-icon\n  * @terminus/ui-input\n  * @terminus/ui-menu\n  * @terminus/ui-option\n  * @terminus/ui-paginator\n  * @terminus/ui-pipes\n  * @terminus/ui-selection-list\n  * @terminus/ui-sort\n  * @terminus/ui-spacing\n  * @terminus/ui-styles\n  * @terminus/ui-table\n  * @terminus/ui-tooltip\n  * @terminus/ui-validation-messages\n  * @terminus/ui-validators\n  * @terminus/ui-utilities\n  * text-mask-addons\n  * text-mask-core\n  * date-fns\n\nUsing `ng add` command can help getting all the dependencies installed:\n\n```bash\n    ng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n  * TsTableModule\n  * TsIconModule\n  * DragDropModule\n  * FlexLayoutModule\n  * FormsModule\n  * ReactiveFormsModule\n  * TsPaginatorModule\n   \n\n### Styles to be added\n\n At top level `styles.scss`, add these styles:\n\n```html\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```\n\n### Links to be added\n\nIn order to have material icons shown up correctly, add this to the top level html\n\n```html\n<link href=\"https://fonts.googleapis.com/icon?family=Material+Icons\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by adding container around your content with `white-space: normal`.\n\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n \nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->    \n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n####  Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[gh-release-badge]:    https://img.shields.io/github/release/GetTerminus/terminus-oss.svg\n[gh-releases]:         https://github.com/GetTerminus/terminus-ui/releases/\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.6","_nodeVersion":"12.18.1","_npmVersion":"lerna/3.22.1/node@v12.18.1+x64 (linux)","dist":{"integrity":"sha512-NRQPurTynXJqKY92zilqfRBb/U0DdLlwgQtjtQZOWC+Oob2uh+SwuJHKVS6ugJiph/jysnlaCVFdLrqFOo65lg==","shasum":"770af7c784574b66971097d5956239a4afde0b80","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.6.tgz","fileCount":89,"unpackedSize":969078,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDLUMCRA9TVsSAnZWagAAAgsP+wXIBgdV7EamkrLNZWw/\nh5PN16tpQgBxaGn7mw9BMKEvbnVpRHySXE4c6JqV3I8x5wmeWLwtcDbhJRy7\naY7Z0trEDZsTE/qgup33EAixbTVJe86UuLyDtnRYwsuU3ElMhbHiQYvO+RIC\nHnEuCq7noXhxrys6+De05P8i2rPYYFZ5Kj6U7qPgzKqsbxyaEm8jJcA4VozL\nRuHFcAHydvonNSm/iI4Lo6VKZQkGCD7eCLQ6MwZ5h8o9yRKeYQQVZiOfDiKd\n28CkAZxuJf12MekoKBOuoZFoVJXWeR2j+RcIIy8+03VyGbmgZ8TV4BqBOlh4\nzS5OpUXzoMOATeiLIGPkkIeh7MHPe6a/SH4xh9t6DoGnre5nhfpVwdJCNJI4\n8+t4M+1TdPL/RJ3CoFingTyErC60qrxl4OvEMgdZb2kjBTIc0ar/e4PZkRmy\ngLl4FYgjGsn9QCKO3F0L+ukKxvqiO2gChNdYLnanoNbOBrKrJXQCb7qqhRu0\nx/Tjj26xOeJqbCv/Du83h734tiRroJbl6g7/cNv3KA195kskEBFSLaTQZezQ\nkq5cUI71K/MPskaPNAeIKMw8SMlcnTUh/nZRpDYKIlZ8z0TED2XLgnuoS+49\nGHGSOtcmE2oOBo7OlUmWIquzGMLOch7gnKb86n04g8tYjYlmLg2vmQPD5XLY\nluID\r\n=RjYk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEFdf+wYtNIAlRAciYg5brHXowaZV0z76boe345Y4Ey8AiAkf9Y+qtfQBYZuyOjMH1XPK5zDua5cKabsLj7kwwDT+g=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.6_1594668299890_0.1392316029370022"},"_hasShrinkwrap":false},"1.0.7":{"name":"@terminus/ui-table","version":"1.0.7","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^2.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","@terminus/ui-selection-list":"1.0.9","@terminus/ui-chip":"1.0.8","@terminus/ui-icon":"1.0.6","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/material":"9.2.4","@terminus/ui-form-field":"1.0.8","@terminus/ui-spacing":"1.0.7","@terminus/ui-validation-messages":"1.0.9","@terminus/ui-pipes":"1.0.6","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.7","@terminus/ui-checkbox":"1.0.7","@terminus/ui-input":"1.0.8","@terminus/ui-validators":"1.0.5","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-button":"1.0.11","@terminus/ui-menu":"1.0.7","@terminus/ui-tooltip":"1.0.8","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@1.0.7","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-MtY4ISRxzFc1N7EN38WYVRWQ/UJ/26750JvjMTSIX/PGC9c+VTgmWkx1D6WN3s6vHbulsU2u3JmgABoxjGO3KA==","shasum":"04418d3c54a36925a7086091dee53a059c809559","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-1.0.7.tgz","fileCount":93,"unpackedSize":976697,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDf8gCRA9TVsSAnZWagAA5vwP/3z1RyENjfafntyP0Zt1\nlGrzKdT/tB5DilDP0HvEC7F+h6iiGmmPTgNdcmulfb8oktFHaJbV3igl+V2y\njS318w/xhkBDHGe5xLnQFFEVn0j48x9mKQG30ECx2BjBnE68PAajV2DfieEP\nTxuDKHJcmjjILJskEtCYWpgCWwL2ToefgPuYf41ExGImNBW2EGjkzuzAuECb\nSqeWmcd6v1yrmfCAz6cneGtqcjT9b3qwRrWTy+0AL1qeSPIHIbwhklxSeky5\nuJ8UmTlyP1oxr07zveqyTApYMBOSFrPZ/ObN+MrzRVibTg13wsjBFlYHVPX4\nNTzj9uRE3/lg7LaYUzpb3dgHAXzyfVYgGNJG9AZacKTJZ3KfT8KArQ/ePu5l\nfXMGoYI02sizF1RdUaSIHSYi2L1JEFYRwlmBPgQUGqaZdpq8fPzGk5rV7tuY\nl8HKj1rHzB8UVrvQ/BQiflnspSLCgA9zaxPHWY51f2L7xWKWJHZqAuVN0eH2\nWuXS3RuTPYFOFT7ndTtQt8+HBZeHM3I9iGFCohb1B3QgQExsT7rAR9SVxT5q\nyzmgnt2xTNDL0NxDN4oi/8uFwZ130nr2qixlSg/Img2PjU1axvzi3OattGyK\nSaDtCyRbMSrkzNc40nugAWJ2l1qc0rdvQHxvIaUMPUSeXMwuTwxcoHNwPZQS\nCgZH\r\n=cCEU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFcAc/PA5kxN0nt26o2ZdddP7bsz3Vp1liAQi21DcbYQAiAyTH63tsluYxQp0isp16ciC/wqkGJprgQp6/skoCgRKA=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_1.0.7_1594752800133_0.06847720629746634"},"_hasShrinkwrap":false},"2.0.0":{"name":"@terminus/ui-table","version":"2.0.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^1.0.0","@terminus/ui-sort":"^1.0.0","@terminus/ui-utilities":"^1.0.0","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/forms":"9.1.11","@terminus/ui-button":"2.0.0","@terminus/ui-icon":"2.0.0","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.0","@terminus/ui-chip":"2.0.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.0","@terminus/ui-spacing":"1.0.9","@terminus/ui-validation-messages":"1.0.11","@terminus/ui-pipes":"1.0.7","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.8","@terminus/ui-checkbox":"2.0.0","@terminus/ui-input":"2.0.0","@terminus/ui-validators":"1.0.7","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.0","@terminus/ui-tooltip":"1.0.9","@angular/animations":"9.1.11"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/ngx-tools`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n   \n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by adding container around your content with `white-space: normal`.\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n \nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->    \n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n####  Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.0","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-VAetTM+3+LujeAg6OKJSA03zUBp3++jhaq3h8f7Pqi9Zq0l3iRpy8uXRtmq6eWvqVv8oCdwzzxsZ76mTHP1U7w==","shasum":"56ceed59ef874cc6a2db5ac6303bc7f98bc02e4d","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.0.tgz","fileCount":93,"unpackedSize":981784,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfHy26CRA9TVsSAnZWagAAGjoP/3lVUphMxBxO9iK7kJs8\noC7whBEk4O6AfFBlj2MF9i/TtHd7BuFpFC8cXQydfCJuN+BgTsurMgHrdvvn\n+uoQ97+qfFKWKSPxMO03rqhgyjyJ9JGYkI4U4ZrWhiPPAtmHN8J4/mPBfGNZ\nALhKJTfYk+bi+tAl6xRkYjiXVUy5O5KnyCJmcqcRFm1IZ7g5kyIM77rI6WXN\n7huVPMQptrr3SurJCVxQkVeSrQJLgk8hTThRXFS7szq5khODiuqkZhk627n9\ngq0P+dPXeqLE3mxytw8cIUMgdQk12/rPv1vI94T2dT5jWXFOu0tvWGhzWy34\n5JZn2l2tsXOndiymRcSC38qFKKaRYOcbiUsL45uCXGt6ZKgOgyyyIS1v0Y47\nGzkOQQPZk9rewDwH/jZKPrNU6RyubBOXsQzbju6manBPv45WoNqB9Ev7E3HK\nuuSxrI4bu+Z4hsla8eOZ3fzhh3BEzAx/fjE9MEj079jboU7z4cNPD1oMWJn2\n911wH/86I22tMYPHL0chheDN2Ax0UMb8M4NY8bd18BCB6sVcWPCuvcbmD3k3\nMgK2YQqyhKYHeLTD20YxRyecE2ff4KA0HUav2bkGhj+ceBtSWljVM2fWZrNI\nEo9z113rQIP+0nutyirMWA8D/QThi1+X0DRuV4nRuJZzgaKDinXVp5iXwgmr\nDhAC\r\n=ZTbf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF0FHJcMfQDzI2NHf30CtaZMuSeiJBxovPA9l1js8hOrAiB6462v3YQqTwYn6QZgDsGhCWl/LQjQsf/913EoQEhHHA=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.0_1595878841416_0.2709953053205534"},"_hasShrinkwrap":false},"2.0.1":{"name":"@terminus/ui-table","version":"2.0.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/forms":"9.1.11","@terminus/ui-button":"2.0.1","@terminus/ui-icon":"2.0.1","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.1","@terminus/ui-chip":"2.0.1","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.1","@terminus/ui-spacing":"1.0.10","@terminus/ui-validation-messages":"1.0.12","@terminus/ui-pipes":"1.0.8","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.9","@terminus/ui-checkbox":"2.0.1","@terminus/ui-input":"2.0.1","@terminus/ui-validators":"1.0.8","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.1","@terminus/ui-tooltip":"1.0.10","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.1","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-SvKpDw33wQWgNz9ncviD39qdR9XdSd8izXcsCGEXlR11zmjzlk46jPsFp1m5mmXssi3wEs87nxQG3QONi6VqWQ==","shasum":"b4744cbcda3cb38468a66b8f9657210cd6e0748c","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.1.tgz","fileCount":93,"unpackedSize":982071,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfIER0CRA9TVsSAnZWagAAoaQQAKCsWt4ShHh4rW1eW5H0\n9kcmbuOW5X0RRCZdrJdWEGGOjj3UoF2MJ7WweWCE3F6OxBzpou1Dp0fXxE6A\nZGA/ItpN68jBgGI3UL72+J2zXMrLjLy0E3Fz3QcSSLR0JRk+hCNoz7gd1mOM\nzH0NFvZvYMvAwun5fzDsOtrg9jH4qX2kMKTW0OnqPcSiLLj8QqeGpjG5eNh9\nw64q1y8jiFlghXudvVP+muj24hQjlY4W5973ALqypPQHKojgt9uK5GpJng8r\n+rWBJXFrf5zVo5+s4Y6/w1z0/VDSy3AUXG+xWeUp0dzvflRIaesXPs5ANukQ\ngLOGUrkTWpM3kEuZB9KTorrhRY16YHiln/y/UZz2qjDvjoQWJf3Oi4oei1yY\nGX8vf0CxjoS7UK+MtGmD+yDW6M/XVx/oKmEkwnIu4s75+l6egck1TYOvUmAH\nlddkO/rActbfmgoBNudYsJa/sC53Ksv6lSXqCwztPzApfpqE+hJUGAv8aS1e\nKkj9e9w6a88umXyxgcBumrF4lroyHf+TKqsR56tIiC+3RWKWu7FMK3bzLhQd\nHqK3S9yVyWojATwOPnOOVRnG9jjCKLcl7EHMVwOvVeFkwZT7Su0EPlE7EWkf\nmC3BeAznjDmoZwg3c2MP2F+gayvO9ceraihvwa4JoGSBcavgw0gUmn+W/Yox\n32SV\r\n=oTyN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZi7WbmQW3YeCbPULj+pCqm1KUOtls6GBSpn7aG8AB6wIgKrwAlIRcCuVyhYcmMBlT8dPYKBRwK1tGemilOqT3zLM="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.1_1595950195873_0.8533875808152314"},"_hasShrinkwrap":false},"2.0.2":{"name":"@terminus/ui-table","version":"2.0.2","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/forms":"9.1.11","@terminus/ui-button":"2.0.2","@terminus/ui-icon":"2.0.2","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.2","@terminus/ui-chip":"2.0.2","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.2","@terminus/ui-spacing":"1.0.10","@terminus/ui-validation-messages":"1.0.12","@terminus/ui-pipes":"1.0.8","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.9","@terminus/ui-checkbox":"2.0.2","@terminus/ui-input":"2.0.2","@terminus/ui-validators":"1.0.8","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.2","@terminus/ui-tooltip":"1.0.10","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.2","_nodeVersion":"12.18.2","_npmVersion":"lerna/3.22.1/node@v12.18.2+x64 (linux)","dist":{"integrity":"sha512-7Ob0vVn8Or0Q7I04qYW8ECE25r+8bOIe4nGmOhvDhRMiacUyPIXD2FG0iLh5Bz5MbWXTtUvcjiS7odpTIQrQPw==","shasum":"4697e567d0079b0f543179c31c10a15d1ac3d914","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.2.tgz","fileCount":93,"unpackedSize":982738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfIG5zCRA9TVsSAnZWagAAjUQP/iufUp90fgDTJjMUE1JS\nHrJl3oldXvfeEmfbhl9iGsE7ABkE1nufit5ZlpBXKr8wNIx/6WkduCyMGoEU\nAyVMZWwG58hAp3CTEf9ffz6GDOT++vxTgSEmnG3x9NAAJIceVPubHLqeljcD\nBjUs1jI+U+bPI6BgwcWkN+kXxDRK9aRKvYBg1B4Vyz2Li1/Bp+frZUKIFelI\n955PsRih1RAC2dXdbuye2uPiOsgFTZIpy+VJGI2x/+qGmFxgps+Cef/0yxon\nnj8C0R91Gfy6Ci4sOF7lcdl+DSY0aGfIe6SMn9Id84EAxzIFvKvSCgPtvXNu\nW9NWOi54HlN9U147IU+0rTFA/HuSnvp9HnWYRFiwZHUQoC4ZgfscszyXWEAk\nzgWCAQ58BryGQM8RRJswmeSHrEiowsQOr0umjmDXensKrkS/mTh2sK5Jt8L2\nl+QPeAqGXsif6RyrYqut1VxChENAVOXEhzVpss1ZgSuhHjwuwyE50omkQVNS\nfDIOLlpLFD2QpnSi7SFxkKBfh1HFvEOoeIqWbTA+jKMrXiZ9A69ip8FC0TOQ\nCG8GtTbwTfNq6GMu8ekQjO2EcxCToMIEY8Ss+7F1pY3ZXXx5EtqtHiN11gZM\ndzrlh+MP8yAuW2hrddZakgzb4niWMbAHROY9gakj1vb32Np/5N91cnvSnTVI\nuwC1\r\n=SvmA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCks9X0PEXkroFrOm/Ghh+Dl+oE3j9PJ86SBYuiOxNnPwIgGuKK8TCrFxn9MO+z6eQ6V862KCsyFTnQbs+Pm3ESqhs="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.2_1595960947527_0.7869090414648483"},"_hasShrinkwrap":false},"2.0.3":{"name":"@terminus/ui-table","version":"2.0.3","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"10.0.0","@angular/forms":"9.1.11","@terminus/ui-button":"2.0.4","@terminus/ui-icon":"2.0.4","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.4","@terminus/ui-chip":"2.0.3","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.4","@terminus/ui-spacing":"1.0.10","@terminus/ui-validation-messages":"1.0.12","@terminus/ui-pipes":"1.0.8","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.9","@terminus/ui-checkbox":"2.0.4","@terminus/ui-input":"2.0.4","@terminus/ui-validators":"1.0.8","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.4","@terminus/ui-tooltip":"1.0.10","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.3","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-qXZ3AUuZyly0/O0F6g9BpHCEb+WjhdMVn1k+eZ4YQv8xpMFyEJbRZnQmHDC+hpBQgNc0c0y1yCkznKXr7mUrdA==","shasum":"7f5019a904d8ef03eedd1b943017bfc3e7321d98","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.3.tgz","fileCount":93,"unpackedSize":982938,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfIZcJCRA9TVsSAnZWagAARUIP/A0rOGKFnCC8CXqeerCY\nkTPtOjiwnflAxj4zGUWfuCTo6YHZhvARw7jy6DWoz4i1kFQ2KCy8Uo9M0mw0\nLToMiAqelc3CuOSc5hXXQvp+aS3U1MtvmUTPhFMMrgOLR8pbb/NQui7tKqlu\nPHZVY200Y5UwD8jPYobFtEa6DxBSLrKwnNrRjgWG7HEb+UkpnkSV5Ez2mWUE\nJm9a9jPa3ibtO3GuIcEs2hBjUsaeRrpGRpmQyB3r9W56ql+MDFtyHrUWiLhV\nZKp88afwuafKj5MpF2Ir/g2xMlmiCU44VrKPxvsTui3/FdH8C8nL5Ov2rgTj\n2ZDtb6NhaCkQEf/QPC8l8ItBj7k934QlSa7rOeMvY0wJQ2/drdEO7Krfu/N8\n6Q7NFbCEvDMNkV0IVBEUt5dg+nwtpGiXW+JE1JHQ90SCSLIbfCrm1tTKu/wh\nWOYFvaWCsfhuD50EblWbaHrGl8SWV7di3jFALbpkpzzaNOM0bWxADVE6uYzV\nETOvJWtY7iuBDnbKBIoclv+oKVOBADa8FwmN0jx3xkAQOXBxQCNrCFba6Lbx\neiyjeO4/1c9OhwH1FDSrg/cQT2DFgxP43otSp6aTuCBZYkxwCmYtaFpaOCW+\n9GbGdomJjbEg8jg0oIc1+R7vvOWse1ai+OmFaJh1t+VxvWXCUIj00r5uYITq\n5/V7\r\n=y0VG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGQXux9bQyaxdyry1Cc6kkqPkCod4xLRNcQInVa5Hpy2AiBBO5l6qFeYI6yhqcMr3GfMjXH7K1EoXwjVVcnrXKBvmQ=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.3_1596036872746_0.4698667695482879"},"_hasShrinkwrap":false},"2.0.4":{"name":"@terminus/ui-table","version":"2.0.4","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/ngx-tools":"^8.0.5","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@angular/forms":"9.1.11","@terminus/ui-button":"2.0.5","@terminus/ui-icon":"2.0.4","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.5","@terminus/ui-chip":"2.0.3","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.5","@terminus/ui-spacing":"1.0.10","@terminus/ui-validation-messages":"1.0.13","@terminus/ui-pipes":"1.0.8","date-fns":"2.14.0","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.9","@terminus/ui-checkbox":"2.0.5","@terminus/ui-input":"2.0.4","@terminus/ui-validators":"1.0.8","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.5","@terminus/ui-tooltip":"1.0.12","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.4","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-JgrQELyWfBkuh3otqF8o86BQvNlLo1G1sJoq/POCo6KjMkaZOtwv3H8gPcoQDX0blBOOWU+6DwbUhtc0tz8BIw==","shasum":"6a60697c5c4de700b2f95cb03080ea9796118233","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.4.tgz","fileCount":93,"unpackedSize":984780,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfIy7wCRA9TVsSAnZWagAAedAP/A7X8BeuHQ5EwWXZcDep\nmNXn+9BKyrmfLdx0tre8sqmmGJHyZ0GUwn4tuUdphofqBm3zQdMmyLIuXiGq\n5nNGyXhsNMbRQpBOo7gs9urjYj7yhrkAEjnV01/q+Ki86Vk8As7XJuMUR3f1\nPkWATaGjdfW2oB9iWNRxNI1zo19uhinJhfUUBd099ceZ9iw+m3NIXnZsHM9S\nkPHR22wXm1pWyPVumrAMb4qcCF3/G8gvV5anByMRv6S8C9twGkZECmxnlVLK\n/xodUQ+6D61FjWQfsBhjeN/SjgxiTmiARzAdLPYFCfgwOClDdNCTcF48FU+O\n6BfWS0lo2I9lM3ZDIQYgdCucbPKqEQlo4IRiOmkW9XyCyftNLnLiu484F/ui\nZ984wPRVWF4xTEkRDv4ePAqXS8p27DFVeJHCiGoMzm+cJBWIDLaiPFoQkWz/\nrwdQuyDb6puxO1/h43hCTeLz2vz7Tv9yeJcDh39Mp70aHvfKntYtmnAcvVIH\ntot7j32Vkxt4NN6RaK9dtybvNuBDZVvtCmh2OhzZ1ETuGIPDFT7x24uh8O97\nQaxU8wrW+K41YOdVscKuAa30cR621TYGdRyRz6jh6V/X0wy+Aeh+i2wvkphv\nxrJNW82WvNOf//tUeQFbLTa/my9Acu/Hmvc5X/4wLJZcaqg4IOAQWx84rysH\ngTRl\r\n=ZBsA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcfyS/GlM3YRynV8AF01NHqMp1WZnc+cg4gkTtqVAaTwIhAMrMwZpb9CsyKBZ8faACvkkcdpDhXiVmYptYKeaYbjlS"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.4_1596141295688_0.08347583639244838"},"_hasShrinkwrap":false},"2.0.5":{"name":"@terminus/ui-table","version":"2.0.5","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.14.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.6","@terminus/ui-icon":"2.0.5","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.6","@terminus/ui-chip":"2.0.4","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.6","@terminus/ui-spacing":"1.0.11","@terminus/ui-validation-messages":"1.0.14","@terminus/ui-pipes":"1.0.9","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.10","@terminus/ui-checkbox":"2.0.6","@terminus/ui-input":"2.0.5","@terminus/ui-validators":"1.0.9","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.6","@terminus/ui-tooltip":"1.0.13","@angular/animations":"9.1.11"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n   \n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table'; \n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table'; \n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by adding container around your content with `white-space: normal`.\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n \nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->    \n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n####  Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.5","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-oNMS/FuD8CqpsVTylJ9w093xa7OH38ZN+QdCpyH4Uv7it5eDkS0KG8mm3CHGGUXV3s66bMRajqnxzW03QILfYA==","shasum":"68f619324b0f06042cea889d40a8d727d2d51579","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.5.tgz","fileCount":93,"unpackedSize":983719,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfJCQ9CRA9TVsSAnZWagAAdsAP/2nwtrOzbwRrnH5aMvv+\nlbH+IHQpvzyoDGatEwlKhjEANs8r+98D9g89jqcxIgIdOgd0uMxOkJL9DlMa\nyKYqDSVj6R3UoIqTPe+Xg/fBqEMoArCZKjZbkS9SjW7xoa7HsF8OFQWSpq8o\nYd2cLwbcCvfy3FVHiJQ996T8GHq4B0CUBYxwVvV6h87fQVpyT6X1tiwkST4V\ncZOB6O0geBqlU4LdST49pyqOkhAhlFUrHstKfpo7s9xYh2OEM62FaGr4ePiy\nyLPI+qrGV7gBG0vHMwpYHArG+UYVQsb/HdRTJzJ71gN23HELlRHNyfZGQQrZ\n2YVZhP+b7i0eKNmDgVMRjdRgKj87o9yPP3nKZFJuzM3Azo2ERgb8xOoiSq1D\n7fjRNrDA8KaVKAusrDfN6wFWgXMl/CnIg0/ILKmcg6Xtl6s8YTx+2k7Hi2sK\neYTI4LIprIdLfQ+WKM2E/bcyMAaZrfGugNoy186v5jZMUwo5P+P6MUGZY3Dk\nLVZAPqLzfJ6Er199fhdW8jonsYzGeekCGRq2mQhIgBu8eyLRswPuf9ugK0Iu\nV4ncltIcOsZWk62wBsdd2vtCK95yfVcGltItoaPN7aKu1QP8NUIpXgw+fwi4\nKt2w0gAz5MMh6h6+Lwp3JTIWqeb33Zp59VCK80bxdSlZnA9FfpVs0Zc1npfZ\nJoEh\r\n=6KGc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIKFhv4vTjsxKEqcDJt2c1bolEEQ+o/RmnA35gmne+YQIhANVEcev5v3FMDjJkhqCYUQn/AcyGXbGoNQu+SQ3rnpiR"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.5_1596204093472_0.546486488000886"},"_hasShrinkwrap":false},"2.0.6":{"name":"@terminus/ui-table","version":"2.0.6","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.0","@terminus/ui-sort":"^1.0.9","@terminus/ui-utilities":"^1.0.7","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.6","@terminus/ui-icon":"2.0.5","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.6","@terminus/ui-chip":"2.0.4","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.7","@terminus/ui-spacing":"1.0.11","@terminus/ui-validation-messages":"1.0.14","@terminus/ui-pipes":"1.0.9","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.10","@terminus/ui-checkbox":"2.0.6","@terminus/ui-input":"2.0.6","@terminus/ui-validators":"1.0.9","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.6","@terminus/ui-tooltip":"1.0.13","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.6","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-4y4vIy2ujFBa/d6K8P0YPErnzdgnaoanOrV1vOeQmyV7aDtDVKf3WPDs3gyOYm3UXTFIfTHxq/eUiby3B9TAmQ==","shasum":"d7ec53529bbb6eb0a35712f4ac51324989cb5012","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.6.tgz","fileCount":93,"unpackedSize":984035,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKwSRCRA9TVsSAnZWagAADtUP/2YjrPsli+U0V5zC8fCw\neeyl3l1z0YW2Y96aPlAXHQcgzdcanp7KWyIwvai/Gg5/1x2x3zZh+8UwqGbh\nibaW/a5w63igojDtXLofonFOq4N02YuF1PhGZenbgUufQYGiFYJmroR4+FVy\ncwadmAVXHDkE0tQslkuzGZzPCF0ZuRM8OaSX/64t/4VgUr3ZbPJTkmcYHY/L\nAl/H67dvaIATlC2or1We2efB5KtWlaGorPOZ4WtIFTTExTahwRizwpeD1AaU\nfhQ9FgkYK736oqaRcImnuOQe3FSqCZZg4Mk+wqZ/PGNbGDFRg7LEGVcsnsMj\n/KwSTfKgyxgJ2acV/NhXMbZCBrYQ+14j0cq96qZVLJeylg46RtM32/htjigx\n7VCZosuBk81mLjgWDlTCaCs4nZBwR8/KVwU4l7OnVIEVeetIVpifZZkuY0AB\nZIsUZjGn6XcNsz6GJ00yoog7IZZUd1X4okt/NssEWogRMVKHi4QCS+rf3sE5\nCKMAXgOtNYS0RuwJZDjK0x4WHpj0Q9M5SHfD6tTTXAf4gkdtMxlogAFJGM/N\nSZR0VR5viPAw4JXmGHYisd2YA/RNsHTJj0x4oTr8PUQU+9lgl8BHc/14lsyP\nmMXQOTiG+o3vaix+PDEA1fg+MURSdHZQ+xY0097vKCZHxrfI0OX+pZIwyc01\nsK4L\r\n=k6e8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAfDDSqLW5KRLVV1UHwUN+cnnWGhgvYO45lk25tO2HDcAiAj4efkNdkn8estByKwyKOZr4Guy5kCN4rxkyyVh+HIwQ=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.6_1596654737350_0.7770773840698859"},"_hasShrinkwrap":false},"2.0.7":{"name":"@terminus/ui-table","version":"2.0.7","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.8","@terminus/ui-icon":"2.0.6","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.7","@terminus/ui-chip":"2.0.5","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.9","@terminus/ui-spacing":"1.0.12","@terminus/ui-validation-messages":"1.0.15","@terminus/ui-pipes":"1.0.10","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.11","@terminus/ui-checkbox":"2.0.7","@terminus/ui-input":"2.0.8","@terminus/ui-validators":"1.0.10","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.8","@terminus/ui-tooltip":"1.0.14","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.7","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-oJwNnIufx/vA/aCgKUT5lmtrsNiTuQO7Ae4K07w4adxpZ7TCIKe0my+6exL8x2qjLr8+y2k+HIyOrcw8Ki+vWQ==","shasum":"f11700169981ad232f4e2ba8492fa4faef8567d6","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.7.tgz","fileCount":93,"unpackedSize":984237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfMXQJCRA9TVsSAnZWagAAOo8QAJ+uBgBfZjogPyvkIwfx\nlCtLof9hXlWT1Zf1pGS4HmnLwUl1pARxoamYD0/MQg1dGRFuLWIN8389ZZOd\nXFAA2G7hycSHvdGe63spXZfYT4OiroieZpkIZpU9ocDvr+awzd+D5jqT1sPO\nfFYaxeEkQcr5YKN58Bgik4IX6qF3Ei4Z71/OeGMq6j3H1jvBqU9KBIRPhmj+\nBPzc759v2IzXBdfbpatsj9u5PcYSmqtj9wWac/nFDt79mmhlzLjGgR5zrogm\nVo30ssP9hfsCt3ufklBsclu327L9EZc0YxlWlMOWwc4EiA7LSTCUTCP3qn9s\npZgT0al9/Fq4rrJvJpTtVnjLpE8XK+ksy0Hoyd2PyvlS3lRTtTs2eTTACP5H\nUdhf3eJRdRQbCOPkQ/Oc0XnKzWqiJyGDESN7CZKoBhz/7kcslHuhLl1UP+Xn\nGnsoVbNPD8cGXOzKrqchAi/an26QDUrVsCN1MdTyzidL4Q/1xdt+HenvDtEm\nK5s5QlAe9G8CrmM2pzV4qV2IdfyabONV5IgwzoQWlVbDDSMp4Jbr1aFKVmFu\nb0iRW7SHTKSP85gTyF3EvvLOLz6RzcDNl2En4mT2/jBVIHnX1ExY5GvmNbbe\nN65wkUkljVElDycjXRa0OwoN/I5yh3UZBfEin/WjDTeWRht+qH35ws/kumlw\n/fFS\r\n=CNDO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmXYvg6Wt5lEVJDHSLQMZnHAxVto3X22IJIiS1PtP2KAIgZWxHYiHlDSUFkVCL4KE9IaGf5t6/Rm8Q8PPfx92G47A="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.7_1597076488937_0.7174683287682977"},"_hasShrinkwrap":false},"2.0.8":{"name":"@terminus/ui-table","version":"2.0.8","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.10","@terminus/ui-icon":"2.0.7","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.7","@terminus/ui-chip":"2.0.5","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.10","@terminus/ui-spacing":"1.0.13","@terminus/ui-validation-messages":"1.0.15","@terminus/ui-pipes":"1.0.11","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.11","@terminus/ui-checkbox":"2.0.7","@terminus/ui-input":"2.0.8","@terminus/ui-validators":"1.0.11","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.9","@terminus/ui-tooltip":"1.0.15","@angular/animations":"9.1.11"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.8","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-t9C4N8HhoqsB2KlmcEA38aedLS+xt2R/aMoZEzgO52u7zFpH1LrPJZVAc9l6fuw9V2P/t8CxgTfRO7Vo5dzr/Q==","shasum":"413d7a8153b386d24e6e8fec48aeb32a5d2c2491","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.8.tgz","fileCount":93,"unpackedSize":989753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfMsg1CRA9TVsSAnZWagAAfLoP/A8Y5eijNd9PijzCMf/G\nb33Rez6TdGrAxOZm25+hhMHMT/hTGLPyY9+NNpcLHTBnCXAs07VCkm2FegOy\nq7hXwokFMGSHog2gsBidEVIAWP/DuN4h4xNP3NKOmwIlaQCgqkJ8UZGAYic1\nOPUnJJsVM/j7N6E8wh4R/S76KmDJPXcfy5GfjPbjijZMCCjHnHSjBOO41FIQ\nzfExFCj9iwx7PIgjHpFUlTTha5n7+V1Cz9rdAVcYYU24ucv3SfzjEWRx7KWL\nVD4hAtPo8Zjlf5E7nJZRu4nL7c7/bzI0xh4CxLOL7ROz4EsWDSaPoO8p8Rk3\n9DreL8lNpof+AIlpnADPXR2mF7SzmTfRKs4nMXOixt7fVLjqkSlxxabIvHf7\nhHpmX/dHf/VRa9gWOe7tlaXB8tF3gcb/iW7DSZiA6G42sCzJxhhmXa0HYwiW\nesfiW75JebpvMRFQqNjOB5q9Zu81h4yrAX1ZMWvS+6imo29IX5tqQcSYWies\nV7NujU4TkYLp6ie4P33LjSG1Qa/10B2t6ihUW3FAc2Grs3bGKjLLmLt0jBda\n49pmO1svXboZa6J9x+QB/7GKBNlfhzJHGaJ4i8zGrAp1G8jY9pO6LJ3hhZ+8\ncLwBc4mEFOEt2ggNGzofM/k9RBbROsTD79gktdTtt2fFz+cX1l0WIje5nqQk\n7EDe\r\n=3Q1q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChBvxQBqZnt+pTCqJB7wLPaTsBmIlrLZAylLoXzerzzwIgfWR3bhZjxVpPja9Iba6FvsQrj8lT/DnN+lRM1Tvnes0="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.8_1597163572545_0.06979601277543668"},"_hasShrinkwrap":false},"2.0.9":{"name":"@terminus/ui-table","version":"2.0.9","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.11","@terminus/ui-icon":"2.0.8","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-regular-svg-icons":"5.14.0","@angular/material":"9.2.4","@terminus/ui-selection-list":"2.0.8","@terminus/ui-chip":"2.0.6","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-form-field":"2.0.12","@terminus/ui-spacing":"1.0.14","@terminus/ui-validation-messages":"1.0.16","@terminus/ui-pipes":"1.0.12","@angular/flex-layout":"9.0.0-beta.31","@terminus/ui-option":"1.0.12","@terminus/ui-checkbox":"2.0.8","@terminus/ui-input":"2.0.9","@terminus/ui-validators":"1.0.12","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.10","@terminus/ui-tooltip":"1.0.16","@angular/animations":"9.1.11"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@2.0.9","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-ybUtWy+d7rfqazvDYEFdoJLQ5Lap/pXNpAwnBeHKiAOCmZU+mSCwmvgP4Lbq6mWnSA1q7tIKdGpF3XwSMhZ+bA==","shasum":"44913fb93f2680661659ffc26579d6dc60a1620e","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-2.0.9.tgz","fileCount":93,"unpackedSize":989868,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfNq6wCRA9TVsSAnZWagAAF0kQAJuphFTPe1dGQLj2wOPM\n9TWhpPJGwLyf6FL9kFjPaGR8RJS33tNtNmBH2AArwE+/TS5OqnVR/Jc5hjr+\nt0S3PoUKvCkCPcnUqTjWoSTrLnYfPTDcyOGKyH81xE36413FoAtnj7K2AlT3\nYwpwXLSD+7kw3gAqmoNcKbQREg+BYnGBElsnePEftH2c1dpa7s9w8WckkJ1v\ntuinaInVXaGk+dHG2hWdrYpaxy+FB7ghX9YBRP2hr3Zp8ooXndiCsv79/WB/\nEhZUuqBZYr5DymJrtV655l1Qlo9rtSGJjo+5TcIJ9qkuQJxWTNvHL9wDmYmj\nKiidhq9e2EzvPRUGVE7cFMx0mqQjOAQfzuzXoek66SMjr3orgUjUJMYXxsUm\nwyG5v+YCM8iDFeNXB8NM/D4JObuydIxHOXkDydtG9bRP/w5Xj/loc4vr1sKx\n4HF5Lbq9XjaqE7GBiI92FdL7T3/edvmQSpnu95WUp175Zdkybja0GEddD91w\nglzIAR71fZI+VjON3QFvPwBxtcyNEjSHtp39cKal2kaSV234ihcI21iMM5Nr\nT1HOti3B0Xh2Qd/pdGrkdlrZ+6dekHvO+AVPptyrtDLUH5auCZnCriNQV6he\nIVtnH0LWWpCA0HfZttZfbf6rR5HvQOiOxtYtxQug85FyztnPuGBg1d79gtPs\nvjJr\r\n=fPJT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICA3FbBlPUIs5hgrn/CCe5+XquiDzMZUA+LH6+vgddo3AiAST/Il0N+4oAwlEJpDStk6NRHGuhKq6l3DNwx7yAzr1g=="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_2.0.9_1597419183771_0.37199552995636265"},"_hasShrinkwrap":false},"3.0.0":{"name":"@terminus/ui-table","version":"3.0.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.18","@terminus/ui-icon":"2.0.10","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-selection-list":"2.0.9","@terminus/ui-chip":"2.0.7","@terminus/ui-spacing":"1.0.14","@terminus/ui-form-field":"2.0.13","@terminus/ui-validation-messages":"1.0.16","@terminus/ui-pipes":"1.0.13","@terminus/ui-option":"1.0.12","@terminus/ui-checkbox":"2.0.11","@terminus/ui-input":"2.0.12","@terminus/ui-validators":"1.0.12","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.13","@terminus/ui-link":"2.0.13","@terminus/ui-tooltip":"1.0.17","@angular/animations":"9.1.11","@terminus/ui-expansion-panel":"1.0.23","@terminus/ui-card":"2.0.12"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.0.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-twwqrNOMkg/oFZq1PsQZtuoyxQQfpd60SVAaGaIjiAbmyC6c+Y8octZ5T7G8Rq9IcyDF9GjX9qwMWediJVOigg==","shasum":"b7219a21bac34df9e16b61b956f9f42405bc2584","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.0.0.tgz","fileCount":93,"unpackedSize":990671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfTUTDCRA9TVsSAnZWagAAoacQAISsEUnLor3mE/xi7MTA\nKxcwT2bCX/OJ24sUz2pUAPw+J/pjGQD1R6wZkA7VEyVBAuLZqkeXK4NdslQe\nvfrc+fyVq5Q3hY9VH2XMgf/vV7Rl3a+JKzT3GF+oWYtZtWkcU1P/MJppTtyO\n5CsaX9ysfgik60djRV3cheOf9E1DOSt23kgvIJCYcCO+x+VfubafAf7dWQ5h\nqmViJ930R+NmVT9tQi6ujxwqKK3QPjxnTVJeQf+dfX+BLbWk4JuUr5p4FaYl\n/m7gioLZ/1TBb6nPGAH95zhDHjg4fRA56Kbxd4dCm3YJhCdSBYnyX9uIL0Fm\nWeooXIOPHcnoaZCscBJAOYtSMTcWTIPARMXZnIjSdhT/6oU4zOmWC6cYZ7qc\nuWs5ROyPKRj/wETWmmMPe7Vvd/nUOREWi7d5DwxE6jqsvig01svcHr+Gh8sO\nHPvqB49S1ITMHquQYI5l6WDjhV1hdtAAiXr95leOOtySFE+WGj+ph4LciXmq\nOLHa+5AgauTBuVBNcdhA/6iEjb0DukPNGHv1KOU7wRujN2Q5pDBRd969eQyN\njljq3VJdAsc4BU9XhPvb58Xor5isIkdoqD6U083Ran8ukDGW7DOJY4/nsjKw\ndCFANOM9jcEBZY1TNVufYYIA+2J+fquD6jJ9zjW/ozIeBitTO33VQ3/okqV9\ngZK3\r\n=28vD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFqXrhHnhfUPmBwSD459o1rLYLUHZacVrPL6H9bQnuyQIgX7VqZacGys8+HFIOvOUAHx8/HNMoOEEFuLxmtYjpZaQ="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.0.0_1598899394813_0.8722848197094009"},"_hasShrinkwrap":false},"3.0.1":{"name":"@terminus/ui-table","version":"3.0.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.20","@terminus/ui-icon":"2.0.11","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-spacing":"1.0.14","@terminus/ui-selection-list":"2.0.10","@terminus/ui-chip":"2.0.8","@terminus/ui-form-field":"2.0.13","@terminus/ui-validation-messages":"1.0.16","@terminus/ui-pipes":"1.0.14","@terminus/ui-option":"1.0.12","@terminus/ui-checkbox":"2.0.12","@terminus/ui-input":"2.0.13","@terminus/ui-validators":"1.0.12","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.15","@terminus/ui-link":"2.0.14","@terminus/ui-tooltip":"1.0.18","@angular/animations":"9.1.11","@terminus/ui-expansion-panel":"1.0.24","@terminus/ui-card":"2.0.13"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.0.1","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-OLlTHCci4DbIAQcZPm+EbWQBvLpygVj3ZSiZXdgR6Os7cIzlePu7LVj2y8OphB2sna5bpu+hlPsMAKXzZzp2hQ==","shasum":"a9e8cc0591ee7b9312f72476d6126a4d6f0cb220","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.0.1.tgz","fileCount":93,"unpackedSize":990866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUXktCRA9TVsSAnZWagAAFhMP/37a72MsPFIuQbZCML9x\nFPaVzMjXuWYjHNHb+M6lWoBqa5Oqu/BqCo+h98Y5VcS8f+ZrE56A9dTUFxcw\nW1hKlSF3uBIraULghc+bHLqFkUn/YHQu918gM1KhNF+EY8S9pZYUpuni4aqs\noRAwiFFRqQ8MZmlgr0L7ELVgLyYyVEyOpqIgjQfILgdpc4tKwNfXloraaoFH\no0/tXa3GuLUHMuz12MYCcPZl0xSxaAFkMt/DI24uAIzAWYmego5vF+HVa17o\nf8NnIfkzp9Cpsw22zs8CJWwdbQCXIobf2lxXeWtwWW5+ET+XQqkOLIoPc7Ov\nfEceqx1sSZfA2/3KpJRZZ3DO4g07Mgv844mdIxrIpgqudUy/50BbT/m5w23J\n/pndQPzLDVy6Iix0RzmWW8CBF43LT0/VnAuZYLIs2kzBYQpvAaxu6FTF26yr\nB6qJJQibQkPynrhsvJ24td4RXiVE9j0rYeQnQd3zdMvz+j2LhpVq+NZhTDzQ\nN5lmJQ+05+peDZNgkAUX6ekyumXUrrHJLQHU3NyO8mPgoWwbJHEmuCCoETLj\nivFOthFZQxIJ+r9dahHzAR1JSpBQ8Bws72dX695Kn5DlZlIJ7hRp317w5VUz\nQRlexyXyrp1JT1Rj8L6asz8nU54RgN5MNr6vXFHe2jCZ6w0NCOzs6z3CUsJb\n8X5G\r\n=CPT8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0HxVAABJUH//XU6PAJHwzfl3wOOOpoMdva33ME29xZwIhAJ/6THYa0SDoFP0UzuMJfvtTjLyZsFoLz7Z3Sr7cISa+"}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.0.1_1599174957197_0.2981497963591828"},"_hasShrinkwrap":false},"3.0.2":{"name":"@terminus/ui-table","version":"3.0.2","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.1.0","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.0.21","@terminus/ui-icon":"2.0.12","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-spacing":"1.0.15","@terminus/ui-selection-list":"2.0.11","@terminus/ui-chip":"2.0.9","@terminus/ui-form-field":"2.0.14","@terminus/ui-validation-messages":"1.0.17","@terminus/ui-pipes":"1.0.15","@terminus/ui-option":"1.0.13","@terminus/ui-checkbox":"2.0.13","@terminus/ui-input":"2.0.14","@terminus/ui-validators":"1.0.13","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.0.16","@terminus/ui-link":"2.0.15","@terminus/ui-tooltip":"1.0.19","@angular/animations":"9.1.11","@terminus/ui-expansion-panel":"1.0.25","@terminus/ui-card":"2.0.14"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.0.2","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-9FnHbDWOENtymribGbTMKukDK6OFYQIHnuA4zdJ3rakQtCr+1Ylc+PRode6C2dZHj03ZYeV1iy251pGKhb3X2A==","shasum":"838172c8b11ba457396b46962161bdb1cee4db33","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.0.2.tgz","fileCount":93,"unpackedSize":991082,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUlcdCRA9TVsSAnZWagAAdRgP+waQzIrWN4nUWY//Acdu\nfFOCkEYlIqGD3Lqqlyh+53KGNi+MjGryX95dfaVCjx6w1FqX7XgV6KC06NOC\n4hrA9Tz0jFD4jvMLkPvY7XP4v1i0kNwCXk3vTIobvH5c6jyJxw6IybJhnFAy\nF/GwMPTnx1AoQYy76cpdyJ7234wskX/H2c142Cr39Dq8KOm4E03BCyxDYb7+\nceZ4ZMj2FLGWupQyUntoUXz2dqnWWbzx12PDRF8NU9j2fp4pveG6nxFXTXQK\ngHBnbdYp1PJuuVCAxipnQdTM/+H/sn+33edbDrqOSy5VL7209O3cD+0Se8nO\n6V8onZut1CQUzXwcdM98+EhBZCNYbGLh+pTlln6hXz94DLpJpU0sJeHhxOEx\nG9XPD4kC8C4XpebZlSl/9QtJ0K9WPs0EdKQoor0uVxaASnouzFlUWOl/Yrt/\nly8JZJb1an8suNsFuqP0rr+r1nhcWKkT0kGcXYaH6SIdlX7X8aaqNaOkqT+L\npi8uTMozxwodAcUTccW5CrnQtn7FLRdskVB//yOlvss91AubiqXbQlJBMICQ\nZwR7Il6QEXbxAORGpvu2OqGdho3iTYecnEWi13frVw0h6rX5iCKhEmA3RCFu\n0JgakBV6gSYjIYI9UwuYFdtqsZm2lJDIKCmU/lu16zlBHKzLmchomiFNWOGF\nZLHc\r\n=5GXZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFbPz/R7UqmCqY6SaACJhM/IUBonWhmuczo5niCdsjrAIgTu3kvX3zRTwJWhljv29P3hDpg7rYVj5I0+JaMqA1UoI="}]},"maintainers":[{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.0.2_1599231772976_0.3905642002273717"},"_hasShrinkwrap":false},"3.1.0":{"name":"@terminus/ui-table","version":"3.1.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.1.0","@terminus/ui-icon":"2.1.0","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-spacing":"1.1.0","@terminus/ui-selection-list":"2.1.0","@terminus/ui-chip":"2.1.0","@terminus/ui-form-field":"2.1.0","@terminus/ui-validation-messages":"1.1.0","@terminus/ui-pipes":"1.1.0","@terminus/ui-option":"1.1.0","@terminus/ui-checkbox":"2.1.0","@terminus/ui-input":"2.1.0","@terminus/ui-validators":"1.1.0","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.1.0","@terminus/ui-link":"2.1.0","@terminus/ui-tooltip":"1.1.0","@angular/animations":"9.1.11","@terminus/ui-expansion-panel":"1.1.0","@terminus/ui-card":"2.1.0"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.0","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-eRqHr/k5bCDNOLZrQbexAmLfVpSxIhS8HNeukBqQGC8PEaAtTR5kmBmiB1EwYxK7opM7piBZkJ6oNz5KGaG37w==","shasum":"aacc07f8a37d4d6fa7fc466923ffbe4303a38311","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.0.tgz","fileCount":93,"unpackedSize":991528,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfV9MZCRA9TVsSAnZWagAAwpYP/i1Ax4FRJvxxZU0aG19v\nyd5wdoGAWFCGdIErnGAoAZ/dtVVvkWbqxN0MVNYb3TtfaYZ1eEuouD0s0yDk\nteeX1rGFinMKAYVWCq0+PQccGnm4JoqnUMPyYbzuAq1Bz4hodI+25joMYIbO\nNDu6OsLCcBpaFENzb4/ElrXojgm98kAeclhPbNDFAT+Mq51gZNt6Zua9cVUc\n4hgrIylEqRbfXnzcFGV4GYHgVVlb3e/5es0427ZPJckvNhMmrYfdu7vJRUJu\nc0tr103z4OwyLcfjYYct+GKTi3VdLumgBu0UcrUwY8pDeYlqBhYzEJuIkA2x\nJFdYWBuzclvjsebcyMm9pK1utzlriS0c4U2im4m5sNA7jmr35vC3XdGJC9ic\nw2+V8/ETm5ojXsBcPU2mV0Bnz6vGyBzCL2JdoaNQKAtWG0SOj8mAsmHNVklQ\n/AFHCFAYvRe5rmrEAizYWcxwbaoKOvStZgMApnPqFpMxRTxKg8+0uX8DBTB5\nvCd+Nat2lYIP8Evl7DHz6dinstBhmkfsv3H094sOZqlWXdyDv1DysWzbpwCd\nSHObQa+68d6jlgSySOXnEEc+CkGhXX3Mh9bNXWyyN2tpognD8gytVq9K9SDt\niwvN4QnHhPNrS3afU78rQjCNnzBs5oiHLy5V8nyuj/FSvcxeYfLRkv0VnQB6\nmalg\r\n=7Qdz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDt7Mvl/VLNaDsrAQ4md+g/qTtts2djoJFusthxJXHfkQIgT4vTz5GU6Yud0yYL6UxvRmjFKSNMrhD3tSXzp8LFZlk="}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.0_1599591193452_0.6001670157394485"},"_hasShrinkwrap":false},"3.1.1":{"name":"@terminus/ui-table","version":"3.1.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-button":"2.1.1","@terminus/ui-icon":"2.1.1","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@terminus/ui-spacing":"1.1.1","@terminus/ui-selection-list":"2.1.2","@terminus/ui-chip":"2.1.1","@terminus/ui-form-field":"2.1.0","@terminus/ui-validation-messages":"1.1.0","@terminus/ui-pipes":"1.1.1","@terminus/ui-option":"1.1.0","@terminus/ui-checkbox":"2.1.1","@terminus/ui-input":"2.1.2","@terminus/ui-validators":"2.0.0","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-menu":"2.1.1","@terminus/ui-link":"2.1.1","@terminus/ui-tooltip":"1.1.1","@angular/animations":"9.1.11","@terminus/ui-expansion-panel":"1.1.1","@terminus/ui-card":"2.1.1"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.1","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-PIM6Ify+SmX95bFF7TzUo9PNqMDgBRZBRdb6So3uCJoUJG7SJs1j++pG5+RJpFN8fe/rnHkxu6uSxO5zLoZsbA==","shasum":"422a2100b634f02130cd8ae41cb09065ea43f617","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.1.tgz","fileCount":93,"unpackedSize":991722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfW7WCCRA9TVsSAnZWagAAeBIP/1/VKRYBPXaxGA8kv50H\nUH2JydKy8VVYCs2UXo+eeiHxxDaqwN7Qf8ZNd6CPpL78/dHdoz2FaU5u91TM\nxpHhqwUCt1zAuop+DRNON8Tx6gXN8fTINygDQ22QmPV2i/gBWH2MAQFnI3Av\nBtP9CQ1YG1RB6URQ6YR8GxsRGi8CfL7rfjFkQFhBLqoLcvvgbFrqw5s2Ikhp\nFoR5YPcY5N1+96QsN8aoAv9fiMCUI23uMhTig2nbq9fspZzGHM9DNWcezfZv\nco8Mb8IWJSPuuQqw4qLvxaESGd2FobcZngUClFVgOjZW15EXzKKtiG9waQ5M\nsgDAsJdWF11Fdv0nfSASo+4v4gT6yHI2uzg56XW9u65dUkDnXCTN2Ey0n1IU\n2d2IM0E2owDsOTFt0eR3AwiWiurAsCKx/1d82uNEixOx2hNBqzC3FZwVXgCF\nEONxg0zeaDcyuZLqLaZBmuuc9Ht50RaP8F5UX4bXXrLrzdzR6shd09i0v3zO\nlXQd+HGR5xeCMj7oRa4SACJglB1EHP2LF2iiIMXF5ol9kgwDERq/LWyV9c4E\nZIN/wibBWmCnz11gYVeg22O5ehG3WjcBfkOZd9KfjwDe16gpNEB4eIz9pqjb\n0/D6pqgXcQ1VNGsr+301U2FMhVACwLeZOgKuaFJu4W0BNEdmKjM3Qkhv0jfm\nrSZX\r\n=RPVM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRgcS8sqAvjMaMKhN8ZDz5m1nuAO0t4sTUrPjgnSdC0gIhANHULaKBli7Verh1ee1sfRk3Twk7tKGlK5Rznj5040WH"}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.1_1599845762205_0.46544372463685835"},"_hasShrinkwrap":false},"3.1.2":{"name":"@terminus/ui-table","version":"3.1.2","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.15.0","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-icon":"2.1.2","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@angular/animations":"9.1.11","@terminus/ui-checkbox":"2.1.2","@terminus/ui-expansion-panel":"1.1.2","@terminus/ui-button":"2.1.2","@terminus/ui-spacing":"1.1.2","@terminus/ui-card":"2.1.2","@terminus/ui-menu":"2.1.2","@terminus/ui-link":"2.1.2","@terminus/ui-pipes":"1.1.2","@terminus/ui-selection-list":"2.1.3","@terminus/ui-chip":"2.1.2","@terminus/ui-form-field":"2.1.0","@terminus/ui-validation-messages":"1.1.2","@terminus/ui-option":"1.1.1","@terminus/ui-input":"2.1.3","@terminus/ui-validators":"2.0.1","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-tooltip":"1.1.2"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.2","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-YTPo6RbuRyU/yTNGCITKgDO/b/uQZPKhdcoc9+skMrN7D3OPrTWJM4SUQnTbjA1t8FMXPe/q/QjUDDjUVS29Cg==","shasum":"3428a1ce7b7ba3dd5f8e38ef3d2c5ec85cd2cc90","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.2.tgz","fileCount":93,"unpackedSize":992336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfX6rUCRA9TVsSAnZWagAAEtoP/jWt9CVYxae7ZKRCyiQP\nTxKh+V3xfqMOdvqaihHEkf6Cz97Nru9mhmRBnmsrS4HTwUogWy0amCXrTMEJ\nKJqclZBU4JFjdSyvj81z6f0ZVc5Yh+r+kFbBf/eP667xbC46b35lbkesU1xF\nz5gQZgSkj4Gm0vLpu/NpjMOYMvtZQSpEiSV6LL1bPIZ5TPrBAPSUC69Mhv6L\nZpafwpLZ5nIA8iQnoPHdYQIvDnicNrrBsGQ9T2MRHE1fNKJjHMB9LK4tHDRb\n0OOzUS5Mr5P+CxWIC1BSTTJeMGDeOVWYEJrHTyaIPgmuzH3LfqZj5el9+4NS\n6bzcTvwOSrWn2JoDpaZetcE5BEE5yX7/8zhuPe9pM2KsdXw+VVf82vN1YcX7\nNzrtmiiN2Mc1muE6CJpyK8YOeDMcD43r304f9iJz2HBItHLYrMPeHsir3v2e\nBF8f+75kVuRwVq1K71UpXb8EJarx61m57gGT4gq9VltVCe8XQSvooep3MQxl\nJqQgTVHZdzIXrpCxQUrYjnBRsmMrldj9G17GSMfKfdOlIorDmUe2gDRRFQrH\nWvrSmL6tw/WoeMxBBBhkXkbJu3sTlNxX9JpmvgwyZ+BqrY0rChhnTREbKcED\ndycRv7Epeg3+nw+MF6OFl4P/chGewXeUyY54Pa+x7/iWA/dqryEPjRzymRwZ\nKKRl\r\n=v/+K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCe0FLcGwhKUZKLdKy0vy4ufvfvl9qD2b9oScTTrBP29gIhAJCuJWtJDDy+KBJdd+UUgwXFri8yzSbmXV9Ktc+Jb5sd"}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.2_1600105172350_0.7429072012042521"},"_hasShrinkwrap":false},"3.1.3":{"name":"@terminus/ui-table","version":"3.1.3","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.16.1","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-icon":"2.1.2","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@angular/animations":"9.1.11","@terminus/ui-checkbox":"2.1.2","@terminus/ui-expansion-panel":"1.1.2","@terminus/ui-button":"2.1.2","@terminus/ui-spacing":"1.1.2","@terminus/ui-card":"2.1.2","@terminus/ui-menu":"2.1.2","@terminus/ui-link":"2.1.2","@terminus/ui-pipes":"1.1.3","@terminus/ui-selection-list":"2.1.3","@terminus/ui-chip":"2.1.2","@terminus/ui-form-field":"2.1.0","@terminus/ui-validation-messages":"1.1.2","@terminus/ui-option":"1.1.1","@terminus/ui-input":"2.1.3","@terminus/ui-validators":"2.0.2","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-tooltip":"1.1.2"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.3","_nodeVersion":"12.18.3","_npmVersion":"lerna/3.22.1/node@v12.18.3+x64 (linux)","dist":{"integrity":"sha512-4K/KzLJidHDa/8WIGnvMGRq8+vM2hnRBvPC3mnO3f0qtMQA8x60lqvlxYni6OMuZEX8lBy4aF6pOo3O3sl7hDw==","shasum":"b0026c1c3354112ae02341757df06b024ca3f10b","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.3.tgz","fileCount":93,"unpackedSize":992530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfY8a5CRA9TVsSAnZWagAAK6oP/RoPrd1F6Rutv0TFVNbC\nV0ZNNfZIBU18vg5DmOiwt5upUPhzysx2JtisHqfkkSV+Ezx3SoNeCDK/HmwR\ngn8u7ElyT+RvMPsdGiGTcBkR/W3spXhFkC1nCqXN5xR52D2aguMOlV68FxS8\ndFkqu6LYJnVb1O0XbMdDUhdtr3fFBwCumTnp2boYVwJMLslMIh7Kr+hLZIc5\n+fky+E28QCQyTE4IE7/7Q/m7Nzmwgpu3CVTTiti+0bJOpAb2QZaiuAr0/e6A\nMkuZWd3//43nQzQNjuSccOqSbE8rLIstsoAzTInnDUJ33ERX7MojcsQqKieM\nzZJj3sNQeLNgQq7kNIN56/qX6Vgin/8wpqNN2jbBeZxaFp/iosJ4wDxxCPyb\npvOT6isIChpu0kQrl6/omyVqOYhn2xwZEqupbMWBRMz0ezFHLWwycQRW4F0n\nWvho02aVMN+t8/RXBH2Qz4HYvaTE8WN2WlPmDWeDN8wBeIPIqAIZPbDzQ61l\nDw68BHNRj1cgxw7vLP4wpqO0DA6Q1F2PG5GTMplhpYKJL4V1wsKNMl1iq6JM\nUMtCQ3hRRP7uMSzSEfhuYWkbcijM1D3wYmueQ9s81vapB6HWu5yjej+79fUI\n7r56od+nkDRgHGVExPlflZnrWvmGV26gIwNqzHAMAyq/QKiX7EWETwWKHDWu\nTUIV\r\n=pcNH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDK3RV+VLLHaLrBnKaHZJqSNPKDvOW+9FUZLMFD0bvdmQIgHl/DsWD1x3xP1gU0o3Mpf+lZRwSiHbL/jGgns8I9rVE="}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.3_1600374456891_0.28744888573975746"},"_hasShrinkwrap":false},"3.1.4":{"name":"@terminus/ui-table","version":"3.1.4","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.16.1","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-icon":"2.1.3","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@angular/animations":"9.1.11","@terminus/ui-checkbox":"2.1.4","@terminus/ui-expansion-panel":"1.1.3","@terminus/ui-button":"2.1.4","@terminus/ui-spacing":"1.1.3","@terminus/ui-card":"3.0.1","@terminus/ui-menu":"3.0.0","@terminus/ui-link":"2.1.3","@terminus/ui-pipes":"1.1.5","@terminus/ui-selection-list":"3.0.0","@terminus/ui-chip":"3.0.2","@terminus/ui-form-field":"2.1.0","@terminus/ui-validation-messages":"1.1.3","@terminus/ui-option":"1.1.2","@terminus/ui-input":"2.1.3","@terminus/ui-validators":"2.0.3","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-tooltip":"1.1.3"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.4","_nodeVersion":"12.18.4","_npmVersion":"lerna/3.22.1/node@v12.18.4+x64 (linux)","dist":{"integrity":"sha512-Hh6K0UpUZUpUeaqRca4i9QBFVJfYvD0xWO8EoqZuZg83Fx0oU+nKd46xiiQHYDdWXxB4h/4R68zNf5QFXYtw9w==","shasum":"c95fb07640e25fb704494e52871eded55bf0bc8c","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.4.tgz","fileCount":93,"unpackedSize":1002364,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfe23HCRA9TVsSAnZWagAAdCcP/iKgNcgQQev6rIkW9ABi\n540Mviy4pAhI+c9W8clDOxlC3oURq5ccTaBuLA4JM8WCCDZZVeAL4S+wnR/A\nCp20nBARGykJfdsBWJiuCNkj2wQLAiqqX59AATV2RIKTeFc0BpeXeqeZlRG3\njZTaqgUY4jRob1xNCceXesGaceZv8jMA0cCB1xuBgT2ErsCtTB2D5/+iOxGf\nKjcH9jL+G0bOMNeERQ+2zQSRwU8Mr/ea/zEquIens1fTlpf8vrFsBuA5YsZs\n8eIyl3LVT40ALTuGMWrKQERjnWfoNjA7EyqbUYZ2JaOTpKp9eVVY/RNYBaQg\nMIhMRm7poUr04Wse2tRp4fynzvxA/9EsPtZFmecyv9xcwnOVYCaXv/TvtYy/\nme1Re5YDUJh/Ghe1qcYFKL6gmfvBHqThFLDdm4mbb/SOUEbeZd58mos4vI2j\nVE8GWWDskAwVwVqNEYr25Vd9s+TeQFxNCUDTIdPjoJKZ2QxmEE3mMe+k1OWD\nbw4IYc2Lve1acZB7TMjRJ7q5YPE1temERhU9O8DKAk7urhfm+d5ImaJb2nb0\nVCakCk7MeJgh4VDzWzcDcsxoE99zN5VtUEDS+9CT066DLLrrI4ayRipJFDfz\nlj7gpt7/+1EbN42EiphkHyV+lcS8h/qeD37tWpUQTs95FYhEdzAj8+vKX69U\nz3k8\r\n=C8lH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBt4QI+gm0Af0iZVutUXHiZv3s9qM/VW7IayIWeED9jrAiAluA5N30hwH4qa0UAAvRx9iYiK9i/DI6HunC9T4tiUhw=="}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.4_1601924550822_0.34274918680088695"},"_hasShrinkwrap":false},"3.1.5":{"name":"@terminus/ui-table","version":"3.1.5","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/platform-browser":"^9.1.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-paginator":"^2.0.5","@terminus/ui-sort":"^1.0.14","@terminus/ui-utilities":"^1.0.9","rxjs":"^6.5.0"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular/forms":"9.1.11","date-fns":"2.16.1","@angular/router":"9.1.11","ngx-webstorage-service":"4.1.0","ngx-clipboard":"13.0.1","@angular/flex-layout":"9.0.0-beta.31","@angular/material":"9.2.4","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12","@terminus/ui-icon":"2.1.3","@fortawesome/fontawesome-svg-core":"1.2.30","@fortawesome/angular-fontawesome":"0.6.0","@fortawesome/pro-solid-svg-icons":"5.14.0","@angular/animations":"9.1.11","@terminus/ui-checkbox":"3.0.0","@terminus/ui-autofocus":"1.1.4","@terminus/ui-input":"2.1.3","@terminus/ui-form-field":"2.1.0","@terminus/ui-spacing":"1.1.3","@terminus/ui-validation-messages":"1.1.3","@terminus/ui-pipes":"1.1.5","@terminus/ui-validators":"2.0.4","text-mask-addons":"3.8.0","text-mask-core":"5.1.2","@terminus/ui-expansion-panel":"1.1.3","@terminus/ui-button":"2.1.4","@terminus/ui-card":"3.0.1","@terminus/ui-menu":"3.0.1","@terminus/ui-link":"2.1.3","@terminus/ui-selection-list":"3.0.0","@terminus/ui-chip":"3.0.2","@terminus/ui-option":"1.1.2","@terminus/ui-tooltip":"1.1.3"},"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [Packages that need to be installed](#packages-that-need-to-be-installed)\n  - [Modules that need to be in NgModule](#modules-that-need-to-be-in-ngmodule)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n### Packages that need to be installed\n\n- `@angular/cdk`\n- `@angular/common`\n- `@angular/core`\n- `@angular/flex-layout`\n- `@angular/forms`\n- `@angular/material`\n- `@angular/platform-browser`\n- `@terminus/design-tokens`\n- `@terminus/fe-utilities`\n- `@terminus/ui-button`\n- `@terminus/ui-checkbox`\n- `@terminus/ui-chip`\n- `@terminus/ui-form-field`\n- `@terminus/ui-icon`\n- `@terminus/ui-input`\n- `@terminus/ui-menu`\n- `@terminus/ui-option`\n- `@terminus/ui-paginator`\n- `@terminus/ui-pipes`\n- `@terminus/ui-selection-list`\n- `@terminus/ui-sort`\n- `@terminus/ui-spacing`\n- `@terminus/ui-styles`\n- `@terminus/ui-table`\n- `@terminus/ui-tooltip`\n- `@terminus/ui-utilities`\n- `@terminus/ui-validation-messages`\n- `@terminus/ui-validators`\n- `date-fns`\n- `text-mask-addons`\n- `text-mask-core`\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### Modules that need to be in NgModule\n\n- `TsTableModule`\n- `TsIconModule`\n- `DragDropModule`\n- `FlexLayoutModule`\n- `FormsModule`\n- `ReactiveFormsModule`\n- `TsPaginatorModule`\n\n### CSS imports\n\nIn your top level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.5","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-v5CVv5tVAaUNZbQvvxVgDqsIZvwwR1WuXGMlienENy5D40f8ehTmWTTdAbvW2VS3VaPCt6yGFBzBnYJEv/+98w==","shasum":"df86ba89d68626b05dee7ac98961b1f2d266ffd3","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.5.tgz","fileCount":93,"unpackedSize":1002597,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnECPCRA9TVsSAnZWagAASeMP/1EMvjitBkwUeH76oAZ2\n5/o5oJRQnpbH1VfUAWZI5aBN+sDNlV06nqYoRSxInG5CN7fIKrTxCXrmfJVv\n5Ai8/wFJaO1aESGZO7uh4+toBLz7XeY21RlNO6wLdhu6+KiQ1TymJ7KRy6al\nB/EepJC2GoAzUgaQHDw9BoezR3D+V9tyhyukpfwes0aPNxnjOAnPYshuW+nc\nMNzI2AQmTZiDCyXAEk2J34ReRBxwRLL7TkfsoPeZw/XNVm/REWZllHiP/k1J\nzgT3wnpyHnupqi/ByEIWs7eWmmQUBS0+dbOIcLuPvpo69F6a5RKCTc2AWQcn\n1roBMn/OpqrhhNbKCIQAQIceEgFwxHzREbnajKiP1poo8VPRkEF7TN6BhZZu\nt0i4M+WP/YeXdR9+GmwuNV4VCnCxb6Hf9+SkjHBGGankhm0ifIjfES4QbKQq\ni+46vSqRE8VrLxXOn3q0/ZWwJA4fuWhKCcBADdOd64OYbZNujOS2uBV68OVz\nbiToKH2TnC8cJhEesd7z6Tpe879HQMQFGXDOLHk0j/Ji23St9OpZatc8Ryct\n4tiw+LyxPZWxJIlB/XJX4GNFgYqvTlDOXMbwKcjgkCq3miN9UoiGECVISel1\nrQ3gCX92S8NPTeMzaADCxf+RFqPrj0+52upHeqgXIj5KR7lzf1IDFSjBFy+A\nntE6\r\n=sml6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBYwGlten5bZuCp+ryDIb8BmCyqED4SwJ5syycAM2u6eAiBYtNgRU3ulYiDHyfr8tUnAeVVRO6+8SOP0cSOv29GMGw=="}]},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.5_1604075662896_0.8316351045087402"},"_hasShrinkwrap":false},"3.1.6":{"name":"@terminus/ui-table","version":"3.1.6","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/animations":"^9.1.0","@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/flex-layout":"~9.0.0-beta.29","@angular/forms":"^9.1.0","@angular/material":"^9.2.4","@angular/platform-browser":"^9.1.0","@fortawesome/angular-fontawesome":"^0.6.0","@fortawesome/fontawesome-svg-core":"^1.2.30","@fortawesome/pro-solid-svg-icons":"^5.14.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-button":"^2.1.5","@terminus/ui-checkbox":"^3.0.0","@terminus/ui-chip":"^3.0.3","@terminus/ui-form-field":"^2.1.0","@terminus/ui-icon":"^2.1.5","@terminus/ui-input":"^2.1.6","@terminus/ui-menu":"^3.0.2","@terminus/ui-option":"^1.1.2","@terminus/ui-paginator":"^2.2.4","@terminus/ui-pipes":"^1.2.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^1.1.3","@terminus/ui-spacing":"^1.1.3","@terminus/ui-tooltip":"^1.1.3","@terminus/ui-utilities":"^1.1.0","@terminus/ui-validation-messages":"^1.1.3","@terminus/ui-validators":"^2.0.4","date-fns":"^2.16.1","rxjs":"^6.5.0","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.6","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-Pbe6k7ClHc5CIe3GZOeCA2xOlLQlo1EZrtVm8TJ/dnh596sRblZ0MjbNDwqaBFVLiV8692L5jJpoEPFmWGjaoQ==","shasum":"b9170d3ea2e5e30ca9d736f59e1be42d4778865d","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.6.tgz","fileCount":93,"unpackedSize":1003101,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfq9M5CRA9TVsSAnZWagAA1usP+wV9DbunH2TIbItL967G\nNNIZvtzmdZyc295UuEWARrraxYaqNum4j2Cng7EroNoqyx0y98/I2PNtwZtO\nXA3weypE5F7B8bJq9CnC5Rn93UOwR7GiFijR1JzezSNBDpCzEbctx4Ngrfh8\nbsVADmFBXLGNzuWqcRfZxiF5QJf1K9jwJTU37B2Ig/RWNqSlKQBjUrI20C5N\ngA17IlBaxfP5kH3B+M7PVC+pweMcOVc6dOMIxnT3295lqptTvpbGw8iMXW2d\nd5BHOsgCHLaBc5iogwn1lcHb73PIOVQrLxGGwaLY5dQp608x5HRIEYxiN9WQ\nQ5VuGASPEFua7BQnbhICCdd86vJs2Ez0oO8DUiB6TNFjB8MJV9AlV/cmQPI8\nQZt32mLWmY4BYT1M8R1jGGYB+cUJe4W6dKCdCLBeAjBwlKF6LeM9S/IPUZ8G\ntFvkUcgqkjIxi1ol0sq/ZURDKto6Bt5mr+ymaRrgf+n8HFAir4NVlUNR9ome\nCxCDpkhXDl0wBy5FVOef4gL+Bjl7Of901IfwPemRlAlcGa3AmC/gLfyddaBk\nmB2q8buPo3M9ggLyVb2GtyY2ZU1A3pqVEF7GdY+yhzv953qiSRRIXHuXuQql\nj0iz9VUtig/0jw5x8iEJhS+PxWDJ+nYCyvOxmaynXLcD8qe6GobI6xR50LH9\n9wEV\r\n=THrJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID1Gu7og1W7Oj50gQzIdPmPCop3wCkLvGU7MYzW4PEUkAiBgQtWEy2OAidC0ffsVgZjrcFcEAqrE/zL//UWzor6Ocg=="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.6_1605096249453_0.10283501147071994"},"_hasShrinkwrap":false},"3.1.7":{"name":"@terminus/ui-table","version":"3.1.7","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/animations":"^9.1.0","@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/flex-layout":"~9.0.0-beta.29","@angular/forms":"^9.1.0","@angular/material":"^9.2.4","@angular/platform-browser":"^9.1.0","@fortawesome/angular-fontawesome":"^0.6.0","@fortawesome/fontawesome-svg-core":"^1.2.30","@fortawesome/pro-solid-svg-icons":"^5.14.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-button":"^2.1.5","@terminus/ui-checkbox":"^3.0.0","@terminus/ui-chip":"^3.0.3","@terminus/ui-form-field":"^2.1.0","@terminus/ui-icon":"^2.1.5","@terminus/ui-input":"^2.1.6","@terminus/ui-menu":"^3.0.2","@terminus/ui-option":"^1.1.2","@terminus/ui-paginator":"^2.2.4","@terminus/ui-pipes":"^1.2.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^1.1.3","@terminus/ui-spacing":"^1.1.3","@terminus/ui-styles":"^1.1.0","@terminus/ui-tooltip":"^1.1.3","@terminus/ui-utilities":"^1.1.0","@terminus/ui-validation-messages":"^1.1.3","@terminus/ui-validators":"^2.0.4","date-fns":"^2.16.1","rxjs":"^6.5.0","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.1.7","_nodeVersion":"12.19.0","_npmVersion":"lerna/3.22.1/node@v12.19.0+x64 (linux)","dist":{"integrity":"sha512-srDhYpWnCGvMS0MQB58EWfgaUQX26fnueDYcz6YzpCiUznMCojN5JMPxX9v+WYNsR7uwNjGR4DBbgv/qbFzIkg==","shasum":"e2de8075a302c7ce71b66415984556c8139a4473","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.1.7.tgz","fileCount":93,"unpackedSize":1003486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrB3cCRA9TVsSAnZWagAAiY4P/3UV4pd6jMsT/wsFnYNi\nBzZ66OtWpBbcPMmTuAnMUdneemdtYMmLCSJwk0Yhqni9xJ2F3Qxah+RSS+Ay\nOwk6BHZCOpDwmY8kI5JvQKU3w2lQt12uc++T9i+81utsqvSxkd18krSmK29Z\nVoPCXKqAHIAUJ/GS1+BI0nwAXRGRYiEiKdgjP6+RM5Oyr/aurrdbAW3n2kDm\ncjCIjLJd28sOn/WnLPS/vHmuFrvp1tVZhG4bHbaH7COLfw//pEUJGrydy6Az\nhLJT5b7dGsvs50C4aACya0rjejbwzr+qIrgWmL4Vmx0n+MdQLX4y5jgycu+c\ntHtwEARvyQM9z2SwSh6JVdNi7Mm1tiyCynVogOYzRjWjbzdFYm6DwSl8Ro0d\n/J0IShcHRwk1nnqCHROT/7LJn6jMpRYpaP3ihuvnOzvIk37/xmsnSnFgBhPU\nvPCKvqjvclbu2mBelQbSxuscXcSVBuUkYYmUK940S0Mnlz4vPdbfZY4l3Jzx\n7vhGgE6JOChQvKwUSlOcxSFBz/IV5e/7O6rURtVTXQ+7A+i2QDZzZPWwJw9k\nQeSm6zL+mDtEkIij2zkA9EUVBa/RWlWjYwupBXuRC71h8SlqXsy3uQRdmhM5\n+lIzrVEmp9uibGiYdrVnJo7UkJhXySfzLWMPqvMLYUpvIfNeFx6AWYy/KqkR\ngOsx\r\n=qejX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqVgF3UPs4a/9nfY2dV8V5UjH0DEAcZUCwhF5ykipOGQIgA9minngUF4Als3GywrUObELN5FXWJRcSiROyTtG4ZLg="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.1.7_1605115356312_0.8641908362829023"},"_hasShrinkwrap":false},"3.2.0":{"name":"@terminus/ui-table","version":"3.2.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"peerDependencies":{"@angular/animations":"^9.1.0","@angular/cdk":"^9.2.4","@angular/common":"^9.1.0","@angular/core":"^9.1.0","@angular/flex-layout":"~9.0.0-beta.29","@angular/forms":"^9.1.0","@angular/material":"^9.2.4","@angular/platform-browser":"^9.1.0","@fortawesome/angular-fontawesome":"^0.6.0","@fortawesome/fontawesome-svg-core":"^1.2.30","@fortawesome/pro-solid-svg-icons":"^5.14.0","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^1.0.0","@terminus/ui-button":"^2.1.5","@terminus/ui-checkbox":"^3.0.0","@terminus/ui-chip":"^3.0.3","@terminus/ui-form-field":"^2.1.0","@terminus/ui-icon":"^2.1.5","@terminus/ui-input":"^2.1.6","@terminus/ui-menu":"^3.0.2","@terminus/ui-option":"^1.1.2","@terminus/ui-paginator":"^2.2.4","@terminus/ui-pipes":"^1.2.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^1.1.3","@terminus/ui-spacing":"^1.1.3","@terminus/ui-styles":"^1.1.0","@terminus/ui-tooltip":"^1.1.3","@terminus/ui-utilities":"^1.1.0","@terminus/ui-validation-messages":"^1.1.3","@terminus/ui-validators":"^2.0.4","date-fns":"^2.16.1","rxjs":"^6.5.0","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm5/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm5":"esm5/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm5":"fesm5/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"dependencies":{"tslib":"^1.10.0","@angular-devkit/schematics":"9.1.9","@schematics/angular":"9.1.12"},"description":"<h1>Table</h1>","_id":"@terminus/ui-table@3.2.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-NO4/qhUeSPL49sbOUVPkBWs1i3KZIKW2i7afamFK3GEwidjQmpPOFmJuverdrlp1kfT78OpFEVLt2T9hzMiu6w==","shasum":"4caadbe3fa940255eb200dc5c1605f971d898f21","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-3.2.0.tgz","fileCount":99,"unpackedSize":1038607,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf06FRCRA9TVsSAnZWagAAyowQAIm6rM1XGJjXGWuCDhfp\n+LuC38xDtnvy844RX8Q811tLpSazmdU9IFDQolqGh3bZULhMb8ep62X9y5PD\n8e4Zv6d4kP5qoxMSj1D3Sb5+H5Qy2x5ERZWXOyeRCWY8P0teM2Y2y0f/sJit\nDb+TuztfAewSjerUvv57TpxMGLeuPBv5Gh054Z7fJX4uwcfDzAkS6WaB5/+4\nAAj9bRKOCRyTi1z/9X41/FRuN26ygCs4pRSxsjLLIowzUym1wf0WU5fJ0y09\nN3t/epr/s9ZDYaMDeFKwhRWUdfmGj6riZAzJLNJ51T5bsTwCHLfxcUne+12d\nWRXCogKc8I0RK+BTUgJd1egvbRG5+AfUQwuJgl2iR0B3U3pQrGV39zAcFC+Q\nnaVgov8nFk7jim8dvN44sNIYU41EejEuKF3eaULaYXl63/ltGPB3iNlaXxMZ\nrttiDR4ojWnqFezRByebTWY5WoNsVEAnKkH/AElGlmDZvtSWBW6ptxpeoE/B\n8vGhvyVIcTUnXuJY0iNl2GKne9UoETQJ5fPFNhnlFr4EZzG6iRwTJfgK1bqA\nWuk2bCXeVuBfR+KyZTgEoMcSzJEopC7rilzIvUkAoPibp5A7EnY8YCYxrR8o\nMBoj261uWd+BVKGg6EMMConx+M2Ao2dhye/4eMrgZb612D8Dct/4aNjrO0bI\ndUES\r\n=p/oC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRRht3JKeA+DHDeFM8mTA8QHMQxw0i0j0S5wobngoozgIhAJEW8NpEGXULXRBMS12freTDCdx6u2hhuWMm4Cq2a1ta"}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"terminus_devops","email":"devops@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"benjamincharity","email":"ben@benjamincharity.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_3.2.0_1607704912530_0.8299961088436429"},"_hasShrinkwrap":false},"4.0.0":{"name":"@terminus/ui-table","version":"4.0.0","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@fortawesome/angular-fontawesome":"^0.8.1","@fortawesome/fontawesome-svg-core":"^1.2.32","@fortawesome/pro-solid-svg-icons":"^5.15.1","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-icon":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.0","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-evUk4O/fjeALzQf5avIoL1QZA8NOCB+a7+xzn8UTP+8y+bE2sGkPeOlIRQ5eXGX6WME6xruC+4gHpDBgijwxtg==","shasum":"26b200fe1fe9cf7300b970034042b799b1434e57","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.0.tgz","fileCount":71,"unpackedSize":751410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2hf7CRA9TVsSAnZWagAADa0P/2cqavHUjjHyZIaG82hB\nW1TkS8o0GxxyJ+79EMSSqruF11PVIbhSYGuw7DTIRcjnB2/S0+LKV++dWetx\nTMNBw2+qBrDBhgBv1W8SbV7c7QsM0jOsCe1gaxDZx4h/U0ak1fHmcXwtIV59\nBTn4ehdi+9Z8jcJo9PidSrv9RAJxIBRbYm4ccbtsIJXJbbnoUUw2fWv432hj\nsSJUCBFxqdaY6jk/60vjbmOlgLM8xqpaPWoUy1+tnYQ3vZ2y5meWEMApy4bA\nZ+iXY7C4+/Cc6FsLTfm0IS/bnJjANuJKAcBg1pi1l81TnKXPEAlS5SQNdzrD\n6n5umkHju7zfqR67iBVb/YEUsARU9m1UNyjwa7wudrsqwPB9l63E4JLfMTHf\nrc0i3vOkvZJvVcRsSIfgsX5Kv/q9S+vubdZIEi2Va5Faf9V0wFhEsLCf9em6\nZXrZymZrHEQdjwvc9AJpjnnDuM+zZM80sCMcrV6ByU+ZlFBqBOfno5H0IlVw\nHQWZjRfXhUIBwlkbQVOI21+nRyn1jrmpVb54TTKvclIpuWC5a5r4ND0/dK6u\neZIpLFmL+xRwKfarOgGfUz8IGPow9mV8NwuCC3jlJYSvZQL2nsfAK9aM8oX2\nsY5clIK+KuGjccwGUs5fS1VpUBOQgb0mX9n0JRoCZxJJQPtv/lprrwP696Am\nWqmc\r\n=R5u+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCr+wya5PpHzSdahUatvIwDPMQYkZAZwBSkXnZIkuHSDwIgSAwrxIGSokMY4oPM7pkXg7+XmPsFESK+OMgNpY1QWE0="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.0_1608128506922_0.10107069787750578"},"_hasShrinkwrap":false},"4.0.1":{"name":"@terminus/ui-table","version":"4.0.1","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.1","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-mN14m7bH0DT2Q1mJygGag3LI9BUG/994uVGq6+0ZvVk765BM2Ewh2DYLoDHKMsOciRVRAY8Hds2+OEN3Zk+8gg==","shasum":"18c996f8024a7e878032f7525518b946418b71fd","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.1.tgz","fileCount":71,"unpackedSize":752509,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf/aeGCRA9TVsSAnZWagAAL7IP/iEazWCqvRyLMwDSHIvi\ntDFmoyovtqfN71g8ASp53LlnFyBgmrU+oZsAAm6qF6J5sWHodpRS7ZRSA9Na\nFwE+I5rz7VdxDTp9/4SDthjNDdHZolXQG6y/GDEwXLnQNwn3gPh/uUw/IwG3\npndsW82UsoU7grJebzgyBH5W06mk2n+P9aIZXReDKDz/yG/T+I2vLU4BBOwu\nnGA0Ki/EvYIm0hjTKCYT2YBveMvEs6Iy57S3vRfjcBQzu/OgJfi7ozpbGujp\ndNSI85yMW/o7CwMEJccjc7v9CG0L7c+jqGiVnPNXTEbUASc1IdvgLVzdfrHM\n7QzaIIbi2mXFP7akQf1YZU0Iqjf2WG5oCkguR+Knilu0w++YXHPjLHIJQ2W6\nZDL7UgLeQ61NV5Rn6G/T5Zq8WknFOrkJm4eQBFARutVe7Pvp7gl5J5I5BDGd\nGL4qlKqXGRqzf+yICvAMtdrgT39UHrpLf61dsKme04Ui3BfFpPvQd5cxxeEF\nFhPwKnLon5e9F7Qzz3nqBRvZ65SYYvh7uyUGd2Lv1xlVmuz+OnIzmN2xDlqc\nTUJZMpXAQ8V2pl0dEmd6O+rDD7BjDSwZDHlxct1PjJXYANhNsbkrZ0EsjKnU\nC4SY4ybLjkIiMjRGLt2KXYIS/Z8ToSqPu7fokKa2uIluUabueRHY5d5pMqfE\n0W/c\r\n=xtsN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDVKcfYyz/loYGIxILt1Bcc3FN2IOLtwe/WInasVPmV9wIgIse1eyMECfP+3ywjOs8B+VKC1dGn3SPVGg3QsY7rIz8="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.1_1610459014330_0.08056738895656412"},"_hasShrinkwrap":false},"4.0.2":{"name":"@terminus/ui-table","version":"4.0.2","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.2","_nodeVersion":"12.20.0","_npmVersion":"lerna/3.22.1/node@v12.20.0+x64 (linux)","dist":{"integrity":"sha512-Rb/MBrQd69M5j0STTrfCWvigaK2wCStLeqvNTTTn0R1wcndCiINwASW/kcsw0nDvdQrpE7kixe+a7jd20sC19g==","shasum":"53a2bac312bcf8439b8b1d4f311f230dfd2ec498","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.2.tgz","fileCount":71,"unpackedSize":752868,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgCOlzCRA9TVsSAnZWagAA1ccP/iuzMND6CWsDU5NFfK5z\nXKi5nTzV3RdfpMa9UZvq/Qv6GGw8br4HGc7IsmF+9LCFUGF2Kcfmlh/UOy5O\n45kr9jCCj8J9PrC3jf+f8GhQXTHB1W7cNTz96Ra26rC4saScl0SHKOpe46+k\n9oS6Uf/FPDrwMiKsJi4Aekhau9nGQXbjSbwz+PQcFbkusx3XuQrarW8A/TPp\n3eRkysKD9wmuNOmXb/1CMW9BMEGgBa2i1ObzZPJ8dZGopniH1nExdvb5EORK\nPppKw3p9PONn6NliS7nHfInV/ttkatM4zbMKg9Rhrlqq2MpWoQFoz1NTE8qU\n7ndD26VZ/DJKYipoSzMcck7w+YCS3H95TrGDLSN8IfjGntBG8hRWnQmIHgLY\nZTL3XmvBOFiOdBfMcufUXfhJSEibZgzqaKbTskVFaQislpvbM36ObhOV6DeX\n/jWYMgxG78lx8trfMjqVjbXbMXe/V3wr8572CvUzzIcp/hTIzqyI1aGsmMcp\n2+uy5KqK5XSSiNndfLnregAFaVFwyEl4J5e7B+WU0R588a4M2h5GLZ+VusRn\nlkyz5sINvtYy3VA1nf+6BQQxTYVa6jPOqUBN9PoBXUvpcBhqPSHCFG6kmNXj\nsm7qUdPeeln77Rt1urCtUOTFMDloMT0WIvhYbH5i9Pp06LVgbEbE/AvWCheC\nc8vH\r\n=YXKv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRJn/KYOHryyGKFTusD+bq+7CzV6f4hld9MmAPoHnK2wIhAM43PYLRbtpf8SkZAUedrYz/EEhi1FFnf5mIYZtgIMXl"}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"benjamincharity","email":"ben@benjamincharity.com"},{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.2_1611196786985_0.8698094242409475"},"_hasShrinkwrap":false},"4.0.3":{"name":"@terminus/ui-table","version":"4.0.3","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Change indicator](#change-indicator)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### CSS imports\n\nIn your top-level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Change indicator\n\nA component to easily show the sentiment for a change. This is most often used in a footer summary row.\n\n```html\n<!-- The sentiment is 'positive' by default -->\n<ts-change-indicator>42%</ts-change-indicator>\n<ts-change-indicator sentiment=\"negative\">3 per week</ts-change-indicator>\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.3","_nodeVersion":"12.22.5","_npmVersion":"lerna/3.22.1/node@v12.22.5+x64 (linux)","dist":{"integrity":"sha512-H0VzTZKNttzxTzCa6tIoP0d/k/IdfK0UaCQs8PmwbFk1jT8rPm7Rq1EEl5eNwCOEHfAcOCUuvTo/n3nnKVyqtQ==","shasum":"a1f6a320834e509567efc092ca974d47b81b3003","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.3.tgz","fileCount":75,"unpackedSize":768708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJnaVCRA9TVsSAnZWagAAe6AP/1J2zs55BevqRS1zM69r\nkpsJ17Y19BawORune+VYZylF3/iSn3TlXVKXKJu9IFkht5v9d1C8Ix80Vuov\nCf3Us02YR1gTYDl32kyqsttUIrKlQD9vIqnPozuwyFNuUubNFA/PctIoLgmE\n5/2YgWwVY0xEmM70G4hdWukdnb+nOIO1dYzYxTFT5vlwaexj7qpKr8noxoJt\nJ3rNVbFl+XpVZL6vWH+hiyvo9pAMblHY3ZQodmZo2MTJ8UjGULNxHLOOSfaM\n7XlgyzkFmEdqHVXHYkHnXddP/VChvtX5Htmirpr2gR7rjhDn0Ni9VajMy/Hn\n51tH7gE062I6KXKVhKgLb4RWxlPQkLJAlF7p9q9CoPSr1G7EPYe8eQH/syd+\nn1EPfiiHPwcC0ud61Kf9ZtH9L07Ihu67FuAIDepNqUNkXehzU3o+EKFwoUqu\nCY0JnyCj7zGW4mQYz/rubvqQbI+9xT6KZ2Jxsgs9SWuDeRjFvNt7Q6+dbSUs\nVgMlPu3Fh+W+QGeucgC5LkaBj9+RDiKaYCmzfhoyENOnZDkHk6ffxYS2EEDC\noaWJF8ize7a7vX/KBRB1AIzgvj74EHI6+wx7iNW2uXx6dHN8jgxeTZfoN2ep\nPCZgDkszrNK7gKGQA/KzbkAVq8MJolFQh7sSy/Mlks0NS04ZINP0BQl0xXsd\nB/yM\r\n=3V/B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDHsei/u1RYOP79P3N8D16sAbViEXSgaQI7hU/HnlEs5AiEAyefK5/WJeD9sijKA802R787slRvyHX2zeqI8ZObpp5w="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.3_1629910677215_0.6192048836028807"},"_hasShrinkwrap":false},"4.0.4":{"name":"@terminus/ui-table","version":"4.0.4","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Change indicator](#change-indicator)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### CSS imports\n\nIn your top-level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Change indicator\n\nA component to easily show the sentiment for a change. This is most often used in a footer summary row.\n\n```html\n<!-- The sentiment is 'positive' by default -->\n<ts-change-indicator>42%</ts-change-indicator>\n<ts-change-indicator sentiment=\"negative\">3 per week</ts-change-indicator>\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.4","_nodeVersion":"12.22.5","_npmVersion":"lerna/3.22.1/node@v12.22.5+x64 (linux)","dist":{"integrity":"sha512-QlRsTZNpx3/AmDD1Ho+LSGsr/+XGovH2rt4cP/6X5SsE7oMC5lqKZS12Y43OsuubpwBE6YtZ1H7DiaYwGiofBg==","shasum":"627817f6ce1f894be8e57df74b421707b1fa54f6","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.4.tgz","fileCount":75,"unpackedSize":772742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKQXpCRA9TVsSAnZWagAAiTcP/jSR7UcAEgLrIqs5H/aP\n/AiyNXHWvYUDCb1nGTbRKNzR7Ghn4SS7tiwtV8MvU2re3DcanBKVejVxp2AJ\nV9y9ixNrVcjJMmVpkqiZZXy3wG9FlVfsiLMJlogKTwoDHOie0APn4HXcITE3\nFVLRQLBfaXdgnI/ompDCl+wkpWPT5AgKDnZP/WmtcJh2eViurVSRLxLCwCIz\nPDQZXgLvHdPDU8e7TTyRZtTn13RnjSe4RxaE3LT613zPwvXmhnuAB8SVZIHs\nogGiB0tsejLYbdG8Jh7PWmBOYg+lSdjppzPeMgwt3DtUxtdObpVpx+Ah9Sg3\n8KM+JCAvnRp1wsHYI7iQZPfuwM+hpPZKAStS9AkkPR0Qwd+P8zZ9tw+Hck0/\n5NWTFoXBfo3jBUIrz4Z/f8yxk6i4wZa1XhBjC+/PEqKZHrDFsVeH29uJNGkQ\ntBiuDUAmguPtOsfUme/0DxCgnRITsnjNX/e1MxVbkbBUBTX3JTEmv9XIf6yC\nzZj/0l0dDU9ST+iGMy0jjtpzuvg1u6HjiZabEkbY8lrp+5h3YUMSnInHIwOT\nj3cd2AOpvk+bRpKfaQY4xlHNlHIwQUfq57TtR1mLRK/3F9ffW19avG0kyceC\n/xiSURoekhjp8C6gUeGat7N2Eey2Zaq6lOL9p1MZtxyMufxd4aqFA2bQFlYM\nnZGF\r\n=D1eY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGMoAvr8W5Rm5dXQtVpgvvBxqgmwEAcZeXCGINGvvabpAiEA5fTLZQNz5chzjwLtgCmN2EqyVWU8XE7ghQXDRZnf5B4="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.4_1630078441271_0.10091929235755681"},"_hasShrinkwrap":false},"4.0.5":{"name":"@terminus/ui-table","version":"4.0.5","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Change indicator](#change-indicator)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### CSS imports\n\nIn your top-level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Change indicator\n\nA component to easily show the sentiment for a change. This is most often used in a footer summary row.\n\n```html\n<!-- The sentiment is 'positive' by default -->\n<ts-change-indicator>42%</ts-change-indicator>\n<ts-change-indicator sentiment=\"negative\">3 per week</ts-change-indicator>\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.5","_nodeVersion":"12.22.5","_npmVersion":"lerna/3.22.1/node@v12.22.5+x64 (linux)","dist":{"integrity":"sha512-AdaL4XwTjYl9hKL0nRmOnDu2vsI/a/tIVul//sRDDBTPPBgZa16xD+jOSsTbq3dCqp/gheGe2vpe/Iagfc583w==","shasum":"5f986a3f3734e8fc3b184fb63040fc12d45564ea","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.5.tgz","fileCount":75,"unpackedSize":773728,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKU12CRA9TVsSAnZWagAAbVgP/365f37H3N7tU8Ju6B0v\nUva1rXb7f5S1aVFcB7b+XBkqlM1EFKrJCfFweI7YGaKz5FQBPB1efZK/F4zI\n1xteTvBr5nrZ5Ew4KyXQmlqO+cDMRoNbUqP5vdjN3/9TnbUfh55t5AGqyqqn\nFJQ3NGqcrT6WY1XEhx3ci2/7tOd5Q0I3s0YUZ+q8fVySayjD23skcpF8VpE+\nnwEJA8dJwWHBizAKH8CC74+S15Z8exLI+Dp45vXUPQZTUGGPhitONzpeqVny\ndzYUvlD9YqaBcHIVuY/3IRji+ynk4HZbQ7gTBgYUKLwyf8QgEObG3zR/4LDR\nQp5gNED5KK4ccKAezlRKIdClDTIWyLy4lTPBxDS8cVGpxcs3lajokPR6sgde\n55XtzNRthYDKj7bSwl4BPHav4sK3n7OhUBvYyFiiDlcVb9VanFjmwwDq+tcU\nvROvGYXIWEORwSADbJp8Hi5qjO8SbRhcuevWuQkTc08ZuiCGmzAv3eU9OwaS\nv+kQEoqbkLhnUaLbmmeHzEov5Gd7o0z2yNAUNIiEnr2QC2AskD2zhy2sejZK\nF+Nlxq11pqQmuoRPZ3Xn+0Oxt2Cxg5xSiXNVBGittNPjObHNB3XzE3qT+dk2\nPFXBjWWpeEfsm93IR+qu3OeptmVLjXIjIzZW+B8GinlG7Obnb7zATm1ewtO0\nzv0V\r\n=nt/u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSsdHbc6IbHnqu/3iW43V2W9SKBgA3wxHYcSZvcUglgAIhAOLou5tj94B/bG68EmHkoYfwvqz8jlurwx4YWESTLVhn"}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.5_1630096758172_0.35838611728342995"},"_hasShrinkwrap":false},"4.0.6":{"name":"@terminus/ui-table","version":"4.0.6","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"11.0.4","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Change indicator](#change-indicator)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### CSS imports\n\nIn your top-level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Change indicator\n\nA component to easily show the sentiment for a change. This is most often used in a footer summary row.\n\n```html\n<!-- The sentiment is 'positive' by default -->\n<ts-change-indicator>42%</ts-change-indicator>\n<ts-change-indicator sentiment=\"negative\">3 per week</ts-change-indicator>\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.6","_nodeVersion":"12.22.5","_npmVersion":"lerna/3.22.1/node@v12.22.5+x64 (linux)","dist":{"integrity":"sha512-dVkXHxsrEh//RXRK8YaGjnpKhypTiKhvUM++WZgwPZT7za6MKarpmubGyJDhIrLkMptAyKw3wCxfqiesT6ap9Q==","shasum":"700e58e8012952c417168cafbdfabc8f3450f564","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.6.tgz","fileCount":75,"unpackedSize":774029,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKVvCCRA9TVsSAnZWagAAXd4QAJiozbzR11lZicZh5Ckw\nqcTNcjr6Aynh7L4YSTU+ho4qDPobQE3BaX47yTkJjAaVlD3yTrqioanaPwQI\n5Ttkc2s4iKoFFhvKKGKKMfR6hiOjbZQjJwMsH7oz8tRlCjtcEmaINGTEUx+7\nkG7dZ/NHabuMJ5QKtuE7VOQV5NsnrRysBCphRny2x4L9uAXh80srP+66o7rj\nXWtlBb1mmd24n7jfENLhBt8cBWyXGkYHkB3Rwqvt46l5DIHDNXF5/DNNQDaL\n2Z4380FBxE+yQwimdp6AlbGiwjDYJ4kJMhURu+rptiE9zB+kZSlEWfqqUa6f\nP6PdPACPXXffsTD6WblnBT+IXdjMNTFInlyoqo7gg9/p5J3Ehg6QjSDNtBpm\nCTohukwrF1tar5y07r8f/ZGAhRPCayiePmmBlvOjpvWnrYgHYNZXtmDZDhX8\nJWLIs+EAToUDQtmZEp7yZ9i4wXS5wFMHojSSVySVtN5DfyvDU+6XvRDJHeWO\n4SurNzzUSJdYTX0Dpi/6DWhVdNMl7JALZEoriaSzAya0yZd1lks8AML4Bz4l\n/5UcxGplqORXL3jbnb/xAT85zYBEtRCOLmy+mPFoppmxKAQmiaL6WFrx0lhH\ndPZFwCsIZXI0PeLSFXR7cfg5elRzQ6netLQFAomBZuE6I/mKraZL8GpwoLCt\nvWlU\r\n=GtiG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDX0YIpqqZJgEBdeSG7r/jDk2bFMVL8SWjr10hjsEWfagIhAOGP7GJiFbqKdGVgEH2Tj8NefDhsdevsAD+xV3Phzhyz"}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.6_1630100418604_0.347129604767362"},"_hasShrinkwrap":false},"4.0.7":{"name":"@terminus/ui-table","version":"4.0.7","license":"MIT","author":{"name":"@terminus"},"private":false,"homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"publishConfig":{"access":"public","tag":"next","directory":"../../../dist/libs/ui/table"},"dependencies":{"tslib":"^2.0.3","@angular-devkit/schematics":"12.2.13","@schematics/angular":"11.0.4"},"peerDependencies":{"@angular/animations":"^11.0.4","@angular/cdk":"^11.0.3","@angular/common":"^11.0.4","@angular/core":"^11.0.4","@angular/flex-layout":"~11.0.0-beta.33","@angular/forms":"^11.0.4","@angular/material":"^11.0.3","@angular/platform-browser":"^11.0.4","@terminus/design-tokens":"^3.1.0","@terminus/fe-utilities":"^2.0.0","@terminus/ui-button":"^4.0.0","@terminus/ui-checkbox":"^4.0.0","@terminus/ui-chip":"^4.0.0","@terminus/ui-form-field":"^3.0.0","@terminus/ui-input":"^4.0.0","@terminus/ui-menu":"^5.0.0","@terminus/ui-option":"^2.0.0","@terminus/ui-paginator":"^4.0.0","@terminus/ui-pipes":"^2.0.0","@terminus/ui-selection-list":"^3.0.0","@terminus/ui-sort":"^2.0.0","@terminus/ui-spacing":"^2.0.0","@terminus/ui-styles":"^2.0.0","@terminus/ui-tooltip":"^2.0.0","@terminus/ui-utilities":"^2.0.0","@terminus/ui-validation-messages":"^2.0.0","@terminus/ui-validators":"^3.0.0","date-fns":"^2.16.1","rxjs":"^6.6.3","text-mask-addons":"^3.8.0","text-mask-core":"^5.1.2"},"schematics":"./schematics/collection.json","main":"bundles/terminus-ui-table.umd.js","module":"fesm2015/terminus-ui-table.js","es2015":"fesm2015/terminus-ui-table.js","esm2015":"esm2015/terminus-ui-table.js","fesm2015":"fesm2015/terminus-ui-table.js","typings":"terminus-ui-table.d.ts","metadata":"terminus-ui-table.metadata.json","sideEffects":false,"readme":"<h1>Table</h1>\n\n[![CI/CD Status][github-action-badge]][github-action-link] [![Codecov][codecov-badge]][codecov-project] [![MIT License][license-image]][license-url]  \n[![NPM version][npm-version-image]][npm-package] [![Library size][file-size-badge]][raw-distribution-js]\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Installation](#installation)\n  - [CSS imports](#css-imports)\n  - [CSS resources](#css-resources)\n- [Usage](#usage)\n  - [Basic](#basic)\n    - [1. Define the columns HTML](#1-define-the-columns-html)\n    - [2. Define the displayed columns](#2-define-the-displayed-columns)\n    - [3. Define the table's rows](#3-define-the-tables-rows)\n    - [4. Provide data](#4-provide-data)\n    - [Full HTML example](#full-html-example)\n  - [Dynamically update table data](#dynamically-update-table-data)\n  - [Dynamic columns](#dynamic-columns)\n  - [Sorting by column](#sorting-by-column)\n  - [Row selection](#row-selection)\n  - [Content wrapping](#content-wrapping)\n  - [Cell alignment](#cell-alignment)\n  - [Sticky header](#sticky-header)\n  - [Footer](#footer)\n  - [Sticky column](#sticky-column)\n    - [Sticky column at end](#sticky-column-at-end)\n  - [Re-orderable columns](#re-orderable-columns)\n  - [Density](#density)\n  - [Outer border](#outer-border)\n  - [Scrolling](#scrolling)\n  - [Events](#events)\n    - [Table](#table)\n    - [Cell](#cell)\n  - [Full example with pagination, sorting, and dynamic columns](#full-example-with-pagination-sorting-and-dynamic-columns)\n- [Change indicator](#change-indicator)\n- [Test Helpers](#test-helpers)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nUse the `ng add` command to quickly install all the needed dependencies:\n\n```bash\nng add @terminus/ui-table\n```\n\n### CSS imports\n\nIn your top-level stylesheet, add these imports:\n\n```css\n@import '~@terminus/design-tokens/css/library-design-tokens.css';\n@import '~@terminus/ui-styles/terminus-ui.css';\n```  \n\n### CSS resources\n\nLoad the needed font families by adding this link to the `<head>` of your application:\n\n```css\n<link href=\"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400&display=swap\" rel=\"stylesheet\">\n```\n\n## Usage\n\n### Basic\n\n#### 1. Define the columns HTML\n\nDefine the table's columns. Each column definition should be given a unique name and contain the content for its header and row cells.\n\nHere's a simple column definition with the name `userName`. The header cell contains the text `Username` and the row cell will render the\nname property of each row's data.\n\n```html\n<ng-container tsColumnDef=\"username\">\n  <th ts-header-cell *tsHeaderCellDef>\n    Username\n  </th>\n\n  <td ts-cell *tsCellDef=\"let item\">\n    <!-- Place any HTML content here -->\n    {{ item.username }}\n  </td>\n</ng-container>\n```\n\n> NOTE: `ts-header-cell` & `ts-cell` can both be used as a component selector or an attribute selector. We _highly_ encourage the attribute\n> usage as it is more accessible and has better support for items such as multiple sticky headers etc.\n\n#### 2. Define the displayed columns\n\nThe table expects an array of `TsColumn` definitions to manage the displayed columns and the initial column width.\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\n```\n\n#### 3. Define the table's rows\n\nAfter defining your columns, provide the header and data row templates that will be rendered out by the table. Each row needs to be given a\nlist of the columns that it should contain. The order of the names will define the order of the cells rendered.\n\nNOTE: It is not required to provide a list of all the _defined_ column names, but only the ones that you want to have\nrendered.\n\n```html\n<tr ts-header-row *tsHeaderRowDef=\"['userName', 'age']\"></tr>\n\n<!-- `let row;` is  exposing the row data to the template as the variable `row` -->\n<tr ts-row *tsRowDef=\"let row; columns: ['userName', 'age']\"></tr>\n```\n\n> NOTE: `ts-header-row` & `ts-row` can both be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged  \n> as it is more accessible and has better support for items such as multiple sticky headers etc.\n\nThe table component provides an array of column names built from the array of `TsColumn` definitions passed to the table. You can use this  \n reference for the rows rather than defining two separate arrays:\n\n```html\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"resizableColumns\"\n  #myTable=\"tsTable\"\n>\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- \n    `myTable` is the template reference for the table component.\n    `columnNames` is a getter that returns an array of column names.\n  -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n</table>\n```\n\n> NOTE: `ts-table` can be used as a component selector or an attribute selector. Attribute usage is _highly_ encouraged as it is more  \n> accessible and has better support for items such as multiple sticky headers etc.\n\nMapping the array of names manually is also fairly simple:\n\n```typescript\nimport { TsColumn } from '@terminus/ui-table';\n\nconst columns: TsColumn = [\n  {name: 'title', width: 200},\n  {name: 'body', width: 400},\n];\nconst columnName = this.columns.map(c => c.name);\n```\n\n#### 4. Provide data\n\nThe column and row definitions capture how data will render - all that's left is to provide the data itself.\n\nCreate an instance of `TsTableDataSource` and set the items to be displayed to the `data` property.\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource();\nthis.myDataSource.data = dataToRender;\n```\n\n```html\n<table ts-table [dataSource]=”myDataSource”>\n  ...\n</table>\n```\n\nThe `DataSource` can be seeded with initial data:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nthis.myDataSource = new TsTableDataSource(INITIAL_DATA);\n```\n\nAn interface for your table data can be passed to `TsTableDataSource` for stricter typing:\n\n```typescript\nimport { TsTableDataSource } from '@terminus/ui-table';\n\nexport interface MyTableItem {\n  name: string;\n  id: number;\n}\n\nthis.myDataSource = new TsTableDataSource<MyTableItem>(INITIAL_DATA);\n```\n\n#### Full HTML example\n\n```html\n<!-- Pass in the dataSource -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Define the header cell and body cell for the `username` Column -->\n  <ng-container tsColumnDef=\"username\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Username\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.username }}\n    </td>\n  </ng-container>\n\n  <!-- Define the header cell and body cell for the `age` Column -->\n  <ng-container tsColumnDef=\"age\">\n    <th ts-header-cell *tsHeaderCellDef>\n      Age\n    </th>\n\n    <td ts-cell *tsCellDef=\"let item\">\n      {{ item.age }}\n    </td>\n  </ng-container>\n\n  <!-- Define the table's header row -->\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n\n  <!-- Define the table's body row(s) -->\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames;\"></tr>\n</table>\n```\n\n### Dynamically update table data\n\nYour data source was created during the bootstraping of your component:\n\n```typescript\nthis.myDataSource = new TsTableDataSource<MyDataType>();\n```\n\nSimply assign the new data to `myDataSource.data`. The table will flush the old data and display the new data:\n\n```typescript\nthis.myDataSource.data = dataToRender;\n```\n\n### Dynamic columns\n\nColumns can be dynamically added and removed with any control. The selected control should affect the array of columns passed to the table\n(`myColumns` in the example below).\n\n```html\n<!-- As the `myColumns` array is changed, columns will be shown/hidden in real time -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" #myTable=\"tsTable\">\n  <!-- Custom column definitions excluded for brevity -->\n\n  <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n  <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNamesr;\"></tr>\n</table>\n```\n\n### Sorting by column\n\nTo add sorting behavior to the table, add the `tsSort` directive to the `<ts-table>` and add `ts-sort-header` to each column header cell\nthat should trigger sorting. Provide the `TsSortDirective` directive to the `TsTableDataSource` and it will automatically listen for sorting\nchanges and change the order of data rendered by the table.\n\nBy default, the `TsTableDataSource` sorts with the assumption that the sorted column's name matches the data property name that the column\ndisplays. For example, the following column definition is named `position`, which matches the name of the property displayed in the row\ncell.\n\n```html\n<!-- Add the `tsSort` directive to the table -->\n<table ts-table [dataSource]=\"myDataSource\" [columns]=\"myColumns\" tsSort>\n  <ng-container tsColumnDef=\"position\">\n    <!-- Add the `ts-sort-header` directive to the column -->\n    <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n      Position\n    </th>\n\n    <td ts-cell *tsCellDef=\"let element\">\n      {{ element.position }}\n    </td>\n  </ng-container>\n</table>\n```\n\nIn your class, get a reference to the `TsSortDirective` using `@ViewChild`:\n\n```typescript\nimport { AfterViewInit, ViewChild } from '@angular/core';\nimport { TsSortDirective } from '@terminus/ui-sort';\n\nexport class TableComponent implements AfterViewInit {\n  // Get a reference to the TsSortDirective instance\n  @ViewChild(TsSortDirective, {static: false})\n  sort: TsSortDirective;\n\n  public ngAfterViewInit(): void {\n    // Subscribe to the sortChange event to reset pagination, fetch new data, etc\n    this.sort.sortChange.subscribe(sortState => {\n      // Table was sorted - go get new data!\n      console.log('Table sort changed! ', sortState);\n    });\n  }\n}\n```\n\n### Row selection\n\nThis can be implemented at the consumer level by adding a column that contains a checkbox.\n\n### Content wrapping\n\nBy default, cell contents do not wrap. This can be overridden by setting the column `noWrap` property:\n\n```html\n<ng-container tsColumnDef=\"created\" [noWrap]=\"false\">\n  ...\n</ng-container>\n```\n\n### Cell alignment\n\nDefining an alignment style for a cell will set the horizontal alignment of text inside the cell. Set the directive `alignment` and pass it\nany valid TsTableColumnAlignment value (`left`, `center` or `right`).\n\n```html\n<!-- Set alignment on the column -->\n<ng-container tsColumnDef=\"created\" alignment=\"right\">\n  <th ts-header-cell *tsHeaderCellDef ts-sort-header>\n    Created\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.created_at | date:shortDate }}\n  </td>\n</ng-container>\n```\n\n### Sticky header\n\nDefining the header as sticky will ensure it is always visible as the table scrolls. Set `sticky: true` in the `ts-header-row`'s\n`tsHeaderRowDef` directive.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n</ng-container>\n\n<tr ts-header-row *tsHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n> NOTE: Multiple sticky headers can be defined.\n\n### Footer\n\nA footer row can be defined to be output at the bottom of the table. It supports the same 'sticky' setting that the\nheader row does.\n\n```html\n<ng-container tsColumnDef=\"example\">\n  <th ts-header-cell *tsHeaderCellDef>Created</th>\n  <td ts-cell *tsCellDef=\"let item\">{{ item.created_at }}</td>\n  <td ts-footer-cell *tsFooterCellDef>Custom footer cell content</td>\n</ng-container>\n<tr ts-footer-row *tsFooterRowDef=\"displayedColumns; sticky: true\"></tr>\n```\n\n### Sticky column\n\nDefining a sticky column will pin it to the left side as the table scrolls horizontally. Add the sticky data attribute to the column\ndefinition. This can be applied to more than one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" sticky>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `sticky` columns can be defined.\n\n#### Sticky column at end\n\nAdding the data attribute `stickyEnd` will pin the column to the end of the table as it scrolls horizontally. This can be applied to more\nthan one column.\n\n```html\n<ng-container tsColumnDef=\"updated\" stickyEnd>\n  <ts-header-cell *tsHeaderCellDef>\n    Updated\n  </ts-header-cell>\n  <ts-cell *tsCellDef=\"let item\">\n    {{ item.updated_at | date:\"shortDate\" }}\n  </ts-cell>\n</ng-container>\n```\n\n> NOTE: Multiple `stickyEnd` columns can be defined.\n\n### Re-orderable columns\n\nColumn reordering is not built into the table itself, but it is supported with the help of the Angular CDK.\n\nThe example below shows how to allow users to adjust column visibility *and* column order via a menu.\n\n> NOTE: Use the custom icon `table_large_plus` to indicate a table settings menu.\n\n```html\n<!-- Set up a TsMenuComponent to control which columns are visible and their order -->\n<ts-menu [menuItemsTemplate]=\"columns\" theme=\"accent\">\n  <ts-icon svgIcon=\"table_large_plus\"></ts-icon>\n  Edit Columns\n</ts-menu>\n\n<!-- Here we are defining the list of controls that will be shown in the menu -->\n<ng-template #columns>\n  <!-- Here we wire up a two CDK drop list directives and one CDK emitter -->\n  <form\n    [formGroup]=\"columnsForm\"\n    cdkDropList\n    cdkDropListLockAxis=\"y\"\n    (cdkDropListDropped)=\"columnDropped($event)\"\n  >\n    <!-- Pass an array of all columns that can be made visible. This array's order will be update when column order is changed. -->\n    <ng-container *ngFor=\"let column of allPossibleColumns\">\n      <!-- The menu normally closes after each interaction, so we need to stop propagation here to\n      support multiple selections without closing using the `(click)` event. -->\n      <ts-checkbox\n        [formControl]=\"column.control\"\n        (click)=\"$event.stopPropagation()\"\n        theme=\"accent\"\n        cdkDrag\n      >\n        <span>\n          {{ column.display }}\n        </span>\n\n        <!-- Here we specify the drag handle with another CDK directive (optional). -->\n        <!-- We also need to stop drag interactions from affecting the checkbox status. -->\n        <ts-icon\n          cdkDragHandle\n          (click)=\"$event.preventDefault() && $event.stopPropagation()\"\n        >drag_indicator</ts-icon>\n\n        <!-- Define a placeholder that is seen in the slot the current item will fill when released (optional) -->\n        <div *cdkDragPlaceholder></div>\n      </ts-checkbox>\n    </ng-container>\n  </form>\n</ng-template>\n\n<!-- The table (column & row definitions omitted for brevity) -->\n<table\n  ts-table\n  [dataSource]=\"dataSource\"\n  [columns]=\"visibleColumns\"\n  (columnsChange)=\"columnsChange($event)\"\n  #myTable=\"tsTable\"\n>\n  <!-- ... -->\n</table>\n```\n\n```typescript\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { TsColumn } from '@terminus/ui-table';\n\n/**\n * Extend the TsColumn interface with properties our component needs.\n *\n * This allows us to add a control we can use to determine which columns should be shown\n */\nexport interface CustomColumn extends TsColumn {\n  // The UI text for the column dropdown\n  display: string;\n  // The associated FormControl\n  control: FormControl;\n}\n\n@Component({...})\nexport class TableComponent {\n  // Define the original column source with all columns defaulted to visible\n  private readonly columnsSource: CustomColumn[] = [\n    {\n      display: 'Title',\n      name: 'title',\n      width: 300,\n      control: new FormControl(true),\n    },\n    {\n      display: 'Number',\n      name: 'number',\n      control: new FormControl(true),\n      width: 100,\n    },\n    // ...etc\n  ];\n\n  // Define the mutable version of your columns (must be mutable as we will be changing the order)\n  public allPossibleColumns: CustomColumn[] = this.columnsSource.slice();\n\n  // Define a getter that filters our columns whose control is currently `false`.\n  // This is what will get passed to the table's `columns` input.\n  public get visibleColumns(): CustomColumn[] {\n    return this.allPossibleColumns.filter(c => c.control && c.control.value);\n  }\n\n  constructor(private changeDetectorRef: ChangeDetectorRef) {}\n\n  // Define a method to handle the drop event. When an item is dropped in the UI, the underlying data has not been changed yet.\n  public columnDropped(event: CdkDragDrop<string[]>): void {\n    // `moveItemInArray` mutates the original array, so first we clone the array\n    const columns = this.allPossibleColumns.slice();\n    // Then update the positions (this function is provided by the CDK)\n    moveItemInArray(columns, event.previousIndex, event.currentIndex);\n    // Update the original columns array\n    this.allPossibleColumns = columns;\n    // Fire the change detector so that the table will update all columns. (Without this, the column widths aren't re-set until\n    // closing the menu.\n    this.changeDetectorRef.detectChanges();\n  }\n}\n```\n\n### Density\n\nThe table supports two density settings: `comfy` (default) & `compact`.\n\n```html\n<table ts-table density=\"compact\">\n  ...\n</table>\n```\n\n### Outer border\n\nSince the scrolling container around the table is now a consumer responsibility, adding a border around the table also\nmust be done by the consumer:\n\n```scss\n.my-table-container {\n  border: 1px solid color(utility, light);\n}\n```\n\n### Scrolling\n\nScrolling is controlled by the containing element that is placed around the table. If persistent scrollbars are desired,\nthe scrollbars mixin can be used:\n\n```scss\n.my-table-container {\n  @include visible-scrollbars;\n}\n```\n\n### Events\n\n#### Table\n\n| Event           | Description                      | Payload                     |\n|:----------------|:---------------------------------|:----------------------------|\n| `columnsChange` | Fired when any column is changed | `TsTableColumnsChangeEvent` |\n\n> NOTE: This event is not throttled or debounced and may be called repeatedly.\n\n```html\n<table ts-table (columnsChange)=\"whenColumnsChange($event)\">\n  ...\n</table>\n```\n\nThe `TsTableColumnsChangeEvent` structure:\n\n```typescript\nclass TsTableColumnsChangeEvent {\n  constructor(\n    // The table instance that originated the event\n    public table: TsTableComponent,\n    // The updated array of columns\n    public columns: TsColumn[],\n  ) {}\n}\n```\n\n#### Cell\n\n| Event     | Description                           | Payload                   |\n|:----------|:--------------------------------------|:--------------------------|\n| `resized` | Fired when the header cell is resized | `TsHeaderCellResizeEvent` |\n\n```html\n<ng-container tsColumnDef=\"title\">\n  <th ts-header-cell *tsHeaderCellDef (resized)=\"whenCellIsResized($event)\">\n    Title\n  </th>\n  <td ts-cell *tsCellDef=\"let item\">\n    {{ item.title }}\n  </td>\n</ng-container>\n```\n\n```typescript\nclass TsHeaderCellResizeEvent {\n  constructor(\n    // The header cell instance that originated the event\n    public instance: TsHeaderCellDirective,\n    // The new width\n    public width: string,\n  ) {}\n}\n```\n\n### Full example with pagination, sorting, and dynamic columns\n\n```typescript\nimport { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { merge, Observable, of } from 'rxjs';\nimport { startWith } from 'rxjs/operators/startWith';\nimport { map } from 'rxjs/operators/map';\nimport { switchMap } from 'rxjs/operators/switchMap';\nimport { catchError } from 'rxjs/operators/catchError';\nimport { TsSortDirective } from '@terminus/ui-sort';\nimport { TsTableDataSource, TsColumn } from '@terminus/ui-table';\nimport { TsPaginatorComponent } from '@terminus/ui-paginator';\n\n@Component({\n  selector: 'my-component',\n  template: `\n    <!-- Align the select to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-select\n        label=\"Show/hide columns\"\n        [multipleAllowed]=\"true\"\n        valueKey=\"value\"\n        [(ngModel)]=\"displayedColumns\"\n        [items]=\"allColumns\"\n      ></ts-select>\n    </div>\n\n    <table\n      ts-table\n      [dataSource]=\"dataSource\"\n      [columns]=\"displayedColumns\"\n      tsSort\n      #myTable=\"tsTable\"\n    >\n      <ng-container tsColumnDef=\"created\">\n        <th ts-header-cell *tsHeaderCellDef ts-sort-header>Created</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.created_at | date:shortDate }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer A</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"number\" alignment=\"right\">\n        <th ts-header-cell *tsHeaderCellDef>Number</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.number }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer B</td>\n      </ng-container>\n\n      <ng-container tsColumnDef=\"title\">\n        <th ts-header-cell *tsHeaderCellDef>Title</th>\n        <td ts-cell *tsCellDef=\"let item\">{{ item.title }}</td>\n        <td ts-footer-cell *tsFooterCellDef>Footer C</td>\n      </ng-container>\n\n      <tr ts-header-row *tsHeaderRowDef=\"myTable.columnNames\"></tr>\n      <tr ts-row *tsRowDef=\"let row; columns: myTable.columnNames\"></tr>\n      <tr ts-footer-row *tsFooterRowDef=\"myTable.columnNames\"></tr>\n    </table>\n\n    <!-- Align the paginator to the right side -->\n    <div fxLayout=\"row\" fxLayoutAlign=\"end center\">\n      <ts-paginator\n        [totalRecords]=\"resultsLength\"\n        recordCountTooHighMessage=\"Please refine your filters.\"\n        (pageSelect)=\"onPageSelect($event)\"\n        (recordsPerPageChange)=\"perPageChange($event)\"\n        (firstPageChosen)=\"first($event)\"\n        (previousPageChosen)=\"previous($event)\"\n        (nextPageChosen)=\"next($event)\"\n        (lastPageChosen)=\"last($event)\"\n      ></ts-paginator>\n    </div>\n  `,\n})\nexport class TableComponent implements AfterViewInit {\n  allColumns: TsColumn[] = [\n    {\n      name: 'Created',\n      value: 'created',\n      width: 100,\n    },\n    {\n      name: 'Title',\n      value: 'title',\n      width: 100,\n    },\n    {\n      name: 'Comments',\n      value: 'comments',\n      width: 200,\n    },\n    {\n      name: 'State',\n      value: 'state',\n      width: 100,\n    },\n    {\n      name: 'Number',\n      value: 'number',\n      width: 100,\n    },\n  ];\n  // Default to all columns visible\n  displayedColumns: TsColumn[] = this.allColumns.slice();\n  exampleDatabase: ExampleHttpDao | null;\n  dataSource = new TsTableDataSource<GithubIssue>();\n  resultsLength = 0;\n\n  @ViewChild(TsSortDirective, {static: true})\n  sort: TsSortDirective;\n\n  @ViewChild(TsPaginatorComponent, {static: true})\n  paginator: TsPaginatorComponent;\n\n  constructor(\n    private http: HttpClient,\n  ) {}\n\n  public ngAfterViewInit(): void {\n    this.exampleDatabase = new ExampleHttpDao(this.http);\n\n    // If the user changes the sort order, reset back to the first page.\n    this.sort.sortChange.subscribe(() => this.paginator.currentPageIndex = 0);\n\n    // Fetch new data anytime the sort is changed, the page is changed, or the records shown per page is changed\n    merge(this.sort.sortChange, this.paginator.pageSelect, this.paginator.recordsPerPageChange)\n      .pipe(\n        startWith({}),\n        switchMap(() => {\n          return this.exampleDatabase.getRepoIssues(\n            this.sort.active,\n            this.sort.direction,\n            this.paginator.currentPageIndex,\n            this.paginator.recordsPerPage,\n          );\n        }),\n        map(data => {\n          console.log('Demo: fetched data: ', data)\n          this.resultsLength = data.total_count;\n          return data.items;\n        }),\n        catchError(() => {\n          // Catch if the GitHub API has reached its rate limit. Return empty data.\n          console.warn('GitHub API rate limit has been reached!')\n          return of([]);\n        })\n      ).subscribe(data => {\n        this.dataSource.data = data;\n      });\n  }\n}\n\nexport interface GithubApi {\n  items: GithubIssue[];\n  total_count: number;\n}\n\nexport interface GithubIssue {\n  created_at: string;\n  number: string;\n  state: string;\n  title: string;\n}\n\n/**\n * An example database that this example uses to retrieve data for the table.\n */\nexport class ExampleHttpDao {\n  constructor(private http: HttpClient) {}\n\n  public getRepoIssues(sort: string, order: string, page: number, perPage: number): Observable<GithubApi> {\n    const href = `https://api.github.com/search/issues`;\n    const requestUrl = `${href}?q=repo:GetTerminus/terminus-ui`;\n    const requestParams = `&sort=${sort}&order=${order}&page=${page + 1}&per_page=${perPage}`;\n    return this.http.get<GithubApi>(`${requestUrl}${requestParams}`);\n  }\n}\n```\n\n## Change indicator\n\nA component to easily show the sentiment for a change. This is most often used in a footer summary row.\n\n```html\n<!-- The sentiment is 'positive' by default -->\n<ts-change-indicator>42%</ts-change-indicator>\n<ts-change-indicator sentiment=\"negative\">3 per week</ts-change-indicator>\n```\n\n## Test Helpers\n\nSome helpers are exposed to assist with testing. These are imported from `@terminus/ui-table/testing`;\n\n[[source]][test-helpers-src]\n\n| Function                    |\n|-----------------------------|\n| `getElements`               |\n| `getHeaderRow`              |\n| `getRows`                   |\n| `getCells`                  |\n| `getHeaderCells`            |\n| `expectTableToMatchContent` |\n\n<!-- Links -->\n[test-helpers-src]:    testing/src/test-helpers.ts\n[license-url]:         https://github.com/GetTerminus/terminus-oss/blob/release/LICENSE\n[license-image]:       http://img.shields.io/badge/license-MIT-blue.svg\n[codecov-project]:     https://codecov.io/gh/GetTerminus/terminus-oss\n[codecov-badge]:       https://codecov.io/gh/GetTerminus/terminus-oss/branch/release/graph/badge.svg\n[npm-version-image]:   http://img.shields.io/npm/v/@terminus/ui-table.svg\n[npm-package]:         https://www.npmjs.com/package/@terminus/ui-table\n[github-action-badge]: https://github.com/GetTerminus/terminus-oss/workflows/Release%20CI/badge.svg\n[github-action-link]:  https://github.com/GetTerminus/terminus-oss/actions?query=workflow%3A%22CI+Release%22\n[file-size-badge]:     http://img.badgesize.io/https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.min.js?compression=gzip\n[raw-distribution-js]: https://unpkg.com/@terminus/ui-table/bundles/terminus-ui-table.umd.js\n","readmeFilename":"README.md","description":"<h1>Table</h1>","_id":"@terminus/ui-table@4.0.7","_nodeVersion":"12.22.7","_npmVersion":"lerna/3.22.1/node@v12.22.7+x64 (linux)","dist":{"integrity":"sha512-gbJwjna+pp+NJp0o39lb2PAv+7Bx66eGMDlYP5aBM4ku0RpfKxn6i3dv52kQv1KznHuPj3LlQCGHrJBBs27n3A==","shasum":"0c75444d6d4295feb22dea0080e072a9768304cf","tarball":"https://registry.npmjs.org/@terminus/ui-table/-/ui-table-4.0.7.tgz","fileCount":70,"unpackedSize":655749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhp6v/CRA9TVsSAnZWagAAMcMP/iNYrNLoidJnEGLepgb4\nYD4GxrTvw9ieK8MjiE4wX5Chz24SKrSg6sZLgzE6jLxRxWwGXx3HIBUZG/61\njBVpD1W0EdHj3d7yKa8tZqr1haBFCwYxawyvy3lrH2jOjTxdJSZDRg8SUVAE\nSt8gvrEAkiohQTFJZN/rYQKg8F2WdZx4ZDDgBOGP7kiUNsNxszmygDel6AIs\nUOzjXXwe3wgRoZ9Us0tIEnPrhHDGrkD4oPItj2BNloOZAt3WJEhOIjbVY5e8\n23wW45Gv+TlozSSjOb37swYCNLDQvEyGD0dA2efpeH2Og40b0wmtC+4Jh6J/\ne/VuBQrU20P9actueNZiCji3k2t5VlUHGGbq6jF1LeDw3yDcST1FtxXXDP8u\nfJWAm0M7a/FhHlBBO/xpWQqMhmXmweClpkt/G5iYYatdJzj5V3wQLga/HnNR\nOANyECJrrxCUVODbyou26aZfJk0IvyCySPlQN51lKy8soNKzON1ZLCwKcQJ6\ngbl4JU4r4RyvrfK346D5ZYpMS/V9vrFoB36YLaRwq5+23zvDEnt+5ZFIhyCf\n7kl5KHHyVcd7mOcQ2QNGDHuiTGu4441vi8qBTHujVt4GzxM4mWlZTCvmQVfE\nh9cNMRrkvdDm4Dv4VOivCI2JE1fr0hxXHp7aEpM+FDLphyaKXKBFftVKGUJ5\nRr0/\r\n=Oeoq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgmqbAdfYDpfzjUSIvuWawKXHAJC39pqy+hTt42SiFsAIgCM7q9MHUVaZ/kKKsywPN86o4URyW+iYXrMHYg1uWse0="}]},"_npmUser":{"name":"terminus_devops","email":"devops@terminus.com"},"directories":{},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ui-table_4.0.7_1638378495331_0.4949789063685419"},"_hasShrinkwrap":false}},"time":{"created":"2020-06-02T04:55:49.011Z","0.0.1":"2020-06-02T04:55:49.297Z","modified":"2022-04-07T05:16:22.843Z","1.0.0":"2020-06-04T18:47:48.369Z","1.0.1":"2020-06-05T02:26:57.280Z","1.0.2":"2020-06-10T19:22:12.897Z","1.0.3":"2020-06-11T14:19:42.392Z","1.0.4":"2020-06-23T15:35:48.248Z","1.0.5":"2020-07-07T15:40:00.377Z","1.0.6":"2020-07-13T19:25:00.073Z","1.0.7":"2020-07-14T18:53:20.280Z","2.0.0":"2020-07-27T19:40:41.586Z","2.0.1":"2020-07-28T15:29:56.066Z","2.0.2":"2020-07-28T18:29:07.649Z","2.0.3":"2020-07-29T15:34:32.926Z","2.0.4":"2020-07-30T20:34:55.902Z","2.0.5":"2020-07-31T14:01:33.609Z","2.0.6":"2020-08-05T19:12:17.524Z","2.0.7":"2020-08-10T16:21:29.468Z","2.0.8":"2020-08-11T16:32:53.180Z","2.0.9":"2020-08-14T15:33:03.956Z","3.0.0":"2020-08-31T18:43:15.027Z","3.0.1":"2020-09-03T23:15:57.352Z","3.0.2":"2020-09-04T15:02:53.173Z","3.1.0":"2020-09-08T18:53:13.648Z","3.1.1":"2020-09-11T17:36:02.355Z","3.1.2":"2020-09-14T17:39:32.484Z","3.1.3":"2020-09-17T20:27:37.099Z","3.1.4":"2020-10-05T19:02:30.941Z","3.1.5":"2020-10-30T16:34:23.079Z","3.1.6":"2020-11-11T12:04:09.587Z","3.1.7":"2020-11-11T17:22:36.582Z","3.2.0":"2020-12-11T16:41:52.646Z","4.0.0":"2020-12-16T14:21:47.137Z","4.0.1":"2021-01-12T13:43:34.510Z","4.0.2":"2021-01-21T02:39:47.202Z","4.0.3":"2021-08-25T16:57:57.453Z","4.0.4":"2021-08-27T15:34:01.471Z","4.0.5":"2021-08-27T20:39:18.378Z","4.0.6":"2021-08-27T21:40:18.786Z","4.0.7":"2021-12-01T17:08:15.539Z"},"maintainers":[{"name":"bmalinconico-terminus","email":"brian.malinconico@terminus.com"},{"name":"atlwendy","email":"atlwendy@gmail.com"},{"name":"terminus_devops","email":"devops@terminus.com"}],"description":"<h1>Table</h1>","homepage":"https://github.com/GetTerminus/terminus-oss","repository":{"type":"git","url":"git+https://github.com/GetTerminus/terminus-oss.git"},"author":{"name":"@terminus"},"bugs":{"url":"https://github.com/GetTerminus/terminus-oss/issues"},"license":"MIT","readme":"","readmeFilename":""}