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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="dial">
|
||||
{value.data && (
|
||||
<>
|
||||
{value.data.value}
|
||||
{data.unit}
|
||||
{displayValue}
|
||||
{` ${data.unit}`}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
|||
<label>Unit</label>
|
||||
<input name="unit" value={formState.unit} onChange={handleChange} />
|
||||
</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) => {
|
||||
return (
|
||||
<div className="filters">
|
||||
<div className="dashboard-head">
|
||||
<div className="inner">
|
||||
<button onClick={onNewBox}>Add box</button>
|
||||
<Filters />
|
||||
|
|
|
|||
|
|
@ -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<HTMLDivElement>(null)
|
||||
const [boxRef, setBoxRef] = useState<HTMLDivElement>()
|
||||
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 (
|
||||
<>
|
||||
<div
|
||||
ref={boxRef}
|
||||
ref={(e) => setBoxRef(e ?? undefined)}
|
||||
className={`grid-box${dragging ? ' dragging' : ''}`}
|
||||
style={
|
||||
verticalMode
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
type: 'dial'
|
||||
unit?: string
|
||||
decimals?: string
|
||||
multiplier?: string
|
||||
}
|
||||
|
||||
export const parseDashboard = (input: string) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue