Skip to content

Commit

Permalink
respectColumnLevels
Browse files Browse the repository at this point in the history
  • Loading branch information
piglovesyou committed Jan 4, 2024
1 parent 2694473 commit 31d4c86
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 19 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ calcRowspanFromObjectArray(
Apply `rowspan` to an HTML table, calculated by `calcRowspanFromObjectArray`. Signature:

```ts
function mergeCells(tableEl: HTMLTableElement, columnIndexes?: number[]): void
function mergeCells(tableEl: HTMLTableElement, columnIndexes?: number[], options?: Options): void
```

### `calcRowspanFromObjectArray`
Expand All @@ -60,9 +60,16 @@ rendering. Signature:
function calcRowspanFromObjectArray<E extends Record<string, any>>(
rows: E[],
columnKeys: string[],
options?: Options,
): Record<string, number>
```

### `Options`

| property | default value | description |
| --------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `respectColumnLevels` | `true` | Whether to merge/separate cells based on column levels. Leave it to `true` if you can aggregate your table with `GROUP BY`. |

## Publishing

```shell
Expand Down
26 changes: 23 additions & 3 deletions src/merge-cells.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,29 @@ describe('lib.test.ts', () => {
['c_1', 'c_2'],
),
).toStrictEqual([
{ c_2: 1, c_1: 1 },
{ c_2: 1, c_1: 1 },
{ c_2: 1, c_1: 1 },
{ c_1: 1, c_2: 1 },
{ c_1: 1, c_2: 1 },
{ c_1: 1, c_2: 1 },
])
})

test('merge vertically regardless of levels', () => {
expect(
calcRowspanFromObjectArray(
[
{ c_1: 'a', c_2: 'a', c_3: 'c' },
{ c_1: 'a', c_2: 'b', c_3: 'c' },
{ c_1: 'b', c_2: 'b', c_3: 'a' },
],
['c_1', 'c_2', 'c_3'],
{
respectColumnLevels: false,
},
),
).toStrictEqual([
{ c_1: 2, c_2: 1, c_3: 2 },
{ c_1: 0, c_2: 2, c_3: 0 },
{ c_1: 1, c_2: 0, c_3: 1 },
])
})
})
55 changes: 40 additions & 15 deletions src/merge-cells.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ interface BaseRow<E, K extends ColumnKey> {
rowspan: Rowspan<K>
}

interface Options {
respectColumnLevels?: boolean
}

type ValueProvider<E, K extends ColumnKey> = (row: E, columnKey: K) => any

const defaultOptions: Options = {
respectColumnLevels: true,
}

export function calcRowspanRecur<E, K extends ColumnKey>(
rows: E[],
fields: K[], // Object keys or array indexes
valueProvider: ValueProvider<E, K>,
options: Options,
baseRows: (BaseRow<E, K> | undefined)[] = [],
acc: Rowspan<K>[] = [],
): Rowspan<K>[] {
Expand All @@ -30,28 +39,37 @@ export function calcRowspanRecur<E, K extends ColumnKey>(
} as BaseRow<E, K>
acc.push(rowspan)

const nextBaseRows = [] as BaseRow<E, K>[]
const nextBaseRows = options.respectColumnLevels ? ([] as BaseRow<E, K>[]) : baseRows

for (let i = 0; i < fields.length; i++) {
const field = fields[i]
const baseRow = baseRows[i]

const isNewValue = !baseRow || valueProvider(top, field) !== valueProvider(baseRow.row, field)

if (isNewValue) {
return calcRowspanRecur(
rest,
fields,
valueProvider,
[...nextBaseRows, ...Array(fields.length - nextBaseRows.length).fill(topAcc)],
acc,
)
if (options.respectColumnLevels) {
return calcRowspanRecur(
rest,
fields,
valueProvider,
options,
[...nextBaseRows, ...Array(fields.length - nextBaseRows.length).fill(topAcc)],
acc,
)
} else {
baseRows[i] = topAcc
}
} else {
baseRows[i]!!.rowspan[field]++
topAcc.rowspan[field] = 0
if (options.respectColumnLevels) {
nextBaseRows.push(baseRow)
}
}

baseRow.rowspan[field]++
topAcc.rowspan[field] = 0
nextBaseRows.push(baseRow)
}

return calcRowspanRecur(rest, fields, valueProvider, nextBaseRows, acc)
return calcRowspanRecur(rest, fields, valueProvider, options, nextBaseRows, acc)
}

export function applyRowspanToTable(tableEl: HTMLTableElement, columnIndexes?: number[]) {
Expand All @@ -78,15 +96,22 @@ export function applyRowspanToTable(tableEl: HTMLTableElement, columnIndexes?: n
export function calcRowspanWithTableRows(
rows: HTMLCollectionOf<HTMLTableRowElement>,
columnIndexes: number[] = rows[0] ? Array.from(Array(rows[0].cells.length).keys()) : [],
options: Options = defaultOptions,
) {
return calcRowspanRecur(Array.from(rows), columnIndexes, (row, fieldIndex) => row.cells[fieldIndex]!!.textContent)
return calcRowspanRecur(
Array.from(rows),
columnIndexes,
(row, fieldIndex) => row.cells[fieldIndex]!!.textContent,
options,
)
}

export function calcRowspanFromObjectArray<E extends Record<string, any>>(
rows: E[],
columnKeys: string[],
options: Options = defaultOptions,
): Rowspan<string>[] {
return calcRowspanRecur(rows, columnKeys, (row, field) => row[field]!!)
return calcRowspanRecur(rows, columnKeys, (row, field) => row[field]!!, options)
}

export const mergeCells = applyRowspanToTable
Expand Down

0 comments on commit 31d4c86

Please sign in to comment.