feat(table): initial structure created

This commit is contained in:
Junior Garcia 2023-04-20 23:35:52 -03:00
parent fd2e75b89d
commit 4309dbf161
14 changed files with 587 additions and 1 deletions

View File

@ -0,0 +1,24 @@
# @nextui-org/table
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/table
# or
npm i @nextui-org/table
```
## Contribution
Yes please! See the
[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md)
for details.
## Licence
This project is licensed under the terms of the
[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE).

View File

@ -0,0 +1,19 @@
import * as React from "react";
import {render} from "@testing-library/react";
import {Table} from "../src";
describe("Table", () => {
it("should render correctly", () => {
const wrapper = render(<Table />);
expect(() => wrapper.unmount()).not.toThrow();
});
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLDivElement>();
render(<Table ref={ref} />);
expect(ref.current).not.toBeNull();
});
});

View File

@ -0,0 +1,66 @@
{
"name": "@nextui-org/table",
"version": "2.0.0-beta.1",
"description": "Tables are used to display tabular data using rows and columns. ",
"keywords": [
"table"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/components/table"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src --dts",
"build:fast": "tsup src",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"peerDependencies": {
"react": ">=18"
},
"dependencies": {
"@nextui-org/checkbox": "workspace:*",
"@nextui-org/dom-utils": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/system": "workspace:*",
"@nextui-org/theme": "workspace:*",
"@react-aria/focus": "^3.12.0",
"@react-aria/table": "^3.9.0",
"@react-aria/utils": "^3.16.0",
"@react-aria/visually-hidden": "^3.8.0",
"@react-stately/table": "^3.9.0"
},
"devDependencies": {
"@react-types/grid": "^3.1.7",
"@react-types/table": "^3.6.0",
"clean-package": "2.2.0",
"react": "^18.0.0"
},
"clean-package": "../../../clean-package.config.json",
"tsup": {
"clean": true,
"target": "es2019",
"format": [
"cjs",
"esm"
]
}
}

View File

@ -0,0 +1,10 @@
import Table from "./table";
// export types
export type {TableProps} from "./table";
// export hooks
export {useTable} from "./use-table";
// export component
export {Table};

View File

@ -0,0 +1,10 @@
import {createContext} from "@nextui-org/shared-utils";
import {ContextType} from "./use-table";
export const [TableProvider, useTableContext] = createContext<ContextType>({
name: "TableContext",
strict: true,
errorMessage:
"useTableContext: `context` is undefined. Seems you forgot to wrap component within <Table />",
});

View File

@ -0,0 +1,22 @@
import {forwardRef} from "@nextui-org/system";
import {TableProvider} from "./table-context";
import {UseTableProps, useTable} from "./use-table";
export interface TableProps extends Omit<UseTableProps, "ref"> {}
const Table = forwardRef<TableProps, "div">((props, ref) => {
const {Component, children, context, getBaseProps, getTableProps} = useTable({ref, ...props});
return (
<TableProvider value={context}>
<div {...getBaseProps()}>
<Component {...getTableProps()}>{children}</Component>
</div>
</TableProvider>
);
});
Table.displayName = "NextUI.Table";
export default Table;

View File

@ -0,0 +1,161 @@
import type {
TableVariantProps,
SlotsToClasses,
TableReturnType,
TableSlots,
} from "@nextui-org/theme";
import type {SelectionBehavior, DisabledBehavior} from "@react-types/shared";
import type {ReactNode, Key} from "react";
import type {TableState, TableStateProps} from "@react-stately/table";
import type {TableCollection} from "@react-types/table";
import {useTableState} from "@react-stately/table";
import {useTable as useReactAriaTable} from "@react-aria/table";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {table} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/dom-utils";
import {mergeProps} from "@react-aria/utils";
import {clsx, ReactRef} from "@nextui-org/shared-utils";
import {useMemo} from "react";
interface Props extends HTMLNextUIProps<"table"> {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLElement | null>;
/** The elements that make up the table. Includes the TableHeader, TableBody, Columns, and Rows. */
children?: ReactNode;
/**
* Classname or List of classes to change the classNames of the element.
* if `className` is passed, it will be added to the base slot.
*
* @example
* ```ts
* <Table classNames={{
* base:"base-classes", // table wrapper
* table: "table-classes",
* thead: "thead-classes",
* tbody: "tbody-classes",
* tr: "tr-classes",
* th: "th-classes",
* td: "td-classes",
* tfoot: "tfoot-classes",
* }} />
* ```
*/
classNames?: SlotsToClasses<TableSlots>;
/**
* How multiple selection should behave in the collection.
* @default "toggle"
*/
selectionBehavior?: SelectionBehavior;
/**
* Whether `disabledKeys` applies to all interactions, or only selection.
* @default "selection"
*/
disabledBehavior?: DisabledBehavior;
/** Handler that is called when a user performs an action on the row. */
onRowAction?: (key: Key) => void;
/** Handler that is called when a user performs an action on the cell. */
onCellAction?: (key: Key) => void;
}
export type UseTableProps<T = object> = Props & TableStateProps<T> & TableVariantProps;
export type ContextType<T = object> = {
state: TableState<T>;
slots: TableReturnType;
collection: TableCollection<T>;
classNames?: SlotsToClasses<TableSlots>;
selectionMode: UseTableProps["selectionMode"];
selectionBehavior: UseTableProps["selectionBehavior"];
disabledBehavior: UseTableProps["disabledBehavior"];
showSelectionCheckboxes: UseTableProps["showSelectionCheckboxes"];
onRowAction?: UseTableProps["onRowAction"];
onCellAction?: UseTableProps["onCellAction"];
};
export function useTable<T extends object>(originalProps: UseTableProps<T>) {
const [props, variantProps] = mapPropsVariants(originalProps, table.variantKeys);
const {
ref,
as,
children,
className,
selectionMode = "none",
selectionBehavior = "replace",
disabledBehavior = "selection",
showSelectionCheckboxes = selectionMode === "multiple" && selectionBehavior !== "replace",
onRowAction,
onCellAction,
classNames,
...otherProps
} = props;
const Component = as || "table";
const domRef = useDOMRef(ref);
const state = useTableState({
...originalProps,
children,
showSelectionCheckboxes,
});
const {collection} = state;
const {gridProps} = useReactAriaTable(originalProps, state, domRef);
const slots = useMemo(
() =>
table({
...variantProps,
}),
[...Object.values(variantProps)],
);
const tableStyles = clsx(classNames?.table, className);
const context = useMemo<ContextType<T>>(
() => ({
state,
slots,
collection,
classNames,
selectionMode,
selectionBehavior,
disabledBehavior,
showSelectionCheckboxes,
onRowAction,
onCellAction,
}),
[
slots,
state,
collection,
classNames,
selectionMode,
selectionBehavior,
disabledBehavior,
showSelectionCheckboxes,
onRowAction,
onCellAction,
],
);
const getBaseProps: PropGetter = (props) => ({
...props,
className: slots.base({class: props?.className}),
});
const getTableProps: PropGetter = (props) => ({
...mergeProps(gridProps, otherProps, props),
ref: domRef,
className: slots.base({class: clsx(tableStyles, props?.className)}),
});
return {Component, children, context, getBaseProps, getTableProps};
}
export type UseTableReturn = ReturnType<typeof useTable>;

View File

@ -0,0 +1,46 @@
import React from "react";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {table} from "@nextui-org/theme";
import {Table, TableProps} from "../src";
export default {
title: "Components/Table",
component: Table,
argTypes: {
color: {
control: {
type: "select",
options: ["neutral", "primary", "secondary", "success", "warning", "danger"],
},
},
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
},
},
size: {
control: {
type: "select",
options: ["xs", "sm", "md", "lg", "xl"],
},
},
isDisabled: {
control: {
type: "boolean",
},
},
},
} as ComponentMeta<typeof Table>;
const defaultProps = {
...table.defaultVariants,
};
const Template: ComponentStory<typeof Table> = (args: TableProps) => <Table {...args} />;
export const Default = Template.bind({});
Default.args = {
...defaultProps,
};

View File

@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"tailwind-variants": ["../../../node_modules/tailwind-variants"]
}
},
"include": ["src", "index.ts"]
}

View File

@ -30,3 +30,4 @@ export * from "./dropdown-menu";
export * from "./image";
export * from "./modal";
export * from "./navbar";
export * from "./table";

View File

@ -3,7 +3,7 @@ import type {VariantProps} from "tailwind-variants";
import {tv} from "tailwind-variants";
/**
* Card **Tailwind Variants** component
* Modal **Tailwind Variants** component
*
* @example
* ```js

View File

@ -0,0 +1,59 @@
import type {VariantProps} from "tailwind-variants";
import {tv} from "tailwind-variants";
/**
* Table **Tailwind Variants** component
*
* @example
* ```js
* const {base, table, thead, tbody, tr, th, td, tfoot} = table({...})
*
* <div className={base()}>
* <table className={table()}>
* <thead className={thead()}>
* <tr className={tr()}>
* <th className={th()}>...</th>
* <th className={th()}>...</th>
* </tr>
* </thead>
* <tbody className={tbody()}>
* <tr className={tr()}>
* <td className={td()}>...</td>
* <td className={td()}>...</td>
* </tr>
* <tr className={tr()}>
* <td className={td()}>...</td>
* <td className={td()}>...</td>
* </tr>
* </tbody>
* <tfoot className={tfoot()}>
* <tr className={tr()}>
* <td className={td()}>...</td>
* <td className={td()}>...</td>
* </tr>
* </tfoot>
* </table>
* </div>
* ```
*/
const table = tv({
slots: {
base: "",
table: "group",
thead: "",
tbody: "",
tr: "",
th: "",
td: "",
tfoot: "",
},
variants: {},
defaultVariants: {},
});
export type TableVariantProps = VariantProps<typeof table>;
export type TableSlots = keyof ReturnType<typeof table>;
export type TableReturnType = ReturnType<typeof table>;
export {table};

