import React, { useState } from 'react' import { applyFilters } from '../../extensions/registry' import { Trash2, Plus, ChevronUp, ChevronDown } from 'lucide-react' import InfoTip from './InfoTip' /** * Choice options editor (dropdown / checkbox / radio), the reference builder CheckboxFieldEditor + DropdownOptionsEditor: * label, value, default selection, price, quiz points, reorder, remove. * Reads string options (older forms) and object options; always writes objects {label, value, price?, points?, image?}. * Default selection uses the reference builder keys: checkbox / multi-dropdown → defaultValue: [values]; radio / dropdown → defaultValue: value. */ export const toOptionObject = (opt) => { if (opt && typeof opt === 'object') { const label = opt.label ?? opt.name ?? opt.value ?? '' return { ...opt, label: String(label), value: opt.value !== undefined && opt.value !== null && opt.value !== '' ? opt.value : String(label) } } const s = opt === undefined || opt === null ? '' : String(opt) return { label: s, value: s } } const slug = (s) => String(s).trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'option' export default function OptionsEditor({ field, onUpdate }) { const options = (field.options || []).map(toOptionObject) const multi = field.type === 'checkbox' || ((field.type === 'select' || field.type === 'dropdown') && Boolean(field.isMulti ?? field.multiSelect)) const [newLabel, setNewLabel] = useState('') const defaults = multi ? (Array.isArray(field.defaultValue) ? field.defaultValue : (field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== '' ? [field.defaultValue] : [])).map(String) : (Array.isArray(field.defaultValue) ? field.defaultValue.slice(0, 1) : [field.defaultValue]).filter((v) => v !== undefined && v !== null && v !== '').map(String) const isDefault = (opt) => defaults.includes(String(opt.value)) const write = (nextOptions, extra = {}) => onUpdate({ options: nextOptions, ...extra }) const setOption = (idx, patch) => { const prev = options[idx] const next = options.map((o, i) => (i === idx ? { ...o, ...patch } : o)) const extra = {} // Keep the default selection pointing at the renamed value. if (patch.value !== undefined && isDefault(prev)) { extra.defaultValue = multi ? defaults.map((v) => (v === String(prev.value) ? patch.value : v)) : patch.value } write(next, extra) } const toggleDefault = (opt) => { const v = String(opt.value) if (multi) { const nextDefaults = defaults.includes(v) ? defaults.filter((d) => d !== v) : [...defaults, v] onUpdate({ defaultValue: nextDefaults }) } else { onUpdate({ defaultValue: defaults.includes(v) ? '' : opt.value }) } } const remove = (idx) => { const opt = options[idx] const extra = {} if (isDefault(opt)) extra.defaultValue = multi ? defaults.filter((d) => d !== String(opt.value)) : '' write(options.filter((_, i) => i !== idx), extra) } const move = (idx, dir) => { const to = idx + dir if (to < 0 || to >= options.length) return const next = [...options] const [o] = next.splice(idx, 1) next.splice(to, 0, o) write(next) } const add = (e) => { e.preventDefault() const label = newLabel.trim() || `Option ${options.length + 1}` let value = slug(label) const taken = new Set(options.map((o) => String(o.value))) let n = 2 while (taken.has(value)) value = `${slug(label)}_${n++}` write([...options, { label, value }]) setNewLabel('') } const numberOrUndef = (v) => (v === '' ? undefined : Number(v)) // Extra per-option inputs from extensions (`optionsEditor.columns` filter: [{ key, width, render({ option, index, // setOption, field }) }]), shown between Value and Pts. const extraColumns = applyFilters('optionsEditor.columns', [], field) || [] return (
No options yet.
}