@harrypaulo/react-data-report
Version:
ReactJS component to preview data and export it as PDF file.
177 lines (150 loc) • 6.78 kB
JSX
import React from 'react';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf'
import T from 'i18n-react';
import ReportPage from "./report-page";
import './css/report.scss';
T.setTexts(require('!json-loader!./languages/en.json'));
export default class Report extends React.Component {
constructor(props) {
super(props);
this.state = {
generating: false,
pages: [],
itemsPerPage: 8,
pageFormat: 'p',
pageSize: 'a4'
};
this._setInitialState = this._setInitialState.bind(this);
this._parseData = this._parseData.bind(this);
this._parseDataIni = this._parseDataIni.bind(this);
this._updatePreview = this._updatePreview.bind(this);
this.generateReport = this.generateReport.bind(this);
this.updateItemsPerPage = this.updateItemsPerPage.bind(this);
this.updatePageFormat = this.updatePageFormat.bind(this);
this.updatePageSize = this.updatePageSize.bind(this);
}
_setInitialState(props) {
this.setState({
pages: this._parseDataIni(props.data, props.opening, props.closing, this.props.itemsPerPage, this.props.pageFormat, this.props.pageSize),
itemsPerPage: this.props.itemsPerPage || 8,
pageFormat: this.props.pageFormat || "p",
pageSize: this.props.pageSize || "letter",
});
}
_parseDataIni(data, opening, closing, itemsPerPage, pageFormat, pageSize) {
const total = data.length;
if (total == 0) return null;
const step = parseInt(itemsPerPage);
const pages = Math.floor(total / step) + ((total % step == 0) ? 0 : 1 );
const className = pageSize + ' ' + (pageFormat == 'p' ? 'portrait' : 'landscape');
let content = [];
for (let pg = 0; pg < pages; pg++) {
const slicedData = data.slice(pg * step, (pg * step) + step);
content.push((<ReportPage className={ className } key={'page_' + pg} data={slicedData} opening={pg == 0 ? opening : null} closing={pg == pages - 1 ? closing : null} />));
}
return content;
}
_parseData(data, opening, closing) {
const total = data.length;
if (total == 0) return null;
const step = parseInt(this.state.itemsPerPage);
const pages = Math.floor(total / step) + ((total % step == 0) ? 0 : 1 );
const className = this.state.pageSize + ' ' + (this.state.pageFormat == 'p' ? 'portrait' : 'landscape');
let content = [];
for (let pg = 0; pg < pages; pg++) {
const slicedData = data.slice(pg * step, (pg * step) + step);
content.push((<ReportPage className={ className } key={'page_' + pg} data={slicedData} opening={pg == 0 ? opening : null} closing={pg == pages - 1 ? closing : null} />));
}
return content;
}
_updatePreview() {
if (this.update) clearTimeout(this.update);
this.update = setTimeout(() => {
this.setState({pages: this._parseData(this.props.data, this.props.opening, this.props.closing)});
}, 2500);
}
componentDidMount() {
this._setInitialState(this.props);
}
componentWillReceiveProps(nextProps) {
this._setInitialState(nextProps);
}
generateReport() {
if (this.divToPrint) {
this.setState({generating: true});
const pdf = new jsPDF(this.state.pageFormat, 'mm', this.state.pageSize);
let promises = [];
this.divToPrint.childNodes.forEach((page) => promises.push(html2canvas(page)));
Promise.all(promises).then((images) => {
const size = {
a4: { w: 210, h: 297 },
letter: { w: 215.9, h: 297 },
legal: { w: 216, h: 356 }
}
const w = this.state.pageFormat == 'p' ? size[this.state.pageSize].w : size[this.state.pageSize].h;
const h = this.state.pageFormat == 'p' ? size[this.state.pageSize].h : size[this.state.pageSize].w;
images.forEach((image, index) => {
const pageData = image.toDataURL('image/png', 1.0);
pdf.addImage(pageData, 'PNG', 0, 0, w, h, '', 'FAST');
if (index + 1 < images.length) {
pdf.addPage(this.state.pageSize, this.state.pageFormat);
}
});
this.setState({generating: false});
pdf.save('report.pdf');
}).catch((e) => console.log(e));
}
}
updateItemsPerPage(value) {
if (value >= 1) {
this.setState({itemsPerPage: value});
this._updatePreview();
}
}
updatePageFormat(value) {
alert(value);
this.setState({pageFormat: value});
this._updatePreview();
}
updatePageSize(value) {
this.setState({pageSize: value});
this._updatePreview();
}
render() {
return (
<div className="report">
<div className="report__control-panel">
<div className="report__items-per-page-container">
<label htmlFor="itemsPerPage">{T.translate('report.controlPanel.itemsPerPage')}</label>
<input id="itemsPerPage" name="itemsPerPage" type="number" min="1" step="1" onChange={(event) => this.updateItemsPerPage(event.target.value)} value={this.state.itemsPerPage} />
</div>
<div className="report__page-format-container">
<label htmlFor="pageFormat">{T.translate('report.controlPanel.pageFormat')}</label>
<select id="pageFormat" name="pageFormat" onChange={(event) => this.updatePageFormat(event.target.value)} value={this.state.pageFormat} >
<option value="p">portrait</option>
<option value="l">landscape</option>
</select>
</div>
<div className="report__page-size-container">
<label htmlFor="pageSize">{T.translate('report.controlPanel.pageSize')}</label>
<select id="pageSize" name="pageSize" onChange={(event) => this.updatePageSize(event.target.value)} value={this.state.pageSize} >
<option value="a4">a4</option>
<option value="letter">letter</option>
<option value="legal">legal</option>
</select>
</div>
<div className="report__generator-container">
{ this.state.generating
? (<button type="button" disabled="disabled">{T.translate('report.controlPanel.processing')}</button>)
: (<button type="button" className="report__generator-button" onClick={() => this.generateReport()}>{T.translate('report.controlPanel.generatePdf')}</button>)
}
</div>
</div>
<div id="divToPrint" ref={(elm) => this.divToPrint = elm} className="report__preview-container">
{this.state.pages}
</div>
</div>
);
}
}