158
pnpm-lock.yaml generated
View File

@ -1451,6 +1451,52 @@ importers:
specifier: ^18.2.0
version: 18.2.0
packages/components/table:
dependencies:
'@nextui-org/checkbox':
specifier: workspace:*
version: link:../checkbox
'@nextui-org/dom-utils':
specifier: workspace:*
version: link:../../utilities/dom-utils
'@nextui-org/shared-utils':
specifier: workspace:*
version: link:../../utilities/shared-utils
'@nextui-org/system':
specifier: workspace:*
version: link:../../core/system
'@nextui-org/theme':
specifier: workspace:*
version: link:../../core/theme
'@react-aria/focus':
specifier: ^3.12.0
version: 3.12.0(react@18.2.0)
'@react-aria/table':
specifier: ^3.9.0
version: 3.9.0(react-dom@18.2.0)(react@18.2.0)
'@react-aria/utils':
specifier: ^3.16.0
version: 3.16.0(react@18.2.0)
'@react-aria/visually-hidden':
specifier: ^3.8.0
version: 3.8.0(react@18.2.0)
'@react-stately/table':
specifier: ^3.9.0
version: 3.9.0(react@18.2.0)
devDependencies:
'@react-types/grid':
specifier: ^3.1.7
version: 3.1.7(react@18.2.0)
'@react-types/table':
specifier: ^3.6.0
version: 3.6.0(react@18.2.0)
clean-package:
specifier: 2.2.0
version: 2.2.0
react:
specifier: ^18.2.0
version: 18.2.0
packages/components/tooltip:
dependencies:
'@nextui-org/aria-utils':
@ -6265,6 +6311,30 @@ packages:
react: 18.2.0
dev: false
/@react-aria/grid@3.7.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-jXo+/wQotHDSaMSVdVT7Hxzz65Nj2yK1wssIUQPEZalRhcosGWI1vhdQOD0g9GQL1l5DLyw0m55sych6naeBlw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-aria/focus': 3.12.0(react@18.2.0)
'@react-aria/i18n': 3.7.1(react@18.2.0)
'@react-aria/interactions': 3.15.0(react@18.2.0)
'@react-aria/live-announcer': 3.3.0
'@react-aria/selection': 3.14.0(react@18.2.0)
'@react-aria/utils': 3.16.0(react@18.2.0)
'@react-stately/collections': 3.7.0(react@18.2.0)
'@react-stately/grid': 3.6.0(react@18.2.0)
'@react-stately/selection': 3.13.0(react@18.2.0)
'@react-stately/virtualizer': 3.5.1(react@18.2.0)
'@react-types/checkbox': 3.4.3(react@18.2.0)
'@react-types/grid': 3.1.7(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-aria/i18n@3.7.1(react@18.2.0):
resolution: {integrity: sha512-2fu1cv8yD3V+rlhOqstTdGAubadoMFuPE7lA1FfYdaJNxXa09iWqvpipUPlxYJrahW0eazkesOPDKFwOEMF1iA==}
peerDependencies:
@ -6318,6 +6388,12 @@ packages:
react: 18.2.0
dev: false
/@react-aria/live-announcer@3.3.0:
resolution: {integrity: sha512-6diTS6mIf70KdxfGqiDxHV+9Qv8a9A88EqBllzXGF6HWPdcwde/GIEmfpTwj8g1ImNGZYUwDkv4Hd9lFj0MXEg==}
dependencies:
'@swc/helpers': 0.4.14
dev: false
/@react-aria/menu@3.9.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-lIbfWzFvYE7EPOno3lVogXHlc6fzswymlpJWiMBKaB68wkfCtknIIL1cwWssiwgGU63v08H5YpQOZdxRwux2PQ==}
peerDependencies:
@ -6428,6 +6504,32 @@ packages:
react: 18.2.0
dev: false
/@react-aria/table@3.9.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-hY1tM7NRjP+gRvm2OGgWeEZ8An0tzljj0O19JCg7oi6IpypFJqeSqSUQml1OIv5wbZ04pQnoYGtMkP7h7YqkPw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-aria/focus': 3.12.0(react@18.2.0)
'@react-aria/grid': 3.7.0(react-dom@18.2.0)(react@18.2.0)
'@react-aria/i18n': 3.7.1(react@18.2.0)
'@react-aria/interactions': 3.15.0(react@18.2.0)
'@react-aria/live-announcer': 3.3.0
'@react-aria/selection': 3.14.0(react@18.2.0)
'@react-aria/utils': 3.16.0(react@18.2.0)
'@react-aria/visually-hidden': 3.8.0(react@18.2.0)
'@react-stately/collections': 3.7.0(react@18.2.0)
'@react-stately/table': 3.9.0(react@18.2.0)
'@react-stately/virtualizer': 3.5.1(react@18.2.0)
'@react-types/checkbox': 3.4.3(react@18.2.0)
'@react-types/grid': 3.1.7(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
'@react-types/table': 3.6.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-aria/toggle@3.6.0(react@18.2.0):
resolution: {integrity: sha512-W6xncx5zzqCaPU2XsgjWnACHL3WBpxphYLvF5XlICRg0nZVjGPIWPDDUGyDoPsSUeGMW2vxtFY6erKXtcy4Kgw==}
peerDependencies:
@ -6554,6 +6656,19 @@ packages:
react: 18.2.0
dev: false
/@react-stately/grid@3.6.0(react@18.2.0):
resolution: {integrity: sha512-Sq/ivfq9Kskghoe6rYh2PfhB9/jBGfoj8wUZ4bqHcalTrBjfUvkcWMSFosibYPNZFDkA7r00bbJPDJVf1VLhuw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-stately/collections': 3.7.0(react@18.2.0)
'@react-stately/selection': 3.13.0(react@18.2.0)
'@react-types/grid': 3.1.7(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
dev: false
/@react-stately/menu@3.5.1(react@18.2.0):
resolution: {integrity: sha512-nnuZlDBFIc3gB34kofbKDStFg9r8rijY+7ez2VWQmss72I9D7+JTn7OXJxV0oQt2lBYmNfS5W6bC9uXk3Z4dLg==}
peerDependencies:
@ -6602,6 +6717,21 @@ packages:
react: 18.2.0
dev: false
/@react-stately/table@3.9.0(react@18.2.0):
resolution: {integrity: sha512-Cl0jmC5eCEhWBAhCjhGklsgYluziNZHF34lHnc99T/DPP+OxwrgwS9rJKTW7L6UOvHU/ADKjEwkE/fZuqVBohg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-stately/collections': 3.7.0(react@18.2.0)
'@react-stately/grid': 3.6.0(react@18.2.0)
'@react-stately/selection': 3.13.0(react@18.2.0)
'@react-types/grid': 3.1.7(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
'@react-types/table': 3.6.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
dev: false
/@react-stately/toggle@3.5.1(react@18.2.0):
resolution: {integrity: sha512-PF4ZaATpXWu7DkneGSZ2/PA6LJ1MrhKNiaENTZlbojXMRr5kK33wPzaDW7I8O25IUm0+rvQicv7A6QkEOxgOPg==}
peerDependencies:
@ -6647,6 +6777,17 @@ packages:
'@swc/helpers': 0.4.14
react: 18.2.0
/@react-stately/virtualizer@3.5.1(react@18.2.0):
resolution: {integrity: sha512-TVszEl8+os5eAwoETAJ0ndz5cnYFQs52OIcWonKRYbNp5KvWAV+OA2HuIrB3SSC29ZRB2bDqpj4S2LY4wWJPCw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-aria/utils': 3.16.0(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
'@swc/helpers': 0.4.14
react: 18.2.0
dev: false
/@react-types/accordion@3.0.0-alpha.13(react@18.2.0):
resolution: {integrity: sha512-RQP+UInEdPjyyX8IbmUbPXgx6NJaKYpNrMDa8yxqDKLH0MIfsKQldu/lBHuRP3OIrVTr18r/ICjdOZjtVObtLw==}
peerDependencies:
@ -6681,6 +6822,14 @@ packages:
react: 18.2.0
dev: false
/@react-types/grid@3.1.7(react@18.2.0):
resolution: {integrity: sha512-YKo/AbJrgWErPmr5y0K4o6Ts9ModFv5+2FVujecIydu3zLuHsVcx//6uVeHSy2W+uTV9vU/dpMP+GGgg+vWQhw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-types/shared': 3.18.0(react@18.2.0)
react: 18.2.0
/@react-types/label@3.7.3(react@18.2.0):
resolution: {integrity: sha512-TKuQ2REPl4UVq/wl3CAujzixeNVVso0Kob+0T1nP8jIt9k9ssdLMAgSh8Z4zNNfR+oBIngYOA9IToMnbx6qACA==}
peerDependencies:
@ -6759,6 +6908,15 @@ packages:
react: 18.2.0
dev: false
/@react-types/table@3.6.0(react@18.2.0):
resolution: {integrity: sha512-jUp8yTWJuJlqpJY+EIEppgjFsZ3oj4y9zg1oUO+l1rqRWEqmAdoq42g3dTZHmnz9hQJkUeo34I1HGaB9kxNqvg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-types/grid': 3.1.7(react@18.2.0)
'@react-types/shared': 3.18.0(react@18.2.0)
react: 18.2.0
/@react-types/textfield@3.7.1(react@18.2.0):
resolution: {integrity: sha512-6V5+6/VgDbmgN61pyVct1VrXb2hqq7Y43BFQ+/ZhFDlVaMpC5xKWKgW/gPbGLLc27gax8t2Brt7VHJj+d+yrUw==}
peerDependencies: