feat(observation): add dynamic camera sources

This commit is contained in:
DCCONSTRUCTIONS
2026-07-16 20:56:41 +03:00
parent 05bdab24a5
commit a281faf923
15 changed files with 1592 additions and 52 deletions
@@ -0,0 +1,78 @@
import type { RefObject } from "react";
import { WorkspaceWindow } from "@nodedc/ui-react";
import type { ObservationWindowRect } from "../core/observation/useObservationLayout";
import type { ObservationSourceDescriptor } from "../core/runtime/contracts";
import { ObservationMedia, observationSourceStatusLabel } from "./ObservationSources";
export function initialObservationWindowRect(index: number): ObservationWindowRect {
return {
x: 18 + (index % 3) * 354,
y: 78 + Math.floor(index / 3) * 226,
width: 336,
height: 210,
};
}
export function FloatingObservationWindow({
source,
index,
boundsRef,
rect,
maximized,
active,
onRectChange,
onMaximizedChange,
onActivate,
onClose,
}: {
source: ObservationSourceDescriptor;
index: number;
boundsRef: RefObject<HTMLElement | null>;
rect?: ObservationWindowRect;
maximized: boolean;
active: boolean;
onRectChange: (rect: ObservationWindowRect) => void;
onMaximizedChange: (maximized: boolean) => void;
onActivate: () => void;
onClose: () => void;
}) {
return (
<WorkspaceWindow
boundsRef={boundsRef}
rect={rect ?? initialObservationWindowRect(index)}
onRectChange={onRectChange}
maximized={maximized}
onMaximizedChange={onMaximizedChange}
onActivate={onActivate}
onClose={onClose}
title={source.label}
subtitle={source.description}
status={(
<span className="floating-observation-window__status">
<i data-availability={source.availability} aria-hidden="true" />
{observationSourceStatusLabel(source)}
</span>
)}
footer={(
<span className="floating-observation-window__footer">
<span>{source.endpointLabel || source.transport}</span>
<span>{source.capabilities.timelineMode === "live-only" ? "Эфир без буфера" : "Временная шкала"}</span>
</span>
)}
minWidth={280}
minHeight={190}
resizable={source.capabilities.resizable}
active={active}
zIndex={maximized ? 15 : active ? 9 : 7}
className="floating-observation-window"
closeLabel={`Закрыть ${source.label}`}
maximizeLabel={`Развернуть ${source.label}`}
restoreLabel={`Восстановить ${source.label}`}
moveLabel={`Переместить ${source.label}`}
resizeLabel={`Изменить размер ${source.label}`}
>
<ObservationMedia source={source} />
</WorkspaceWindow>
);
}