More graph options
This commit is contained in:
parent
f50b8f1d2e
commit
da7f4aa5b2
|
|
@ -53,7 +53,7 @@ export const BoxDialContent = ({ box, data, refreshRef }: Props) => {
|
||||||
{value.data && (
|
{value.data && (
|
||||||
<>
|
<>
|
||||||
{displayValue}
|
{displayValue}
|
||||||
{` ${data.unit}`}
|
{data.unit && ` ${data.unit}`}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { getSensorValues } from '@/api/sensorValues'
|
import { getSensorValues } from '@/api/sensorValues'
|
||||||
|
import { max } from '@/utils/max'
|
||||||
|
import { min } from '@/utils/min'
|
||||||
import { DashboardGraphData } from '@/utils/parseDashboard'
|
import { DashboardGraphData } from '@/utils/parseDashboard'
|
||||||
import { RefObject } from 'preact'
|
import { RefObject } from 'preact'
|
||||||
import { useEffect, useRef } from 'preact/hooks'
|
import { useEffect, useRef } from 'preact/hooks'
|
||||||
|
|
@ -33,42 +35,58 @@ export const BoxGraphContent = ({ box, data, refreshRef }: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (bodyRef.current && values.data) {
|
||||||
// TODO: These should be probably returned by server, could be outdated
|
// TODO: These should be probably returned by server, could be outdated
|
||||||
const from = filter.customFrom
|
const from = filter.customFrom
|
||||||
const to = filter.customTo
|
const to = filter.customTo
|
||||||
const minValue = parseFloat(data.min ?? '')
|
const minValue = parseFloat(data.min ?? '')
|
||||||
const maxValue = parseFloat(data.max ?? '')
|
const maxValue = parseFloat(data.max ?? '')
|
||||||
const customRange = !isNaN(minValue) && !isNaN(maxValue)
|
const customRange = !isNaN(minValue) || !isNaN(maxValue)
|
||||||
|
const graphType = data.graphType ?? 'line'
|
||||||
|
const fill = data.fill ?? undefined
|
||||||
|
const colorMode = data.colorMode
|
||||||
|
const staticColor = data.staticColor
|
||||||
|
|
||||||
|
const x = values.data.map((v) => new Date(v.timestamp * 1000))
|
||||||
|
const y = values.data.map((v) => v.value)
|
||||||
|
|
||||||
if (bodyRef.current && values.data) {
|
|
||||||
window.Plotly.newPlot(
|
window.Plotly.newPlot(
|
||||||
bodyRef.current,
|
bodyRef.current,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
...(data.graphType === 'line' && {
|
...(graphType === 'line' && {
|
||||||
type: 'scatter',
|
type: 'scatter',
|
||||||
mode: 'lines',
|
mode: 'lines',
|
||||||
|
fill,
|
||||||
}),
|
}),
|
||||||
...(data.graphType === 'points' && {
|
...(graphType === 'points' && {
|
||||||
type: 'scatter',
|
type: 'scatter',
|
||||||
mode: 'markers',
|
mode: 'markers',
|
||||||
|
fill,
|
||||||
}),
|
}),
|
||||||
...(data.graphType === 'lineAndPoints' && {
|
...(graphType === 'lineAndPoints' && {
|
||||||
type: 'scatter',
|
type: 'scatter',
|
||||||
mode: 'lines+markers',
|
mode: 'lines+markers',
|
||||||
|
fill,
|
||||||
}),
|
}),
|
||||||
...(data.graphType === 'bar' && { type: 'bar' }),
|
...(graphType === 'bar' && { type: 'bar' }),
|
||||||
x: values.data.map((v) => new Date(v.timestamp * 1000)),
|
x,
|
||||||
y: values.data.map((v) => v.value),
|
y,
|
||||||
line: {
|
line: {
|
||||||
width: 1,
|
width: 1,
|
||||||
|
...(colorMode === 'static' && { color: staticColor }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
xaxis: { range: [from, to], type: 'date' },
|
xaxis: { range: [from, to], type: 'date' },
|
||||||
yaxis: {
|
yaxis: {
|
||||||
...(customRange && { range: [minValue, maxValue] }),
|
...(customRange && {
|
||||||
|
range: [
|
||||||
|
isNaN(minValue) ? min(y) : minValue,
|
||||||
|
isNaN(maxValue) ? max(y) : maxValue,
|
||||||
|
],
|
||||||
|
}),
|
||||||
...(data.unit && { ticksuffix: ` ${data.unit}` }),
|
...(data.unit && { ticksuffix: ` ${data.unit}` }),
|
||||||
},
|
},
|
||||||
margin: {
|
margin: {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { omit } from '@/utils/omit'
|
||||||
import { DashboardGraphData } from '@/utils/parseDashboard'
|
import { DashboardGraphData } from '@/utils/parseDashboard'
|
||||||
import { useEffect, useState } from 'preact/hooks'
|
import { useEffect, useState } from 'preact/hooks'
|
||||||
|
|
||||||
|
|
@ -8,10 +9,7 @@ type Props = {
|
||||||
|
|
||||||
export const GraphSettings = ({ value, onChange }: Props) => {
|
export const GraphSettings = ({ value, onChange }: Props) => {
|
||||||
const [formState, setFormState] = useState(() => ({
|
const [formState, setFormState] = useState(() => ({
|
||||||
min: value?.min,
|
...(value && omit(value, ['type'])),
|
||||||
max: value?.max,
|
|
||||||
graphType: value?.graphType,
|
|
||||||
unit: value?.unit,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const handleChange = (e: Event) => {
|
const handleChange = (e: Event) => {
|
||||||
|
|
@ -43,18 +41,67 @@ export const GraphSettings = ({ value, onChange }: Props) => {
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="input">
|
||||||
|
<label>Fill area</label>
|
||||||
|
<select
|
||||||
|
name="fill"
|
||||||
|
value={formState.fill || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<option value="">None</option>
|
||||||
|
<option value="tozeroy">To zero</option>
|
||||||
|
<option value="tonexty">To next value</option>
|
||||||
|
<option value="toself">To self</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<label>Unit</label>
|
<label>Unit</label>
|
||||||
<input name="unit" value={formState.unit} onChange={handleChange} />
|
<input name="unit" value={formState.unit} onChange={handleChange} />
|
||||||
</div>
|
</div>
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<label>Min value</label>
|
<label>Min value</label>
|
||||||
<input name="min" value={formState.min} onChange={handleChange} />
|
<input
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
name="min"
|
||||||
|
value={formState.min}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<label>Max value</label>
|
<label>Max value</label>
|
||||||
<input name="max" value={formState.max} onChange={handleChange} />
|
<input
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
name="max"
|
||||||
|
value={formState.max}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<label>Color mode</label>
|
||||||
|
<select
|
||||||
|
name="colorMode"
|
||||||
|
value={formState.colorMode ?? ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<option value="">None</option>
|
||||||
|
<option value="static">Static</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{formState.colorMode === 'static' && (
|
||||||
|
<div className="input">
|
||||||
|
<label>Max value</label>
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
name="staticColor"
|
||||||
|
value={formState.staticColor}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const max = (v: number[]) =>
|
||||||
|
v.reduce((acc, item) => (item > acc ? item : acc), 0)
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const min = (v: number[]) =>
|
||||||
|
v.reduce((acc, item) => (item > acc ? item : acc), 0)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const omit = <T, TOmitKeys extends keyof T>(v: T, keys: TOmitKeys[]) => {
|
||||||
|
const keysSet = new Set<string>(keys as string[])
|
||||||
|
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(v).filter(([key]) => !keysSet.has(key))
|
||||||
|
) as Omit<T, TOmitKeys>
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,16 @@ export type DashboardGraphData = {
|
||||||
max?: string
|
max?: string
|
||||||
unit?: string
|
unit?: string
|
||||||
graphType?: string
|
graphType?: string
|
||||||
|
fill?:
|
||||||
|
| 'none'
|
||||||
|
| 'tozeroy'
|
||||||
|
| 'tozerox'
|
||||||
|
| 'tonexty'
|
||||||
|
| 'tonextx'
|
||||||
|
| 'toself'
|
||||||
|
| 'tonext'
|
||||||
|
colorMode?: 'static'
|
||||||
|
staticColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DashboardDialData = {
|
export type DashboardDialData = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue