Fixed some resizing issues, added more dial options
This commit is contained in:
parent
a1f29e66c2
commit
52469eda4d
|
|
@ -142,14 +142,15 @@ form.horizontal .input label {
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters .inner {
|
.dashboard-head .inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
padding: 0.5rem 0.5rem;
|
padding: 0.5rem 0.5rem;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters .shadow {
|
.dashboard-head .shadow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: linear-gradient(0deg, rgba(255,255,255,0) 5%, rgba(190,190,190,1) 100%);
|
background: linear-gradient(0deg, rgba(255,255,255,0) 5%, rgba(190,190,190,1) 100%);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -157,12 +158,6 @@ form.horizontal .input label {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters .actions {
|
|
||||||
margin-left: auto;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label {
|
.checkbox-label {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -251,6 +246,12 @@ form.horizontal .input label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-right: 0.4rem;
|
padding-right: 0.4rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-sensors .grid-box .box:hover .header .actions {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-sensors .grid-box .box .header .actions .action {
|
.grid-sensors .grid-box .box .header .actions .action {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { getLatestSensorValue } from '@/api/sensorValues'
|
import { getLatestSensorValue } from '@/api/sensorValues'
|
||||||
import { DashboardDialData } from '@/utils/parseDashboard'
|
import { DashboardDialData } from '@/utils/parseDashboard'
|
||||||
|
import { useMemo } from 'preact/hooks'
|
||||||
import { useQuery } from 'react-query'
|
import { useQuery } from 'react-query'
|
||||||
import { useDashboardContext } from '../contexts/DashboardContext'
|
import { useDashboardContext } from '../contexts/DashboardContext'
|
||||||
import { BoxDefinition } from '../types'
|
import { BoxDefinition } from '../types'
|
||||||
|
|
@ -21,12 +22,29 @@ export const BoxDialContent = ({ box, data }: Props) => {
|
||||||
getLatestSensorValue(valuesQuery)
|
getLatestSensorValue(valuesQuery)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const displayValue = useMemo(() => {
|
||||||
|
if (!value.data) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const decimals = parseInt(data.decimals ?? '', 10)
|
||||||
|
const multiplier = parseFloat(data.multiplier ?? '')
|
||||||
|
|
||||||
|
const numericValue = value.data.value * (isNaN(multiplier) ? 1 : multiplier)
|
||||||
|
|
||||||
|
if (!isNaN(decimals)) {
|
||||||
|
return numericValue.toFixed(decimals)
|
||||||
|
}
|
||||||
|
|
||||||
|
return numericValue
|
||||||
|
}, [value.data, data])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dial">
|
<div className="dial">
|
||||||
{value.data && (
|
{value.data && (
|
||||||
<>
|
<>
|
||||||
{value.data.value}
|
{displayValue}
|
||||||
{data.unit}
|
{` ${data.unit}`}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DialSettings = ({ value, onChange }: Props) => {
|
export const DialSettings = ({ value, onChange }: Props) => {
|
||||||
const [formState, setFormState] = useState(() => ({ unit: value?.unit }))
|
const [formState, setFormState] = useState(() => ({
|
||||||
|
unit: value?.unit,
|
||||||
|
decimals: value?.decimals,
|
||||||
|
multiplier: value?.multiplier,
|
||||||
|
}))
|
||||||
|
|
||||||
const handleChange = (e: Event) => {
|
const handleChange = (e: Event) => {
|
||||||
const target = e.target as HTMLSelectElement | HTMLInputElement
|
const target = e.target as HTMLSelectElement | HTMLInputElement
|
||||||
|
|
@ -28,6 +32,25 @@ export const DialSettings = ({ value, onChange }: Props) => {
|
||||||
<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">
|
||||||
|
<label>Decimals</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="decimals"
|
||||||
|
value={formState.decimals}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<label>Multiplier</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="multiplier"
|
||||||
|
value={formState.multiplier}
|
||||||
|
onChange={handleChange}
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ type Props = {
|
||||||
|
|
||||||
export const DashboardHeader = ({ onNewBox, onRefresh }: Props) => {
|
export const DashboardHeader = ({ onNewBox, onRefresh }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="filters">
|
<div className="dashboard-head">
|
||||||
<div className="inner">
|
<div className="inner">
|
||||||
<button onClick={onNewBox}>Add box</button>
|
<button onClick={onNewBox}>Add box</button>
|
||||||
<Filters />
|
<Filters />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
|
import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
|
||||||
import { useRef, useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
import { GRID_WIDTH } from '../constants'
|
import { GRID_WIDTH } from '../constants'
|
||||||
import { useDashboardContext } from '../contexts/DashboardContext'
|
import { useDashboardContext } from '../contexts/DashboardContext'
|
||||||
import { useDragging } from '../hooks/useDragging'
|
import { useDragging } from '../hooks/useDragging'
|
||||||
|
|
@ -10,6 +10,7 @@ import { BoxGraphContent } from './BoxGraphContent'
|
||||||
import { BoxSettings } from './BoxSettings/BoxSettings'
|
import { BoxSettings } from './BoxSettings/BoxSettings'
|
||||||
import { ReactComponent as SettingsIcon } from '@/assets/icons/settings.svg'
|
import { ReactComponent as SettingsIcon } from '@/assets/icons/settings.svg'
|
||||||
import { ReactComponent as RefreshIcon } from '@/assets/icons/refresh.svg'
|
import { ReactComponent as RefreshIcon } from '@/assets/icons/refresh.svg'
|
||||||
|
import { useElementOffsets } from '@/utils/hooks/useElementOffsets'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
box: BoxDefinition
|
box: BoxDefinition
|
||||||
|
|
@ -26,12 +27,13 @@ export const EditableBox = ({
|
||||||
onResize,
|
onResize,
|
||||||
onEdit,
|
onEdit,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const boxRef = useRef<HTMLDivElement>(null)
|
const [boxRef, setBoxRef] = useState<HTMLDivElement>()
|
||||||
const { verticalMode } = useDashboardContext()
|
const { verticalMode } = useDashboardContext()
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
|
|
||||||
const outerWidth = boxRef.current?.parentElement?.offsetWidth ?? 100
|
const parentOffsets = useElementOffsets(boxRef?.parentElement)
|
||||||
|
const outerWidth = parentOffsets?.width ?? 100
|
||||||
const cellWidth = outerWidth / GRID_WIDTH
|
const cellWidth = outerWidth / GRID_WIDTH
|
||||||
|
|
||||||
const { dragging, setDragging, draggingPosition } = useDragging({
|
const { dragging, setDragging, draggingPosition } = useDragging({
|
||||||
|
|
@ -49,10 +51,10 @@ export const EditableBox = ({
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (!dragging.active && boxRef.current) {
|
if (!dragging.active && boxRef) {
|
||||||
const pos = {
|
const pos = {
|
||||||
top: boxRef.current.offsetTop,
|
top: boxRef.offsetTop,
|
||||||
left: boxRef.current.offsetLeft,
|
left: boxRef.offsetLeft,
|
||||||
}
|
}
|
||||||
|
|
||||||
setDragging({
|
setDragging({
|
||||||
|
|
@ -72,7 +74,7 @@ export const EditableBox = ({
|
||||||
if (resizing.mode === ResizingMode.NONE) {
|
if (resizing.mode === ResizingMode.NONE) {
|
||||||
setResizing({
|
setResizing({
|
||||||
mode: target,
|
mode: target,
|
||||||
w: (box.w / GRID_WIDTH) * outerWidth,
|
w: box.w * cellWidth,
|
||||||
h: box.h,
|
h: box.h,
|
||||||
offsetX: e.clientX,
|
offsetX: e.clientX,
|
||||||
offsetY: e.clientY,
|
offsetY: e.clientY,
|
||||||
|
|
@ -95,7 +97,7 @@ export const EditableBox = ({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
ref={boxRef}
|
ref={(e) => setBoxRef(e ?? undefined)}
|
||||||
className={`grid-box${dragging ? ' dragging' : ''}`}
|
className={`grid-box${dragging ? ' dragging' : ''}`}
|
||||||
style={
|
style={
|
||||||
verticalMode
|
verticalMode
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => {
|
||||||
|
|
||||||
const isResizing = state.mode !== ResizingMode.NONE
|
const isResizing = state.mode !== ResizingMode.NONE
|
||||||
|
|
||||||
|
// TODO: This is most likely incorrect?
|
||||||
const maxHeights = isResizing
|
const maxHeights = isResizing
|
||||||
? Array(GRID_WIDTH)
|
? Array(GRID_WIDTH)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
|
|
@ -48,12 +49,15 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => {
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const actualHeight = isResizing
|
const actualHeight = isResizing
|
||||||
? Math.min(
|
? Math.max(
|
||||||
...Array(box.w)
|
GRID_H_SNAP * 5,
|
||||||
.fill(null)
|
Math.min(
|
||||||
.map((_, x) => maxHeights[box.x + x])
|
...Array(box.w)
|
||||||
.filter((x) => x > 0),
|
.fill(null)
|
||||||
Math.round(state.h / GRID_H_SNAP) * GRID_H_SNAP
|
.map((_, x) => maxHeights[box.x + x])
|
||||||
|
.filter((x) => x > 0),
|
||||||
|
Math.round(state.h / GRID_H_SNAP) * GRID_H_SNAP
|
||||||
|
)
|
||||||
)
|
)
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
|
|
@ -70,7 +74,10 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => {
|
||||||
.reduce((acc, item) => (item < acc ? item : acc), GRID_WIDTH)
|
.reduce((acc, item) => (item < acc ? item : acc), GRID_WIDTH)
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
const actualWidth = Math.min(maxWidth, Math.round(state.w / cellWidth))
|
const actualWidth = Math.max(
|
||||||
|
1,
|
||||||
|
Math.min(maxWidth, Math.round(state.w / cellWidth))
|
||||||
|
)
|
||||||
|
|
||||||
useWindowEvent('mousemove', (e) => {
|
useWindowEvent('mousemove', (e) => {
|
||||||
if (isResizing) {
|
if (isResizing) {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export function normalizeBoxes(boxes: BoxDefinition[]) {
|
||||||
sorted = false
|
sorted = false
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
const newY = above[0].h + above[0].y
|
const newY = Math.max(...above.map((a) => a.h + a.y))
|
||||||
|
|
||||||
if (box.y !== newY) {
|
if (box.y !== newY) {
|
||||||
box.y = newY
|
box.y = newY
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useEffect, useState } from 'preact/hooks'
|
||||||
|
import { useWindowEvent } from './useWindowEvent'
|
||||||
|
|
||||||
|
const getOffsets = (e: HTMLElement) => ({
|
||||||
|
width: e.offsetWidth,
|
||||||
|
height: e.offsetHeight,
|
||||||
|
left: e.offsetLeft,
|
||||||
|
top: e.offsetTop,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const useElementOffsets = (e: HTMLElement | undefined | null) => {
|
||||||
|
const [offsets, setOffsets] = useState(() => (e ? getOffsets(e) : undefined))
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
e && setOffsets(getOffsets(e))
|
||||||
|
}, [e])
|
||||||
|
|
||||||
|
useWindowEvent('resize', () => {
|
||||||
|
e && setOffsets(getOffsets(e))
|
||||||
|
})
|
||||||
|
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,8 @@ export type DashboardGraphData = {
|
||||||
export type DashboardDialData = {
|
export type DashboardDialData = {
|
||||||
type: 'dial'
|
type: 'dial'
|
||||||
unit?: string
|
unit?: string
|
||||||
|
decimals?: string
|
||||||
|
multiplier?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseDashboard = (input: string) => {
|
export const parseDashboard = (input: string) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue