Skip to content

Commit

Permalink
Merge pull request #7 from matthewgallo/nested-rows-lit
Browse files Browse the repository at this point in the history
feat: add nested row lit example
  • Loading branch information
matthewgallo authored Sep 23, 2024
2 parents 48e492c + de8acfe commit d5bb0d1
Show file tree
Hide file tree
Showing 12 changed files with 1,082 additions and 0 deletions.
24 changes: 24 additions & 0 deletions web-components/nested-rows/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
6 changes: 6 additions & 0 deletions web-components/nested-rows/.sassrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"includePaths": [
"node_modules",
"../../node_modules"
]
}
15 changes: 15 additions & 0 deletions web-components/nested-rows/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Lit + TS</title>
<link rel="stylesheet" href="https://1.www.s81c.com/common/carbon-for-ibm-dotcom/tag/v1/latest/plex.css" />
<link rel="stylesheet" href="./src/index.scss" />
<script type="module" src="/src/nestedRowTable.ts"></script>
</head>
<body>
<nested-row-table></nested-row-table>
</body>
</html>
22 changes: 22 additions & 0 deletions web-components/nested-rows/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "nested-rows",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@carbon/styles": "^1.34.0",
"@carbon/web-components": "latest",
"@tanstack/lit-table": "^8.20.5",
"sass": "^1.64.1",
"lit": "^3.2.0"
},
"devDependencies": {
"typescript": "^5.5.3",
"vite": "^5.4.1"
}
}
1 change: 1 addition & 0 deletions web-components/nested-rows/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions web-components/nested-rows/src/customTypings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module '@carbon/web-components/es/icons/chevron--right/16'
9 changes: 9 additions & 0 deletions web-components/nested-rows/src/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@use '@carbon/styles/scss/reset';
@use '@carbon/styles/scss/theme';
@use '@carbon/styles/scss/themes';

:root {
@include theme.theme(themes.$white);
background-color: var(--cds-background);
color: var(--cds-text-primary);
}
76 changes: 76 additions & 0 deletions web-components/nested-rows/src/makeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { faker } from '@faker-js/faker'

export type Resource = {
id: string
name: string
rule: string
status: string
other: string
example: string
subRows?: Resource[]
}

const range = (len: number) => {
const arr: number[] = []
for (let i = 0; i < len; i++) {
arr.push(i)
}
return arr
}

const newResource = (id: string, index: number): Resource => {
return {
id,
name: `Load balancer ${index}`,
rule: faker.helpers.shuffle<Resource['rule']>([
'DNS delegation',
'Round Robin'
])[0],
status: faker.helpers.shuffle<Resource['status']>([
'starting',
'active',
'disabled',
])[0]!,
other: 'Test',
example: faker.number.int(1000).toString(),
}
}

export function makeData(...lens: number[]) {
const makeDataLevel = (depth = 0): Resource[] => {
const len = lens[depth]!
return range(len).map((index): Resource => {
return {
...newResource(`load-balancer-${index}`, index),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
}
})
}

return makeDataLevel()
}

//simulates a backend api
const data = makeData(1000)
export const fetchData = async (
start: number,
size: number,
) => {

//simulate a backend api
await new Promise(resolve => setTimeout(resolve, 2000))

return {
data: data.slice(start, start + size),
meta: {
totalRowCount: data.length,
},
}
}

export type ResourceApiResponse = {
data: Resource[]
meta: {
totalRowCount: number
}
}
220 changes: 220 additions & 0 deletions web-components/nested-rows/src/nestedRowTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { LitElement, css, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { repeat } from 'lit/directives/repeat.js'
import {
createColumnHelper,
flexRender,
getCoreRowModel,
getExpandedRowModel,
TableController,
} from '@tanstack/lit-table'
import '@carbon/web-components/es/components/data-table/index.js';
import '@carbon/web-components/es/components/button/index.js';
import ChevronRight from '@carbon/web-components/es/icons/chevron--right/16';
import { styleMap } from 'lit/directives/style-map.js'
import { makeData } from './makeData';


type Resource = {
id: string
name: string
rule: string
status: string
other: string
example: string
subRows?: Resource[]
}

const columnHelper = createColumnHelper<Resource>()

const data: Resource[] = makeData(10, 5);

/**
* An example table using `@tanstack/lit-table` and `@carbon/web-components` DataTable.
*
*/

@customElement('nested-row-table')
export class NestedRowTable extends LitElement {
private tableController = new TableController<Resource>(this);

_columns = [
columnHelper.accessor(row => row.name, {
id: 'lastName',
header: ({ table }) => html`
<div class='flex'>
<cds-button
@click=${() => {
const rows = table.getRowModel().rows;
const newExpansionState = {} as {
[ key: string]: boolean
};
if (!this._expandAll) {
rows.forEach(row => {
newExpansionState[row.id] = true;
});
this._expanded = newExpansionState;
this._expandAll = true;
} else {
rows.forEach(row => {
newExpansionState[row.id] = false;
});
this._expanded = newExpansionState;
this._expandAll = false;
}
// doesn't seem to be working, but would expand all without having to do it manually
// table.getToggleAllRowsExpandedHandler();
}}
className='row-expander'
kind="ghost"
size='sm'
>
${ChevronRight({
slot: 'icon',
class: table.getIsAllRowsExpanded() ? `row-expanded-icon` : 'row-expandable-icon',
})}
</cds-button>
<span>Name</span>
</div>`,
cell: ({ row, renderValue }) => {
return html`
<div
style='${styleMap({
paddingLeft: row.depth > 0 ? `${(row.depth * 2) + 1.5}rem` : 0,
})}'
>
<div className='flex'>
${row.getCanExpand() ? (
html`<cds-button
@click=${() => {
// row.getToggleExpandedHandler()
if (!row.getIsExpanded()) {
this._expanded = {...this._expanded, [row.id]: true};
return;
}
this._expanded = {...this._expanded, [row.id]: false};
}}
className='row-expander'
kind='ghost'
size='sm'
>
${ChevronRight({
slot: 'icon',
class: row.getIsExpanded() ? `row-expanded-icon` : 'row-expandable-icon',
})}
</cds-button>`
) : (
null
)}
${renderValue()}
</div>
</div>`},
}),
columnHelper.accessor('rule', {
header: () => 'Rule',
cell: info => info.renderValue(),
}),
columnHelper.accessor('status', {
header: () => html`<span>Status</span>`,
}),
columnHelper.accessor('other', {
header: 'Other',
}),
columnHelper.accessor('example', {
header: 'Example',
}),
]

@state()
private _expanded = {};

@state()
private _expandAll = false;

render() {
const table = this.tableController.table({
columns: this._columns,
data,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
// onExpandedChange: (row) => console.log('onChange', row),
getSubRows: row => row.subRows,
state: {
expanded: this._expanded,
}
})

return html`
<cds-table>
<cds-table-head>
${repeat(
table.getHeaderGroups(),
headerGroup => headerGroup.id,
headerGroup =>
html`<cds-table-header-row>
${repeat(
headerGroup.headers,
header => header.id,
header =>
html` <cds-table-header-cell>
${header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</cds-table-header-cell>`
)}</cds-table-header-row>`
)}
</cds-table-head>
<cds-table-body>
${repeat(
table.getRowModel().rows,
row => row.id,
row => html`
<cds-table-row>
${repeat(
row.getVisibleCells(),
cell => cell.id,
cell =>
html` <cds-table-cell>
${flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</cds-table-cell>`
)}
</cds-table-row>
`
)}
</cds-table-body>
</cds-table>
`
}

static styles = css`
:host {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
display: flex;
place-items: center;
}
.row-expandable-icon {
transition: transform 150ms ease-in;
}
.row-expanded-icon {
transform: rotate(0.25turn);
transition: transform 150ms ease-in; // replace with carbon motion easing
}
`
}

declare global {
interface HTMLElementTagNameMap {
'nested-row-table': NestedRowTable
}

}
1 change: 1 addition & 0 deletions web-components/nested-rows/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
Loading

0 comments on commit d5bb0d1

Please sign in to comment.