Added 'print' button to print the current chart

This commit is contained in:
Przemek Wiech
2019-02-21 22:38:22 +01:00
parent 61b65eb1ac
commit a920147ba8
5 changed files with 70 additions and 5 deletions

View File

@@ -133,4 +133,27 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
</div> </div>
); );
} }
/** Shows the print dialog to print the currently displayed chart. */
print() {
const printWindow = document.createElement('iframe');
printWindow.style.position = 'absolute';
printWindow.style.top = '-1000px';
printWindow.style.left = '-1000px';
printWindow.onload = () => {
const svg = document.getElementById('chart')!.cloneNode(true) as Element;
svg.removeAttribute('transform');
const contents = svg.outerHTML;
printWindow.contentDocument!.open();
printWindow.contentDocument!.write(contents);
printWindow.contentDocument!.close();
// Doesn't work on Firefox without the setTimeout.
setTimeout(() => {
printWindow.contentWindow!.focus();
printWindow.contentWindow!.print();
printWindow.parentNode!.removeChild(printWindow);
}, 500);
};
document.body.appendChild(printWindow);
}
} }

View File

@@ -50,6 +50,7 @@ interface State {
/** The main area of the application dedicated for rendering the family chart. */ /** The main area of the application dedicated for rendering the family chart. */
export class ChartView extends React.Component<RouteComponentProps, State> { export class ChartView extends React.Component<RouteComponentProps, State> {
state: State = {loading: false}; state: State = {loading: false};
chartRef: Chart | null = null;
/** /**
* Called when the user clicks an individual box in the chart. * Called when the user clicks an individual box in the chart.
@@ -102,7 +103,8 @@ export class ChartView extends React.Component<RouteComponentProps, State> {
? 'https://cors-anywhere.herokuapp.com/' + url ? 'https://cors-anywhere.herokuapp.com/' + url
: url; : url;
window.fetch(urlToFetch) window
.fetch(urlToFetch)
.then((response) => { .then((response) => {
if (response.status !== 200) { if (response.status !== 200) {
return Promise.reject(new Error(response.statusText)); return Promise.reject(new Error(response.statusText));
@@ -237,6 +239,7 @@ export class ChartView extends React.Component<RouteComponentProps, State> {
data={this.state.data} data={this.state.data}
onSelection={this.onSelection} onSelection={this.onSelection}
selection={this.state.selection} selection={this.state.selection}
ref={(ref) => (this.chartRef = ref)}
/> />
); );
} }
@@ -245,4 +248,11 @@ export class ChartView extends React.Component<RouteComponentProps, State> {
} }
return <Loader active size="large" />; return <Loader active size="large" />;
} }
/** Shows the print dialog to print the currently displayed chart. */
print() {
if (this.chartRef) {
this.chartRef.print();
}
}
} }

View File

@@ -6,7 +6,12 @@ import messages_pl from './translations/pl.json';
import {addLocaleData} from 'react-intl'; import {addLocaleData} from 'react-intl';
import {detect} from 'detect-browser'; import {detect} from 'detect-browser';
import {ChartView} from './chart_view'; import {ChartView} from './chart_view';
import {HashRouter as Router, Route, Switch} from 'react-router-dom'; import {
HashRouter as Router,
Route,
RouteComponentProps,
Switch,
} from 'react-router-dom';
import {IntlProvider} from 'react-intl'; import {IntlProvider} from 'react-intl';
import {Intro} from './intro'; import {Intro} from './intro';
import {TopBar} from './top_bar'; import {TopBar} from './top_bar';
@@ -22,6 +27,8 @@ const language = navigator.language && navigator.language.split(/[-_]/)[0];
const browser = detect(); const browser = detect();
let chartViewRef: ChartView | null = null;
if (browser && browser.name === 'ie') { if (browser && browser.name === 'ie') {
ReactDOM.render( ReactDOM.render(
<p> <p>
@@ -35,10 +42,23 @@ if (browser && browser.name === 'ie') {
<IntlProvider locale={language} messages={messages[language]}> <IntlProvider locale={language} messages={messages[language]}>
<Router> <Router>
<div className="root"> <div className="root">
<Route component={TopBar} /> <Route
component={(props: RouteComponentProps) => (
<TopBar
{...props}
onPrint={() => chartViewRef && chartViewRef.print()}
/>
)}
/>
<Switch> <Switch>
<Route exact path="/" component={Intro} /> <Route exact path="/" component={Intro} />
<Route exact path="/view" component={ChartView} /> <Route
exact
path="/view"
component={(props: RouteComponentProps) => (
<ChartView {...props} ref={(ref) => (chartViewRef = ref)} />
)}
/>
</Switch> </Switch>
</div> </div>
</Router> </Router>

View File

@@ -20,7 +20,14 @@ interface State {
url?: string; url?: string;
} }
export class TopBar extends React.Component<RouteComponentProps, State> { interface Props {
onPrint: () => void;
}
export class TopBar extends React.Component<
RouteComponentProps & Props,
State
> {
state: State = {loadUrlDialogOpen: false}; state: State = {loadUrlDialogOpen: false};
inputRef?: Input; inputRef?: Input;
@@ -164,6 +171,10 @@ export class TopBar extends React.Component<RouteComponentProps, State> {
/> />
</Menu.Item> </Menu.Item>
</label> </label>
<Menu.Item as="a" onClick={() => this.props.onPrint()}>
<Icon name="print" />
<FormattedMessage id="menu.print" defaultMessage="Print" />
</Menu.Item>
<Menu.Item <Menu.Item
as="a" as="a"
href="https://github.com/PeWu/topola-viewer" href="https://github.com/PeWu/topola-viewer"

View File

@@ -1,6 +1,7 @@
{ {
"menu.load_from_url": "Otwórz URL", "menu.load_from_url": "Otwórz URL",
"menu.load_from_file": "Otwórz plik", "menu.load_from_file": "Otwórz plik",
"menu.print": "Drukuj",
"menu.github": "Źródła na GitHub", "menu.github": "Źródła na GitHub",
"intro.title": "Topola Genealogy", "intro.title": "Topola Genealogy",
"intro.description": "Topola Genealogy pozwala przeglądać drzewo genealogiczne w interaktywny sposób.", "intro.description": "Topola Genealogy pozwala przeglądać drzewo genealogiczne w interaktywny sposób.",