diff --git a/client/src/assets/style.css b/client/src/assets/style.css index ada3b5b..fbc47b5 100644 --- a/client/src/assets/style.css +++ b/client/src/assets/style.css @@ -142,14 +142,15 @@ form.horizontal .input label { margin-right: 0.25rem; } -.filters .inner { +.dashboard-head .inner { display: flex; align-items: center; + justify-content: flex-end; padding: 0.5rem 0.5rem; background-color: #fff; } -.filters .shadow { +.dashboard-head .shadow { position: absolute; background: linear-gradient(0deg, rgba(255,255,255,0) 5%, rgba(190,190,190,1) 100%); width: 100%; @@ -157,12 +158,6 @@ form.horizontal .input label { z-index: 1; } -.filters .actions { - margin-left: auto; - display: flex; - align-items: center; -} - .checkbox-label { display: inline-flex; align-items: center; @@ -251,6 +246,12 @@ form.horizontal .input label { display: flex; align-items: center; 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 { diff --git a/client/src/pages/dashboard/components/BoxDialContent.tsx b/client/src/pages/dashboard/components/BoxDialContent.tsx index 5347dce..46cbcfe 100644 --- a/client/src/pages/dashboard/components/BoxDialContent.tsx +++ b/client/src/pages/dashboard/components/BoxDialContent.tsx @@ -1,5 +1,6 @@ import { getLatestSensorValue } from '@/api/sensorValues' import { DashboardDialData } from '@/utils/parseDashboard' +import { useMemo } from 'preact/hooks' import { useQuery } from 'react-query' import { useDashboardContext } from '../contexts/DashboardContext' import { BoxDefinition } from '../types' @@ -21,12 +22,29 @@ export const BoxDialContent = ({ box, data }: Props) => { 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 (
{value.data && ( <> - {value.data.value} - {data.unit} + {displayValue} + {` ${data.unit}`} )}
diff --git a/client/src/pages/dashboard/components/BoxSettings/components/DialSettings.tsx b/client/src/pages/dashboard/components/BoxSettings/components/DialSettings.tsx index 15e3541..0bf494a 100644 --- a/client/src/pages/dashboard/components/BoxSettings/components/DialSettings.tsx +++ b/client/src/pages/dashboard/components/BoxSettings/components/DialSettings.tsx @@ -7,7 +7,11 @@ type 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 target = e.target as HTMLSelectElement | HTMLInputElement @@ -28,6 +32,25 @@ export const DialSettings = ({ value, onChange }: Props) => { +
+ + +
+
+ + +
) } diff --git a/client/src/pages/dashboard/components/DashboardHeader.tsx b/client/src/pages/dashboard/components/DashboardHeader.tsx index 79e9ed9..703c215 100644 --- a/client/src/pages/dashboard/components/DashboardHeader.tsx +++ b/client/src/pages/dashboard/components/DashboardHeader.tsx @@ -7,7 +7,7 @@ type Props = { export const DashboardHeader = ({ onNewBox, onRefresh }: Props) => { return ( -
+
diff --git a/client/src/pages/dashboard/components/EditableBox.tsx b/client/src/pages/dashboard/components/EditableBox.tsx index a4d965b..1c8a830 100644 --- a/client/src/pages/dashboard/components/EditableBox.tsx +++ b/client/src/pages/dashboard/components/EditableBox.tsx @@ -1,5 +1,5 @@ import { useWindowEvent } from '@/utils/hooks/useWindowEvent' -import { useRef, useState } from 'preact/hooks' +import { useState } from 'preact/hooks' import { GRID_WIDTH } from '../constants' import { useDashboardContext } from '../contexts/DashboardContext' import { useDragging } from '../hooks/useDragging' @@ -10,6 +10,7 @@ import { BoxGraphContent } from './BoxGraphContent' import { BoxSettings } from './BoxSettings/BoxSettings' import { ReactComponent as SettingsIcon } from '@/assets/icons/settings.svg' import { ReactComponent as RefreshIcon } from '@/assets/icons/refresh.svg' +import { useElementOffsets } from '@/utils/hooks/useElementOffsets' type Props = { box: BoxDefinition @@ -26,12 +27,13 @@ export const EditableBox = ({ onResize, onEdit, }: Props) => { - const boxRef = useRef(null) + const [boxRef, setBoxRef] = useState() const { verticalMode } = useDashboardContext() 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 { dragging, setDragging, draggingPosition } = useDragging({ @@ -49,10 +51,10 @@ export const EditableBox = ({ const handleMouseDown = (e: MouseEvent) => { e.preventDefault() - if (!dragging.active && boxRef.current) { + if (!dragging.active && boxRef) { const pos = { - top: boxRef.current.offsetTop, - left: boxRef.current.offsetLeft, + top: boxRef.offsetTop, + left: boxRef.offsetLeft, } setDragging({ @@ -72,7 +74,7 @@ export const EditableBox = ({ if (resizing.mode === ResizingMode.NONE) { setResizing({ mode: target, - w: (box.w / GRID_WIDTH) * outerWidth, + w: box.w * cellWidth, h: box.h, offsetX: e.clientX, offsetY: e.clientY, @@ -95,7 +97,7 @@ export const EditableBox = ({ return ( <>
setBoxRef(e ?? undefined)} className={`grid-box${dragging ? ' dragging' : ''}`} style={ verticalMode diff --git a/client/src/pages/dashboard/hooks/useResize.ts b/client/src/pages/dashboard/hooks/useResize.ts index 658f5d0..665a6ed 100644 --- a/client/src/pages/dashboard/hooks/useResize.ts +++ b/client/src/pages/dashboard/hooks/useResize.ts @@ -28,6 +28,7 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => { const isResizing = state.mode !== ResizingMode.NONE + // TODO: This is most likely incorrect? const maxHeights = isResizing ? Array(GRID_WIDTH) .fill(null) @@ -48,12 +49,15 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => { : [] const actualHeight = isResizing - ? Math.min( - ...Array(box.w) - .fill(null) - .map((_, x) => maxHeights[box.x + x]) - .filter((x) => x > 0), - Math.round(state.h / GRID_H_SNAP) * GRID_H_SNAP + ? Math.max( + GRID_H_SNAP * 5, + Math.min( + ...Array(box.w) + .fill(null) + .map((_, x) => maxHeights[box.x + x]) + .filter((x) => x > 0), + Math.round(state.h / GRID_H_SNAP) * GRID_H_SNAP + ) ) : 0 @@ -70,7 +74,10 @@ export const useResize = ({ cellWidth, box, boxes }: Props) => { .reduce((acc, item) => (item < acc ? item : acc), GRID_WIDTH) : 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) => { if (isResizing) { diff --git a/client/src/pages/dashboard/utils/normalizeBoxes.tsx b/client/src/pages/dashboard/utils/normalizeBoxes.tsx index 334d41d..8b9a3fc 100644 --- a/client/src/pages/dashboard/utils/normalizeBoxes.tsx +++ b/client/src/pages/dashboard/utils/normalizeBoxes.tsx @@ -31,7 +31,7 @@ export function normalizeBoxes(boxes: BoxDefinition[]) { sorted = false break } else { - const newY = above[0].h + above[0].y + const newY = Math.max(...above.map((a) => a.h + a.y)) if (box.y !== newY) { box.y = newY diff --git a/client/src/utils/hooks/useElementOffsets.ts b/client/src/utils/hooks/useElementOffsets.ts new file mode 100644 index 0000000..3b3cafe --- /dev/null +++ b/client/src/utils/hooks/useElementOffsets.ts @@ -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 +} diff --git a/client/src/utils/parseDashboard.ts b/client/src/utils/parseDashboard.ts index 93b812b..2649e54 100644 --- a/client/src/utils/parseDashboard.ts +++ b/client/src/utils/parseDashboard.ts @@ -25,6 +25,8 @@ export type DashboardGraphData = { export type DashboardDialData = { type: 'dial' unit?: string + decimals?: string + multiplier?: string } export const parseDashboard = (input: string) => {