'use client'; import { createPortal } from 'react-dom'; import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { BringToFront, Pause, Play, Repeat2, SendToBack, Trash2 } from 'lucide-react'; import type { MouseEvent } from 'react'; import type { PPTAudioElement } from '@openmaic/dsl'; import { computeToolbarPosition } from '../text/TextToolbarOverlay'; import { useToolbarAnchor } from '../text/useToolbarAnchor'; import type { AudioEditorLabels } from '../types'; interface ToolbarSize { readonly width: number; readonly height: number; } export interface AudioToolbarOverlayProps { readonly element: PPTAudioElement; readonly elementIdPrefix?: string; readonly labels: AudioEditorLabels; readonly onLoopChange: (loop: boolean) => void; readonly onBringToFront?: () => void; readonly onSendToBack?: () => void; readonly onDelete?: () => void; } export function AudioToolbarOverlay({ element, elementIdPrefix = 'slide-element-', labels, onLoopChange, onBringToFront, onSendToBack, onDelete, }: AudioToolbarOverlayProps) { const anchor = useToolbarAnchor(element.id, elementIdPrefix); const toolbarRef = useRef(null); const previewRef = useRef(null); const [toolbarSize, setToolbarSize] = useState(null); const [previewing, setPreviewing] = useState(false); const preventFocusLoss = (event: MouseEvent) => event.preventDefault(); const stopPreview = useCallback(() => { previewRef.current?.pause(); previewRef.current = null; setPreviewing(false); }, []); useEffect(() => stopPreview, [element.id, element.src, stopPreview]); useLayoutEffect(() => { const toolbar = toolbarRef.current; if (!anchor || !toolbar) return; const measure = () => { const rect = toolbar.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { setToolbarSize((current) => current?.width === rect.width && current.height === rect.height ? current : { width: rect.width, height: rect.height }, ); } }; measure(); const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure); observer?.observe(toolbar); return () => observer?.disconnect(); }, [anchor]); const togglePreview = async () => { if (!element.src) return; if (previewing) { stopPreview(); return; } const preview = new Audio(element.src); preview.loop = element.loop; preview.onended = stopPreview; preview.onerror = stopPreview; previewRef.current = preview; try { await preview.play(); setPreviewing(true); } catch { stopPreview(); } }; if (!anchor || typeof document === 'undefined') return null; const position = toolbarSize ? computeToolbarPosition( anchor, toolbarSize, { width: window.innerWidth, height: window.innerHeight }, 'top', ) : null; const previewLabel = previewing ? labels.pause : labels.preview; return createPortal(
{onBringToFront || onSendToBack || onDelete ? (
, document.body, ); }