import { useMemo, useCallback, useRef, useEffect } from 'react'
import type { VaultEntry } from '../types'
import type { FrontmatterValue } from './Inspector'
import type { ParsedFrontmatter } from '../utils/frontmatter'
import { usePropertyPanelState } from '../hooks/usePropertyPanelState'
import { getEffectiveDisplayMode, detectPropertyType } from '../utils/propertyTypes'
import { SmartPropertyValueCell, DisplayModeSelector } from './PropertyValueCells'
import { TypeSelector } from './TypeSelector'
import { AddPropertyForm } from './AddPropertyForm'
import type { PropertyDisplayMode } from '../utils/propertyTypes'
function toSentenceCase(key: string): string {
const spaced = key.replace(/[_-]/g, ' ')
if (!spaced) return spaced
return spaced.charAt(0).toUpperCase() + spaced.slice(1)
}
// eslint-disable-next-line react-refresh/only-export-components -- utility co-located with component
export function containsWikilinks(value: FrontmatterValue): boolean {
if (typeof value === 'string') return /^\[\[.*\]\]$/.test(value)
if (Array.isArray(value)) return value.some(v => typeof v === 'string' && /^\[\[.*\]\]$/.test(v))
return false
}
function PropertyRow({ propKey, value, editingKey, displayMode, autoMode, vaultStatuses, vaultTags, onStartEdit, onSave, onSaveList, onUpdate, onDelete, onDisplayModeChange }: {
propKey: string; value: FrontmatterValue; editingKey: string | null
displayMode: PropertyDisplayMode; autoMode: PropertyDisplayMode
vaultStatuses: string[]; vaultTags: string[]
onStartEdit: (key: string | null) => void; onSave: (key: string, value: string) => void
onSaveList: (key: string, items: string[]) => void
onUpdate?: (key: string, value: FrontmatterValue) => void; onDelete?: (key: string) => void
onDisplayModeChange: (key: string, mode: PropertyDisplayMode | null) => void
}) {
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && editingKey !== propKey) {
e.preventDefault()
onStartEdit(propKey)
}
}
return (
{toSentenceCase(propKey)}
{onDelete && (
)}
)
}
function AddPropertyButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
return (
)
}
const SUGGESTED_PROPERTIES = ['Status', 'Date', 'URL'] as const
function SuggestedPropertySlot({ label, onAdd }: { label: string; onAdd: () => void }) {
return (
)
}
export function DynamicPropertiesPanel({
entry, frontmatter, entries,
onUpdateProperty, onDeleteProperty, onAddProperty, onNavigate,
}: {
entry: VaultEntry
content?: string | null
frontmatter: ParsedFrontmatter
entries?: VaultEntry[]
onUpdateProperty?: (key: string, value: FrontmatterValue) => void
onDeleteProperty?: (key: string) => void
onAddProperty?: (key: string, value: FrontmatterValue) => void
onNavigate?: (target: string) => void
}) {
const {
editingKey, setEditingKey, showAddDialog, setShowAddDialog, displayOverrides,
availableTypes, customColorKey, typeColorKeys, typeIconKeys, vaultStatuses, vaultTagsByKey, propertyEntries,
handleSaveValue, handleSaveList, handleAdd, handleDisplayModeChange,
} = usePropertyPanelState({ entries, entryIsA: entry.isA, frontmatter, onUpdateProperty, onDeleteProperty, onAddProperty })
const existingKeys = useMemo(() => {
const keys = new Set(propertyEntries.map(([k]) => k.toLowerCase()))
// Also check full frontmatter for relationship keys that are filtered out of propertyEntries
for (const k of Object.keys(frontmatter)) keys.add(k.toLowerCase())
return keys
}, [propertyEntries, frontmatter])
const missingSuggested = onAddProperty
? SUGGESTED_PROPERTIES.filter(p => !existingKeys.has(p.toLowerCase()))
: []
const pendingEditKeyRef = useRef(null)
useEffect(() => {
if (pendingEditKeyRef.current && propertyEntries.some(([k]) => k === pendingEditKeyRef.current)) {
setEditingKey(pendingEditKeyRef.current)
pendingEditKeyRef.current = null
}
}, [propertyEntries, setEditingKey])
const handleSuggestedAdd = useCallback((key: string) => {
if (!onAddProperty) return
pendingEditKeyRef.current = key
onAddProperty(key, '')
}, [onAddProperty])
return (
{propertyEntries.map(([key, value]) => (
))}
{missingSuggested.map(key => (
handleSuggestedAdd(key)} />
))}
{showAddDialog
?
setShowAddDialog(false)} vaultStatuses={vaultStatuses} />
: setShowAddDialog(true)} disabled={!onAddProperty} />
}
)
}