Compare commits

...
Author SHA1 Message Date
Thomas Nordquist 181575f71f Prepare release v0.3.0-alpha2 2019-06-17 18:29:46 +02:00
Thomas Nordquist 3d5a4af705 Tidy up ui 2019-06-17 18:27:19 +02:00
Thomas Nordquist 4e982be613 Show notification when adding charts 2019-06-17 18:19:26 +02:00
Thomas Nordquist 8c1c6387c9 Add colors and move capability to charts 2019-06-17 18:08:35 +02:00
17 changed files with 323 additions and 26 deletions
+15 -1
View File
@@ -2,7 +2,7 @@ import { Action, ActionTypes, ChartParameters } from '../reducers/Charts'
import { AppState } from '../reducers'
import { default as persistentStorage, StorageIdentifier } from '../utils/PersistentStorage'
import { Dispatch } from 'redux'
import { showError } from './Global'
import { showError, showNotification } from './Global'
interface ConnectionViewState {
charts: Array<ChartParameters>
@@ -72,6 +72,7 @@ export const addChart = (chartParameters: ChartParameters) => async (
.find(chart => chart.topic === chartParameters.topic && chart.dotPath === chartParameters.dotPath)
)
if (chartExists) {
dispatch(showNotification('Already added'))
return
}
@@ -80,6 +81,7 @@ export const addChart = (chartParameters: ChartParameters) => async (
chart: chartParameters,
})
dispatch(saveCharts())
dispatch(showNotification('Added to chart panel'))
}
export const updateChart = (chartParameters: ChartParameters) => async (
@@ -106,6 +108,18 @@ export const removeChart = (chartParameters: ChartParameters) => async (
dispatch(saveCharts())
}
export const moveChartUp = (parameters: { topic: string; dotPath?: string }) => async (
dispatch: Dispatch<any>,
getState: () => AppState
) => {
dispatch({
topic: parameters.topic,
dotPath: parameters.dotPath,
type: ActionTypes.CHARTS_MOVE_UP,
})
dispatch(saveCharts())
}
export const setCharts = (charts: Array<ChartParameters>): Action => {
return {
charts,
+1
View File
@@ -53,6 +53,7 @@ function Chart(props: Props) {
<br />
{treeNode ? (
<TopicPlot
color={props.parameters.color}
interpolation={props.parameters.interpolation}
range={props.parameters.range ? [props.parameters.range.from, props.parameters.range.to] : undefined}
history={treeNode.messageHistory}
@@ -0,0 +1,71 @@
import * as React from 'react'
import { bindActionCreators } from 'redux'
import { chartActions } from '../../../actions'
import { ChartParameters } from '../../../reducers/Charts'
import { connect } from 'react-redux'
import { Menu, MenuItem } from '@material-ui/core'
import { colors as createColors } from './colors'
function chartParametersForColor(chart: ChartParameters, color?: string) {
return {
color,
topic: chart.topic,
dotPath: chart.dotPath,
}
}
const colors: Array<string> = createColors()
function ColorSettings(props: {
chart: ChartParameters
actions: {
chart: typeof chartActions
}
anchorEl?: Element
open: boolean
close: () => void
}) {
const setColor = React.useCallback(
(color?: string) => props.actions.chart.updateChart(chartParametersForColor(props.chart, color)),
[props.chart]
)
const menuItems = React.useMemo(() => {
return colors.map(color => (
<MenuItem
style={{ minWidth: '8em', minHeight: '36px', backgroundColor: color, textAlign: 'center' }}
key={color}
onClick={() => setColor(color)}
>
{props.chart.color === color ? 'X' : ''}
</MenuItem>
))
}, [colors, props.chart])
return (
<Menu anchorEl={props.anchorEl} open={props.open} onClose={props.close}>
<MenuItem
style={{ minWidth: '8em', minHeight: '36px', textAlign: 'center' }}
key="none"
onClick={() => setColor()}
selected={props.chart.color === undefined}
>
default
</MenuItem>
{menuItems}
</Menu>
)
}
const mapDispatchToProps = (dispatch: any) => {
return {
actions: {
chart: bindActionCreators(chartActions, dispatch),
},
}
}
export default connect(
undefined,
mapDispatchToProps
)(ColorSettings)
@@ -23,7 +23,7 @@ function InterpolationSettings(props: {
}
anchorEl?: Element
open: boolean
onClose: () => void
close: () => void
}) {
const callbacks = React.useMemo(() => {
const createCurveCallback = (curve: PlotCurveTypes) => () => {
@@ -46,7 +46,7 @@ function InterpolationSettings(props: {
}, [curves, props.chart])
return (
<Menu id="long-menu" anchorEl={props.anchorEl} open={props.open} onClose={props.onClose}>
<Menu anchorEl={props.anchorEl} open={props.open} onClose={props.close}>
{menuItems}
</Menu>
)
@@ -0,0 +1,35 @@
import * as React from 'react'
import { ChartParameters } from '../../../reducers/Charts'
import { Menu, MenuItem, TextField, Typography } from '@material-ui/core'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { chartActions } from '../../../actions'
function MoveUp(props: { actions: { chart: typeof chartActions }; chart: ChartParameters; close: () => void }) {
const moveUp = React.useCallback(() => {
props.actions.chart.moveChartUp({
topic: props.chart.topic,
dotPath: props.chart.dotPath,
})
props.close()
}, [props.chart])
return (
<MenuItem key="size" onClick={moveUp}>
Move up
</MenuItem>
)
}
const mapDispatchToProps = (dispatch: any) => {
return {
actions: {
chart: bindActionCreators(chartActions, dispatch),
},
}
}
export default connect(
undefined,
mapDispatchToProps
)(MoveUp)
@@ -34,13 +34,7 @@ function RangeSettings(props: {
const setFromHandler = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => setRangeFrom(e.target.value), [])
const setToHandler = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => setRangeTo(e.target.value), [])
return (
<Menu
style={{ textAlign: 'center' }}
id="long-menu"
anchorEl={props.anchorEl}
open={props.open}
onClose={props.onClose}
>
<Menu style={{ textAlign: 'center' }} anchorEl={props.anchorEl} open={props.open} onClose={props.onClose}>
<Typography>Define custom ranges for the Y-Axis</Typography>
<div style={{ padding: '0 16px' }}>
<TextField
@@ -0,0 +1,53 @@
import * as React from 'react'
import { ChartParameters } from '../../../reducers/Charts'
import { Menu, MenuItem, TextField, Typography } from '@material-ui/core'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { chartActions } from '../../../actions'
function Size(props: {
actions: { chart: typeof chartActions }
chart: ChartParameters
anchorEl?: Element
open: boolean
close: () => void
}) {
const setChartWidth = (width?: 'big' | 'medium' | 'small') => () => {
props.actions.chart.updateChart({
width,
topic: props.chart.topic,
dotPath: props.chart.dotPath,
})
props.close()
}
return (
<Menu anchorEl={props.anchorEl} open={props.open} onClose={props.close}>
<MenuItem selected={props.chart.width === undefined} onClick={setChartWidth()}>
auto
</MenuItem>
<MenuItem selected={props.chart.width === 'big'} onClick={setChartWidth('big')}>
100% width
</MenuItem>
<MenuItem selected={props.chart.width === 'medium'} onClick={setChartWidth('medium')}>
50% width
</MenuItem>
<MenuItem selected={props.chart.width === 'small'} onClick={setChartWidth('small')}>
33% width
</MenuItem>
</Menu>
)
}
const mapDispatchToProps = (dispatch: any) => {
return {
actions: {
chart: bindActionCreators(chartActions, dispatch),
},
}
}
export default connect(
undefined,
mapDispatchToProps
)(Size)
@@ -0,0 +1,46 @@
import {
amber,
orange,
pink,
purple,
deepPurple,
teal,
red,
green,
lime,
indigo,
yellow,
brown,
blueGrey,
} from '@material-ui/core/colors'
export function colors() {
function colorToInt(color: string): [number, number, number] {
const str = color.replace('#', '')
return [parseInt(str.slice(0, 2), 16), parseInt(str.slice(2, 4), 16), parseInt(str.slice(4, 6), 16)]
}
function colorCompare(colorA: string, colorB: string) {
const a = colorToInt(colorA)
const b = colorToInt(colorB)
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2))
}
const colors: Array<string> = [
brown,
blueGrey,
amber,
orange,
pink,
purple,
deepPurple,
teal,
red,
green,
lime,
indigo,
yellow,
]
.map(color => [color[200], color[500], color[700]])
.reduce((a, b) => a.concat(b), [])
.sort((a, b) => colorCompare(a, b))
return colors
}
@@ -3,6 +3,9 @@ import InterpolationSettings from './InterpolationSettings'
import { ChartParameters } from '../../../reducers/Charts'
import { Menu, MenuItem } from '@material-ui/core'
import RangeSettings from './RangeSettings'
import Size from './Size'
import MoveUp from './MoveUp'
import ColorSettings from './ColorSettings'
function ChartSettings(props: {
open: boolean
@@ -12,21 +15,37 @@ function ChartSettings(props: {
}) {
const [rangeVisible, setRangeVisible] = React.useState(false)
const [interpolationVisible, setInterpolationVisible] = React.useState(false)
const [sizeVisible, setSizeVisible] = React.useState(false)
const [colorVisible, setColorVisible] = React.useState(false)
const toggleRange = React.useCallback(() => {
if (!rangeVisible && open) {
if (open) {
props.close()
}
setRangeVisible(!rangeVisible)
}, [rangeVisible, open])
const toggleInterpolation = React.useCallback(() => {
if (!interpolationVisible && open) {
if (open) {
props.close()
}
setInterpolationVisible(!interpolationVisible)
}, [interpolationVisible, open])
const toggleSize = React.useCallback(() => {
if (open) {
props.close()
}
setSizeVisible(!sizeVisible)
}, [sizeVisible, open])
const toggleColor = React.useCallback(() => {
if (open) {
props.close()
}
setColorVisible(!colorVisible)
}, [colorVisible, open])
return (
<span>
<Menu id="long-menu" anchorEl={props.anchorEl.current} open={props.open} onClose={props.close}>
@@ -36,14 +55,23 @@ function ChartSettings(props: {
<MenuItem key="interpolation" onClick={toggleInterpolation}>
Curve interpolation
</MenuItem>
<MenuItem key="size" onClick={toggleSize}>
Size
</MenuItem>
<MenuItem key="color" onClick={toggleColor}>
Color
</MenuItem>
<MoveUp chart={props.chart} close={props.close} />
</Menu>
<RangeSettings chart={props.chart} anchorEl={props.anchorEl.current} open={rangeVisible} onClose={toggleRange} />
<InterpolationSettings
chart={props.chart}
anchorEl={props.anchorEl.current}
open={interpolationVisible}
onClose={toggleInterpolation}
close={toggleInterpolation}
/>
<Size chart={props.chart} anchorEl={props.anchorEl.current} open={sizeVisible} close={toggleSize} />
<ColorSettings chart={props.chart} anchorEl={props.anchorEl.current} open={colorVisible} close={toggleColor} />
</span>
)
}
+14 -1
View File
@@ -28,6 +28,19 @@ function spacingForChartCount(count: number): 4 | 6 | 12 {
}
}
function mapWidth(width: 'big' | 'medium' | 'small' | undefined, calculatedSpacing: 4 | 6 | 12): 4 | 6 | 12 {
switch (width) {
case 'big':
return 12
case 'medium':
return 6
case 'small':
return 4
default:
return calculatedSpacing
}
}
function ChartPanel(props: Props) {
const chartsInView = props.charts.count()
@@ -53,7 +66,7 @@ function ChartPanel(props: Props) {
timeout={{ enter: 500, exit: 500 }}
classNames="example"
>
<Grid item xs={spacing}>
<Grid item xs={mapWidth(chartParameters.width, spacing)}>
<Chart parameters={chartParameters} />
</Grid>
</CSSTransition>
@@ -40,7 +40,7 @@ function ChartPreview(props: Props) {
const hasEnoughDataToDisplayDiagrams = props.treeNode.messageHistory.count() > 1
const addChartToPanelButton = hasEnoughDataToDisplayDiagrams ? (
<Tooltip title="Click to add to chart panel">
<Tooltip title="Add to chart panel">
<ShowChart
ref={chartIconRef}
className={props.classes.icon}
@@ -50,7 +50,7 @@ function ChartPreview(props: Props) {
/>
</Tooltip>
) : (
<Tooltip title="Click to add to chart panel, not enough data for preview">
<Tooltip title="Add to chart panel, not enough data for preview">
<ShowChart onClick={onClick} className={props.classes.icon} style={{ color: '#aaa' }} />
</Tooltip>
)
+8 -5
View File
@@ -12,6 +12,7 @@ interface Props {
theme: Theme
interpolation?: PlotCurveTypes
range?: [number?, number?]
color?: string
}
function mapCurveType(type: PlotCurveTypes | undefined) {
@@ -58,6 +59,12 @@ export default withTheme((props: Props) => {
? [props.range[0] || calculatedDomain[0], props.range[1] || calculatedDomain[1]]
: calculatedDomain
let color: string =
props.theme.palette.type === 'light' ? props.theme.palette.secondary.dark : props.theme.palette.primary.light
if (props.color) {
color = props.color
}
return (
<div style={{ height: '150px', overflow: 'hidden' }}>
<XYPlot width={width} height={180} yDomain={yDomain}>
@@ -65,11 +72,7 @@ export default withTheme((props: Props) => {
<XAxis />
<YAxis width={45} tickFormat={(num: number) => abbreviate(num)} />
<LineMarkSeries
color={
props.theme.palette.type === 'light'
? props.theme.palette.secondary.dark
: props.theme.palette.primary.light
}
color={color}
onValueMouseOver={showTooltip}
onValueMouseOut={hideTooltip}
size={3}
@@ -11,6 +11,7 @@ import { TopicViewModel } from '../../../model/TopicViewModel'
import { bindActionCreators } from 'redux'
import { chartActions } from '../../../actions'
import { connect } from 'react-redux'
import CustomIconButton from '../../helper/CustomIconButton'
const throttle = require('lodash.throttle')
@@ -109,7 +110,19 @@ class MessageHistory extends React.Component<Props, State> {
<div>
<History
items={historyElements}
contentTypeIndicator={isMessagePlottable ? <ShowChart onClick={this.addNodeToCharts} /> : undefined}
contentTypeIndicator={
isMessagePlottable ? (
<CustomIconButton
style={{ height: '22px', width: '22px' }}
onClick={this.addNodeToCharts}
tooltip="Add to chart panel"
>
<ShowChart style={{ marginTop: '-5px' }} />
</CustomIconButton>
) : (
undefined
)
}
onClick={this.displayMessage}
>
{isMessagePlottable ? <TopicPlot history={node.messageHistory} /> : null}
+2 -1
View File
@@ -11,6 +11,7 @@ interface Props {
dotPath?: string
interpolation?: PlotCurveTypes
range?: [number?, number?]
color?: string
}
function nodeToHistory(history: q.MessageHistory) {
@@ -41,7 +42,7 @@ function nodeDotPathToHistory(history: q.MessageHistory, dotPath: string) {
function render(props: Props) {
const data = props.dotPath ? nodeDotPathToHistory(props.history, props.dotPath) : nodeToHistory(props.history)
return <PlotHistory range={props.range} interpolation={props.interpolation} data={data} />
return <PlotHistory color={props.color} range={props.range} interpolation={props.interpolation} data={data} />
}
export default render
@@ -6,6 +6,7 @@ interface Props {
onClick: any
tooltip: string
classes: any
style?: React.CSSProperties
}
const styles = (theme: Theme) => ({
@@ -32,7 +33,7 @@ class CustomIconButton extends React.Component<Props, {}> {
public render() {
return (
<IconButton className={this.props.classes.button} onClick={this.onClick}>
<IconButton className={this.props.classes.button} style={this.props.style} onClick={this.onClick}>
<Tooltip title={this.props.tooltip} className={this.props.classes.label}>
<span>{this.props.children}</span>
</Tooltip>
+25 -1
View File
@@ -1,6 +1,7 @@
import { Action } from 'redux'
import { createReducer } from './lib'
import { Record, List } from 'immutable'
import MoveUp from '../components/ChartPanel/ChartSettings/MoveUp'
export type PlotCurveTypes = 'curve' | 'linear' | 'cubic_basis_spline' | 'step_after' | 'step_before'
@@ -12,6 +13,8 @@ export interface ChartParameters {
from?: number
to?: number
}
width?: 'big' | 'medium' | 'small'
color?: string
}
export interface ChartsStateModel {
@@ -20,13 +23,14 @@ export interface ChartsStateModel {
export type ChartsState = Record<ChartsStateModel>
export type Action = AddChart | RemoveChart | SetCharts | UpdateChart
export type Action = AddChart | RemoveChart | SetCharts | UpdateChart | MoveUp
export enum ActionTypes {
CHARTS_ADD = 'CHARTS_ADD',
CHARTS_REMOVE = 'CHARTS_REMOVE',
CHARTS_SET = 'CHARTS_SET',
CHARTS_UPDATE = 'CHARTS_UPDATE',
CHARTS_MOVE_UP = 'CHARTS_MOVE_UP',
}
export interface AddChart {
@@ -34,6 +38,12 @@ export interface AddChart {
chart: ChartParameters
}
export interface MoveUp {
type: ActionTypes.CHARTS_ADD
topic: string
dotPath?: string
}
export interface UpdateChart {
type: ActionTypes.CHARTS_ADD
topic: string
@@ -60,12 +70,26 @@ export const chartsReducer = createReducer(initialState(), {
CHARTS_REMOVE: removeChart,
CHARTS_SET: setCharts,
CHARTS_UPDATE: updateChart,
CHARTS_MOVE_UP: moveUp,
})
function addChart(state: ChartsState, action: AddChart) {
return state.set('charts', state.get('charts').push(action.chart))
}
function moveUp(state: ChartsState, action: MoveUp) {
const charts = state.get('charts')
const idx = charts.findIndex(chart => chart.topic === action.topic && chart.dotPath === action.dotPath)
const item = charts.get(idx)
const previousItem = charts.get(idx - 1)
if (idx === 0 || !item || !previousItem) {
return
}
const newlyOrderedCharts = charts.set(idx - 1, item).set(idx, previousItem)
return state.set('charts', newlyOrderedCharts)
}
function updateChart(state: ChartsState, action: UpdateChart) {
const charts = state.get('charts')
const chartIdx = charts.findIndex(chart => chart.topic === action.topic && chart.dotPath === action.dotPath)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "MQTT-Explorer",
"version": "0.3.0-alpha",
"version": "0.3.0-alpha1",
"description": "Explore your message queues",
"main": "dist/src/electron.js",
"scripts": {