diff --git a/package-lock.json b/package-lock.json index 40d0887..ad52b11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/react-fontawesome": "^0.1.16", + "@heroicons/react": "^1.0.5", "@reduxjs/toolkit": "^1.7.1", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", @@ -2148,6 +2149,14 @@ "react": ">=16.x" } }, + "node_modules/@heroicons/react": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz", + "integrity": "sha512-UDMyLM2KavIu2vlWfMspapw9yii7aoLwzI2Hudx4fyoPwfKfxU8r3cL8dEBXOjcLG0/oOONZzbT14M1HoNtEcg==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", @@ -18327,6 +18336,12 @@ "prop-types": "^15.7.2" } }, + "@heroicons/react": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz", + "integrity": "sha512-UDMyLM2KavIu2vlWfMspapw9yii7aoLwzI2Hudx4fyoPwfKfxU8r3cL8dEBXOjcLG0/oOONZzbT14M1HoNtEcg==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", diff --git a/package.json b/package.json index f7feb74..a2057b3 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/react-fontawesome": "^0.1.16", + "@heroicons/react": "^1.0.5", "@reduxjs/toolkit": "^1.7.1", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", diff --git a/src/App.js b/src/App.js index c8a3bb9..1c0238e 100644 --- a/src/App.js +++ b/src/App.js @@ -43,6 +43,8 @@ import SpecifiedDomainRadarChart from "./components/Chart/SpecifiedDomainRadarCh import Design1 from "./components/ProductSalesAmount/Design1"; import Design2 from "./components/ProductSalesAmount/Design2"; import TableDesign1 from "./components/Table/Design1"; +import ReactTable from "./components/ReactTable"; +import ReactTable2 from "./components/ReactTable2"; function App() { return ( @@ -86,6 +88,8 @@ function App() { {/* */} {/* */} {/* */} + {/* */} + ); diff --git a/src/components/ReactTable/Table.js b/src/components/ReactTable/Table.js new file mode 100644 index 0000000..0ddba7e --- /dev/null +++ b/src/components/ReactTable/Table.js @@ -0,0 +1,294 @@ +import React from "react"; + +import { + useTable, + useGlobalFilter, + useAsyncDebounce, + useSortBy, + usePagination, +} from "react-table"; // new + +import { + ChevronDoubleLeftIcon, + ChevronLeftIcon, + ChevronRightIcon, + ChevronDoubleRightIcon, +} from "@heroicons/react/solid"; + +import { Button, PageButton } from "./shared/Button"; +import { classNames } from "./shared/Utils"; +import { SortIcon, SortUpIcon, SortDownIcon } from "./shared/Icons"; + +function GlobalFilter({ + preGlobalFilteredRows, + globalFilter, + setGlobalFilter, +}) { + const count = preGlobalFilteredRows.length; + const [value, setValue] = React.useState(globalFilter); + const onChange = useAsyncDebounce((value) => { + setGlobalFilter(value || undefined); + }, 200); + + return ( + + Search:{" "} + { + setValue(e.target.value); + onChange(e.target.value); + }} + placeholder={`${count} records...`} + /> + + ); +} + +// This is a custom filter UI for selecting +export function StatusPill({ value }) { + // const status = value ? value.toLowerCase() : "unknown"; + + return ( + // + // {/* {status} */} + // {status ? "فعال" : "غیر فعال"} + // + + {value ? "فعال" : "غیر فعال"} + + ); + console.log(value); +} + +function Table({ columns, data }) { + // Use the state and functions returned from useTable to build your UI + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + //new + page, // Instead of using 'rows', we'll use page, + // which has only the rows for the active page + + // The rest of these things are super handy, too ;) + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + setPageSize, + + state, // new + preGlobalFilteredRows, // new + setGlobalFilter, + } = useTable( + { + columns, + data, + }, + useGlobalFilter, + useSortBy, // new + usePagination + ); + + // Render the UI for your table + return ( + <> +
+ + {headerGroups.map((headerGroup) => + headerGroup.headers.map((column) => + column.Filter ? ( +
+ {column.render("Filter")} +
+ ) : null + ) + )} +
+ {/* table */} +
+
+
+
+ + + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + // Add the sorting props to control sorting. For this example + // we can add them into the header props + + ))} + + ))} + + + {page.map((row, i) => { + // new + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + ); + })} + + ); + })} + +
+
+ {column.render("Header")} + {/* Add a sort direction indicator */} + + {column.isSorted ? ( + column.isSortedDesc ? ( + + ) : ( + + ) + ) : ( + + )} + +
+
+ {cell.column.Cell.name === "defaultRenderer" ? ( +
+ {cell.render("Cell")} +
+ ) : ( + cell.render("Cell") + )} +
+
+
+
+
+ {/* Pagination */} +
+
+ + +
+
+ {/* pagintaion */} +
+ + صفحه {state.pageIndex + 1} از{" "} + {pageOptions.length} + + +
+ {/* arrows */} +
+ +
+
+
+ + ); +} + +export default Table; diff --git a/src/components/ReactTable/index.js b/src/components/ReactTable/index.js new file mode 100644 index 0000000..4573be4 --- /dev/null +++ b/src/components/ReactTable/index.js @@ -0,0 +1,58 @@ +import React from "react"; +import Table, { AvatarCell, SelectColumnFilter, StatusPill } from "./Table"; // new + +const getData = () => { + const data = [ + { + name: "مایکروویر سامسونگ مدل ", + status: true, + date: "1400 / 09 / 21", + price: 20000, + }, + { + name: "مایکروویر سامسونگ مدل ", + status: false, + date: "1400 / 09 / 21", + price: 20000, + }, + ]; + return [...data, ...data, ...data]; +}; + +function ReactTable() { + const columns = React.useMemo( + () => [ + { + Header: "عنوان", + accessor: "name", + }, + { + Header: "وضعیت", + accessor: "status", + }, + { + Header: "تاریخ", + accessor: "date", + }, + { + Header: "مبلغ پرداختی", + accessor: "price", + }, + ], + [] + ); + + const data = React.useMemo(() => getData(), []); + + return ( +
+
+
+ + + + + ); +} + +export default ReactTable; diff --git a/src/components/ReactTable/shared/Button.js b/src/components/ReactTable/shared/Button.js new file mode 100644 index 0000000..0eb5e88 --- /dev/null +++ b/src/components/ReactTable/shared/Button.js @@ -0,0 +1,35 @@ +import React from 'react' +import { classNames } from './Utils' + +export function Button({ children, className, ...rest }) { + return ( + + ) +} + +export function PageButton({ children, className, ...rest }) { + return ( + + ) +} + diff --git a/src/components/ReactTable/shared/Icons.js b/src/components/ReactTable/shared/Icons.js new file mode 100644 index 0000000..8751e5e --- /dev/null +++ b/src/components/ReactTable/shared/Icons.js @@ -0,0 +1,17 @@ +export function SortIcon({ className }) { + return ( + + ) +} + +export function SortUpIcon({ className }) { + return ( + + ) +} + +export function SortDownIcon({ className }) { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/ReactTable/shared/Utils.js b/src/components/ReactTable/shared/Utils.js new file mode 100644 index 0000000..91dcb3e --- /dev/null +++ b/src/components/ReactTable/shared/Utils.js @@ -0,0 +1,3 @@ +export function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} \ No newline at end of file diff --git a/src/components/ReactTable2/Table.js b/src/components/ReactTable2/Table.js new file mode 100644 index 0000000..844d37e --- /dev/null +++ b/src/components/ReactTable2/Table.js @@ -0,0 +1,305 @@ +import React from "react"; +import { + useTable, + useFilters, + useGlobalFilter, + useAsyncDebounce, + useSortBy, + usePagination, +} from "react-table"; +import { + ChevronDoubleLeftIcon, + ChevronLeftIcon, + ChevronRightIcon, + ChevronDoubleRightIcon, +} from "@heroicons/react/solid"; +import { Button, PageButton } from "./shared/Button"; +import { classNames } from "./shared/Utils"; +import { SortIcon, SortUpIcon, SortDownIcon } from "./shared/Icons"; + +// Define a default UI for filtering +function GlobalFilter({ + preGlobalFilteredRows, + globalFilter, + setGlobalFilter, +}) { + const count = preGlobalFilteredRows.length; + const [value, setValue] = React.useState(globalFilter); + const onChange = useAsyncDebounce((value) => { + setGlobalFilter(value || undefined); + }, 200); + + return ( + + ); +} + +export function StatusPill({ value }) { + // const status = value ? value.toLowerCase() : "unknown"; + + return ( + + {value ? "فعال" : "غیر فعال"} + + ); +} + +// export function AvatarCell({ value, column, row }) { +// return ( +//
+// {/* برای عکس */} +// {/*
+// +//
*/} +// {/* اگر بخواهیم در کنار عنوان یک محتوا دیگه ای بنویسیم */} +// {/*
+//
{value}
+//
+// {row.original[column.emailAccessor]} +//
+//
*/} +//
+// ); +// } + +function Table({ columns, data }) { + // Use the state and functions returned from useTable to build your UI + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, // Instead of using 'rows', we'll use page, + // which has only the rows for the active page + + // The rest of these things are super handy, too ;) + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + setPageSize, + + state, + preGlobalFilteredRows, + setGlobalFilter, + } = useTable( + { + columns, + data, + }, + useFilters, // useFilters! + useGlobalFilter, + useSortBy, + usePagination // new + ); + + // Render the UI for your table + return ( + <> +
+ + {headerGroups.map((headerGroup) => + headerGroup.headers.map((column) => + column.Filter ? ( +
+ {column.render("Filter")} +
+ ) : null + ) + )} +
+ {/* table */} +
+
+
+
+
+ + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + // Add the sorting props to control sorting. For this example + // we can add them into the header props + + ))} + + ))} + + + {page.map((row, i) => { + // new + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + ); + })} + + ); + })} + +
+
+ {column.render("Header")} + {/* Add a sort direction indicator */} + + {column.isSorted ? ( + column.isSortedDesc ? ( + + ) : ( + + ) + ) : ( + + )} + +
+
+ {cell.column.Cell.name === "defaultRenderer" ? ( +
+ {cell.render("Cell")} +
+ ) : ( + cell.render("Cell") + )} +
+
+
+ + + {/* Pagination */} +
+
+ + +
+
+ {/* numbers */} +
+ + صفحه {state.pageIndex + 1} از + {pageOptions.length} + + +
+ {/* arrows */} +
+ +
+
+
+ + ); +} + +export default Table; diff --git a/src/components/ReactTable2/index.js b/src/components/ReactTable2/index.js new file mode 100644 index 0000000..657057f --- /dev/null +++ b/src/components/ReactTable2/index.js @@ -0,0 +1,90 @@ +import React from "react"; +import Table, { AvatarCell, SelectColumnFilter, StatusPill } from "./Table"; // new + +const getData = () => { + const data = [ + { + name: "مایکروویر سامسونگ مدل ", + // email: "jane.cooper@example.com", + status: true, + date: "1400/09/21", + price: 20000, + // imgUrl: + // "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60", + }, + { + name: "آزمون آنلاین درس علوم تجربی ", + status: false, + date: "1400/09/21", + price: 18000, + }, + { + name: "مایکروویر سامسونگ مدل ", + status: true, + date: "1400/09/21", + price: 20000, + }, + { + name: "آزمون آنلاین درس علوم تجربی ", + status: false, + date: "1400/09/21", + price: 18000, + }, + { + name: "مایکروویر سامسونگ مدل ", + status: true, + date: "1400/09/21", + price: 27000000, + }, + { + name: "آزمون آنلاین درس علوم تجربی ", + status: false, + date: "1400/09/21", + price: 1000, + }, + ]; + return [...data, ...data, ...data]; +}; + +const ReactTable2 = () => { + const columns = React.useMemo( + () => [ + { + Header: "عنوان", + accessor: "name", + // اگر بخواهیم عکسی در جدول نشان بدیم یا یک محتوایی در کنار عنوان داشته باشیم اول باید اواتار سل رو در فایل تیبل جی اس ان کامنت کنیم و بعد ... + // Cell: AvatarCell, + // imgAccessor: "imgUrl", + // emailAccessor: "email", + }, + { + Header: "وضعیت", + accessor: "status", + Cell: StatusPill, + }, + { + Header: "تاریخ", + accessor: "date", + }, + { + Header: "مبلغ پرداختی", + accessor: "price", + }, + ], + [] + ); + + const data = React.useMemo(() => getData(), []); + + return ( +
+
+
+ + + + + ); +}; + +export default ReactTable2; diff --git a/src/components/ReactTable2/shared/Button.js b/src/components/ReactTable2/shared/Button.js new file mode 100644 index 0000000..0eb5e88 --- /dev/null +++ b/src/components/ReactTable2/shared/Button.js @@ -0,0 +1,35 @@ +import React from 'react' +import { classNames } from './Utils' + +export function Button({ children, className, ...rest }) { + return ( + + ) +} + +export function PageButton({ children, className, ...rest }) { + return ( + + ) +} + diff --git a/src/components/ReactTable2/shared/Icons.js b/src/components/ReactTable2/shared/Icons.js new file mode 100644 index 0000000..8751e5e --- /dev/null +++ b/src/components/ReactTable2/shared/Icons.js @@ -0,0 +1,17 @@ +export function SortIcon({ className }) { + return ( + + ) +} + +export function SortUpIcon({ className }) { + return ( + + ) +} + +export function SortDownIcon({ className }) { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/ReactTable2/shared/Utils.js b/src/components/ReactTable2/shared/Utils.js new file mode 100644 index 0000000..91dcb3e --- /dev/null +++ b/src/components/ReactTable2/shared/Utils.js @@ -0,0 +1,3 @@ +export function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 310739e..b7237fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1258,6 +1258,11 @@ dependencies: "prop-types" "^15.7.2" +"@heroicons/react@^1.0.5": + "integrity" "sha512-UDMyLM2KavIu2vlWfMspapw9yii7aoLwzI2Hudx4fyoPwfKfxU8r3cL8dEBXOjcLG0/oOONZzbT14M1HoNtEcg==" + "resolved" "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz" + "version" "1.0.5" + "@humanwhocodes/config-array@^0.9.2": "integrity" "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==" "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz"