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 66d1319 commit 31b736a
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 48 deletions.
38 changes: 10 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# merge-cells [![Node.js CI](https://github.com/piglovesyou/merge-cells/actions/workflows/node.js.yml/badge.svg)](https://github.com/piglovesyou/merge-cells/actions/workflows/node.js.yml)

Provide JavaScript/TypeScript functions to help you merge cells of an HTML `GROUP BY`-like table.
Provide JavaScript/TypeScript functions to help you merge cells with vertically the same values.

👉 Example: https://piglovesyou.github.io/merge-cells/

Expand All @@ -10,7 +10,7 @@ Example usage in a browser:

```html
<script type="module" defer>
import { mergeCells } from 'https://cdn.jsdelivr.net/npm/merge-cells@latest/dist/merge-cells.min.js'
import { mergeCells } from 'https://cdn.jsdelivr.net/npm/merge-cells@latest'
const tableEl = document.getElementById('table')
mergeCells(tableEl)
Expand Down Expand Up @@ -41,20 +41,14 @@ calcRowspanFromObjectArray(
// ]
```

## Available functions

### `mergeCells`

Apply `rowspan` to an HTML table, calculated by `calcRowspanFromObjectArray`. Signature:

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

Example:

```ts
const tableEl = document.getElementById('table') as HTMLTableElement
mergeCells(tableEl)
function mergeCells(tableEl: HTMLTableElement, columnIndexes?: number[], options?: Options): void
```

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

Example:
### `Options`

```ts
expect(
calcRowspanFromObjectArray(
[
{ c_1: 'a', c_2: 'a' },
{ c_1: 'a', c_2: 'a' },
{ c_1: 'b', c_2: 'a' },
],
['c_1', 'c_2'],
),
).toStrictEqual([
{ c_1: 2, c_2: 2 },
{ c_1: 0, c_2: 0 },
{ c_1: 1, c_2: 1 },
])
```
| 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

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
],
"author": "Soichi Takamura <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/piglovesyou/merge-cells/issues"
},
"homepage": "https://github.com/piglovesyou/merge-cells#readme",
"devDependencies": {
"@babel/preset-env": "^7.23.6",
"@babel/preset-typescript": "^7.23.3",
Expand Down
4 changes: 2 additions & 2 deletions scripts/index.pug
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ html(lang="en")

.container
h1= pageTitle
p Provide JavaScript/TypeScript functions to help you merge cells of an HTML #[code GROUP BY]-like table.
p Go to #[a(href="https://github.com/piglovesyou/merge-cells") github.com/piglovesyou/merge-cells] for usage and more.
p Provide JavaScript/TypeScript functions to help you merge cells with vertically the same values.
p Go to #[a(href="https://github.com/piglovesyou/merge-cells") github.com/piglovesyou/merge-cells] for the usage and more.

table#table.table.table-bordered
thead
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 31b736a

Please sign in to comment.