diff --git a/package.json b/package.json index 29c170e..c17f7f2 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", + "babel-eslint": "^10.0.3", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-transform-import-css": "^0.1.6", "babel-plugin-transform-react-remove-prop-types": "^0.4.6", @@ -32,7 +33,7 @@ "eslint": "^4.2.0", "eslint-plugin-react": "^7.1.0", "prop-types": "^15.6.1", - "react": "^16.4.0", + "react": "^16.8.0", "react-dom": "^16.4.0", "rollup": "^0.60.7", "rollup-plugin-babel": "^3.0.4", diff --git a/src/NoPrint.jsx b/src/NoPrint.jsx index a0ba0e2..1c425f9 100644 --- a/src/NoPrint.jsx +++ b/src/NoPrint.jsx @@ -7,11 +7,9 @@ const propTypes = { children: PropTypes.node.isRequired, }; -export default class NoPrint extends React.PureComponent { - render() { - const { children, force } = this.props; - const force_ = force ? s._force : ''; - return
{children}
; - } +export default function NoPrint ({ children, force }) { + const force_ = force ? s._force : ''; + return
{children}
; } + NoPrint.propTypes = propTypes; diff --git a/src/Print.jsx b/src/Print.jsx index 8fc8117..22f9abe 100644 --- a/src/Print.jsx +++ b/src/Print.jsx @@ -1,7 +1,6 @@ -import React from 'react'; +import React, { useState, useEffect, useRef } from 'react'; +import { usePrintProvider } from './PrintProviderContext'; import PropTypes from 'prop-types'; - -import PrintProvider from './PrintProvider'; import { debug } from './common'; import s from './Print.css'; @@ -13,76 +12,61 @@ const propTypes = { exclusive: PropTypes.bool, printOnly: PropTypes.bool }; -const contextTypes = PrintProvider.childContextTypes; - -export default class Print extends React.Component { - - constructor(props) { - super(props); - this.state = { - printOffsetLeft: 0, - printOffsetTop: 0 - }; - } - componentDidMount() { +export default function Print (props) { + const [printOffsetLeft, setPrintOffsetLeft] = useState(0); + const [printOffsetTop, setPrintOffsetTop] = useState(0); + const { regPrintable, unregPrintable } = usePrintProvider(); + const printElement = useRef(null); + const main_ = (props.main || props.single) ? s._main : ''; + const excl_ = (props.exclusive || props.printOnly) ? s._exclusive : ''; + const isPrint = window.matchMedia('print').matches; + const offset_ = ((printOffsetTop || printOffsetLeft) && main_ && isPrint) ? + { marginTop: -printOffsetTop, marginLeft: -printOffsetLeft } : {}; + const globalClassName = 'react-easy-print-print'; // using in hiddenAll + const className = `${globalClassName} ${s.root} ${main_} ${excl_}`; - if (this.props.name) { - debug('init printable', this.props.name); - const isSingle = (this.props.main || this.props.single); - this.context.printProvider && this.context.printProvider.regPrintable(this.props.name, , isSingle); + useEffect(() => { + if (props.name) { + debug('init printable', props.name); + const isSingle = (props.main || props.single); + regPrintable(name, , isSingle); } - if (this.props.main || this.props.single) { + if (props.main || props.single) { window.matchMedia('print').onchange = () => { const isPrint = window.matchMedia('print').matches; if (isPrint) { const bodyRect = document.body.getBoundingClientRect(); - const elem = this.printElement; + const elem = printElement.current; const elemRect = elem && elem.getBoundingClientRect(); const printOffsetLeft = elemRect && (elemRect.left - bodyRect.left); const printOffsetTop = elemRect && (elemRect.top - bodyRect.top); - this.setState({ - printOffsetTop, - printOffsetLeft, - }); + setPrintOffsetLeft(printOffsetLeft); + setPrintOffsetTop(printOffsetTop); } else { - this.setState({ - printOffsetTop: 0, - printOffsetLeft: 0 - }) + setPrintOffsetLeft(0); + setPrintOffsetTop(0); } }; } - } - - componentWillUnmount() { - if (this.props.name) { - debug('remove printable', this.props.name); - const isSingle = (this.props.main || this.props.single); - this.context.printProvider && this.context.printProvider.unregPrintable(this.props.name, isSingle); - } - if (this.props.main || this.props.single) { - window.matchMedia('print').onchange = null; - } - } - - render() { - - const { children, main, single, exclusive, printOnly } = this.props; - const { printOffsetLeft, printOffsetTop } = this.state; - const main_ = (main || single) ? s._main : ''; - const excl_ = (exclusive || printOnly) ? s._exclusive : ''; - const isPrint = window.matchMedia('print').matches; - const offset_ = ((printOffsetTop || printOffsetLeft) && main_ && isPrint) ? { marginTop: -printOffsetTop, marginLeft: -printOffsetLeft } : {}; - const globalClassName = 'react-easy-print-print'; // using in hiddenAll - const className = `${globalClassName} ${s.root} ${main_} ${excl_}`; - return
this.printElement = el} style={offset_} className={className}>{children}
; - } + return () => { + if (props.name) { + debug('remove printable', props.name); + const isSingle = (props.main || props.single); + unregPrintable(props.name, isSingle); + } + + if (props.main || props.single) { + window.matchMedia('print').onchange = null; + } + }; + }, []); + return
{props.children}
; } + Print.propTypes = propTypes; -Print.contextTypes = contextTypes; diff --git a/src/PrintProvider.jsx b/src/PrintProvider.jsx index 7fc68c5..3c8ebc8 100644 --- a/src/PrintProvider.jsx +++ b/src/PrintProvider.jsx @@ -1,125 +1,129 @@ -import React from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; +import PrintProviderContext from './PrintProviderContext'; import s from './PrintProvider.css'; import { spliced, debug } from './common'; export const propTypes = { loose: PropTypes.bool, children: PropTypes.node.isRequired, - invert: PropTypes.bool, + invert: PropTypes.bool }; -export const childContextTypes = { - printProvider: PropTypes.shape({ - regPrintable: PropTypes.func.isRequired, - unregPrintable: PropTypes.func.isRequired, - isPrint: PropTypes.bool.isRequired, - }).isRequired, +const ID = 'react-easy-print-render'; + +const createRender = children => { + const el = document.createElement('div'); + el.id = ID; + document.body.appendChild(el); + ReactDOM.render(
{children}
, el); }; -export default class PrintProvider extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - isInPrintPreview: false, - printableNodes: [], - }; - this.printableRegistry = {}; +const deleteRender = () => { + const el = document.getElementById(ID); + el && document.body.removeChild(el); +}; + +export default function PrintProvider (props) { + + const [isInPrintPreview, setIsInPrintPreview] = useState(false); + const [printableNodes, setPrintableNodes ] = useState([]); + + const printableRegistry = useRef({}); + const hasSingle = useRef(false); + + const loose = props.loose || props.invert; + const loose_ = loose ? s.loose : ''; + const invert_ = props.invert ? s.invert : ''; + + useEffect(() => { window.matchMedia('print').onchange = () => { debug('toggle print mode', window.matchMedia('print').matches); - this.setState({ isInPrintPreview: window.matchMedia('print').matches }); + setIsInPrintPreview(window.matchMedia('print').matches); }; + }, []); - this.hasSingle = false; - } + useEffect(() => { + if (isInPrintPreview && printableNodes.length && !loose && !hasSingle.current) { + debug('render printable only', printableNodes); + const children = React.Children.map(printableNodes, (child, key) => { + return React.cloneElement(child, { key }); + }); + createRender(
{children}
); + } + setTimeout(() => deleteRender(), 0); + }); - getChildContext() { - return { - printProvider: { - isPrint: window.matchMedia('print').matches, - regPrintable: this.regPrintable.bind(this), - unregPrintable: this.unregPrintable.bind(this), - }, - }; - } + useEffect(() => { + debug('render everything', isInPrintPreview, printableNodes.length, !loose); + }, [isInPrintPreview, printableNodes.length, loose]); // hideAll - is being used to cover all of React Portals, popups and modals and etc. - hideAll() { + const hideAll = () => { document.body.classList.add(s.hiddenAll); - this.hasSingle = true; - } + hasSingle.current = true; + }; - unhideAll() { + const unhideAll = () => { document.body.classList.remove(s.hiddenAll); - this.hasSingle = false; - } + hasSingle.current = false; + }; - regPrintable(key, node) { - const loose = this.props.loose || this.props.invert; + const regPrintable = (key, node) => { + const loose = props.loose || props.invert; const isSingle = node.props.single || node.props.main; - const { hasSingle } = this; debug('reg printable', key, node); - if (this.printableRegistry[key] !== undefined || loose) return; - setTimeout(() => this.setState({ - printableNodes: this.state.printableNodes.concat(node), - }), 0); - this.printableRegistry[key] = this.state.printableNodes.length; + if (printableRegistry.current[key] !== undefined || loose) return; + setTimeout( + () => + setPrintableNodes(printableNodes.concat(node)), + 0 + ); + printableRegistry.current[key] = printableNodes.length; - if (isSingle && !hasSingle) { - this.hideAll(); + if (isSingle && !hasSingle.current) { + hideAll(); } else if (isSingle) { - console.warn(new Error('react-easy-print warning \n\t you\'re using more than one `single` Print component')); + console.warn( + new Error( + 'react-easy-print warning \n\t you\'re using more than one `single` Print component' + ) + ); } - } - - unregPrintable(key, isSingle) { - const loose = this.props.loose || this.props.invert; - if (this.printableRegistry[key] === undefined || this.state.isInPrintPreview || loose) return; - this.setState({ - printableNodes: spliced(this.state.printableNodes, this.printableRegistry[key]), + }; + + const unregPrintable = (key, isSingle) => { + const loose = props.loose || props.invert; + if ( + printableRegistry.current[key] === undefined || + isInPrintPreview || + loose + ) + return; + setPrintableNodes( + spliced( + printableNodes, + printableRegistry.current[key] + ) + ); + printableRegistry.current = Object.assign({}, printableRegistry.current, { + [key]: undefined }); - this.printableRegistry = Object.assign({}, this.printableRegistry, { [key]: undefined }); if (isSingle) { - this.unhideAll(); - } - } - - createRender(children) { - const el = document.createElement('div'); - el.id = 'render'; - document.body.appendChild(el); - - ReactDOM.render(
{children}
, el); - } - - deleteRender() { - const el = document.getElementById('render'); - el && document.body.removeChild(el); - } - - render() { - const { isInPrintPreview, printableNodes } = this.state; - const loose = this.props.loose || this.props.invert; - const { hasSingle } = this; - - if (isInPrintPreview && printableNodes.length && !loose && !hasSingle) { - debug('render printable only', printableNodes); - const children = React.Children.map(printableNodes, (child, key) => { - return React.cloneElement(child, { key }); - }); - - this.createRender(
{children}
); + unhideAll(); } - setTimeout(() => this.deleteRender(), 0); + }; + + return ( + +
+ {props.children} +
+
+ ); +}; - debug('render everything', isInPrintPreview, printableNodes.length, !loose); - const loose_ = loose ? s.loose : ''; - const invert_ = this.props.invert ? s.invert : ''; - return
{this.props.children}
; - } -} PrintProvider.propTypes = propTypes; -PrintProvider.childContextTypes = childContextTypes; diff --git a/src/PrintProviderContext.js b/src/PrintProviderContext.js new file mode 100644 index 0000000..4e2d6b4 --- /dev/null +++ b/src/PrintProviderContext.js @@ -0,0 +1,15 @@ + +import React, { useContext } from 'react'; + +const PrintProviderContext = React.createContext(); + +export const usePrintProvider = () => { + const { regPrintable, unregPrintable } = useContext(PrintProviderContext); + + return { + regPrintable, + unregPrintable + }; +}; + +export default PrintProviderContext;