From c73e9a84cb3ec1b91d3971844589cf0816733cfd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:14:38 +0300 Subject: [PATCH 1/6] Bump MUI Internal (#14937) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Lukas --- docs/package.json | 4 +- package.json | 4 +- .../src/BarChartPro/BarChartPro.tsx | 40 +++++++++ .../src/LineChartPro/LineChartPro.tsx | 90 +++++++++++++++++++ packages/x-charts/package.json | 2 +- packages/x-data-grid-premium/package.json | 2 +- packages/x-data-grid-pro/package.json | 2 +- packages/x-data-grid/package.json | 2 +- .../x-data-grid/src/components/GridRow.tsx | 8 ++ .../src/components/base/GridOverlays.tsx | 9 ++ .../menu/columnMenu/GridColumnMenu.tsx | 33 +++++++ .../components/toolbar/GridToolbarExport.tsx | 63 ++++++++++++- packages/x-date-pickers-pro/package.json | 2 +- .../DateRangeCalendar/DateRangeCalendar.tsx | 6 +- packages/x-date-pickers/package.json | 2 +- .../PickersSectionList/PickersSectionList.tsx | 40 +++++++-- packages/x-internals/package.json | 2 +- packages/x-license/package.json | 2 +- packages/x-tree-view-pro/package.json | 2 +- packages/x-tree-view/package.json | 2 +- pnpm-lock.yaml | 90 +++++++++---------- 21 files changed, 334 insertions(+), 73 deletions(-) diff --git a/docs/package.json b/docs/package.json index f1559e60742d..cfe7ab58704a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -27,7 +27,7 @@ "@emotion/react": "^11.13.3", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.13.0", - "@mui/docs": "6.1.2", + "@mui/docs": "6.1.4", "@mui/icons-material": "^5.16.7", "@mui/joy": "^5.0.0-beta.48", "@mui/lab": "^5.0.0-alpha.173", @@ -102,7 +102,7 @@ "@babel/plugin-transform-react-constant-elements": "^7.25.7", "@babel/preset-typescript": "^7.25.7", "@mui/internal-docs-utils": "^1.0.14", - "@mui/internal-scripts": "^1.0.22", + "@mui/internal-scripts": "^1.0.24", "@types/chance": "^1.1.6", "@types/d3-scale": "^4.0.8", "@types/d3-scale-chromatic": "^3.0.3", diff --git a/package.json b/package.json index 02e54753d4f5..741584687456 100644 --- a/package.json +++ b/package.json @@ -92,8 +92,8 @@ "@emotion/styled": "^11.13.0", "@mui/icons-material": "^5.16.7", "@mui/internal-babel-plugin-resolve-imports": "1.0.18", - "@mui/internal-markdown": "^1.0.15", - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-markdown": "^1.0.17", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/monorepo": "github:mui/material-ui#010de4505361345951824d905d1508d6f258ba67", "@mui/utils": "^5.16.6", diff --git a/packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx b/packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx index 0c3af2884f47..887cd1058746 100644 --- a/packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx +++ b/packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx @@ -24,6 +24,46 @@ function BarChartPlotZoom(props: BarPlotProps) { return ; } +BarChartPlotZoom.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + /** + * If provided, the function will be used to format the label of the bar. + * It can be set to 'value' to display the current value. + * @param {BarItem} item The item to format. + * @param {BarLabelContext} context data about the bar. + * @returns {string} The formatted label. + */ + barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]), + /** + * Defines the border radius of the bar element. + */ + borderRadius: PropTypes.number, + /** + * Callback fired when a bar item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {BarItemIdentifier} barItemIdentifier The bar item identifier. + */ + onItemClick: PropTypes.func, + /** + * If `true`, animations are skipped. + * @default undefined + */ + skipAnimation: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; + export interface BarChartProProps extends BarChartProps, ZoomProps {} /** diff --git a/packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx b/packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx index 732756fe1731..b7d66add50ba 100644 --- a/packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx +++ b/packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx @@ -31,16 +31,106 @@ function AreaPlotZoom(props: AreaPlotProps) { return ; } +AreaPlotZoom.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + /** + * Callback fired when a line area item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {LineItemIdentifier} lineItemIdentifier The line item identifier. + */ + onItemClick: PropTypes.func, + /** + * If `true`, animations are skipped. + * @default false + */ + skipAnimation: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; + function LinePlotZoom(props: LinePlotProps) { const { isInteracting } = useZoom(); return ; } +LinePlotZoom.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + /** + * Callback fired when a line item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {LineItemIdentifier} lineItemIdentifier The line item identifier. + */ + onItemClick: PropTypes.func, + /** + * If `true`, animations are skipped. + * @default false + */ + skipAnimation: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; + function MarkPlotZoom(props: MarkPlotProps) { const { isInteracting } = useZoom(); return ; } +MarkPlotZoom.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + /** + * If `true` the mark element will only be able to render circle. + * Giving fewer customization options, but saving around 40ms per 1.000 marks. + * @default false + */ + experimentalRendering: PropTypes.bool, + /** + * Callback fired when a line mark item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {LineItemIdentifier} lineItemIdentifier The line mark item identifier. + */ + onItemClick: PropTypes.func, + /** + * If `true`, animations are skipped. + * @default false + */ + skipAnimation: PropTypes.bool, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; + export interface LineChartProProps extends LineChartProps, ZoomProps {} /** diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index 65038b813fb0..181b81b28723 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -65,7 +65,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@react-spring/core": "^9.7.5", diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index 2831040fbfb4..0099d542a43d 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -72,7 +72,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/prop-types": "^15.7.13", diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index b6833d5bf47d..fb2a91f52a6c 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -70,7 +70,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/prop-types": "^15.7.13", diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index 61dbcb5a3321..753e36dc322b 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -71,7 +71,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/joy": "^5.0.0-beta.48", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx index f326364029d1..40fec6af4592 100644 --- a/packages/x-data-grid/src/components/GridRow.tsx +++ b/packages/x-data-grid/src/components/GridRow.tsx @@ -81,6 +81,14 @@ function EmptyCell({ width }: { width: number }) { ); } +EmptyCell.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + width: PropTypes.number.isRequired, +} as any; + const GridRow = React.forwardRef(function GridRow(props, refProp) { const { selected, diff --git a/packages/x-data-grid/src/components/base/GridOverlays.tsx b/packages/x-data-grid/src/components/base/GridOverlays.tsx index e21561bcdf27..79baf162939d 100644 --- a/packages/x-data-grid/src/components/base/GridOverlays.tsx +++ b/packages/x-data-grid/src/components/base/GridOverlays.tsx @@ -93,6 +93,15 @@ function GridOverlayWrapper(props: React.PropsWithChildren) { ); } +GridOverlayWrapper.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + loadingOverlayVariant: PropTypes.oneOf(['circular-progress', 'linear-progress', 'skeleton']), + overlayType: PropTypes.oneOf(['loadingOverlay', 'noResultsOverlay', 'noRowsOverlay']), +} as any; + GridOverlays.propTypes = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | diff --git a/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx b/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx index 3842fe844f93..b5a94ecc60df 100644 --- a/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx +++ b/packages/x-data-grid/src/components/menu/columnMenu/GridColumnMenu.tsx @@ -42,6 +42,39 @@ const GridGenericColumnMenu = React.forwardRef( function GridColumnMenu(props, ref) { return ( diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx index 855233ca9ecc..672e5e32dbe1 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarExport.tsx @@ -35,7 +35,7 @@ export interface GridToolbarExportProps { [key: string]: any; } -export function GridCsvExportMenuItem(props: GridCsvExportMenuItemProps) { +function GridCsvExportMenuItem(props: GridCsvExportMenuItemProps) { const apiRef = useGridApiContext(); const { hideMenu, options, ...other } = props; @@ -52,7 +52,28 @@ export function GridCsvExportMenuItem(props: GridCsvExportMenuItemProps) { ); } -export function GridPrintExportMenuItem(props: GridPrintExportMenuItemProps) { +GridCsvExportMenuItem.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + hideMenu: PropTypes.func, + options: PropTypes.shape({ + allColumns: PropTypes.bool, + delimiter: PropTypes.string, + disableToolbarButton: PropTypes.bool, + escapeFormulas: PropTypes.bool, + fields: PropTypes.arrayOf(PropTypes.string), + fileName: PropTypes.string, + getRowsToExport: PropTypes.func, + includeColumnGroupsHeaders: PropTypes.bool, + includeHeaders: PropTypes.bool, + shouldAppendQuotes: PropTypes.bool, + utf8WithBom: PropTypes.bool, + }), +} as any; + +function GridPrintExportMenuItem(props: GridPrintExportMenuItemProps) { const apiRef = useGridApiContext(); const { hideMenu, options, ...other } = props; @@ -69,6 +90,42 @@ export function GridPrintExportMenuItem(props: GridPrintExportMenuItemProps) { ); } +GridPrintExportMenuItem.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + hideMenu: PropTypes.func, + options: PropTypes.shape({ + allColumns: PropTypes.bool, + bodyClassName: PropTypes.string, + copyStyles: PropTypes.bool, + disableToolbarButton: PropTypes.bool, + fields: PropTypes.arrayOf(PropTypes.string), + fileName: PropTypes.string, + getRowsToExport: PropTypes.func, + hideFooter: PropTypes.bool, + hideToolbar: PropTypes.bool, + includeCheckboxes: PropTypes.bool, + pageStyle: PropTypes.oneOfType([ + PropTypes.shape({ + '__@hasInstance@646': PropTypes.func.isRequired, + '__@metadata@648': PropTypes.any, + apply: PropTypes.func.isRequired, + arguments: PropTypes.any.isRequired, + bind: PropTypes.func.isRequired, + call: PropTypes.func.isRequired, + caller: PropTypes.object.isRequired, + length: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + prototype: PropTypes.any.isRequired, + toString: PropTypes.func.isRequired, + }), + PropTypes.string, + ]), + }), +} as any; + const GridToolbarExport = React.forwardRef( function GridToolbarExport(props, ref) { const { csvOptions = {}, printOptions = {}, excelOptions, ...other } = props; @@ -107,4 +164,4 @@ GridToolbarExport.propTypes = { slotProps: PropTypes.object, } as any; -export { GridToolbarExport }; +export { GridToolbarExport, GridCsvExportMenuItem, GridPrintExportMenuItem }; diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 109a85767b9b..7c823343bcd9 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -96,7 +96,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/luxon": "^3.4.2", diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 823a5adc4fa0..e41859e4304f 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -79,7 +79,7 @@ const DateRangeCalendarMonthContainer = styled('div', { const weeksContainerHeight = (DAY_RANGE_SIZE + DAY_MARGIN * 2) * 6; -const DayCalendarForRange = styled(DayCalendar)(({ theme }) => ({ +const InnerDayCalendarForRange = styled(DayCalendar)(({ theme }) => ({ minWidth: 312, minHeight: weeksContainerHeight, [`&.${dateRangeCalendarClasses.dayDragging}`]: { @@ -99,7 +99,9 @@ const DayCalendarForRange = styled(DayCalendar)(({ theme }) => ({ }, }, }, -})) as typeof DayCalendar; +})); + +const DayCalendarForRange = InnerDayCalendarForRange as typeof DayCalendar; function useDateRangeCalendarDefaultizedProps( props: DateRangeCalendarProps, diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index bd7ce9da83d5..c3c5af7e6c28 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -98,7 +98,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/luxon": "^3.4.2", diff --git a/packages/x-date-pickers/src/PickersSectionList/PickersSectionList.tsx b/packages/x-date-pickers/src/PickersSectionList/PickersSectionList.tsx index 362fecaaa716..ae8db0f9ee84 100644 --- a/packages/x-date-pickers/src/PickersSectionList/PickersSectionList.tsx +++ b/packages/x-date-pickers/src/PickersSectionList/PickersSectionList.tsx @@ -60,15 +60,6 @@ interface PickersSectionProps extends Pick, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [Custom field](https://mui.com/x/react-date-pickers/custom-field/) + * + * API: + * + * - [PickersSectionList API](https://mui.com/x/api/date-pickers/pickers-section-list/) + */ const PickersSectionList = React.forwardRef(function PickersSectionList( inProps: PickersSectionListProps, ref: React.Ref, diff --git a/packages/x-internals/package.json b/packages/x-internals/package.json index 94c1f8969389..2aafdced02cc 100644 --- a/packages/x-internals/package.json +++ b/packages/x-internals/package.json @@ -48,7 +48,7 @@ "react": "^17.0.0 || ^18.0.0" }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "rimraf": "^6.0.1" }, "engines": { diff --git a/packages/x-license/package.json b/packages/x-license/package.json index dca472c63710..e1ec343ddd59 100644 --- a/packages/x-license/package.json +++ b/packages/x-license/package.json @@ -41,7 +41,7 @@ "react": "^17.0.0 || ^18.0.0" }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "rimraf": "^6.0.1" }, "engines": { diff --git a/packages/x-tree-view-pro/package.json b/packages/x-tree-view-pro/package.json index 9f7b6ba9177b..bfc58d01f83f 100644 --- a/packages/x-tree-view-pro/package.json +++ b/packages/x-tree-view-pro/package.json @@ -70,7 +70,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/prop-types": "^15.7.13", diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index 1614f7d4374d..bab5e2987428 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -68,7 +68,7 @@ } }, "devDependencies": { - "@mui/internal-test-utils": "^1.0.15", + "@mui/internal-test-utils": "^1.0.17", "@mui/material": "^5.16.7", "@mui/system": "^5.16.7", "@types/prop-types": "^15.7.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7af41d44bba6..57785408e6f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,11 +90,11 @@ importers: specifier: 1.0.18 version: 1.0.18(@babel/core@7.25.8) '@mui/internal-markdown': - specifier: ^1.0.15 - version: 1.0.15 + specifier: ^1.0.17 + version: 1.0.17 '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -429,8 +429,8 @@ importers: specifier: ^11.13.0 version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1) '@mui/docs': - specifier: 6.1.2 - version: 6.1.2(ycp24r7hmgzk3bu34shnp2ejgu) + specifier: 6.1.4 + version: 6.1.4(ycp24r7hmgzk3bu34shnp2ejgu) '@mui/icons-material': specifier: ^5.16.7 version: 5.16.7(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.4)(react@18.3.1) @@ -649,8 +649,8 @@ importers: specifier: ^1.0.14 version: 1.0.14 '@mui/internal-scripts': - specifier: ^1.0.22 - version: 1.0.22 + specifier: ^1.0.24 + version: 1.0.24 '@types/chance': specifier: ^1.1.6 version: 1.1.6 @@ -768,8 +768,8 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1012,8 +1012,8 @@ importers: version: 5.1.1 devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/joy': specifier: ^5.0.0-beta.48 version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1124,8 +1124,8 @@ importers: version: 5.1.1 devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1186,8 +1186,8 @@ importers: version: 5.1.1 devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1239,8 +1239,8 @@ importers: version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1334,8 +1334,8 @@ importers: version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1381,8 +1381,8 @@ importers: version: 18.3.1 devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -1401,8 +1401,8 @@ importers: version: 18.3.1 devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -1445,8 +1445,8 @@ importers: version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1504,8 +1504,8 @@ importers: version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mui/internal-test-utils': - specifier: ^1.0.15 - version: 1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.0.17 + version: 1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.16.7 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3025,8 +3025,8 @@ packages: '@mui/core-downloads-tracker@5.16.7': resolution: {integrity: sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==} - '@mui/docs@6.1.2': - resolution: {integrity: sha512-/xR1vchUwkMUzECOilcQu4Sx8SoOoJ5AJ9H0jg+s5Jn/2IiYIr2WMx34Dg3dt9KGuvnOr08j2zEefwnxxRa95Q==} + '@mui/docs@6.1.4': + resolution: {integrity: sha512-pUMMy4Hxx1se+gG0stiX/ICphFfOx9RC3bKI4da11iUNVsSaUfYaCtiPc296BUbilaTMcQ82v8jYu+AOexHT3w==} engines: {node: '>=14.0.0'} peerDependencies: '@mui/base': '*' @@ -3061,14 +3061,14 @@ packages: '@mui/internal-docs-utils@1.0.14': resolution: {integrity: sha512-jb8RyzoGkV8zvTJndB4An95WCasbDwmDiTibw2YQaUh3+4XiAX3jS2XUB9ab3BxMv3TDCCywL5g0viB1bxQVBw==} - '@mui/internal-markdown@1.0.15': - resolution: {integrity: sha512-efFhzNpiTtzBJYe0looj5L36DQ+mQFWpycJfl7M4jm0VrpJhyNd8egQaaQyMFf6LzCzNQPKT7KuFoovGTOkB2w==} + '@mui/internal-markdown@1.0.17': + resolution: {integrity: sha512-sws1OTUvJxNZ04mTYgahB7Z35yWcAEzVQxqqnOB+tPgDL/iMLtMQXB6wEFbW77e/P3O6Y/XOe7FhHXnK46vYTQ==} - '@mui/internal-scripts@1.0.22': - resolution: {integrity: sha512-+GEmp73U9o2touhiMRNO+hv8AlYWwVpu6ZjKLnZ3OLoYq5kg0e2p8GMpHTBTmalNKXXM3kOchgs0NYoYJwsX4g==} + '@mui/internal-scripts@1.0.24': + resolution: {integrity: sha512-omDYril4RiR8PSmPWkja16BjNyGkcj7N9sK1Ul9pSVpKSw8LF4BUxLOkF5NzAfDxNQgoZp+7DJPWBm9c/hmC+A==} - '@mui/internal-test-utils@1.0.15': - resolution: {integrity: sha512-KRj5CTQ1EtUQ/9c6LuDl32g49saCrg2VxTwpuyGv064fSDTdvqzIe8LLssIG6MnPFqdGao/d10Oh4VVMryQ9/w==} + '@mui/internal-test-utils@1.0.17': + resolution: {integrity: sha512-tLOlGMIkDIUnExfl+3LoaV60PWniOjfv44C5gCVW6PDhg0O0P5T1E2vM1zvOwm4T7xM+6IKY9E5BKcEUsdpqaQ==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -7580,8 +7580,8 @@ packages: resolution: {integrity: sha512-wgp8yesWjFBL7bycA3hxwHRdsZGJhjhyP1dSxKVKrza0EPFYtn+mHtkVy6dvP1kGSjovyG5B8yNP6Frj0UFUJg==} engines: {node: '>=18'} - marked@13.0.3: - resolution: {integrity: sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==} + marked@14.1.3: + resolution: {integrity: sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==} engines: {node: '>= 18'} hasBin: true @@ -11782,12 +11782,12 @@ snapshots: '@mui/core-downloads-tracker@5.16.7': {} - '@mui/docs@6.1.2(ycp24r7hmgzk3bu34shnp2ejgu)': + '@mui/docs@6.1.4(ycp24r7hmgzk3bu34shnp2ejgu)': dependencies: '@babel/runtime': 7.25.7 '@mui/base': 5.0.0-beta.40(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/icons-material': 5.16.7(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.4)(react@18.3.1) - '@mui/internal-markdown': 1.0.15 + '@mui/internal-markdown': 1.0.17 '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1))(@types/react@18.3.4)(react@18.3.1) chai: 5.1.1 @@ -11819,14 +11819,14 @@ snapshots: rimraf: 6.0.1 typescript: 5.6.3 - '@mui/internal-markdown@1.0.15': + '@mui/internal-markdown@1.0.17': dependencies: '@babel/runtime': 7.25.7 lodash: 4.17.21 - marked: 13.0.3 + marked: 14.1.3 prismjs: 1.29.0 - '@mui/internal-scripts@1.0.22': + '@mui/internal-scripts@1.0.24': dependencies: '@babel/core': 7.25.8 '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.8) @@ -11841,7 +11841,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@mui/internal-test-utils@1.0.15(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/internal-test-utils@1.0.17(@babel/core@7.25.8)(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/plugin-transform-modules-commonjs': 7.25.7(@babel/core@7.25.8) '@babel/preset-typescript': 7.25.7(@babel/core@7.25.8) @@ -17277,7 +17277,7 @@ snapshots: markdown-it: 14.1.0 markdownlint-micromark: 0.1.10 - marked@13.0.3: {} + marked@14.1.3: {} mdast-util-from-markdown@2.0.1: dependencies: From fc6cc5f93e88e94b1e16edff92a59f62776f75f1 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Thu, 17 Oct 2024 15:45:06 +0200 Subject: [PATCH 2/6] [DataGrid] List View (#14393) Signed-off-by: Kenan Yusuf Co-authored-by: Kenan Yusuf Co-authored-by: Bilal Shafi --- docs/data/data-grid/list-view/ListView.js | 107 +++++ docs/data/data-grid/list-view/ListView.tsx | 113 ++++++ .../data-grid/list-view/ListViewAdvanced.js | 355 +++++++++++++++++ .../data-grid/list-view/ListViewAdvanced.tsx | 375 ++++++++++++++++++ .../list-view/components/ActionDrawer.js | 88 ++++ .../list-view/components/ActionDrawer.tsx | 101 +++++ .../data-grid/list-view/components/Card.js | 70 ++++ .../data-grid/list-view/components/Card.tsx | 70 ++++ .../list-view/components/DetailsDrawer.js | 142 +++++++ .../list-view/components/DetailsDrawer.tsx | 158 ++++++++ .../data-grid/list-view/components/Drawer.js | 88 ++++ .../data-grid/list-view/components/Drawer.tsx | 97 +++++ .../list-view/components/FileIcon.js | 47 +++ .../list-view/components/FileIcon.tsx | 54 +++ .../list-view/components/ListCell.js | 110 +++++ .../list-view/components/ListCell.tsx | 117 ++++++ .../list-view/components/RenameDialog.js | 58 +++ .../list-view/components/RenameDialog.tsx | 67 ++++ .../data-grid/list-view/components/Toolbar.js | 105 +++++ .../list-view/components/Toolbar.tsx | 115 ++++++ .../list-view/components/ToolbarAddItem.js | 75 ++++ .../list-view/components/ToolbarAddItem.tsx | 81 ++++ .../list-view/components/ToolbarButton.js | 7 + .../list-view/components/ToolbarButton.tsx | 8 + .../components/ToolbarColumnsItem.js | 78 ++++ .../components/ToolbarColumnsItem.tsx | 83 ++++ .../components/ToolbarDensityItem.js | 87 ++++ .../components/ToolbarDensityItem.tsx | 94 +++++ .../list-view/components/ToolbarFilterItem.js | 217 ++++++++++ .../components/ToolbarFilterItem.tsx | 225 +++++++++++ .../list-view/components/ToolbarSortItem.js | 93 +++++ .../list-view/components/ToolbarSortItem.tsx | 99 +++++ docs/data/data-grid/list-view/constants.ts | 17 + docs/data/data-grid/list-view/data.ts | 204 ++++++++++ docs/data/data-grid/list-view/list-view.md | 29 ++ docs/data/data-grid/list-view/types.ts | 30 ++ docs/data/data-grid/list-view/utils.ts | 56 +++ docs/data/pages.ts | 6 + .../x/api/data-grid/data-grid-premium.json | 7 + docs/pages/x/api/data-grid/data-grid-pro.json | 7 + docs/pages/x/react-data-grid/list-view.js | 7 + .../data-grid-premium/data-grid-premium.json | 6 + .../data-grid-pro/data-grid-pro.json | 6 + .../src/columns/employees.columns.tsx | 2 +- .../src/DataGridPremium/DataGridPremium.tsx | 15 + .../useDataGridPremiumComponent.tsx | 6 +- .../src/DataGridPro/DataGridPro.tsx | 15 + .../DataGridPro/useDataGridProComponent.tsx | 6 +- .../src/DataGridPro/useDataGridProProps.ts | 1 + .../headerFiltering/GridHeaderFilterCell.tsx | 4 +- .../src/models/dataGridProProps.ts | 10 + .../src/DataGrid/useDataGridComponent.tsx | 8 +- .../src/DataGrid/useDataGridProps.ts | 1 + .../x-data-grid/src/components/GridRow.tsx | 6 + .../components/containers/GridRootStyles.ts | 1 + .../virtualization/GridVirtualScrollbar.tsx | 5 + .../virtualization/GridVirtualScroller.tsx | 4 +- .../columnHeaders/useGridColumnHeaders.tsx | 4 +- .../features/columns/gridColumnsUtils.ts | 9 +- .../useGridKeyboardNavigation.ts | 18 +- .../listView/gridListViewSelectors.ts | 6 + .../features/listView/useGridListView.tsx | 75 ++++ .../hooks/features/rows/useGridParamsApi.ts | 16 +- .../hooks/features/scroll/useGridScroll.ts | 16 +- .../virtualization/useGridVirtualScroller.tsx | 34 +- packages/x-data-grid/src/internals/index.ts | 4 + .../src/models/colDef/gridColDef.ts | 10 + .../x-data-grid/src/models/colDef/index.ts | 1 + .../src/models/gridStateCommunity.ts | 2 + .../src/models/props/DataGridProps.ts | 17 +- scripts/x-data-grid-premium.exports.json | 1 + scripts/x-data-grid-pro.exports.json | 1 + scripts/x-data-grid.exports.json | 1 + 73 files changed, 4229 insertions(+), 29 deletions(-) create mode 100644 docs/data/data-grid/list-view/ListView.js create mode 100644 docs/data/data-grid/list-view/ListView.tsx create mode 100644 docs/data/data-grid/list-view/ListViewAdvanced.js create mode 100644 docs/data/data-grid/list-view/ListViewAdvanced.tsx create mode 100644 docs/data/data-grid/list-view/components/ActionDrawer.js create mode 100644 docs/data/data-grid/list-view/components/ActionDrawer.tsx create mode 100644 docs/data/data-grid/list-view/components/Card.js create mode 100644 docs/data/data-grid/list-view/components/Card.tsx create mode 100644 docs/data/data-grid/list-view/components/DetailsDrawer.js create mode 100644 docs/data/data-grid/list-view/components/DetailsDrawer.tsx create mode 100644 docs/data/data-grid/list-view/components/Drawer.js create mode 100644 docs/data/data-grid/list-view/components/Drawer.tsx create mode 100644 docs/data/data-grid/list-view/components/FileIcon.js create mode 100644 docs/data/data-grid/list-view/components/FileIcon.tsx create mode 100644 docs/data/data-grid/list-view/components/ListCell.js create mode 100644 docs/data/data-grid/list-view/components/ListCell.tsx create mode 100644 docs/data/data-grid/list-view/components/RenameDialog.js create mode 100644 docs/data/data-grid/list-view/components/RenameDialog.tsx create mode 100644 docs/data/data-grid/list-view/components/Toolbar.js create mode 100644 docs/data/data-grid/list-view/components/Toolbar.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarAddItem.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarAddItem.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarButton.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarButton.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarColumnsItem.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarColumnsItem.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarDensityItem.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarDensityItem.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarFilterItem.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarFilterItem.tsx create mode 100644 docs/data/data-grid/list-view/components/ToolbarSortItem.js create mode 100644 docs/data/data-grid/list-view/components/ToolbarSortItem.tsx create mode 100644 docs/data/data-grid/list-view/constants.ts create mode 100644 docs/data/data-grid/list-view/data.ts create mode 100644 docs/data/data-grid/list-view/list-view.md create mode 100644 docs/data/data-grid/list-view/types.ts create mode 100644 docs/data/data-grid/list-view/utils.ts create mode 100644 docs/pages/x/react-data-grid/list-view.js create mode 100644 packages/x-data-grid/src/hooks/features/listView/gridListViewSelectors.ts create mode 100644 packages/x-data-grid/src/hooks/features/listView/useGridListView.tsx diff --git a/docs/data/data-grid/list-view/ListView.js b/docs/data/data-grid/list-view/ListView.js new file mode 100644 index 000000000000..a7ab930852af --- /dev/null +++ b/docs/data/data-grid/list-view/ListView.js @@ -0,0 +1,107 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import Box from '@mui/material/Box'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import Avatar from '@mui/material/Avatar'; +import Typography from '@mui/material/Typography'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import IconButton from '@mui/material/IconButton'; +import MessageIcon from '@mui/icons-material/Message'; + +function MessageAction(params) { + const handleMessage = () => { + console.log(`send message to ${params.row.phone}`); + }; + return ( + + + + ); +} + +function ListViewCell(params) { + return ( + + + + + {params.row.name} + + + {params.row.position} + + + + + ); +} + +const listColDef = { + field: 'listColumn', + renderCell: ListViewCell, +}; + +const VISIBLE_FIELDS = ['avatar', 'name', 'position']; + +export default function ListView() { + const [isListView, setIsListView] = React.useState(true); + + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 20, + visibleFields: VISIBLE_FIELDS, + }); + + const columns = React.useMemo(() => { + return [ + ...data.columns, + { + type: 'actions', + field: 'actions', + width: 75, + getActions: (params) => [], + }, + ]; + }, [data.columns]); + + const rowHeight = isListView ? 64 : 52; + + return ( + + setIsListView(event.target.checked)} + /> + } + label="Enable list view" + /> + + + + + ); +} diff --git a/docs/data/data-grid/list-view/ListView.tsx b/docs/data/data-grid/list-view/ListView.tsx new file mode 100644 index 000000000000..40239d5a8e41 --- /dev/null +++ b/docs/data/data-grid/list-view/ListView.tsx @@ -0,0 +1,113 @@ +import * as React from 'react'; +import { + DataGridPro, + GridRenderCellParams, + GridListColDef, + GridColDef, + GridRowParams, +} from '@mui/x-data-grid-pro'; +import Box from '@mui/material/Box'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import Avatar from '@mui/material/Avatar'; +import Typography from '@mui/material/Typography'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import IconButton from '@mui/material/IconButton'; +import MessageIcon from '@mui/icons-material/Message'; + +function MessageAction(params: Pick) { + const handleMessage = () => { + console.log(`send message to ${params.row.phone}`); + }; + return ( + + + + ); +} + +function ListViewCell(params: GridRenderCellParams) { + return ( + + + + + {params.row.name} + + + {params.row.position} + + + + + ); +} + +const listColDef: GridListColDef = { + field: 'listColumn', + renderCell: ListViewCell, +}; + +const VISIBLE_FIELDS = ['avatar', 'name', 'position']; + +export default function ListView() { + const [isListView, setIsListView] = React.useState(true); + + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 20, + visibleFields: VISIBLE_FIELDS, + }); + + const columns: GridColDef[] = React.useMemo(() => { + return [ + ...data.columns, + { + type: 'actions', + field: 'actions', + width: 75, + getActions: (params) => [], + }, + ]; + }, [data.columns]); + + const rowHeight = isListView ? 64 : 52; + + return ( + + setIsListView(event.target.checked)} + /> + } + label="Enable list view" + /> + + + + + ); +} diff --git a/docs/data/data-grid/list-view/ListViewAdvanced.js b/docs/data/data-grid/list-view/ListViewAdvanced.js new file mode 100644 index 000000000000..b27633b63270 --- /dev/null +++ b/docs/data/data-grid/list-view/ListViewAdvanced.js @@ -0,0 +1,355 @@ +import * as React from 'react'; +import { + GridActionsCellItem, + useGridApiRef, + DataGridPremium, + gridClasses, +} from '@mui/x-data-grid-premium'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import Avatar from '@mui/material/Avatar'; +import Stack from '@mui/material/Stack'; +import OpenIcon from '@mui/icons-material/Visibility'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import CSSBaseline from '@mui/material/CssBaseline'; +import { randomId } from '@mui/x-data-grid-generator'; +import { FileIcon } from './components/FileIcon'; +import { DetailsDrawer } from './components/DetailsDrawer'; +import { ListCell } from './components/ListCell'; +import { Toolbar } from './components/Toolbar'; +import { INITIAL_ROWS } from './data'; +import { FILE_TYPES } from './constants'; + +import { formatDate, formatSize, stringAvatar } from './utils'; +import { ActionDrawer } from './components/ActionDrawer'; +import { RenameDialog } from './components/RenameDialog'; + +export default function ListViewAdvanced() { + // This is used only for the example - renders the drawer inside the container + const containerRef = React.useRef(null); + const container = () => containerRef.current; + + const isListView = useMediaQuery('(min-width: 700px)'); + + const apiRef = useGridApiRef(); + + const [rows, setRows] = React.useState(INITIAL_ROWS); + + const [loading, setLoading] = React.useState(false); + + const [overlayState, setOverlayState] = React.useState({ + overlay: null, + params: null, + }); + + const handleCloseOverlay = () => { + setOverlayState({ overlay: null, params: null }); + }; + + const handleDelete = React.useCallback((ids) => { + setRows((prevRows) => prevRows.filter((row) => !ids.includes(row.id))); + }, []); + + const handleUpdate = React.useCallback((id, field, value) => { + setRows((prevRows) => + prevRows.map((row) => + row.id === id + ? { ...row, [field]: value, updatedAt: new Date().toISOString() } + : row, + ), + ); + }, []); + + const handleUpload = React.useCallback((event) => { + if (!event.target.files) { + return; + } + + const file = event.target.files[0]; + const createdAt = new Date().toISOString(); + + const fileType = file.type.split('/')[1]; + + // validate file type + if (!FILE_TYPES.includes(fileType)) { + alert('Invalid file type'); + return; + } + + const row = { + id: randomId(), + name: file.name, + description: '', + type: fileType, + size: file.size, + createdBy: 'Kenan Yusuf', + createdAt, + updatedAt: createdAt, + state: 'pending', + }; + + event.target.value = ''; + + // Add temporary row + setLoading(true); + setRows((prevRows) => [...prevRows, row]); + + // Simulate server response time + const timeout = Math.floor(Math.random() * 3000) + 2000; + setTimeout(() => { + const uploadedRow = { ...row, state: 'uploaded' }; + setRows((prevRows) => + prevRows.map((r) => (r.id === row.id ? uploadedRow : r)), + ); + setOverlayState({ overlay: 'actions', params: { row } }); + setLoading(false); + }, timeout); + }, []); + + const columns = React.useMemo( + () => [ + { + field: 'name', + headerName: 'Name', + width: 350, + editable: true, + hideable: false, + renderCell: (params) => { + return ( + + + {params.value} + + ); + }, + }, + { + field: 'createdBy', + headerName: 'Owner', + width: 200, + renderCell: (params) => { + const avatarProps = stringAvatar(params.value); + return ( + + + {params.value} + + ); + }, + }, + { + field: 'createdAt', + headerName: 'Added', + type: 'date', + width: 200, + valueFormatter: formatDate, + }, + { + field: 'updatedAt', + headerName: 'Modified', + type: 'date', + width: 200, + valueFormatter: formatDate, + }, + { + field: 'type', + headerName: 'Type', + width: 150, + }, + { + field: 'size', + headerName: 'Size', + width: 120, + valueFormatter: formatSize, + }, + { + type: 'actions', + field: 'actions', + resizable: false, + width: 50, + getActions: (params) => [ + } + onClick={() => { + setOverlayState({ overlay: 'actions', params }); + }} + showInMenu + />, + } + onClick={() => + apiRef.current?.startCellEditMode({ + id: params.id, + field: 'name', + }) + } + showInMenu + />, + } + onClick={() => handleDelete([params.id])} + showInMenu + />, + ], + }, + ], + [handleDelete, apiRef], + ); + + const listColDef = React.useMemo( + () => ({ + field: 'listCell', + renderCell: (params) => ( + { + setOverlayState({ overlay: 'actions', params }); + }} + /> + ), + }), + [], + ); + + const getEstimatedRowHeight = () => { + const density = apiRef.current?.state?.density; + + if (isListView) { + switch (density) { + case 'compact': + return 47; + case 'standard': + return 67; + case 'comfortable': + return 97; + default: + return 67; + } + } else { + switch (density) { + case 'compact': + return 47; + case 'standard': + return 55; + case 'comfortable': + return 63; + default: + return 55; + } + } + }; + + const getRowHeight = React.useCallback( + () => (isListView ? 'auto' : undefined), + [isListView], + ); + + return ( + + +
+ + setOverlayState({ overlay: 'actions', params }) + } + hideFooterSelectedRowCount + /> + handleUpdate(id, 'description', value)} + onClose={handleCloseOverlay} + /> + + setOverlayState({ overlay: 'details', params: overlayState.params }) + } + onRename={() => + setOverlayState({ overlay: 'rename', params: overlayState.params }) + } + onDelete={(id) => { + handleDelete([id]); + handleCloseOverlay(); + }} + onClose={handleCloseOverlay} + /> + handleUpdate(id, 'name', value)} + onClose={handleCloseOverlay} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/list-view/ListViewAdvanced.tsx b/docs/data/data-grid/list-view/ListViewAdvanced.tsx new file mode 100644 index 000000000000..e7b9c692bede --- /dev/null +++ b/docs/data/data-grid/list-view/ListViewAdvanced.tsx @@ -0,0 +1,375 @@ +import * as React from 'react'; +import { + GridActionsCellItem, + GridColDef, + useGridApiRef, + GridRowParams, + DataGridPremium, + GridRowId, + gridClasses, + GridRowModel, +} from '@mui/x-data-grid-premium'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import Avatar from '@mui/material/Avatar'; +import Stack from '@mui/material/Stack'; +import OpenIcon from '@mui/icons-material/Visibility'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import CSSBaseline from '@mui/material/CssBaseline'; +import { randomId } from '@mui/x-data-grid-generator'; +import { FileIcon } from './components/FileIcon'; +import { DetailsDrawer } from './components/DetailsDrawer'; +import { ListCell } from './components/ListCell'; +import { Toolbar } from './components/Toolbar'; +import { INITIAL_ROWS } from './data'; +import { FILE_TYPES } from './constants'; +import { RowModel, FileType } from './types'; +import { formatDate, formatSize, stringAvatar } from './utils'; +import { ActionDrawer } from './components/ActionDrawer'; +import { RenameDialog } from './components/RenameDialog'; + +export default function ListViewAdvanced() { + // This is used only for the example - renders the drawer inside the container + const containerRef = React.useRef(null); + const container = () => containerRef.current as HTMLElement; + + const isListView = useMediaQuery('(min-width: 700px)'); + + const apiRef = useGridApiRef(); + + const [rows, setRows] = React.useState[]>(INITIAL_ROWS); + + const [loading, setLoading] = React.useState(false); + + const [overlayState, setOverlayState] = React.useState<{ + overlay: 'actions' | 'details' | 'rename' | null; + params: Pick, 'row'> | null; + }>({ + overlay: null, + params: null, + }); + + const handleCloseOverlay = () => { + setOverlayState({ overlay: null, params: null }); + }; + + const handleDelete = React.useCallback((ids: GridRowId[]) => { + setRows((prevRows) => prevRows.filter((row) => !ids.includes(row.id))); + }, []); + + const handleUpdate = React.useCallback( + ( + id: GridRowId, + field: GridRowParams['columns'][number]['field'], + value: string, + ) => { + setRows((prevRows) => + prevRows.map((row) => + row.id === id + ? { ...row, [field]: value, updatedAt: new Date().toISOString() } + : row, + ), + ); + }, + [], + ); + + const handleUpload = React.useCallback( + (event: React.ChangeEvent) => { + if (!event.target.files) { + return; + } + + const file = event.target.files[0]; + const createdAt = new Date().toISOString(); + + const fileType = file.type.split('/')[1]; + + // validate file type + if (!FILE_TYPES.includes(fileType as FileType)) { + alert('Invalid file type'); + return; + } + + const row: RowModel = { + id: randomId(), + name: file.name, + description: '', + type: fileType as FileType, + size: file.size, + createdBy: 'Kenan Yusuf', + createdAt, + updatedAt: createdAt, + state: 'pending', + }; + + event.target.value = ''; + + // Add temporary row + setLoading(true); + setRows((prevRows) => [...prevRows, row]); + + // Simulate server response time + const timeout = Math.floor(Math.random() * 3000) + 2000; + setTimeout(() => { + const uploadedRow: RowModel = { ...row, state: 'uploaded' }; + setRows((prevRows) => + prevRows.map((r) => (r.id === row.id ? uploadedRow : r)), + ); + setOverlayState({ overlay: 'actions', params: { row } }); + setLoading(false); + }, timeout); + }, + [], + ); + + const columns: GridColDef[] = React.useMemo( + () => [ + { + field: 'name', + headerName: 'Name', + width: 350, + editable: true, + hideable: false, + renderCell: (params) => { + return ( + + + {params.value} + + ); + }, + }, + { + field: 'createdBy', + headerName: 'Owner', + width: 200, + renderCell: (params) => { + const avatarProps = stringAvatar(params.value); + return ( + + + {params.value} + + ); + }, + }, + { + field: 'createdAt', + headerName: 'Added', + type: 'date', + width: 200, + valueFormatter: formatDate, + }, + { + field: 'updatedAt', + headerName: 'Modified', + type: 'date', + width: 200, + valueFormatter: formatDate, + }, + { + field: 'type', + headerName: 'Type', + width: 150, + }, + { + field: 'size', + headerName: 'Size', + width: 120, + valueFormatter: formatSize, + }, + { + type: 'actions', + field: 'actions', + resizable: false, + width: 50, + getActions: (params) => [ + } + onClick={() => { + setOverlayState({ overlay: 'actions', params }); + }} + showInMenu + />, + } + onClick={() => + apiRef.current?.startCellEditMode({ + id: params.id, + field: 'name', + }) + } + showInMenu + />, + } + onClick={() => handleDelete([params.id])} + showInMenu + />, + ], + }, + ], + [handleDelete, apiRef], + ); + + const listColDef: GridColDef = React.useMemo( + () => ({ + field: 'listCell', + renderCell: (params) => ( + { + setOverlayState({ overlay: 'actions', params }); + }} + /> + ), + }), + [], + ); + + const getEstimatedRowHeight = () => { + const density = apiRef.current?.state?.density; + + if (isListView) { + switch (density) { + case 'compact': + return 47; + case 'standard': + return 67; + case 'comfortable': + return 97; + default: + return 67; + } + } else { + switch (density) { + case 'compact': + return 47; + case 'standard': + return 55; + case 'comfortable': + return 63; + default: + return 55; + } + } + }; + + const getRowHeight = React.useCallback( + () => (isListView ? 'auto' : undefined), + [isListView], + ); + + return ( + + +
+ + setOverlayState({ overlay: 'actions', params }) + } + hideFooterSelectedRowCount + /> + + handleUpdate(id, 'description', value)} + onClose={handleCloseOverlay} + /> + + + setOverlayState({ overlay: 'details', params: overlayState.params }) + } + onRename={() => + setOverlayState({ overlay: 'rename', params: overlayState.params }) + } + onDelete={(id) => { + handleDelete([id]); + handleCloseOverlay(); + }} + onClose={handleCloseOverlay} + /> + + handleUpdate(id, 'name', value)} + onClose={handleCloseOverlay} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/list-view/components/ActionDrawer.js b/docs/data/data-grid/list-view/components/ActionDrawer.js new file mode 100644 index 000000000000..8c4e29e9955d --- /dev/null +++ b/docs/data/data-grid/list-view/components/ActionDrawer.js @@ -0,0 +1,88 @@ +import * as React from 'react'; + +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import OpenIcon from '@mui/icons-material/Visibility'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize } from '../utils'; +import { Drawer, DrawerHeader } from './Drawer'; + +function DrawerContent(props) { + const { params, onDelete, onPreview, onRename } = props; + return ( + + + + + {params.row.name} + + + {params.row.createdBy} + + + · + + + {formatSize(params.row.size)} + + + + {params.row.updatedAt + ? `Updated ${formatDate(params.row.updatedAt)}` + : `Added ${formatDate(params.row.createdAt)}`} + + + + + + + + + + + Preview + + + + + + + + Rename + + + + onDelete(params.row.id)}> + + + + Delete + + + + + ); +} + +export function ActionDrawer(props) { + const { params, onPreview, onRename, onDelete, ...other } = props; + return ( + + {params && ( + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/ActionDrawer.tsx b/docs/data/data-grid/list-view/components/ActionDrawer.tsx new file mode 100644 index 000000000000..533c8cddad5f --- /dev/null +++ b/docs/data/data-grid/list-view/components/ActionDrawer.tsx @@ -0,0 +1,101 @@ +import * as React from 'react'; +import { GridRowId, GridRowParams } from '@mui/x-data-grid-premium'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import OpenIcon from '@mui/icons-material/Visibility'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize } from '../utils'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; +import { RowModel } from '../types'; + +interface ActionDrawerProps extends DrawerProps { + params: Pick, 'row'> | null; + onPreview: () => void; + onRename: () => void; + onDelete: (id: GridRowId) => void; +} + +function DrawerContent( + props: { params: Pick, 'row'> } & Pick< + ActionDrawerProps, + 'onDelete' | 'onPreview' | 'onRename' + >, +) { + const { params, onDelete, onPreview, onRename } = props; + return ( + + + + + {params.row.name} + + + {params.row.createdBy} + + + · + + + {formatSize(params.row.size)} + + + + {params.row.updatedAt + ? `Updated ${formatDate(params.row.updatedAt)}` + : `Added ${formatDate(params.row.createdAt)}`} + + + + + + + + + + + Preview + + + + + + + + Rename + + + + onDelete(params.row.id)}> + + + + Delete + + + + + ); +} + +export function ActionDrawer(props: ActionDrawerProps) { + const { params, onPreview, onRename, onDelete, ...other } = props; + return ( + + {params && ( + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Card.js b/docs/data/data-grid/list-view/components/Card.js new file mode 100644 index 000000000000..5d2e880bf605 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Card.js @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; + +export function Card(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardMedia(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardContent(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardTitle(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardDetailList(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardDetail(props) { + const { children, ...other } = props; + return ( + + {children} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Card.tsx b/docs/data/data-grid/list-view/components/Card.tsx new file mode 100644 index 000000000000..bd0df76f5db1 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Card.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Stack, { StackProps } from '@mui/material/Stack'; +import Typography, { TypographyProps } from '@mui/material/Typography'; + +export function Card(props: StackProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardMedia(props: StackProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardContent(props: StackProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardTitle(props: TypographyProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardDetailList(props: StackProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} + +export function CardDetail(props: TypographyProps) { + const { children, ...other } = props; + return ( + + {children} + + ); +} diff --git a/docs/data/data-grid/list-view/components/DetailsDrawer.js b/docs/data/data-grid/list-view/components/DetailsDrawer.js new file mode 100644 index 000000000000..44b1808c622b --- /dev/null +++ b/docs/data/data-grid/list-view/components/DetailsDrawer.js @@ -0,0 +1,142 @@ +import * as React from 'react'; + +import Divider from '@mui/material/Divider'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import Avatar from '@mui/material/Avatar'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import { Drawer, DrawerHeader } from './Drawer'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize, stringAvatar } from '../utils'; + +function Thumbnail() { + return ( + ({ + aspectRatio: '16/9', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'text.secondary', + borderRadius: 2, + gap: 1, + backgroundColor: 'grey.200', + ...theme.applyStyles('dark', { + backgroundColor: 'grey.800', + }), + })} + > + + + No preview available + + + ); +} + +function DrawerContent(props) { + const { params, onDescriptionChange, onClose } = props; + const [description, setDescription] = React.useState(params.row.description || ''); + const avatarProps = stringAvatar(params.row.createdBy); + + const handleSave = (event) => { + onDescriptionChange(params.row.id, description); + onClose(event); + }; + + return ( + + + + {params.row.name} + + + + + + setDescription(event.target.value)} + sx={{ mt: 1 }} + multiline + /> + + + + Type + + {params.row.type} + + + + + Size + + {formatSize(params.row.size)} + + + + + Created + + {formatDate(params.row.createdAt)} + + + + + Modified + + {formatDate(params.row.updatedAt)} + + + + + Owner + + + + + {params.row.createdBy} + + + + ); +} + +export function DetailsDrawer(props) { + const { params, listView, onDescriptionChange, onClose, ...other } = props; + return ( + + {params && ( + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/DetailsDrawer.tsx b/docs/data/data-grid/list-view/components/DetailsDrawer.tsx new file mode 100644 index 000000000000..6e4f2545dfeb --- /dev/null +++ b/docs/data/data-grid/list-view/components/DetailsDrawer.tsx @@ -0,0 +1,158 @@ +import * as React from 'react'; +import { GridRowId, GridRowParams } from '@mui/x-data-grid-premium'; +import Divider from '@mui/material/Divider'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import Avatar from '@mui/material/Avatar'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize, stringAvatar } from '../utils'; +import { RowModel } from '../types'; + +interface DetailsDrawerProps extends DrawerProps { + params: Pick, 'row'> | null; + listView: boolean; + onDescriptionChange: (id: GridRowId, value: string) => void; +} + +function Thumbnail() { + return ( + ({ + aspectRatio: '16/9', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'text.secondary', + borderRadius: 2, + gap: 1, + backgroundColor: 'grey.200', + ...theme.applyStyles('dark', { + backgroundColor: 'grey.800', + }), + })} + > + + + No preview available + + + ); +} + +function DrawerContent( + props: { params: Pick, 'row'> } & Pick< + DetailsDrawerProps, + 'onDescriptionChange' | 'onClose' + >, +) { + const { params, onDescriptionChange, onClose } = props; + const [description, setDescription] = React.useState(params.row.description || ''); + const avatarProps = stringAvatar(params.row.createdBy); + + const handleSave = (event: React.MouseEvent) => { + onDescriptionChange(params.row.id, description); + onClose(event); + }; + + return ( + + + + {params.row.name} + + + + + + + setDescription(event.target.value)} + sx={{ mt: 1 }} + multiline + /> + + + + + + Type + + {params.row.type} + + + + + Size + + {formatSize(params.row.size)} + + + + + Created + + {formatDate(params.row.createdAt)} + + + + + Modified + + {formatDate(params.row.updatedAt)} + + + + + + Owner + + + + + {params.row.createdBy} + + + + ); +} + +export function DetailsDrawer(props: DetailsDrawerProps) { + const { params, listView, onDescriptionChange, onClose, ...other } = props; + return ( + + {params && ( + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Drawer.js b/docs/data/data-grid/list-view/components/Drawer.js new file mode 100644 index 000000000000..fcf9d182a6a3 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Drawer.js @@ -0,0 +1,88 @@ +import * as React from 'react'; +import MUISwipeableDrawer from '@mui/material/SwipeableDrawer'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import Paper from '@mui/material/Paper'; +import { useMediaQuery } from '@mui/system'; + +function SwipeIndicator() { + return ( + + + + ); +} + +export function DrawerHeader(props) { + const { children, ...other } = props; + + return ( + + + {children} + + + ); +} + +export function Drawer(props) { + const { children, anchor, width = 320, container, ...other } = props; + const isBottomDrawer = anchor === 'bottom'; + const isTouch = useMediaQuery('(hover: none)'); + + return ( + {}} // required by SwipeableDrawer but not used in this demo + > + {isTouch && isBottomDrawer && } + {children} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Drawer.tsx b/docs/data/data-grid/list-view/components/Drawer.tsx new file mode 100644 index 000000000000..a859781e4417 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Drawer.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import MUISwipeableDrawer, { + SwipeableDrawerProps as MUISwipeableDrawerProps, +} from '@mui/material/SwipeableDrawer'; +import Box from '@mui/material/Box'; +import Stack, { StackProps } from '@mui/material/Stack'; +import Paper from '@mui/material/Paper'; +import { useMediaQuery } from '@mui/system'; + +function SwipeIndicator() { + return ( + + + + ); +} + +export interface DrawerHeaderProps extends StackProps {} + +export function DrawerHeader(props: DrawerHeaderProps) { + const { children, ...other } = props; + + return ( + + + {children} + + + ); +} + +export interface DrawerProps extends Omit { + width?: number; + container?: () => HTMLElement; +} + +export function Drawer(props: DrawerProps) { + const { children, anchor, width = 320, container, ...other } = props; + const isBottomDrawer = anchor === 'bottom'; + const isTouch = useMediaQuery('(hover: none)'); + + return ( + {}} // required by SwipeableDrawer but not used in this demo + > + {isTouch && isBottomDrawer && } + {children} + + ); +} diff --git a/docs/data/data-grid/list-view/components/FileIcon.js b/docs/data/data-grid/list-view/components/FileIcon.js new file mode 100644 index 000000000000..bc1818bb50f4 --- /dev/null +++ b/docs/data/data-grid/list-view/components/FileIcon.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import SvgIcon from '@mui/material/SvgIcon'; +import TextSnippetIcon from '@mui/icons-material/TextSnippet'; +import VideocamIcon from '@mui/icons-material/Videocam'; +import ImageIcon from '@mui/icons-material/Image'; +import FolderZipIcon from '@mui/icons-material/FolderZip'; + +const FILE_TYPE_ICONS = { + video: { + component: VideocamIcon, + color: 'error', + }, + image: { + component: ImageIcon, + color: 'error', + }, + document: { + component: TextSnippetIcon, + color: 'primary', + }, + archive: { + component: FolderZipIcon, + color: 'inherit', + }, +}; + +const FILE_ICON = { + pdf: FILE_TYPE_ICONS.document, + docx: FILE_TYPE_ICONS.document, + txt: FILE_TYPE_ICONS.document, + mp4: FILE_TYPE_ICONS.video, + mov: FILE_TYPE_ICONS.video, + webm: FILE_TYPE_ICONS.video, + jpg: FILE_TYPE_ICONS.image, + jpeg: FILE_TYPE_ICONS.image, + png: FILE_TYPE_ICONS.image, + gif: FILE_TYPE_ICONS.image, + tiff: FILE_TYPE_ICONS.image, + webp: FILE_TYPE_ICONS.image, + zip: FILE_TYPE_ICONS.archive, +}; + +export function FileIcon(props) { + const { type } = props; + const iconProps = FILE_ICON[type]; + return ; +} diff --git a/docs/data/data-grid/list-view/components/FileIcon.tsx b/docs/data/data-grid/list-view/components/FileIcon.tsx new file mode 100644 index 000000000000..ac70cced33bf --- /dev/null +++ b/docs/data/data-grid/list-view/components/FileIcon.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; +import TextSnippetIcon from '@mui/icons-material/TextSnippet'; +import VideocamIcon from '@mui/icons-material/Videocam'; +import ImageIcon from '@mui/icons-material/Image'; +import FolderZipIcon from '@mui/icons-material/FolderZip'; +import { FileType } from '../types'; + +const FILE_TYPE_ICONS = { + video: { + component: VideocamIcon, + color: 'error', + }, + image: { + component: ImageIcon, + color: 'error', + }, + document: { + component: TextSnippetIcon, + color: 'primary', + }, + archive: { + component: FolderZipIcon, + color: 'inherit', + }, +} satisfies Record< + string, + { component: React.ElementType; color: SvgIconProps['color'] } +>; + +const FILE_ICON: Record< + FileType, + { component: React.ElementType; color: SvgIconProps['color'] } +> = { + pdf: FILE_TYPE_ICONS.document, + docx: FILE_TYPE_ICONS.document, + txt: FILE_TYPE_ICONS.document, + mp4: FILE_TYPE_ICONS.video, + mov: FILE_TYPE_ICONS.video, + webm: FILE_TYPE_ICONS.video, + jpg: FILE_TYPE_ICONS.image, + jpeg: FILE_TYPE_ICONS.image, + png: FILE_TYPE_ICONS.image, + gif: FILE_TYPE_ICONS.image, + tiff: FILE_TYPE_ICONS.image, + webp: FILE_TYPE_ICONS.image, + zip: FILE_TYPE_ICONS.archive, +}; + +export function FileIcon(props: SvgIconProps & { type: FileType }) { + const { type } = props; + const iconProps = FILE_ICON[type]; + return ; +} diff --git a/docs/data/data-grid/list-view/components/ListCell.js b/docs/data/data-grid/list-view/components/ListCell.js new file mode 100644 index 000000000000..19c1688b1d74 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ListCell.js @@ -0,0 +1,110 @@ +import * as React from 'react'; +import { + gridColumnVisibilityModelSelector, + gridDensitySelector, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid-premium'; +import IconButton from '@mui/material/IconButton'; +import Box from '@mui/material/Box'; +import GridMoreVertIcon from '@mui/icons-material/MoreVert'; +import { + Card, + CardContent, + CardDetailList, + CardDetail, + CardTitle, + CardMedia, +} from './Card'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize } from '../utils'; + +const ICON_SIZE_BY_DENSITY = { + compact: 24, + standard: 32, + comfortable: 16, +}; + +function Thumbnail(props) { + const { fileIcon } = props; + return ( + ({ + position: 'relative', + borderRadius: 1, + width: 64, + height: 64, + overflow: 'hidden', + backgroundColor: 'grey.200', + ...theme.applyStyles('dark', { + backgroundColor: 'grey.800', + }), + })} + > + + {fileIcon} + + + ); +} + +export function ListCell(props) { + const { onOpenActions, ...params } = props; + const apiRef = useGridApiContext(); + const density = useGridSelector(apiRef, gridDensitySelector); + const columnVisibilityModel = useGridSelector( + apiRef, + gridColumnVisibilityModelSelector, + ); + + const showCreatedBy = columnVisibilityModel.createdBy !== false; + const showSize = columnVisibilityModel.size !== false; + const showCreatedAt = columnVisibilityModel.createdAt !== false; + const showUpdatedAt = columnVisibilityModel.updatedAt !== false; + const showThumbnail = density === 'comfortable'; + + const icon = ( + + ); + + return ( + + {showThumbnail ? : icon} + + {params.row.name} + {density !== 'compact' && (showCreatedBy || showSize) && ( + + {showCreatedBy && {params.row.createdBy}} + {showSize && {formatSize(params.row.size)}} + + )} + {density === 'comfortable' && (showCreatedAt || showUpdatedAt) && ( + + {showUpdatedAt && `Updated ${formatDate(params.row.updatedAt)}`} + + )} + + + { + event.stopPropagation(); + onOpenActions(); + }} + sx={{ mr: -0.75 }} + > + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ListCell.tsx b/docs/data/data-grid/list-view/components/ListCell.tsx new file mode 100644 index 000000000000..250d5b82c217 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ListCell.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; +import { + gridColumnVisibilityModelSelector, + GridDensity, + gridDensitySelector, + GridRenderCellParams, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid-premium'; +import IconButton from '@mui/material/IconButton'; +import Box from '@mui/material/Box'; +import GridMoreVertIcon from '@mui/icons-material/MoreVert'; +import { + Card, + CardContent, + CardDetailList, + CardDetail, + CardTitle, + CardMedia, +} from './Card'; +import { FileIcon } from './FileIcon'; +import { formatDate, formatSize } from '../utils'; +import { RowModel } from '../types'; + +interface ListCellProps extends GridRenderCellParams { + onOpenActions: () => void; +} + +const ICON_SIZE_BY_DENSITY: Record = { + compact: 24, + standard: 32, + comfortable: 16, +}; + +function Thumbnail(props: { fileIcon: React.ReactNode }) { + const { fileIcon } = props; + return ( + ({ + position: 'relative', + borderRadius: 1, + width: 64, + height: 64, + overflow: 'hidden', + backgroundColor: 'grey.200', + ...theme.applyStyles('dark', { + backgroundColor: 'grey.800', + }), + })} + > + + {fileIcon} + + + ); +} + +export function ListCell(props: ListCellProps) { + const { onOpenActions, ...params } = props; + const apiRef = useGridApiContext(); + const density = useGridSelector(apiRef, gridDensitySelector); + const columnVisibilityModel = useGridSelector( + apiRef, + gridColumnVisibilityModelSelector, + ); + + const showCreatedBy = columnVisibilityModel.createdBy !== false; + const showSize = columnVisibilityModel.size !== false; + const showCreatedAt = columnVisibilityModel.createdAt !== false; + const showUpdatedAt = columnVisibilityModel.updatedAt !== false; + const showThumbnail = density === 'comfortable'; + + const icon = ( + + ); + + return ( + + {showThumbnail ? : icon} + + {params.row.name} + {density !== 'compact' && (showCreatedBy || showSize) && ( + + {showCreatedBy && {params.row.createdBy}} + {showSize && {formatSize(params.row.size)}} + + )} + {density === 'comfortable' && (showCreatedAt || showUpdatedAt) && ( + + {showUpdatedAt && `Updated ${formatDate(params.row.updatedAt)}`} + + )} + + + { + event.stopPropagation(); + onOpenActions(); + }} + sx={{ mr: -0.75 }} + > + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/RenameDialog.js b/docs/data/data-grid/list-view/components/RenameDialog.js new file mode 100644 index 000000000000..b7d1a49972c4 --- /dev/null +++ b/docs/data/data-grid/list-view/components/RenameDialog.js @@ -0,0 +1,58 @@ +import * as React from 'react'; + +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; + +export function RenameDialog(props) { + const { params, open, container, onSave, onClose } = props; + + const handleSave = (event) => { + event.preventDefault(); + + const formData = new FormData(event.currentTarget); + const value = formData.get('name'); + + onSave(params.row.id, value); + + onClose(); + }; + + return ( + + {params && ( + + Rename file + + + + + + + + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/RenameDialog.tsx b/docs/data/data-grid/list-view/components/RenameDialog.tsx new file mode 100644 index 000000000000..670e77c81ecd --- /dev/null +++ b/docs/data/data-grid/list-view/components/RenameDialog.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import { GridRowId, GridRowParams } from '@mui/x-data-grid-premium'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import { RowModel } from '../types'; + +export interface RenameDialogProps { + params: Pick, 'row'> | null; + open: boolean; + container?: () => HTMLElement; + onSave: (id: GridRowId, value: string) => void; + onClose: () => void; +} + +export function RenameDialog(props: RenameDialogProps) { + const { params, open, container, onSave, onClose } = props; + + const handleSave = (event: React.FormEvent) => { + event.preventDefault(); + + const formData = new FormData(event.currentTarget); + const value = formData.get('name') as string; + + onSave(params!.row.id, value); + + onClose(); + }; + + return ( + + {params && ( + + Rename file + + + + + + + + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Toolbar.js b/docs/data/data-grid/list-view/components/Toolbar.js new file mode 100644 index 000000000000..412b5dc9f3c5 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Toolbar.js @@ -0,0 +1,105 @@ +import * as React from 'react'; +import { + GridClearIcon, + GridDeleteIcon, + GridToolbarContainer, + GridToolbarQuickFilter, + selectedGridRowsSelector, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid-premium'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { outlinedInputClasses } from '@mui/material/OutlinedInput'; +import { iconButtonClasses } from '@mui/material/IconButton'; +import { ToolbarAddItem } from './ToolbarAddItem'; +import { ToolbarColumnsItem } from './ToolbarColumnsItem'; +import { ToolbarSortItem } from './ToolbarSortItem'; +import { ToolbarDensityItem } from './ToolbarDensityItem'; +import { ToolbarFilterItem } from './ToolbarFilterItem'; +import { ToolbarButton } from './ToolbarButton'; + +export function Toolbar(props) { + const { listView = false, container, handleUpload, handleDelete } = props; + const apiRef = useGridApiContext(); + const selectedRows = useGridSelector(apiRef, selectedGridRowsSelector); + const selectionCount = selectedRows.size; + const showSelectionOptions = selectionCount > 0; + + const handleClearSelection = () => { + apiRef.current.setRowSelectionModel([]); + }; + + const handleDeleteSelectedRows = () => { + handleClearSelection(); + handleDelete?.(Array.from(selectedRows.keys())); + }; + + const itemProps = { + listView, + container, + }; + + return ( + + {showSelectionOptions ? ( + + + + + + {selectionCount} selected + + + + + + ) : ( + + + + + + + + + + + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/Toolbar.tsx b/docs/data/data-grid/list-view/components/Toolbar.tsx new file mode 100644 index 000000000000..62980799d4b2 --- /dev/null +++ b/docs/data/data-grid/list-view/components/Toolbar.tsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import { + GridClearIcon, + GridDeleteIcon, + GridRowId, + GridToolbarContainer, + GridToolbarProps, + GridToolbarQuickFilter, + selectedGridRowsSelector, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid-premium'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { outlinedInputClasses } from '@mui/material/OutlinedInput'; +import { iconButtonClasses } from '@mui/material/IconButton'; +import { ToolbarAddItem } from './ToolbarAddItem'; +import { ToolbarColumnsItem } from './ToolbarColumnsItem'; +import { ToolbarSortItem } from './ToolbarSortItem'; +import { ToolbarDensityItem } from './ToolbarDensityItem'; +import { ToolbarFilterItem } from './ToolbarFilterItem'; +import { ToolbarButton } from './ToolbarButton'; +import { DrawerProps } from './Drawer'; + +export interface ToolbarProps extends GridToolbarProps { + container?: DrawerProps['container']; + listView?: boolean; + handleDelete?: (ids: GridRowId[]) => void; + handleUpload?: (event: React.ChangeEvent) => void; +} + +export function Toolbar(props: ToolbarProps) { + const { listView = false, container, handleUpload, handleDelete } = props; + const apiRef = useGridApiContext(); + const selectedRows = useGridSelector(apiRef, selectedGridRowsSelector); + const selectionCount = selectedRows.size; + const showSelectionOptions = selectionCount > 0; + + const handleClearSelection = () => { + apiRef.current.setRowSelectionModel([]); + }; + + const handleDeleteSelectedRows = () => { + handleClearSelection(); + handleDelete?.(Array.from(selectedRows.keys())); + }; + + const itemProps = { + listView, + container, + }; + + return ( + + {showSelectionOptions ? ( + + + + + + {selectionCount} selected + + + + + + ) : ( + + + + + + + + + + + + )} + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarAddItem.js b/docs/data/data-grid/list-view/components/ToolbarAddItem.js new file mode 100644 index 000000000000..48053f1bb08f --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarAddItem.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import AddIcon from '@mui/icons-material/Add'; +import CloudUploadIcon from '@mui/icons-material/CloudUpload'; +import NewFolderIcon from '@mui/icons-material/CreateNewFolder'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; + +import { ToolbarButton } from './ToolbarButton'; +import { Drawer, DrawerHeader } from './Drawer'; + +const ListItemUploadButton = React.forwardRef( + function ListItemUploadButton(props, ref) { + const { children, ...other } = props; + return ( + + {children} + + ); + }, +); + +export function ToolbarAddItem(props) { + const { container } = props; + const [open, setOpen] = React.useState(false); + const { handleUpload, listView } = props; + + const handleFileSelect = (event) => { + handleUpload?.(event); + setOpen(false); + }; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Add new + + + + + + + + + Upload file + + + + + + {}} disabled> + + + + New folder + + + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarAddItem.tsx b/docs/data/data-grid/list-view/components/ToolbarAddItem.tsx new file mode 100644 index 000000000000..a3c43ffda857 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarAddItem.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import AddIcon from '@mui/icons-material/Add'; +import CloudUploadIcon from '@mui/icons-material/CloudUpload'; +import NewFolderIcon from '@mui/icons-material/CreateNewFolder'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import { ButtonProps } from '@mui/material/Button'; +import { ToolbarButton } from './ToolbarButton'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; + +const ListItemUploadButton = React.forwardRef( + function ListItemUploadButton(props, ref) { + const { children, ...other } = props; + return ( + + {children} + + ); + }, +); + +export interface ToolbarAddItemProps { + container: DrawerProps['container']; + listView: boolean; + handleUpload?: (event: React.ChangeEvent) => void; +} + +export function ToolbarAddItem(props: ToolbarAddItemProps) { + const { container } = props; + const [open, setOpen] = React.useState(false); + const { handleUpload, listView } = props; + + const handleFileSelect = (event: React.ChangeEvent) => { + handleUpload?.(event); + setOpen(false); + }; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Add new + + + + + + + + + Upload file + + + + + + {}} disabled> + + + + New folder + + + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarButton.js b/docs/data/data-grid/list-view/components/ToolbarButton.js new file mode 100644 index 000000000000..7ed7b6f21a51 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarButton.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import IconButton from '@mui/material/IconButton'; + +export function ToolbarButton(props) { + const { className, children, ...other } = props; + return {children}; +} diff --git a/docs/data/data-grid/list-view/components/ToolbarButton.tsx b/docs/data/data-grid/list-view/components/ToolbarButton.tsx new file mode 100644 index 000000000000..48b5dcf1ed30 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarButton.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import IconButton from '@mui/material/IconButton'; +import { ButtonProps } from '@mui/material/Button'; + +export function ToolbarButton(props: ButtonProps) { + const { className, children, ...other } = props; + return {children}; +} diff --git a/docs/data/data-grid/list-view/components/ToolbarColumnsItem.js b/docs/data/data-grid/list-view/components/ToolbarColumnsItem.js new file mode 100644 index 000000000000..e6fe57471fd5 --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarColumnsItem.js @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { + GridColumnIcon, + useGridApiContext, + useGridSelector, + gridColumnDefinitionsSelector, + gridColumnVisibilityModelSelector, +} from '@mui/x-data-grid-premium'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import Typography from '@mui/material/Typography'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import { ToolbarButton } from './ToolbarButton'; +import { Drawer, DrawerHeader } from './Drawer'; + +export function ToolbarColumnsItem(props) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const columns = useGridSelector(apiRef, gridColumnDefinitionsSelector); + const columnVisibilityModel = useGridSelector( + apiRef, + gridColumnVisibilityModelSelector, + ); + + const toggleFieldVisibility = (field) => { + apiRef.current.setColumnVisibility( + field, + columnVisibilityModel[field] === false, + ); + }; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Fields + + + + {columns.map((column) => { + const isVisible = columnVisibilityModel[column.field] !== false; + return ( + + toggleFieldVisibility(column.field)} + disabled={column.hideable === false} + > + + {isVisible ? ( + + ) : ( + + )} + + {column.headerName || column.field} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarColumnsItem.tsx b/docs/data/data-grid/list-view/components/ToolbarColumnsItem.tsx new file mode 100644 index 000000000000..f4f55499d06e --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarColumnsItem.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import { + GridColumnIcon, + useGridApiContext, + useGridSelector, + gridColumnDefinitionsSelector, + gridColumnVisibilityModelSelector, +} from '@mui/x-data-grid-premium'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import Typography from '@mui/material/Typography'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import { ToolbarButton } from './ToolbarButton'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; + +interface ToolbarColumnsItemProps { + listView: boolean; + container: DrawerProps['container']; +} + +export function ToolbarColumnsItem(props: ToolbarColumnsItemProps) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const columns = useGridSelector(apiRef, gridColumnDefinitionsSelector); + const columnVisibilityModel = useGridSelector( + apiRef, + gridColumnVisibilityModelSelector, + ); + + const toggleFieldVisibility = (field: string) => { + apiRef.current.setColumnVisibility( + field, + columnVisibilityModel[field] === false, + ); + }; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Fields + + + + {columns.map((column) => { + const isVisible = columnVisibilityModel[column.field] !== false; + return ( + + toggleFieldVisibility(column.field)} + disabled={column.hideable === false} + > + + {isVisible ? ( + + ) : ( + + )} + + {column.headerName || column.field} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarDensityItem.js b/docs/data/data-grid/list-view/components/ToolbarDensityItem.js new file mode 100644 index 000000000000..afb8447721ee --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarDensityItem.js @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { + GridViewStreamIcon, + useGridApiContext, + useGridSelector, + gridDensitySelector, + GridCheckIcon, + GridViewHeadlineIcon, + GridTableRowsIcon, +} from '@mui/x-data-grid-premium'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Typography from '@mui/material/Typography'; +import { Drawer, DrawerHeader } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; + +const DENSITY_ICONS = { + compact: GridViewHeadlineIcon, + standard: GridTableRowsIcon, + comfortable: GridViewStreamIcon, +}; + +const DENSITY_OPTIONS = [ + { + label: 'Compact', + value: 'compact', + }, + { + label: 'Standard', + value: 'standard', + }, + { + label: 'Comfortable', + value: 'comfortable', + }, +]; + +export function ToolbarDensityItem(props) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const density = useGridSelector(apiRef, gridDensitySelector); + + const handleDensityChange = (value) => { + apiRef.current.setDensity(value); + setOpen(false); + }; + + const Icon = DENSITY_ICONS[density]; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Density + + + + {DENSITY_OPTIONS.map((option) => { + const isActive = density === option.value; + + return ( + + handleDensityChange(option.value)}> + {isActive ? : null} + {option.label} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarDensityItem.tsx b/docs/data/data-grid/list-view/components/ToolbarDensityItem.tsx new file mode 100644 index 000000000000..8f9ce7a1846e --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarDensityItem.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import { + GridViewStreamIcon, + useGridApiContext, + useGridSelector, + GridDensityOption, + gridDensitySelector, + GridCheckIcon, + GridDensity, + GridViewHeadlineIcon, + GridTableRowsIcon, +} from '@mui/x-data-grid-premium'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Typography from '@mui/material/Typography'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; + +const DENSITY_ICONS = { + compact: GridViewHeadlineIcon, + standard: GridTableRowsIcon, + comfortable: GridViewStreamIcon, +}; + +const DENSITY_OPTIONS: Omit[] = [ + { + label: 'Compact', + value: 'compact', + }, + { + label: 'Standard', + value: 'standard', + }, + { + label: 'Comfortable', + value: 'comfortable', + }, +]; + +interface ToolbarDensityItemProps { + listView: boolean; + container: DrawerProps['container']; +} + +export function ToolbarDensityItem(props: ToolbarDensityItemProps) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const density = useGridSelector(apiRef, gridDensitySelector); + + const handleDensityChange = (value: GridDensity) => { + apiRef.current.setDensity(value); + setOpen(false); + }; + + const Icon = DENSITY_ICONS[density]; + + return ( + + setOpen(true)}> + + + + setOpen(false)} + > + + Density + + + + {DENSITY_OPTIONS.map((option) => { + const isActive = density === option.value; + + return ( + + handleDensityChange(option.value)}> + {isActive ? : null} + {option.label} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarFilterItem.js b/docs/data/data-grid/list-view/components/ToolbarFilterItem.js new file mode 100644 index 000000000000..06cdb3c4690e --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarFilterItem.js @@ -0,0 +1,217 @@ +import * as React from 'react'; +import { + GridFilterListIcon, + useGridApiContext, + useGridSelector, + gridFilterActiveItemsSelector, + GridCheckIcon, +} from '@mui/x-data-grid-premium'; +import Badge from '@mui/material/Badge'; +import Typography from '@mui/material/Typography'; +import Divider from '@mui/material/Divider'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListSubheader from '@mui/material/ListSubheader'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import FilterAltOffIcon from '@mui/icons-material/FilterAltOff'; +import { Drawer, DrawerHeader } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; + +import { FILE_TYPES } from '../constants'; + +const DATE_MODIFIED_FILTERS = [ + { + label: 'All', + id: 'all', + }, + { + label: 'Today', + id: 'today', + operator: 'onOrAfter', + value: new Date(new Date().setHours(0, 0, 0, 0)).toISOString(), + }, + { + label: 'Last week', + id: 'last-week', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, + { + label: 'Last month', + id: 'last-month', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, + { + label: 'Last 3 months', + id: 'last-3-months', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 3 * 30 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, +]; + +export function ToolbarFilterItem(props) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const activeFilters = useGridSelector(apiRef, gridFilterActiveItemsSelector); + const currentFileTypeFilter = + activeFilters.filter((filter) => filter.field === 'type')?.[0]?.value ?? []; + const currentDateModifiedFilter = activeFilters.find( + (filter) => filter.field === 'updatedAt', + ); + + const applyDateModifiedFilter = (filterItem) => { + if (currentDateModifiedFilter) { + apiRef.current.deleteFilterItem(currentDateModifiedFilter); + } + + apiRef.current.upsertFilterItem({ + field: 'updatedAt', + ...filterItem, + }); + }; + + const resetDateModifiedFilter = () => { + if (currentDateModifiedFilter) { + apiRef.current.deleteFilterItem(currentDateModifiedFilter); + } + }; + + const applyFileTypeFilter = (fileType, enable) => { + apiRef.current.upsertFilterItem({ + id: 'file-type', + field: 'type', + operator: 'isAnyOf', + value: enable + ? [...currentFileTypeFilter, fileType] + : currentFileTypeFilter.filter((type) => type !== fileType), + }); + }; + + const clearFilters = () => { + apiRef.current.setFilterModel({ + items: [], + }); + }; + + return ( + + setOpen(true)}> + + + + + + setOpen(false)} + > + + Filters + + + + Date modified + + } + > + {DATE_MODIFIED_FILTERS.map((option) => { + const isActive = + option.id === 'all' + ? !currentDateModifiedFilter + : activeFilters.some((filter) => filter.id === option.id); + + return ( + + + option.id === 'all' + ? resetDateModifiedFilter() + : applyDateModifiedFilter({ + id: option.id, + operator: option.operator, + value: option.value, + }) + } + > + {isActive ? : null} + {option.label} + + + ); + })} + + + + + File types + + } + > + {FILE_TYPES.map((type) => { + const isActive = currentFileTypeFilter.includes(type); + + return ( + + applyFileTypeFilter(type, !isActive)}> + + {isActive ? ( + + ) : ( + + )} + + {type} + + + ); + })} + + + + + + + + + + Clear filters + + + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarFilterItem.tsx b/docs/data/data-grid/list-view/components/ToolbarFilterItem.tsx new file mode 100644 index 000000000000..2bb3040df9cd --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarFilterItem.tsx @@ -0,0 +1,225 @@ +import * as React from 'react'; +import { + GridFilterListIcon, + useGridApiContext, + useGridSelector, + gridFilterActiveItemsSelector, + GridCheckIcon, + GridFilterItem, +} from '@mui/x-data-grid-premium'; +import Badge from '@mui/material/Badge'; +import Typography from '@mui/material/Typography'; +import Divider from '@mui/material/Divider'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListSubheader from '@mui/material/ListSubheader'; +import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import FilterAltOffIcon from '@mui/icons-material/FilterAltOff'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; +import { FileType } from '../types'; +import { FILE_TYPES } from '../constants'; + +const DATE_MODIFIED_FILTERS = [ + { + label: 'All', + id: 'all', + }, + { + label: 'Today', + id: 'today', + operator: 'onOrAfter', + value: new Date(new Date().setHours(0, 0, 0, 0)).toISOString(), + }, + { + label: 'Last week', + id: 'last-week', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, + { + label: 'Last month', + id: 'last-month', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, + { + label: 'Last 3 months', + id: 'last-3-months', + operator: 'onOrAfter', + value: new Date( + new Date(Date.now() - 3 * 30 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0), + ).toISOString(), + }, +]; + +interface ToolbarFilterItemProps { + listView: boolean; + container: DrawerProps['container']; +} + +export function ToolbarFilterItem(props: ToolbarFilterItemProps) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const activeFilters = useGridSelector(apiRef, gridFilterActiveItemsSelector); + const currentFileTypeFilter = + activeFilters.filter((filter) => filter.field === 'type')?.[0]?.value ?? []; + const currentDateModifiedFilter = activeFilters.find( + (filter) => filter.field === 'updatedAt', + ); + + const applyDateModifiedFilter = (filterItem: Omit) => { + if (currentDateModifiedFilter) { + apiRef.current.deleteFilterItem(currentDateModifiedFilter); + } + + apiRef.current.upsertFilterItem({ + field: 'updatedAt', + ...filterItem, + }); + }; + + const resetDateModifiedFilter = () => { + if (currentDateModifiedFilter) { + apiRef.current.deleteFilterItem(currentDateModifiedFilter); + } + }; + + const applyFileTypeFilter = (fileType: FileType, enable: boolean) => { + apiRef.current.upsertFilterItem({ + id: 'file-type', + field: 'type', + operator: 'isAnyOf', + value: enable + ? [...currentFileTypeFilter, fileType] + : currentFileTypeFilter.filter((type: string) => type !== fileType), + }); + }; + + const clearFilters = () => { + apiRef.current.setFilterModel({ + items: [], + }); + }; + + return ( + + setOpen(true)}> + + + + + + setOpen(false)} + > + + Filters + + + + Date modified + + } + > + {DATE_MODIFIED_FILTERS.map((option) => { + const isActive = + option.id === 'all' + ? !currentDateModifiedFilter + : activeFilters.some((filter) => filter.id === option.id); + + return ( + + + option.id === 'all' + ? resetDateModifiedFilter() + : applyDateModifiedFilter({ + id: option.id, + operator: option.operator!, + value: option.value, + }) + } + > + {isActive ? : null} + {option.label} + + + ); + })} + + + + + + File types + + } + > + {FILE_TYPES.map((type) => { + const isActive = currentFileTypeFilter.includes(type); + + return ( + + applyFileTypeFilter(type, !isActive)}> + + {isActive ? ( + + ) : ( + + )} + + {type} + + + ); + })} + + + + + + + + + + + Clear filters + + + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarSortItem.js b/docs/data/data-grid/list-view/components/ToolbarSortItem.js new file mode 100644 index 000000000000..91c07622847f --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarSortItem.js @@ -0,0 +1,93 @@ +import * as React from 'react'; +import { + useGridApiContext, + GridArrowUpwardIcon, + GridArrowDownwardIcon, + useGridSelector, + gridSortModelSelector, + gridColumnDefinitionsSelector, +} from '@mui/x-data-grid-premium'; +import SwapVertIcon from '@mui/icons-material/SwapVert'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Typography from '@mui/material/Typography'; +import Badge from '@mui/material/Badge'; +import { Drawer, DrawerHeader } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; + +export function ToolbarSortItem(props) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const fields = useGridSelector(apiRef, gridColumnDefinitionsSelector); + const sortModel = useGridSelector(apiRef, gridSortModelSelector); + const sortableFields = fields.filter((field) => field.sortable); + const sortCount = sortModel.length; + + const handleSortChange = (field, sort) => { + apiRef.current.sortColumn(field, sort, true); + }; + + return ( + + setOpen(true)}> + + + + + + setOpen(false)} + > + + Sort by + + + + {sortableFields.map((field) => { + const fieldIndexInSortModel = sortModel.findIndex( + (sort) => sort.field === field.field, + ); + const fieldInSortModel = sortModel[fieldIndexInSortModel]; + let nextSort = 'asc'; + + if (fieldInSortModel) { + nextSort = fieldInSortModel.sort === 'asc' ? 'desc' : null; + } + + return ( + + handleSortChange(field.field, nextSort)} + > + + {fieldInSortModel && ( + 1 ? fieldIndexInSortModel + 1 : null + } + > + {fieldInSortModel.sort === 'asc' ? ( + + ) : ( + + )} + + )} + + {field.headerName} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/components/ToolbarSortItem.tsx b/docs/data/data-grid/list-view/components/ToolbarSortItem.tsx new file mode 100644 index 000000000000..4c3633f6ec3d --- /dev/null +++ b/docs/data/data-grid/list-view/components/ToolbarSortItem.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { + useGridApiContext, + GridArrowUpwardIcon, + GridArrowDownwardIcon, + useGridSelector, + gridSortModelSelector, + gridColumnDefinitionsSelector, + GridSortDirection, +} from '@mui/x-data-grid-premium'; +import SwapVertIcon from '@mui/icons-material/SwapVert'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Typography from '@mui/material/Typography'; +import Badge from '@mui/material/Badge'; +import { Drawer, DrawerHeader, DrawerProps } from './Drawer'; +import { ToolbarButton } from './ToolbarButton'; + +interface ToolbarSortItemProps { + listView: boolean; + container: DrawerProps['container']; +} + +export function ToolbarSortItem(props: ToolbarSortItemProps) { + const { listView, container } = props; + const [open, setOpen] = React.useState(false); + const apiRef = useGridApiContext(); + const fields = useGridSelector(apiRef, gridColumnDefinitionsSelector); + const sortModel = useGridSelector(apiRef, gridSortModelSelector); + const sortableFields = fields.filter((field) => field.sortable); + const sortCount = sortModel.length; + + const handleSortChange = (field: string, sort: GridSortDirection) => { + apiRef.current.sortColumn(field, sort, true); + }; + + return ( + + setOpen(true)}> + + + + + + setOpen(false)} + > + + Sort by + + + + {sortableFields.map((field) => { + const fieldIndexInSortModel = sortModel.findIndex( + (sort) => sort.field === field.field, + ); + const fieldInSortModel = sortModel[fieldIndexInSortModel]; + let nextSort: GridSortDirection = 'asc'; + + if (fieldInSortModel) { + nextSort = fieldInSortModel.sort === 'asc' ? 'desc' : null; + } + + return ( + + handleSortChange(field.field, nextSort)} + > + + {fieldInSortModel && ( + 1 ? fieldIndexInSortModel + 1 : null + } + > + {fieldInSortModel.sort === 'asc' ? ( + + ) : ( + + )} + + )} + + {field.headerName} + + + ); + })} + + + + ); +} diff --git a/docs/data/data-grid/list-view/constants.ts b/docs/data/data-grid/list-view/constants.ts new file mode 100644 index 000000000000..ee67fc4fb92c --- /dev/null +++ b/docs/data/data-grid/list-view/constants.ts @@ -0,0 +1,17 @@ +import { FileType } from './types'; + +export const FILE_TYPES: FileType[] = [ + 'pdf', + 'docx', + 'txt', + 'mp4', + 'mov', + 'webm', + 'jpg', + 'jpeg', + 'png', + 'gif', + 'tiff', + 'webp', + 'zip', +]; diff --git a/docs/data/data-grid/list-view/data.ts b/docs/data/data-grid/list-view/data.ts new file mode 100644 index 000000000000..3e54d301d2c7 --- /dev/null +++ b/docs/data/data-grid/list-view/data.ts @@ -0,0 +1,204 @@ +import { randomId } from '@mui/x-data-grid-generator'; +import { GridRowModel } from '@mui/x-data-grid-premium'; +import { RowModel } from './types'; + +export const INITIAL_ROWS: GridRowModel[] = [ + { + id: randomId(), + type: 'zip', + name: 'archive.zip', + description: 'Compressed archive of project files', + size: 128_313_213, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2023-09-15T08:30:00').toISOString(), + updatedAt: new Date('2023-09-15T08:30:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'docx', + name: 'invoice-322.docx', + description: 'Invoice document for client 322', + size: 1_694_986, + createdBy: 'José Freitas', + createdAt: new Date('2024-01-18T11:30:00').toISOString(), + updatedAt: new Date().toISOString(), // Today + state: 'uploaded', + }, + { + id: randomId(), + type: 'png', + name: 'screenshot_2024-02-14_12-34-56.png', + description: 'Screenshot of application interface', + size: 522_078, + createdBy: 'José Freitas', + createdAt: new Date('2024-02-14T12:35:16').toISOString(), + updatedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(), // Last week + state: 'uploaded', + }, + { + id: randomId(), + type: 'mp4', + name: 'strategy-meeting.mp4', + description: 'Recording of the strategy planning meeting', + size: 2_442_044, + createdBy: 'José Freitas', + createdAt: new Date('2023-12-05T15:40:30').toISOString(), + updatedAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString(), // Last month + state: 'uploaded', + }, + { + id: randomId(), + type: 'docx', + name: 'project-proposal.docx', + description: 'Detailed project proposal document', + size: 3_567_890, + createdBy: 'Olivier Tassinari', + createdAt: new Date('2024-03-01T09:15:00').toISOString(), + updatedAt: new Date('2024-03-02T14:30:45').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'png', + name: 'logo-design-final.png', + description: 'Final version of the company logo design', + size: 1_234_567, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2024-02-28T16:20:00').toISOString(), + updatedAt: new Date('2024-02-28T16:20:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'mp4', + name: 'product-demo.mp4', + description: 'Video demonstration of the new product features', + size: 15_789_012, + createdBy: 'Olivier Tassinari', + createdAt: new Date('2024-02-25T11:45:00').toISOString(), + updatedAt: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000).toISOString(), // Last 3 months + state: 'uploaded', + }, + { + id: randomId(), + type: 'zip', + name: 'project-assets.zip', + description: 'Compressed folder containing all project assets', + size: 87_654_321, + createdBy: 'José Freitas', + createdAt: new Date('2024-03-03T13:00:00').toISOString(), + updatedAt: new Date('2024-03-03T13:00:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'docx', + name: 'meeting-minutes-2024-03-10.docx', + description: 'Minutes from the team meeting on March 10, 2024', + size: 567_890, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2024-03-10T14:00:00').toISOString(), + updatedAt: new Date('2024-03-10T16:30:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'png', + name: 'ui-mockup-v2.png', + description: 'Updated user interface mockup', + size: 3_456_789, + createdBy: 'Olivier Tassinari', + createdAt: new Date('2024-03-05T10:20:00').toISOString(), + updatedAt: new Date('2024-03-05T10:20:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'mp4', + name: 'user-feedback-session.mp4', + description: 'Recording of user feedback session', + size: 234_567_890, + createdBy: 'José Freitas', + createdAt: new Date('2024-03-08T13:45:00').toISOString(), + updatedAt: new Date('2024-03-08T15:30:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'zip', + name: 'legacy-codebase.zip', + description: 'Archive of the legacy project codebase', + size: 567_890_123, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2024-02-20T09:00:00').toISOString(), + updatedAt: new Date('2024-02-20T09:00:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'docx', + name: 'q1-2024-report.docx', + description: 'Quarterly report for Q1 2024', + size: 4_567_890, + createdBy: 'Olivier Tassinari', + createdAt: new Date('2024-03-31T23:59:59').toISOString(), + updatedAt: new Date('2024-04-01T10:15:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'png', + name: 'data-visualization.png', + description: 'Chart showing project progress', + size: 789_012, + createdBy: 'José Freitas', + createdAt: new Date('2024-03-15T11:30:00').toISOString(), + updatedAt: new Date('2024-03-15T11:30:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'mp4', + name: 'code-review-session.mp4', + description: 'Recording of code review meeting', + size: 345_678_901, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2024-03-20T14:00:00').toISOString(), + updatedAt: new Date('2024-03-20T16:45:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'zip', + name: 'design-assets-v3.zip', + description: 'Compressed folder of updated design assets', + size: 98_765_432, + createdBy: 'Olivier Tassinari', + createdAt: new Date('2024-03-25T09:30:00').toISOString(), + updatedAt: new Date('2024-03-25T09:30:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'docx', + name: 'api-documentation.docx', + description: 'Comprehensive API documentation', + size: 2_345_678, + createdBy: 'José Freitas', + createdAt: new Date('2024-03-28T16:20:00').toISOString(), + updatedAt: new Date('2024-03-29T11:45:00').toISOString(), + state: 'uploaded', + }, + { + id: randomId(), + type: 'png', + name: 'error-screenshot.png', + description: 'Screenshot of error message for debugging', + size: 345_678, + createdBy: 'Kenan Yusuf', + createdAt: new Date('2024-03-30T08:15:00').toISOString(), + updatedAt: new Date('2024-03-30T08:15:00').toISOString(), + state: 'uploaded', + }, +]; diff --git a/docs/data/data-grid/list-view/list-view.md b/docs/data/data-grid/list-view/list-view.md new file mode 100644 index 000000000000..11f4b7ca3e88 --- /dev/null +++ b/docs/data/data-grid/list-view/list-view.md @@ -0,0 +1,29 @@ +--- +title: Data Grid - List view +--- + +# Data Grid - List view [](/x/introduction/licensing/#pro-plan 'Pro plan') + +

Display data in a single-column list view. Can be used to present a more compact grid on smaller screens and mobile devices.

+ +List view can be enabled by providing the `unstable_listView` prop. + +Unlike the default grid view, the list view makes no assumptions on how data is presented to end users. + +In order to display data in a list view, a `unstable_listColumn` prop must be provided with a `renderCell` function. + +:::warning +This feature is under development and is marked as **unstable**. While you can use the list view feature in production, the API could change in the future. +::: + +{{"demo": "ListView.js", "bg": "inline"}} + +## Advanced usage + +The list view feature can be combined with [custom subcomponents](/x/react-data-grid/components/) to provide an improved user experience on small screens. + +{{"demo": "ListViewAdvanced.js", "bg": "inline", "iframe": true, "maxWidth": 360, "height": 600, "hideToolbar": true}} + +:::info +See the code for this demo in [CodeSandbox](https://codesandbox.io/p/sandbox/x-react-data-grid-list-view-zmkzhz). +::: diff --git a/docs/data/data-grid/list-view/types.ts b/docs/data/data-grid/list-view/types.ts new file mode 100644 index 000000000000..fd0975f3a0b9 --- /dev/null +++ b/docs/data/data-grid/list-view/types.ts @@ -0,0 +1,30 @@ +import { GridRowId } from '@mui/x-data-grid-premium'; + +export type FileType = + | 'pdf' + | 'docx' + | 'txt' + | 'mp4' + | 'mov' + | 'webm' + | 'jpg' + | 'jpeg' + | 'png' + | 'gif' + | 'tiff' + | 'webp' + | 'zip'; + +export type FileState = 'uploaded' | 'pending'; + +export type RowModel = { + id: GridRowId; + type: FileType; + name: string; + description: string; + size: number; + createdBy: string; + createdAt: string; + updatedAt: string; + state: FileState; +}; diff --git a/docs/data/data-grid/list-view/utils.ts b/docs/data/data-grid/list-view/utils.ts new file mode 100644 index 000000000000..08c93be0d27c --- /dev/null +++ b/docs/data/data-grid/list-view/utils.ts @@ -0,0 +1,56 @@ +export function formatDate(value: string | null) { + if (!value) { + return '—'; + } + const date = new Date(value); + const formatter = new Intl.DateTimeFormat('en-US', { + month: 'short', + day: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); + return formatter.format(date); +} + +export function formatSize(size: number) { + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let unitIndex = 0; + let formattedSize = size; + + while (formattedSize >= 1024 && unitIndex < units.length - 1) { + formattedSize /= 1024; + unitIndex += 1; + } + + return `${formattedSize.toFixed(2)} ${units[unitIndex]}`; +} + +export function stringToColor(string: string) { + let hash = 0; + let i: number; + + /* eslint-disable no-bitwise */ + for (i = 0; i < string.length; i += 1) { + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } + + let color = '#'; + + for (i = 0; i < 3; i += 1) { + const value = (hash >> (i * 8)) & 0xff; + color += `00${value.toString(16)}`.slice(-2); + } + /* eslint-enable no-bitwise */ + + return color; +} + +export function stringAvatar(name: string) { + return { + sx: { + bgcolor: stringToColor(name), + }, + children: `${name.split(' ')[0][0]}${name.split(' ')[1][0]}`, + }; +} diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 890060cd717b..b952b8a5b722 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -110,6 +110,12 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/row-grouping', plan: 'premium' }, { pathname: '/x/react-data-grid/aggregation', plan: 'premium' }, { pathname: '/x/react-data-grid/pivoting', plan: 'premium', planned: true }, + { + pathname: '/x/react-data-grid/list-view', + title: 'List view', + plan: 'pro', + unstable: true, + }, { pathname: '/x/react-data-grid/server-side-data-group', title: 'Server-side data', diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index b9c22994df5f..478f8854a196 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -649,6 +649,13 @@ }, "throttleRowsMs": { "type": { "name": "number" }, "default": "0" }, "treeData": { "type": { "name": "bool" }, "default": "false" }, + "unstable_listColumn": { + "type": { + "name": "shape", + "description": "{ align?: 'center'
| 'left'
| 'right', cellClassName?: func
| string, display?: 'flex'
| 'text', field: string, renderCell?: func }" + } + }, + "unstable_listView": { "type": { "name": "bool" } }, "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPremium", diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index f0e2cc6f48c4..54a8ab54294e 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -580,6 +580,13 @@ }, "throttleRowsMs": { "type": { "name": "number" }, "default": "0" }, "treeData": { "type": { "name": "bool" }, "default": "false" }, + "unstable_listColumn": { + "type": { + "name": "shape", + "description": "{ align?: 'center'
| 'left'
| 'right', cellClassName?: func
| string, display?: 'flex'
| 'text', field: string, renderCell?: func }" + } + }, + "unstable_listView": { "type": { "name": "bool" } }, "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPro", diff --git a/docs/pages/x/react-data-grid/list-view.js b/docs/pages/x/react-data-grid/list-view.js new file mode 100644 index 000000000000..cb417266ff2e --- /dev/null +++ b/docs/pages/x/react-data-grid/list-view.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/list-view/list-view.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 78865102281c..5e463ec7b112 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -661,6 +661,12 @@ "treeData": { "description": "If true, the rows will be gathered in a tree structure according to the getTreeDataPath prop." }, + "unstable_listColumn": { + "description": "Definition of the column rendered when the unstable_listView prop is enabled." + }, + "unstable_listView": { + "description": "If true, displays the data in a list view. Use in combination with unstable_listColumn." + }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." } diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index a0509f2e4549..b2b762362095 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -599,6 +599,12 @@ "treeData": { "description": "If true, the rows will be gathered in a tree structure according to the getTreeDataPath prop." }, + "unstable_listColumn": { + "description": "Definition of the column rendered when the unstable_listView prop is enabled." + }, + "unstable_listView": { + "description": "If true, displays the data in a list view. Use in combination with unstable_listColumn." + }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." } diff --git a/packages/x-data-grid-generator/src/columns/employees.columns.tsx b/packages/x-data-grid-generator/src/columns/employees.columns.tsx index 1f4ea311b299..4f29ce843a4a 100644 --- a/packages/x-data-grid-generator/src/columns/employees.columns.tsx +++ b/packages/x-data-grid-generator/src/columns/employees.columns.tsx @@ -170,7 +170,7 @@ export const getEmployeeColumns = (): GridColDefGenerator[] => [ if (!value || typeof value !== 'number') { return value; } - return `${value.toLocaleString()}$`; + return `$${value.toLocaleString()}`; }, }, ]; diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 86be3303b736..0f05ca94e442 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -1106,6 +1106,21 @@ DataGridPremiumRaw.propTypes = { get: PropTypes.func.isRequired, set: PropTypes.func.isRequired, }), + /** + * Definition of the column rendered when the `unstable_listView` prop is enabled. + */ + unstable_listColumn: PropTypes.shape({ + align: PropTypes.oneOf(['center', 'left', 'right']), + cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + display: PropTypes.oneOf(['flex', 'text']), + field: PropTypes.string.isRequired, + renderCell: PropTypes.func, + }), + /** + * If `true`, displays the data in a list view. + * Use in combination with `unstable_listColumn`. + */ + unstable_listView: PropTypes.bool, unstable_onDataSourceError: PropTypes.func, /** * If `true`, the Data Grid will auto span the cells over the rows having the same value. diff --git a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx index 12899dd06ce2..8106dec99163 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx @@ -70,6 +70,8 @@ import { dataSourceStateInitializer, useGridRowSpanning, rowSpanningStateInitializer, + useGridListView, + listViewStateInitializer, } from '@mui/x-data-grid-pro/internals'; import { GridApiPremium, GridPrivateApiPremium } from '../models/gridApiPremium'; import { DataGridPremiumProcessedProps } from '../models/dataGridPremiumProps'; @@ -143,6 +145,7 @@ export const useDataGridPremiumComponent = ( useGridInitializeState(columnGroupsStateInitializer, apiRef, props); useGridInitializeState(virtualizationStateInitializer, apiRef, props); useGridInitializeState(dataSourceStateInitializer, apiRef, props); + useGridInitializeState(listViewStateInitializer, apiRef, props); useGridRowGrouping(apiRef, props); useGridHeaderFiltering(apiRef, props); @@ -156,7 +159,7 @@ export const useDataGridPremiumComponent = ( useGridColumns(apiRef, props); useGridRows(apiRef, props); useGridRowSpanning(apiRef, props); - useGridParamsApi(apiRef); + useGridParamsApi(apiRef, props); useGridDetailPanel(apiRef, props); useGridColumnSpanning(apiRef); useGridColumnGrouping(apiRef, props); @@ -185,6 +188,7 @@ export const useDataGridPremiumComponent = ( useGridStatePersistence(apiRef); useGridDataSource(apiRef, props); useGridVirtualization(apiRef, props); + useGridListView(apiRef, props); return apiRef; }; diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 8cea3a676d0d..cf10a788ce68 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -1005,6 +1005,21 @@ DataGridProRaw.propTypes = { get: PropTypes.func.isRequired, set: PropTypes.func.isRequired, }), + /** + * Definition of the column rendered when the `unstable_listView` prop is enabled. + */ + unstable_listColumn: PropTypes.shape({ + align: PropTypes.oneOf(['center', 'left', 'right']), + cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + display: PropTypes.oneOf(['flex', 'text']), + field: PropTypes.string.isRequired, + renderCell: PropTypes.func, + }), + /** + * If `true`, displays the data in a list view. + * Use in combination with `unstable_listColumn`. + */ + unstable_listView: PropTypes.bool, unstable_onDataSourceError: PropTypes.func, /** * If `true`, the Data Grid will auto span the cells over the rows having the same value. diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index 6b8b06fc21bb..0240d83802fc 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -49,6 +49,8 @@ import { columnResizeStateInitializer, useGridRowSpanning, rowSpanningStateInitializer, + useGridListView, + listViewStateInitializer, } from '@mui/x-data-grid/internals'; import { GridApiPro, GridPrivateApiPro } from '../models/gridApiPro'; import { DataGridProProcessedProps } from '../models/dataGridProProps'; @@ -132,6 +134,7 @@ export const useDataGridProComponent = ( useGridInitializeState(columnGroupsStateInitializer, apiRef, props); useGridInitializeState(virtualizationStateInitializer, apiRef, props); useGridInitializeState(dataSourceStateInitializer, apiRef, props); + useGridInitializeState(listViewStateInitializer, apiRef, props); useGridHeaderFiltering(apiRef, props); useGridTreeData(apiRef, props); @@ -142,7 +145,7 @@ export const useDataGridProComponent = ( useGridColumns(apiRef, props); useGridRows(apiRef, props); useGridRowSpanning(apiRef, props); - useGridParamsApi(apiRef); + useGridParamsApi(apiRef, props); useGridDetailPanel(apiRef, props); useGridColumnSpanning(apiRef); useGridColumnGrouping(apiRef, props); @@ -169,6 +172,7 @@ export const useDataGridProComponent = ( useGridStatePersistence(apiRef); useGridVirtualization(apiRef, props); useGridDataSource(apiRef, props); + useGridListView(apiRef, props); return apiRef; }; diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts index a54285727759..295702230804 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts +++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts @@ -55,6 +55,7 @@ export const DATA_GRID_PRO_PROPS_DEFAULT_VALUES: DataGridProPropsWithDefaultValu rowsLoadingMode: 'client', scrollEndThreshold: 80, treeData: false, + unstable_listView: false, }; const defaultSlots = DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS; diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index 58ba0623f1bd..2c8c8cbce7a9 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -193,7 +193,9 @@ const GridHeaderFilterCell = React.forwardRef; } diff --git a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx index 85f9a09cb3ea..2c888c05b238 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx +++ b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx @@ -57,6 +57,10 @@ import { rowSpanningStateInitializer, useGridRowSpanning, } from '../hooks/features/rows/useGridRowSpanning'; +import { + listViewStateInitializer, + useGridListView, +} from '../hooks/features/listView/useGridListView'; export const useDataGridComponent = ( inputApiRef: React.MutableRefObject | undefined, @@ -93,13 +97,14 @@ export const useDataGridComponent = ( useGridInitializeState(columnMenuStateInitializer, apiRef, props); useGridInitializeState(columnGroupsStateInitializer, apiRef, props); useGridInitializeState(virtualizationStateInitializer, apiRef, props); + useGridInitializeState(listViewStateInitializer, apiRef, props); useGridKeyboardNavigation(apiRef, props); useGridRowSelection(apiRef, props); useGridColumns(apiRef, props); useGridRows(apiRef, props); useGridRowSpanning(apiRef, props); - useGridParamsApi(apiRef); + useGridParamsApi(apiRef, props); useGridColumnSpanning(apiRef); useGridColumnGrouping(apiRef, props); useGridEditing(apiRef, props); @@ -120,6 +125,7 @@ export const useDataGridComponent = ( useGridEvents(apiRef, props); useGridStatePersistence(apiRef); useGridVirtualization(apiRef, props); + useGridListView(apiRef, props); return apiRef; }; diff --git a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts index 1ae57fbf5d8f..7a2d045607e5 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts +++ b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts @@ -22,6 +22,7 @@ const DATA_GRID_FORCED_PROPS: { [key in DataGridForcedPropsKey]?: DataGridProces disableColumnReorder: true, keepColumnPositionIfDraggedOutside: false, signature: 'DataGrid', + unstable_listView: false, }; const defaultSlots = DATA_GRID_DEFAULT_SLOTS_COMPONENTS; diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx index 40fec6af4592..8358424a82a7 100644 --- a/packages/x-data-grid/src/components/GridRow.tsx +++ b/packages/x-data-grid/src/components/GridRow.tsx @@ -419,12 +419,18 @@ const GridRow = React.forwardRef(function GridRow( ), ); } + for (let i = renderContext.firstColumnIndex; i < renderContext.lastColumnIndex; i += 1) { const column = visibleColumns[i]; const indexInSection = i - pinnedColumns.left.length; + if (!column) { + continue; + } + cells.push(getCell(column, indexInSection, i, middleColumnsLength)); } + if (hasVirtualFocusCellRight) { cells.push( getCell( diff --git a/packages/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/x-data-grid/src/components/containers/GridRootStyles.ts index f66edba975ea..86b93de0962d 100644 --- a/packages/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/x-data-grid/src/components/containers/GridRootStyles.ts @@ -535,6 +535,7 @@ export const GridRootStyles = styled('div', { /* Cell styles */ [`& .${c.cell}`]: { + flex: '0 0 auto', height: 'var(--height)', width: 'var(--width)', lineHeight: 'calc(var(--height) - 1px)', // -1px for the border diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx index e5bf5410f4cc..c832d1414553 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx @@ -148,6 +148,11 @@ const GridVirtualScrollbar = React.forwardRef