diff --git a/docs/api.md b/docs/api.md index 55ca580..0d969a8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -97,6 +97,19 @@ The following options are supported via the main options object passed to `useTa - Defaults to `initialState` - This key is used to look for the initial state of a row when initializing the `rowState` for a`data` array. - If the value located at `row[initialRowStateKey]` is falsey, `{}` will be used instead. +- `getSubRows: Function(row, relativeIndex) => Rows[]` + - Optional + - Must be **memoized** + - Defaults to `(row) => row.subRows || []` + - Use this function to change how React Table detects subrows. You could even use this function to generate sub rows if you want. + - By default, it will attempt to return the `subRows` property on the row, or an empty array if that is not found. +- `getRowPathID: Function(row, relativeIndex) => string` + - Optional + - Must be **memoized** + - Defaults to `(row, relativeIndex) => relativeIndex` + - Use this function to change how React Table constructs each row's underlying `path` property. + - You may want to change this function if + - By default, it will attempt to return the `subRows` property on the row, or an empty array if that is not found. - `debug: Bool` - Optional - A flag to turn on debug mode. @@ -143,17 +156,24 @@ The following options are supported on any column object you can pass to `column The following properties are available on the table instance returned from `useTable` -- `headers[] Array` - - A **nested** array of final column objects, similar in structure to the original columns configuration option. - - See [Column Properties](#column-properties) for more information - `columns: Array` - - A **flat** array of all final column objects computed from the original columns configuration option. + - A **nested** array of final column objects, **similar in structure to the original columns configuration option**. + - See [Column Properties](#column-properties) for more information +- `flatColumns: Array` + - A **flat** array of all final column objects. - See [Column Properties](#column-properties) for more information - `headerGroups: Array` - An array of normalized header groups, each containing a flattened array of final column objects for that row. + - **Some of these headers may be materialized as placeholders** - See [Header Group Properties](#headergroup-properties) for more information +- `headers: Array` + - An **nested** array of final header objects, **similar in structure to the original columns configuration option, but rebuilt for ordering** + - Each contains the headers that are displayed underneath it. + - **Some of these headers may be materialized as placeholders** + - See [Column Properties](#column-properties) for more information - `flatHeaders[] Array` - - A **flat** array of final header objects found in each header group. Columns may be duplicated (if columns are not adjacent) + - A **flat** array of final header objects found in each header group. + - **Some of these headers may be materialized as placeholders** - See [Column Properties](#column-properties) for more information - `rows: Array` - An array of **materialized row objects** from the original `data` array and `columns` passed into the table options @@ -182,7 +202,7 @@ The following properties are available on the table instance returned from `useT - Pass it a valid `rowPath` array, `columnID` and `updater`. The `updater` may be a value or function, similar to `React.useState`'s usage. - If `updater` is a function, it will be passed the previous value -### `HeaderGroup` Properties +### HeaderGroup Properties The following additional properties are available on every `headerGroup` object returned by the table instance. @@ -477,7 +497,11 @@ function Table({ columns, data }) { {/* Add a sort direction indicator */} - {column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''} + {column.isSorted + ? column.isSortedDesc + ? ' 🔽' + : ' 🔼' + : ''} {/* Add a sort index indicator */} ({column.isSorted ? column.sortedIndex + 1 : ''}) @@ -921,7 +945,7 @@ The following options are supported via the main options object passed to `useTa - A `pathIndex` can be set as the key and its value set to `true` to expand that row's subRows into view. For example, if `{ '3': true }` was passed as the `expanded` state, the **4th row in the original data array** would be expanded. - For nested expansion, you may **use another object** instead of a Boolean to expand sub rows. For example, if `{ '3': { '5' : true }}` was passed as the `expanded` state, then the **6th subRow of the 4th row and the 4th row of the original data array** would be expanded. - This information is stored in state since the table is allowed to manipulate the filter through user interaction. -- `subRowsKey: String` +- `getSubRows: Function(row, relativeIndex) => Rows[]` - Optional - See the [useTable hook](#table-options) for more details - `manualExpandedKey: String` diff --git a/examples/sub-components/src/App.js b/examples/sub-components/src/App.js index 5843a89..5d55c43 100644 --- a/examples/sub-components/src/App.js +++ b/examples/sub-components/src/App.js @@ -42,7 +42,7 @@ function Table({ columns: userColumns, data, renderRowSubComponent }) { headerGroups, rows, prepareRow, - columns, + flatColumns, state: [{ expanded }], } = useTable( { @@ -87,7 +87,7 @@ function Table({ columns: userColumns, data, renderRowSubComponent }) { */} {row.isExpanded ? ( - + {/* Inside it, call our renderRowSubComponent function. In reality, you coul pass whatever you want as props to diff --git a/media/src/logo.sketch b/media/src/logo.sketch index 47abd10..72b3c10 100644 Binary files a/media/src/logo.sketch and b/media/src/logo.sketch differ diff --git a/src/hooks/useTable.js b/src/hooks/useTable.js index 88417e3..bfcf399 100755 --- a/src/hooks/useTable.js +++ b/src/hooks/useTable.js @@ -8,9 +8,8 @@ import { flexRender, decorateColumnTree, makeHeaderGroups, - findMaxDepth, flattenBy, - determineColumnVisibility, + determineHeaderVisibility, } from '../utils' import { useTableState } from './useTableState' @@ -18,14 +17,10 @@ import { useTableState } from './useTableState' const propTypes = { // General data: PropTypes.array.isRequired, - columns: PropTypes.arrayOf( - PropTypes.shape({ - Cell: PropTypes.any, - Header: PropTypes.any, - }) - ).isRequired, + columns: PropTypes.arrayOf(PropTypes.object).isRequired, defaultColumn: PropTypes.object, - subRowsKey: PropTypes.string, + getSubRows: PropTypes.func, + getRowPathID: PropTypes.func, debug: PropTypes.bool, } @@ -34,6 +29,9 @@ const renderErr = const defaultColumnInstance = {} +const defaultGetSubRows = (row, index) => row.subRows || [] +const defaultGetRowPathID = (row, index) => index + export const useTable = (props, ...plugins) => { // Validate props PropTypes.checkPropTypes(propTypes, props, 'property', 'useTable') @@ -44,7 +42,8 @@ export const useTable = (props, ...plugins) => { state: userState, columns: userColumns, defaultColumn = defaultColumnInstance, - subRowsKey = 'subRows', + getSubRows = defaultGetSubRows, + getRowPathID = defaultGetRowPathID, debug, } = props @@ -89,22 +88,20 @@ export const useTable = (props, ...plugins) => { console.timeEnd('plugins') // Decorate All the columns - let headers = React.useMemo( + let columns = React.useMemo( () => decorateColumnTree(userColumns, defaultColumn), [defaultColumn, userColumns] ) - // Get the flat list of all columns - let columns = React.useMemo(() => flattenBy(headers, 'columns'), [headers]) - - // Allow hooks to decorate columns (and trigger this memoization via deps) - columns = React.useMemo(() => { + // Get the flat list of all columns andllow hooks to decorate + // those columns (and trigger this memoization via deps) + let flatColumns = React.useMemo(() => { if (process.env.NODE_ENV === 'development' && debug) console.time('hooks.columnsBeforeHeaderGroups') let newColumns = applyHooks( instanceRef.current.hooks.columnsBeforeHeaderGroups, - columns, + flattenBy(columns, 'columns'), instanceRef.current ) @@ -124,12 +121,15 @@ export const useTable = (props, ...plugins) => { // Make the headerGroups const headerGroups = React.useMemo( - () => makeHeaderGroups(columns, findMaxDepth(headers), defaultColumn), - [columns, defaultColumn, headers] + () => makeHeaderGroups(flatColumns, columns, defaultColumn), + [columns, defaultColumn, flatColumns] ) + const headers = React.useMemo(() => headerGroups[0].headers, [headerGroups]) + Object.assign(instanceRef.current, { columns, + flatColumns, headerGroups, headers, }) @@ -147,18 +147,20 @@ export const useTable = (props, ...plugins) => { // Keep the original reference around const original = originalRow + const rowID = getRowPathID(originalRow, i) + // Make the new path for the row - const path = [...parentPath, i] + const path = [...parentPath, rowID] flatRows++ rowPaths.push(path.join('.')) // Process any subRows - const subRows = originalRow[subRowsKey] - ? originalRow[subRowsKey].map((d, i) => - accessRow(d, i, depth + 1, path) - ) - : [] + let subRows = getSubRows(originalRow, i) + + if (subRows) { + subRows = subRows.map((d, i) => accessRow(d, i, depth + 1, path)) + } const row = { original, @@ -183,7 +185,7 @@ export const useTable = (props, ...plugins) => { // Create the cells and values row.values = {} - columns.forEach(column => { + flatColumns.forEach(column => { row.values[column.id] = column.accessor ? column.accessor(originalRow, i, { subRows, depth, data }) : undefined @@ -197,14 +199,14 @@ export const useTable = (props, ...plugins) => { if (process.env.NODE_ENV === 'development' && debug) console.timeEnd('getAccessedRows') return [accessedData, rowPaths, flatRows] - }, [debug, data, columns, subRowsKey]) + }, [debug, data, getRowPathID, getSubRows, flatColumns]) instanceRef.current.rows = rows instanceRef.current.rowPaths = rowPaths instanceRef.current.flatRows = flatRows // Determine column visibility - determineColumnVisibility(instanceRef.current) + determineHeaderVisibility(instanceRef.current) // Provide a flat header list for utilities instanceRef.current.flatHeaders = headerGroups.reduce( @@ -258,15 +260,15 @@ export const useTable = (props, ...plugins) => { instanceRef.current.headerGroups.forEach((headerGroup, i) => { // Filter out any headers and headerGroups that don't have visible columns headerGroup.headers = headerGroup.headers.filter(header => { - const recurse = columns => - columns.filter(column => { - if (column.columns) { - return recurse(column.columns) + const recurse = headers => + headers.filter(header => { + if (header.headers) { + return recurse(header.headers) } - return column.isVisible + return header.isVisible }).length - if (header.columns) { - return recurse(header.columns) + if (header.headers) { + return recurse(header.headers) } return header.isVisible }) @@ -316,53 +318,51 @@ export const useTable = (props, ...plugins) => { props ) - const visibleColumns = instanceRef.current.columns.filter( - column => column.isVisible - ) - - // Build the cells for each row - row.cells = visibleColumns.map(column => { - const cell = { - column, - row, - value: row.values[column.id], - } - - // Give each cell a getCellProps base - cell.getCellProps = props => { - const columnPathStr = [...row.path, column.id].join('_') - return mergeProps( - { - key: ['cell', columnPathStr].join('_'), - }, - applyPropHooks( - instanceRef.current.hooks.getCellProps, - cell, - instanceRef.current - ), - props - ) - } - - // Give each cell a renderer function (supports multiple renderers) - cell.render = (type, userProps = {}) => { - const Comp = typeof type === 'string' ? column[type] : type - - if (typeof Comp === 'undefined') { - throw new Error(renderErr) - } - - return flexRender(Comp, { - ...instanceRef.current, + // Build the visible cells for each row + row.cells = instanceRef.current.flatColumns + .filter(d => d.isVisible) + .map(column => { + const cell = { column, row, - cell, - ...userProps, - }) - } + value: row.values[column.id], + } - return cell - }) + // Give each cell a getCellProps base + cell.getCellProps = props => { + const columnPathStr = [...row.path, column.id].join('_') + return mergeProps( + { + key: ['cell', columnPathStr].join('_'), + }, + applyPropHooks( + instanceRef.current.hooks.getCellProps, + cell, + instanceRef.current + ), + props + ) + } + + // Give each cell a renderer function (supports multiple renderers) + cell.render = (type, userProps = {}) => { + const Comp = typeof type === 'string' ? column[type] : type + + if (typeof Comp === 'undefined') { + throw new Error(renderErr) + } + + return flexRender(Comp, { + ...instanceRef.current, + column, + row, + cell, + ...userProps, + }) + } + + return cell + }) // need to apply any row specific hooks (useExpanded requires this) applyHooks(instanceRef.current.hooks.prepareRow, row, instanceRef.current) diff --git a/src/plugin-hooks/tests/__snapshots__/useExpanded.test.js.snap b/src/plugin-hooks/tests/__snapshots__/useExpanded.test.js.snap index e867f88..a83e7e2 100644 --- a/src/plugin-hooks/tests/__snapshots__/useExpanded.test.js.snap +++ b/src/plugin-hooks/tests/__snapshots__/useExpanded.test.js.snap @@ -19,7 +19,7 @@ Snapshot Diff: @@ -73,7 +73,7 @@ Snapshot Diff:
@@ -289,7 +289,7 @@ Snapshot Diff: @@ -535,7 +535,7 @@ Snapshot Diff: - First value + Second value -@@ -101,11 +101,11 @@ +@@ -109,11 +109,11 @@ @@ -1065,7 +1065,7 @@ Snapshot Diff: - First value + Second value -@@ -101,11 +101,11 @@ +@@ -109,11 +109,11 @@ @@ -1147,7 +1147,7 @@ Snapshot Diff: - First value + Second value -@@ -213,11 +213,11 @@ +@@ -221,11 +221,11 @@ @@ -1216,7 +1216,7 @@ Snapshot Diff: - First value + Second value -@@ -269,11 +269,11 @@ +@@ -277,11 +277,11 @@ @@ -1257,7 +1257,7 @@ Snapshot Diff: - First value + Second value -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ @@ -1299,7 +1299,7 @@ Snapshot Diff: - First value + Second value -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ diff --git a/src/plugin-hooks/tests/useExpanded.test.js b/src/plugin-hooks/tests/useExpanded.test.js index 2cbf476..24af0a0 100644 --- a/src/plugin-hooks/tests/useExpanded.test.js +++ b/src/plugin-hooks/tests/useExpanded.test.js @@ -50,7 +50,7 @@ function Table({ columns: userColumns, data, SubComponent }) { headerGroups, rows, prepareRow, - columns, + flatColumns, state: [{ expanded }], } = useTable( { @@ -90,7 +90,9 @@ function Table({ columns: userColumns, data, SubComponent }) { {!row.subRows.length && row.isExpanded ? ( - + ) : null} diff --git a/src/plugin-hooks/useFilters.js b/src/plugin-hooks/useFilters.js index ce6b971..e8e72a5 100755 --- a/src/plugin-hooks/useFilters.js +++ b/src/plugin-hooks/useFilters.js @@ -33,7 +33,7 @@ function useMain(instance) { const { debug, rows, - columns, + flatColumns, filterTypes: userFilterTypes, manualFilters, disableFilters, @@ -43,7 +43,7 @@ function useMain(instance) { const preFilteredRows = rows const setFilter = (id, updater) => { - const column = columns.find(d => d.id === id) + const column = flatColumns.find(d => d.id === id) if (!column) { throw new Error(`React-Table: Could not find a column with id: ${id}`) @@ -85,7 +85,7 @@ function useMain(instance) { // Filter out undefined values Object.keys(newFilters).forEach(id => { const newFilter = newFilters[id] - const column = columns.find(d => d.id === id) + const column = flatColumns.find(d => d.id === id) const filterMethod = getFilterMethod( column.filter, userFilterTypes || {}, @@ -104,7 +104,7 @@ function useMain(instance) { }, actions.setAllFilters) } - columns.forEach(column => { + flatColumns.forEach(column => { const { id, accessor, disableFilters: columnDisableFilters } = column // Determine if a column is filterable @@ -144,7 +144,7 @@ function useMain(instance) { filteredRows = Object.entries(filters).reduce( (filteredSoFar, [columnID, filterValue]) => { // Find the filters column - const column = columns.find(d => d.id === columnID) + const column = flatColumns.find(d => d.id === columnID) if (!column) { return filteredSoFar @@ -195,12 +195,12 @@ function useMain(instance) { } return filterRows(rows) - }, [manualFilters, filters, debug, rows, columns, userFilterTypes]) + }, [manualFilters, filters, debug, rows, flatColumns, userFilterTypes]) React.useMemo(() => { // Now that each filtered column has it's partially filtered rows, // lets assign the final filtered rows to all of the other columns - const nonFilteredColumns = columns.filter( + const nonFilteredColumns = flatColumns.filter( column => !Object.keys(filters).includes(column.id) ) @@ -209,7 +209,7 @@ function useMain(instance) { nonFilteredColumns.forEach(column => { column.preFilteredRows = filteredRows }) - }, [columns, filteredRows, filters]) + }, [filteredRows, filters, flatColumns]) return { ...instance, diff --git a/src/plugin-hooks/useGroupBy.js b/src/plugin-hooks/useGroupBy.js index 3e0d374..9a5b203 100755 --- a/src/plugin-hooks/useGroupBy.js +++ b/src/plugin-hooks/useGroupBy.js @@ -48,16 +48,16 @@ export const useGroupBy = hooks => { useGroupBy.pluginName = 'useGroupBy' -function columnsBeforeHeaderGroups(columns, { state: [{ groupBy }] }) { +function columnsBeforeHeaderGroups(flatColumns, { state: [{ groupBy }] }) { // Sort grouped columns to the start of the column list // before the headers are built - const groupByColumns = groupBy.map(g => columns.find(col => col.id === g)) - const nonGroupByColumns = columns.filter(col => !groupBy.includes(col.id)) + const groupByColumns = groupBy.map(g => flatColumns.find(col => col.id === g)) + const nonGroupByColumns = flatColumns.filter(col => !groupBy.includes(col.id)) // If a groupByBoundary column is found, place the groupBy's after it const groupByBoundaryColumnIndex = - columns.findIndex(column => column.groupByBoundary) + 1 + flatColumns.findIndex(column => column.groupByBoundary) + 1 return [ ...nonGroupByColumns.slice(0, groupByBoundaryColumnIndex), @@ -72,8 +72,8 @@ function useMain(instance) { const { debug, rows, - columns, - headers, + flatColumns, + flatHeaders, groupByFn = defaultGroupByFn, manualGroupBy, disableGrouping, @@ -85,7 +85,7 @@ function useMain(instance) { ensurePluginOrder(plugins, [], 'useGroupBy', ['useExpanded']) - columns.forEach(column => { + flatColumns.forEach(column => { const { id, accessor, disableGrouping: columnDisableGrouping } = column column.isGrouped = groupBy.includes(id) column.groupedIndex = groupBy.indexOf(id) @@ -124,16 +124,15 @@ function useMain(instance) { hooks.getGroupByToggleProps = [] - // - ;[...columns, ...headers].forEach(column => { - const { canGroupBy } = column - column.getGroupByToggleProps = props => { + flatHeaders.forEach(header => { + const { canGroupBy } = header + header.getGroupByToggleProps = props => { return mergeProps( { onClick: canGroupBy ? e => { e.persist() - column.toggleGroupBy() + header.toggleGroupBy() } : undefined, style: { @@ -141,7 +140,7 @@ function useMain(instance) { }, title: 'Toggle GroupBy', }, - applyPropHooks(instance.hooks.getGroupByToggleProps, column, instance), + applyPropHooks(instance.hooks.getGroupByToggleProps, header, instance), props ) } @@ -173,7 +172,7 @@ function useMain(instance) { const aggregateRowsToValues = (rows, isSourceRows) => { const values = {} - columns.forEach(column => { + flatColumns.forEach(column => { // Don't aggregate columns that are in the groupBy if (groupBy.includes(column.id)) { values[column.id] = rows[0] ? rows[0].values[column.id] : null @@ -266,7 +265,7 @@ function useMain(instance) { groupBy, debug, rows, - columns, + flatColumns, userAggregations, groupByFn, ]) diff --git a/src/plugin-hooks/useSortBy.js b/src/plugin-hooks/useSortBy.js index 7693639..25dcd53 100755 --- a/src/plugin-hooks/useSortBy.js +++ b/src/plugin-hooks/useSortBy.js @@ -49,7 +49,7 @@ function useMain(instance) { const { debug, rows, - columns, + flatColumns, orderByFn = defaultOrderByFn, sortTypes: userSortTypes, manualSorting, @@ -75,7 +75,7 @@ function useMain(instance) { const { sortBy } = old // Find the column for this columnID - const column = columns.find(d => d.id === columnID) + const column = flatColumns.find(d => d.id === columnID) const { sortDescFirst } = column // Find any existing sortBy for this column @@ -213,7 +213,7 @@ function useMain(instance) { // Filter out sortBys that correspond to non existing columns const availableSortBy = sortBy.filter(sort => - columns.find(col => col.id === sort.id) + flatColumns.find(col => col.id === sort.id) ) const sortData = rows => { @@ -224,7 +224,7 @@ function useMain(instance) { rows, availableSortBy.map(sort => { // Support custom sorting methods for each column - const column = columns.find(d => d.id === sort.id) + const column = flatColumns.find(d => d.id === sort.id) if (!column) { throw new Error( @@ -254,7 +254,7 @@ function useMain(instance) { // Map the directions availableSortBy.map(sort => { // Detect and use the sortInverted option - const column = columns.find(d => d.id === sort.id) + const column = flatColumns.find(d => d.id === sort.id) if (column && column.sortInverted) { return sort.desc @@ -279,7 +279,15 @@ function useMain(instance) { console.timeEnd('getSortedRows') return sortData(rows) - }, [manualSorting, sortBy, debug, columns, rows, orderByFn, userSortTypes]) + }, [ + manualSorting, + sortBy, + debug, + rows, + flatColumns, + orderByFn, + userSortTypes, + ]) return { ...instance, diff --git a/src/utils.js b/src/utils.js index 943d2ac..d2f4b00 100755 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,11 @@ import React from 'react' +const columnFallbacks = { + Header: () => null, + Cell: ({ cell: { value = '' } }) => value, + show: true, +} + // Find the depth of the columns export function findMaxDepth(columns, depth = 0) { return columns.reduce((prev, curr) => { @@ -38,9 +44,7 @@ export function decorateColumn(column, defaultColumn, parent, depth, index) { } column = { - Header: () => null, - Cell: ({ cell: { value = '' } }) => value, - show: true, + ...columnFallbacks, ...column, id, accessor, @@ -69,20 +73,27 @@ export function decorateColumnTree(columns, defaultColumn, parent, depth = 0) { } // Build the header groups from the bottom up -export function makeHeaderGroups(columns, maxDepth, defaultColumn) { +export function makeHeaderGroups(flatColumns, columns, defaultColumn) { const headerGroups = [] - const buildGroup = (columns, depth = 0) => { + const maxDepth = findMaxDepth(columns) + + // Build each header group from the bottom up + const buildGroup = (columns, depth) => { const headerGroup = { headers: [], } const parentColumns = [] + // Do any of these columns have parents? const hasParents = columns.some(col => col.parent) columns.forEach(column => { + // Are we the first column in this group? const isFirst = !parentColumns.length + + // What is the latest (last) parent column? let latestParentColumn = [...parentColumns].reverse()[0] // If the column has a parent, add it if necessary @@ -95,7 +106,7 @@ export function makeHeaderGroups(columns, maxDepth, defaultColumn) { }) } } else if (hasParents) { - // If other columns have parents, add a place holder if necessary + // If other columns have parents, we'll need to add a place holder if necessary const placeholderColumn = decorateColumn( { originalID: [column.id, 'placeholder', maxDepth - depth].join('_'), @@ -131,23 +142,22 @@ export function makeHeaderGroups(columns, maxDepth, defaultColumn) { 0 ) : 1 // Leaf node columns take up at least one count - headerGroup.headers.push(column) }) headerGroups.push(headerGroup) if (parentColumns.length) { - buildGroup(parentColumns) + buildGroup(parentColumns, depth + 1) } } - buildGroup(columns) + buildGroup(flatColumns, 0) return headerGroups.reverse() } -export function determineColumnVisibility(instance) { +export function determineHeaderVisibility(instance) { const { headers } = instance const handleColumn = (column, parentVisible) => { @@ -156,14 +166,15 @@ export function determineColumnVisibility(instance) { ? column.show(instance) : !!column.show : false - if (column.columns && column.columns.length) { - column.columns.forEach(subColumn => + + if (column.headers && column.headers.length) { + column.headers.forEach(subColumn => handleColumn(subColumn, column.isVisible) ) } } - headers.forEach(subColumn => handleColumn(subColumn, true)) + headers.forEach(subHeader => handleColumn(subHeader, true)) } export function getBy(obj, path, def) {
-@@ -168,10 +170,53 @@ +@@ -172,10 +174,53 @@ @@ -505,7 +505,7 @@ Snapshot Diff: -@@ -214,11 +216,11 @@ +@@ -218,11 +220,11 @@ class="" > -@@ -246,10 +248,30 @@ +@@ -250,10 +252,30 @@ -@@ -157,11 +157,11 @@ +@@ -165,11 +165,11 @@ -@@ -213,11 +213,11 @@ +@@ -221,11 +221,11 @@ -@@ -269,11 +269,11 @@ +@@ -277,11 +277,11 @@ -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ -@@ -381,11 +381,11 @@ +@@ -389,11 +389,11 @@ -@@ -437,11 +437,11 @@ +@@ -445,11 +445,11 @@ -@@ -493,11 +493,11 @@ +@@ -501,11 +501,11 @@ -@@ -549,11 +549,11 @@ +@@ -557,11 +557,11 @@ -@@ -605,11 +605,11 @@ +@@ -613,11 +613,11 @@ -@@ -661,11 +661,11 @@ +@@ -669,11 +669,11 @@ -@@ -717,11 +717,11 @@ +@@ -725,11 +725,11 @@ -@@ -773,11 +773,11 @@ +@@ -781,11 +781,11 @@ -@@ -829,11 +829,11 @@ +@@ -837,11 +837,11 @@ -@@ -885,11 +885,11 @@ +@@ -893,11 +893,11 @@ -@@ -941,11 +941,11 @@ +@@ -949,11 +949,11 @@ -@@ -997,11 +997,11 @@ +@@ -1005,11 +1005,11 @@ -@@ -1053,11 +1053,11 @@ +@@ -1061,11 +1061,11 @@ -@@ -1109,11 +1109,11 @@ +@@ -1117,11 +1117,11 @@ -@@ -1165,11 +1165,11 @@ +@@ -1173,11 +1173,11 @@ -@@ -1221,11 +1221,11 @@ +@@ -1229,11 +1229,11 @@ -@@ -1277,11 +1277,11 @@ +@@ -1285,11 +1285,11 @@ -@@ -1333,11 +1333,11 @@ +@@ -1341,11 +1341,11 @@ -@@ -1389,11 +1389,11 @@ +@@ -1397,11 +1397,11 @@ -@@ -1445,11 +1445,11 @@ +@@ -1453,11 +1453,11 @@ -@@ -1501,11 +1501,11 @@ +@@ -1509,11 +1509,11 @@ -@@ -1557,11 +1557,11 @@ +@@ -1565,11 +1565,11 @@ -@@ -1613,11 +1613,11 @@ +@@ -1621,11 +1621,11 @@ -@@ -1669,11 +1669,11 @@ +@@ -1677,11 +1677,11 @@ -@@ -1725,11 +1725,11 @@ +@@ -1733,11 +1733,11 @@ -@@ -1781,11 +1781,11 @@ +@@ -1789,11 +1789,11 @@ -@@ -1837,11 +1837,11 @@ +@@ -1845,11 +1845,11 @@ -@@ -1893,11 +1893,11 @@ +@@ -1901,11 +1901,11 @@ -@@ -1949,11 +1949,11 @@ +@@ -1957,11 +1957,11 @@ -@@ -2005,11 +2005,11 @@ +@@ -2013,11 +2013,11 @@ -@@ -2061,11 +2061,11 @@ +@@ -2069,11 +2069,11 @@ -@@ -2098,15 +2098,52 @@ +@@ -2106,15 +2106,52 @@
-@@ -157,11 +157,11 @@ +@@ -165,11 +165,11 @@ -@@ -213,11 +213,11 @@ +@@ -221,11 +221,11 @@ -@@ -269,11 +269,11 @@ +@@ -277,11 +277,11 @@ -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ -@@ -381,11 +381,11 @@ +@@ -389,11 +389,11 @@ -@@ -437,11 +437,11 @@ +@@ -445,11 +445,11 @@ -@@ -493,11 +493,11 @@ +@@ -501,11 +501,11 @@ -@@ -549,11 +549,11 @@ +@@ -557,11 +557,11 @@ -@@ -605,11 +605,11 @@ +@@ -613,11 +613,11 @@ -@@ -661,11 +661,11 @@ +@@ -669,11 +669,11 @@ -@@ -717,11 +717,11 @@ +@@ -725,11 +725,11 @@ -@@ -773,11 +773,11 @@ +@@ -781,11 +781,11 @@ -@@ -829,11 +829,11 @@ +@@ -837,11 +837,11 @@ -@@ -885,11 +885,11 @@ +@@ -893,11 +893,11 @@ -@@ -941,11 +941,11 @@ +@@ -949,11 +949,11 @@ -@@ -997,11 +997,11 @@ +@@ -1005,11 +1005,11 @@ -@@ -1053,11 +1053,11 @@ +@@ -1061,11 +1061,11 @@ -@@ -1109,11 +1109,11 @@ +@@ -1117,11 +1117,11 @@ -@@ -1165,11 +1165,11 @@ +@@ -1173,11 +1173,11 @@ -@@ -1221,11 +1221,11 @@ +@@ -1229,11 +1229,11 @@ -@@ -1277,11 +1277,11 @@ +@@ -1285,11 +1285,11 @@ -@@ -1333,11 +1333,11 @@ +@@ -1341,11 +1341,11 @@ -@@ -1389,11 +1389,11 @@ +@@ -1397,11 +1397,11 @@ -@@ -1445,11 +1445,11 @@ +@@ -1453,11 +1453,11 @@ -@@ -1501,11 +1501,11 @@ +@@ -1509,11 +1509,11 @@ -@@ -1557,11 +1557,11 @@ +@@ -1565,11 +1565,11 @@ -@@ -1613,11 +1613,11 @@ +@@ -1621,11 +1621,11 @@ -@@ -1669,11 +1669,11 @@ +@@ -1677,11 +1677,11 @@ -@@ -1725,11 +1725,11 @@ +@@ -1733,11 +1733,11 @@ -@@ -1781,11 +1781,11 @@ +@@ -1789,11 +1789,11 @@ -@@ -1837,11 +1837,11 @@ +@@ -1845,11 +1845,11 @@ -@@ -1893,11 +1893,11 @@ +@@ -1901,11 +1901,11 @@ -@@ -1949,11 +1949,11 @@ +@@ -1957,11 +1957,11 @@ -@@ -2005,11 +2005,11 @@ +@@ -2013,11 +2013,11 @@ -@@ -2061,11 +2061,11 @@ +@@ -2069,11 +2069,11 @@ -@@ -2098,52 +2098,15 @@ +@@ -2106,52 +2106,15 @@
-@@ -213,11 +213,11 @@ +@@ -221,11 +221,11 @@ -@@ -269,11 +269,11 @@ +@@ -277,11 +277,11 @@ -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ -@@ -2098,15 +2098,20 @@ +@@ -2106,15 +2106,20 @@
-@@ -269,11 +269,11 @@ +@@ -277,11 +277,11 @@ -@@ -325,11 +325,11 @@ +@@ -333,11 +333,11 @@ -@@ -2098,20 +2098,17 @@ +@@ -2106,20 +2106,17 @@
-@@ -2098,17 +2098,18 @@ +@@ -2106,17 +2106,18 @@
-@@ -2098,18 +2098,19 @@ +@@ -2106,18 +2106,19 @@
-@@ -2098,19 +2098,18 @@ +@@ -2106,19 +2106,18 @@
{SubComponent({ row })} + {SubComponent({ row })} +