import { DownOutlined, ExpandOutlined, MutedOutlined, PauseCircleOutlined, PlayCircleOutlined, SoundOutlined, } from "@ant-design/icons"; import { useEffect, useRef, useState, type ChangeEvent, type MouseEvent } from "react"; import type { CanvasOption } from "./canvasTypes"; import { getOptionLabel } from "./canvasUtils"; import { formatCanvasVideoTime } from "./canvasWorkflowDeserialize"; export function CanvasSelectChip({ value, options, open, onToggle, onChange, ariaLabel, className, compact = false, }: { value: string; options: CanvasOption[]; open: boolean; onToggle: () => void; onChange: (value: string) => void; ariaLabel: string; className?: string; compact?: boolean; }) { return (
{open ? (
event.stopPropagation()} onClick={(event) => event.stopPropagation()} > {options.map((option, index) => ( ))}
) : null}
); } export function CanvasNodeVideoPlayer({ src, title, onVideoMeta }: { src: string; title: string; onVideoMeta?: (width: number, height: number) => void }) { const videoRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [isMuted, setIsMuted] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); useEffect(() => { setIsPlaying(false); setCurrentTime(0); setDuration(0); }, [src]); const stopPlayerEvent = (event: MouseEvent) => { event.preventDefault(); event.stopPropagation(); }; const syncVideoState = () => { const video = videoRef.current; if (!video) return; setCurrentTime(video.currentTime || 0); setDuration(Number.isFinite(video.duration) ? video.duration : 0); setIsMuted(video.muted); setIsPlaying(!video.paused && !video.ended); }; const togglePlayback = async (event: MouseEvent) => { stopPlayerEvent(event); const video = videoRef.current; if (!video) return; if (video.paused || video.ended) { try { await video.play(); } catch { setIsPlaying(false); } } else { video.pause(); } syncVideoState(); }; const toggleMuted = (event: MouseEvent) => { stopPlayerEvent(event); const video = videoRef.current; if (!video) return; video.muted = !video.muted; setIsMuted(video.muted); }; const handleSeek = (event: ChangeEvent) => { event.stopPropagation(); const video = videoRef.current; const nextTime = Number(event.target.value); if (!video || !Number.isFinite(nextTime)) return; video.currentTime = nextTime; setCurrentTime(nextTime); }; return (
); } export interface CanvasNodeToolbarAction { key: string; label: string; icon: React.ReactNode; disabled?: boolean; loading?: boolean; } export function CanvasNodeToolbar({ actions, onAction, moreActions, onMoreAction, }: { actions: CanvasNodeToolbarAction[]; onAction: (key: string) => void; moreActions?: CanvasNodeToolbarAction[]; onMoreAction?: (key: string) => void; }) { const [moreOpen, setMoreOpen] = useState(false); const moreRef = useRef(null); useEffect(() => { if (!moreOpen) return; const handler = (e: globalThis.MouseEvent) => { if (moreRef.current && !moreRef.current.contains(e.target as Node)) { setMoreOpen(false); } }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [moreOpen]); return (
e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > {actions.map((action) => ( ))} {moreActions && moreActions.length > 0 && (
{moreOpen && (
{moreActions.map((action) => ( ))}
)}
)}
); }