UNPKG

@mui/x-data-grid

Version:

The Community plan edition of the Data Grid components (MUI X).

298 lines (285 loc) 14.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useGridPrintExport = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var _utils = require("@mui/utils"); var _useGridLogger = require("../../utils/useGridLogger"); var _gridFilterSelector = require("../filter/gridFilterSelector"); var _gridColumnsSelector = require("../columns/gridColumnsSelector"); var _gridClasses = require("../../../constants/gridClasses"); var _useGridApiMethod = require("../../utils/useGridApiMethod"); var _gridRowsMetaSelector = require("../rows/gridRowsMetaSelector"); var _gridRowsUtils = require("../rows/gridRowsUtils"); var _utils2 = require("./utils"); var _useGridPaginationModel = require("../pagination/useGridPaginationModel"); var _pipeProcessing = require("../../core/pipeProcessing"); var _GridToolbarExport = require("../../../components/toolbar/GridToolbarExport"); var _gridColumnsUtils = require("../columns/gridColumnsUtils"); var _gridCheckboxSelectionColDef = require("../../../colDef/gridCheckboxSelectionColDef"); var _jsxRuntime = require("react/jsx-runtime"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function raf() { return new Promise(resolve => { requestAnimationFrame(() => { resolve(); }); }); } function buildPrintWindow(title) { const 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) */ const useGridPrintExport = (apiRef, props) => { const logger = (0, _useGridLogger.useGridLogger)(apiRef, 'useGridPrintExport'); const doc = React.useRef(null); const previousGridState = React.useRef(null); const previousColumnVisibility = React.useRef({}); const previousRows = React.useRef([]); const previousVirtualizationState = React.useRef(); React.useEffect(() => { doc.current = (0, _utils.unstable_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 const updateGridColumnsForPrint = React.useCallback((fields, allColumns, includeCheckboxes) => new Promise(resolve => { const exportedColumnFields = (0, _utils2.getColumnsToExport)({ apiRef, options: { fields, allColumns } }).map(column => column.field); const columns = (0, _gridColumnsSelector.gridColumnDefinitionsSelector)(apiRef); const newColumnVisibilityModel = {}; columns.forEach(column => { newColumnVisibilityModel[column.field] = exportedColumnFields.includes(column.field); }); if (includeCheckboxes) { newColumnVisibilityModel[_gridCheckboxSelectionColDef.GRID_CHECKBOX_SELECTION_COL_DEF.field] = true; } apiRef.current.setColumnVisibilityModel(newColumnVisibilityModel); resolve(); }), [apiRef]); const updateGridRowsForPrint = React.useCallback(getRowsToExport => { const rowsToExportIds = getRowsToExport({ apiRef }); const newRows = rowsToExportIds.reduce((acc, id) => { const row = apiRef.current.getRow(id); if (!row[_gridRowsUtils.GRID_ID_AUTOGENERATED]) { acc.push(row); } return acc; }, []); apiRef.current.setRows(newRows); }, [apiRef]); const handlePrintWindowLoad = React.useCallback((printWindow, options) => { const normalizeOptions = (0, _extends2.default)({ copyStyles: true, hideToolbar: false, hideFooter: false, includeCheckboxes: false }, options); const printDoc = printWindow.contentDocument; if (!printDoc) { return; } const rowsMeta = (0, _gridRowsMetaSelector.gridRowsMetaSelector)(apiRef.current.state); const gridRootElement = apiRef.current.rootElementRef.current; const gridClone = gridRootElement.cloneNode(true); // Allow to overflow to not hide the border of the last row const gridMain = gridClone.querySelector(`.${_gridClasses.gridClasses.main}`); gridMain.style.overflow = 'visible'; // See https://support.google.com/chrome/thread/191619088?hl=en&msgid=193009642 gridClone.style.contain = 'size'; let gridToolbarElementHeight = gridRootElement.querySelector(`.${_gridClasses.gridClasses.toolbarContainer}`)?.offsetHeight || 0; let gridFooterElementHeight = gridRootElement.querySelector(`.${_gridClasses.gridClasses.footerContainer}`)?.offsetHeight || 0; if (normalizeOptions.hideToolbar) { gridClone.querySelector(`.${_gridClasses.gridClasses.toolbarContainer}`)?.remove(); gridToolbarElementHeight = 0; } if (normalizeOptions.hideFooter) { gridClone.querySelector(`.${_gridClasses.gridClasses.footerContainer}`)?.remove(); gridFooterElementHeight = 0; } // Expand container height to accommodate all rows const computedTotalHeight = rowsMeta.currentPageTotalHeight + (0, _gridColumnsUtils.getTotalHeaderHeight)(apiRef, props) + gridToolbarElementHeight + gridFooterElementHeight; gridClone.style.height = `${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 const gridFooterElement = gridClone.querySelector(`.${_gridClasses.gridClasses.footerContainer}`); gridFooterElement.style.position = 'absolute'; gridFooterElement.style.width = '100%'; gridFooterElement.style.top = `${computedTotalHeight - gridFooterElementHeight}px`; // printDoc.body.appendChild(gridClone); should be enough but a clone isolation bug in Safari // prevents us to do it const container = document.createElement('div'); container.appendChild(gridClone); // To avoid an empty page in start on Chromium based browsers printDoc.body.style.marginTop = '0px'; printDoc.body.innerHTML = container.innerHTML; const defaultPageStyle = typeof normalizeOptions.pageStyle === 'function' ? normalizeOptions.pageStyle() : normalizeOptions.pageStyle; if (typeof defaultPageStyle === 'string') { // TODO custom styles should always win const styleElement = printDoc.createElement('style'); styleElement.appendChild(printDoc.createTextNode(defaultPageStyle)); printDoc.head.appendChild(styleElement); } if (normalizeOptions.bodyClassName) { printDoc.body.classList.add(...normalizeOptions.bodyClassName.split(' ')); } const stylesheetLoadPromises = []; if (normalizeOptions.copyStyles) { const rootCandidate = gridRootElement.getRootNode(); const root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc.current; const headStyleElements = root.querySelectorAll("style, link[rel='stylesheet']"); for (let i = 0; i < headStyleElements.length; i += 1) { const node = headStyleElements[i]; if (node.tagName === 'STYLE') { const newHeadStyleElements = printDoc.createElement(node.tagName); const sheet = node.sheet; if (sheet) { let styleCSS = ''; // NOTE: for-of is not supported by IE for (let j = 0; j < sheet.cssRules.length; j += 1) { if (typeof sheet.cssRules[j].cssText === 'string') { styleCSS += `${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 const newHeadStyleElements = printDoc.createElement(node.tagName); for (let j = 0; j < node.attributes.length; j += 1) { const attr = node.attributes[j]; if (attr) { newHeadStyleElements.setAttribute(attr.nodeName, attr.nodeValue || ''); } } stylesheetLoadPromises.push(new Promise(resolve => { newHeadStyleElements.addEventListener('load', () => resolve()); })); printDoc.head.appendChild(newHeadStyleElements); } } } // Trigger print if (process.env.NODE_ENV !== 'test') { // wait for remote stylesheets to load Promise.all(stylesheetLoadPromises).then(() => { printWindow.contentWindow.print(); }); } }, [apiRef, doc, props]); const handlePrintWindowAfterPrint = React.useCallback(printWindow => { // Remove the print iframe doc.current.body.removeChild(printWindow); // Revert grid to previous state apiRef.current.restoreState(previousGridState.current || {}); if (!previousGridState.current?.columns?.columnVisibilityModel) { // if the apiRef.current.exportState(); did not exported the column visibility, we update it apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current); } apiRef.current.setState(state => (0, _extends2.default)({}, state, { virtualization: previousVirtualizationState.current })); apiRef.current.setRows(previousRows.current); // Clear local state previousGridState.current = null; previousColumnVisibility.current = {}; previousRows.current = []; }, [apiRef]); const exportDataAsPrint = React.useCallback(async options => { logger.debug(`Export data as Print`); if (!apiRef.current.rootElementRef.current) { throw new Error('MUI X: No grid root element available.'); } previousGridState.current = apiRef.current.exportState(); // It appends that the visibility model is not exported, especially if columnVisibility is not controlled previousColumnVisibility.current = (0, _gridColumnsSelector.gridColumnVisibilityModelSelector)(apiRef); previousRows.current = apiRef.current.getSortedRows().filter(row => !row[_gridRowsUtils.GRID_ID_AUTOGENERATED]); if (props.pagination) { const visibleRowCount = (0, _gridFilterSelector.gridExpandedRowCountSelector)(apiRef); const paginationModel = { page: 0, pageSize: visibleRowCount }; apiRef.current.setState(state => (0, _extends2.default)({}, state, { pagination: (0, _extends2.default)({}, state.pagination, { paginationModel: (0, _useGridPaginationModel.getDerivedPaginationModel)(state.pagination, // Using signature `DataGridPro` to allow more than 100 rows in the print export 'DataGridPro', paginationModel) }) })); } previousVirtualizationState.current = apiRef.current.state.virtualization; apiRef.current.setState(state => (0, _extends2.default)({}, state, { virtualization: (0, _extends2.default)({}, state.virtualization, { enabled: false, enabledForColumns: false }) })); await updateGridColumnsForPrint(options?.fields, options?.allColumns, options?.includeCheckboxes); updateGridRowsForPrint(options?.getRowsToExport ?? _utils2.defaultGetRowsToExport); await raf(); // wait for the state changes to take action const printWindow = buildPrintWindow(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 = () => { handlePrintWindowLoad(printWindow, options); const mediaQueryList = printWindow.contentWindow.matchMedia('print'); mediaQueryList.addEventListener('change', mql => { const isAfterPrint = mql.matches === false; if (isAfterPrint) { handlePrintWindowAfterPrint(printWindow); } }); }; doc.current.body.appendChild(printWindow); } }, [props, logger, apiRef, handlePrintWindowLoad, handlePrintWindowAfterPrint, updateGridColumnsForPrint, updateGridRowsForPrint]); const printExportApi = { exportDataAsPrint }; (0, _useGridApiMethod.useGridApiMethod)(apiRef, printExportApi, 'public'); /** * PRE-PROCESSING */ const addExportMenuButtons = React.useCallback((initialValue, options) => { if (options.printOptions?.disableToolbarButton) { return initialValue; } return [...initialValue, { component: /*#__PURE__*/(0, _jsxRuntime.jsx)(_GridToolbarExport.GridPrintExportMenuItem, { options: options.printOptions }), componentName: 'printExport' }]; }, []); (0, _pipeProcessing.useGridRegisterPipeProcessor)(apiRef, 'exportMenu', addExportMenuButtons); }; exports.useGridPrintExport = useGridPrintExport;