terriajs
Version:
Geospatial data visualization platform.
313 lines (273 loc) • 11.4 kB
JSX
;
import { buildShareLink, buildShortShareLink, canShorten } from './BuildShareLink';
import classNames from 'classnames';
import Clipboard from '../../../Clipboard';
import createReactClass from 'create-react-class';
import defined from 'terriajs-cesium/Source/Core/defined';
import DropdownStyles from '../panel.scss';
import Icon from "../../../Icon.jsx";
import Loader from '../../../Loader';
import MenuPanel from '../../../StandardUserInterface/customizable/MenuPanel.jsx';
import ObserverModelMixin from '../../../ObserveModelMixin';
import PrintView from './PrintView';
import printWindow from '../../../../Core/printWindow';
import PropTypes from 'prop-types';
import React from 'react';
import Styles from './share-panel.scss';
const SharePanel = createReactClass({
displayName: 'SharePanel',
mixins: [ObserverModelMixin],
propTypes: {
terria: PropTypes.object,
userPropWhiteList: PropTypes.array,
advancedIsOpen: PropTypes.bool,
shortenUrls: PropTypes.bool,
viewState: PropTypes.object.isRequired
},
getDefaultProps() {
return {
advancedIsOpen: false,
shortenUrls: false
};
},
getInitialState() {
return {
isOpen: false,
shortenUrls: this.props.shortenUrls && this.props.terria.getLocalProperty('shortenShareUrls'),
shareUrl: '',
creatingPrintView: false,
creatingDownload: false
};
},
componentDidMount() {
if (this.props.terria.configParameters.interceptBrowserPrint) {
window.addEventListener('beforeprint', this.beforeBrowserPrint, false);
window.addEventListener('afterprint', this.afterBrowserPrint, false);
const handlePrintMediaChange = evt => {
if (evt.matches) {
this.beforeBrowserPrint();
} else {
this.afterBrowserPrint();
}
};
if (window.matchMedia) {
const matcher = window.matchMedia('print');
matcher.addListener(handlePrintMediaChange);
this._unsubscribeFromPrintMediaChange = function () {
matcher.removeListener(handlePrintMediaChange);
};
}
this._oldPrint = window.print;
window.print = () => {
this.print();
};
}
},
componentWillUnmount() {
window.removeEventListener('beforeprint', this.beforeBrowserPrint, false);
window.removeEventListener('afterprint', this.afterBrowserPrint, false);
if (this._unsubscribeFromPrintMediaChange) {
this._unsubscribeFromPrintMediaChange();
}
if (this._oldPrint) {
window.print = this._oldPrint;
}
},
beforeBrowserPrint() {
this.afterBrowserPrint();
this._message = document.createElement('div');
this._message.innerText = 'For better printed results, please use ' + this.props.terria.appName + '\'s Print button instead of your web browser\'s print feature.';
window.document.body.insertBefore(this._message, window.document.body.childNodes[0]);
},
afterBrowserPrint() {
if (this._message) {
window.document.body.removeChild(this._message);
this._message = undefined;
}
this.changeOpenState(true);
},
advancedIsOpen() {
return this.state.advancedIsOpen;
},
toggleAdvancedOptions(e) {
this.setState((prevState) => ({
advancedIsOpen: !prevState.advancedIsOpen
}));
},
updateForShortening() {
this.setState({
shareUrl: ''
});
if (this.shouldShorten()) {
this.setState({
placeholder: 'Shortening...'
});
buildShortShareLink(this.props.terria)
.then(shareUrl => this.setState({ shareUrl }))
.otherwise(() => {
this.setUnshortenedUrl();
this.setState({
errorMessage: 'An error occurred while attempting to shorten the URL. Please check your internet connection and try again.'
});
});
} else {
this.setUnshortenedUrl();
}
},
setUnshortenedUrl() {
this.setState({
shareUrl: buildShareLink(this.props.terria)
});
},
isUrlShortenable() {
return canShorten(this.props.terria);
},
shouldShorten() {
const localStoragePref = this.props.terria.getLocalProperty('shortenShareUrls');
return this.isUrlShortenable() && (localStoragePref || !defined(localStoragePref));
},
onShortenClicked(e) {
if (this.shouldShorten()) {
this.props.terria.setLocalProperty('shortenShareUrls', false);
} else if (this.isUrlShortenable()) {
this.props.terria.setLocalProperty('shortenShareUrls', true);
} else {
return;
}
this.updateForShortening();
this.forceUpdate();
},
changeOpenState(open) {
this.setState({
isOpen: open
});
if (open) {
this.updateForShortening();
}
},
print() {
this.createPrintView(true, true);
},
showPrintView() {
this.createPrintView(false, false);
},
createPrintView(hidden, printAutomatically) {
this.setState({
creatingPrintView: true
});
let iframe;
if (hidden) {
iframe = document.createElement('iframe');
document.body.appendChild(iframe);
}
PrintView.create({
terria: this.props.terria,
viewState: this.props.viewState,
printWindow: iframe ? iframe.contentWindow : undefined,
readyCallback: windowToPrint => {
if (printAutomatically) {
printWindow(windowToPrint).otherwise(e => {
this.props.terria.error.raiseEvent(e);
}).always(() => {
if (iframe) {
document.body.removeChild(iframe);
}
if (hidden) {
this.setState({
creatingPrintView: false
});
}
});
}
},
closeCallback: windowToPrint => {
if (hidden) {
this.setState({
creatingPrintView: false
});
}
}
});
if (!hidden) {
this.setState({
creatingPrintView: false
});
}
},
renderContent() {
const iframeCode = this.state.shareUrl.length ?
`<iframe style="width: 720px; height: 600px; border: none;" src="${this.state.shareUrl}" allowFullScreen mozAllowFullScreen webkitAllowFullScreen></iframe>`
: '';
const shareUrlTextBox = <input className={Styles.shareUrlfield} type="text" value={this.state.shareUrl}
placeholder={this.state.placeholder} readOnly
onClick={e => e.target.select()} id='share-url' />;
return (
<div>
<div className={Styles.clipboard}><Clipboard source={shareUrlTextBox} id='share-url' /></div>
<div className={DropdownStyles.section}>
<div>Print Map</div>
<div className={Styles.explanation}>Open a printable version of this map.</div>
<div>
<button className={Styles.printButton} onClick={this.print} disabled={this.state.creatingPrintView}>Print</button>
<button className={Styles.printButton} onClick={this.showPrintView} disabled={this.state.creatingPrintView}>Show Print View</button>
<div className={Styles.printViewLoader}>
{this.state.creatingPrintView && <Loader message="Creating print view..." />}
</div>
</div>
</div>
<div className={classNames(DropdownStyles.section, Styles.shortenUrl)}>
<div className={Styles.btnWrapper}>
<button type='button' onClick={this.toggleAdvancedOptions} className={Styles.btnAdvanced}>
<span>Advanced options</span>
{this.advancedIsOpen() ? <Icon glyph={Icon.GLYPHS.opened} /> : <Icon glyph={Icon.GLYPHS.closed} />}
</button>
</div>
<If condition={this.advancedIsOpen()}>
<div className={DropdownStyles.section}>
<p className={Styles.paragraph}>To embed, copy this code to embed this map into an HTML page:</p>
<input className={Styles.field} type="text" readOnly placeholder={this.state.placeholder}
value={iframeCode}
onClick={e => e.target.select()} />
</div>
<If condition={this.isUrlShortenable()}>
<div className={classNames(DropdownStyles.section, Styles.shortenUrl)}>
<button onClick={this.onShortenClicked}>
{this.shouldShorten() ? <Icon glyph={Icon.GLYPHS.checkboxOn} /> : <Icon glyph={Icon.GLYPHS.checkboxOff} />}
Shorten the share URL using a web service
</button>
</div>
</If>
</If>
</div>
</div>);
},
renderDownloadFormatButton(format) {
return (
<button key={format.name} className={Styles.formatButton} onClick={this.download} disabled={this.state.creatingDownload}>{format.name}</button>
);
},
render() {
const dropdownTheme = {
btn: Styles.btnShare,
outer: Styles.sharePanel,
inner: Styles.dropdownInner,
icon: 'share'
};
return (
<div>
<MenuPanel theme={dropdownTheme}
btnText="Share"
viewState={this.props.viewState}
btnTitle="Share your map with others"
isOpen={this.state.isOpen}
onOpenChanged={this.changeOpenState}
smallScreen={this.props.viewState.useSmallScreenInterface}>
<If condition={this.state.isOpen}>
{this.renderContent()}
</If>
</MenuPanel>
</div>
);
},
});
export default SharePanel;