catreact
Version:
Catavolt Core React Components
337 lines (336 loc) • 20.3 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, CvWorkbench, CvScope, CvLauncher, CvNavigation, CvForm, CvListPane, CvDetailsPane, CvRecord, CvRecordList, CvProp, CvResource, CvAction, CvLoginPanel, CvValueAdapter } from '../../catreact';
import { Log, LogLevel } from 'catavolt-sdk';
Log.logLevel(LogLevel.DEBUG);
const BuzzBase = {
contextTypes: {
router: React.PropTypes.object
},
};
const BuzzApp = React.createClass({
mixins: [BuzzBase],
render: function () {
return (<div className="container">
<CatavoltPane enableResourceCaching={true}>
<div>
<div className="header"></div>
{this.props.children}
</div>
</CatavoltPane>
</div>);
}
});
const BuzzLogin = React.createClass({
mixins: [BuzzBase],
render: function () {
return <CvLoginPanel defaultGatewayUrl={'gw.catavolt.net'} defaultTenantId={'catavolt-dev'} defaultUserId={'rob'} defaultPassword={'rob123'} showGatewayUrl={false} showClientType={false} loginListeners={[(event) => {
const sessionId = event.resourceId; //get the session from the LoginEvent
this.context.router.push('/workbench/' + sessionId);
}]}/>;
}
});
/*
Sets up a buzz workbench with one launcher for 'streams'
*/
const BuzzWorkbench = React.createClass({
mixins: [BuzzBase],
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
return (<CvAppWindow windowId={windowId}>
<span>
<CvWorkbench workbenchId={"AAABACffAAAABpZL"}>
<div className="panel panel-primary">
<div className="panel-heading">
<h3 className="panel-title">
<CvScope get={'name'}/>
</h3>
</div>
<div className="panel-body row">
<CvLauncher actionId={"AAABACfaAAAABpIk"} launchListeners={[(launchEvent) => {
const navigationId = launchEvent.resourceId;
this.context.router.push('/buzzstream/' + windowId + '/' + navigationId);
}]} renderer={(cvContext, callback) => {
const launcher = cvContext.scopeCtx.scopeObj;
return (<div onClick={callback.fireLaunchAction} className="col-sm-8 launch-div">
<img className="launch-icon img-responsive center-block" src={launcher.iconBase}/>
<h4 className="launch-text small text-center">{launcher.name}</h4>
</div>);
}}/>
</div>
</div>
</CvWorkbench>
</span>
</CvAppWindow>);
}
});
/*
Component that shows the 'streams' list
*/
const BuzzStream = React.createClass({
mixins: [BuzzBase],
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
const navigationId = this.props.params.navigationId;
return (<CvAppWindow windowId={windowId}>
<span>
<CvNavigation persistent={false} navigationId={navigationId}>
<CvForm>
<div className="panel panel-primary">
<div className="panel-heading">
<h4>
<CvScope get={'paneTitle'}/>
</h4>
</div>
<div style={{ maxHeight: '800px', overflow: 'auto' }}>
<ul className={'list-group'}>
<CvListPane paneRef={0}>
<CvRecordList wrapperElemName={'span'} rowRenderer={(cvContext, record) => {
const listContext = cvContext.scopeCtx.scopeObj;
//select "this record" so that the action can find the target via the selectionProvider
const selectionAdapter = new CvValueAdapter();
selectionAdapter.createValueListener()([record.objectId]);
return (<CvRecord entityRec={record} key={record.objectId}>
<CvAction actionId={listContext.listDef.defaultActionId} paneContext={listContext} navigationListeners={[(navEvent) => {
const navigationId = navEvent.resourceId;
this.context.router.push('/buzzmessages/' + windowId + '/' + navigationId);
}]} selectionProvider={selectionAdapter} wrapperElemName="li" wrapperElemProps={{ className: "list-group-item" }}>
<CvProp propName={'name'}/>
</CvAction>
</CvRecord>);
}}/>
</CvListPane>
</ul>
</div>
</div>
</CvForm>
</CvNavigation>
</span>
</CvAppWindow>);
}
});
/*
Component that shows the buzz 'Messages' list and allows for posting
*/
const BuzzMessages = React.createClass({
mixins: [BuzzBase],
getInitialState: function () {
return { openNewMessageFormResult: null };
},
render: function () {
const windowId = this.props.params.windowId; //get the window from the url param
const navigationId = this.props.params.navigationId;
return (<CvAppWindow windowId={windowId}>
<span>
<CvNavigation navigationId={navigationId} persistent={true}>
<CvForm>
<CvListPane paneRef={0}>
<div className="panel panel-primary">
<div className="panel-heading">
<i id={'loading'} className="fa fa-spinner fa-pulse fa-2x pull-right show"/>
<h4>Messages</h4>
<div className="messageToolbar text-right">
<CvAction actionId={'alias_createMessage'} navigationListeners={[(e) => {
this.setState({ openNewMessageFormResult: e.eventObj });
}]} actionListeners={[(e) => {
$('#loading').addClass('show').removeClass('hidden');
}]}>
<span>
<CvResource resourceName={'icon-action-join.png'}/>
<a className="hlText">New Message</a>
</span>
</CvAction>
</div>
</div>
<div style={{ maxHeight: '800px', overflow: 'auto' }}>
<div className="messageCol">
<CvRecordList wrapperElemName={"span"} rowRenderer={(cvContext, record) => {
$('#loading').addClass('hidden').removeClass('show');
return (<CvRecord entityRec={record} key={record.objectId}>
<div className="row">
<div className="col-sm-12">
<div className="messagePanel">
<div className="row">
<div className="col-sm-6">
<div className="row">
<div className="col-sm-2">
<CvProp propName={'avatar_large'} className={'img-rounded avatar'}/>
</div>
<div className="col-sm-4 text-center attrib-box">
<h4> <CvProp propName={'created-by'}/> </h4>
<small> <CvProp propName={'group_name'}/> </small>
<small className="text-muted"> <CvProp propName={'created-at'}/> </small>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="pull-right">
<CvProp propName={'is_flagged'} handler={(prop) => {
return prop.value ?
<CvResource resourceName={'icon-bookmark.png'} style={{ width: 24, height: 38 }}/> :
<CvResource resourceName={'icon-bookmark-unchecked.png'} style={{ width: 24, height: 38 }}/>;
}}/>
</div>
</div>
</div>
<div className="like-row">
<span> <CvProp propName={'likes_count'}/> </span>
<span>liked</span> <span /> <span />
<span> <CvProp propName={'comments_count'}/> </span>
<span>comments</span>
</div>
<div> <div> <CvProp propName={'title'}/> </div>
<blockquote> <p> <CvProp propName={'body_preview'}/> </p> </blockquote>
<div className="text-center">{function () {
const attachments = [];
for (let i = 1; i <= 10; i++) {
attachments.push(<CvProp propName={'attachment_preview_' + i} key={'' + i}/>);
}
return attachments;
}()}
</div>
</div>
<div className="badge-row">
<div className="text-right">
<CvProp propName={'deletable'} handler={(prop) => {
return prop.value ?
<CvResource resourceName={'icon-humana-delete.png'} style={{ width: 20, height: 20, marginRight: 5 }}/>
: null;
}}/>
<CvResource resourceName={'icon-action-comment.png'} style={{ width: 24, height: 24 }}/>
</div>
</div>
</div>
</div>
</div>
</CvRecord>);
}}/>
</div>
</div>
</div>
</CvListPane>
</CvForm>
</CvNavigation>
<BuzzPostMessage navigationResult={this.state.openNewMessageFormResult}/>
</span>
</CvAppWindow>);
}
});
const BuzzPostMessage = React.createClass({
mixins: [BuzzBase],
getDefaultProps: function () {
return { navigationResult: null };
},
render: function () {
return (<CvNavigation navigationResult={this.props.navigationResult}>
<CvForm>
<CvDetailsPane paneRef={0} detailsRenderer={(cvContext, record, detailsCallback) => {
return (<CvRecord entityRec={record} renderer={(cvContext) => {
const entityRec = cvContext.scopeCtx.scopeObj;
const eventRegistry = cvContext.eventRegistry;
const maxAttachments = 4;
var numAttachments = 0;
const propChange = (fieldName, e) => {
detailsCallback.setPropValue(fieldName, e.target.value);
};
const imageChange = (fieldName, e) => {
numAttachments++;
if (numAttachments > maxAttachments) {
alert('4 is the maximum number of attachments.');
}
else {
if (e.target.files) {
const files = e.target.files;
if (files[0]) {
var fr = new FileReader();
fr.onload = function (e) {
const propName = fieldName + numAttachments;
const imgEl = $('#' + propName + '_preview');
const dataUrl = e.target.result;
imgEl.attr("src", dataUrl);
detailsCallback.setBinaryPropWithDataUrl(propName, dataUrl);
};
fr.readAsDataURL(files[0]);
}
}
}
};
const onSubmit = (e) => {
e.preventDefault();
$('#loading').addClass('show').removeClass('hidden');
detailsCallback.saveChanges((navEvent, error) => {
if (error) {
$('#loading').addClass('hidden').removeClass('show');
Log.error(error);
}
else {
if (navEvent)
Log.info("There was a navigation.");
var m = $('#postModal');
if (m && m.modal) {
m.modal('hide');
}
}
});
};
return (<form className="form-horizontal" onSubmit={onSubmit}>
<div className="modal fade" role="dialog" id="postModal" ref={(d) => {
var m = $(d);
if (m && m.modal) {
m.modal({ show: true, keyboard: true });
}
$('#loading').addClass('hidden').removeClass('show');
}}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">Post A New Message</h4>
</div>
<div className="modal-body">
<div className="well">
<div className="form-group">
<label htmlFor="messageBody" className="col-sm-3 control-label small">Message:</label>
<div className="col-sm-9">
<textarea className="form-control" id="messageBody" onBlur={propChange.bind(this, 'P_body')}/>
</div>
</div>
<div className="form-group">
<label htmlFor="image1" className="col-sm-3 control-label small">Add attachment: </label>
<div className="col-sm-9">
<input id="image1" type="file" className="form-control" onChange={imageChange.bind(undefined, 'P_image')}/>
<br />
<div><img className="image-attachments center-block img-rounded" id={'P_image1_preview'}/></div>
<div><img className="image-attachments center-block img-rounded" id={'P_image2_preview'}/></div>
<div><img className="image-attachments center-block img-rounded" id={'P_image3_preview'}/></div>
<div><img className="image-attachments center-block img-rounded" id={'P_image4_preview'}/></div>
</div>
</div>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" className="btn btn-primary">Post <span className="glyphicon glyphicon-log-in" aria-hidden="true"/></button>
</div>
</div>
</div>
</div>
</form>);
}}/>);
}}/>
</CvForm>
</CvNavigation>);
}
});
ReactDOM.render(<Router history={hashHistory}>
<Route path="/" component={BuzzApp}>
<IndexRoute component={BuzzLogin}/>
<Route path="/workbench/:windowId" component={BuzzWorkbench}/>
<Route path="/buzzstream/:windowId/:navigationId" component={BuzzStream}/>
<Route path="/buzzmessages/:windowId/:navigationId" component={BuzzMessages}/>
</Route>
</Router>, document.getElementById('cvApp'));