import { useCallback, useRef } from 'react'; import { NavIcon } from '../NavIcon'; import { ButtonSecondary } from './buttons'; import { useSikshyaDialog } from './SikshyaDialogContext'; import { __ } from '../../lib/i18n'; /** Compact URL preview; merge with caller `FIELD_INPUT` without double margins. */ const URL_PREVIEW_BASE = 'mt-2 w-full max-w-lg truncate rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-snug text-slate-600 shadow-sm dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-400'; function mergedUrlPreviewClass(passed?: string): string { if (!passed || passed.trim() === '') { return URL_PREVIEW_BASE; } const cleaned = passed .replace(/\bmt-1\.5\b/g, '') .replace(/\s+/g, ' ') .trim(); return `${URL_PREVIEW_BASE} ${cleaned}`.trim(); } /** Minimal typing for the `wp.media()` frame returned by WordPress. */ type WpMediaFrame = { open: () => void; on: (event: string, handler: () => void) => void; state: () => { get: (sel: string) => { first: () => { toJSON: () => { id?: number | string; url?: string } }; }; }; }; declare global { interface Window { wp?: { media?: (attrs: { library?: { type?: string }; multiple?: boolean }) => WpMediaFrame; }; } } type Props = { id: string; value: string; onChange: (url: string) => void; onAttachmentIdChange?: (id: number) => void; className?: string; placeholder?: string; imageOnly?: boolean; }; const THUMB_BTN = 'group relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-xl border shadow-sm transition focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500/40'; /** * Opens the native WordPress media modal (`wp.media`). * PHP side must call `wp_enqueue_media()` on the admin screen. * * Compact layout: fixed thumbnail target + browse / replace actions (no full‑width drop zone). */ export function WPMediaPickerField(props: Props) { const { id, value, onChange, onAttachmentIdChange, className, placeholder, imageOnly = true } = props; const dialog = useSikshyaDialog(); const frameRef = useRef(null); const openFrame = useCallback(() => { if (typeof window.wp?.media !== 'function') { void dialog.alert({ title: __('Media library unavailable', 'sikshya'), message: __('WordPress media library is not loaded. Please reload the page.', 'sikshya'), }); return; } if (!frameRef.current) { const frame = window.wp.media({ library: imageOnly ? { type: 'image' } : {}, multiple: false, }); frame.on('select', () => { const attachment = frame.state().get('selection').first().toJSON(); const url = attachment.url; const aid = typeof attachment.id === 'number' ? attachment.id : Number(attachment.id); if (Number.isFinite(aid) && aid > 0) { onAttachmentIdChange?.(aid); } if (typeof url === 'string' && url.length > 0) { onChange(url); } }); frameRef.current = frame; } frameRef.current.open(); }, [dialog, imageOnly, onChange, onAttachmentIdChange]); const thumbLabel = imageOnly ? __('Featured image thumbnail', 'sikshya') : __('Selected file', 'sikshya'); const hint = placeholder || __('Opens the WordPress media library — upload something new or pick an existing file.', 'sikshya'); const chooseCtaLabel = imageOnly ? __('Choose image', 'sikshya') : __('Choose file', 'sikshya'); const thumbStateClass = value ? 'border-slate-200 bg-white hover:border-brand-300 dark:border-slate-600 dark:bg-slate-900 dark:hover:border-brand-500/60' : 'border-dashed border-slate-200 bg-slate-50/90 hover:border-brand-300 hover:bg-brand-50/50 dark:border-slate-600 dark:bg-slate-800/70 dark:hover:border-brand-500/50 dark:hover:bg-brand-950/20'; const inputClass = mergedUrlPreviewClass(className); const remove = () => { onAttachmentIdChange?.(0); onChange(''); }; return (
{!value ? ( <> {chooseCtaLabel}

{hint}

{hint} ) : ( <>
{imageOnly ? __('Replace', 'sikshya') : __('Replace file', 'sikshya')}
)}
); }