diff --git a/packages/widgets/src/beszel-system-stats/component.tsx b/packages/widgets/src/beszel-system-stats/component.tsx index 0aca4348f..2a209e85e 100644 --- a/packages/widgets/src/beszel-system-stats/component.tsx +++ b/packages/widgets/src/beszel-system-stats/component.tsx @@ -23,11 +23,16 @@ import { getUsableWidgetQueryData } from "../common/query-state"; import { BeszelStatsView } from "../beszel/_shared/stats-view"; import { createBeszelSystemChoices, resolveBeszelSystemChoice } from "./selection"; +const CONTROLS_ROW_HEIGHT = 36; +const CONTROLS_ROW_GAP = 16; +const STACK_PADDING = 24; + export default function BeszelSystemStatsWidget({ options, integrationIds, isEditMode, width, + height, boardId, itemId, setOptions, @@ -256,6 +261,10 @@ export default function BeszelSystemStatsWidget({ systemId={selectedSystem?.systemId ?? ""} timePeriod={options.timePeriod as BeszelTimePeriod} columns={width > 600 ? 2 : 1} + availableHeight={Math.max( + 0, + height - STACK_PADDING - (isEditMode ? 0 : CONTROLS_ROW_HEIGHT + CONTROLS_ROW_GAP), + )} visibility={{ cpu: options.showCpu, memory: options.showMemory, diff --git a/packages/widgets/src/beszel/_shared/stats-view.tsx b/packages/widgets/src/beszel/_shared/stats-view.tsx index 66a24f38f..ef67f40ee 100644 --- a/packages/widgets/src/beszel/_shared/stats-view.tsx +++ b/packages/widgets/src/beszel/_shared/stats-view.tsx @@ -62,9 +62,20 @@ interface BeszelStatsViewProps { timePeriod: BeszelTimePeriod; visibility: BeszelStatsVisibility; columns: 1 | 2; + availableHeight?: number; onSwitchToHistorical?: () => void; } +const MIN_CHART_HEIGHT = 100; +const PANEL_ROW_OVERHEAD = 28; +const GRID_ROW_GAP = 16; + +const computeChartHeight = (availableHeight: number | undefined, rowCount: number) => { + if (availableHeight === undefined || rowCount <= 0) return CHART_HEIGHT; + const perRow = (availableHeight - (rowCount - 1) * GRID_ROW_GAP) / rowCount - PANEL_ROW_OVERHEAD; + return Math.max(MIN_CHART_HEIGHT, Math.min(CHART_HEIGHT, Math.floor(perRow))); +}; + const whenVisible = (visible: boolean, data: T | undefined) => (visible ? data : undefined); export function BeszelStatsView({ @@ -73,6 +84,7 @@ export function BeszelStatsView({ timePeriod, visibility, columns, + availableHeight, onSwitchToHistorical, }: BeszelStatsViewProps) { const t = useI18n("widget.beszelSystemStats"); @@ -248,14 +260,29 @@ export function BeszelStatsView({ ); } + const renderedPanelCount = + (visibility.cpu && cpuData.length > 0 ? 1 : 0) + + (visibility.memory && memoryData.length > 0 ? 1 : 0) + + (visibility.disk && diskData.length > 0 ? 1 : 0) + + (visibility.diskIO && diskIOData.length > 0 ? 1 : 0) + + (visibility.network && networkData.length > 0 ? 1 : 0) + + (showDocker && containerSeries.length > 0 + ? (visibility.dockerCpu && dockerCpuData.length > 0 ? 1 : 0) + + (visibility.dockerMemory && dockerMemoryData.length > 0 ? 1 : 0) + + (visibility.dockerNetwork && dockerNetworkData.length > 0 ? 1 : 0) + : 0); + const effectiveColumns = Math.min(columns, Math.max(1, renderedPanelCount)) as 1 | 2; + const rowCount = Math.ceil(renderedPanelCount / effectiveColumns); + const chartHeight = computeChartHeight(availableHeight, rowCount); + return ( - + {visibility.cpu && cpuData.length > 0 && ( + dayScaleTiers.find((tier) => minAxisSize >= tier.minAxisSize) ?? fallbackDayScale; + export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: CalendarDayProps) => { const board = useRequiredBoard(); const mantineTheme = useMantineTheme(); const actualItemRadius = mantineTheme.radius[board.itemRadius]; const minAxisSize = Math.min(rootWidth, rootHeight); - const shouldScaleDown = minAxisSize < 350; const shouldShowIndicators = rootHeight >= 256; - const indicatorSize = shouldScaleDown ? 3 : 4; + const { textSize, indicatorSize, indicatorMarginTop } = useMemo(() => getDayScale(minAxisSize), [minAxisSize]); const cell = ( - + {date.getDate()} - + ); @@ -70,19 +86,20 @@ export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: C interface NotificationIndicatorProps { events: CalendarEvent[]; size: number; + marginTop: number; visible: boolean; } -const NotificationIndicator = ({ events, size, visible }: NotificationIndicatorProps) => { +const NotificationIndicator = ({ events, size, marginTop, visible }: NotificationIndicatorProps) => { const notificationEvents = [...new Set(events.map((event) => event.indicatorColor))].filter( (color): color is string => Boolean(color), ); - if (!visible) return null; + if (!visible || notificationEvents.length === 0) return null; return (