The NestedTable Component renders a table with rows that can be nested within one another and columns that can sort said rows.

# Usage

## Properties

### `rows` (required)
A list of `TableRow` objects.  This will populate our rows in the table.  If a row has children, then you must pass its children in as well - otherwise they won't be displayed.  The rows are displayed in the order they are listed.

### `cols` (required)
A list of TableCol objects.  This will populate our columns in the table.  The columns are displayed in the order they are listed.

### `values`
A nested JS Object that maps `rowId` -> `colId` -> `TableValue` object.  This is what's used to populate the cells in our table.  If not set, then cells won't show anything.

### `sorter`
A `TableSorter` object to be passed to our columns.  The `TableSorter` doesn't actually do our sorting, instead it contains information about what column is being used for sorting and what type of sort it is.  It's your responsibility to provide the rows property with a list of sorted rows.

### `onSortByCol`
Function to call when we sort by a column.  The function will receive a `TableSorter` object that contains info about our sort.

### `loading`
Boolean indicating if our table is loading or not.  If set, then the table will display a loading animation for our cells and disable interaction with any of our table cells (i.e. cannot sort columns or expand/collapse rows).

### `loadedValues`
A nested JS Object that maps `rowId` -> `colId` -> bool.  This is optional, but if used correctly it can allow the table to render values as they arrive rather than only showing them once all cells have been loaded.  While loading, we'll query this to see if a cell already has data to be displayed.  If we do, then we won't show a loading animation in that cell anymore and instead show the value mapped in our `values` property.

### `urlPrefix`
This is passed to our cells and use to make them clickable.  In hindsight this isn't very good design and might be changed at a later date.

## Nested Rows
Any row can have children rows which appear nested within that row.  Rows with children can be expanded and collapsed.  If a row is collapsed, then all of its children are hidden.  When expanded, a row displays all of its children.

Each row has a unique `id` and `rowId`.  The `rowId` is a composition of a row's parent `rowId` as well as its own id.  

For example, say we have a row with the id `CAPE`.  Since it has no parent, it's `rowId` is also `CAPE`.  Within this row is a child row with the id `DTA`.  Since this child row does have a parent, it's `rowId` is `CAPE.DTA`.  Were it to have children as well (let's say a row with the id `rps` and one with `skadi`), then those children ids would be: `CAPE.DTA.rps` and `CAPE.DTA.skadi`.  Listing this out we'd see:

- `CAPE`
- `CAPE.DTA`
- `CAPE.DTA.rps`
- `CAPE.DTA.skadi`

The `rowId` gives us a means of figuring out what rows a row is related to.  In addition it's used to compute our rowLevel.  The rowLevel let's now how nested a row is.  it is simply the number of `.` in our `rowId`.  So in the above example or levels would be:

- `CAPE` (rowLevel = 0)
- `CAPE.DTA` (rowLevel = 1)
- `CAPE.DTA.rps` (rowLevel = 2)
- `CAPE.DTA.skadi` (rowLevel = 2)

## Column Sorting
Clicking a column can sort the rows by said column.  Sorting is either done in `ASCENDING` or `DESCENDING` order (these are reflected by the `TableSortType` enum).  We use the values in a given column for each row when sorting (i.e. cell value).

**Note:** The actual sorting logic is ***NOT*** handled by the NestedTable.  It only handles the styling for the sort, not the sort itself.  Instead, it will pass its `onSortByCol()` property a `TableSorter` object with information about the sort.  it's up to you to handle the sort outside of this class and pass a sorted list of rows to to the `rows` property.
