UNPKG

@mui/x-data-grid

Version:

The community edition of the data grid component (MUI X).

316 lines (304 loc) 14.7 kB
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _extends from "@babel/runtime/helpers/esm/extends"; import _regeneratorRuntime from "@babel/runtime/regenerator"; import * as React from 'react'; import { unstable_ownerDocument as ownerDocument } from '@mui/utils'; import { useGridLogger } from '../../utils/useGridLogger'; import { gridExpandedRowCountSelector } from '../filter/gridFilterSelector'; import { gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector } from '../columns/gridColumnsSelector'; import { gridClasses } from '../../../constants/gridClasses'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getColumnsToExport } from './utils'; import { mergeStateWithPaginationModel } from '../pagination/useGridPagination'; import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; import { GridPrintExportMenuItem } from '../../../components/toolbar/GridToolbarExport'; import { getTotalHeaderHeight } from '../columns/gridColumnsUtils'; import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../colDef/gridCheckboxSelectionColDef'; import { jsx as _jsx } from "react/jsx-runtime"; function raf() { return new Promise(function (resolve) { requestAnimationFrame(function () { resolve(); }); }); } function buildPrintWindow(title) { var iframeEl = document.createElement('iframe'); iframeEl.style.position = 'absolute'; iframeEl.style.width = '0px'; iframeEl.style.height = '0px'; iframeEl.title = title || document.title; return iframeEl; } /** * @requires useGridColumns (state) * @requires useGridFilter (state) * @requires useGridSorting (state) * @requires useGridParamsApi (method) */ export var useGridPrintExport = function useGridPrintExport(apiRef, props) { var logger = useGridLogger(apiRef, 'useGridPrintExport'); var doc = React.useRef(null); var previousGridState = React.useRef(null); var previousColumnVisibility = React.useRef({}); var previousRows = React.useRef([]); React.useEffect(function () { doc.current = ownerDocument(apiRef.current.rootElementRef.current); }, [apiRef]); // Returns a promise because updateColumns triggers state update and // the new state needs to be in place before the grid can be sized correctly var updateGridColumnsForPrint = React.useCallback(function (fields, allColumns, includeCheckboxes) { return new Promise(function (resolve) { var exportedColumnFields = getColumnsToExport({ apiRef: apiRef, options: { fields: fields, allColumns: allColumns } }).map(function (column) { return column.field; }); var columns = gridColumnDefinitionsSelector(apiRef); var newColumnVisibilityModel = {}; columns.forEach(function (column) { newColumnVisibilityModel[column.field] = exportedColumnFields.includes(column.field); }); if (includeCheckboxes) { newColumnVisibilityModel[GRID_CHECKBOX_SELECTION_COL_DEF.field] = true; } apiRef.current.setColumnVisibilityModel(newColumnVisibilityModel); resolve(); }); }, [apiRef]); var updateGridRowsForPrint = React.useCallback(function (getRowsToExport) { var rowsToExportIds = getRowsToExport({ apiRef: apiRef }); var newRows = rowsToExportIds.map(function (id) { return apiRef.current.getRow(id); }); apiRef.current.setRows(newRows); }, [apiRef]); var handlePrintWindowLoad = React.useCallback(function (printWindow, options) { var _querySelector, _querySelector2; var normalizeOptions = _extends({ copyStyles: true, hideToolbar: false, hideFooter: false, includeCheckboxes: false }, options); var printDoc = printWindow.contentDocument; if (!printDoc) { return; } var rowsMeta = gridRowsMetaSelector(apiRef.current.state); var gridRootElement = apiRef.current.rootElementRef.current; var gridClone = gridRootElement.cloneNode(true); // Allow to overflow to not hide the border of the last row var gridMain = gridClone.querySelector(".".concat(gridClasses.main)); gridMain.style.overflow = 'visible'; // See https://support.google.com/chrome/thread/191619088?hl=en&msgid=193009642 gridClone.style.contain = 'size'; var columnHeaders = gridClone.querySelector(".".concat(gridClasses.columnHeaders)); var columnHeadersInner = columnHeaders.querySelector(".".concat(gridClasses.columnHeadersInner)); columnHeadersInner.style.width = '100%'; var gridToolbarElementHeight = ((_querySelector = gridRootElement.querySelector(".".concat(gridClasses.toolbarContainer))) == null ? void 0 : _querySelector.offsetHeight) || 0; var gridFooterElementHeight = ((_querySelector2 = gridRootElement.querySelector(".".concat(gridClasses.footerContainer))) == null ? void 0 : _querySelector2.offsetHeight) || 0; if (normalizeOptions.hideToolbar) { var _gridClone$querySelec; (_gridClone$querySelec = gridClone.querySelector(".".concat(gridClasses.toolbarContainer))) == null || _gridClone$querySelec.remove(); gridToolbarElementHeight = 0; } if (normalizeOptions.hideFooter) { var _gridClone$querySelec2; (_gridClone$querySelec2 = gridClone.querySelector(".".concat(gridClasses.footerContainer))) == null || _gridClone$querySelec2.remove(); gridFooterElementHeight = 0; } // Expand container height to accommodate all rows var computedTotalHeight = rowsMeta.currentPageTotalHeight + getTotalHeaderHeight(apiRef, props.columnHeaderHeight) + gridToolbarElementHeight + gridFooterElementHeight; gridClone.style.height = "".concat(computedTotalHeight, "px"); // The height above does not include grid border width, so we need to exclude it gridClone.style.boxSizing = 'content-box'; // the footer is always being placed at the bottom of the page as if all rows are exported // so if getRowsToExport is being used to only export a subset of rows then we need to // adjust the footer position to be correctly placed at the bottom of the grid if (options != null && options.getRowsToExport) { var gridFooterElement = gridClone.querySelector(".".concat(gridClasses.footerContainer)); gridFooterElement.style.position = 'absolute'; gridFooterElement.style.width = '100%'; gridFooterElement.style.top = "".concat(computedTotalHeight - gridFooterElementHeight, "px"); } // printDoc.body.appendChild(gridClone); should be enough but a clone isolation bug in Safari // prevents us to do it var container = document.createElement('div'); container.appendChild(gridClone); printDoc.body.innerHTML = container.innerHTML; var defaultPageStyle = typeof normalizeOptions.pageStyle === 'function' ? normalizeOptions.pageStyle() : normalizeOptions.pageStyle; if (typeof defaultPageStyle === 'string') { // TODO custom styles should always win var styleElement = printDoc.createElement('style'); styleElement.appendChild(printDoc.createTextNode(defaultPageStyle)); printDoc.head.appendChild(styleElement); } if (normalizeOptions.bodyClassName) { var _printDoc$body$classL; (_printDoc$body$classL = printDoc.body.classList).add.apply(_printDoc$body$classL, _toConsumableArray(normalizeOptions.bodyClassName.split(' '))); } var stylesheetLoadPromises = []; if (normalizeOptions.copyStyles) { var rootCandidate = gridRootElement.getRootNode(); var root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc.current; var headStyleElements = root.querySelectorAll("style, link[rel='stylesheet']"); var _loop = function _loop() { var node = headStyleElements[i]; if (node.tagName === 'STYLE') { var newHeadStyleElements = printDoc.createElement(node.tagName); var sheet = node.sheet; if (sheet) { var styleCSS = ''; // NOTE: for-of is not supported by IE for (var j = 0; j < sheet.cssRules.length; j += 1) { if (typeof sheet.cssRules[j].cssText === 'string') { styleCSS += "".concat(sheet.cssRules[j].cssText, "\r\n"); } } newHeadStyleElements.appendChild(printDoc.createTextNode(styleCSS)); printDoc.head.appendChild(newHeadStyleElements); } } else if (node.getAttribute('href')) { // If `href` tag is empty, avoid loading these links var _newHeadStyleElements = printDoc.createElement(node.tagName); for (var _j = 0; _j < node.attributes.length; _j += 1) { var attr = node.attributes[_j]; if (attr) { _newHeadStyleElements.setAttribute(attr.nodeName, attr.nodeValue || ''); } } stylesheetLoadPromises.push(new Promise(function (resolve) { _newHeadStyleElements.addEventListener('load', function () { return resolve(); }); })); printDoc.head.appendChild(_newHeadStyleElements); } }; for (var i = 0; i < headStyleElements.length; i += 1) { _loop(); } } // Trigger print if (process.env.NODE_ENV !== 'test') { // wait for remote stylesheets to load Promise.all(stylesheetLoadPromises).then(function () { printWindow.contentWindow.print(); }); } }, [apiRef, doc, props.columnHeaderHeight]); var handlePrintWindowAfterPrint = React.useCallback(function (printWindow) { var _previousGridState$cu; // Remove the print iframe doc.current.body.removeChild(printWindow); // Revert grid to previous state apiRef.current.restoreState(previousGridState.current || {}); if (!((_previousGridState$cu = previousGridState.current) != null && (_previousGridState$cu = _previousGridState$cu.columns) != null && _previousGridState$cu.columnVisibilityModel)) { // if the apiRef.current.exportState(); did not exported the column visibility, we update it apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current); } apiRef.current.unstable_setVirtualization(true); apiRef.current.setRows(previousRows.current); // Clear local state previousGridState.current = null; previousColumnVisibility.current = {}; previousRows.current = []; }, [apiRef]); var exportDataAsPrint = React.useCallback( /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(options) { var visibleRowCount, paginationModel, printWindow; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: logger.debug("Export data as Print"); if (apiRef.current.rootElementRef.current) { _context.next = 3; break; } throw new Error('MUI: No grid root element available.'); case 3: previousGridState.current = apiRef.current.exportState(); // It appends that the visibility model is not exported, especially if columnVisibility is not controlled previousColumnVisibility.current = gridColumnVisibilityModelSelector(apiRef); previousRows.current = apiRef.current.getSortedRows(); if (props.pagination) { visibleRowCount = gridExpandedRowCountSelector(apiRef); paginationModel = { page: 0, pageSize: visibleRowCount }; apiRef.current.updateControlState('pagination', // Using signature `DataGridPro` to allow more than 100 rows in the print export mergeStateWithPaginationModel(visibleRowCount, 'DataGridPro', paginationModel)); apiRef.current.forceUpdate(); } _context.next = 9; return updateGridColumnsForPrint(options == null ? void 0 : options.fields, options == null ? void 0 : options.allColumns, options == null ? void 0 : options.includeCheckboxes); case 9: if (options != null && options.getRowsToExport) { updateGridRowsForPrint(options.getRowsToExport); } apiRef.current.unstable_setVirtualization(false); _context.next = 13; return raf(); case 13: // wait for the state changes to take action printWindow = buildPrintWindow(options == null ? void 0 : options.fileName); if (process.env.NODE_ENV === 'test') { doc.current.body.appendChild(printWindow); // In test env, run the all pipeline without waiting for loading handlePrintWindowLoad(printWindow, options); handlePrintWindowAfterPrint(printWindow); } else { printWindow.onload = function () { handlePrintWindowLoad(printWindow, options); var mediaQueryList = printWindow.contentWindow.matchMedia('print'); mediaQueryList.addEventListener('change', function (mql) { var isAfterPrint = mql.matches === false; if (isAfterPrint) { handlePrintWindowAfterPrint(printWindow); } }); }; doc.current.body.appendChild(printWindow); } case 15: case "end": return _context.stop(); } }, _callee); })); return function (_x) { return _ref.apply(this, arguments); }; }(), [props, logger, apiRef, handlePrintWindowLoad, handlePrintWindowAfterPrint, updateGridColumnsForPrint, updateGridRowsForPrint]); var printExportApi = { exportDataAsPrint: exportDataAsPrint }; useGridApiMethod(apiRef, printExportApi, 'public'); /** * PRE-PROCESSING */ var addExportMenuButtons = React.useCallback(function (initialValue, options) { var _options$printOptions; if ((_options$printOptions = options.printOptions) != null && _options$printOptions.disableToolbarButton) { return initialValue; } return [].concat(_toConsumableArray(initialValue), [{ component: /*#__PURE__*/_jsx(GridPrintExportMenuItem, { options: options.printOptions }), componentName: 'printExport' }]); }, []); useGridRegisterPipeProcessor(apiRef, 'exportMenu', addExportMenuButtons); };