add view all button, add dropdown for contentviews

This commit is contained in:
Clemens 2016-08-03 12:08:10 +02:00
parent bcc496527e
commit 34fe391afb
7 changed files with 213 additions and 64 deletions

View File

@ -65,6 +65,7 @@
} }
.view-options { .view-options {
margin-bottom: 10px;
margin-top: 10px; margin-top: 10px;
} }

View File

@ -5,6 +5,7 @@ import * as MetaViews from './ContentView/MetaViews'
import ViewSelector from './ContentView/ViewSelector' import ViewSelector from './ContentView/ViewSelector'
import UploadContentButton from './ContentView/UploadContentButton' import UploadContentButton from './ContentView/UploadContentButton'
import DownloadContentButton from './ContentView/DownloadContentButton' import DownloadContentButton from './ContentView/DownloadContentButton'
import ShowFullContentButton from './ContentView/ShowFullContentButton'
import { setContentView, displayLarge, updateEdit } from '../ducks/ui/flow' import { setContentView, displayLarge, updateEdit } from '../ducks/ui/flow'
@ -19,7 +20,7 @@ ContentView.propTypes = {
ContentView.isContentTooLarge = msg => msg.contentLength > 1024 * 1024 * (ContentViews.ViewImage.matches(msg) ? 10 : 0.2) ContentView.isContentTooLarge = msg => msg.contentLength > 1024 * 1024 * (ContentViews.ViewImage.matches(msg) ? 10 : 0.2)
function ContentView(props) { function ContentView(props) {
const { flow, message, contentView, isDisplayLarge, displayLarge, uploadContent, onContentChange, readonly } = props const { flow, message, contentView, isDisplayLarge, displayLarge, uploadContent, onContentChange, readonly, contentViewDescription } = props
if (message.contentLength === 0 && readonly) { if (message.contentLength === 0 && readonly) {
return <MetaViews.ContentEmpty {...props}/> return <MetaViews.ContentEmpty {...props}/>
@ -37,13 +38,15 @@ function ContentView(props) {
return ( return (
<div className="contentview"> <div className="contentview">
<View flow={flow} message={message} contentView={contentView} readonly={readonly} onChange={onContentChange}/> <View flow={flow} message={message} contentView={contentView} readonly={readonly} onChange={onContentChange}/>
<ShowFullContentButton/>
<div className="view-options text-center"> <div className="view-options">
<ViewSelector message={message}/> <ViewSelector message={message}/>
&nbsp; &nbsp;
<DownloadContentButton flow={flow} message={message}/> <DownloadContentButton flow={flow} message={message}/>
&nbsp; &nbsp;
<UploadContentButton uploadContent={uploadContent}/> <UploadContentButton uploadContent={uploadContent}/>
&nbsp;
<span>{contentViewDescription}</span>
</div> </div>
</div> </div>
) )
@ -53,6 +56,7 @@ export default connect(
state => ({ state => ({
contentView: state.ui.flow.contentView, contentView: state.ui.flow.contentView,
isDisplayLarge: state.ui.flow.displayLarge, isDisplayLarge: state.ui.flow.displayLarge,
contentViewDescription: state.ui.flow.viewDescription
}), }),
{ {
displayLarge, displayLarge,

View File

@ -1,4 +1,6 @@
import React, { PropTypes } from 'react' import React, { PropTypes, Component } from 'react'
import { connect } from 'react-redux'
import { setContentViewDescription, setShowFullContent } from '../../ducks/ui/flow'
import ContentLoader from './ContentLoader' import ContentLoader from './ContentLoader'
import { MessageUtils } from '../../flow/utils' import { MessageUtils } from '../../flow/utils'
import CodeEditor from './CodeEditor' import CodeEditor from './CodeEditor'
@ -27,32 +29,63 @@ function Edit({ content, onChange }) {
} }
Edit = ContentLoader(Edit) Edit = ContentLoader(Edit)
class ViewServer extends Component {
constructor(props){
super(props)
this.maxLines = 80
}
function ViewServer(props){ componentWillMount(){
const {content, contentView, message} = props this.setContentView(this.props)
let data = JSON.parse(content) }
componentWillReceiveProps(nextProps){
this.setContentView(nextProps)
}
setContentView(props){
try {
this.data = JSON.parse(props.content)
}catch(err) {
this.data= {lines: [], description: err.message}
}
return <div> props.setContentViewDescription(props.contentView != this.data.description ? this.data.description : '')
{contentView != data.description &&
<div className="alert alert-warning">{data.description}</div> let isFullContentShown = this.data.lines.length < this.maxLines
} if (isFullContentShown) props.setShowFullContent(true)
<pre> }
{data.lines.map((line, i) => render() {
<div key={`line${i}`}> const {content, contentView, message} = this.props
{line.map((tuple, j) =>
<span key={`tuple${j}`} className={tuple[0]}> let lines = this.props.showFullContent ? this.data.lines : this.data.lines.slice(0, this.maxLines)
{tuple[1]}
</span> return <div>
)} <pre>
</div> {lines.map((line, i) =>
)} <div key={`line${i}`}>
</pre> {line.map((tuple, j) =>
<span key={`tuple${j}`} className={tuple[0]}>
{tuple[1]}
</span>
)}
</div>
)}
</pre>
{ViewImage.matches(message) && {ViewImage.matches(message) &&
<ViewImage {...props} /> <ViewImage {...this.props} />
} }
</div> </div>
}
} }
ViewServer = ContentLoader(ViewServer) ViewServer = connect(
state => ({
showFullContent: state.ui.flow.showFullContent
}),
{
setContentViewDescription,
setShowFullContent
}
)(ContentLoader(ViewServer))
export { Edit, ViewServer, ViewImage } export { Edit, ViewServer, ViewImage }

View File

@ -0,0 +1,29 @@
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { render } from 'react-dom';
import Button from '../common/Button';
import { setShowFullContent } from '../../ducks/ui/flow'
ShowFullContentButton.propTypes = {
setShowFullContent: PropTypes.func.isRequired,
showFullContent: PropTypes.bool.isRequired
}
function ShowFullContentButton ( {setShowFullContent, showFullContent} ){
return (
!showFullContent && <Button isXs={true} onClick={() => setShowFullContent(true)} text="Show full content"/>
)
}
export default connect(
state => ({
showFullContent: state.ui.flow.showFullContent
}),
{
setShowFullContent
}
)(ShowFullContentButton)

View File

@ -1,48 +1,82 @@
import React, { PropTypes } from 'react' import React, { PropTypes, Component } from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import * as ContentViews from './ContentViews' import * as ContentViews from './ContentViews'
import { setContentView } from "../../ducks/ui/flow"; import { setContentView, setContentViewSelectorOpen } from "../../ducks/ui/flow";
function ViewButton({ name, setContentView, children, activeView }) { function ViewItem({ name, setContentView, children }) {
return ( return (
<button <li>
onClick={() => setContentView(name)} <a href="#" onClick={() => setContentView(name)}>
className={classnames('btn btn-default', { active: name === activeView })}> {children}
{children} </a>
</button> </li>
) )
} }
ViewButton = connect(state => ({
activeView: state.ui.flow.contentView
}), {
setContentView
})(ViewButton)
ViewSelector.propTypes = { /*ViewSelector.propTypes = {
message: PropTypes.object.isRequired, contentViews: PropTypes.array.isRequired,
} activeView: PropTypes.string.isRequired,
function ViewSelector({contentViews, isEdit }) { isEdit: PropTypes.bool.isRequired,
let edit = ContentViews.Edit.displayName isContentViewSelectorOpen: PropTypes.bool.isRequired,
return ( setContentViewSelectorOpen: PropTypes.func.isRequired
<div className="view-selector btn-group btn-group-xs"> }*/
{contentViews.map(name =>
<ViewButton key={name} name={name}>{name.toLowerCase().replace('_', ' ')}</ViewButton>
)}
{isEdit && class ViewSelector extends Component {
<ViewButton key={edit} name={edit}>{edit.toLowerCase()}</ViewButton> constructor(props, context) {
} super(props, context)
this.close = this.close.bind(this)
}
close() {
this.props.setContentViewSelectorOpen(false)
document.removeEventListener('click', this.close)
}
</div> onDropdown(e){
) e.preventDefault()
this.props.setContentViewSelectorOpen(!this.props.isContentViewSelectorOpen)
document.addEventListener('click', this.close)
}
render() {
const {contentViews, activeView, isEdit, isContentViewSelectorOpen, setContentViewSelectorOpen, setContentView} = this.props
let edit = ContentViews.Edit.displayName
return (
<div className={classnames('dropup pull-left', { open: isContentViewSelectorOpen })}>
<a className="btn btn-default btn-xs"
onClick={ e => this.onDropdown(e) }
href="#">
<b>View:</b> {activeView}<span className="caret"></span>
</a>
<ul className="dropdown-menu" role="menu">
{contentViews.map(name =>
<ViewItem key={name} setContentView={setContentView} name={name}>
{name.toLowerCase().replace('_', ' ')}
</ViewItem>
)}
{isEdit &&
<ViewItem key={edit} setContentView={setContentView} name={edit}>
{edit.toLowerCase()}
</ViewItem>
}
</ul>
</div>
)
}
} }
export default connect ( export default connect (
state => ({ state => ({
contentViews: state.settings.contentViews, contentViews: state.settings.contentViews,
activeView: state.ui.flow.contentView,
isEdit: !!state.ui.flow.modifiedFlow, isEdit: !!state.ui.flow.modifiedFlow,
}))(ViewSelector) isContentViewSelectorOpen: state.ui.flow.isContentViewSelectorOpen
}), {
setContentView,
setContentViewSelectorOpen
}
)(ViewSelector)

View File

@ -1,4 +1,5 @@
import React, { PropTypes } from 'react' import React, { PropTypes } from 'react'
import classnames from 'classnames'
Button.propTypes = { Button.propTypes = {
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,
@ -6,9 +7,9 @@ Button.propTypes = {
icon: PropTypes.string icon: PropTypes.string
} }
export default function Button({ onClick, text, icon, disabled }) { export default function Button({ onClick, text, icon, disabled, isXs }) {
return ( return (
<div className={"btn btn-default"} <div className={classnames('btn btn-default', { 'btn-xs': isXs})}
onClick={onClick} onClick={onClick}
disabled={disabled}> disabled={disabled}>
{icon && (<i className={"fa fa-fw " + icon}/> )} {icon && (<i className={"fa fa-fw " + icon}/> )}

View File

@ -3,16 +3,22 @@ import { getDiff } from "../../utils"
import _ from 'lodash' import _ from 'lodash'
export const SET_CONTENT_VIEW = 'UI_FLOWVIEW_SET_CONTENT_VIEW', export const SET_CONTENT_VIEW = 'UI_FLOWVIEW_SET_CONTENT_VIEW',
DISPLAY_LARGE = 'UI_FLOWVIEW_DISPLAY_LARGE', DISPLAY_LARGE = 'UI_FLOWVIEW_DISPLAY_LARGE',
SET_TAB = "UI_FLOWVIEW_SET_TAB", SET_TAB = "UI_FLOWVIEW_SET_TAB",
START_EDIT = 'UI_FLOWVIEW_START_EDIT', START_EDIT = 'UI_FLOWVIEW_START_EDIT',
UPDATE_EDIT = 'UI_FLOWVIEW_UPDATE_EDIT', UPDATE_EDIT = 'UI_FLOWVIEW_UPDATE_EDIT',
UPLOAD_CONTENT = 'UI_FLOWVIEW_UPLOAD_CONTENT' UPLOAD_CONTENT = 'UI_FLOWVIEW_UPLOAD_CONTENT',
SET_SHOW_FULL_CONTENT = 'UI_SET_SHOW_FULL_CONTENT',
SET_CONTENT_VIEW_DESCRIPTION = "UI_SET_CONTENT_VIEW_DESCRIPTION",
SET_CONTENT_VIEW_SELECTOR = "UI_SET_CONTENT_VIEW_SELECTOR"
const defaultState = { const defaultState = {
displayLarge: false, displayLarge: false,
contentViewDescription: '',
showFullContent: false,
isContentViewSelectorOpen: false,
modifiedFlow: false, modifiedFlow: false,
contentView: 'Auto', contentView: 'Auto',
tab: 'request', tab: 'request',
@ -26,7 +32,8 @@ export default function reducer(state = defaultState, action) {
return { return {
...state, ...state,
modifiedFlow: action.flow, modifiedFlow: action.flow,
contentView: 'Edit' contentView: 'Edit',
showFullContent: true
} }
case UPDATE_EDIT: case UPDATE_EDIT:
@ -40,7 +47,9 @@ export default function reducer(state = defaultState, action) {
...state, ...state,
modifiedFlow: false, modifiedFlow: false,
displayLarge: false, displayLarge: false,
contentView: (wasInEditMode ? 'Auto' : state.contentView) contentView: (wasInEditMode ? 'Auto' : state.contentView),
viewDescription: '',
showFullContent: false,
} }
case flowsActions.UPDATE: case flowsActions.UPDATE:
@ -52,24 +61,46 @@ export default function reducer(state = defaultState, action) {
...state, ...state,
modifiedFlow: false, modifiedFlow: false,
displayLarge: false, displayLarge: false,
contentView: (wasInEditMode ? 'Auto' : state.contentView) contentView: (wasInEditMode ? 'Auto' : state.contentView),
viewDescription: '',
showFullContent: false
} }
} else { } else {
return state return state
} }
case SET_CONTENT_VIEW_DESCRIPTION:
return {
...state,
viewDescription: action.description
}
case SET_SHOW_FULL_CONTENT:
return {
...state,
showFullContent: action.show
}
case SET_CONTENT_VIEW_SELECTOR:
return {
...state,
isContentViewSelectorOpen: action.contentViewSelector
}
case SET_TAB: case SET_TAB:
return { return {
...state, ...state,
tab: action.tab, tab: action.tab,
displayLarge: false, displayLarge: false,
showFullContent: false
} }
case SET_CONTENT_VIEW: case SET_CONTENT_VIEW:
return { return {
...state, ...state,
contentView: action.contentView, contentView: action.contentView,
showFullContent: action.contentView == 'Edit'
} }
case DISPLAY_LARGE: case DISPLAY_LARGE:
@ -102,6 +133,22 @@ export function updateEdit(update) {
return { type: UPDATE_EDIT, update } return { type: UPDATE_EDIT, update }
} }
export function setContentViewDescription(description) {
return { type: SET_CONTENT_VIEW_DESCRIPTION, description }
}
export function setShowFullContent(show) {
return { type: SET_SHOW_FULL_CONTENT, show }
}
export function setContentViewSelectorOpen(open){
return {type: SET_CONTENT_VIEW_SELECTOR, contentViewSelector: open}
}
export function updateEdit(update) {
return { type: UPDATE_EDIT, update }
}
export function stopEdit(flow, modifiedFlow) { export function stopEdit(flow, modifiedFlow) {
let diff = getDiff(flow, modifiedFlow) let diff = getDiff(flow, modifiedFlow)
return flowsActions.update(flow, diff) return flowsActions.update(flow, diff)