import React, { useState } from 'react'
import {
Type, Mail, AlignLeft, ChevronDown, CheckSquare, CircleDot, Calendar, Hash, Search, Eye, EyeOff, Box,
ChevronRight, Columns, Layout, LayoutGrid, Square, User, Phone, Clock, Image, FileText, Code, Send, MapPin,
Layers,
} from 'lucide-react'
import { hasNestedColumns, useFormStore } from '../store/useFormStore'
import { applyFilters, doAction, getFieldType, Slot, useRegistryVersion } from '../extensions/registry'
import FaIcon from './shell/FaIcon'
// the reference builder LayersPanel.jsx getFieldIcon (FA) with lucide fallbacks when Font Awesome is not loaded.
const ICONS = {
grid: ['fa-th-large', LayoutGrid],
section: ['fa-square', Square],
text: ['fa-font', Type],
email: ['fa-envelope', Mail],
textarea: ['fa-align-left', AlignLeft],
name: ['fa-user', User],
number: ['fa-hashtag', Hash],
phone: ['fa-phone', Phone],
tel: ['fa-phone', Phone],
date: ['fa-calendar', Calendar],
time: ['fa-clock', Clock],
image: ['fa-image', Image],
'static-content': ['fa-file-alt', FileText],
html: ['fa-code', Code],
checkbox: ['fa-check-square', CheckSquare],
radio: ['fa-dot-circle', CircleDot],
dropdown: ['fa-chevron-down', ChevronDown],
select: ['fa-chevron-down', ChevronDown],
submit: ['fa-paper-plane', Send],
hidden: ['fa-eye-slash', EyeOff],
address: ['fa-map-marker-alt', MapPin],
}
const iconFor = (type) => {
if (ICONS[type]) return ICONS[type]
const registered = getFieldType(type)
if (registered && (registered.layerIcon || registered.fa)) return registered.layerIcon || [registered.fa, registered.icon || Box]
return ['fa-cube', Box]
}
/**
* Optional prop (App wiring): `onOpenWrapperEditor()` opens the form wrapper style editor. Extensions can group the
* field list (`layers.render` filter, which receives the default list element and helpers) and add nodes above or
* below it (slots `layers.top` / `layers.bottom`, which receive { specialNode }).
*/
export default function LayersPanel({ onOpenWrapperEditor }) {
useRegistryVersion()
const fields = useFormStore((state) => state.fields)
const activeFieldId = useFormStore((state) => state.activeFieldId)
const setActiveFieldId = useFormStore((state) => state.setActiveFieldId)
const hiddenElements = useFormStore((state) => state.hiddenElements)
const toggleHiddenElement = useFormStore((state) => state.toggleHiddenElement)
const hoveredFieldId = useFormStore((state) => state.hoveredFieldId)
const setHoveredFieldId = useFormStore((state) => state.setHoveredFieldId)
const [searchTerm, setSearchTerm] = useState('')
const [expandedNodes, setExpandedNodes] = useState(new Set())
const toggleExpand = (id, e) => {
e.stopPropagation()
setExpandedNodes((prev) => {
const next = new Set(prev)
if (next.has(id)) next.delete(id)
else next.add(id)
return next
})
}
// Extensions can bring the field on screen first (`builder.revealField`), then it scrolls (retries while it renders).
const scrollToField = (id, groupId) => {
doAction('builder.revealField', id, groupId)
let tries = 0
const attempt = () => {
const el = document.querySelector(`[data-field-id="${id}"]`)
if (!el) {
if (tries++ < 5) setTimeout(attempt, 120)
return
}
el.scrollIntoView({ behavior: 'smooth', block: 'center' })
el.classList.add('ring-2', 'ring-blue-500', 'ring-offset-2', 'shadow-[0_0_15px_rgba(59,130,246,0.5)]', 'transition-all', 'duration-500')
setTimeout(() => {
el.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-2', 'shadow-[0_0_15px_rgba(59,130,246,0.5)]')
}, 2000)
}
attempt()
}
const nestedIds = (list) => (list || []).flatMap((f) => [f.id, ...(hasNestedColumns(f) ? Object.values(f.columns).flatMap((c) => nestedIds(c)) : [])])
const eyeButton = (hidden, onClick, label) => (
)
const renderField = (field, depth = 0, groupId) => {
const [fa, Fallback] = iconFor(field.type)
const isHidden = hiddenElements.has(field.id)
const isExpanded = expandedNodes.has(field.id) || Boolean(searchTerm)
// Containers hold nested fields (a `columns` array holds definitions).
const isGrid = hasNestedColumns(field)
const name = field.label || field.name || field.type
return (
No form fields yet
Add fields to see them in the layers panel