import { useState, useCallback, useRef } from 'react' import { normalizeUrl, openExternalUrl } from '../utils/url' import { getTagStyle } from '../utils/tagStyles' export function UrlValue({ value, onSave, onCancel, isEditing, onStartEdit, }: { value: string onSave: (newValue: string) => void onCancel: () => void isEditing: boolean onStartEdit: () => void }) { const [editValue, setEditValue] = useState(value) const openingRef = useRef(false) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { onSave(editValue) } else if (e.key === 'Escape') { setEditValue(value) onCancel() } } const handleOpen = useCallback((e: React.MouseEvent) => { e.stopPropagation() if (openingRef.current) return openingRef.current = true const normalized = normalizeUrl(value) try { new URL(normalized) openExternalUrl(normalized).catch(() => { // opener failed — ignore silently }) } catch { // malformed URL — do nothing } finally { setTimeout(() => { openingRef.current = false }, 500) } }, [value]) const handleEditClick = useCallback((e: React.MouseEvent) => { e.stopPropagation() onStartEdit() }, [onStartEdit]) if (isEditing) { return ( setEditValue(e.target.value)} onKeyDown={handleKeyDown} onBlur={() => onSave(editValue)} autoFocus /> ) } return ( {value || '\u2014'} ) } export function EditableValue({ value, onSave, onCancel, isEditing, onStartEdit }: { value: string onSave: (newValue: string) => void onCancel: () => void isEditing: boolean onStartEdit: () => void }) { const [editValue, setEditValue] = useState(value) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { onSave(editValue) } else if (e.key === 'Escape') { setEditValue(value) onCancel() } } if (isEditing) { return ( setEditValue(e.target.value)} onKeyDown={handleKeyDown} onBlur={() => onSave(editValue)} autoFocus /> ) } return ( {value || '\u2014'} ) } export function TagPillList({ items, onSave, label, }: { items: string[] onSave: (newItems: string[]) => void label: string }) { const [editingIndex, setEditingIndex] = useState(null) const [editValue, setEditValue] = useState('') const [isAddingNew, setIsAddingNew] = useState(false) const [newValue, setNewValue] = useState('') const handleStartEdit = (index: number) => { setEditingIndex(index) setEditValue(items[index]) } const handleSaveEdit = () => { if (editingIndex !== null) { const newItems = [...items] if (editValue.trim()) { newItems[editingIndex] = editValue.trim() } else { newItems.splice(editingIndex, 1) } onSave(newItems) setEditingIndex(null) } } const handleDeleteItem = (index: number) => { const newItems = items.filter((_, i) => i !== index) onSave(newItems) } const handleAddNew = () => { if (newValue.trim()) { onSave([...items, newValue.trim()]) setNewValue('') setIsAddingNew(false) } } const handleKeyDown = (e: React.KeyboardEvent, action: 'edit' | 'add') => { if (e.key === 'Enter') { if (action === 'edit') handleSaveEdit() else handleAddNew() } else if (e.key === 'Escape') { if (action === 'edit') { setEditingIndex(null) setEditValue('') } else { setIsAddingNew(false) setNewValue('') } } } return (
{items.map((item, idx) => editingIndex === idx ? ( setEditValue(e.target.value)} onKeyDown={(e) => handleKeyDown(e, 'edit')} onBlur={handleSaveEdit} autoFocus /> ) : ( handleStartEdit(idx)} title="Click to edit" > {item} ) )} {isAddingNew ? ( setNewValue(e.target.value)} onKeyDown={(e) => handleKeyDown(e, 'add')} onBlur={() => { if (newValue.trim()) handleAddNew() else { setIsAddingNew(false); setNewValue('') } }} placeholder={`${label}...`} autoFocus /> ) : ( )}
) }