catreact
Version:
Catavolt Core React Components
354 lines (351 loc) • 15 kB
JSX
/**
* Created by rburson on 2/8/16.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, Route, hashHistory, IndexRoute } from 'react-router';
import { CatavoltPane, CvAppWindow, CvLoginPanel, CvGraphicalWorkbench, CvWorkbenchManager, CvNavigator, CvDropdownWorkbenchMenu, CvValueAdapter, CvLogout, CvStateChangeType, CvNavigationResultType, CvActionFiredResultType, CvMessagePanel } from '../../catreact';
import { Log, LogLevel } from 'catavolt-sdk';
Log.logLevel(LogLevel.DEBUG);
const CvReactBase = {
componentWillMount: function () {
this.waitProvider = new CvValueAdapter();
this.waitListener = this.waitProvider.createValueListener();
this.navPopupProvider = new CvValueAdapter();
this.navPopupListener = this.navPopupProvider.createValueListener();
},
contextTypes: {
router: React.PropTypes.object
},
_showWaitState: function (event) {
if (this.isMounted()) {
if (event.eventObj.type === CvActionFiredResultType.ACTION_STARTED) {
this.waitListener(true);
}
else if (event.eventObj.type === CvActionFiredResultType.ACTION_COMPLETED) {
this.waitListener(false);
}
}
},
};
/**
* *********************************************************
* Create a Catavolt root pane and top-level wrapper
* *********************************************************
*/
const CvReactApp = React.createClass({
mixins: [CvReactBase],
render: function () {
return (<div className="cv-container container-fluid">
<CatavoltPane enableResourceCaching={true}>
{this.props.children}
</CatavoltPane>
<CvReactFooter />
</div>);
}
});
const CvReactFooter = React.createClass({
render: function () {
return <div className="cv-footer navbar navbar-fixed-bottom">
<ul className="list-inline text-right">
<li><a className="cv-target">About</a></li>
<li><a className="cv-target">Contact</a></li>
</ul>
</div>;
}
});
/**
* *********************************************************
* Create and configure a basic login panel
* *********************************************************
*/
const CvReactLogin = React.createClass({
mixins: [CvReactBase],
render: function () {
return <div>
<div className="cv-login-wrapper">
<div className="cv-login-logo"/>
<CvLoginPanel defaultGatewayUrl={'gw.catavolt.net'} defaultTenantId={'solarsourcez'} defaultUserId={'sales'} showGatewayUrl={false} showClientType={false} loginListeners={[(event) => {
const windowId = event.resourceId; //get the session (window) from the LoginEvent
this.context.router.replace('/workbench/' + windowId + '/' + '0');
}]}/>
<CvMessagePanel />
</div>
</div>;
}
});
/**
* *********************************************************
* Create a 'window' top-level container, with a logout button
* *********************************************************
*/
const CvReactWindow = React.createClass({
mixins: [CvReactBase],
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
return <CvAppWindow windowId={windowId}>
<div className="cv-window">
<div className="cv-logo pull-left"/>
<CvMessagePanel />
<div className="cv-top-nav text-right">
<CvLogout renderer={(cvContext, callback) => {
return <a className="cv-target" onClick={callback.logout}>Logout</a>;
}} logoutListeners={[() => { this.context.router.replace('/'); }]}/>
</div>
{this.props.children}
</div>
</CvAppWindow>;
}
});
/**
* *********************************************************
* Create a Workbench for the supplied window
* *********************************************************
*/
const CvReactWorkbench = React.createClass({
mixins: [CvReactBase],
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
let workbenchId = this.props.params.workbenchId; //get the workbench from the url param
const selectionAdapter = new CvValueAdapter(); //the glue for our menu and workbench
selectionAdapter.subscribe((workbench) => {
this.context.router.replace('/workbench/' + windowId + '/' + workbench.workbenchId);
});
let workbenchEl = null;
/**
****************************************************************
* Example 1 - Graphical Workbench with a 'Dropdown' Menu
****************************************************************
*/
//*
const menuRenderer = () => {
return <div className="col-sm-3 col-sm-offset-9 text-right">
<CvDropdownWorkbenchMenu workbenchSelectionListener={selectionAdapter.createValueListener()} initialSelectedWorkbenchId={workbenchId}/>
</div>;
};
const workbenchRenderer = () => {
return <CvGraphicalWorkbench initialWorkbenchId={workbenchId} numCols={3} actionListeners={[this._showWaitState]} launchListeners={[(launchEvent) => {
const navigationId = launchEvent.resourceId;
this.context.router.push('/navigator/' + windowId + '/' + navigationId);
}]}/>;
};
workbenchEl = <CvWorkbenchManager menuRenderer={menuRenderer} workbenchRenderer={workbenchRenderer} selectionProvider={selectionAdapter}/>;
//*/
/**
*********** End Example 1 ****************************************
*/
/**
****************************************************************
* Example 2 - Graphical Workbench with a 'Tabbed' Menu
****************************************************************
*/
/*
const menuRenderer = ()=>{
return <div className="cv-workbench-tab-menu">
<CvTabbedWorkbenchMenu workbenchSelectionListener={selectionAdapter.createValueListener()} initialSelectedWorkbenchId={workbenchId}/>
</div>;
}
const workbenchRenderer = ()=>{
return <CvGraphicalWorkbench initialWorkbenchId={workbenchId} numCols={3}
actionListeners={[this._showWaitState]}
launchListeners={[(launchEvent:CvEvent<CvNavigationResult>)=>{
const navigationId = launchEvent.resourceId;
this.context.router.push('/navigator/' + windowId + '/' + navigationId);
}]}
/>
}
workbenchEl = <CvWorkbenchManager menuRenderer={menuRenderer} workbenchRenderer={workbenchRenderer} selectionProvider={selectionAdapter}/>
*/
/**
*********** End Example 2 ****************************************
*/
/**
****************************************************************
* Example 3 - Place a specific workbench (without menu)
****************************************************************
*/
/*
workbenchEl = <CvGraphicalWorkbench initialWorkbenchId="AAABACffAAAAAa6T" numCols={4}
actionListeners={[this._showWaitState]}
launchListeners={[(launchEvent:CvEvent<CvNavigationResult>)=>{
const navigationId = launchEvent.resourceId;
this.context.router.push('/navigator/' + windowId + '/' + navigationId);
}]}
/>
*/
/**
*********** End Example 3 ****************************************
*/
return (<div>
{workbenchEl}
<CvWaitPopup dataProvider={this.waitProvider}/>
</div>);
},
});
/**
* *********************************************************
* Create a Navigator for the supplied window
* e.g. layoutOverrideElem={CvTabbedFormPanel}
* or
* layoutOverrideElem={CvVerticalLayoutFormPane} //mobile
* *********************************************************
*/
const CvReactNavigator = React.createClass({
mixins: [CvReactBase],
componentWillMount: function () {
console.log("mounting CvReactNavigator");
},
componentWillReceiveProps: function (nextProps) {
console.log("receive props CvReactNavigator");
},
componentWillUpdate: function (nextProps, nextState, nextContext) {
console.log("updating CvReactNavigator");
},
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
const currentNavId = this.props.params.navId;
const router = this.context.router;
return <div>
<CvNavigator navigationId={currentNavId} navigationListeners={[(event) => {
const nextNavigationId = event.resourceId;
if (nextNavigationId) {
if (event.eventObj.type === CvNavigationResultType.FORM) {
if (event.eventObj.noTransition) {
this.navPopupListener(nextNavigationId);
}
else {
const target = '/navigator/' + windowId + '/' + nextNavigationId;
event.eventObj.sourceIsDestroyed ? router.replace(target) : router.push(target);
}
}
else if (event.eventObj.type === CvNavigationResultType.URL) {
window.open(event.eventObj.navRequest.webURL, '_blank');
}
}
else {
//force a refresh if there's no new resource (i.e. a NullNavigation)
router.replace('/navigator/' + windowId + '/' + currentNavId);
}
}]} actionListeners={[this._showWaitState]} stateChangeListeners={[(event) => {
if (event.eventObj.type === CvStateChangeType.DESTROYED) {
router.goBack();
}
}]}/>
<CvPopupNavigator dataProvider={this.navPopupProvider}/>
<CvWaitPopup dataProvider={this.waitProvider}/>
</div>;
},
});
/**
Component that overlays a 'wait' spinner, shown between navigations
*/
const CvWaitPopup = React.createClass({
componentWillMount: function () {
if (this.props.dataProvider) {
this.props.dataProvider.subscribe(this._handleDataChange);
}
},
getDefaultProps: function () {
return { dataProvider: null };
},
getInitialState: function () {
return { visible: false };
},
render: function () {
return (<div className="modal" role="dialog" ref={(d) => {
if (this.state.visible) {
var m = $(d);
if (m && m.modal) {
m.modal({ show: true, keyboard: false, backdrop: 'static' });
}
}
else {
var m = $(d);
if (m && m.modal) {
m.modal('hide');
}
}
}}>
<div className="modal-dialog">
<div className="modal-body">
<div className="cv-wait-screen text-center">
<i className="fa fa-spinner fa-pulse fa-5x"/>
</div>
</div>
</div>
</div>);
},
_handleDataChange: function (visible) {
this.setState({ visible: visible });
},
});
const CvPopupNavigator = React.createClass({
componentWillMount: function () {
if (this.props.dataProvider) {
this.props.dataProvider.subscribe(this._handleDataChange);
}
},
getDefaultProps: function () {
return { dataProvider: null };
},
getInitialState: function () {
return { navigationId: null };
},
render: function () {
return (<div className="modal" role="dialog" ref={(d) => {
if (this.state.navigationId) {
var m = $(d);
if (m && m.modal) {
m.modal({ show: true, keyboard: false, backdrop: 'static' });
}
}
else {
var m = $(d);
if (m && m.modal) {
m.modal('hide');
}
}
}}>
<div className="modal-dialog modal-lg">
<div className="modal-body">
<div className="cv-popup-nav-container">
{this.state.navigationId ?
<CvNavigator navigationId={this.state.navigationId} navigationListeners={[(event) => {
const nextNavigationId = event.resourceId;
if (nextNavigationId) {
if (event.eventObj.type === CvNavigationResultType.FORM) {
this.setState({ navigationId: nextNavigationId });
}
else if (event.eventObj.type === CvNavigationResultType.URL) {
window.open(event.eventObj.navRequest.webURL, '_blank');
}
}
}]} actionListeners={[]} stateChangeListeners={[(event) => {
if (event.eventObj.type === CvStateChangeType.DESTROYED) {
this.setState({ navigationId: null });
}
}]}/>
: null}
</div>
</div>
</div>
</div>);
},
_handleDataChange: function (navigationId) {
this.setState({ navigationId: navigationId });
}
});
/**
* *********************************************************
* Wire things together with 'routes'
* *********************************************************
*/
ReactDOM.render(<Router history={hashHistory}>
<Route path="/" component={CvReactApp}>
<IndexRoute component={CvReactLogin}/>
<Route path="app" component={CvReactWindow}>
<Route path="/workbench/:windowId/:workbenchId" component={CvReactWorkbench}/>
<Route path="/navigator/:windowId/:navId" component={CvReactNavigator}/>
</Route>
</Route>
</Router>, document.getElementById('cvApp'));