diff --git a/package-lock.json b/package-lock.json index e48f017..230914a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "smartreport", "version": "0.0.0", + "dependencies": { + "react-icons": "^5.5.0" + }, "devDependencies": { "@biomejs/biome": "1.9.4", "@chromatic-com/storybook": "^3.2.6", @@ -7014,6 +7017,15 @@ "react": "^18.3.1" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", diff --git a/package.json b/package.json index 082bf8e..8f31139 100644 --- a/package.json +++ b/package.json @@ -59,5 +59,8 @@ "extends": [ "plugin:storybook/recommended" ] + }, + "dependencies": { + "react-icons": "^5.5.0" } } diff --git a/src/TableContextProvider.tsx b/src/TableContextProvider.tsx new file mode 100644 index 0000000..4c12bee --- /dev/null +++ b/src/TableContextProvider.tsx @@ -0,0 +1,41 @@ +import React, { createContext, useContext } from "react"; +import { MRT_ColumnDef, MRT_RowData, MRT_TableInstance, useMaterialReactTable } from "material-react-table"; +import { MaterialReactTableProps } from "material-react-table"; + +// Create context with a type that can be cast to any table instance +export const SmartMRTContext = createContext(null); + +export const useTableContext = (): MRT_TableInstance => { + const context = useContext(SmartMRTContext); + if (!context) { + throw new Error('useTableContext must be used within a TableContextProvider'); + } + return context as MRT_TableInstance; +} + +export const TableContextProvider = ({ + children, + tableProps, + table +}: { + children: React.ReactNode, + tableProps: Omit, 'columns' | 'data'> & { + columns: MRT_ColumnDef[]; + data: T[]; + }, + table?: MRT_TableInstance +}) => { + const { columns, data, ...tablePropsWithoutColumnsAndData } = tableProps; + const tableInstance = table || useMaterialReactTable({ + columns, + data, + globalFilterModeOptions: ['fuzzy', 'startsWith'], + ...tablePropsWithoutColumnsAndData + }); + + return ( + + {children} + + ); +} diff --git a/src/components/smart_report/smart_report.tsx b/src/components/smart_report/smart_report.tsx index 932ed1a..6816d0e 100644 --- a/src/components/smart_report/smart_report.tsx +++ b/src/components/smart_report/smart_report.tsx @@ -1,22 +1,22 @@ -import { MaterialReactTableProps, MRT_RowData } from "material-react-table"; -import { SmartReportMRT } from "../smart_report_mrt"; - +import { MaterialReactTableProps, MRT_RowData, MRT_ColumnDef, MRT_TableInstance } from "material-react-table"; +import { TableContextProvider } from "../../TableContextProvider"; +import { SmartReportMRT } from "../smart_report_mrt/smart_report_mrt"; type SmartReportProps = { - children: React.ReactNode; - tableProps?: never; -} | { - children?: never; - tableProps: MaterialReactTableProps; + tableProps: Omit, 'columns' | 'data'> & { + columns: MRT_ColumnDef[]; + data: T[]; + }; + table?: MRT_TableInstance; } -export const SmartReport = ({children, tableProps}:SmartReportProps) => { - if (children) { - return <>{children}; - } - - if (!tableProps) { - return null; - } +export const SmartReport = ({ + tableProps, + table +}: SmartReportProps) => { - return ; -} \ No newline at end of file + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/components/smart_report_mrt/smart_mrt.css b/src/components/smart_report_mrt/smart_mrt.css new file mode 100644 index 0000000..4d59af7 --- /dev/null +++ b/src/components/smart_report_mrt/smart_mrt.css @@ -0,0 +1,136 @@ +/* --- Responsive Table CSS --- */ + +.responsive-card-table { + width: 100%; + border-collapse: collapse; + } + + /* Default Table Cell Padding (Example) */ + .responsive-card-table th, + .responsive-card-table td { + padding: 8px 12px; /* Adjust to match default MRT/MUI padding */ + text-align: left; + border-bottom: 1px solid #ddd; /* Default row separator */ + } + .responsive-card-table th { + font-weight: bold; + } + /* Remove bottom border from last row */ + .responsive-card-table tr:last-child td { + border-bottom: none; + } + + + /* --- Mobile Card View (Default Responsive) --- */ + /* @media screen and (max-width: 767px) { */ + + /* Hide header by default in mobile */ + .responsive-card-table:not(.force-table-view) thead { + display: none; + } + + /* Apply card styles only when NOT forced into table view */ + .responsive-card-table:not(.force-table-view) { + border: none; + box-shadow: none; + } + + .responsive-card-table:not(.force-table-view) tbody, + .responsive-card-table:not(.force-table-view) tr, + .responsive-card-table:not(.force-table-view) td { + display: block; + width: 100% !important; + box-sizing: border-box; + } + + .responsive-card-table:not(.force-table-view) tr { + margin-bottom: 1.5rem; + border: 1px solid #ddd; + border-radius: 5px; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + background-color: #fff; + } + + .responsive-card-table:not(.force-table-view) tr.MuiTableRow-root { + background-color: transparent; + } + + + .responsive-card-table:not(.force-table-view) td { + text-align: right; + padding-left: 50%; + padding-top: 10px; + padding-bottom: 10px; + position: relative; + border: none; + border-bottom: 1px solid #eee; + min-height: 30px; + display: flex; + align-items: center; + justify-content: flex-end; + } + + .responsive-card-table:not(.force-table-view) tr td:last-child { + border-bottom: none; + } + + .responsive-card-table:not(.force-table-view) td::before { + content: attr(data-label); + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + width: calc(50% - 20px); + padding-right: 10px; + font-weight: bold; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: #555; + } + + /* --- Overrides when .force-table-view is active on mobile --- */ + + /* IMPORTANT: Show header when table view is forced */ + .responsive-card-table.force-table-view thead { + display: table-header-group !important; /* Or initial */ + } + + /* Reset row display */ + .responsive-card-table.force-table-view tr { + display: table-row !important; + /* Reset card styles */ + margin-bottom: 0 !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; + background-color: transparent !important; + overflow: visible !important; + } + + /* Reset cell display and styles */ + .responsive-card-table.force-table-view td { + display: table-cell !important; + width: auto !important; /* Allow table layout */ + /* Reset card cell styles */ + text-align: left !important; /* Default alignment */ + padding: 8px 12px !important; /* Restore original padding */ + position: static !important; + border: none !important; /* Remove card border */ + border-bottom: 1px solid #ddd !important; /* Restore row border */ + min-height: auto !important; + } + + /* Hide the generated labels when table view is forced */ + .responsive-card-table.force-table-view td::before { + display: none !important; + } + + /* Ensure last row border is correctly removed in forced table view */ + .responsive-card-table.force-table-view tr:last-child td { + border-bottom: none !important; + } + + /* } */ \ No newline at end of file diff --git a/src/components/smart_report_mrt/smart_report_mrt.tsx b/src/components/smart_report_mrt/smart_report_mrt.tsx index d62d553..ad0e38c 100644 --- a/src/components/smart_report_mrt/smart_report_mrt.tsx +++ b/src/components/smart_report_mrt/smart_report_mrt.tsx @@ -1,19 +1,19 @@ import TableViewIcon from '@mui/icons-material/TableView'; import ViewModuleIcon from '@mui/icons-material/ViewModule'; import { Button } from "@mui/material"; -import { MaterialReactTable, MaterialReactTableProps, MRT_RowData } from "material-react-table"; +import { MaterialReactTable } from "material-react-table"; import { useState } from "react"; -import "./smart_report_mrt.css"; +import "./smart_mrt.css"; +import { FaFilePdf } from 'react-icons/fa'; +import { useTableContext } from '../../TableContextProvider'; -/* - TODO: (Responsive Card View) - - Add a field `showInCardView` to the column definition. Default to false. - - Show columns that have `showInCardView` set to true in card view. - - When user toggles visibility of a column in card view, update the `showInCardView` field. -*/ -export const SmartReportMRT = (props: MaterialReactTableProps) => { +export const SmartReportMRT = () => { + // Get table instance from context + const table = useTableContext(); + // State to control the view mode override. Default to table view on desktop & card view on mobile. const [forceTableView, setForceTableView] = useState(window.innerWidth >= 500); + return ( ) @@ -21,7 +21,7 @@ export const SmartReportMRT = (props: MaterialReactTableP // Conditionally add the 'force-table-view' class className: `responsive-card-table ${forceTableView ? 'force-table-view' : ''}`, sx: { - maxWidth: forceTableView ? '100%' : '500px', + maxWidth: forceTableView ? '100%' : '500px', } }} // Add props to the table body cells () @@ -31,16 +31,29 @@ export const SmartReportMRT = (props: MaterialReactTableP })} renderTopToolbar = {() => (
- +
)} - {...props} + // Use table instance from context instead of props + state={table.getState()} + columns={table.options.columns} + data={table.options.data} + enableColumnFilters={table.options.enableColumnFilters} + enableGlobalFilter={table.options.enableGlobalFilter} + enableColumnOrdering={table.options.enableColumnOrdering} + enableGrouping={table.options.enableGrouping} + enablePagination={table.options.enablePagination} + enableSorting={table.options.enableSorting} + // Add more table props as needed /> ); }; \ No newline at end of file