diff --git a/README.md b/README.md index 0d32236..5f42346 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@ # Data table extensions with `@tanstack/table` -This monorepo contains various examples of how to build complex data tables with the `@tanstack/table` library. There will be support for both React and web components for each example included here. +This monorepo contains various examples of how to build complex data tables with +the `@tanstack/table` library. There will be support for both React and web +components for each example included here. ### Why? -The work and approach here is an iteration of the `Datagrid` component from `@carbon/ibm-products`. Some of the challenges faced with building and maintaining this component is that it is _not_ composable and thus creates a barrier for the flexibility of experiences that can be created with it. +The work and approach here is an iteration of the `Datagrid` component from +`@carbon/ibm-products`. Some of the challenges faced with building and +maintaining this component is that it is _not_ composable and thus creates a +barrier for the flexibility of experiences that can be created with it. -The example based approach outlined within this repo provides teams with a launch point for creating complex data table interactions within the Carbon Design System. The key difference is that this functionality is not wrapped around one component (as is the case with the `Datagrid` from `@carbon/ibm-products`). +The example based approach outlined within this repo provides teams with a +launch point for creating complex data table interactions within the Carbon +Design System. The key difference is that this functionality is not wrapped +around one component (as is the case with the `Datagrid` from +`@carbon/ibm-products`). -See readmes for code sandbox and stackblitz example environments. \ No newline at end of file +See readmes for code sandbox and stackblitz example environments. diff --git a/package.json b/package.json index 8615e2e..09962dc 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,16 @@ "version": "0.0.0", "license": "MIT", "scripts": { - "build": "nx run-many -t build" + "build": "nx run-many -t build", + "format": "prettier --cache --write '**/*.{js,md,scss,ts,tsx}' '!**/{build,es,lib,storybook,ts,umd,dist}/**'" }, "private": true, "dependencies": {}, "devDependencies": { "@nx/js": "^19.6.4", - "nx": "^19.6.4" + "nx": "^19.6.4", + "prettier": "^2.8.8", + "prettier-config-carbon": "^0.11.0" }, "workspaces": [ "react/*" diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..273013f --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,12 @@ +/** + * Copyright IBM Corp. 2024, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const prettierConfig = require('prettier-config-carbon'); + +module.exports = prettierConfig; diff --git a/react/README.md b/react/README.md index e8cd67d..5ba2172 100644 --- a/react/README.md +++ b/react/README.md @@ -1,18 +1,18 @@ ## Data table extensions, `@tanstack/react-table` -| Example | Code sandbox | Stackblitz -| --- | --- | --- | -| Batch actions | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/batch-actions) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/batch-actions) -| Customize column order | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/customizeColumns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/customizeColumns) -| Editable cells | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/editableCells) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/editableCells) -| Filter flyout | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/filterFlyout) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/filterFlyout) -| Filter panel | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/filterPanel) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/filterPanel) -| Global filter | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/globalFilter) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/globalFilter) -| Infinite scroll | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/infiniteScroll) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/infiniteScroll) -| Nested rows | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/nestedRows) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/nestedRows) -| Pagination | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/pagination) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/pagination) -| Column resizing | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/resizing) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/resizing) -| Row click | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/row-click) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/row-click) -| Row expansion | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/rowExpansion) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/rowExpansion) -| Sortable columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/sortable) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/sortable) -| Sticky columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/sticky-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/sticky-columns) +| Example | Code sandbox | Stackblitz | +| ---------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| Batch actions | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/batch-actions) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/batch-actions) | +| Customize column order | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/customizeColumns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/customizeColumns) | +| Editable cells | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/editableCells) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/editableCells) | +| Filter flyout | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/filterFlyout) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/filterFlyout) | +| Filter panel | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/filterPanel) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/filterPanel) | +| Global filter | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/globalFilter) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/globalFilter) | +| Infinite scroll | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/infiniteScroll) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/infiniteScroll) | +| Nested rows | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/nestedRows) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/nestedRows) | +| Pagination | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/pagination) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/pagination) | +| Column resizing | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/resizing) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/resizing) | +| Row click | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/row-click) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/row-click) | +| Row expansion | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/rowExpansion) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/rowExpansion) | +| Sortable columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/sortable) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/sortable) | +| Sticky columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/react/sticky-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/react/sticky-columns) | diff --git a/react/batch-actions/README.md b/react/batch-actions/README.md index 74872fd..f2dc982 100644 --- a/react/batch-actions/README.md +++ b/react/batch-actions/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/batch-actions/eslint.config.ts b/react/batch-actions/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/batch-actions/eslint.config.ts +++ b/react/batch-actions/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/batch-actions/src/BatchActions.tsx b/react/batch-actions/src/BatchActions.tsx index 60e35d0..70e9efc 100644 --- a/react/batch-actions/src/BatchActions.tsx +++ b/react/batch-actions/src/BatchActions.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useLayoutEffect } from 'react' +import React, { useRef, useState, useLayoutEffect } from 'react'; import { Checkbox, DataTable, Pagination, Button } from '@carbon/react'; import { TrashCan, Add, Save, Download } from '@carbon/react/icons'; const { @@ -27,52 +27,49 @@ import { PaginationState, getPaginationRowModel, getFilteredRowModel, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; // A TanStack fork of Kent C. Dodds' match-sorter library that provides ranking information -import { - rankItem, -} from '@tanstack/match-sorter-utils' +import { rankItem } from '@tanstack/match-sorter-utils'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item - const itemRank = rankItem(row.getValue(columnId), value) + const itemRank = rankItem(row.getValue(columnId), value); // Store the itemRank info addMeta({ itemRank, - }) + }); // Return if the item should be filtered in/out - return itemRank.passed -} - + return itemRank.passed; +}; export const BatchActions = () => { - const columnHelper = createColumnHelper() - - const [globalFilter, setGlobalFilter] = React.useState('') - const [rowSelection, setRowSelection] = React.useState({}) - const [data] = useState(makeData(200)) + const columnHelper = createColumnHelper(); + + const [globalFilter, setGlobalFilter] = React.useState(''); + const [rowSelection, setRowSelection] = React.useState({}); + const [data] = useState(makeData(200)); const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: 10, - }) + }); const columns = [ { @@ -100,7 +97,7 @@ export const BatchActions = () => { }, id: 'batch-checkbox', labelText: 'header checkbox', - hideLabel: true + hideLabel: true, }} /> ), @@ -113,19 +110,19 @@ export const BatchActions = () => { onChange: row.getToggleSelectedHandler(), id: `batch-checkbox__${row.id}`, labelText: 'row checkbox', - hideLabel: true + hideLabel: true, }} /> ), }, - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -136,7 +133,7 @@ export const BatchActions = () => { columnHelper.accessor('example', { header: 'Example', }), - ] + ]; const table = useReactTable({ data, @@ -148,7 +145,7 @@ export const BatchActions = () => { state: { pagination, rowSelection, - globalFilter + globalFilter, }, onGlobalFilterChange: setGlobalFilter, globalFilterFn: 'fuzzy', //apply fuzzy filter to the global filter (most common use case for fuzzy filter) @@ -156,10 +153,10 @@ export const BatchActions = () => { fuzzy: fuzzyFilter, //define as a filter function that can be used in column definitions }, // enableRowSelection: true, //enable row selection for all rows - enableRowSelection: row => row.original.status !== 'disabled', // conditionally disable rows - // enableRowSelection: row => row.original.age > 18, // or enable row selection + enableRowSelection: (row) => row.original.status !== 'disabled', // conditionally disable rows + // enableRowSelection: row => row.original.age > 18, // or enable row selection onRowSelectionChange: setRowSelection, - }) + }); const tableWrap = useRef(); @@ -177,14 +174,27 @@ export const BatchActions = () => { - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}> { onSelectAll={() => { table.toggleAllRowsSelected(true); }} - totalCount={data?.length} - > - table.resetRowSelection()}> + totalCount={data?.length}> + table.resetRowSelection()}> Delete - table.resetRowSelection()}> + table.resetRowSelection()}> Delete - table.resetRowSelection()}> + table.resetRowSelection()}> Save - table.resetRowSelection()}> + table.resetRowSelection()}> Download @@ -212,7 +237,9 @@ export const BatchActions = () => { ) => setGlobalFilter(event.target.value)} + onChange={(event: React.ChangeEvent) => + setGlobalFilter(event.target.value) + } placeholder="Search all columns..." /> @@ -226,42 +253,36 @@ export const BatchActions = () => { Action 3 - - +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( - + {headerGroup.headers.map((header) => ( + {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( - + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -281,14 +302,14 @@ export const BatchActions = () => { pageSizes={[10, 20, 30, 40, 50]} itemsPerPageText={'Items per page:'} onChange={({ pageSize, page }) => { - table.setPageSize(Number(pageSize)) - table.setPageIndex(page - 1) + table.setPageSize(Number(pageSize)); + table.setPageIndex(page - 1); }} /> -

- {Object.keys(rowSelection).length} of{' '} +

+ {Object.keys(rowSelection).length} of{' '} {table.getPreFilteredRowModel().rows.length} Total Rows Selected -

+

- ) -} + ); +}; diff --git a/react/batch-actions/src/ExampleLink.tsx b/react/batch-actions/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/batch-actions/src/ExampleLink.tsx +++ b/react/batch-actions/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/batch-actions/src/customTypings.d.ts b/react/batch-actions/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/batch-actions/src/customTypings.d.ts +++ b/react/batch-actions/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/batch-actions/src/index.scss b/react/batch-actions/src/index.scss index f8f9347..b7ef722 100644 --- a/react/batch-actions/src/index.scss +++ b/react/batch-actions/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/batch-actions/src/index.ts b/react/batch-actions/src/index.ts index 694ac68..7c19e07 100644 --- a/react/batch-actions/src/index.ts +++ b/react/batch-actions/src/index.ts @@ -1,3 +1,3 @@ -import { BatchActions } from "./BatchActions"; +import { BatchActions } from './BatchActions'; -export { BatchActions }; \ No newline at end of file +export { BatchActions }; diff --git a/react/batch-actions/src/main.tsx b/react/batch-actions/src/main.tsx index aab8d1a..dbb835b 100644 --- a/react/batch-actions/src/main.tsx +++ b/react/batch-actions/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { BatchActions } from './BatchActions' +import { BatchActions } from './BatchActions'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/batch-actions/src/makeData.ts b/react/batch-actions/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/batch-actions/src/makeData.ts +++ b/react/batch-actions/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/batch-actions/vite.config.ts b/react/batch-actions/vite.config.ts index 6ab5f59..e08f3c6 100644 --- a/react/batch-actions/vite.config.ts +++ b/react/batch-actions/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import sass from 'sass' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import sass from 'sass'; // https://vitejs.dev/config/ export default defineConfig({ @@ -12,4 +12,4 @@ export default defineConfig({ }, }, }, -}) +}); diff --git a/react/customizeColumns/README.md b/react/customizeColumns/README.md index 74872fd..f2dc982 100644 --- a/react/customizeColumns/README.md +++ b/react/customizeColumns/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/customizeColumns/eslint.config.ts b/react/customizeColumns/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/customizeColumns/eslint.config.ts +++ b/react/customizeColumns/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/customizeColumns/src/App.scss b/react/customizeColumns/src/App.scss index 5e692aa..cba95ad 100644 --- a/react/customizeColumns/src/App.scss +++ b/react/customizeColumns/src/App.scss @@ -112,4 +112,4 @@ $horizontal-drag-height: 6rem; .visibility-checkbox { max-width: 32px; -} \ No newline at end of file +} diff --git a/react/customizeColumns/src/CustomizeColumns.tsx b/react/customizeColumns/src/CustomizeColumns.tsx index 29d0e5f..5c039f7 100644 --- a/react/customizeColumns/src/CustomizeColumns.tsx +++ b/react/customizeColumns/src/CustomizeColumns.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react'; import { DataTable, IconButton } from '@carbon/react'; import { Column } from '@carbon/react/icons'; const { @@ -19,36 +19,39 @@ import { flexRender, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { TearsheetNarrow } from '@carbon/ibm-products'; import { Sortable } from './drag-drop/Sortable'; -import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers'; +import { + restrictToParentElement, + restrictToVerticalAxis, +} from '@dnd-kit/modifiers'; import { verticalListSortingStrategy } from '@dnd-kit/sortable'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -59,22 +62,22 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; export const CustomizeColumns = () => { const [showTearsheet, setShowTearsheet] = useState(false); - const [columnVisibility, setColumnVisibility] = React.useState({}) - const [columnOrder, setColumnOrder] = React.useState([]) + const [columnVisibility, setColumnVisibility] = React.useState({}); + const [columnOrder, setColumnOrder] = React.useState([]); const [tempNewOrder, setTempNewOrder] = useState(null); const [newVisibilityList, setNewVisibilityList] = useState(null); - - const [data] = useState(makeData(7)) + + const [data] = useState(makeData(7)); useEffect(() => { const dragItems = table.getAllLeafColumns().map((d) => d.id); setTempNewOrder(dragItems); - - // eslint-disable-next-line react-hooks/exhaustive-deps + + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const table = useReactTable({ @@ -87,64 +90,74 @@ export const CustomizeColumns = () => { }, onColumnVisibilityChange: setColumnVisibility, onColumnOrderChange: setColumnOrder, - }) + }); return ( <> - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}> - setShowTearsheet(true)}> + setShowTearsheet(true)}> -
+
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -157,44 +170,48 @@ export const CustomizeColumns = () => { open={showTearsheet} title="Customize column order" onClose={() => setShowTearsheet(false)} - actions={[{ - kind: 'secondary', - label: 'Cancel', - onClick: () => setShowTearsheet(false) - }, { - kind: 'primary', - label: 'Submit', - onClick: () => { - if (tempNewOrder) { - setColumnOrder(tempNewOrder); - } - setShowTearsheet(false); - const cols = newVisibilityList ?? table.getAllLeafColumns(); - const nonSelectedItems = table.getAllLeafColumns().filter(obj => cols.every(s => s.id !== obj.id)); - // Toggle visibility state for cols - cols.forEach(col => { - col.toggleVisibility(true); - }); - nonSelectedItems.forEach(col => { - col.toggleVisibility(false); - }) - } - }]} - > + actions={[ + { + kind: 'secondary', + label: 'Cancel', + onClick: () => setShowTearsheet(false), + }, + { + kind: 'primary', + label: 'Submit', + onClick: () => { + if (tempNewOrder) { + setColumnOrder(tempNewOrder); + } + setShowTearsheet(false); + const cols = newVisibilityList ?? table.getAllLeafColumns(); + const nonSelectedItems = table + .getAllLeafColumns() + .filter((obj) => cols.every((s) => s.id !== obj.id)); + // Toggle visibility state for cols + cols.forEach((col) => { + col.toggleVisibility(true); + }); + nonSelectedItems.forEach((col) => { + col.toggleVisibility(false); + }); + }, + }, + ]}> { + onDragEnd={(newOrder) => { setTimeout(() => { setTempNewOrder(newOrder); }, 5); }} - onVisibilityChange={cols => { + onVisibilityChange={(cols) => { console.log(cols); - setNewVisibilityList(cols) + setNewVisibilityList(cols); }} dragItems={tempNewOrder} originalColumns={table.getAllLeafColumns()} @@ -202,5 +219,5 @@ export const CustomizeColumns = () => { /> - ) -} + ); +}; diff --git a/react/customizeColumns/src/ExampleLink.tsx b/react/customizeColumns/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/customizeColumns/src/ExampleLink.tsx +++ b/react/customizeColumns/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/customizeColumns/src/customTypings.d.ts b/react/customizeColumns/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/customizeColumns/src/customTypings.d.ts +++ b/react/customizeColumns/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/customizeColumns/src/drag-drop/GridContainer.tsx b/react/customizeColumns/src/drag-drop/GridContainer.tsx index d83ae1e..f689f78 100644 --- a/react/customizeColumns/src/drag-drop/GridContainer.tsx +++ b/react/customizeColumns/src/drag-drop/GridContainer.tsx @@ -20,11 +20,12 @@ export const GridContainer = ({ `${draggableClass}__list-container`, `${draggableClass}__list-container--grid` )} - style={{ - '--col-count': columns, - '--grid-gap': gridGap, - } as React.CSSProperties} - > + style={ + { + '--col-count': columns, + '--grid-gap': gridGap, + } as React.CSSProperties + }> {children} ); diff --git a/react/customizeColumns/src/drag-drop/Item.tsx b/react/customizeColumns/src/drag-drop/Item.tsx index 7df0dc8..86d3613 100644 --- a/react/customizeColumns/src/drag-drop/Item.tsx +++ b/react/customizeColumns/src/drag-drop/Item.tsx @@ -22,7 +22,7 @@ export const Item = ({ wrapperStyle, transform, transition, - className + className, }) => { const draggableClass = `c4p__draggable-item`; return ( @@ -47,8 +47,7 @@ export const Item = ({ {...attributes} {...listeners} role="option" - aria-selected - > + aria-selected> {assistiveText} diff --git a/react/customizeColumns/src/drag-drop/ListContainer.tsx b/react/customizeColumns/src/drag-drop/ListContainer.tsx index 720cb4c..cf76d81 100644 --- a/react/customizeColumns/src/drag-drop/ListContainer.tsx +++ b/react/customizeColumns/src/drag-drop/ListContainer.tsx @@ -14,8 +14,7 @@ export const ListContainer = ({ children, draggableClass, type }) => { className={cx(`${draggableClass}__list-container`, { [`${draggableClass}__list-container--horizontal`]: type === 'horizontal', - })} - > + })}> {children} ); diff --git a/react/customizeColumns/src/drag-drop/Sortable.tsx b/react/customizeColumns/src/drag-drop/Sortable.tsx index 0baedbb..e93cf37 100644 --- a/react/customizeColumns/src/drag-drop/Sortable.tsx +++ b/react/customizeColumns/src/drag-drop/Sortable.tsx @@ -123,7 +123,7 @@ export const Sortable = ({ const overIndex = getIndex(over.id); if (activeIndex !== overIndex) { setItems((items) => { - arrayMove(items, activeIndex, overIndex) + arrayMove(items, activeIndex, overIndex); onDragEnd(arrayMove(items, activeIndex, overIndex)); }); } @@ -144,8 +144,7 @@ export const Sortable = ({ const sensors = useSensors(pointerSensor, keyboardSensor); - return ( - items ? + return items ? ( + {...args}> - + - {items && items.length && items?.map((i, index: number) => { - const originalCol = originalColumns.filter(c => c.id === i)[0]; - const currentRowChecked = tempVisible.some(e => e.id === originalCol.id); - return ( - - - { - console.log(checked); - if (!checked) { - const cloneVisible = [...tempVisible]; - const newCols = cloneVisible.filter(c => c.id !== id); - setTempVisible(newCols); - onVisibilityChange(newCols); - return; - } else { - const cloneVisible = [...tempVisible]; - cloneVisible.push(originalCol); - setTempVisible(cloneVisible); - onVisibilityChange(cloneVisible); - } - }} - checked={currentRowChecked} - /> - {i} - - )})} + {items && + items.length && + items?.map((i, index: number) => { + const originalCol = originalColumns.filter((c) => c.id === i)[0]; + const currentRowChecked = tempVisible.some( + (e) => e.id === originalCol.id + ); + return ( + + + + + { + console.log(checked); + if (!checked) { + const cloneVisible = [...tempVisible]; + const newCols = cloneVisible.filter((c) => c.id !== id); + setTempVisible(newCols); + onVisibilityChange(newCols); + return; + } else { + const cloneVisible = [...tempVisible]; + cloneVisible.push(originalCol); + setTempVisible(cloneVisible); + onVisibilityChange(cloneVisible); + } + }} + checked={currentRowChecked} + /> + {i} + + ); + })} - : null - ); + + ) : null; }; Sortable.propTypes = { diff --git a/react/customizeColumns/src/drag-drop/SortableItem.tsx b/react/customizeColumns/src/drag-drop/SortableItem.tsx index a9c8762..162610d 100644 --- a/react/customizeColumns/src/drag-drop/SortableItem.tsx +++ b/react/customizeColumns/src/drag-drop/SortableItem.tsx @@ -19,7 +19,7 @@ export const SortableItem = ({ useDragOverlay, wrapperStyle, index, - className + className, }) => { const { active, @@ -46,8 +46,7 @@ export const SortableItem = ({ assistiveText={assistiveText} dragOverlay={!useDragOverlay && isDragging} wrapperStyle={wrapperStyle?.({ index, isDragging, active, id })} - className={className} - > + className={className}> {children} ); diff --git a/react/customizeColumns/src/drag-drop/Underlay.tsx b/react/customizeColumns/src/drag-drop/Underlay.tsx index cc61aa7..5b22401 100644 --- a/react/customizeColumns/src/drag-drop/Underlay.tsx +++ b/react/customizeColumns/src/drag-drop/Underlay.tsx @@ -28,8 +28,7 @@ export const Underlay = ({ [`${draggableClass}__draggable-underlay--grid`]: grid, })} aria-hidden="true" - key={`draggable-underlay`} - > + key={`draggable-underlay`}> {items.map((i) => (
( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/customizeColumns/src/makeData.ts b/react/customizeColumns/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/customizeColumns/src/makeData.ts +++ b/react/customizeColumns/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/customizeColumns/vite.config.ts b/react/customizeColumns/vite.config.ts index 5a33944..627a319 100644 --- a/react/customizeColumns/vite.config.ts +++ b/react/customizeColumns/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/editableCells/README.md b/react/editableCells/README.md index 74872fd..f2dc982 100644 --- a/react/editableCells/README.md +++ b/react/editableCells/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/editableCells/eslint.config.ts b/react/editableCells/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/editableCells/eslint.config.ts +++ b/react/editableCells/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/editableCells/src/App.scss b/react/editableCells/src/App.scss index d08fe9e..bbf1ead 100644 --- a/react/editableCells/src/App.scss +++ b/react/editableCells/src/App.scss @@ -24,11 +24,12 @@ } // Editable cells -[role="gridcell"]:focus, [role="gridcell"] *:focus, -[role="grid"] [tabindex="0"]:focus { - @include utilities.focus-outline('outline'); +[role='gridcell']:focus, +[role='gridcell'] *:focus, +[role='grid'] [tabindex='0']:focus { + @include utilities.focus-outline('outline'); } .editable-cell-input .cds--text-input { height: 48px; -} \ No newline at end of file +} diff --git a/react/editableCells/src/EditableCells.tsx b/react/editableCells/src/EditableCells.tsx index 07483cb..4eaf1f1 100644 --- a/react/editableCells/src/EditableCells.tsx +++ b/react/editableCells/src/EditableCells.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react' +import { useState, useRef, useEffect } from 'react'; import { DataTable, TextInput } from '@carbon/react'; const { Table, @@ -7,7 +7,7 @@ const { TableContainer, TableHead, TableHeader, - TableRow + TableRow, } = DataTable; import { @@ -15,85 +15,123 @@ import { flexRender, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; +import { useKeyPress } from './hooks/useKeyPress'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const EditableCell = ({tableContainerRef, table, cell, editingId, setEditingId, id, children, ...rest}) => { +const EditableCell = ({ + tableContainerRef, + table, + cell, + editingId, + setEditingId, + id, + children, + ...rest +}) => { const [editValue, setEditValue] = useState(null); const handleEditableCellKeyDown = (event: KeyboardEvent) => { if (event.code !== 'Enter') return; setEditingId((event.target as HTMLElement).id); - } + }; const handleEditModeKeyDown = (event: KeyboardEvent) => { if (event.code === 'Enter' || event.code === 'Escape') { - table.options.meta?.updateData(cell.row.index, cell.column.id, editValue ?? cell.getValue()) + table.options.meta?.updateData( + cell.row.index, + cell.column.id, + editValue ?? cell.getValue() + ); setEditingId(null); // This is sketchy, refactor later setTimeout(() => { - const activeCell = tableContainerRef?.current.querySelector(`#cell__${id}`); + const activeCell = tableContainerRef?.current.querySelector( + `#cell__${id}` + ); activeCell.tabIndex = 0; activeCell.focus(); }, 10); } - } - + }; + const { style } = rest; - return editingId === `cell__${id}` - ?
- // @ts-expect-error TableCell doesn't like passing onKeyDown - : {children}; -} + return editingId === `cell__${id}` ? ( + + ) : ( + + {children} + + ); +}; export const EditableCells = () => { - const columnHelper = createColumnHelper() - + const columnHelper = createColumnHelper(); + + const commandLeft = useKeyPress([ + 'MetaLeft+ArrowLeft', + 'MetaRight+ArrowLeft', + ]); + const captureCommandLeft = useRef(false); + + useEffect(() => { + captureCommandLeft.current = commandLeft; + }, [commandLeft]); + const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => info.getValue(), + cell: (info) => info.getValue(), header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -104,35 +142,35 @@ export const EditableCells = () => { columnHelper.accessor('example', { header: 'Example', }), - ] + ]; const tableContainer = useRef(); - const [data, setData] = useState(makeData(7)) + const [data, setData] = useState(makeData(7)); const [editingId, setEditingId] = useState(null); - + const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), meta: { updateData: (rowIndex, columnId, value) => { - setData(old => + setData((old) => old.map((row, index) => { if (index === rowIndex) { return { ...old[rowIndex]!, [columnId]: value, - } + }; } - return row + return row; }) - ) + ); }, }, - }) + }); type HTMLElementEvent = Event & { target: T; - } + }; const removeActiveCell = () => { if (editingId) return; @@ -141,19 +179,20 @@ export const EditableCells = () => { cell.tabIndex = -1; }); (document.activeElement as HTMLElement).blur(); - } + }; const getActiveCell = () => { - const activeCellElement = tableContainer.current.querySelector('td[tabindex="0"]'); + const activeCellElement = + tableContainer.current.querySelector('td[tabindex="0"]'); return activeCellElement; - } + }; const addActiveCell = (target: Element) => { if (editingId) return; const activeCell = target.closest('td'); activeCell.tabIndex = 0; activeCell.focus(); - } + }; const handleFocusChange = (event: HTMLElementEvent) => { if (tableContainer?.current) { @@ -163,17 +202,22 @@ export const EditableCells = () => { } } removeActiveCell(); - addActiveCell(event.target) - } + addActiveCell(event.target); + }; const getChildElementIndex = (node: Element) => { return Array.prototype.indexOf.call(node.parentNode.children, node); - } + }; + console.log(commandLeft, captureCommandLeft.current); const handleKeyDownActiveCell = (event: KeyboardEvent) => { event.preventDefault(); const key = event.code; const activeCellElement = getActiveCell(); + // console.log(commandLeft); + if (!!commandLeft) { + return; + } // Don't enter switch if there is no active cell if (!getActiveCell()) return; switch (key) { @@ -204,7 +248,7 @@ export const EditableCells = () => { const newParentRow = parentRow.previousElementSibling; const newRowCells = newParentRow.children; removeActiveCell(); - addActiveCell(newRowCells[activeCellRowIndex]) + addActiveCell(newRowCells[activeCellRowIndex]); } return; } @@ -229,77 +273,101 @@ export const EditableCells = () => { return; } } - } + }; + + const handleMultiKeyPress = () => { + console.log('multi'); + }; return (
- - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}>
- { - // Save cell data - table.options.meta?.updateData(cell.row.index, cell.column.id, editValue ?? cell.getValue()) - setEditingId(null); - }} - onChange={e => setEditValue(e.target.value)} - // @ts-expect-error TextInput doesn't like passing onKeyDown - onKeyDown={handleEditModeKeyDown} - /> - + { + // Save cell data + table.options.meta?.updateData( + cell.row.index, + cell.column.id, + editValue ?? cell.getValue() + ); + setEditingId(null); + }} + onChange={(e) => setEditValue(e.target.value)} + // @ts-expect-error TextInput doesn't like passing onKeyDown + onKeyDown={handleEditModeKeyDown} + /> +
+ onKeyDown={ + !editingId + ? commandLeft + ? handleMultiKeyPress + : handleKeyDownActiveCell + : undefined + } + role="grid"> - {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => { + {row.getVisibleCells().map((cell) => { return ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - )})} + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ); + })} ))}
- ) -} + ); +}; diff --git a/react/editableCells/src/ExampleLink.tsx b/react/editableCells/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/editableCells/src/ExampleLink.tsx +++ b/react/editableCells/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/editableCells/src/customTypings.d.ts b/react/editableCells/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/editableCells/src/customTypings.d.ts +++ b/react/editableCells/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/editableCells/src/hooks/useKeyPress.ts b/react/editableCells/src/hooks/useKeyPress.ts new file mode 100644 index 0000000..877adea --- /dev/null +++ b/react/editableCells/src/hooks/useKeyPress.ts @@ -0,0 +1,186 @@ +// This hook is from @xyflow/react +// Wanted to avoid installing the entire package to use this one hook +import { useState, useEffect, useRef, useMemo } from 'react'; + +type KeyCode = string | Array; +type Keys = Array; +type PressedKeys = Set; +type KeyOrCode = 'key' | 'code'; + +export type UseKeyPressOptions = { + target?: Window | Document | HTMLElement | ShadowRoot | null; + actInsideInputWithModifier?: boolean; +}; + +const defaultDoc = typeof document !== 'undefined' ? document : null; + +const inputTags = ['INPUT', 'SELECT', 'TEXTAREA']; + +export function isInputDOMNode(event: KeyboardEvent): boolean { + // using composed path for handling shadow dom + const target = (event.composedPath?.()?.[0] || event.target) as HTMLElement; + const isInput = + inputTags.includes(target?.nodeName) || + target?.hasAttribute('contenteditable'); + + // when an input field is focused we don't want to trigger deletion or movement of nodes + return isInput || !!target?.closest('.nokey'); +} + +/** + * Hook for handling key events. + * + * @public + * @param param.keyCode - The key code (string or array of strings) to use + * @param param.options - Options + * @returns boolean + */ +export function useKeyPress( + // the keycode can be a string 'a' or an array of strings ['a', 'a+d'] + // a string means a single key 'a' or a combination when '+' is used 'a+d' + // an array means different possibilites. Explainer: ['a', 'd+s'] here the + // user can use the single key 'a' or the combination 'd' + 's' + keyCode: KeyCode | null = null, + options: UseKeyPressOptions = { + target: defaultDoc, + actInsideInputWithModifier: true, + } +): boolean { + const [keyPressed, setKeyPressed] = useState(false); + + // we need to remember if a modifier key is pressed in order to track it + const modifierPressed = useRef(false); + + // we need to remember the pressed keys in order to support combinations + const pressedKeys = useRef(new Set([])); + + // keyCodes = array with single keys [['a']] or key combinations [['a', 's']] + // keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft'] + // used to check if we store event.code or event.key. When the code is in the list of keysToWatch + // we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft" + // and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when + // we can't find it in the list of keysToWatch. + const [keyCodes, keysToWatch] = useMemo<[Array, Keys]>(() => { + if (keyCode !== null) { + const keyCodeArr = Array.isArray(keyCode) ? keyCode : [keyCode]; + const keys = keyCodeArr + .filter((kc) => typeof kc === 'string') + .map((kc) => kc.split('+')); + const keysFlat = keys.reduce( + (res: Keys, item) => res.concat(...item), + [] + ); + + return [keys, keysFlat]; + } + + return [[], []]; + }, [keyCode]); + + useEffect(() => { + const target = options?.target || defaultDoc; + + if (keyCode !== null) { + const downHandler = (event: KeyboardEvent) => { + modifierPressed.current = + event.ctrlKey || event.metaKey || event.shiftKey; + const preventAction = + (!modifierPressed.current || + (modifierPressed.current && !options.actInsideInputWithModifier)) && + isInputDOMNode(event); + + if (preventAction) { + return false; + } + const keyOrCode = useKeyOrCode(event.code, keysToWatch); + pressedKeys.current.add(event[keyOrCode]); + + if (isMatchingKey(keyCodes, pressedKeys.current, false)) { + event.preventDefault(); + setKeyPressed(true); + } + }; + + const upHandler = (event: KeyboardEvent) => { + const preventAction = + (!modifierPressed.current || + (modifierPressed.current && !options.actInsideInputWithModifier)) && + isInputDOMNode(event); + + if (preventAction) { + return false; + } + const keyOrCode = useKeyOrCode(event.code, keysToWatch); + + if (isMatchingKey(keyCodes, pressedKeys.current, true)) { + setKeyPressed(false); + pressedKeys.current.clear(); + } else { + pressedKeys.current.delete(event[keyOrCode]); + } + + // fix for Mac: when cmd key is pressed, keyup is not triggered for any other key, see: https://stackoverflow.com/questions/27380018/when-cmd-key-is-kept-pressed-keyup-is-not-triggered-for-any-other-key + if (event.key === 'Meta') { + pressedKeys.current.clear(); + } + + modifierPressed.current = false; + }; + + const resetHandler = () => { + pressedKeys.current.clear(); + setKeyPressed(false); + }; + + target?.addEventListener( + 'keydown', + downHandler as EventListenerOrEventListenerObject + ); + target?.addEventListener( + 'keyup', + upHandler as EventListenerOrEventListenerObject + ); + window.addEventListener('blur', resetHandler); + window.addEventListener('contextmenu', resetHandler); + + return () => { + target?.removeEventListener( + 'keydown', + downHandler as EventListenerOrEventListenerObject + ); + target?.removeEventListener( + 'keyup', + upHandler as EventListenerOrEventListenerObject + ); + window.removeEventListener('blur', resetHandler); + window.removeEventListener('contextmenu', resetHandler); + }; + } + }, [keyCode, setKeyPressed, keyPressed]); + + console.log(keyPressed); + + return keyPressed; +} + +// utils +function isMatchingKey( + keyCodes: Array, + pressedKeys: PressedKeys, + isUp: boolean +): boolean { + return ( + keyCodes + // we only want to compare same sizes of keyCode definitions + // and pressed keys. When the user specified 'Meta' as a key somewhere + // this would also be truthy without this filter when user presses 'Meta' + 'r' + .filter((keys) => isUp || keys.length === pressedKeys.size) + // since we want to support multiple possibilities only one of the + // combinations need to be part of the pressed keys + .some((keys) => keys.every((k) => pressedKeys.has(k))) + ); +} + +function useKeyOrCode(eventCode: string, keysToWatch: KeyCode): KeyOrCode { + return keysToWatch.includes(eventCode) ? 'code' : 'key'; +} diff --git a/react/editableCells/src/index.scss b/react/editableCells/src/index.scss index f8f9347..b7ef722 100644 --- a/react/editableCells/src/index.scss +++ b/react/editableCells/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/editableCells/src/index.ts b/react/editableCells/src/index.ts index e5ac97c..3273c32 100644 --- a/react/editableCells/src/index.ts +++ b/react/editableCells/src/index.ts @@ -1,3 +1,3 @@ -import { EditableCells } from "./EditableCells"; +import { EditableCells } from './EditableCells'; -export { EditableCells }; \ No newline at end of file +export { EditableCells }; diff --git a/react/editableCells/src/main.tsx b/react/editableCells/src/main.tsx index eee999c..9f35f4d 100644 --- a/react/editableCells/src/main.tsx +++ b/react/editableCells/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { EditableCells } from './EditableCells' +import { EditableCells } from './EditableCells'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/editableCells/src/makeData.ts b/react/editableCells/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/editableCells/src/makeData.ts +++ b/react/editableCells/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/editableCells/vite.config.ts b/react/editableCells/vite.config.ts index 5a33944..627a319 100644 --- a/react/editableCells/vite.config.ts +++ b/react/editableCells/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/filterFlyout/README.md b/react/filterFlyout/README.md index 74872fd..f2dc982 100644 --- a/react/filterFlyout/README.md +++ b/react/filterFlyout/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/filterFlyout/eslint.config.ts b/react/filterFlyout/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/filterFlyout/eslint.config.ts +++ b/react/filterFlyout/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/filterFlyout/src/App.scss b/react/filterFlyout/src/App.scss index 1390a02..e651136 100644 --- a/react/filterFlyout/src/App.scss +++ b/react/filterFlyout/src/App.scss @@ -69,4 +69,4 @@ .flyout-dropdown-selected-item { text-transform: capitalize; -} \ No newline at end of file +} diff --git a/react/filterFlyout/src/ExampleLink.tsx b/react/filterFlyout/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/filterFlyout/src/ExampleLink.tsx +++ b/react/filterFlyout/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/filterFlyout/src/FilterFlyout.tsx b/react/filterFlyout/src/FilterFlyout.tsx index fbb0f4c..96b5ce0 100644 --- a/react/filterFlyout/src/FilterFlyout.tsx +++ b/react/filterFlyout/src/FilterFlyout.tsx @@ -1,5 +1,5 @@ -import React, { Dispatch, SetStateAction, useState, useRef } from 'react' -import cx from 'classnames' +import React, { Dispatch, SetStateAction, useState, useRef } from 'react'; +import cx from 'classnames'; import { DataTable, IconButton, @@ -11,7 +11,7 @@ import { ButtonSet, Button, Checkbox, - NumberInput + NumberInput, } from '@carbon/react'; import { Filter } from '@carbon/react/icons'; const { @@ -24,7 +24,7 @@ const { TableRow, TableToolbar, TableToolbarContent, - TableToolbarSearch + TableToolbarSearch, } = DataTable; import { @@ -38,54 +38,52 @@ import { Column, Header, getFacetedUniqueValues, - ColumnFilter -} from '@tanstack/react-table' -import { - rankItem, -} from '@tanstack/match-sorter-utils' + ColumnFilter, +} from '@tanstack/react-table'; +import { rankItem } from '@tanstack/match-sorter-utils'; import { makeData } from './makeData'; import { TagOverflow, pkg } from '@carbon/ibm-products'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; pkg.component.TagOverflow = true; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item - const itemRank = rankItem(row.getValue(columnId), value) + const itemRank = rankItem(row.getValue(columnId), value); // Store the itemRank info addMeta({ itemRank, - }) + }); // Return if the item should be filtered in/out - return itemRank.passed -} + return itemRank.passed; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), filterFn: 'arrIncludesSome', meta: { filterVariant: 'checkbox', @@ -104,17 +102,17 @@ const columns = [ header: 'Example', filterFn: 'weakEquals', meta: { - filterVariant: 'number' - } + filterVariant: 'number', + }, }), -] +]; export const FilterFlyout = () => { - const [data] = useState(makeData(7)) - const [globalFilter, setGlobalFilter] = React.useState('') + const [data] = useState(makeData(7)); + const [globalFilter, setGlobalFilter] = React.useState(''); const [popoverOpen, setPopoverOpen] = useState(false); - const [columnFilters, setColumnFilters] = useState([]) - const [localFilters, setLocalFilters] = useState([]) + const [columnFilters, setColumnFilters] = useState([]); + const [localFilters, setLocalFilters] = useState([]); const table = useReactTable({ data, @@ -132,7 +130,7 @@ export const FilterFlyout = () => { onGlobalFilterChange: setGlobalFilter, globalFilterFn: 'fuzzy', //apply fuzzy filter to the global filter (most common use case for fuzzy filter) getFacetedUniqueValues: getFacetedUniqueValues(), - }) + }); interface ExtendedColFilter extends ColumnFilter { label: string; @@ -144,13 +142,20 @@ export const FilterFlyout = () => { const tagFilters = columnFilters.map((c: ExtendedColFilter) => { const buildTag = (col, checkboxParentColumnData?: ColumnFilter) => { const tagData = {} as ExtendedColFilter; - tagData.label = typeof col === 'string' ? `${checkboxParentColumnData.id}: ${col}` : `${col.id}: ${col.value}`; + tagData.label = + typeof col === 'string' + ? `${checkboxParentColumnData.id}: ${col}` + : `${col.id}: ${col.value}`; tagData.onClose = () => { if (typeof col === 'string') { const groupValues = checkboxParentColumnData.value as string[]; - const newGroupValues = groupValues.filter(val => val !== col); - const foundLocalIndex = localFilters.findIndex(f => f.id === checkboxParentColumnData.id); - const foundColumnIndex = columnFilters.findIndex(f => f.id === checkboxParentColumnData.id); + const newGroupValues = groupValues.filter((val) => val !== col); + const foundLocalIndex = localFilters.findIndex( + (f) => f.id === checkboxParentColumnData.id + ); + const foundColumnIndex = columnFilters.findIndex( + (f) => f.id === checkboxParentColumnData.id + ); const tempLocal = [...localFilters]; const tempColumnFilters = [...columnFilters]; if (foundLocalIndex > -1) { @@ -158,7 +163,10 @@ export const FilterFlyout = () => { if (!newGroupValues.length) { setLocalFilters(tempLocal); } else { - setLocalFilters([...tempLocal, { id: checkboxParentColumnData.id, value: newGroupValues }]); + setLocalFilters([ + ...tempLocal, + { id: checkboxParentColumnData.id, value: newGroupValues }, + ]); } } if (foundColumnIndex > -1) { @@ -166,14 +174,22 @@ export const FilterFlyout = () => { if (!newGroupValues.length) { setColumnFilters(tempColumnFilters); } else { - setColumnFilters([...tempColumnFilters, { id: checkboxParentColumnData.id, value: newGroupValues }]); + setColumnFilters([ + ...tempColumnFilters, + { id: checkboxParentColumnData.id, value: newGroupValues }, + ]); } } return; } - const parentData = typeof col === 'string' ? checkboxParentColumnData : col; - const foundLocalIndex = localFilters.findIndex(f => f.id === parentData.id && f.value === parentData.value); - const foundColumnIndex = columnFilters.findIndex(f => f.id === parentData.id && f.value === parentData.value); + const parentData = + typeof col === 'string' ? checkboxParentColumnData : col; + const foundLocalIndex = localFilters.findIndex( + (f) => f.id === parentData.id && f.value === parentData.value + ); + const foundColumnIndex = columnFilters.findIndex( + (f) => f.id === parentData.id && f.value === parentData.value + ); const tempFilters = [...localFilters]; const tempColumnFilters = [...columnFilters]; if (foundColumnIndex > -1) { @@ -189,10 +205,10 @@ export const FilterFlyout = () => { }; tagData.filter = true; tagData.id = typeof col === 'string' ? col : col.id; - return tagData - } + return tagData; + }; if (Array.isArray(c.value)) { - return c.value.map(val => buildTag(val, c)); + return c.value.map((val) => buildTag(val, c)); } return buildTag(c); }); @@ -204,7 +220,7 @@ export const FilterFlyout = () => { const triggerButton = popoverRef?.current.querySelector('button'); triggerButton?.focus(); } - } + }; const containerRef = useRef(); const popoverRef = useRef(); @@ -214,19 +230,34 @@ export const FilterFlyout = () => { - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}> ) => setGlobalFilter(event.target.value)} + onChange={(event: React.ChangeEvent) => + setGlobalFilter(event.target.value) + } placeholder="Search all columns..." persistent /> @@ -235,50 +266,52 @@ export const FilterFlyout = () => { open={popoverOpen} isTabTip onRequestClose={() => { - setPopoverOpen(false) + setPopoverOpen(false); }} - align='bottom-end' + align="bottom-end" autoAlign - ref={popoverRef} - > + ref={popoverRef}> setPopoverOpen(prev => !prev)} + onClick={() => setPopoverOpen((prev) => !prev)} label="Filter" - kind='ghost' - > + kind="ghost"> -
-

Filter

+
+

Filter

{table.getHeaderGroups().map((headerGroup, index) => ( {headerGroup.headers.map((header, index) => { if (header.column.getCanFilter()) { - return
- {popoverOpen && ( - - )} -
+ return ( +
+ {popoverOpen && ( + + )} +
+ ); } })}
))}
- - @@ -298,56 +330,56 @@ export const FilterFlyout = () => { {buildTagFilters().length ? ( -
- + - +
- ): null} - + ) : null} +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -357,31 +389,37 @@ export const FilterFlyout = () => {
- ) -} + ); +}; -const FilterColumn = ( - { column, header, setLocalFilters, localFilters }: - { - column: Column, - header: Header, - setLocalFilters: Dispatch>, - localFilters: ColumnFiltersState, - }) => { - const { filterVariant } = column.columnDef.meta ?? {} +const FilterColumn = ({ + column, + header, + setLocalFilters, + localFilters, +}: { + column: Column; + header: Header; + setLocalFilters: Dispatch>; + localFilters: ColumnFiltersState; +}) => { + const { filterVariant } = column.columnDef.meta ?? {}; const sortedUniqueValues = React.useMemo( - () => Array.from(column.getFacetedUniqueValues().keys()) - .sort() - .slice(0, 5000), + () => + Array.from(column.getFacetedUniqueValues().keys()).sort().slice(0, 5000), [column] - ) + ); const getCheckboxState = (value) => { - const foundFilter = localFilters.find(c => c.id === column.id); - const isChecked = foundFilter ? (localFilters.find(c => c.id === column.id)?.value as string[]).includes(value) : false; + const foundFilter = localFilters.find((c) => c.id === column.id); + const isChecked = foundFilter + ? ( + localFilters.find((c) => c.id === column.id)?.value as string[] + ).includes(value) + : false; return !!isChecked; - } + }; return filterVariant === 'select' ? ( @@ -390,25 +428,33 @@ const FilterColumn = ( titleText={`Filter ${column.id}`} label="Choose a status" items={sortedUniqueValues} - initialSelectedItem={localFilters.find(c => c.id === column.id)?.value as string} - renderSelectedItem={() =>
{localFilters.find(c => c.id === column.id)?.value as string}
} - itemToElement={(item: { value: any }) =>
{item.value}
} + initialSelectedItem={ + localFilters.find((c) => c.id === column.id)?.value as string + } + renderSelectedItem={() => ( +
+ {localFilters.find((c) => c.id === column.id)?.value as string} +
+ )} + itemToElement={(item: { value: any }) => ( +
{item.value}
+ )} onChange={({ selectedItem }) => { - const temp = [...localFilters] - const foundIndex = temp.findIndex(c => c.id === column.id); + const temp = [...localFilters]; + const foundIndex = temp.findIndex((c) => c.id === column.id); if (foundIndex > -1) { temp.splice(foundIndex, 1); temp.push({ id: column.id, value: selectedItem }); setLocalFilters(temp); return; } - setLocalFilters([...temp, { id: column.id, value: selectedItem }]) + setLocalFilters([...temp, { id: column.id, value: selectedItem }]); }} />
) : filterVariant === 'checkbox' ? ( -

{column.id}

+

{column.id}

{sortedUniqueValues.map((value: string) => ( { const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; // This means there is only one checkbox selected if (checked) { if (foundFilterIndex > -1) { @@ -428,13 +476,15 @@ const FilterColumn = ( setLocalFilters(temp); return; } - setLocalFilters([...temp, { id: column.id, value: [ id ] }]); + setLocalFilters([...temp, { id: column.id, value: [id] }]); return; } if (!checked) { if (foundFilterIndex > -1) { const foundFilterValues = foundLocalFilter[0].value as []; - const newFoundFilterValues = foundFilterValues.filter(item => item !== id); + const newFoundFilterValues = foundFilterValues.filter( + (item) => item !== id + ); temp.splice(foundFilterIndex, 1); if (newFoundFilterValues && newFoundFilterValues.length) { temp.push({ id: column.id, value: newFoundFilterValues }); @@ -451,20 +501,22 @@ const FilterColumn = ( c.id === column.id)?.value as number} + value={localFilters.find((c) => c.id === column.id)?.value as number} hideSteppers label={column.id} onChange={(_, { value }) => { const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; if (foundFilterIndex > -1) { temp.splice(foundFilterIndex, 1); temp.push({ id: column.id, value }); - setLocalFilters(temp) + setLocalFilters(temp); return; } else { - setLocalFilters([...temp, { id: column.id, value }]) + setLocalFilters([...temp, { id: column.id, value }]); return; } }} @@ -473,24 +525,31 @@ const FilterColumn = ( ) : ( { + onChange={(event) => { // column.setFilterValue(event.target.value) // instant filter option const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; if (foundFilterIndex > -1) { temp.splice(foundFilterIndex, 1); temp.push({ id: column.id, value: event.target.value }); setLocalFilters(temp); return; } else { - setLocalFilters([...temp, { id: column.id, value: event.target.value }]); + setLocalFilters([ + ...temp, + { id: column.id, value: event.target.value }, + ]); return; } }} placeholder={`Filter ${column.id}`} type="text" - value={localFilters.find(c => c.id === column.id)?.value as string ?? ''} + value={ + (localFilters.find((c) => c.id === column.id)?.value as string) ?? '' + } labelText={flexRender( header.column.columnDef.header, header.getContext() @@ -498,5 +557,5 @@ const FilterColumn = ( id={column.id} /> - ) -} + ); +}; diff --git a/react/filterFlyout/src/customTypings.d.ts b/react/filterFlyout/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/filterFlyout/src/customTypings.d.ts +++ b/react/filterFlyout/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/filterFlyout/src/index.scss b/react/filterFlyout/src/index.scss index f8f9347..b7ef722 100644 --- a/react/filterFlyout/src/index.scss +++ b/react/filterFlyout/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/filterFlyout/src/index.ts b/react/filterFlyout/src/index.ts index d76b87b..6ea2cea 100644 --- a/react/filterFlyout/src/index.ts +++ b/react/filterFlyout/src/index.ts @@ -1,3 +1,3 @@ -import { FilterFlyout } from "./FilterFlyout"; +import { FilterFlyout } from './FilterFlyout'; -export { FilterFlyout }; \ No newline at end of file +export { FilterFlyout }; diff --git a/react/filterFlyout/src/main.tsx b/react/filterFlyout/src/main.tsx index 967b3d0..c2a8a76 100644 --- a/react/filterFlyout/src/main.tsx +++ b/react/filterFlyout/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { FilterFlyout } from './FilterFlyout' +import { FilterFlyout } from './FilterFlyout'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/filterFlyout/src/makeData.ts b/react/filterFlyout/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/filterFlyout/src/makeData.ts +++ b/react/filterFlyout/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/filterFlyout/vite.config.ts b/react/filterFlyout/vite.config.ts index 5a33944..627a319 100644 --- a/react/filterFlyout/vite.config.ts +++ b/react/filterFlyout/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/filterPanel/README.md b/react/filterPanel/README.md index 74872fd..f2dc982 100644 --- a/react/filterPanel/README.md +++ b/react/filterPanel/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/filterPanel/eslint.config.ts b/react/filterPanel/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/filterPanel/eslint.config.ts +++ b/react/filterPanel/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/filterPanel/src/App.scss b/react/filterPanel/src/App.scss index d821128..3525c53 100644 --- a/react/filterPanel/src/App.scss +++ b/react/filterPanel/src/App.scss @@ -106,7 +106,9 @@ position: relative; width: 100%; height: var(--table-height); - .filter--panel__close-wrapper.cds--popover-container:not(.cds--popover--auto-align) { + .filter--panel__close-wrapper.cds--popover-container:not( + .cds--popover--auto-align + ) { position: initial; } .filter--panel__close-wrapper.cds--popover-caret { @@ -141,4 +143,4 @@ background-color: $layer-02; border-bottom: 1px solid $border-subtle; z-index: 2; -} \ No newline at end of file +} diff --git a/react/filterPanel/src/ExampleLink.tsx b/react/filterPanel/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/filterPanel/src/ExampleLink.tsx +++ b/react/filterPanel/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/filterPanel/src/FilterPanel.tsx b/react/filterPanel/src/FilterPanel.tsx index 75b1dea..db57b60 100644 --- a/react/filterPanel/src/FilterPanel.tsx +++ b/react/filterPanel/src/FilterPanel.tsx @@ -1,6 +1,12 @@ -import React, { Dispatch, SetStateAction, useState, useRef, useEffect } from 'react' +import React, { + Dispatch, + SetStateAction, + useState, + useRef, + useEffect, +} from 'react'; import { animate } from 'motion'; -import cx from 'classnames' +import cx from 'classnames'; import { DataTable, IconButton, @@ -10,7 +16,7 @@ import { ButtonSet, Button, Checkbox, - NumberInput + NumberInput, } from '@carbon/react'; import { Close, Filter } from '@carbon/react/icons'; const { @@ -23,7 +29,7 @@ const { TableRow, TableToolbar, TableToolbarContent, - TableToolbarSearch + TableToolbarSearch, } = DataTable; import { @@ -37,54 +43,52 @@ import { Column, Header, getFacetedUniqueValues, - ColumnFilter -} from '@tanstack/react-table' -import { - rankItem, -} from '@tanstack/match-sorter-utils' + ColumnFilter, +} from '@tanstack/react-table'; +import { rankItem } from '@tanstack/match-sorter-utils'; import { makeData } from './makeData'; import { TagOverflow, pkg } from '@carbon/ibm-products'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; pkg.component.TagOverflow = true; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item - const itemRank = rankItem(row.getValue(columnId), value) + const itemRank = rankItem(row.getValue(columnId), value); // Store the itemRank info addMeta({ itemRank, - }) + }); // Return if the item should be filtered in/out - return itemRank.passed -} + return itemRank.passed; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), filterFn: 'arrIncludesSome', meta: { filterVariant: 'checkbox', @@ -103,17 +107,17 @@ const columns = [ header: 'Example', filterFn: 'weakEquals', meta: { - filterVariant: 'number' - } + filterVariant: 'number', + }, }), -] +]; export const FilterPanel = () => { - const [data] = useState(makeData(7)) - const [globalFilter, setGlobalFilter] = React.useState('') + const [data] = useState(makeData(7)); + const [globalFilter, setGlobalFilter] = React.useState(''); const [popoverOpen, setPopoverOpen] = useState(false); - const [columnFilters, setColumnFilters] = useState([]) - const [localFilters, setLocalFilters] = useState([]) + const [columnFilters, setColumnFilters] = useState([]); + const [localFilters, setLocalFilters] = useState([]); const table = useReactTable({ data, @@ -131,7 +135,7 @@ export const FilterPanel = () => { onGlobalFilterChange: setGlobalFilter, globalFilterFn: 'fuzzy', //apply fuzzy filter to the global filter (most common use case for fuzzy filter) getFacetedUniqueValues: getFacetedUniqueValues(), - }) + }); interface ExtendedColFilter extends ColumnFilter { label: string; @@ -143,13 +147,20 @@ export const FilterPanel = () => { const tagFilters = columnFilters.map((c: ExtendedColFilter) => { const buildTag = (col, checkboxParentColumnData?: ColumnFilter) => { const tagData = {} as ExtendedColFilter; - tagData.label = typeof col === 'string' ? `${checkboxParentColumnData.id}: ${col}` : `${col.id}: ${col.value}`; + tagData.label = + typeof col === 'string' + ? `${checkboxParentColumnData.id}: ${col}` + : `${col.id}: ${col.value}`; tagData.onClose = () => { if (typeof col === 'string') { const groupValues = checkboxParentColumnData.value as string[]; - const newGroupValues = groupValues.filter(val => val !== col); - const foundLocalIndex = localFilters.findIndex(f => f.id === checkboxParentColumnData.id); - const foundColumnIndex = columnFilters.findIndex(f => f.id === checkboxParentColumnData.id); + const newGroupValues = groupValues.filter((val) => val !== col); + const foundLocalIndex = localFilters.findIndex( + (f) => f.id === checkboxParentColumnData.id + ); + const foundColumnIndex = columnFilters.findIndex( + (f) => f.id === checkboxParentColumnData.id + ); const tempLocal = [...localFilters]; const tempColumnFilters = [...columnFilters]; if (foundLocalIndex > -1) { @@ -157,7 +168,10 @@ export const FilterPanel = () => { if (!newGroupValues.length) { setLocalFilters(tempLocal); } else { - setLocalFilters([...tempLocal, { id: checkboxParentColumnData.id, value: newGroupValues }]); + setLocalFilters([ + ...tempLocal, + { id: checkboxParentColumnData.id, value: newGroupValues }, + ]); } } if (foundColumnIndex > -1) { @@ -165,14 +179,22 @@ export const FilterPanel = () => { if (!newGroupValues.length) { setColumnFilters(tempColumnFilters); } else { - setColumnFilters([...tempColumnFilters, { id: checkboxParentColumnData.id, value: newGroupValues }]); + setColumnFilters([ + ...tempColumnFilters, + { id: checkboxParentColumnData.id, value: newGroupValues }, + ]); } } return; } - const parentData = typeof col === 'string' ? checkboxParentColumnData : col; - const foundLocalIndex = localFilters.findIndex(f => f.id === parentData.id && f.value === parentData.value); - const foundColumnIndex = columnFilters.findIndex(f => f.id === parentData.id && f.value === parentData.value); + const parentData = + typeof col === 'string' ? checkboxParentColumnData : col; + const foundLocalIndex = localFilters.findIndex( + (f) => f.id === parentData.id && f.value === parentData.value + ); + const foundColumnIndex = columnFilters.findIndex( + (f) => f.id === parentData.id && f.value === parentData.value + ); const tempFilters = [...localFilters]; const tempColumnFilters = [...columnFilters]; if (foundColumnIndex > -1) { @@ -188,10 +210,10 @@ export const FilterPanel = () => { }; tagData.filter = true; tagData.id = typeof col === 'string' ? col : col.id; - return tagData - } + return tagData; + }; if (Array.isArray(c.value)) { - return c.value.map(val => buildTag(val, c)); + return c.value.map((val) => buildTag(val, c)); } return buildTag(c); }); @@ -203,7 +225,7 @@ export const FilterPanel = () => { const triggerButton = popoverRef?.current.querySelector('button'); triggerButton?.focus(); } - } + }; const containerRef = useRef(); const popoverRef = useRef(); @@ -211,9 +233,14 @@ export const FilterPanel = () => { useEffect(() => { const tableContainer = containerRef?.current; if (tableContainer as HTMLDivElement) { - const tableContent = (tableContainer as HTMLDivElement)?.querySelector('.cds--data-table-content'); + const tableContent = (tableContainer as HTMLDivElement)?.querySelector( + '.cds--data-table-content' + ); if (tableContent) { - (tableContainer as HTMLDivElement)?.style.setProperty("--table-height", `${(tableContent as HTMLDivElement)?.clientHeight}px`); + (tableContainer as HTMLDivElement)?.style.setProperty( + '--table-height', + `${(tableContent as HTMLDivElement)?.clientHeight}px` + ); } } }, []); @@ -221,208 +248,222 @@ export const FilterPanel = () => { const animatePanel = () => { if (popoverOpen) { animate( - ".panel--container", + '.panel--container', { opacity: [1, 0], - transform: [`translateX(0px)`, `translateX(-320px)`] + transform: [`translateX(0px)`, `translateX(-320px)`], }, { - duration: .25, + duration: 0.25, } - ) + ); // .cds--data-table-content animate( - ".cds--data-table-content", + '.cds--data-table-content', { width: '100%', transform: 'translateX(0px)', }, { - duration: .25, + duration: 0.25, } - ) + ); } else { animate( - ".panel--container", + '.panel--container', { opacity: [0, 1], - transform: [`translateX(-320px)`, `translateX(0px)`] + transform: [`translateX(-320px)`, `translateX(0px)`], }, { - duration: .25, + duration: 0.25, } - ) + ); animate( - ".filter-panel-example .cds--data-table-content", + '.filter-panel-example .cds--data-table-content', { width: 'calc(100% - 336px)', transform: 'translateX(336px)', }, { - duration: .25, + duration: 0.25, } - ) + ); } - } + }; return (
- - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}> { - setPopoverOpen(prev => !prev) - animatePanel(); - }} - label="Filter" - kind='ghost' - className={cx({ - [`filter--panel__triggering-icon-open`]: popoverOpen - })} - > - - + onClick={() => { + setPopoverOpen((prev) => !prev); + animatePanel(); + }} + label="Filter" + kind="ghost" + className={cx({ + [`filter--panel__triggering-icon-open`]: popoverOpen, + })}> + + ) => setGlobalFilter(event.target.value)} + onChange={(event: React.ChangeEvent) => + setGlobalFilter(event.target.value) + } placeholder="Search all columns..." persistent /> {buildTagFilters().length ? ( -
- + - +
- ): null} -
- { - popoverOpen && ( - <> -
-
- { + ) : null} +
+ {popoverOpen && ( + <> +
+
+ { setPopoverOpen(false); animatePanel(); - }} - > - - -

Filter

+ }}> + + +

Filter

+
+
+
+ {table.getHeaderGroups().map((headerGroup, index) => ( + + {headerGroup.headers.map((header, index) => { + if (header.column.getCanFilter()) { + return ( +
+ {popoverOpen && ( + + )} +
+ ); + } + })} +
+ ))}
-
-
- {table.getHeaderGroups().map((headerGroup, index) => ( - - {headerGroup.headers.map((header, index) => { - if (header.column.getCanFilter()) { - return
- {popoverOpen && ( - - )} -
- } - })} -
- ))} -
-
- -
+ + - - -
- - ) - } + Clear + + + +
+ + )}
- +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -432,31 +473,37 @@ export const FilterPanel = () => {
- ) -} + ); +}; -const FilterColumn = ( - { column, header, setLocalFilters, localFilters }: - { - column: Column, - header: Header, - setLocalFilters: Dispatch>, - localFilters: ColumnFiltersState, - }) => { - const { filterVariant } = column.columnDef.meta ?? {} +const FilterColumn = ({ + column, + header, + setLocalFilters, + localFilters, +}: { + column: Column; + header: Header; + setLocalFilters: Dispatch>; + localFilters: ColumnFiltersState; +}) => { + const { filterVariant } = column.columnDef.meta ?? {}; const sortedUniqueValues = React.useMemo( - () => Array.from(column.getFacetedUniqueValues().keys()) - .sort() - .slice(0, 5000), + () => + Array.from(column.getFacetedUniqueValues().keys()).sort().slice(0, 5000), [column] - ) + ); const getCheckboxState = (value) => { - const foundFilter = localFilters.find(c => c.id === column.id); - const isChecked = foundFilter ? (localFilters.find(c => c.id === column.id)?.value as string[]).includes(value) : false; + const foundFilter = localFilters.find((c) => c.id === column.id); + const isChecked = foundFilter + ? ( + localFilters.find((c) => c.id === column.id)?.value as string[] + ).includes(value) + : false; return !!isChecked; - } + }; return filterVariant === 'select' ? ( @@ -465,25 +512,33 @@ const FilterColumn = ( titleText={`Filter ${column.id}`} label="Choose a status" items={sortedUniqueValues} - initialSelectedItem={localFilters.find(c => c.id === column.id)?.value as string} - renderSelectedItem={() =>
{localFilters.find(c => c.id === column.id)?.value as string}
} - itemToElement={(item: { value: any }) =>
{item.value}
} + initialSelectedItem={ + localFilters.find((c) => c.id === column.id)?.value as string + } + renderSelectedItem={() => ( +
+ {localFilters.find((c) => c.id === column.id)?.value as string} +
+ )} + itemToElement={(item: { value: any }) => ( +
{item.value}
+ )} onChange={({ selectedItem }) => { - const temp = [...localFilters] - const foundIndex = temp.findIndex(c => c.id === column.id); + const temp = [...localFilters]; + const foundIndex = temp.findIndex((c) => c.id === column.id); if (foundIndex > -1) { temp.splice(foundIndex, 1); temp.push({ id: column.id, value: selectedItem }); setLocalFilters(temp); return; } - setLocalFilters([...temp, { id: column.id, value: selectedItem }]) + setLocalFilters([...temp, { id: column.id, value: selectedItem }]); }} />
) : filterVariant === 'checkbox' ? ( -

{column.id}

+

{column.id}

{sortedUniqueValues.map((value: string) => ( { const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; // This means there is only one checkbox selected if (checked) { if (foundFilterIndex > -1) { @@ -503,13 +560,15 @@ const FilterColumn = ( setLocalFilters(temp); return; } - setLocalFilters([...temp, { id: column.id, value: [ id ] }]); + setLocalFilters([...temp, { id: column.id, value: [id] }]); return; } if (!checked) { if (foundFilterIndex > -1) { const foundFilterValues = foundLocalFilter[0].value as []; - const newFoundFilterValues = foundFilterValues.filter(item => item !== id); + const newFoundFilterValues = foundFilterValues.filter( + (item) => item !== id + ); temp.splice(foundFilterIndex, 1); if (newFoundFilterValues && newFoundFilterValues.length) { temp.push({ id: column.id, value: newFoundFilterValues }); @@ -526,20 +585,22 @@ const FilterColumn = ( c.id === column.id)?.value as number} + value={localFilters.find((c) => c.id === column.id)?.value as number} hideSteppers label={column.id} onChange={(_, { value }) => { const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; if (foundFilterIndex > -1) { temp.splice(foundFilterIndex, 1); temp.push({ id: column.id, value }); - setLocalFilters(temp) + setLocalFilters(temp); return; } else { - setLocalFilters([...temp, { id: column.id, value }]) + setLocalFilters([...temp, { id: column.id, value }]); return; } }} @@ -548,24 +609,31 @@ const FilterColumn = ( ) : ( { + onChange={(event) => { // column.setFilterValue(event.target.value) // instant filter option const temp = [...localFilters]; - const foundLocalFilter = temp.filter(f => f.id === column.id); - const foundFilterIndex = foundLocalFilter.length ? temp.findIndex(f => f.id === foundLocalFilter[0].id) : -1; + const foundLocalFilter = temp.filter((f) => f.id === column.id); + const foundFilterIndex = foundLocalFilter.length + ? temp.findIndex((f) => f.id === foundLocalFilter[0].id) + : -1; if (foundFilterIndex > -1) { temp.splice(foundFilterIndex, 1); temp.push({ id: column.id, value: event.target.value }); setLocalFilters(temp); return; } else { - setLocalFilters([...temp, { id: column.id, value: event.target.value }]); + setLocalFilters([ + ...temp, + { id: column.id, value: event.target.value }, + ]); return; } }} placeholder={`Filter ${column.id}`} type="text" - value={localFilters.find(c => c.id === column.id)?.value as string ?? ''} + value={ + (localFilters.find((c) => c.id === column.id)?.value as string) ?? '' + } labelText={flexRender( header.column.columnDef.header, header.getContext() @@ -573,5 +641,5 @@ const FilterColumn = ( id={column.id} /> - ) -} + ); +}; diff --git a/react/filterPanel/src/customTypings.d.ts b/react/filterPanel/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/filterPanel/src/customTypings.d.ts +++ b/react/filterPanel/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/filterPanel/src/index.scss b/react/filterPanel/src/index.scss index f8f9347..b7ef722 100644 --- a/react/filterPanel/src/index.scss +++ b/react/filterPanel/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/filterPanel/src/index.ts b/react/filterPanel/src/index.ts index a8b3ac6..ea24f08 100644 --- a/react/filterPanel/src/index.ts +++ b/react/filterPanel/src/index.ts @@ -1,3 +1,3 @@ -import { FilterPanel } from "./FilterPanel"; +import { FilterPanel } from './FilterPanel'; -export { FilterPanel }; \ No newline at end of file +export { FilterPanel }; diff --git a/react/filterPanel/src/main.tsx b/react/filterPanel/src/main.tsx index aab738f..69a7741 100644 --- a/react/filterPanel/src/main.tsx +++ b/react/filterPanel/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { FilterPanel } from './FilterPanel' +import { FilterPanel } from './FilterPanel'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/filterPanel/src/makeData.ts b/react/filterPanel/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/filterPanel/src/makeData.ts +++ b/react/filterPanel/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/filterPanel/vite.config.ts b/react/filterPanel/vite.config.ts index 5a33944..627a319 100644 --- a/react/filterPanel/vite.config.ts +++ b/react/filterPanel/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/gallery/README.md b/react/gallery/README.md index 74872fd..f2dc982 100644 --- a/react/gallery/README.md +++ b/react/gallery/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/gallery/eslint.config.ts b/react/gallery/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/gallery/eslint.config.ts +++ b/react/gallery/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/gallery/src/App.scss b/react/gallery/src/App.scss index b2a63e0..372a67f 100644 --- a/react/gallery/src/App.scss +++ b/react/gallery/src/App.scss @@ -57,9 +57,6 @@ padding-bottom: 2rem; } - - - // Virtual .virtual-skeleton-row { background-color: $layer; @@ -79,7 +76,6 @@ cursor: pointer; } - // Sorting .descending-sorting-icon { transform: rotate(180deg); @@ -103,7 +99,6 @@ padding-bottom: 2rem; } - // Nested row .row-expander { width: 32px; @@ -119,7 +114,6 @@ transition: transform 200ms; } - // Customize cols $drag-class: c4p__draggable-item; $horizontal-drag-height: 6rem; @@ -212,9 +206,10 @@ $horizontal-drag-height: 6rem; } // Editable cells -[role="gridcell"]:focus, [role="gridcell"] *:focus, -[role="grid"] [tabindex="0"]:focus { - @include utilities.focus-outline('outline'); +[role='gridcell']:focus, +[role='gridcell'] *:focus, +[role='grid'] [tabindex='0']:focus { + @include utilities.focus-outline('outline'); } .editable-cell-input .cds--text-input { @@ -358,7 +353,9 @@ $horizontal-drag-height: 6rem; position: relative; width: 100%; height: var(--table-height); - .filter--panel__close-wrapper.cds--popover-container:not(.cds--popover--auto-align) { + .filter--panel__close-wrapper.cds--popover-container:not( + .cds--popover--auto-align + ) { position: initial; } .filter--panel__close-wrapper.cds--popover-caret { @@ -393,4 +390,4 @@ $horizontal-drag-height: 6rem; background-color: $layer-02; border-bottom: 1px solid $border-subtle; z-index: 2; -} \ No newline at end of file +} diff --git a/react/gallery/src/index.scss b/react/gallery/src/index.scss index f8f9347..b7ef722 100644 --- a/react/gallery/src/index.scss +++ b/react/gallery/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/gallery/src/main.tsx b/react/gallery/src/main.tsx index 33f5213..012fae9 100644 --- a/react/gallery/src/main.tsx +++ b/react/gallery/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,34 +7,38 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' -import { QueryClient, QueryClientProvider, } from '@tanstack/react-query' +} from '@carbon/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // Imported from local monorepo packages directory -import { ResizableCols } from 'resizing' -import { PaginationExample } from 'pagination' -import { GlobalFilter } from 'global-filter' -import { SortableColumns } from 'sortable' -import { CustomizeColumns } from 'customize-columns' -import { EditableCells } from 'editable-cells' -import { RowExpansion } from 'row-expansion' -import { FilterFlyout } from 'filter-flyout' -import { InfiniteScroll } from 'infinite-scroll' -import { NestedRows } from 'nested-rows' -import { RowClick } from 'row-click' -import { BatchActions } from 'batch-actions' -import { StickyColumns } from 'sticky-columns' -import { FilterPanel } from 'filter-panel' +import { ResizableCols } from 'resizing'; +import { PaginationExample } from 'pagination'; +import { GlobalFilter } from 'global-filter'; +import { SortableColumns } from 'sortable'; +import { CustomizeColumns } from 'customize-columns'; +import { EditableCells } from 'editable-cells'; +import { RowExpansion } from 'row-expansion'; +import { FilterFlyout } from 'filter-flyout'; +import { InfiniteScroll } from 'infinite-scroll'; +import { NestedRows } from 'nested-rows'; +import { RowClick } from 'row-click'; +import { BatchActions } from 'batch-actions'; +import { StickyColumns } from 'sticky-columns'; +import { FilterPanel } from 'filter-panel'; -import './index.scss' -const queryClient = new QueryClient() +import './index.scss'; +const queryClient = new QueryClient(); const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -43,10 +47,9 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( - {renderUIShellHeader()} - + @@ -91,5 +94,5 @@ createRoot(document.getElementById('root')!).render( - , -) + +); diff --git a/react/gallery/vite.config.ts b/react/gallery/vite.config.ts index 5a33944..627a319 100644 --- a/react/gallery/vite.config.ts +++ b/react/gallery/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/globalFilter/README.md b/react/globalFilter/README.md index 74872fd..f2dc982 100644 --- a/react/globalFilter/README.md +++ b/react/globalFilter/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/globalFilter/eslint.config.ts b/react/globalFilter/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/globalFilter/eslint.config.ts +++ b/react/globalFilter/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/globalFilter/src/ExampleLink.tsx b/react/globalFilter/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/globalFilter/src/ExampleLink.tsx +++ b/react/globalFilter/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/globalFilter/src/GlobalFilter.tsx b/react/globalFilter/src/GlobalFilter.tsx index 6223f61..bfa060c 100644 --- a/react/globalFilter/src/GlobalFilter.tsx +++ b/react/globalFilter/src/GlobalFilter.tsx @@ -1,4 +1,4 @@ -import React, { useLayoutEffect, useRef } from 'react' +import React, { useLayoutEffect, useRef } from 'react'; import { DataTable } from '@carbon/react'; const { Table, @@ -8,7 +8,7 @@ const { TableHead, TableHeader, TableRow, - TableToolbarSearch + TableToolbarSearch, } = DataTable; import { @@ -20,45 +20,43 @@ import { getSortedRowModel, useReactTable, createColumnHelper, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; // A TanStack fork of Kent C. Dodds' match-sorter library that provides ranking information -import { - rankItem, -} from '@tanstack/match-sorter-utils' +import { rankItem } from '@tanstack/match-sorter-utils'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; -import { makeData, Resource } from './makeData' +import { makeData, Resource } from './makeData'; // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item - const itemRank = rankItem(row.getValue(columnId), value) + const itemRank = rankItem(row.getValue(columnId), value); // Store the itemRank info addMeta({ itemRank, - }) + }); // Return if the item should be filtered in/out - return itemRank.passed -} + return itemRank.passed; +}; export const GlobalFilter = () => { - const [globalFilter, setGlobalFilter] = React.useState('') + const [globalFilter, setGlobalFilter] = React.useState(''); - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -69,9 +67,9 @@ export const GlobalFilter = () => { columnHelper.accessor('example', { header: 'Example', }), - ] + ]; - const [data] = React.useState(() => makeData(5)) + const [data] = React.useState(() => makeData(5)); const table = useReactTable({ data, @@ -104,60 +102,69 @@ export const GlobalFilter = () => { }, [table]); return ( -
+
- - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > + }}> ) => setGlobalFilter(event.target.value)} + onChange={(event: React.ChangeEvent) => + setGlobalFilter(event.target.value) + } placeholder="Search all columns..." persistent /> - +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -167,5 +174,5 @@ export const GlobalFilter = () => {
- ) -} + ); +}; diff --git a/react/globalFilter/src/customTypings.d.ts b/react/globalFilter/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/globalFilter/src/customTypings.d.ts +++ b/react/globalFilter/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/globalFilter/src/index.scss b/react/globalFilter/src/index.scss index f8f9347..b7ef722 100644 --- a/react/globalFilter/src/index.scss +++ b/react/globalFilter/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/globalFilter/src/index.ts b/react/globalFilter/src/index.ts index a056a43..49f4700 100644 --- a/react/globalFilter/src/index.ts +++ b/react/globalFilter/src/index.ts @@ -1,3 +1,3 @@ -import { GlobalFilter } from "./GlobalFilter"; +import { GlobalFilter } from './GlobalFilter'; -export { GlobalFilter }; \ No newline at end of file +export { GlobalFilter }; diff --git a/react/globalFilter/src/main.tsx b/react/globalFilter/src/main.tsx index 8547f1e..be19653 100644 --- a/react/globalFilter/src/main.tsx +++ b/react/globalFilter/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { GlobalFilter } from './GlobalFilter' +import { GlobalFilter } from './GlobalFilter'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/globalFilter/src/makeData.ts b/react/globalFilter/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/globalFilter/src/makeData.ts +++ b/react/globalFilter/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/globalFilter/vite.config.ts b/react/globalFilter/vite.config.ts index 5a33944..627a319 100644 --- a/react/globalFilter/vite.config.ts +++ b/react/globalFilter/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/infiniteScroll/README.md b/react/infiniteScroll/README.md index 74872fd..f2dc982 100644 --- a/react/infiniteScroll/README.md +++ b/react/infiniteScroll/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/infiniteScroll/eslint.config.ts b/react/infiniteScroll/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/infiniteScroll/eslint.config.ts +++ b/react/infiniteScroll/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/infiniteScroll/src/App.scss b/react/infiniteScroll/src/App.scss index e892279..9fa50d1 100644 --- a/react/infiniteScroll/src/App.scss +++ b/react/infiniteScroll/src/App.scss @@ -40,4 +40,4 @@ // infinite scroll .infinite-scroll-container { margin-top: 1rem; -} \ No newline at end of file +} diff --git a/react/infiniteScroll/src/ExampleLink.tsx b/react/infiniteScroll/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/infiniteScroll/src/ExampleLink.tsx +++ b/react/infiniteScroll/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/infiniteScroll/src/InfiniteScroll.tsx b/react/infiniteScroll/src/InfiniteScroll.tsx index a174778..7b0d183 100644 --- a/react/infiniteScroll/src/InfiniteScroll.tsx +++ b/react/infiniteScroll/src/InfiniteScroll.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; import { flexRender, @@ -8,46 +8,37 @@ import { Row, SortingState, useReactTable, - createColumnHelper -} from '@tanstack/react-table' -import { - keepPreviousData, - useInfiniteQuery, -} from '@tanstack/react-query' -import { useVirtualizer } from '@tanstack/react-virtual' + createColumnHelper, +} from '@tanstack/react-table'; +import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query'; +import { useVirtualizer } from '@tanstack/react-virtual'; import { DataTable, SkeletonText } from '@carbon/react'; -const { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} = DataTable; +const { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } = + DataTable; -import { fetchData, Resource, ResourceApiResponse } from './makeData' +import { fetchData, Resource, ResourceApiResponse } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; -const fetchSize = 50 +const fetchSize = 50; export const InfiniteScroll = () => { - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper(); //we need a reference to the scrolling element for logic down below - const tableContainerRef = React.useRef(null) + const tableContainerRef = React.useRef(null); - const [sorting, setSorting] = React.useState([]) + const [sorting, setSorting] = React.useState([]); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -58,7 +49,7 @@ export const InfiniteScroll = () => { columnHelper.accessor('example', { header: 'Example', }), - ] + ]; //react-query has a useInfiniteQuery hook that is perfect for this use case const { data, fetchNextPage, isFetching, isLoading } = @@ -68,46 +59,46 @@ export const InfiniteScroll = () => { sorting, // refetch when sorting changes ], queryFn: async ({ pageParam = 0 }) => { - const start = (pageParam as number) * fetchSize - const fetchedData = await fetchData(start, fetchSize) // pretend api call - return fetchedData + const start = (pageParam as number) * fetchSize; + const fetchedData = await fetchData(start, fetchSize); // pretend api call + return fetchedData; }, initialPageParam: 0, getNextPageParam: (_lastGroup, groups) => groups.length, refetchOnWindowFocus: false, placeholderData: keepPreviousData, - }) + }); //flatten the array of arrays from the useInfiniteQuery hook const flatData = React.useMemo( - () => data?.pages?.flatMap(page => page.data) ?? [], + () => data?.pages?.flatMap((page) => page.data) ?? [], [data] - ) - const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0 - const totalFetched = flatData.length + ); + const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0; + const totalFetched = flatData.length; //called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table const fetchMoreOnBottomReached = React.useCallback( (containerRefElement?: HTMLDivElement | null) => { if (containerRefElement) { - const { scrollHeight, scrollTop, clientHeight } = containerRefElement + const { scrollHeight, scrollTop, clientHeight } = containerRefElement; //once the user has scrolled within 500px of the bottom of the table, fetch more data if we can if ( scrollHeight - scrollTop - clientHeight < 500 && !isFetching && totalFetched < totalDBRowCount ) { - fetchNextPage() + fetchNextPage(); } } }, [fetchNextPage, isFetching, totalFetched, totalDBRowCount] - ) + ); //a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data React.useEffect(() => { - fetchMoreOnBottomReached(tableContainerRef.current) - }, [fetchMoreOnBottomReached]) + fetchMoreOnBottomReached(tableContainerRef.current); + }, [fetchMoreOnBottomReached]); const table = useReactTable({ data: flatData, @@ -119,23 +110,23 @@ export const InfiniteScroll = () => { getSortedRowModel: getSortedRowModel(), manualSorting: true, // debugTable: true, - }) + }); //scroll to top of table when sorting changes - const handleSortingChange: OnChangeFn = updater => { - setSorting(updater) + const handleSortingChange: OnChangeFn = (updater) => { + setSorting(updater); if (table.getRowModel().rows.length) { - rowVirtualizer.scrollToIndex?.(0) + rowVirtualizer.scrollToIndex?.(0); } - } + }; //since this table option is derived from table row model state, we're using the table.setOptions utility - table.setOptions(prev => ({ + table.setOptions((prev) => ({ ...prev, onSortingChange: handleSortingChange, - })) + })); - const { rows } = table.getRowModel() + const { rows } = table.getRowModel(); const rowVirtualizer = useVirtualizer({ count: rows.length, @@ -145,133 +136,142 @@ export const InfiniteScroll = () => { measureElement: typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') === -1 - ? element => element?.getBoundingClientRect().height + ? (element) => element?.getBoundingClientRect().height : undefined, overscan: 5, - }) + }); if (isLoading) { - return <>Loading... + return <>Loading...; } return ( -
-

Virtualized infinite scroll

- - - - - {/* {process.env.NODE_ENV === 'development' ? ( +
+

Virtualized infinite scroll

+ + + + + {/* {process.env.NODE_ENV === 'development' ? (

Notice: You are currently running React in development mode. Virtualized rendering performance will be slightly degraded until this application is built for production.

) : null} */} - ({flatData.length} of {totalDBRowCount} rows fetched) -
fetchMoreOnBottomReached(e.target as HTMLDivElement)} - ref={tableContainerRef} - style={{ - overflow: 'auto', //our scrollable table container - position: 'relative', //needed for sticky header - height: '600px', //should be a fixed height - width: table.getCenterTotalSize() - }} - > - {/* Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights */} - {/* @ts-expect-error adding `style` purposefully for example */} - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => { - return ( - -
- {flexRender( - header.column.columnDef.header, - header.getContext() - )} - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
-
- ) - })} -
- ))} -
- - {rowVirtualizer.getVirtualItems().map((virtualRow, index) => { - const row = rows[virtualRow.index] as Row - return ( - - rowVirtualizer.measureElement(node)} //measure dynamic row height - key={row.id} + ({flatData.length} of {totalDBRowCount} rows fetched) +
fetchMoreOnBottomReached(e.target as HTMLDivElement)} + ref={tableContainerRef} + style={{ + overflow: 'auto', //our scrollable table container + position: 'relative', //needed for sticky header + height: '600px', //should be a fixed height + width: table.getCenterTotalSize(), + }}> + {/* Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights */} + {/* @ts-expect-error adding `style` purposefully for example */} +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + - {row.getVisibleCells().map(cell => { - return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ) - })} - - {/* {index === 0 && ( */} - {isFetching && rowVirtualizer.getVirtualItems().length - 1 === index && ( + width: header.getSize(), + }}> +
+ {flexRender( + header.column.columnDef.header, + header.getContext() + )} + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ + ); + })} + + ))} +
+ + {rowVirtualizer.getVirtualItems().map((virtualRow, index) => { + const row = rows[virtualRow.index] as Row; + return ( + + rowVirtualizer.measureElement(node)} //measure dynamic row height + key={row.id} + style={{ + display: 'flex', + position: 'absolute', + transform: `translateY(${virtualRow.start}px)`, //this should always be a `style` as it changes on scroll + width: '100%', + }}> + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ); + })} + + {/* {index === 0 && ( */} + {isFetching && + rowVirtualizer.getVirtualItems().length - 1 === index && ( { width: '100%', alignItems: 'center', height: 48, - }} - > + }}> {columns.map((_, index) => ( { width: 148, alignItems: 'center', height: 48, - }} - > + }}> ))} )} - - ) - })} - -
-
+ + ); + })} + +
- ) -} +
+ ); +}; diff --git a/react/infiniteScroll/src/customTypings.d.ts b/react/infiniteScroll/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/infiniteScroll/src/customTypings.d.ts +++ b/react/infiniteScroll/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/infiniteScroll/src/index.scss b/react/infiniteScroll/src/index.scss index f8f9347..b7ef722 100644 --- a/react/infiniteScroll/src/index.scss +++ b/react/infiniteScroll/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/infiniteScroll/src/index.ts b/react/infiniteScroll/src/index.ts index a3cf358..1dc3b66 100644 --- a/react/infiniteScroll/src/index.ts +++ b/react/infiniteScroll/src/index.ts @@ -1,3 +1,3 @@ -import { InfiniteScroll } from "./InfiniteScroll"; +import { InfiniteScroll } from './InfiniteScroll'; -export { InfiniteScroll }; \ No newline at end of file +export { InfiniteScroll }; diff --git a/react/infiniteScroll/src/main.tsx b/react/infiniteScroll/src/main.tsx index 81dea83..319d685 100644 --- a/react/infiniteScroll/src/main.tsx +++ b/react/infiniteScroll/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,20 +7,24 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' -import { QueryClient, QueryClientProvider, } from '@tanstack/react-query' +} from '@carbon/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { InfiniteScroll } from './InfiniteScroll' +import { InfiniteScroll } from './InfiniteScroll'; -import './index.scss' -const queryClient = new QueryClient() +import './index.scss'; +const queryClient = new QueryClient(); const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -31,11 +35,11 @@ createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/infiniteScroll/src/makeData.ts b/react/infiniteScroll/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/infiniteScroll/src/makeData.ts +++ b/react/infiniteScroll/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/infiniteScroll/vite.config.ts b/react/infiniteScroll/vite.config.ts index 5a33944..627a319 100644 --- a/react/infiniteScroll/vite.config.ts +++ b/react/infiniteScroll/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/nestedRows/README.md b/react/nestedRows/README.md index 74872fd..f2dc982 100644 --- a/react/nestedRows/README.md +++ b/react/nestedRows/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/nestedRows/eslint.config.ts b/react/nestedRows/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/nestedRows/eslint.config.ts +++ b/react/nestedRows/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/nestedRows/src/App.scss b/react/nestedRows/src/App.scss index 47d20c7..3268c3b 100644 --- a/react/nestedRows/src/App.scss +++ b/react/nestedRows/src/App.scss @@ -36,4 +36,4 @@ .row-expander .row-expanded-icon { transform: rotate(90deg); transition: transform 200ms; -} \ No newline at end of file +} diff --git a/react/nestedRows/src/ExampleLink.tsx b/react/nestedRows/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/nestedRows/src/ExampleLink.tsx +++ b/react/nestedRows/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/nestedRows/src/NestedRows.tsx b/react/nestedRows/src/NestedRows.tsx index 9db8be2..082c777 100644 --- a/react/nestedRows/src/NestedRows.tsx +++ b/react/nestedRows/src/NestedRows.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; import { ExpandedState, @@ -7,40 +7,36 @@ import { getExpandedRowModel, ColumnDef, flexRender, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; import { DataTable, TableContainer, Button } from '@carbon/react'; import { ChevronRight } from '@carbon/react/icons'; -const { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} = DataTable; +const { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } = + DataTable; -import { makeData, Resource } from './makeData' +import { makeData, Resource } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; export const NestedRows = () => { - const columns = React.useMemo[]>( () => [ { accessorKey: 'name', header: ({ table }) => ( -
+
{' '} Name
@@ -52,34 +48,36 @@ export const NestedRows = () => { // we can use the row.depth property // and paddingLeft to visually indicate the depth // of the row - paddingLeft: `${(row.depth * 2) + (!row.getCanExpand() ? 2 : 0)}rem`, - }} - > -
+ paddingLeft: `${ + row.depth * 2 + (!row.getCanExpand() ? 2 : 0) + }rem`, + }}> +
{row.getCanExpand() ? ( - ) : ( - null - )}{' '} + ) : null}{' '} {getValue()}
), }, { - accessorFn: row => row.rule, + accessorFn: (row) => row.rule, id: 'rule', - cell: info => info.getValue(), + cell: (info) => info.getValue(), header: () => Rule, }, { @@ -92,11 +90,11 @@ export const NestedRows = () => { }, ], [] - ) + ); - const [data] = React.useState(() => makeData(10, 5, 3)) + const [data] = React.useState(() => makeData(10, 5, 3)); - const [expanded, setExpanded] = React.useState({}) + const [expanded, setExpanded] = React.useState({}); const table = useReactTable({ data, @@ -105,47 +103,60 @@ export const NestedRows = () => { expanded, }, onExpandedChange: setExpanded, - getSubRows: row => row.subRows, + getSubRows: (row) => row.subRows, getCoreRowModel: getCoreRowModel(), getExpandedRowModel: getExpandedRowModel(), // filterFromLeafRows: true, // maxLeafRowFilterDepth: 0, // debugTable: true, - }) + }); return ( - - - } - > + className="tanstack-example" + description={ + + + + + }> - {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => { + {headerGroup.headers.map((header) => { return ( - {header.isPlaceholder ? null : ( - flexRender( - header.column.columnDef.header, - header.getContext() - ) - )} + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} - ) + ); })} ))} - {table.getRowModel().rows.map(row => { + {table.getRowModel().rows.map((row) => { return ( - {row.getVisibleCells().map(cell => { + {row.getVisibleCells().map((cell) => { return ( {flexRender( @@ -153,13 +164,13 @@ export const NestedRows = () => { cell.getContext() )} - ) + ); })} - ) + ); })}
- ) -} + ); +}; diff --git a/react/nestedRows/src/customTypings.d.ts b/react/nestedRows/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/nestedRows/src/customTypings.d.ts +++ b/react/nestedRows/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/nestedRows/src/index.scss b/react/nestedRows/src/index.scss index f8f9347..b7ef722 100644 --- a/react/nestedRows/src/index.scss +++ b/react/nestedRows/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/nestedRows/src/index.ts b/react/nestedRows/src/index.ts index d3c5ff6..f443303 100644 --- a/react/nestedRows/src/index.ts +++ b/react/nestedRows/src/index.ts @@ -1,3 +1,3 @@ -import { NestedRows } from "./NestedRows"; +import { NestedRows } from './NestedRows'; -export { NestedRows }; \ No newline at end of file +export { NestedRows }; diff --git a/react/nestedRows/src/main.tsx b/react/nestedRows/src/main.tsx index 5c941e9..ca99489 100644 --- a/react/nestedRows/src/main.tsx +++ b/react/nestedRows/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { NestedRows } from './NestedRows' +import { NestedRows } from './NestedRows'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/nestedRows/src/makeData.ts b/react/nestedRows/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/nestedRows/src/makeData.ts +++ b/react/nestedRows/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/nestedRows/vite.config.ts b/react/nestedRows/vite.config.ts index 5a33944..627a319 100644 --- a/react/nestedRows/vite.config.ts +++ b/react/nestedRows/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/pagination/README.md b/react/pagination/README.md index 74872fd..f2dc982 100644 --- a/react/pagination/README.md +++ b/react/pagination/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/pagination/eslint.config.ts b/react/pagination/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/pagination/eslint.config.ts +++ b/react/pagination/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/pagination/src/ExampleLink.tsx b/react/pagination/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/pagination/src/ExampleLink.tsx +++ b/react/pagination/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/pagination/src/Pagination.tsx b/react/pagination/src/Pagination.tsx index a224ac0..7bc765d 100644 --- a/react/pagination/src/Pagination.tsx +++ b/react/pagination/src/Pagination.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useLayoutEffect } from 'react' +import React, { useRef, useState, useLayoutEffect } from 'react'; import { DataTable, Pagination } from '@carbon/react'; const { Table, @@ -7,7 +7,7 @@ const { TableContainer, TableHead, TableHeader, - TableRow + TableRow, } = DataTable; import { @@ -16,33 +16,33 @@ import { getCoreRowModel, useReactTable, PaginationState, - getPaginationRowModel -} from '@tanstack/react-table' + getPaginationRowModel, +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -53,14 +53,14 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; export const PaginationExample = () => { - const [data] = useState(makeData(200)) + const [data] = useState(makeData(200)); const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: 10, - }) + }); const table = useReactTable({ data, @@ -72,7 +72,7 @@ export const PaginationExample = () => { state: { pagination, }, - }) + }); const tableWrap = useRef(); @@ -88,50 +88,57 @@ export const PaginationExample = () => { - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > - + }}> +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -151,10 +158,10 @@ export const PaginationExample = () => { pageSizes={[10, 20, 30, 40, 50]} itemsPerPageText={'Items per page:'} onChange={({ pageSize, page }) => { - table.setPageSize(Number(pageSize)) - table.setPageIndex(page - 1) + table.setPageSize(Number(pageSize)); + table.setPageIndex(page - 1); }} /> - ) -} + ); +}; diff --git a/react/pagination/src/customTypings.d.ts b/react/pagination/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/pagination/src/customTypings.d.ts +++ b/react/pagination/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/pagination/src/index.scss b/react/pagination/src/index.scss index f8f9347..b7ef722 100644 --- a/react/pagination/src/index.scss +++ b/react/pagination/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/pagination/src/index.ts b/react/pagination/src/index.ts index 1fc83d3..4cf2089 100644 --- a/react/pagination/src/index.ts +++ b/react/pagination/src/index.ts @@ -1,3 +1,3 @@ -import { PaginationExample } from "./Pagination"; +import { PaginationExample } from './Pagination'; -export { PaginationExample }; \ No newline at end of file +export { PaginationExample }; diff --git a/react/pagination/src/main.tsx b/react/pagination/src/main.tsx index 52a09ea..c2d87db 100644 --- a/react/pagination/src/main.tsx +++ b/react/pagination/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { PaginationExample } from './Pagination' +import { PaginationExample } from './Pagination'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/pagination/src/makeData.ts b/react/pagination/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/pagination/src/makeData.ts +++ b/react/pagination/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/pagination/vite.config.ts b/react/pagination/vite.config.ts index 5a33944..627a319 100644 --- a/react/pagination/vite.config.ts +++ b/react/pagination/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/resizing/README.md b/react/resizing/README.md index 74872fd..f2dc982 100644 --- a/react/resizing/README.md +++ b/react/resizing/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/resizing/eslint.config.ts b/react/resizing/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/resizing/eslint.config.ts +++ b/react/resizing/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/resizing/src/ExampleLink.tsx b/react/resizing/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/resizing/src/ExampleLink.tsx +++ b/react/resizing/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/resizing/src/ResizableCols.tsx b/react/resizing/src/ResizableCols.tsx index 6682f8f..2152c61 100644 --- a/react/resizing/src/ResizableCols.tsx +++ b/react/resizing/src/ResizableCols.tsx @@ -1,4 +1,4 @@ -import { useRef, useState, useLayoutEffect } from 'react' +import { useRef, useState, useLayoutEffect } from 'react'; import { DataTable } from '@carbon/react'; const { Table, @@ -7,7 +7,7 @@ const { TableContainer, TableHead, TableHeader, - TableRow + TableRow, } = DataTable; import { @@ -15,33 +15,33 @@ import { flexRender, getCoreRowModel, useReactTable, - ColumnResizeMode -} from '@tanstack/react-table' + ColumnResizeMode, +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -52,19 +52,19 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; export const ResizableCols = () => { - const [data] = useState(makeData(7)) + const [data] = useState(makeData(7)); - const [columnResizeMode] = useState('onChange') + const [columnResizeMode] = useState('onChange'); const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), columnResizeMode, - }) + }); const tableWrap = useRef(); @@ -80,76 +80,80 @@ export const ResizableCols = () => { - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > -
+ }}> +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} -
header.column.resetSize(), - onMouseDown: header.getResizeHandler(), - onTouchStart: header.getResizeHandler(), - className: `resizer ${ - table.options.columnResizeDirection - } ${ - header.column.getIsResizing() ? 'isResizing' : '' - }`, - style: { - transform: - columnResizeMode === 'onEnd' && - header.column.getIsResizing() - ? `translateX(${ - (table.options.columnResizeDirection === - 'rtl' - ? -1 - : 1) * - (table.getState().columnSizingInfo - .deltaOffset ?? 0) - }px)` - : '', - }, - }} - /> + header.column.columnDef.header, + header.getContext() + )} +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `resizer ${ + table.options.columnResizeDirection + } ${header.column.getIsResizing() ? 'isResizing' : ''}`, + style: { + transform: + columnResizeMode === 'onEnd' && + header.column.getIsResizing() + ? `translateX(${ + (table.options.columnResizeDirection === 'rtl' + ? -1 + : 1) * + (table.getState().columnSizingInfo + .deltaOffset ?? 0) + }px)` + : '', + }, + }} + /> ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( - {row.getVisibleCells().map(cell => ( + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -158,5 +162,5 @@ export const ResizableCols = () => {
- ) -} + ); +}; diff --git a/react/resizing/src/customTypings.d.ts b/react/resizing/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/resizing/src/customTypings.d.ts +++ b/react/resizing/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/resizing/src/index.scss b/react/resizing/src/index.scss index d7ecac8..55372d7 100644 --- a/react/resizing/src/index.scss +++ b/react/resizing/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; diff --git a/react/resizing/src/index.ts b/react/resizing/src/index.ts index bbf3ed5..0229c69 100644 --- a/react/resizing/src/index.ts +++ b/react/resizing/src/index.ts @@ -1,3 +1,3 @@ -import { ResizableCols } from "./ResizableCols"; +import { ResizableCols } from './ResizableCols'; -export { ResizableCols }; \ No newline at end of file +export { ResizableCols }; diff --git a/react/resizing/src/main.tsx b/react/resizing/src/main.tsx index a85a08b..8a32e12 100644 --- a/react/resizing/src/main.tsx +++ b/react/resizing/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { ResizableCols } from './ResizableCols' +import { ResizableCols } from './ResizableCols'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/resizing/src/makeData.ts b/react/resizing/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/resizing/src/makeData.ts +++ b/react/resizing/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/resizing/vite.config.ts b/react/resizing/vite.config.ts index 5a33944..627a319 100644 --- a/react/resizing/vite.config.ts +++ b/react/resizing/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/row-click/README.md b/react/row-click/README.md index 74872fd..f2dc982 100644 --- a/react/row-click/README.md +++ b/react/row-click/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/row-click/eslint.config.ts b/react/row-click/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/row-click/eslint.config.ts +++ b/react/row-click/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/row-click/src/App.scss b/react/row-click/src/App.scss index 41cbc06..bee447b 100644 --- a/react/row-click/src/App.scss +++ b/react/row-click/src/App.scss @@ -25,4 +25,4 @@ .row-click { cursor: pointer; -} \ No newline at end of file +} diff --git a/react/row-click/src/ExampleLink.tsx b/react/row-click/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/row-click/src/ExampleLink.tsx +++ b/react/row-click/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/row-click/src/RowClick.tsx b/react/row-click/src/RowClick.tsx index 7151947..da5fe31 100644 --- a/react/row-click/src/RowClick.tsx +++ b/react/row-click/src/RowClick.tsx @@ -1,4 +1,4 @@ -import { useRef, useState, useLayoutEffect } from 'react' +import { useRef, useState, useLayoutEffect } from 'react'; import { DataTable } from '@carbon/react'; import { SidePanel } from '@carbon/ibm-products'; const { @@ -8,7 +8,7 @@ const { TableContainer, TableHead, TableHeader, - TableRow + TableRow, } = DataTable; import { @@ -16,32 +16,32 @@ import { flexRender, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -52,10 +52,10 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; export const RowClick = () => { - const [data] = useState(makeData(7)) + const [data] = useState(makeData(7)); const [panelOpen, setPanelOpen] = useState(false); const [panelData, setPanelData] = useState(null); @@ -63,7 +63,7 @@ export const RowClick = () => { data, columns, getCoreRowModel: getCoreRowModel(), - }) + }); const tableWrap = useRef(); @@ -80,57 +80,63 @@ export const RowClick = () => { - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > - + }}> +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => ( + {headerGroup.headers.map((header) => ( + }}> {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( { - setPanelOpen(prev => !prev) + setPanelOpen((prev) => !prev); setPanelData(row.original); }} - className='row-click' - > - {row.getVisibleCells().map(cell => ( + className="row-click"> + {row.getVisibleCells().map((cell) => ( + }}> {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -144,10 +150,9 @@ export const RowClick = () => { size="md" onRequestClose={() => setPanelOpen(false)} title={panelData?.name ?? 'Default title'} - labelText='Resource' - > + labelText="Resource"> testing - ) -} + ); +}; diff --git a/react/row-click/src/customTypings.d.ts b/react/row-click/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/row-click/src/customTypings.d.ts +++ b/react/row-click/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/row-click/src/index.scss b/react/row-click/src/index.scss index f8f9347..b7ef722 100644 --- a/react/row-click/src/index.scss +++ b/react/row-click/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/row-click/src/index.ts b/react/row-click/src/index.ts index 734d81c..637ea0c 100644 --- a/react/row-click/src/index.ts +++ b/react/row-click/src/index.ts @@ -1,3 +1,3 @@ -import { RowClick } from "./RowClick"; +import { RowClick } from './RowClick'; -export { RowClick }; \ No newline at end of file +export { RowClick }; diff --git a/react/row-click/src/main.tsx b/react/row-click/src/main.tsx index 365e2fb..fe8efbd 100644 --- a/react/row-click/src/main.tsx +++ b/react/row-click/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { RowClick } from './RowClick' +import { RowClick } from './RowClick'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/row-click/src/makeData.ts b/react/row-click/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/row-click/src/makeData.ts +++ b/react/row-click/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/row-click/vite.config.ts b/react/row-click/vite.config.ts index 5a33944..627a319 100644 --- a/react/row-click/vite.config.ts +++ b/react/row-click/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/rowExpansion/README.md b/react/rowExpansion/README.md index 74872fd..f2dc982 100644 --- a/react/rowExpansion/README.md +++ b/react/rowExpansion/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/rowExpansion/eslint.config.ts b/react/rowExpansion/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/rowExpansion/eslint.config.ts +++ b/react/rowExpansion/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/rowExpansion/src/ExampleLink.tsx b/react/rowExpansion/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/rowExpansion/src/ExampleLink.tsx +++ b/react/rowExpansion/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/rowExpansion/src/RowExpansion.tsx b/react/rowExpansion/src/RowExpansion.tsx index 5d55ae5..5208455 100644 --- a/react/rowExpansion/src/RowExpansion.tsx +++ b/react/rowExpansion/src/RowExpansion.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState } from 'react'; import { DataTable } from '@carbon/react'; const { Table, @@ -10,7 +10,7 @@ const { TableRow, TableExpandHeader, TableExpandRow, - TableExpandedRow + TableExpandedRow, } = DataTable; import { @@ -19,33 +19,33 @@ import { flexRender, getCoreRowModel, useReactTable, - getExpandedRowModel -} from '@tanstack/react-table' + getExpandedRowModel, +} from '@tanstack/react-table'; import { makeData } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -56,12 +56,12 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; export const RowExpansion = () => { - const [data] = useState(makeData(7)) - const [expanded, setExpanded] = useState({}) - const [isAllExpanded, setIsAllExpanded] = useState(false) + const [data] = useState(makeData(7)); + const [expanded, setExpanded] = useState({}); + const [isAllExpanded, setIsAllExpanded] = useState(false); const table = useReactTable({ data, @@ -72,27 +72,36 @@ export const RowExpansion = () => { state: { expanded, }, - }) + }); return ( - - - } + description={ + + + + + } style={{ width: table.getCenterTotalSize(), - }} - > -
+ }}> +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( { onExpand={() => { const newExpandState = {}; if (!isAllExpanded) { - table.getRowModel().rows.map(row => { + table.getRowModel().rows.map((row) => { newExpandState[row.id] = true; }); setIsAllExpanded(true); @@ -114,51 +123,45 @@ export const RowExpansion = () => { }} isExpanded={false} /> - {headerGroup.headers.map(header => ( - + {headerGroup.headers.map((header) => ( + {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext() + )} ))} ))} - {table.getRowModel().rows.map(row => ( + {table.getRowModel().rows.map((row) => ( { const isRowExpanded = !!expanded[row.id]; if (isRowExpanded) { - const newExpansionState = {...expanded, [row.id]: false}; + const newExpansionState = { ...expanded, [row.id]: false }; setExpanded(newExpansionState); return; } - const newExpansionState = {...expanded, [row.id]: true}; + const newExpansionState = { ...expanded, [row.id]: true }; setExpanded(newExpansionState); }} isExpanded={!!expanded[row.id]} - aria-label="Row expander" - > - {row.getVisibleCells().map(cell => ( - + aria-label="Row expander"> + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} + className="demo-expanded-td">
Expandable row content
Description here
@@ -167,5 +170,5 @@ export const RowExpansion = () => {
- ) -} + ); +}; diff --git a/react/rowExpansion/src/customTypings.d.ts b/react/rowExpansion/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/rowExpansion/src/customTypings.d.ts +++ b/react/rowExpansion/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/rowExpansion/src/index.scss b/react/rowExpansion/src/index.scss index f8f9347..b7ef722 100644 --- a/react/rowExpansion/src/index.scss +++ b/react/rowExpansion/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/rowExpansion/src/index.ts b/react/rowExpansion/src/index.ts index d25e7ad..f9cb7c3 100644 --- a/react/rowExpansion/src/index.ts +++ b/react/rowExpansion/src/index.ts @@ -1,3 +1,3 @@ -import { RowExpansion } from "./RowExpansion"; +import { RowExpansion } from './RowExpansion'; -export { RowExpansion }; \ No newline at end of file +export { RowExpansion }; diff --git a/react/rowExpansion/src/main.tsx b/react/rowExpansion/src/main.tsx index a6f0a7f..65e68e7 100644 --- a/react/rowExpansion/src/main.tsx +++ b/react/rowExpansion/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { RowExpansion } from './RowExpansion' +import { RowExpansion } from './RowExpansion'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/rowExpansion/src/makeData.ts b/react/rowExpansion/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/rowExpansion/src/makeData.ts +++ b/react/rowExpansion/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/rowExpansion/vite.config.ts b/react/rowExpansion/vite.config.ts index 5a33944..627a319 100644 --- a/react/rowExpansion/vite.config.ts +++ b/react/rowExpansion/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/sortable/README.md b/react/sortable/README.md index 74872fd..f2dc982 100644 --- a/react/sortable/README.md +++ b/react/sortable/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/sortable/eslint.config.ts b/react/sortable/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/sortable/eslint.config.ts +++ b/react/sortable/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/sortable/src/App.scss b/react/sortable/src/App.scss index 18671ee..021aacb 100644 --- a/react/sortable/src/App.scss +++ b/react/sortable/src/App.scss @@ -44,4 +44,4 @@ .sortable-example { padding-bottom: 2rem; -} \ No newline at end of file +} diff --git a/react/sortable/src/ExampleLink.tsx b/react/sortable/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/sortable/src/ExampleLink.tsx +++ b/react/sortable/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/sortable/src/SortableColumns.tsx b/react/sortable/src/SortableColumns.tsx index 1b9702f..788677b 100644 --- a/react/sortable/src/SortableColumns.tsx +++ b/react/sortable/src/SortableColumns.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; import { ColumnDef, @@ -8,49 +8,42 @@ import { SortingFn, SortingState, useReactTable, -} from '@tanstack/react-table' -import { Button, DataTable, TableContainer } from '@carbon/react' -import { ArrowUp } from '@carbon/react/icons' -import cx from 'classnames' +} from '@tanstack/react-table'; +import { Button, DataTable, TableContainer } from '@carbon/react'; +import { ArrowUp } from '@carbon/react/icons'; +import cx from 'classnames'; -const { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} = DataTable; +const { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } = + DataTable; - -import { makeData, Resource } from './makeData' +import { makeData, Resource } from './makeData'; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; //custom sorting logic for one of our enum columns const sortStatusFn: SortingFn = (rowA, rowB) => { - const statusA = rowA.original.status - const statusB = rowB.original.status - const statusOrder = ['starting', 'active', 'disabled'] - return statusOrder.indexOf(statusA) - statusOrder.indexOf(statusB) -} + const statusA = rowA.original.status; + const statusB = rowB.original.status; + const statusOrder = ['starting', 'active', 'disabled']; + return statusOrder.indexOf(statusA) - statusOrder.indexOf(statusB); +}; export const SortableColumns = () => { - const [sorting, setSorting] = React.useState([]) + const [sorting, setSorting] = React.useState([]); const columns = React.useMemo[]>( () => [ { accessorKey: 'name', - cell: info => info.getValue(), + cell: (info) => info.getValue(), header: () => Name, //this column will sort in ascending order by default since it is a string column }, { - accessorFn: row => row.rule, + accessorFn: (row) => row.rule, id: 'rule', - cell: info => info.getValue(), + cell: (info) => info.getValue(), header: () => Rule, sortUndefined: 'last', //force undefined values to the end sortDescFirst: false, //first sort order will be ascending (nullable values can mess up auto detection of sort order) @@ -79,9 +72,9 @@ export const SortableColumns = () => { // }, ], [] - ) + ); - const [data] = React.useState(() => makeData(1000)) + const [data] = React.useState(() => makeData(1000)); const table = useReactTable({ columns, @@ -103,7 +96,7 @@ export const SortableColumns = () => { // enableSortingRemoval: false, //Don't allow - default on/true // isMultiSortEvent: (e) => true, //Make all clicks multi-sort - default requires `shift` key // maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once - default is Infinity - }) + }); //access sorting state from the table instance // console.log(table.getState().sorting) @@ -111,53 +104,61 @@ export const SortableColumns = () => { return ( - - - } - > - + className="tanstack-example" + description={ + + + + + }> +
- {table.getHeaderGroups().map(headerGroup => ( + {table.getHeaderGroups().map((headerGroup) => ( - {headerGroup.headers.map(header => { + {headerGroup.headers.map((header) => { return ( - + {header.isPlaceholder ? null : ( )} - ) + ); })} ))} @@ -166,10 +167,10 @@ export const SortableColumns = () => { {table .getRowModel() .rows.slice(0, 10) - .map(row => { + .map((row) => { return ( - {row.getVisibleCells().map(cell => { + {row.getVisibleCells().map((cell) => { return ( {flexRender( @@ -177,13 +178,13 @@ export const SortableColumns = () => { cell.getContext() )} - ) + ); })} - ) + ); })}
- ) -} + ); +}; diff --git a/react/sortable/src/customTypings.d.ts b/react/sortable/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/sortable/src/customTypings.d.ts +++ b/react/sortable/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/sortable/src/index.scss b/react/sortable/src/index.scss index f8f9347..b7ef722 100644 --- a/react/sortable/src/index.scss +++ b/react/sortable/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/sortable/src/index.ts b/react/sortable/src/index.ts index c37e7e4..f80e7ac 100644 --- a/react/sortable/src/index.ts +++ b/react/sortable/src/index.ts @@ -1,3 +1,3 @@ -import { SortableColumns } from "./SortableColumns"; +import { SortableColumns } from './SortableColumns'; -export { SortableColumns }; \ No newline at end of file +export { SortableColumns }; diff --git a/react/sortable/src/main.tsx b/react/sortable/src/main.tsx index 104f38e..98e2c52 100644 --- a/react/sortable/src/main.tsx +++ b/react/sortable/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { SortableColumns } from './SortableColumns' +import { SortableColumns } from './SortableColumns'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/sortable/src/makeData.ts b/react/sortable/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/sortable/src/makeData.ts +++ b/react/sortable/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/sortable/vite.config.ts b/react/sortable/vite.config.ts index 5a33944..627a319 100644 --- a/react/sortable/vite.config.ts +++ b/react/sortable/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/react/sticky-columns/README.md b/react/sticky-columns/README.md index 74872fd..f2dc982 100644 --- a/react/sticky-columns/README.md +++ b/react/sticky-columns/README.md @@ -1,15 +1,19 @@ # React + TypeScript + Vite -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React working in Vite with HMR and +some ESLint rules. Currently, two official plugins are available: -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) + uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) + uses [SWC](https://swc.rs/) for Fast Refresh ## Expanding the ESLint configuration -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +If you are developing a production application, we recommend updating the +configuration to enable type aware lint rules: - Configure the top-level `parserOptions` property like this: @@ -22,16 +26,20 @@ export default tseslint.config({ tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Replace `tseslint.configs.recommended` to + `tseslint.configs.recommendedTypeChecked` or + `tseslint.configs.strictTypeChecked` - Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: +- Install + [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and + update the config: ```js // eslint.config.js -import react from 'eslint-plugin-react' +import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version @@ -46,5 +54,5 @@ export default tseslint.config({ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, -}) +}); ``` diff --git a/react/sticky-columns/eslint.config.ts b/react/sticky-columns/eslint.config.ts index 77e7ca3..e029aa5 100644 --- a/react/sticky-columns/eslint.config.ts +++ b/react/sticky-columns/eslint.config.ts @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -23,7 +23,7 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], - "@typescript-eslint/no-explicit-any": "off", + '@typescript-eslint/no-explicit-any': 'off', }, - }, -) + } +); diff --git a/react/sticky-columns/src/ExampleLink.tsx b/react/sticky-columns/src/ExampleLink.tsx index b721e60..05bca8e 100644 --- a/react/sticky-columns/src/ExampleLink.tsx +++ b/react/sticky-columns/src/ExampleLink.tsx @@ -2,8 +2,10 @@ import React from 'react'; export const ExampleLink = ({ icon, label, url }) => { const Icon = icon; - return - {icon && } - {label} - -} \ No newline at end of file + return ( + + {icon && } + {label} + + ); +}; diff --git a/react/sticky-columns/src/StickyColumns.tsx b/react/sticky-columns/src/StickyColumns.tsx index 57d96d8..4907eef 100644 --- a/react/sticky-columns/src/StickyColumns.tsx +++ b/react/sticky-columns/src/StickyColumns.tsx @@ -1,40 +1,38 @@ -import React, { CSSProperties } from 'react' +import React, { CSSProperties } from 'react'; import { Column, flexRender, getCoreRowModel, useReactTable, - createColumnHelper -} from '@tanstack/react-table' -import { makeData, Resource } from './makeData' + createColumnHelper, +} from '@tanstack/react-table'; +import { makeData, Resource } from './makeData'; import { DataTable, IconButton, TableContainer } from '@carbon/react'; import { TrashCan, Edit } from '@carbon/react/icons'; -const { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} = DataTable; +const { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } = + DataTable; import { ExampleLink } from './ExampleLink'; -import { Launch } from '@carbon/react/icons' -import * as packageJson from '../package.json' +import { Launch } from '@carbon/react/icons'; +import * as packageJson from '../package.json'; //These are the important styles to make sticky column pinning work! //Apply styles like this using your CSS strategy of choice with this kind of logic to head cells, data cells, footer cells, etc. //View the index.css file for more needed styles such as border-collapse: separate const getCommonPinningStyles = (column: Column): CSSProperties => { - const isPinned = column.getIsPinned() + const isPinned = column.getIsPinned(); const isLastLeftPinnedColumn = - isPinned === 'left' && column.getIsLastColumn('left') + isPinned === 'left' && column.getIsLastColumn('left'); const isFirstRightPinnedColumn = - isPinned === 'right' && column.getIsFirstColumn('right') + isPinned === 'right' && column.getIsFirstColumn('right'); return { - borderRight: isLastLeftPinnedColumn ? '1px solid var(--cds-border-subtle)' : 0, - borderLeft: isFirstRightPinnedColumn ? '1px solid var(--cds-border-subtle)' : 0, + borderRight: isLastLeftPinnedColumn + ? '1px solid var(--cds-border-subtle)' + : 0, + borderLeft: isFirstRightPinnedColumn + ? '1px solid var(--cds-border-subtle)' + : 0, left: isPinned === 'left' ? `${column.getStart('left')}px` : undefined, right: isPinned === 'right' ? `${column.getAfter('right')}px` : undefined, opacity: isPinned ? 0.95 : 1, @@ -42,27 +40,26 @@ const getCommonPinningStyles = (column: Column): CSSProperties => { width: column.getSize(), zIndex: isPinned ? 1 : 0, backgroundColor: 'var(--cds-layer)', - } -} - + }; +}; export const StickyColumns = () => { const onDelete = (row: Resource) => { console.log(row); - } + }; const onEdit = (row: Resource) => { console.log(row); - } - const columnHelper = createColumnHelper() + }; + const columnHelper = createColumnHelper(); const defaultCols = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => {info.getValue()}, + cell: (info) => {info.getValue()}, header: () => Name, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => Status, @@ -72,25 +69,35 @@ export const StickyColumns = () => { }), columnHelper.accessor('example', { header: 'Example', - id: 'example' + id: 'example', }), { header: 'Actions', id: 'actions', cell: ({ row }) => { - return
- onDelete(row)} kind="ghost"> - - - onEdit(row)} kind="ghost"> - - -
- } + return ( +
+ onDelete(row)} + kind="ghost"> + + + onEdit(row)} + kind="ghost"> + + +
+ ); + }, }, - ] - const [data] = React.useState(() => makeData(5)) - const [columns] = React.useState(() => [...defaultCols]) + ]; + const [data] = React.useState(() => makeData(5)); + const [columns] = React.useState(() => [...defaultCols]); const table = useReactTable({ data, @@ -101,71 +108,79 @@ export const StickyColumns = () => { left: ['name'], right: ['actions'], }, - } - }) + }, + }); return ( - - - - } - style={{ - width: 500, - }} - > - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => { - const { column } = header + + + + + } + style={{ + width: 500, + }}> +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const { column } = header; - return ( - -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} -
-
- ) - })} -
- ))} -
- - {table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => { - const { column } = cell - return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ) - })} - - ))} - -
-
- ) -} + return ( + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
+
+ ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + const { column } = cell; + return ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ); + })} + + ))} + + + + ); +}; diff --git a/react/sticky-columns/src/customTypings.d.ts b/react/sticky-columns/src/customTypings.d.ts index 0a4913c..b6c0132 100644 --- a/react/sticky-columns/src/customTypings.d.ts +++ b/react/sticky-columns/src/customTypings.d.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import "@tanstack/react-table"; +import '@tanstack/react-table'; -declare module "@tanstack/table-core" { +declare module '@tanstack/table-core' { interface TableOptions - extends PartialKeys, "state" | "onStateChange" | "renderFallbackValue"> { + extends PartialKeys< + TableOptionsResolved, + 'state' | 'onStateChange' | 'renderFallbackValue' + > { filterFns?: FilterFns; } @@ -13,6 +16,6 @@ declare module "@tanstack/table-core" { //allows us to define custom properties for our columns interface ColumnMeta { - filterVariant?: 'text' | 'select' | 'checkbox' | 'number' + filterVariant?: 'text' | 'select' | 'checkbox' | 'number'; } -} \ No newline at end of file +} diff --git a/react/sticky-columns/src/index.scss b/react/sticky-columns/src/index.scss index f8f9347..b7ef722 100644 --- a/react/sticky-columns/src/index.scss +++ b/react/sticky-columns/src/index.scss @@ -1,4 +1,6 @@ -@use '@carbon/react' with ($font-path: '@ibm/plex'); +@use '@carbon/react' with ( + $font-path: '@ibm/plex' +); @use '@carbon/styles/scss/type'; @use '@carbon/ibm-products/css/index'; @use './App.scss'; @@ -18,4 +20,4 @@ body { .example--link__icon { margin-right: 0.25rem; -} \ No newline at end of file +} diff --git a/react/sticky-columns/src/index.ts b/react/sticky-columns/src/index.ts index 322442c..58b5bbf 100644 --- a/react/sticky-columns/src/index.ts +++ b/react/sticky-columns/src/index.ts @@ -1,3 +1,3 @@ -import { StickyColumns } from "./StickyColumns"; +import { StickyColumns } from './StickyColumns'; -export { StickyColumns }; \ No newline at end of file +export { StickyColumns }; diff --git a/react/sticky-columns/src/main.tsx b/react/sticky-columns/src/main.tsx index 2bc1516..29c072c 100644 --- a/react/sticky-columns/src/main.tsx +++ b/react/sticky-columns/src/main.tsx @@ -1,5 +1,5 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; import { CodeSnippet, Column, @@ -7,18 +7,22 @@ import { Header, HeaderContainer, HeaderName, -} from '@carbon/react' +} from '@carbon/react'; -import { StickyColumns } from './StickyColumns' +import { StickyColumns } from './StickyColumns'; -import './index.scss' +import './index.scss'; const renderUIShellHeader = () => ( (
- DataTable / @tanstack/tableexplorations + DataTable /{' '} + + @tanstack/table + + explorations
)} @@ -28,10 +32,10 @@ const renderUIShellHeader = () => ( createRoot(document.getElementById('root')!).render( {renderUIShellHeader()} - + -) +); diff --git a/react/sticky-columns/src/makeData.ts b/react/sticky-columns/src/makeData.ts index 2751502..f7fa331 100644 --- a/react/sticky-columns/src/makeData.ts +++ b/react/sticky-columns/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/react/sticky-columns/vite.config.ts b/react/sticky-columns/vite.config.ts index 5a33944..627a319 100644 --- a/react/sticky-columns/vite.config.ts +++ b/react/sticky-columns/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/web-components/README.md b/web-components/README.md index 7a8c90b..62300ce 100644 --- a/web-components/README.md +++ b/web-components/README.md @@ -1,11 +1,11 @@ ## Data table extensions, `@tanstack/lit-table` -| Example | Code sandbox | Stackblitz -| --- | --- | --- | -| Basic | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/basic) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/basic) -| Batch actions | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/batch-actions) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/batch-actions) -| Column resizing | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/resizing) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/resizing) -| Customize columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/customize-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/customize-columns) -| Nested rows | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/nested-rows) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/nested-rows) -| Sticky columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/sticky-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/sticky-columns) -| Virtualized | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/virtual) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/virtual) +| Example | Code sandbox | Stackblitz | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| Basic | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/basic) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/basic) | +| Batch actions | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/batch-actions) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/batch-actions) | +| Column resizing | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/resizing) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/resizing) | +| Customize columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/customize-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/customize-columns) | +| Nested rows | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/nested-rows) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/nested-rows) | +| Sticky columns | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/sticky-columns) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/sticky-columns) | +| Virtualized | [View example](https://codesandbox.io/s/github/matthewgallo/tanstack-carbon/tree/main/web-components/virtual) | [View example](https://stackblitz.com/github/matthewgallo/tanstack-carbon/tree/main/web-components/virtual) | diff --git a/web-components/basic/src/basic.ts b/web-components/basic/src/basic.ts index 8b5c063..f12779e 100644 --- a/web-components/basic/src/basic.ts +++ b/web-components/basic/src/basic.ts @@ -1,36 +1,35 @@ -import { LitElement, css, html } from 'lit' -import { customElement } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { createColumnHelper, flexRender, getCoreRowModel, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import { makeData } from './makeData'; - type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -41,7 +40,7 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; const data: Resource[] = makeData(10); @@ -59,41 +58,42 @@ export class MyBasicTable extends LitElement { columns, data, getCoreRowModel: getCoreRowModel(), - }) + }); return html` ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -106,7 +106,7 @@ export class MyBasicTable extends LitElement { )} - ` + `; } static styles = css` @@ -117,11 +117,11 @@ export class MyBasicTable extends LitElement { display: flex; place-items: center; } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'basic-tanstack-table': MyBasicTable + 'basic-tanstack-table': MyBasicTable; } } diff --git a/web-components/basic/src/makeData.ts b/web-components/basic/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/basic/src/makeData.ts +++ b/web-components/basic/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/batch-actions/src/batch.ts b/web-components/batch-actions/src/batch.ts index 566749b..ba56fa5 100644 --- a/web-components/batch-actions/src/batch.ts +++ b/web-components/batch-actions/src/batch.ts @@ -1,6 +1,6 @@ -import { LitElement, css, html } from 'lit' -import { customElement, state } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { ColumnDef, flexRender, @@ -8,7 +8,7 @@ import { getFilteredRowModel, getPaginationRowModel, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import '@carbon/web-components/es/components/checkbox/index.js'; import '@carbon/web-components/es/components/pagination/index.js'; @@ -19,17 +19,19 @@ import Add from '@carbon/web-components/es/icons/add/16'; import Save from '@carbon/web-components/es/icons/save/16'; import Download from '@carbon/web-components/es/icons/download/16'; import { makeData } from './makeData'; -import { CDSPagination, CDSTableToolbarSearch } from '@carbon/web-components/es'; - +import { + CDSPagination, + CDSTableToolbarSearch, +} from '@carbon/web-components/es'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; const columns: ColumnDef[] = [ { @@ -37,12 +39,12 @@ const columns: ColumnDef[] = [ header: ({ table }) => { console.log(table.getIsAllRowsSelected()); return html` - - `}, + + `; + }, cell: ({ row }) => html` [] = [ }, { accessorKey: 'name', - cell: info => info.getValue(), + cell: (info) => info.getValue(), }, { - accessorFn: row => row.rule, + accessorFn: (row) => row.rule, id: 'rule', - cell: info => info.getValue(), + cell: (info) => info.getValue(), header: () => html`Rule`, }, { @@ -74,7 +76,7 @@ const columns: ColumnDef[] = [ accessorKey: 'example', header: 'Example', }, -] +]; const data: Resource[] = makeData(100); @@ -88,7 +90,7 @@ export class MyBatchTable extends LitElement { private tableController = new TableController(this); @state() - private _rowSelection: Record = {} + private _rowSelection: Record = {}; @state() private _globalFilter = ''; @@ -104,18 +106,18 @@ export class MyBatchTable extends LitElement { }, enableRowSelection: true, enableGlobalFilter: true, - onRowSelectionChange: updaterOrValue => { + onRowSelectionChange: (updaterOrValue) => { if (typeof updaterOrValue === 'function') { - this._rowSelection = updaterOrValue(this._rowSelection) + this._rowSelection = updaterOrValue(this._rowSelection); } else { - this._rowSelection = updaterOrValue + this._rowSelection = updaterOrValue; } }, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), debugTable: true, - }) + }); console.log(table.getRowModel().rowsById); @@ -127,28 +129,31 @@ export class MyBatchTable extends LitElement { detail: { pageSize: number; page: number; - } + }; } interface toolbarSearchDetail { detail: { value: string; - } + }; } interface searchFull extends CDSTableToolbarSearch, toolbarSearchDetail {} interface paginationFull extends CDSPagination, paginationDetail {} return html` - Batch actions + Batch actions With toolbar + >With toolbar table.toggleAllRowsSelected(false)} - > + @cds-table-batch-actions-cancel-clicked=${() => + table.toggleAllRowsSelected(false)}> Delete ${TrashCan({ slot: 'icon' })} ${Add({ slot: 'icon' })} this._globalFilter = e.detail.value} - > + @cds-search-input=${(e: searchFull) => + (this._globalFilter = + e.detail.value)}> ${Settings({ slot: 'icon', @@ -188,34 +194,35 @@ export class MyBatchTable extends LitElement { ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -233,17 +240,16 @@ export class MyBatchTable extends LitElement { total-items="${table.getRowCount()}" @cds-pagination-changed-current="${(event: paginationFull) => { const { pageSize, page } = event.detail; - table.setPageSize(Number(pageSize)) - table.setPageIndex(page - 1) - }}" - > + table.setPageSize(Number(pageSize)); + table.setPageIndex(page - 1); + }}"> 10 20 30 40 50 - ` + `; } static styles = css` @@ -262,11 +268,11 @@ export class MyBatchTable extends LitElement { pointer-events: all; transform: translate3d(0, 0, 0); } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'batch-tanstack-table': MyBatchTable + 'batch-tanstack-table': MyBatchTable; } } diff --git a/web-components/batch-actions/src/customTypings.d.ts b/web-components/batch-actions/src/customTypings.d.ts index 0b06273..1a1535a 100644 --- a/web-components/batch-actions/src/customTypings.d.ts +++ b/web-components/batch-actions/src/customTypings.d.ts @@ -1,5 +1,5 @@ -declare module "@carbon/web-components/es/icons/settings/16" -declare module "@carbon/web-components/es/icons/trash-can/16" -declare module "@carbon/web-components/es/icons/add/16" -declare module "@carbon/web-components/es/icons/download/16" -declare module "@carbon/web-components/es/icons/save/16" \ No newline at end of file +declare module '@carbon/web-components/es/icons/settings/16'; +declare module '@carbon/web-components/es/icons/trash-can/16'; +declare module '@carbon/web-components/es/icons/add/16'; +declare module '@carbon/web-components/es/icons/download/16'; +declare module '@carbon/web-components/es/icons/save/16'; diff --git a/web-components/batch-actions/src/makeData.ts b/web-components/batch-actions/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/batch-actions/src/makeData.ts +++ b/web-components/batch-actions/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/customize-columns/src/basic.ts b/web-components/customize-columns/src/basic.ts index 7e3b833..af6648a 100644 --- a/web-components/customize-columns/src/basic.ts +++ b/web-components/customize-columns/src/basic.ts @@ -1,12 +1,12 @@ -import { LitElement, css, html } from 'lit' -import { customElement, state } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { createColumnHelper, flexRender, getCoreRowModel, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import '@carbon/web-components/es/components/overflow-menu/index.js'; import '@carbon/web-components/es/components/button/index.js'; @@ -16,27 +16,26 @@ import { CDSTableToolbarSearch } from '@carbon/web-components/es'; import { makeData } from './makeData'; - type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -47,7 +46,7 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; const data: Resource[] = makeData(10); @@ -77,64 +76,68 @@ export class MyBasicTable extends LitElement { getCoreRowModel: getCoreRowModel(), state: { globalFilter: this._globalFilter, - } - }) + }, + }); interface toolbarSearchDetail { detail: { value: string; - } + }; } interface searchFull extends CDSTableToolbarSearch, toolbarSearchDetail {} return html` - + this._globalFilter = e.detail.value} - > + @cds-search-input=${(e: searchFull) => + (this._globalFilter = + e.detail.value)}> ${Column({ + tooltipText="Customize columns" + kind="ghost" + >${Column({ slot: 'icon', class: `customize-col-icon`, - })} + })} ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -148,22 +151,21 @@ export class MyBasicTable extends LitElement { this._tearsheetOpen = false} - > - Customize column order - ${repeat( - table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => - html`
+ width="narrow" + @cds-tearsheet-closed=${() => (this._tearsheetOpen = false)}> + Customize column order + ${repeat( + table.getHeaderGroups(), + (headerGroup) => headerGroup.id, + (headerGroup) => + html`
${repeat( headerGroup.headers, - header => header.id, - header => + (header) => header.id, + (header) => html`
${header.isPlaceholder ? null @@ -172,18 +174,23 @@ export class MyBasicTable extends LitElement { header.getContext() )}
` - )}
` - )} - this._tearsheetOpen = false} - slot="actions" - kind=${'secondary'}>Cancel - this._tearsheetOpen = false} - slot="actions" - kind=${'primary'}>Save + )} +
` + )} + (this._tearsheetOpen = false)} + slot="actions" + kind=${'secondary'} + >Cancel + (this._tearsheetOpen = false)} + slot="actions" + kind=${'primary'} + >Save
- ` + `; } static styles = css` @@ -196,7 +203,7 @@ export class MyBasicTable extends LitElement { } .customize-col-icon { - fill: var(--cds-icon-primary) + fill: var(--cds-icon-primary); } .drag-wrapper { @@ -226,11 +233,11 @@ export class MyBasicTable extends LitElement { .ghost-item { opacity: 0; } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'basic-tanstack-table': MyBasicTable + 'basic-tanstack-table': MyBasicTable; } } diff --git a/web-components/customize-columns/src/customTypings.d.ts b/web-components/customize-columns/src/customTypings.d.ts index c713950..8ebd0ef 100644 --- a/web-components/customize-columns/src/customTypings.d.ts +++ b/web-components/customize-columns/src/customTypings.d.ts @@ -1 +1 @@ -declare module "@carbon/web-components/es/icons/column/16" \ No newline at end of file +declare module '@carbon/web-components/es/icons/column/16'; diff --git a/web-components/customize-columns/src/makeData.ts b/web-components/customize-columns/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/customize-columns/src/makeData.ts +++ b/web-components/customize-columns/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/nested-rows/src/customTypings.d.ts b/web-components/nested-rows/src/customTypings.d.ts index 39648c2..702e1c2 100644 --- a/web-components/nested-rows/src/customTypings.d.ts +++ b/web-components/nested-rows/src/customTypings.d.ts @@ -1 +1 @@ -declare module '@carbon/web-components/es/icons/chevron--right/16' \ No newline at end of file +declare module '@carbon/web-components/es/icons/chevron--right/16'; diff --git a/web-components/nested-rows/src/makeData.ts b/web-components/nested-rows/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/nested-rows/src/makeData.ts +++ b/web-components/nested-rows/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/nested-rows/src/nestedRowTable.ts b/web-components/nested-rows/src/nestedRowTable.ts index 6006f20..a89afe5 100644 --- a/web-components/nested-rows/src/nestedRowTable.ts +++ b/web-components/nested-rows/src/nestedRowTable.ts @@ -1,31 +1,30 @@ -import { LitElement, css, html } from 'lit' -import { customElement, state } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +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' +} 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 { 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[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const data: Resource[] = makeData(10, 5); @@ -39,80 +38,78 @@ export class NestedRowTable extends LitElement { private tableController = new TableController(this); _columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - header: ({ table }) => html` -
- { - 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' - > + header: ({ table }) => html`
+ { + 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', + class: table.getIsAllRowsExpanded() + ? `row-expanded-icon` + : 'row-expandable-icon', })} - - Name -
`, +
+ Name +
`, cell: ({ row, renderValue }) => { - return html` -
-
- ${row.getCanExpand() ? ( - html` { - // 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', - })} - ` - ) : ( - null - )} + return html`
+
+ ${row.getCanExpand() + ? html` { + // 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', + })} + ` + : null} ${renderValue()}
-
`}, +
`; + }, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -123,7 +120,7 @@ export class NestedRowTable extends LitElement { columnHelper.accessor('example', { header: 'Example', }), - ] + ]; @state() private _expanded = {}; @@ -138,45 +135,46 @@ export class NestedRowTable extends LitElement { getCoreRowModel: getCoreRowModel(), getExpandedRowModel: getExpandedRowModel(), // onExpandedChange: (row) => console.log('onChange', row), - getSubRows: row => row.subRows, + getSubRows: (row) => row.subRows, state: { expanded: this._expanded, - } - }) + }, + }); return html` ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -189,7 +187,7 @@ export class NestedRowTable extends LitElement { )} - ` + `; } static styles = css` @@ -209,12 +207,11 @@ export class NestedRowTable extends LitElement { transform: rotate(0.25turn); transition: transform 150ms ease-in; // replace with carbon motion easing } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'nested-row-table': NestedRowTable + 'nested-row-table': NestedRowTable; } - -} \ No newline at end of file +} diff --git a/web-components/resizing/src/makeData.ts b/web-components/resizing/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/resizing/src/makeData.ts +++ b/web-components/resizing/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/resizing/src/resizing.ts b/web-components/resizing/src/resizing.ts index 98d3430..2b8b032 100644 --- a/web-components/resizing/src/resizing.ts +++ b/web-components/resizing/src/resizing.ts @@ -1,36 +1,35 @@ -import { LitElement, css, html } from 'lit' -import { customElement } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { createColumnHelper, flexRender, getCoreRowModel, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import { makeData } from './makeData'; - type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -41,7 +40,7 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; const data: Resource[] = makeData(10); @@ -61,55 +60,53 @@ export class ResizableColumns extends LitElement { columnResizeMode: 'onChange', columnResizeDirection: 'ltr', getCoreRowModel: getCoreRowModel(), - }) + }); return html` ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - - ${flexRender( - header.column.columnDef.header, - header.getContext() - )} - ${header.isPlaceholder - ? null - : html`
`} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${flexRender( + header.column.columnDef.header, + header.getContext() + )} + ${header.isPlaceholder + ? null + : html`
`} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -122,7 +119,7 @@ export class ResizableColumns extends LitElement { )} - ` + `; } static styles = css` @@ -171,11 +168,11 @@ export class ResizableColumns extends LitElement { opacity: 1; } } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'resizable-columns': ResizableColumns + 'resizable-columns': ResizableColumns; } } diff --git a/web-components/row-click/src/basic.ts b/web-components/row-click/src/basic.ts index 13e04d9..4e5e409 100644 --- a/web-components/row-click/src/basic.ts +++ b/web-components/row-click/src/basic.ts @@ -1,37 +1,37 @@ -import { LitElement, html, css } from 'lit' -import { customElement, state } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, html, css } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { createColumnHelper, flexRender, getCoreRowModel, RowData, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import '@carbon/web-components/es/components/side-panel/index.js'; import { makeData } from './makeData'; type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -42,7 +42,7 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; const data: Resource[] = makeData(10); @@ -70,49 +70,48 @@ export class MyBasicTable extends LitElement { this._panelOpen = !this._panelOpen; } - - render() { - console.log(this._panelRowData) + console.log(this._panelRowData); const table = this.tableController.table({ columns, data, getCoreRowModel: getCoreRowModel(), - }) + }); return html` ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` this._handlePanelState(row)}> ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => + (cell) => cell.id, + (cell) => html` ${flexRender( cell.column.columnDef.cell, @@ -126,11 +125,14 @@ export class MyBasicTable extends LitElement { this._panelOpen = false} - >children content - ` + @cds-side-panel-closed=${() => (this._panelOpen = false)} + >children content + `; } static styles = css` @@ -141,11 +143,11 @@ export class MyBasicTable extends LitElement { display: flex; place-items: center; } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'basic-tanstack-table': MyBasicTable + 'basic-tanstack-table': MyBasicTable; } } diff --git a/web-components/row-click/src/makeData.ts b/web-components/row-click/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/row-click/src/makeData.ts +++ b/web-components/row-click/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/sticky-columns/src/basic.ts b/web-components/sticky-columns/src/basic.ts index ec911bf..9278be6 100644 --- a/web-components/sticky-columns/src/basic.ts +++ b/web-components/sticky-columns/src/basic.ts @@ -1,40 +1,39 @@ -import { LitElement, css, html } from 'lit' -import { customElement } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { Column, createColumnHelper, flexRender, getCoreRowModel, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; import TrashCan from '@carbon/web-components/es/icons/trash-can/16'; import Edit from '@carbon/web-components/es/icons/edit/16'; import { makeData } from './makeData'; import { styleMap } from 'lit/directives/style-map.js'; - type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'name', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -49,31 +48,35 @@ const columns = [ header: 'Actions', id: 'actions', cell: () => { - return html`
+ return html`
${TrashCan({ slot: 'icon' })} - ${Edit({ slot: 'icon' })} -
` - } - } -] + >${TrashCan({ slot: 'icon' })} + ${Edit({ slot: 'icon' })} +
`; + }, + }, +]; const data: Resource[] = makeData(10); const getCommonPinningStyles = (column: Column) => { - const isPinned = column.getIsPinned() + const isPinned = column.getIsPinned(); const isLastLeftPinnedColumn = - isPinned === 'left' && column.getIsLastColumn('left') + isPinned === 'left' && column.getIsLastColumn('left'); const isFirstRightPinnedColumn = - isPinned === 'right' && column.getIsFirstColumn('right') + isPinned === 'right' && column.getIsFirstColumn('right'); console.log(column.getSize()); return { - borderRight: isLastLeftPinnedColumn ? '1px solid var(--cds-border-subtle)' : 0, - borderLeft: isFirstRightPinnedColumn ? '1px solid var(--cds-border-subtle)' : 0, + borderRight: isLastLeftPinnedColumn + ? '1px solid var(--cds-border-subtle)' + : 0, + borderLeft: isFirstRightPinnedColumn + ? '1px solid var(--cds-border-subtle)' + : 0, left: isPinned === 'left' ? `${column.getStart('left')}px` : undefined, right: isPinned === 'right' ? `${column.getAfter('right')}px` : undefined, opacity: isPinned ? 0.95 : 1, @@ -82,9 +85,9 @@ const getCommonPinningStyles = (column: Column) => { zIndex: isPinned ? 1 : 0, backgroundColor: 'var(--cds-layer)', display: 'flex', - alignItems: 'center' - } -} + alignItems: 'center', + }; +}; /** * An example table using `@tanstack/lit-table` and `@carbon/web-components` DataTable. @@ -105,46 +108,55 @@ export class MyBasicTable extends LitElement { left: ['name'], right: ['actions'], }, - } - }) + }, + }); return html` -
+
${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => + (headerGroup) => headerGroup.id, + (headerGroup) => html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - ` - )}` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` )} ${repeat( table.getRowModel().rows, - row => row.id, - row => html` + (row) => row.id, + (row) => html` ${repeat( row.getVisibleCells(), - cell => cell.id, - cell => - html` + (cell) => cell.id, + (cell) => + html` ${flexRender( cell.column.columnDef.cell, cell.getContext() @@ -157,7 +169,7 @@ export class MyBasicTable extends LitElement {
- ` + `; } static styles = css` @@ -176,11 +188,11 @@ export class MyBasicTable extends LitElement { .sticky-container { overflow: auto; } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'basic-tanstack-table': MyBasicTable + 'basic-tanstack-table': MyBasicTable; } } diff --git a/web-components/sticky-columns/src/customTypings.d.ts b/web-components/sticky-columns/src/customTypings.d.ts index 0a967fb..a92c16f 100644 --- a/web-components/sticky-columns/src/customTypings.d.ts +++ b/web-components/sticky-columns/src/customTypings.d.ts @@ -1,3 +1,2 @@ - -declare module "@carbon/web-components/es/icons/trash-can/16" -declare module "@carbon/web-components/es/icons/edit/16" \ No newline at end of file +declare module '@carbon/web-components/es/icons/trash-can/16'; +declare module '@carbon/web-components/es/icons/edit/16'; diff --git a/web-components/sticky-columns/src/makeData.ts b/web-components/sticky-columns/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/sticky-columns/src/makeData.ts +++ b/web-components/sticky-columns/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/virtual/src/makeData.ts b/web-components/virtual/src/makeData.ts index 2751502..f7fa331 100644 --- a/web-components/virtual/src/makeData.ts +++ b/web-components/virtual/src/makeData.ts @@ -1,22 +1,22 @@ -import { faker } from '@faker-js/faker' +import { faker } from '@faker-js/faker'; export type Resource = { - id: string - name: string - rule: string - status: string - other: string - example: string - subRows?: Resource[] -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; + subRows?: Resource[]; +}; const range = (len: number) => { - const arr: number[] = [] + const arr: number[] = []; for (let i = 0; i < len; i++) { - arr.push(i) + arr.push(i); } - return arr -} + return arr; +}; const newResource = (id: string, index: number): Resource => { return { @@ -24,7 +24,7 @@ const newResource = (id: string, index: number): Resource => { name: `Load balancer ${index}`, rule: faker.helpers.shuffle([ 'DNS delegation', - 'Round Robin' + 'Round Robin', ])[0], status: faker.helpers.shuffle([ 'starting', @@ -33,44 +33,40 @@ const newResource = (id: string, index: number): Resource => { ])[0]!, other: 'Test', example: faker.number.int(1000).toString(), - } -} + }; +}; export function makeData(...lens: number[]) { const makeDataLevel = (depth = 0): Resource[] => { - const len = lens[depth]! + 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() + return makeDataLevel(); } //simulates a backend api -const data = makeData(1000) -export const fetchData = async ( - start: number, - size: number, -) => { - +const data = makeData(1000); +export const fetchData = async (start: number, size: number) => { //simulate a backend api - await new Promise(resolve => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); return { data: data.slice(start, start + size), meta: { totalRowCount: data.length, }, - } -} + }; +}; export type ResourceApiResponse = { - data: Resource[] + data: Resource[]; meta: { - totalRowCount: number - } -} \ No newline at end of file + totalRowCount: number; + }; +}; diff --git a/web-components/virtual/src/virtual.ts b/web-components/virtual/src/virtual.ts index 1d39263..540b7b9 100644 --- a/web-components/virtual/src/virtual.ts +++ b/web-components/virtual/src/virtual.ts @@ -1,6 +1,6 @@ -import { LitElement, css, html } from 'lit' -import { customElement } from 'lit/decorators.js' -import { repeat } from 'lit/directives/repeat.js' +import { LitElement, css, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import { createColumnHelper, flexRender, @@ -8,35 +8,34 @@ import { getSortedRowModel, Row, TableController, -} from '@tanstack/lit-table' +} from '@tanstack/lit-table'; import '@carbon/web-components/es/components/data-table/index.js'; -import { VirtualizerController } from '@tanstack/lit-virtual' -import { createRef, ref, Ref } from 'lit/directives/ref.js' -import { styleMap } from 'lit/directives/style-map.js' +import { VirtualizerController } from '@tanstack/lit-virtual'; +import { createRef, ref, Ref } from 'lit/directives/ref.js'; +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 -} + id: string; + name: string; + rule: string; + status: string; + other: string; + example: string; +}; -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor(row => row.name, { + columnHelper.accessor((row) => row.name, { id: 'lastName', - cell: info => html`${info.getValue()}`, + cell: (info) => html`${info.getValue()}`, header: () => html`Name`, }), columnHelper.accessor('rule', { header: () => 'Rule', - cell: info => info.renderValue(), + cell: (info) => info.renderValue(), }), columnHelper.accessor('status', { header: () => html`Status`, @@ -47,7 +46,7 @@ const columns = [ columnHelper.accessor('example', { header: 'Example', }), -] +]; const data: Resource[] = makeData(1000); @@ -60,9 +59,9 @@ const data: Resource[] = makeData(1000); export class VirtualTable extends LitElement { private tableController = new TableController(this); - private tableContainerRef: Ref = createRef() + private tableContainerRef: Ref = createRef(); - private rowVirtualizerController: VirtualizerController + private rowVirtualizerController: VirtualizerController; connectedCallback() { this.rowVirtualizerController = new VirtualizerController(this, { @@ -70,8 +69,8 @@ export class VirtualTable extends LitElement { getScrollElement: () => this.tableContainerRef.value!, estimateSize: () => 48, overscan: 5, - }) - super.connectedCallback() + }); + super.connectedCallback(); } render() { @@ -80,10 +79,10 @@ export class VirtualTable extends LitElement { data, getSortedRowModel: getSortedRowModel(), getCoreRowModel: getCoreRowModel(), - }) - const { rows } = table.getRowModel() + }); + const { rows } = table.getRowModel(); - const virtualizer = this.rowVirtualizerController.getVirtualizer() + const virtualizer = this.rowVirtualizerController.getVirtualizer(); return html`
+ })}"> + })}"> ${repeat( table.getHeaderGroups(), - headerGroup => headerGroup.id, - headerGroup => - html` - ${repeat( - headerGroup.headers, - header => header.id, - header => - html` + ${repeat( + headerGroup.headers, + (header) => header.id, + (header) => + html` + ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + ` + )}` + )} + + + ${repeat( + this.rowVirtualizerController.getVirtualizer().getVirtualItems(), + (item) => item.key, + (item) => { + const row = rows[item.index] as Row; + return html` + + this.rowVirtualizerController + .getVirtualizer() + .measureElement(node) + )}> + ${repeat( + row.getVisibleCells(), + (cell) => cell.id, + (cell) => html` + - ${header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() + })}> + ${flexRender( + cell.column.columnDef.cell, + cell.getContext() )} - ` - )}` + + ` + )} + + `; + } )} - - - ${repeat( - this.rowVirtualizerController - .getVirtualizer() - .getVirtualItems(), - item => item.key, - item => { - const row = rows[item.index] as Row - return html` - - this.rowVirtualizerController - .getVirtualizer() - .measureElement(node) - )} - > - ${repeat( - row.getVisibleCells(), - cell => cell.id, - cell => html` - - ${flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ` - )} - - ` - } - )}
- ` + `; } static styles = css` @@ -190,11 +187,11 @@ export class VirtualTable extends LitElement { display: flex; place-items: center; } - ` + `; } declare global { interface HTMLElementTagNameMap { - 'virtual-tanstack-table': VirtualTable + 'virtual-tanstack-table': VirtualTable; } } diff --git a/yarn.lock b/yarn.lock index 10baa3f..f489dbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3653,6 +3653,16 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-config-carbon@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/prettier-config-carbon/-/prettier-config-carbon-0.11.0.tgz#4b5f4d83dcaedf97277fba5f6f4e9d7510f7e3a7" + integrity sha512-TEnwTd3lSARCENhlzy7zfqxDAq9mNCpqv7OssAHqnM/BUBNcgGSXR1mfO/gYtIl1NbVyl68ZqyDGILri12MKJg== + +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"