Skip to content

Commit 756888b

Browse files
Update devDependencies, break up and organize code (MatthewHerbst#683)
1 parent 3cc32aa commit 756888b

12 files changed

+1051
-734
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The component accepts the following props:
2626
| Name | Type | Description |
2727
| :-------------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------- |
2828
| **`bodyClass?`** | `string` | One or more class names to pass to the print window, separated by spaces |
29-
| **`content?`** | `function` | A function that returns a component reference value. The content of this reference value is then used for print |
29+
| **`content?`** | `function` | A function that returns a component reference value. The content of this reference value is then used for print. Alternatively, pass the content directly to the callback returned by `useReactToPrint` |
3030
| **`copyStyles?`** | `boolean` | Copy all `<style>` and `<link type="stylesheet" />` tags from `<head>` inside the parent window into the print window. (default: `true`) |
3131
| **`documentTitle?`** | `string` | Set the title for printing when saving as a file |
3232
| **`fonts?`** | `{ family: string, source: string; weight?: string; style?: string; }[]` | You may optionally provide a list of fonts which will be loaded into the printing iframe. This is useful if you are using custom fonts |
@@ -47,7 +47,7 @@ If you need extra control over printing and don't want to specify `trigger` dire
4747

4848
### `useReactToPrint`
4949

50-
For functional components, use the `useReactToPrint` hook, which accepts an object with the same configuration props as `<ReactToPrint />` and returns a `handlePrint` function which when called will trigger the print action. Requires React >=16.8.0. See the examples below for usage.
50+
For functional components, use the `useReactToPrint` hook, which accepts an object with the same configuration props as `<ReactToPrint />` and returns a `handlePrint` function which when called will trigger the print action. Requires React >=16.8.0. See the examples below for usage. Additionally, for-fine tuned control, the `handlePrint` callback can accept an optional `content` prop which will can be used instead of passing a `content` prop to the hook itself.
5151

5252
## Compatibility
5353

@@ -528,4 +528,4 @@ Set the container to `overflow: visible; height: fit-content` when printing, can
528528
529529
## Running locally
530530
531-
*NOTE*: Node >=12 is required to build the library locally. We use Node ^14 for our tests.
531+
*NOTE*: The library is tested and built locally using Node >= 20.

package-lock.json

Lines changed: 868 additions & 575 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,27 @@
3737
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
3838
},
3939
"devDependencies": {
40-
"@types/react": "^18.2.22",
41-
"@types/react-dom": "^18.2.7",
42-
"@typescript-eslint/eslint-plugin": "^6.7.2",
43-
"@typescript-eslint/parser": "^6.7.2",
44-
"acorn": "^8.10.0",
40+
"@types/react": "^18.2.55",
41+
"@types/react-dom": "^18.2.19",
42+
"@typescript-eslint/eslint-plugin": "^6.21.0",
43+
"@typescript-eslint/parser": "^6.21.0",
44+
"acorn": "^8.11.3",
4545
"clean-webpack-plugin": "^4.0.0",
46-
"copy-webpack-plugin": "^11.0.0",
47-
"css-loader": "^6.8.1",
48-
"eslint": "^8.49.0",
49-
"html-webpack-plugin": "^5.5.3",
50-
"husky": "^8.0.3",
51-
"lint-staged": "^14.0.1",
46+
"copy-webpack-plugin": "^12.0.2",
47+
"css-loader": "^6.10.0",
48+
"eslint": "^8.56.0",
49+
"html-webpack-plugin": "^5.6.0",
50+
"husky": "^9.0.10",
51+
"lint-staged": "^15.2.2",
5252
"react": "^18.2.0",
5353
"react-dom": "^18.2.0",
5454
"react-tabs": "^6.0.2",
55-
"style-loader": "^3.3.3",
56-
"ts-loader": "^9.4.4",
55+
"style-loader": "^3.3.4",
56+
"ts-loader": "^9.5.1",
5757
"tslib": "^2.6.2",
58-
"typescript": "^5.2.2",
58+
"typescript": "^5.3.3",
5959
"url-loader": "^4.1.1",
60-
"webpack": "^5.88.2",
60+
"webpack": "^5.90.1",
6161
"webpack-cli": "^5.1.4",
6262
"webpack-dev-server": "^4.15.1"
6363
},

src/components/PrintContext.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from "react";
2+
3+
const contextEnabled = Object.prototype.hasOwnProperty.call(React, "createContext");
4+
5+
export interface IPrintContextProps {
6+
handlePrint: (event: unknown, content?: (() => React.ReactInstance | null)) => void,
7+
}
8+
9+
export const PrintContext = contextEnabled ? React.createContext({} as IPrintContextProps) : null;
10+
export const PrintContextConsumer = PrintContext ? PrintContext.Consumer : () => null;

src/index.tsx renamed to src/components/ReactToPrint.tsx

Lines changed: 13 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,13 @@
11
import * as React from "react";
22
import { findDOMNode } from "react-dom";
33

4-
const contextEnabled = Object.prototype.hasOwnProperty.call(React, "createContext");
5-
const hooksEnabled = Object.prototype.hasOwnProperty.call(React, "useMemo") && Object.prototype.hasOwnProperty.call(React, "useCallback");
6-
7-
export interface IPrintContextProps {
8-
handlePrint: (event: unknown, content?: (() => React.ReactInstance | null)) => void,
9-
}
10-
const PrintContext = contextEnabled ? React.createContext({} as IPrintContextProps) : null;
11-
export const PrintContextConsumer = PrintContext ? PrintContext.Consumer : () => null;
12-
13-
export interface ITriggerProps<T> {
14-
onClick: (event: unknown) => void;
15-
ref: (v: T) => void;
16-
}
17-
18-
// https://developer.mozilla.org/en-US/docs/Web/API/FontFace/FontFace
19-
type Font = {
20-
family: string;
21-
source: string;
22-
weight?: string;
23-
style?: string;
24-
};
25-
26-
type PropertyFunction<T> = () => T;
27-
28-
/**
29-
* This function helps to curry arguments to a bound function
30-
* and partially apply them at the end of the argument list.
31-
*
32-
* @param {Object} thisArg
33-
* @param {Function} callback
34-
* @param {Array.<*>} predefinedArgs
35-
*
36-
* @returns {*}
37-
*/
38-
function wrapCallbackWithArgs<CallbackReturnValue, BoundObject>(
39-
thisArg: BoundObject,
40-
callback: (...curryArgs: any[]) => CallbackReturnValue,
41-
...predefinedArgs: unknown[]
42-
) {
43-
return function (...args: unknown[]) {
44-
return callback.apply(thisArg, [...args, ...predefinedArgs]);
45-
};
46-
}
47-
48-
// NOTE: https://github.com/Microsoft/TypeScript/issues/23812
49-
const defaultProps = {
50-
copyStyles: true,
51-
pageStyle: `
52-
@page {
53-
/* Remove browser default header (title) and footer (url) */
54-
margin: 0;
55-
}
56-
@media print {
57-
body {
58-
/* Tell browsers to print background colors */
59-
-webkit-print-color-adjust: exact; /* Chrome/Safari/Edge/Opera */
60-
color-adjust: exact; /* Firefox */
61-
}
62-
}
63-
`,
64-
removeAfterPrint: false,
65-
suppressErrors: false,
66-
};
67-
68-
export interface IReactToPrintProps {
69-
/** Class to pass to the print window body */
70-
bodyClass?: string;
71-
children?: React.ReactNode;
72-
/** Content to be printed */
73-
content?: () => React.ReactInstance | null;
74-
/** Copy styles over into print window. default: true */
75-
copyStyles?: boolean;
76-
/**
77-
* Set the title for printing when saving as a file.
78-
* Will result in the calling page's `<title>` being temporarily changed while printing.
79-
*/
80-
documentTitle?: string;
81-
/** Pre-load these fonts to ensure availability when printing */
82-
fonts?: Font[];
83-
/** Callback function to trigger after print */
84-
onAfterPrint?: () => void;
85-
/** Callback function to trigger before page content is retrieved for printing */
86-
onBeforeGetContent?: () => void | Promise<any>;
87-
/** Callback function to trigger before print */
88-
onBeforePrint?: () => void | Promise<any>;
89-
/** Callback function to listen for printing errors */
90-
onPrintError?: (errorLocation: "onBeforeGetContent" | "onBeforePrint" | "print", error: Error) => void;
91-
/** Override default print window styling */
92-
pageStyle?: string | PropertyFunction<string>;
93-
/** Override the default `window.print` method that is used for printing */
94-
print?: (target: HTMLIFrameElement) => Promise<any>;
95-
/**
96-
* Remove the iframe after printing.
97-
* NOTE: `onAfterPrint` will run before the iframe is removed
98-
*/
99-
removeAfterPrint?: boolean;
100-
/** Suppress error messages */
101-
suppressErrors?: boolean;
102-
/** Trigger action used to open browser print */
103-
trigger?: <T>() => React.ReactElement<ITriggerProps<T>>;
104-
/** Set the nonce attribute for whitelisting script and style -elements for CSP (content security policy) */
105-
nonce?: string;
106-
}
107-
108-
export default class ReactToPrint extends React.Component<IReactToPrintProps> {
4+
import { PrintContext } from "./PrintContext";
5+
import type { IPrintContextProps } from "./PrintContext";
6+
import { defaultProps } from "../consts/defaultProps";
7+
import type { Font } from "../types/font";
8+
import type { IReactToPrintProps } from "../types/reactToPrintProps";
9+
10+
export class ReactToPrint extends React.Component<IReactToPrintProps> {
10911
private numResourcesToLoad!: number;
11012
private resourcesLoaded!: (Element | Font | FontFace)[];
11113
private resourcesErrored!: (Element | Font | FontFace)[];
@@ -205,7 +107,6 @@ export default class ReactToPrint extends React.Component<IReactToPrintProps> {
205107
this.startPrint(target);
206108
}
207109
}
208-
209110

210111
public handleClick (
211112
/* eslint-disable-next-line no-unused-vars */
@@ -247,6 +148,10 @@ export default class ReactToPrint extends React.Component<IReactToPrintProps> {
247148
} = this.props;
248149

249150
let contentEl = typeof optionalContent === "function" ? optionalContent() : null;
151+
152+
if (contentEl && typeof content === "function") {
153+
this.logMessages(['"react-to-print" received a `content` prop and a content param passed the callback return by `useReactToPrint. The `content` prop will be ignored.'], "warning");
154+
}
250155

251156
if (!contentEl && typeof content === "function") {
252157
contentEl = content();
@@ -468,7 +373,7 @@ export default class ReactToPrint extends React.Component<IReactToPrintProps> {
468373

469374
for (let i = 0, styleAndLinkNodesLen = styleAndLinkNodes.length; i < styleAndLinkNodesLen; ++i) {
470375
const node = styleAndLinkNodes[i];
471-
376+
472377
if (node.tagName.toLowerCase() === 'style') { // <style> nodes
473378
const newHeadEl = domDoc.createElement(node.tagName);
474379
const sheet = (node as HTMLStyleElement).sheet as CSSStyleSheet;
@@ -616,36 +521,4 @@ export default class ReactToPrint extends React.Component<IReactToPrintProps> {
616521
);
617522
}
618523
}
619-
}
620-
621-
type UseReactToPrintHookReturn = (
622-
event: unknown,
623-
content?: (() => React.ReactInstance | null)
624-
) => void;
625-
626-
export const useReactToPrint = (props: IReactToPrintProps): UseReactToPrintHookReturn => {
627-
if (!hooksEnabled) {
628-
if (!props.suppressErrors) {
629-
console.error('"react-to-print" requires React ^16.8.0 to be able to use "useReactToPrint"'); // eslint-disable-line no-console
630-
}
631-
632-
return () => {
633-
throw new Error('"react-to-print" requires React ^16.8.0 to be able to use "useReactToPrint"');
634-
};
635-
}
636-
637-
const reactToPrint = React.useMemo(
638-
// TODO: is there a better way of applying the defaultProps?
639-
() => new ReactToPrint({ ...defaultProps, ...props }),
640-
[props]
641-
);
642-
643-
return React.useCallback(
644-
(event: unknown, content?: (() => React.ReactInstance | null)) => {
645-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
646-
const triggerPrint = wrapCallbackWithArgs(reactToPrint, reactToPrint.handleClick, content);
647-
// NOTE: `event` is a no-use argument
648-
// (necessary for backward compatibility with older versions)
649-
return triggerPrint(event);
650-
}, [reactToPrint]);
651-
};
524+
}

src/consts/defaultProps.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// NOTE: https://github.com/Microsoft/TypeScript/issues/23812
2+
export const defaultProps = {
3+
copyStyles: true,
4+
pageStyle: `
5+
@page {
6+
/* Remove browser default header (title) and footer (url) */
7+
margin: 0;
8+
}
9+
@media print {
10+
body {
11+
/* Tell browsers to print background colors */
12+
-webkit-print-color-adjust: exact; /* Chrome/Safari/Edge/Opera */
13+
color-adjust: exact; /* Firefox */
14+
}
15+
}
16+
`,
17+
removeAfterPrint: false,
18+
suppressErrors: false,
19+
};

src/hooks/useReactToPrint.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from "react";
2+
3+
import { ReactToPrint } from "../components/ReactToPrint";
4+
import { defaultProps } from "../consts/defaultProps";
5+
import type { IReactToPrintProps } from "../types/reactToPrintProps";
6+
import { wrapCallbackWithArgs } from "../utils/wrapCallbackWithArgs";
7+
8+
type UseReactToPrintHookReturn = (
9+
event: unknown,
10+
content?: (() => React.ReactInstance | null)
11+
) => void;
12+
13+
const hooksEnabled = Object.prototype.hasOwnProperty.call(React, "useMemo") && Object.prototype.hasOwnProperty.call(React, "useCallback");
14+
15+
export const useReactToPrint = (props: IReactToPrintProps): UseReactToPrintHookReturn => {
16+
if (!hooksEnabled) {
17+
if (!props.suppressErrors) {
18+
console.error('"react-to-print" requires React ^16.8.0 to be able to use "useReactToPrint"'); // eslint-disable-line no-console
19+
}
20+
21+
return () => {
22+
throw new Error('"react-to-print" requires React ^16.8.0 to be able to use "useReactToPrint"');
23+
};
24+
}
25+
26+
const reactToPrint = React.useMemo(
27+
// TODO: is there a better way of applying the defaultProps?
28+
() => new ReactToPrint({ ...defaultProps, ...props }),
29+
[props]
30+
);
31+
32+
return React.useCallback(
33+
(event: unknown, content?: (() => React.ReactInstance | null)) => {
34+
/* eslint-disable-next-line @typescript-eslint/unbound-method */
35+
const triggerPrint = wrapCallbackWithArgs(reactToPrint, reactToPrint.handleClick, content);
36+
// NOTE: `event` is a no-use argument
37+
// (necessary for backward compatibility with older versions)
38+
return triggerPrint(event);
39+
}, [reactToPrint]);
40+
};

src/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export type { IPrintContextProps } from "./components/PrintContext";
2+
export { PrintContextConsumer } from "./components/PrintContext";
3+
export { ReactToPrint } from "./components/ReactToPrint";
4+
export { useReactToPrint } from "./hooks/useReactToPrint";
5+
export type { IReactToPrintProps, ITriggerProps } from "./types/reactToPrintProps";
6+
7+
import { ReactToPrint } from "./components/ReactToPrint";
8+
export default ReactToPrint;
9+
10+
11+
12+
13+
14+
15+
16+

src/types/font.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// https://developer.mozilla.org/en-US/docs/Web/API/FontFace/FontFace
2+
export type Font = {
3+
family: string;
4+
source: string;
5+
weight?: string;
6+
style?: string;
7+
};

0 commit comments

Comments
 (0)