domma-cms 0.98.0 → 0.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/index.html +1 -1
- package/admin/js/lib/collection-editor-arrange.js +2 -2
- package/admin/js/templates/collection-editor.html +3 -0
- package/admin/js/templates/docs/api-forms.html +5 -0
- package/admin/js/templates/docs/tutorial-forms.html +3 -0
- package/admin/js/templates/docs/usage-collections.html +4 -1
- package/admin/js/templates/form-editor.html +4 -0
- package/admin/js/views/collection-editor.js +20 -4
- package/admin/js/views/collection-entries.js +5 -5
- package/admin/js/views/form-editor.js +6 -6
- package/package.json +2 -2
- package/public/js/form-logic-engine.js +1 -1
- package/server/templates/page.html +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools CSS v0.
|
|
2
|
+
* Domma Tools CSS v0.46.1
|
|
3
3
|
* Dynamic Object Manipulation & Modeling API
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-09-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-09-27T20:43:29.160Z
|
|
6
|
+
* Commit: 92f2ee4
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/*!
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools v0.
|
|
2
|
+
* Domma Tools v0.46.1
|
|
3
3
|
* Developer tools: Theme Roller & Page Roller
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-09-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-09-27T20:43:15.062Z
|
|
6
|
+
* Commit: 92f2ee4
|
|
7
7
|
*
|
|
8
8
|
* Requires: domma.min.js
|
|
9
9
|
*/
|
package/admin/index.html
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
81
81
|
|
|
82
82
|
<!-- Form Logic Engine - needed for conditional logic preview -->
|
|
83
|
-
<script src="/public/js/form-logic-engine.js?v=
|
|
83
|
+
<script src="/public/js/form-logic-engine.js?v=20260927-field-tokens"></script>
|
|
84
84
|
|
|
85
85
|
<!-- Admin App (ES module entry) -->
|
|
86
86
|
<script type="module" src="/admin/js/app.js"></script>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const FIELD_TYPES=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],OPTION_TYPES=new Set(["select","radio","checkbox-group"]),OPERATIONS=["create","read","update","delete"],EXPORT_FORMATS=["csv","json"],FIELD_NAME_RE=/^[A-Za-z_][A-Za-z0-9_-]*$/;export function slugify(e){return String(e??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}export const nameFromLabel=e=>slugify(e).replace(/-/g,"_"),typeLabel=e=>FIELD_TYPES.find(
|
|
1
|
+
export const FIELD_TYPES=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],OPTION_TYPES=new Set(["select","radio","checkbox-group"]),ADDON_TYPES=new Set(["string","email","tel","number","textarea","select","date","time","url"]);const f=/^[a-z0-9-]+$/i,m=24;function p(e){return e?typeof e=="string"?{icon:"",text:e}:{icon:e.icon||"",text:e.icon?"":String(e.text??"")}:{icon:"",text:""}}export function addonFromBoxes(e,r){const n=String(e??"").trim(),t=String(r??"").trim();return n&&f.test(n)?{icon:n}:t?{text:t.slice(0,m)}:null}export const OPERATIONS=["create","read","update","delete"],EXPORT_FORMATS=["csv","json"],FIELD_NAME_RE=/^[A-Za-z_][A-Za-z0-9_-]*$/;export function slugify(e){return String(e??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}export const nameFromLabel=e=>slugify(e).replace(/-/g,"_"),typeLabel=e=>FIELD_TYPES.find(r=>r.value===e)?.label||e||"";export function optionsText(e){return(Array.isArray(e)?e:[]).map(r=>typeof r=="string"?`${r}: ${r}`:`${r?.value??""}: ${r?.label??r?.value??""}`).join(`
|
|
2
2
|
`)}export function parseOptions(e){return String(e??"").split(`
|
|
3
|
-
`).filter(
|
|
3
|
+
`).filter(r=>r.trim()).map(r=>{const[n,...t]=r.split(":");return{value:n.trim(),label:t.join(":").trim()||n.trim()}})}const g=()=>({create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}}),b=e=>({enabled:!0,access:e||"",formats:["csv","json"]});export function roleLadder(e){return(Array.isArray(e)?e:e?.entries||[]).map(n=>({name:n?.data?.name,label:n?.data?.label||n?.data?.name,level:Number(n?.data?.level)})).filter(n=>typeof n.name=="string"&&n.name&&Number.isFinite(n.level)).sort((n,t)=>n.level-t.level||n.name.localeCompare(t.name))}export function accessOptions(e,r,{token:n=!0}={}){const t=[{value:"public",label:"Public - anyone"}];n&&t.push({value:"token",label:"API token"});for(const l of[...e||[]].reverse())t.push({value:l.name,label:l.level===0?`${l.label} only`:`${l.label} and above`});return typeof r=="string"&&r&&!t.some(l=>l.value===r)&&t.push(e?.length?{value:r,label:`${r} (role not found)`,missing:!0}:{value:r,label:r}),t}let x=0;export const fieldKey=()=>`f${Date.now().toString(36)}${(x++).toString(36)}`;export function formFromCollection(e=null,{project:r="",defaultRole:n=""}={}){const t=e||{},l={...g(),...t.api||{}},o={...b(n),...t.export||{}},a={title:String(t.title??""),slug:String(t.slug??""),description:String(t.description??""),layout:t.layout==="grid"?"grid":"stacked",columns:String(Number.parseInt(t.columns,10)||2),project:String(t.meta?.project??r??""),bundled:t.bundled===!0,storageAdapter:t.storage?.adapter==="mongodb"?"mongodb":"file",storageConnection:String(t.storage?.connection||"default"),fields:(Array.isArray(t.fields)?t.fields:[]).map(i=>({...i,_key:fieldKey()})),readFields:Array.isArray(l.read?.fields)?l.read.fields.join(", "):"",exportEnabled:o.enabled!==!1,exportAccess:typeof o.access=="string"&&o.access?o.access:String(n||""),exportCsv:(Array.isArray(o.formats)?o.formats:EXPORT_FORMATS).includes("csv"),exportJson:(Array.isArray(o.formats)?o.formats:EXPORT_FORMATS).includes("json")};for(const i of OPERATIONS){a[`${i}Enabled`]=l[i]?.enabled===!0;const s=l[i]?.access;a[`${i}Access`]=typeof s=="string"&&s?s:"admin"}return a}export function cleanField(e){const{_key:r,...n}=e||{};return n}export function payloadFromForm(e,{isNew:r=!1,meta:n={}}={}){const t=e||{},l={};for(const i of OPERATIONS)l[i]={enabled:t[`${i}Enabled`]===!0,access:t[`${i}Access`]||"admin"};const o=String(t.readFields??"").split(",").map(i=>i.trim()).filter(Boolean);o.length&&(l.read.fields=o);const a=Math.min(6,Math.max(1,Number.parseInt(t.columns,10)||2));return{title:String(t.title??"").trim(),...r&&{slug:slugify(t.slug||t.title)},description:String(t.description??"").trim(),layout:t.layout==="grid"?"grid":"stacked",columns:a,fields:(t.fields||[]).map(cleanField),api:l,export:{enabled:t.exportEnabled!==!1,...t.exportAccess&&{access:t.exportAccess},formats:EXPORT_FORMATS.filter(i=>i==="csv"?t.exportCsv:t.exportJson)},storage:t.storageAdapter==="mongodb"?{adapter:"mongodb",connection:t.storageConnection||"default"}:{adapter:"file"},meta:{...n||{},project:String(t.project??"")||null},bundled:t.bundled===!0}}export function snapshot(e){const{fields:r=[],...n}=e||{};return JSON.stringify({...n,fields:r.map(cleanField)})}export function checkForm(e,{isNew:r=!1,taken:n=[],original:t={}}={}){const l=e||{},o={};if(String(l.title??"").trim()||(o.title="A collection needs a title."),r){const c=slugify(l.slug||l.title);c?n.includes(c)&&(o.slug=`"${c}" is already a collection.`):o.slug="The slug needs a letter or a number."}const a=Number.parseInt(l.columns,10);l.layout==="grid"&&!(a>=1&&a<=6)&&(o.columns="Between 1 and 6 columns.");const i={},s=new Map;return(l.fields||[]).forEach(c=>{const d=fieldProblem(c,t[c._key]);d&&(i[c._key]=d);const u=String(c.name??"").trim();u&&(s.has(u)?i[c._key]=i[c._key]||`"${u}" is used by another field.`:s.set(u,c._key))}),Object.keys(i).length&&(o.fields=i),l.exportEnabled&&!l.exportCsv&&!l.exportJson&&(o.export="Pick at least one export format, or turn export off."),o}export function fieldProblem(e,r){const n=String(e?.name??"").trim();return n?n!==r&&!FIELD_NAME_RE.test(n)?"Names start with a letter or _, then letters, digits, _ or -.":OPTION_TYPES.has(e.type)&&!(Array.isArray(e.options)&&e.options.length)?`A ${typeLabel(e.type).toLowerCase()} needs at least one option.`:"":"A field needs a name - it is what each entry is keyed by."}export const errorCount=e=>Object.keys(e||{}).filter(r=>r!=="fields").length+Object.keys(e?.fields||{}).length;export function fieldRow(e,r={},{grid:n=!1}={}){const t=[e.name||"(no name)",typeLabel(e.type)];return OPTION_TYPES.has(e.type)&&t.push(`${(e.options||[]).length} options`),n&&t.push(e.fullWidth?"full width":`span ${e.span>1?e.span:1}`),{key:e._key,label:e.label||"(Untitled field)",meta:t.join(" \xB7 "),required:e.required===!0,hidden:e.type==="hidden",problem:r[e._key]||"",rowCls:r[e._key]?"is-bad":""}}export function moveField(e,r,n){const t=[...e];if(r<0||r>=t.length||n<0||n>=t.length||r===n)return t;const[l]=t.splice(r,1);return t.splice(n,0,l),t}export function duplicateField(e,r){const n=e[r];if(!n)return[...e];const t=new Set(e.map(s=>s.name)),l=`${n.name||"field"}_copy`;let o=l;for(let s=2;t.has(o);s++)o=`${l}_${s}`;const a={...JSON.parse(JSON.stringify(cleanField(n))),name:o,label:`${n.label||"Field"} (copy)`,_key:fieldKey()};delete a.id;const i=[...e];return i.splice(r+1,0,a),i}export const newField=()=>({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null,_key:fieldKey()});export function panelForm(e){return{label:String(e.label??""),name:String(e.name??""),type:e.type||"string",required:e.required===!0,placeholder:String(e.placeholder??""),helper:String(e.helper??""),options:optionsText(e.options),span:String(e.span>1?e.span:1),fullWidth:e.fullWidth===!0,prefixIcon:p(e.prefix).icon,prefixText:p(e.prefix).text,suffixIcon:p(e.suffix).icon,suffixText:p(e.suffix).text}}export function fieldFromPanel(e,r){const n={...e,label:String(r.label??"").trim(),name:String(r.name??"").trim(),type:r.type||"string",required:r.required===!0,placeholder:String(r.placeholder??"").trim(),helper:String(r.helper??"").trim()};OPTION_TYPES.has(n.type)&&(n.options=parseOptions(r.options));for(const t of["prefix","suffix"]){const l=`${t}Icon`in r||`${t}Text`in r;if(!ADDON_TYPES.has(n.type)){delete n[t];continue}if(!l)continue;const o=addonFromBoxes(r[`${t}Icon`],r[`${t}Text`]);o?n[t]=o:delete n[t]}if(r.fullWidth)n.fullWidth=!0,delete n.span;else{delete n.fullWidth;const t=Number.parseInt(r.span,10);t>1?n.span=Math.min(6,t):delete n.span}return n}export function storageChange(e,r){const n=e||"file",t=r||"file";return n===t?"":`${n} \u2192 ${t}`}export function draftOf(e){const{fields:r=[],...n}=e||{};return{...n,fields:r.map(cleanField)}}export function formFromDraft(e,r){const n=e||{};return{...r,...n,fields:(Array.isArray(n.fields)?n.fields:r.fields).map(t=>({...cleanField(t),_key:fieldKey()}))}}
|
|
@@ -194,6 +194,9 @@
|
|
|
194
194
|
.cex-list { display: flex; flex-direction: column; }
|
|
195
195
|
.cex-row { display: flex; align-items: center; gap: .6rem; padding: .5rem .35rem; border-bottom: 1px solid var(--dm-border); cursor: pointer; }
|
|
196
196
|
.cex-row:last-child { border-bottom: 0; }
|
|
197
|
+
.cex-addon-row { display: flex; align-items: center; gap: .4rem; margin-top: .35rem; }
|
|
198
|
+
.cex-addon-row > .form-input { flex: 1; min-width: 0; }
|
|
199
|
+
.cex-addon-side { flex: none; width: 3.2rem; font-size: var(--dm-text-sm, .85rem); color: var(--dm-text-muted); }
|
|
197
200
|
.cex-row:hover, .cex-row:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
|
|
198
201
|
.cex-row.is-bad { box-shadow: inset 3px 0 0 var(--dm-danger); }
|
|
199
202
|
/* Six dots drawn in CSS: the row template is a data-each template, so an icon
|
|
@@ -226,6 +226,11 @@
|
|
|
226
226
|
stored. <code>notify</code> exists only on the server (notification source
|
|
227
227
|
<code>core:form-triggers</code>).</li>
|
|
228
228
|
</ul>
|
|
229
|
+
<p>Messages that the visitor sees (banner, toast, block reason, end message and done message) fill
|
|
230
|
+
<code>{{field_name}}</code> tokens with the posted answers - option labels for choices, nothing for
|
|
231
|
+
passwords, files, images and any field a trigger hid, disabled or masked - so the <code>400</code> body and the
|
|
232
|
+
done message match what the browser showed. Values are plain text, one line, at most 200 characters.
|
|
233
|
+
Redirect URLs and notify titles are not filled.</p>
|
|
229
234
|
|
|
230
235
|
<h2>Submissions</h2>
|
|
231
236
|
<p>All submissions routes read and write the form's target collection, and only this form's entries in it (in a
|
|
@@ -64,6 +64,9 @@ Subject Prefix: [Contact Form]</code></pre>
|
|
|
64
64
|
because MongoDB is not connected - the submission is still stored.</p>
|
|
65
65
|
<p>A field's <strong>triggers</strong> (Fields tab) can also run an action, notify the admins or redirect
|
|
66
66
|
after submit, only when the answers meet the trigger's condition.</p>
|
|
67
|
+
<p>A trigger's messages can use the visitor's answers: a banner reading
|
|
68
|
+
<code>Hello {{name}}</code> greets them by name as they type. Use <strong>Insert field</strong> beside the
|
|
69
|
+
message box to pick a field. Passwords, files and images are never repeated back.</p>
|
|
67
70
|
|
|
68
71
|
<h3>4. Success message vs. redirect</h3>
|
|
69
72
|
<p>After a successful submission the visitor sees one of two things:</p>
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
number, textarea, dropdown, radio buttons, single checkbox, checkbox group, date, time,
|
|
22
22
|
URL and hidden fields. Reference (a link to an entry in another collection), file and
|
|
23
23
|
multi-select fields exist too, but are added by editing the collection's
|
|
24
|
-
<code>schema.json</code
|
|
24
|
+
<code>schema.json</code>. Click a field to edit it; under <strong>Joined to the
|
|
25
|
+
input</strong> a text, number, dropdown, date, time, URL or textarea field can have an icon
|
|
26
|
+
or a short text (such as <code>£</code> or <code>kg</code>) joined to the start or end of
|
|
27
|
+
its input in the entry form.</li>
|
|
25
28
|
<li><strong>API & Export</strong> - what the public API may do (read, create, update,
|
|
26
29
|
delete) and who may do it, which fields a read returns, and whether visitors may export
|
|
27
30
|
what a <code>[collection]</code> display shows.</li>
|
|
@@ -328,6 +328,10 @@
|
|
|
328
328
|
.fb-trig-p-fields { background-image: none; height: auto; min-height: 6rem; padding: .3rem; }
|
|
329
329
|
.fb-trig-f { display: flex; flex-direction: column; gap: .3rem; min-width: 0; margin: 0; }
|
|
330
330
|
.fb-trig-f--full { grid-column: 1 / -1; }
|
|
331
|
+
/* A trigger message box and its "Insert field" picker ({{field_name}} tokens). */
|
|
332
|
+
.fb-token-row { display: flex; gap: .4rem; min-width: 0; }
|
|
333
|
+
.fb-token-row > .form-input:first-child { flex: 1; min-width: 0; }
|
|
334
|
+
.fb-token-pick { flex: none; width: auto; max-width: 12rem; }
|
|
331
335
|
.fb-trig-f-label { font-size: var(--dm-text-xs); font-weight: 600; color: var(--dm-text-muted); }
|
|
332
336
|
.fb-trig-f-hint { font-size: var(--dm-text-xs); color: var(--dm-text-muted); }
|
|
333
337
|
.fb-trig-opts { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; padding-top: .65rem; border-top: 1px dashed var(--dm-border); }
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{api as g}from"../api.js";import{addBannerAction as de}from"../lib/plugin-chrome.js";import{helpPopovers as
|
|
1
|
+
import{api as g}from"../api.js";import{addBannerAction as de}from"../lib/plugin-chrome.js";import{helpPopovers as Ie}from"../lib/help-popover.js";import{onceClosed as st,openMenuAt as Re}from"../lib/tool-kit.js";import{ADDON_TYPES as it,FIELD_TYPES as rt,OPERATIONS as ct,OPTION_TYPES as dt,accessOptions as pt,checkForm as ut,draftOf as ft,duplicateField as mt,errorCount as q,fieldFromPanel as Te,fieldProblem as ht,fieldRow as vt,formFromCollection as Fe,formFromDraft as bt,moveField as De,nameFromLabel as gt,newField as yt,panelForm as xt,payloadFromForm as wt,roleLadder as kt,slugify as X,snapshot as _,storageChange as Ne}from"../lib/collection-editor-arrange.js";import{fmtDateTime as Et}from"../lib/dates.js";import{rowPlaceKeeper as St}from"../lib/menu-drag.js";import{openIconPicker as Pe}from"../lib/card-builder.js";const Mt=/^\/collections\/(new|edit\/[^/?#]+)\/?(\?.*)?$/,$t=s=>`collection-editor.draft.${s||"new"}`,Ct=`
|
|
2
2
|
<div class="cex-row" data-key="{{key}}" tabindex="0" data-bind-class="rowCls">
|
|
3
3
|
<span class="cex-grip" title="Drag to reorder" aria-hidden="true"></span>
|
|
4
4
|
<span class="cex-main"><strong data-bind-text="label"></strong><small data-bind-text="meta"></small><small class="cex-problem" data-bind-hidden="!problem" data-bind-text="problem"></small></span>
|
|
5
5
|
<span class="cex-chip is-ok" data-bind-hidden="!required">Required</span>
|
|
6
6
|
<span class="cex-chip" data-bind-hidden="!hidden">Hidden</span>
|
|
7
7
|
<button type="button" class="cex-kebab" aria-label="Actions for this field" title="Actions">\u22EE</button>
|
|
8
|
-
</div>`,Z=s=>String(s??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function
|
|
8
|
+
</div>`,Z=s=>String(s??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Ot(s){const p=document.createElement("template");return p.innerHTML=s.trim(),p.content.firstElementChild}function Be(s,p){const i=s.querySelector(".dm-plugin-banner-name");if(!i)return;const C=document.createElement("span");C.className="dm-plugin-banner-parent",C.textContent="Collections";const x=document.createElement("span");x.className="dm-plugin-banner-sep",x.setAttribute("aria-hidden","true"),x.textContent="\u203A",i.replaceChildren(C,x,document.createTextNode(p))}let y=null,Le=!1;const ee=s=>String(s||"").split("?")[0];function jt(){Le||!window.R?.use||(Le=!0,R.use((s,p,i)=>{if(!(y&&Mt.test(p?.path||"")&&ee(s?.path)!==ee(p?.path))||!y.dirty())return i();const x=`#${p.path}`;return E.confirm("This collection has unsaved changes. Leave anyway? They are kept as a draft for your next visit.").then(d=>{d?i():history.replaceState(null,"",x)})}),M.subscribe("router:afterChange",({to:s}={})=>{if(y&&ee(s?.path)!==y.path){const p=y;y=null,p.dispose()}}))}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(s){if(jt(),y){const e=y;y=null,e.dispose()}const p=s.get(0),i=p.querySelector(".cex")||p,C=ee(location.hash.replace(/^#/,""))||"/collections/new",x=C.match(/^\/collections\/edit\/([^/?#]+)/),d=x?decodeURIComponent(x[1]):null,D=!d,pe=[],v=e=>(pe.push(e),e);let ue=!1;const te=()=>!ue&&i.isConnected,fe=E.loader(p,{type:"dots"});let O=null,ne=[],N=null,me=[],u=0,P=[];try{[O,ne,N,me,P]=await Promise.all([d?g.collections.get(d):Promise.resolve(null),g.projects.list().catch(()=>[]),g.collections.proStatus().catch(()=>null),D?g.collections.list().then(e=>(e||[]).map(t=>t.slug)).catch(()=>[]):Promise.resolve([]),g.collections.listEntries("roles",{limit:1e3}).then(kt).catch(()=>[])]),d&&(u=(await g.collections.listEntries(d,{limit:1}).catch(()=>null))?.total??0)}catch(e){fe.destroy(),E.toast(e?.status===404?"Collection not found.":`Could not load the collection: ${e.message||e}`,{type:"error"}),R.navigate("/collections");return}if(fe.destroy(),!i.isConnected)return;if(d&&!O){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const he=new URLSearchParams(location.hash.split("?")[1]||"").get("project")||"",m=Fe(O,{project:he,defaultRole:P.at(-1)?.name||""}),w=Object.fromEntries(m.fields.map(e=>[e._key,e.name])),qe=O?.meta||{};let K=m.storageAdapter;const _e=new Set(["public","token",...P.map(e=>e.name)]),ve=(e,t)=>{if(!P.length)return"";const o=[...new Set(e.filter(l=>l&&!_e.has(l)))];return o.length?`Not a role on this site: ${o.join(", ")}. Until you pick another, ${t}.`:""},n={};for(const[e,t]of Object.entries(m))n[e]=M.observable(t);const k=M.observable(D),U=M.observable(!1),H=()=>Object.fromEntries(Object.keys(n).map(e=>[e,n[e].peek()])),be=()=>Object.fromEntries(Object.keys(n).map(e=>[e,n[e].value])),F=M.observable(_(m)),B=M.computed(()=>_(be())!==F.value);let W=!D;v(M.effect(()=>{const e=n.title.value;W||(n.slug.value=X(e))}));const h=M.computed(()=>ut(be(),{isNew:k.value,taken:me,original:w})),ge=M.observable([]);v(M.effect(()=>{const e=h.value.fields||{},t=n.layout.value==="grid";ge.value=n.fields.value.map(o=>vt(o,e,{grid:t}))}));const Ke=M.computed(()=>new Set(n.fields.value.map(e=>e.name))),Ue={...n,isNew:k,saving:U,dirty:B,rows:ge,errTitle:M.computed(()=>h.value.title||""),errSlug:M.computed(()=>h.value.slug||""),errColumns:M.computed(()=>h.value.columns||""),errExport:M.computed(()=>h.value.export||""),problemCount:M.computed(()=>q(h.value)),problemText:M.computed(()=>{const e=q(h.value);return e?`${e} thing${e===1?"":"s"} to fix before it can be saved`:""}),settingsBad:M.computed(()=>!!(h.value.title||h.value.slug||h.value.columns)),fieldsBad:M.computed(()=>!!h.value.fields),exportBad:M.computed(()=>!!h.value.export),fieldCount:M.computed(()=>n.fields.value.length),slugHint:M.computed(()=>k.value?`Will be created as "${X(n.slug.value||n.title.value)||"\u2026"}".`:"Cannot be changed once the collection exists."),apiPath:M.computed(()=>`/api/collections/${X(n.slug.value||n.title.value)||"<slug>"}/public`),unknownReadFields:M.computed(()=>{const e=String(n.readFields.value||"").split(",").map(t=>t.trim()).filter(t=>t&&!Ke.value.has(t));return e.length?`Not a field of this collection: ${e.join(", ")}`:""}),missingApiRoles:M.computed(()=>ve(ct.filter(e=>n[`${e}Enabled`].value).map(e=>n[`${e}Access`].value),"only the level-0 role passes")),missingExportRole:M.computed(()=>ve(n.exportEnabled.value?[n.exportAccess.value]:[],"only the level-0 role is offered export")),showStorage:M.observable(!!N?.pro&&d!=="roles"),storageNote:M.computed(()=>{const e=Ne(K,n.storageAdapter.value);return!e||k.value?"":u?`Switching ${e}: Save asks whether to move the ${u} existing ${u===1?"entry":"entries"} across. Export them first if they matter.`:`Switching ${e}. There are no entries to move.`})},ye=i.querySelector("#collection-project");for(const e of ne){const t=document.createElement("option");t.value=e.slug,t.textContent=e.name||e.slug,ye.appendChild(t)}if(m.project&&!ne.some(e=>e.slug===m.project)){const e=document.createElement("option");e.value=m.project,e.textContent=`${m.project} (not found)`,ye.appendChild(e)}const He=[...i.querySelectorAll('.cex-op select[data-model$="Access.value"]')],xe=e=>{for(const t of He){const o=t.getAttribute("data-model").split(".")[0],l=e[o];t.replaceChildren(...pt(P,l,{token:t.id!=="export-access"}).map(r=>new Option(r.label,r.value))),t.value=l}};xe(m);const We=i.querySelector("#storage-connection"),Ye=Array.isArray(N?.connections)&&N.connections.length?N.connections:["default"];for(const e of new Set([...Ye,m.storageConnection])){const t=document.createElement("option");t.value=e,t.textContent=e,We.appendChild(t)}const Y=e=>n.fields.peek().findIndex(t=>t._key===e),j=e=>{n.fields.value=e};function z(e,t){const o=Y(e);o<0||(j(De(n.fields.peek(),o,o+t)),requestAnimationFrame(()=>i.querySelector(`.cex-row[data-key="${e}"]`)?.focus()))}async function oe(e){const t=Y(e);if(t<0)return;const o=n.fields.peek()[t];!k.peek()&&u&&w[e]&&!await E.confirm(`Remove the field "${Z(o.label||o.name)}"? The ${u} existing ${u===1?"entry keeps its":"entries keep their"} value for it, but it is no longer shown or edited. Nothing is lost until you Save - Discard puts it back.`)||j(n.fields.peek().filter(l=>l._key!==e))}function G(e,{isAdded:t=!1}={}){const o=Y(e);if(o<0)return;const l=n.fields.peek()[o],r=xt(l),c={};for(const[a,$]of Object.entries(r))c[a]=M.observable($);const f=JSON.stringify(r),V=()=>Object.fromEntries(Object.keys(c).map(a=>[a,c[a].peek()])),$e=()=>JSON.stringify(V())!==f;let Ce=!!l.name;const Ze=M.effect(()=>{const a=c.label.value;Ce||(c.name.value=gt(a))}),et=new Set(n.fields.peek().filter(a=>a._key!==e).map(a=>a.name)),Oe=M.computed(()=>{const a=Te(l,Object.fromEntries(Object.keys(c).map($=>[$,c[$].value])));return a.name&&et.has(a.name)?`"${a.name}" is used by another field.`:ht(a,w[e])}),tt=M.computed(()=>!k.peek()&&u&&w[e]&&c.name.value.trim()!==w[e]?`Entries keep their value under "${w[e]}" - renaming the field does not move it.`:""),nt={...c,problem:Oe,renamed:tt,isOptions:M.computed(()=>dt.has(c.type.value)),canAddon:M.computed(()=>it.has(c.type.value)),isGrid:M.computed(()=>n.layout.value==="grid")},b=E.slideover({title:t?"New field":`Field: ${Z(l.label||l.name||"Untitled")}`,size:"md",position:"right"}),T=Ot(`
|
|
9
9
|
<div class="cex-panel">
|
|
10
10
|
<div class="cex-grid">
|
|
11
11
|
<div>
|
|
@@ -20,7 +20,7 @@ import{api as g}from"../api.js";import{addBannerAction as de}from"../lib/plugin-
|
|
|
20
20
|
<div>
|
|
21
21
|
<label class="form-label" for="cex-f-type">Type</label>
|
|
22
22
|
<select id="cex-f-type" class="form-input" data-model="type.value">
|
|
23
|
-
${
|
|
23
|
+
${rt.map(a=>`<option value="${a.value}">${a.label}</option>`).join("")}
|
|
24
24
|
</select>
|
|
25
25
|
</div>
|
|
26
26
|
<div class="cex-setting">
|
|
@@ -42,6 +42,22 @@ import{api as g}from"../api.js";import{addBannerAction as de}from"../lib/plugin-
|
|
|
42
42
|
<span data-help-title="Options" data-help="One per line, as value: Label - the value is what is stored, the label what is shown. A line with no colon uses the same text for both."></span></label>
|
|
43
43
|
<textarea id="cex-f-options" class="form-input font-mono" rows="5" placeholder="s: Small m: Medium" data-model="options.value"></textarea>
|
|
44
44
|
</div>
|
|
45
|
+
<div class="cex-wide cex-addons" data-bind-hidden="!canAddon.value">
|
|
46
|
+
<span class="form-label">Joined to the input
|
|
47
|
+
<span data-help-title="Icon or text beside the input" data-help="An icon or a short text joined to the start or end of the input in the entry form - a magnifier before a search box, \xA3 before a price, kg after a weight. Pick an icon, or type text; an icon wins if both are set."></span></span>
|
|
48
|
+
<div class="cex-addon-row">
|
|
49
|
+
<span class="cex-addon-side">Before</span>
|
|
50
|
+
<input id="cex-f-prefix-icon" type="text" class="form-input" maxlength="60" placeholder="Icon" aria-label="Icon before the input" data-model="prefixIcon.value">
|
|
51
|
+
<button type="button" class="btn btn-sm btn-ghost" title="Browse icons" aria-label="Browse icons for before the input" data-on-click="pickPrefix"><span data-icon="grid" data-icon-size="14"></span></button>
|
|
52
|
+
<input type="text" class="form-input" maxlength="24" placeholder="or text, e.g. \xA3" aria-label="Text before the input" data-model="prefixText.value">
|
|
53
|
+
</div>
|
|
54
|
+
<div class="cex-addon-row">
|
|
55
|
+
<span class="cex-addon-side">After</span>
|
|
56
|
+
<input id="cex-f-suffix-icon" type="text" class="form-input" maxlength="60" placeholder="Icon" aria-label="Icon after the input" data-model="suffixIcon.value">
|
|
57
|
+
<button type="button" class="btn btn-sm btn-ghost" title="Browse icons" aria-label="Browse icons for after the input" data-on-click="pickSuffix"><span data-icon="grid" data-icon-size="14"></span></button>
|
|
58
|
+
<input type="text" class="form-input" maxlength="24" placeholder="or text, e.g. kg" aria-label="Text after the input" data-model="suffixText.value">
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
45
61
|
<div data-bind-hidden="!isGrid.value">
|
|
46
62
|
<label class="form-label" for="cex-f-span">Column span
|
|
47
63
|
<span data-help="How many of the grid's columns the field takes, in the entry form."></span></label>
|
|
@@ -61,4 +77,4 @@ import{api as g}from"../api.js";import{addBannerAction as de}from"../lib/plugin-
|
|
|
61
77
|
<button type="button" class="btn btn-sm btn-ghost" data-on-click="cancel">Cancel</button>
|
|
62
78
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="done" data-bind-disabled="problem.value"><span data-icon="check" data-icon-size="13"></span> Done</button>
|
|
63
79
|
</div>
|
|
64
|
-
</div>`);let Q=!1,re=!1;const
|
|
80
|
+
</div>`);let Q=!1,re=!1;const ot=b.close.bind(b);function je(){if(Oe.peek())return;const a=Te(l,V());j(n.fields.peek().map($=>$._key===e?a:$)),Q=!0,b.close()}b.element.appendChild(T);const at=Ie(T),lt=M.applyBindings(nt,T,{methods:{done:je,cancel:()=>b.close(),pickPrefix:()=>Pe(a=>{c.prefixIcon.value=a,c.prefixText.value=""}),pickSuffix:()=>Pe(a=>{c.suffixIcon.value=a,c.suffixText.value=""}),remove:async()=>{Q=!0,b.close(),await oe(e)}}}),Ae=T.querySelector("#cex-f-name");Ae.addEventListener("input",()=>{Ce=!!Ae.value}),T.addEventListener("keydown",a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),je())}),st(b,()=>{Ze(),lt.dispose(),at(),se.delete(b)}),b.close=async a=>{if(!Q&&a!==!0&&$e()){if(re)return;re=!0;const ce=await E.confirm("Discard the changes to this field?");if(re=!1,!ce)return}Q=!0;const $=ot();return t&&!$e()&&!l.name&&j(n.fields.peek().filter(ce=>ce._key!==e)),$},se.add(b),I.scan(T),b.open(),T.querySelector("#cex-f-label")?.focus()}function ze(){const e=yt();j([...n.fields.peek(),e]),Ve.activate?.(1),G(e._key,{isAdded:!0})}function Ge(e){const t=Y(e);if(t<0)return[];const o=n.fields.peek()[t],l=n.fields.peek().length;return[{header:o.label||o.name||"Field"},{label:"Edit\u2026",icon:"edit",action:()=>G(e)},{label:"Move up",icon:"arrow-up",disabled:t===0,action:()=>z(e,-1)},{label:"Move down",icon:"arrow-down",disabled:t===l-1,action:()=>z(e,1)},{label:"Duplicate",icon:"copy",action:()=>j(mt(n.fields.peek(),t))},{type:"divider"},{label:"Remove\u2026",icon:"trash",danger:!0,action:()=>oe(e)}]}async function ae(){if(U.peek())return;const e=h.peek();if(q(e)){E.toast(`Not saved - ${q(e)} thing${q(e)===1?"":"s"} to fix first (marked on the tabs).`,{type:"warning"});return}const t=wt(H(),{isNew:k.peek(),meta:qe});U.value=!0;try{if(k.peek()){const l=await g.collections.create(t);F.value=_(H()),ie(),E.toast(`Created "${t.title}".`,{type:"success"}),R.navigate(`/collections/edit/${encodeURIComponent(l.slug)}`);return}const o=Ne(K,t.storage.adapter);if(o&&u){if(await E.confirm(`You changed the storage (${o}). Move the ${u} existing ${u===1?"entry":"entries"} to the new storage?`)){const r=await g.collections.migrateStorage(d,t.storage);E.toast(`Moved ${r.migrated} of ${r.total} entries.`,{type:"success"})}else if(!await E.confirm(`Switch without moving them? The ${u} ${u===1?"entry stays":"entries stay"} in ${Z(K)} storage and this collection will show none until they are moved.`))return}if(await g.collections.update(d,t),!te())return;K=t.storage.adapter,o&&(u=(await g.collections.listEntries(d,{limit:1}).catch(()=>null))?.total??u);for(const l of n.fields.peek())w[l._key]=l.name;F.value=_(H()),ie(),Be(p,t.title),E.toast("Collection saved.",{type:"success"})}catch(o){E.toast(`Not saved: ${o.message||o}`,{type:"error"})}finally{U.value=!1}}async function Je(){if(!B.peek()||!await E.confirm("Discard the changes you have not saved?"))return;const e=Fe(O,{project:he});for(const t of e.fields)w[t._key]=t.name;for(const[t,o]of Object.entries(e))n[t].value=o;F.value=_(e),W=!k.peek()}const L=i.querySelector(".cex-list");L.innerHTML=Ct;const Ve=E.tabs(i.querySelector(".cex-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),Qe=M.applyBindings(Ue,i,{methods:{save:()=>ae(),discard:()=>Je(),addField:()=>ze()}});v(()=>Qe.dispose()),v(Ie(i,{position:"bottom"}));const le=i.querySelector("#field-slug"),we=()=>{W=!!le.value};le.addEventListener("input",we),v(()=>le.removeEventListener("input",we));const se=new Set;v(()=>[...se].forEach(e=>{try{e.close(!0)}catch{}}));const Xe=E.contextMenu(L,{cursor:"glyph",match:".cex-row",inherit:!1,density:"compact",items:e=>Ge(e.dataset.key)});v(()=>Xe?.destroy?.());const ke={click:e=>{const t=e.target.closest(".cex-row");if(!t)return;const o=e.target.closest(".cex-kebab");if(o){Re(o);return}G(t.dataset.key)},keydown:e=>{const t=e.target.closest(".cex-row");!t||e.target!==t||(e.key==="F10"&&e.shiftKey?(e.preventDefault(),Re(t)):e.key==="Enter"?G(t.dataset.key):e.altKey&&e.key==="ArrowUp"?(e.preventDefault(),z(t.dataset.key,-1)):e.altKey&&e.key==="ArrowDown"?(e.preventDefault(),z(t.dataset.key,1)):e.key==="Delete"&&(e.preventDefault(),oe(t.dataset.key)))}};for(const[e,t]of Object.entries(ke))L.addEventListener(e,t);if(v(()=>{for(const[e,t]of Object.entries(ke))L.removeEventListener(e,t)}),typeof E.sortable=="function"){const e=St(),t=E.sortable(L,{items:".cex-row",key:"data-key",keyboard:!1,onStart:e.start,onEnd:({item:o})=>e.restore(o),onSort:({item:o,from:l,to:r})=>{e.restore(o),j(De(n.fields.peek(),l,r))}});v(()=>t.destroy())}Be(p,O?.title||"New collection"),de(s,{id:"collection-editor-back",icon:"arrow-left",title:"All collections",onClick:()=>R.navigate("/collections")}),d&&de(s,{id:"collection-editor-entries",icon:"list",title:"Open entries",onClick:()=>R.navigate(`/collections/${encodeURIComponent(d)}/entries`)}),de(s,{id:"collection-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>ae()});const Ee=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||!te()||(e.preventDefault(),e.stopImmediatePropagation(),ae())};document.addEventListener("keydown",Ee,!0),v(()=>document.removeEventListener("keydown",Ee,!0));const J=$t(d);function ie(){try{S.remove?S.remove(J):S.set(J,null)}catch{}}const Se=()=>{if(B.peek())try{S.set(J,{form:ft(H()),base:F.peek(),at:new Date().toISOString()})}catch{}},Me=e=>{B.peek()&&(Se(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",Me),v(()=>window.removeEventListener("beforeunload",Me)),y={path:C,dirty:()=>B.peek(),dispose(){ue=!0,Se(),pe.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(i);let A=null;try{A=S.get(J)}catch{}if(A?.form){ie();const e=A.base&&A.base!==F.peek(),t=A.at?` (${Et(A.at)})`:"",o=d?`changes to "${Z(O?.title||d)}"`:"a new collection",l=e?`You left unsaved ${o}${t}, but the saved collection has changed since. Restore your version anyway?`:`You left unsaved ${o}${t}. Restore them?`;if(await E.confirm(l)&&te()){const r=bt(A.form,m);D||(r.slug=m.slug);const c=Object.fromEntries(m.fields.filter(f=>f.id).map(f=>[f.id,f.name]));for(const f of r.fields)f.id&&c[f.id]!==void 0&&(w[f._key]=c[f.id]);xe(r);for(const[f,V]of Object.entries(r))n[f]&&(n[f].value=V);D&&(W=!!r.slug&&r.slug!==X(r.title))}}}};
|
|
@@ -4,7 +4,7 @@ import{api as g,apiRequest as it}from"../api.js";import{addBannerAction as z}fro
|
|
|
4
4
|
${Array.from({length:m},(v,a)=>`<td data-col="${a}" title="{{t${a}}}">{{c${a}}}</td>`).join("")}
|
|
5
5
|
<td class="cex-when" title="{{whenFull}}">{{when}}</td>
|
|
6
6
|
<td class="cex-tools"><button type="button" class="cex-run" data-bind-hidden="!runs" title="Run one of this collection's actions">\u26A1 {{runs}}</button><button type="button" class="cex-kebab" aria-label="Actions for this entry" title="Actions">\u22EE</button></td>
|
|
7
|
-
</tr>`}function wt(m,h){const v={};return m.forEach(a=>{const
|
|
7
|
+
</tr>`}function wt(m,h){const v={};return m.forEach(a=>{const r={};a.fullWidth?r.span=h:a.span>1&&(r.span=a.span),a.placeholder&&(r.placeholder=a.placeholder),a.helper&&(r.hint=a.helper),a.prefix&&(r.prefix=a.prefix),a.suffix&&(r.suffix=a.suffix);const d=a.type==="checkbox"?"boolean":a.type==="date"?"string":a.type;v[a.name]={type:d,label:a.label,required:a.required,options:a.options,...Object.keys(r).length?{formConfig:r}:{}}}),v}function ce(m,h){const v={};for(const a of h){const r=[...m.querySelectorAll(`[name="${CSS.escape(a.name)}"]`)];if(!r.length)continue;const d=r[0];d.type==="radio"?v[a.name]=r.find(p=>p.checked)?.value??"":d.type==="checkbox"&&r.length>1?v[a.name]=r.filter(p=>p.checked).map(p=>p.value):d.type==="checkbox"?v[a.name]=d.checked:d.tagName==="SELECT"&&d.multiple?v[a.name]=[...d.selectedOptions].map(p=>p.value):v[a.name]=gt(a,d.value)}return v}function Fe(m,h,v){for(const a of h){if(!(a.name in v))continue;const r=v[a.name],d=[...m.querySelectorAll(`[name="${CSS.escape(a.name)}"]`)];if(!d.length)continue;const p=d[0];p.type==="radio"?d.forEach(c=>{c.checked=c.value===String(r)}):p.type==="checkbox"&&d.length>1?d.forEach(c=>{c.checked=Array.isArray(r)&&r.includes(c.value)}):p.type==="checkbox"?p.checked=r===!0:p.tagName==="SELECT"&&p.multiple?[...p.options].forEach(c=>{c.selected=Array.isArray(r)&&r.includes(c.value)}):p.value=r??"",d.forEach(c=>c.dispatchEvent(new Event("input",{bubbles:!0})))}}const Pe=dt(Be);export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(m){Pe.reset();const h=m.get(0).querySelector(".cex")||m.get(0),v=location.hash.match(/^#\/collections\/([^/?#]+)\/entries/),a=v?decodeURIComponent(v[1]):null,r=()=>Be.test(location.hash)&&h.isConnected&&location.hash.includes(`/collections/${encodeURIComponent(a)}/entries`),d=e=>(Pe.add(e),e);if(!a){E.toast("No collection selected.",{type:"error"});return}const p=M.observable(null),c=M.observable([]),x=M.observable(0),Y=M.observable(!0),_=M.observable(""),_e=M.computed(()=>p.value!==null),le=M.observable(""),w=M.observable([]),Z=M.observable({key:"created",dir:"desc"}),y=M.observable([]),q=M.observable(X),J=M.observable([]),Q=M.observable({}),A=M.observable(!1);let ee=1;const ie=()=>p.peek()?.fields||[],re=M.computed(()=>(p.value?.fields||[]).slice(0,ut)),V=e=>c.peek().find(t=>t.id===e),ye=e=>J.peek().filter(t=>{const n=Q.peek()[t.slug];return n==null||n.has(e)}),T=M.observable([]),ve=M.observable([]);d(M.effect(()=>{const e=pt(c.value,{filters:w.value,query:le.value,sort:Z.value});T.value=e})),d(M.effect(()=>{const e=re.value,t=new Set(y.value);Q.value,J.value,ve.value=T.value.slice(0,q.value).map(n=>{const s={id:n.id,sel:t.has(n.id),rowCls:t.has(n.id)?"is-selected":""};e.forEach((i,u)=>{const f=he(i,n.data?.[i.name]);s[`c${u}`]=Ue(f),s[`t${u}`]=je(f)});const o=n.meta?.createdAt;s.when=o?Et(o):"",s.whenFull=o?`Created ${B(o)}${n.meta?.updatedAt?` \xB7 changed ${B(n.meta.updatedAt)}`:""}`:"";const l=ye(n.id).length;return s.runs=l?String(l):"",s})}));const Je=M.computed(()=>w.value.map((e,t)=>({key:`${t}:${e.field}:${e.op}:${e.value}`,idx:t,text:vt(e)}))),Ve=M.computed(()=>y.value.length),He={schema:p,loaded:c,loading:Y,loadError:_,ready:_e,query:le,rows:ve,chips:Je,busy:A,selectedCount:Ve,countLine:M.computed(()=>mt(T.value.length,c.value.length,x.value)),selectedText:M.computed(()=>`${y.value.length} selected`),canClear:M.computed(()=>ft(a)),introText:M.computed(()=>{const e=p.value;return e?`${e.description?`${e.description} - `:""}${(e.fields||[]).length} field${(e.fields||[]).length===1?"":"s"}${e.storage?.adapter==="mongodb"?", on MongoDB":""}.`:""}),emptyText:M.computed(()=>c.value.length?"No entries match the search or filters.":"No entries yet - Add entry in the banner makes one, or Import brings many."),moreText:M.computed(()=>{const e=T.value.length-q.value;if(e>0)return`Show ${Math.min(X,e)} more (${e} not shown)`;const t=x.value-c.value.length;return t>0?`Load the next ${Math.min(se,t)} from the server`:""})};async function H({reset:e=!0}={}){Y.value=!0;try{const t=e?Math.max(se,c.peek().length):se;e&&(ee=1);const n=await g.collections.listEntries(a,{limit:t,page:ee});if(!r())return;const s=n?.entries||(Array.isArray(n)?n:[]);c.value=e?s:[...c.peek(),...s.filter(l=>!V(l.id))],x.value=Number(n?.total)||c.peek().length,e&&(ee=Math.max(1,Math.ceil(c.peek().length/se)));const o=new Set(c.peek().map(l=>l.id));y.value=y.peek().filter(l=>o.has(l)),_.value="",await We()}catch(t){_.value=`Could not load the entries: ${t.message||t}`}finally{Y.value=!1}}async function Ke(){ee+=1,await H({reset:!1})}async function Ge(){try{if((await it("/actions/_count",{method:"GET"}).catch(()=>null))?.available===!1)return;const t=await g.actions.forCollection(a);J.value=Array.isArray(t)?t:[]}catch{J.value=[]}}async function We(){const e=J.peek().filter(s=>s.access?.rowLevel);if(!e.length||!c.peek().length)return;const t=c.peek().map(s=>s.id),n={...Q.peek()};await Promise.all(e.map(async s=>{try{n[s.slug]=new Set((await g.actions.checkAccess(s.slug,t)).allowed)}catch{n[s.slug]=null}})),Q.value=n}const K=e=>{const t=ie()[0];return(t?Ue(he(t,e?.data?.[t.name]),40):"")||`Entry ${String(e?.id||"").slice(0,8)}`};async function be(e){if(await E.confirm(`Delete \u201C${D(K(e))}\u201D? This cannot be undone.`))try{await g.collections.deleteEntry(a,e.id),c.value=c.peek().filter(t=>t.id!==e.id),x.value=Math.max(0,x.peek()-1),y.value=y.peek().filter(t=>t!==e.id),j("#/collections"),E.toast("Entry deleted.",{type:"success"})}catch(t){E.toast(`Not deleted: ${t.message||t}`,{type:"error"})}}async function Xe(){const e=[...y.peek()];if(!e.length||!await E.confirm(`Delete ${e.length} entr${e.length===1?"y":"ies"}? This cannot be undone.`))return;A.value=!0;let t=0;const n=[];for(const s of e)try{await g.collections.deleteEntry(a,s),t++}catch(o){n.push(o.message||String(o))}A.value=!1,y.value=[],j("#/collections"),E.toast(n.length?`Deleted ${t}; ${n.length} refused (${n[0]}).`:`Deleted ${t} entr${t===1?"y":"ies"}.`,{type:n.length?"warning":"success",duration:n.length?7e3:4e3}),r()&&await H()}async function Ye(){const e=p.peek();if(await E.confirm(`Delete ALL ${x.peek()} entries in \u201C${D(e?.title||a)}\u201D? This cannot be undone.`)){A.value=!0;try{await g.collections.clearEntries(a),c.value=[],x.value=0,y.value=[],j("#/collections"),E.toast("All entries deleted.",{type:"success"})}catch(t){E.toast(`Not cleared: ${t.message||t}`,{type:"error"})}finally{A.value=!1}}}async function Ze(e){try{const t=await g.collections.createEntry(a,{...e.data||{}});c.value=[t,...c.peek()],x.value=x.peek()+1,j("#/collections"),E.toast("Duplicated - the copy is at the top.",{type:"success"})}catch(t){E.toast(`Not duplicated: ${t.message||t}`,{type:"error"})}}async function Qe(e,t){const n=e.trigger?.confirmMessage;if(!(n&&!await E.confirm(D(n)))){A.value=!0;try{const s=await g.actions.execute(e.slug,t.id);if(s?.success){const o=s.stepsCompleted??0;E.toast(`${e.trigger?.label||e.title||"Action"} done (${o} step${o===1?"":"s"}).`,{type:"success"}),r()&&await H()}else E.toast(s?.results?.find(o=>!o.success)?.error||"The action failed.",{type:"error"})}catch(s){E.toast(s.message||"The action failed.",{type:"error"})}finally{A.value=!1}}}async function ge(e){try{await navigator.clipboard.writeText(e),E.toast("Copied.",{type:"success",duration:1500})}catch{E.toast("Copy failed - your browser refused the clipboard.",{type:"error"})}}function de(e){const t=y.peek();y.value=t.includes(e)?t.filter(n=>n!==e):[...t,e]}function Ee(e,t,n){w.value=[...w.peek(),{field:e.name,label:e.label||e.name,op:t,value:n}],q.value=X}const ue=new Set;let N=null,ke=!1;function xe(e,t,n){const s=e.close.bind(e);let o=!1,l=!1;e.close=async i=>{if(!o&&i!==!0&&!ke&&t()){if(l)return;l=!0;const u=await E.confirm(n);if(l=!1,!u)return}return o=!0,s()}}function te(e,t=()=>{}){ue.add(e),rt(e,()=>{ue.delete(e),t()})}function O(e,t=null){const n=p.peek(),s=ie(),o=!!e,l=E.slideover({title:o?`Edit ${D(K(e))}`:"New entry",size:"md",position:"right"}),i=oe(`
|
|
8
8
|
<div class="cex-form">
|
|
9
9
|
<div class="cex-fields"></div>
|
|
10
10
|
<small class="cex-hint cex-when-line"></small>
|
|
@@ -15,7 +15,7 @@ import{api as g,apiRequest as it}from"../api.js";import{addBannerAction as z}fro
|
|
|
15
15
|
<button type="button" class="btn btn-sm btn-ghost" data-act="cancel">Cancel</button>
|
|
16
16
|
<button type="button" class="btn btn-sm btn-primary" data-act="save"><span data-icon="check" data-icon-size="13"></span> <span class="cex-save-text"></span></button>
|
|
17
17
|
</div>
|
|
18
|
-
</div>`);i.querySelector(".cex-save-text").textContent=o?"Save":"Add entry";const
|
|
18
|
+
</div>`);i.querySelector(".cex-save-text").textContent=o?"Save":"Add entry";const u=i.querySelector(".cex-fields");F.create(wt(s,n?.columns||2),e?.data||{},{layout:n?.layout||"stacked",columns:n?.columns||2,showSubmitButton:!1}).renderTo(u),s.forEach(k=>{if(k.type!=="date"||!k.name)return;const U=u.querySelector(`[name="${CSS.escape(k.name)}"]`);U&&U.type!=="date"&&(U.type="date")}),e?.data&&Fe(u,s,e.data),o&&e.meta?.createdAt&&(i.querySelector(".cex-when-line").textContent=`Created ${B(e.meta.createdAt)}${e.meta.updatedAt?`, last changed ${B(e.meta.updatedAt)}`:""}${e.meta.source?` \xB7 via ${e.meta.source}`:""}.`),l.element.appendChild(i);const f=ze(ce(u,s));t&&Fe(u,s,t);const b=()=>ze(ce(u,s))!==f,lt=i.querySelector(".cex-dirty"),fe=()=>{lt.hidden=!b()};u.addEventListener("input",fe),u.addEventListener("change",fe),fe();let me=!1;async function Ne(){if(me)return;document.activeElement?.dispatchEvent?.(new Event("change",{bubbles:!0}));const k=ce(u,s);me=!0;const U=i.querySelector('[data-act="save"]');U.disabled=!0;try{if(o){const W=await g.collections.updateEntry(a,e.id,k);c.value=c.peek().map(De=>De.id===e.id?W:De),E.toast("Entry saved.",{type:"success"})}else{const W=await g.collections.createEntry(a,k);c.value=[W,...c.peek()],x.value=x.peek()+1,j("#/collections"),E.toast("Entry added.",{type:"success"})}try{S.remove?S.remove(P(a)):S.set(P(a),null)}catch{}l.close(!0)}catch(W){E.toast(W.message||"Not saved.",{type:"error"})}finally{me=!1,U.disabled=!1}}i.querySelector('[data-act="save"]').addEventListener("click",Ne),i.querySelector('[data-act="cancel"]').addEventListener("click",()=>l.close()),i.addEventListener("keydown",k=>{(k.ctrlKey||k.metaKey)&&(k.key==="Enter"||k.key==="s")&&(k.preventDefault(),Ne())});const Ie={id:e?.id??null,dirty:b,values:()=>ht(ce(u,s),s)};N=Ie,te(l,()=>{N===Ie&&(N=null)}),xe(l,b,"Discard the changes you have not saved?"),I.scan(i),l.open(),u.querySelector("input, select, textarea")?.focus()}function et(e){const t=E.slideover({title:D(K(e)),size:"md",position:"right"}),n=oe(`
|
|
19
19
|
<div class="cex-form">
|
|
20
20
|
<dl class="cex-detail"></dl>
|
|
21
21
|
<div class="cex-meta"></div>
|
|
@@ -24,7 +24,7 @@ import{api as g,apiRequest as it}from"../api.js";import{addBannerAction as z}fro
|
|
|
24
24
|
<span class="cex-spacer"></span>
|
|
25
25
|
<button type="button" class="btn btn-sm btn-primary" data-act="edit"><span data-icon="edit" data-icon-size="13"></span> Edit</button>
|
|
26
26
|
</div>
|
|
27
|
-
</div>`),s=n.querySelector(".cex-detail");for(const i of ie()){const
|
|
27
|
+
</div>`),s=n.querySelector(".cex-detail");for(const i of ie()){const u=je(he(i,e.data?.[i.name])),f=document.createElement("dt");f.textContent=i.label||i.name;const b=document.createElement("dd");b.textContent=u===""?"-":u,s.append(f,b)}const o=n.querySelector(".cex-meta"),l=i=>{const u=document.createElement("span");u.textContent=i,o.appendChild(u)};l(`Id: ${e.id}`),e.meta?.createdAt&&l(`Created: ${B(e.meta.createdAt)}`),e.meta?.updatedAt&&l(`Changed: ${B(e.meta.updatedAt)}`),e.meta?.source&&l(`Source: ${e.meta.source}`),n.querySelector('[data-act="copy"]').addEventListener("click",()=>ge(JSON.stringify(e,null,2))),n.querySelector('[data-act="edit"]').addEventListener("click",()=>{t.close(!0),O(e)}),t.element.appendChild(n),te(t),I.scan(n),t.open()}function tt(){const e=E.slideover({title:"Import entries",size:"md",position:"right"}),t=oe(`
|
|
28
28
|
<div class="cex-form">
|
|
29
29
|
<p class="cex-hint">A JSON array of entries, each with a "data" object of field values - what Export gives you.
|
|
30
30
|
<span data-help="Each entry is added as new, with a new id and today's date (the Data Transfer plugin from the Marketplace keeps ids and dates). Entries are checked like any other save - one that fails, or points a reference at an entry that does not exist, is skipped and reported. Values are stored as given, including fields the collection does not have."></span></p>
|
|
@@ -40,11 +40,11 @@ import{api as g,apiRequest as it}from"../api.js";import{addBannerAction as z}fro
|
|
|
40
40
|
<button type="button" class="btn btn-sm btn-ghost" data-act="cancel">Cancel</button>
|
|
41
41
|
<button type="button" class="btn btn-sm btn-primary" data-act="import"><span data-icon="upload" data-icon-size="13"></span> Import</button>
|
|
42
42
|
</div>
|
|
43
|
-
</div>`),n=t.querySelector("textarea"),s=t.querySelector(".cex-err"),o=t.querySelector(".cex-ok"),l=t.querySelector('input[type="file"]');t.querySelector('[data-act="file"]').addEventListener("click",()=>l.click()),l.addEventListener("change",async()=>{const
|
|
43
|
+
</div>`),n=t.querySelector("textarea"),s=t.querySelector(".cex-err"),o=t.querySelector(".cex-ok"),l=t.querySelector('input[type="file"]');t.querySelector('[data-act="file"]').addEventListener("click",()=>l.click()),l.addEventListener("change",async()=>{const u=l.files?.[0];l.value="",u&&(n.value=await u.text())}),t.querySelector('[data-act="cancel"]').addEventListener("click",()=>e.close());let i=!1;t.querySelector('[data-act="import"]').addEventListener("click",async()=>{s.hidden=!0,o.hidden=!0;let u;try{if(u=JSON.parse(n.value),!Array.isArray(u))throw new Error("it must be a JSON array")}catch(b){s.textContent=`Not valid: ${b.message}`,s.hidden=!1;return}const f=t.querySelector('[data-act="import"]');f.disabled=!0;try{const b=await g.collections.import(a,u);o.textContent=`Imported ${b.imported}; skipped ${b.skipped}.`,o.hidden=!1,i=!0,j("#/collections"),r()&&await H(),setTimeout(()=>e.close(!0),1200)}catch(b){s.textContent=b.message||"Import failed.",s.hidden=!1}finally{f.disabled=!1}}),e.element.appendChild(t),te(e),xe(e,()=>!i&&n.value.trim()!=="","Discard the entries you pasted?"),Oe(t),I.scan(t),e.open(),n.focus()}function nt(){const e=E.slideover({title:"Export entries",size:"sm",position:"right"}),t=oe(`
|
|
44
44
|
<div class="cex-form">
|
|
45
45
|
<p class="cex-hint">Every entry in the collection - not only the ones shown.</p>
|
|
46
46
|
<div class="cex-export">
|
|
47
47
|
<button type="button" class="btn btn-ghost" data-fmt="json"><span data-icon="download" data-icon-size="14"></span> Export as JSON</button>
|
|
48
48
|
<button type="button" class="btn btn-ghost" data-fmt="csv"><span data-icon="download" data-icon-size="14"></span> Export as CSV</button>
|
|
49
49
|
</div>
|
|
50
|
-
</div>`);t.querySelectorAll("[data-fmt]").forEach(n=>n.addEventListener("click",async()=>{const s=n.dataset.fmt;try{const o=await fetch(`/api/collections/${encodeURIComponent(a)}/export?format=${s}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}});if(!o.ok)throw new Error(`${o.status}`);const l=await o.blob(),i=document.createElement("a");i.href=URL.createObjectURL(l),i.download=`${a}-entries.${s}`,document.body.appendChild(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(i.href),1e3),e.close(!0)}catch(o){E.toast(`Export failed (${o.message}).`,{type:"error"})}})),e.element.appendChild(t),te(e),I.scan(t),e.open()}function at(e,t){const n=V(e.dataset.id);if(!n)return[];const s=t?.event?.target?.closest?.("td[data-col]"),o=s?re.peek()[Number(s.dataset.col)]:null,l=ye(n.id),i=y.peek().includes(n.id),
|
|
50
|
+
</div>`);t.querySelectorAll("[data-fmt]").forEach(n=>n.addEventListener("click",async()=>{const s=n.dataset.fmt;try{const o=await fetch(`/api/collections/${encodeURIComponent(a)}/export?format=${s}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}});if(!o.ok)throw new Error(`${o.status}`);const l=await o.blob(),i=document.createElement("a");i.href=URL.createObjectURL(l),i.download=`${a}-entries.${s}`,document.body.appendChild(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(i.href),1e3),e.close(!0)}catch(o){E.toast(`Export failed (${o.message}).`,{type:"error"})}})),e.element.appendChild(t),te(e),I.scan(t),e.open()}function at(e,t){const n=V(e.dataset.id);if(!n)return[];const s=t?.event?.target?.closest?.("td[data-col]"),o=s?re.peek()[Number(s.dataset.col)]:null,l=ye(n.id),i=y.peek().includes(n.id),u=o?yt(o,n.data?.[o.name]):[];return[{header:K(n)},{label:"Edit\u2026",icon:"edit",action:()=>O(n)},{label:"See all of it\u2026",icon:"eye",action:()=>et(n)},{label:"Duplicate",icon:"copy",action:()=>Ze(n)},{label:"Copy id",icon:"hash",action:()=>ge(n.id)},{label:i?"Deselect":"Select",icon:"check",action:()=>de(n.id)},...l.length?[{type:"divider"},...l.map(f=>({label:`${f.trigger?.label||f.title||f.slug}\u2026`,icon:f.trigger?.icon||"zap",disabled:A.peek(),action:()=>Qe(f,n)}))]:[],...o?[{type:"divider"},{label:`Filter by ${o.label||o.name}`,icon:"filter",submenu:[...u.map(f=>({label:f.label,action:()=>Ee(o,f.op,f.value)})),{label:`${o.label||o.name} contains\u2026`,action:async()=>{const f=await E.prompt(`Show entries whose ${D(o.label||o.name)} contains`,{inputPlaceholder:"text"});f&&String(f).trim()&&Ee(o,"contains",String(f).trim())}}]}]:[],...w.peek().length?[{label:`Clear filters (${w.peek().length})`,icon:"x",action:()=>{w.value=[]}}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>be(n)}]}const L=h.querySelector(".cex-rows"),G=h.querySelector(".cex-head"),pe=h.querySelector(".cex-chips-list");pe.innerHTML=kt;const st=E.loader(h,{type:"dots"});try{p.value=await g.collections.get(a)}catch(e){_.value=`Could not load the collection: ${e.message||e}`}finally{st.destroy()}if(!r())return;if(!p.peek()&&!_.peek()){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const we=re.peek();L.innerHTML=xt(we.length),G.replaceChildren();const ne=(e,t)=>{const n=document.createElement("th");return e&&(n.className=e),t&&n.append(t),G.appendChild(n),n},$=document.createElement("input");$.type="checkbox",$.title="Select every entry shown",$.setAttribute("aria-label","Select every entry shown"),ne("cex-check",$);const Se=(e,t)=>{const n=document.createElement("button");return n.type="button",n.dataset.sort=e,n.append(document.createTextNode(t),Object.assign(document.createElement("span"),{className:"cex-sort"})),n};we.forEach(e=>ne("",Se(e.name,e.label||e.name))),ne("cex-when",Se("created","Created")),ne("cex-tools"),d(M.effect(()=>{const e=Z.value;G.querySelectorAll("[data-sort]").forEach(t=>{t.querySelector(".cex-sort").textContent=t.dataset.sort===e.key?e.dir==="asc"?"\u25B2":"\u25BC":"",t.parentElement.setAttribute("aria-sort",t.dataset.sort===e.key?e.dir==="asc"?"ascending":"descending":"none")})})),d(M.effect(()=>{const e=T.value.map(n=>n.id),t=new Set(y.value);$.checked=e.length>0&&e.every(n=>t.has(n)),$.indeterminate=!$.checked&&e.some(n=>t.has(n))}));const Ce=e=>{const t=e.target.closest("[data-sort]");if(!t)return;const n=Z.peek();Z.value={key:t.dataset.sort,dir:n.key===t.dataset.sort&&n.dir==="asc"?"desc":"asc"}};G.addEventListener("click",Ce),$.addEventListener("change",()=>{const e=T.peek().map(t=>t.id);y.value=$.checked?[...new Set([...y.peek(),...e])]:y.peek().filter(t=>!e.includes(t))}),d(()=>G.removeEventListener("click",Ce));const ot=M.applyBindings(He,h,{methods:{clearSelection:()=>{y.value=[]},deleteSelected:()=>Xe(),clearAll:()=>Ye(),clearFilters:()=>{w.value=[]},showMore:()=>{T.peek().length>q.peek()?q.value=q.peek()+X:Ke()}}});d(()=>ot.dispose()),d(Oe(h,{position:"bottom"})),d(M.effect(()=>{le.value,w.value,q.value=X}));const $e=e=>{const t=e.target.closest("button[data-idx]");if(!t)return;const n=Number(t.dataset.idx);w.value=w.peek().filter((s,o)=>o!==n)};pe.addEventListener("click",$e),d(()=>pe.removeEventListener("click",$e));const ct=E.contextMenu(L,{cursor:"glyph",match:".cex-row",inherit:!1,density:"compact",items:(e,t)=>at(e,t)});d(()=>ct?.destroy?.());const Ae=e=>{const t=e.target.closest(".cex-row");if(!t)return;if(e.target.closest(".cex-kebab, .cex-run")){Re(e.target.closest(".cex-kebab, .cex-run"));return}if(e.target.closest(".cex-check"))return;const n=V(t.dataset.id);n&&O(n)},Me=e=>{if(!e.target.matches(".cex-sel"))return;const t=e.target.closest(".cex-row");t&&de(t.dataset.id)},Le=e=>{const t=e.target.closest(".cex-row");if(!t||e.target!==t)return;const n=V(t.dataset.id);n&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),Re(t)):e.key==="Enter"?(e.preventDefault(),O(n)):e.key===" "?(e.preventDefault(),de(n.id)):e.key==="Delete"&&(e.preventDefault(),be(n)))};L.addEventListener("click",Ae),L.addEventListener("change",Me),L.addEventListener("keydown",Le),d(()=>{L.removeEventListener("click",Ae),L.removeEventListener("change",Me),L.removeEventListener("keydown",Le)});const qe=p.peek(),ae=m.get(0).querySelector(".dm-plugin-banner-name")?.querySelector(".dm-plugin-banner-sep");if(ae&&qe?.title){for(;ae.nextSibling;)ae.nextSibling.remove();ae.after(document.createTextNode(qe.title))}z(m,{id:"entries-back",icon:"arrow-left",title:"All collections",onClick:()=>{location.hash="#/collections"}}),z(m,{id:"entries-fields",icon:"edit-3",title:"Edit its fields and settings",onClick:()=>{location.hash=`#/collections/edit/${encodeURIComponent(a)}`}}),bt(a)&&z(m,{id:"entries-form",icon:"layout",title:"Open its form",onClick:()=>{location.hash=`#/forms/edit/${encodeURIComponent(a)}`}}),z(m,{id:"entries-export",icon:"download",title:"Export",onClick:()=>nt()}),z(m,{id:"entries-import",icon:"upload",title:"Import",onClick:()=>tt()}),z(m,{id:"entries-add",icon:"plus",title:"Add entry",onClick:()=>O(null)});const Te=e=>{N?.dirty()&&(e.preventDefault(),e.returnValue="")};if(window.addEventListener("beforeunload",Te),d(()=>window.removeEventListener("beforeunload",Te)),d(()=>{if(N?.dirty())try{S.set(P(a),{id:N.id,values:N.values(),at:new Date().toISOString()})}catch{}ke=!0,[...ue].forEach(e=>{try{e.close(!0)}catch{}})}),I.scan(h),!p.peek()){Y.value=!1;return}if(await Ge(),!r()||(await H(),!r()))return;let C=null;try{C=S.get(P(a))}catch{}if(C?.values){try{S.remove?S.remove(P(a)):S.set(P(a),null)}catch{}let e=C.id?V(C.id):null;if(C.id&&!e&&(e=await g.collections.getEntry(a,C.id).catch(()=>null)),!r()||C.id&&!e)return;const t=e?`your changes to \u201C${D(K(e))}\u201D`:"a new entry",n=e?.meta?.updatedAt&&C.at&&e.meta.updatedAt>C.at?" It has been changed since - check before saving.":"";await E.confirm(`You left ${t} unsaved.${n} Pick up where you left off?`)&&r()&&O(e,C.values)}}};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{apiRequest as ie}from"/admin/js/api.js";import{makeIconInput as
|
|
2
|
-
`).filter(x=>x.trim()).map(x=>{const[
|
|
3
|
-
`).filter(f=>f.trim()).map(f=>{const[k,...h]=f.split(":");return{value:k.trim(),label:h.join(":").trim()||k.trim()}});n.cascade={sourceField:b,mapping:y,defaultOptions:m},l=!0}}return l?n:void 0}function z(){C=C.map((e,t)=>Wt(t))}function Ut(e){const t=e.firstElementChild;if(!t||t.querySelector(".fb-drag-grip"))return;const o=document.createElement("span");o.className="fb-drag-grip",o.setAttribute("data-icon","grip-vertical"),o.setAttribute("data-icon-size","16"),o.title="Drag to reorder",t.insertBefore(o,t.firstChild),window.I?.scan?.(t)}function Q(e){const t=e.find("#fields-list").get(0),o=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),C.length===0){o&&(o.style.display=""),ce?.(t),Oe();return}o&&(o.style.display="none"),C.forEach((n,l)=>{const a=n.type==="page-break"?Jt(n,l,e):n.type==="spacer"?Yt(n,l,e):Kt(n,l,e);Ut(a),t.appendChild(a)}),ce?.(t),Oe()}}function Jt(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--dm-border);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const a=document.createElement("span");a.textContent="- Page Break -",a.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted);white-space:nowrap;flex-shrink:0;font-style:italic;";const i=document.createElement("span");i.textContent=e.label||"Untitled Step",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",q(c,"more-horizontal"),c.addEventListener("click",y=>{y.stopPropagation(),d.style.display=d.style.display==="none"?"":"none"}),r.appendChild(c);const s=document.createElement("button");s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove page break",q(s,"x"),s.addEventListener("click",async y=>{y.stopPropagation(),await E.confirm("Remove this page break?")&&(z(),C.splice(t,1),Q(o))}),r.appendChild(s),l.appendChild(a),l.appendChild(i),l.appendChild(r),l.addEventListener("click",()=>{d.style.display=d.style.display==="none"?"":"none"});const d=document.createElement("div");d.className="fb-field-body",d.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border);display:none;";const u=D([P("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),P("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),b=u.querySelector(`#fb-pb-label-${t}`);return b&&b.addEventListener("input",()=>{i.textContent=b.value||"Untitled Step"}),d.appendChild(u),n.appendChild(l),n.appendChild(d),n}function Yt(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--dm-border);border-radius:6px;margin-bottom:.5rem;background:transparent;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const a=document.createElement("div");a.style.cssText="flex:1;height:1px;background:var(--dm-border);";const i=document.createElement("span");i.textContent="Spacer",i.style.cssText="font-size:.7rem;color:var(--dm-text-muted);white-space:nowrap;padding:0 .4rem;font-style:italic;";const r=document.createElement("div");r.style.cssText="flex:1;height:1px;background:var(--dm-border);";const c=document.createElement("div");c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const s=document.createElement("button");return s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove spacer",q(s,"x"),s.addEventListener("click",async d=>{d.stopPropagation(),z(),C.splice(t,1),Q(o)}),c.appendChild(s),l.appendChild(a),l.appendChild(i),l.appendChild(r),l.appendChild(c),n.appendChild(l),n}function Kt(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--dm-border);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg);cursor:pointer;user-select:none;";const a=document.createElement("span");a.textContent=rt(e.type),a.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary);white-space:nowrap;flex-shrink:0;";const i=document.createElement("span");i.textContent=e.label||"(unlabelled)",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",q(c,"more-horizontal"),c.addEventListener("click",u=>{u.stopPropagation(),d.style.display=d.style.display==="none"?"":"none"}),r.appendChild(c);const s=document.createElement("button");if(s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove field",q(s,"x"),s.addEventListener("click",async u=>{u.stopPropagation(),await E.confirm("Remove this field?")&&(z(),C.splice(t,1),Q(o))}),r.appendChild(s),l.appendChild(a),l.appendChild(i),e.required){const u=document.createElement("span");u.textContent="required",u.style.cssText="font-size:.7rem;color:var(--dm-danger);flex-shrink:0;",l.appendChild(u)}if(dt(e.logic)){const u=document.createElement("span");q(u,"zap"),u.title="Has conditional logic",u.style.cssText="font-size:.75rem;color:var(--dm-primary);flex-shrink:0;",l.appendChild(u)}l.appendChild(r),l.addEventListener("click",()=>{d.style.display=d.style.display==="none"?"":"none"});const d=Qt(e,t,i);return d.style.display="none",n.appendChild(l),n.appendChild(d),n}function Qt(e,t,o){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border);";const l=D([P("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),P("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),a=D([bn("Type",`fb-type-${t}`,ot,e.type||"string"),Nt("Required",`fb-required-${t}`,e.required||!1)]),i=D([P("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),r=D([P("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),P("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,s=D([P("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),Nt("Full Width",`fb-fullwidth-${t}`,c==="full")]);s.classList.add("fb-grid-row"),s.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(l),n.appendChild(a),n.appendChild(i),n.appendChild(r),n.appendChild(s);const d=n.querySelector(`#fb-label-${t}`),u=n.querySelector(`#fb-name-${t}`);d&&d.addEventListener("input",()=>{o&&(o.textContent=d.value||"(unlabelled)"),u&&!u.dataset.manuallyEdited&&(u.value=st(d.value))}),u&&u.addEventListener("input",()=>{u.dataset.manuallyEdited="1"});const b=n.querySelector(`#fb-type-${t}`);b&&b.addEventListener("change",()=>{const m=n.closest(".fb-field-card");if(m){const h=m.querySelector("span");h&&(h.textContent=rt(b.value))}const f=n.querySelector(".fb-field-extras");f&&f.remove();const k=wt(b.value,e,t);k&&n.appendChild(k)});const y=wt(e.type,e,t);return y&&n.appendChild(y),n.appendChild(nn(e,t)),n.appendChild(sn(e,t)),n}const ye=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],Re=new Set(["is_empty","is_not_empty"]),pt={equals:"includes",not_equals:"does not include",contains:"any value contains",not_contains:"no value contains",starts_with:"any value starts with",ends_with:"any value ends with",greater_than:"any value is greater than",less_than:"any value is less than",is_empty:"nothing selected",is_not_empty:"something selected",in:"includes one of (comma sep)",not_in:"includes none of (comma sep)",matches_regex:"any value matches regex"};function Me(e){return!!e&&(e.type==="checkbox-group"||!!e.multiple)}function ve(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--dm-text-muted);margin:0 0 .7rem;text-transform:uppercase;letter-spacing:.06em;",t}function xe(e,t,o,n,l){const a=document.createElement("div");a.className="fb-logic-cond-row",a.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const i=document.createElement("span");i.textContent="When",i.style.cssText="font-size:.73rem;color:var(--dm-text-muted);flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-cond-field",r.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.field&&(f.selected=!0),r.appendChild(f)});const c=document.createElement("select");c.className="form-select fb-logic-cond-op",c.style.cssText="flex:1 1 160px;min-width:140px;",ye.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.operator&&(f.selected=!0),c.appendChild(f)});const s=()=>{const m=t.find(f=>f.value===r.value);Array.from(c.options).forEach(f=>{const k=ye.find(h=>h.value===f.value);f.textContent=m?.multi&&pt[f.value]||k?.label||f.value})};s(),r.addEventListener("change",s);const d=document.createElement("input");d.type="text",d.className="form-input fb-logic-cond-val",d.placeholder="value",d.style.cssText="flex:1 1 120px;min-width:100px;",d.value=e?.value||"",e&&Re.has(e.operator)&&(d.style.display="none"),c.addEventListener("change",()=>{d.style.display=Re.has(c.value)?"none":""});const u=document.createElement("span");q(u,"arrow-right"),u.style.cssText="font-size:.73rem;color:var(--dm-text-muted);flex-shrink:0;";const b=document.createElement("select");b.className=`form-select ${n}`,b.style.cssText="flex:1 1 140px;min-width:120px;",o.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===l&&(f.selected=!0),b.appendChild(f)});const y=document.createElement("button");return y.type="button",y.className="btn btn-xs btn-ghost",y.style.cssText="line-height:1;opacity:.6;",q(y,"x"),y.style.flexShrink="0",y.addEventListener("click",()=>a.remove()),a.appendChild(i),a.appendChild(r),a.appendChild(c),a.appendChild(d),a.appendChild(u),a.appendChild(b),a.appendChild(y),a}function Xt(e,t,o){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(ve("Visibility"));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const a=document.createElement("span");a.textContent="Default:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-vis-default",i.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(h=>{const x=document.createElement("option");x.value=h.value,x.textContent=h.label,h.value===(e.default||"visible")&&(x.selected=!0),i.appendChild(x)}),l.appendChild(a),l.appendChild(i),n.appendChild(l);const r=document.createElement("div");r.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-vis-transition",s.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(h=>{const x=document.createElement("option");x.value=h.value,x.textContent=h.label,h.value===(e.transition||"none")&&(x.selected=!0),s.appendChild(x)}),r.appendChild(c),r.appendChild(s),n.appendChild(r);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const u=document.createElement("span");u.textContent="Speed:",u.style.cssText="font-size:.8rem;flex-shrink:0;";const b=document.createElement("select");b.className="form-select fb-logic-vis-duration",b.style.cssText="max-width:240px;",[{value:"",label:"Default"},{value:"120",label:"Very fast (120ms)"},{value:"200",label:"Fast (200ms)"},{value:"350",label:"Medium (350ms)"},{value:"600",label:"Slow (600ms)"},{value:"1000",label:"Very slow (1s)"}].forEach(h=>{const x=document.createElement("option");x.value=h.value,x.textContent=h.label,h.value===String(e.duration??"")&&(x.selected=!0),b.appendChild(x)}),d.appendChild(u),d.appendChild(b),n.appendChild(d);const y=()=>{d.style.display=s.value==="none"?"none":""};y(),s.addEventListener("change",y);const m=document.createElement("div");m.className="fb-logic-vis-rules";const f=o.map(h=>({value:h.name,label:h.label||h.name,multi:Me(h)})),k=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(h=>{const x=(h.when?.all||h.when?.any||[])[0],$=h.then==="hidden"?"hidden":"visible";f.length>0&&m.appendChild(xe(x,f,k,"fb-logic-vis-then",$))}),n.appendChild(m),f.length>0){const h=document.createElement("button");h.type="button",h.className="btn btn-xs btn-ghost",h.style.cssText="font-size:.73rem;margin-top:.35rem;",re(h,"plus","Add visibility rule"),h.addEventListener("click",()=>m.appendChild(xe(null,f,k,"fb-logic-vis-then","visible"))),n.appendChild(h)}return n}function Zt(e,t,o){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(ve("Conditional Requirement"));const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.8rem;cursor:pointer;margin-bottom:.65rem;";const a=document.createElement("input");a.type="checkbox",a.className="fb-logic-req-default",a.checked=e.default===!0,l.appendChild(a),l.appendChild(document.createTextNode("Required by default")),n.appendChild(l);const i=document.createElement("div");i.className="fb-logic-req-rules";const r=o.map(s=>({value:s.name,label:s.label||s.name,multi:Me(s)})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(s=>{const d=(s.when?.all||s.when?.any||[])[0],u=s.then===!0?"true":"false";r.length>0&&i.appendChild(xe(d,r,c,"fb-logic-req-then",u))}),n.appendChild(i),r.length>0){const s=document.createElement("button");s.type="button",s.className="btn btn-xs btn-ghost",s.style.cssText="font-size:.73rem;margin-top:.35rem;",re(s,"plus","Add requirement rule"),s.addEventListener("click",()=>i.appendChild(xe(null,r,c,"fb-logic-req-then","true"))),n.appendChild(s)}return n}function ut(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const o=document.createElement("select");o.className="form-select fb-logic-val-type",o.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===(e?.type||"regex")&&(c.selected=!0),o.appendChild(c)});const n=document.createElement("input");n.type="text",n.className="form-input fb-logic-val-pattern",n.placeholder=e?.type==="match"?"field name":"pattern",n.value=e?.pattern||e?.field||"",n.style.cssText="flex:3;";const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-flags",l.placeholder="flags",l.value=e?.flags||"",l.style.cssText="flex:0 0 55px;",e?.type==="match"&&(l.style.display="none"),o.addEventListener("change",()=>{l.style.display=o.value==="match"?"none":"",n.placeholder=o.value==="match"?"field name":"pattern"});const a=document.createElement("input");a.type="text",a.className="form-input fb-logic-val-message",a.placeholder="Error message",a.value=e?.message||"",a.style.cssText="flex:4;";const i=document.createElement("button");return i.type="button",i.className="btn btn-xs btn-ghost",i.style.cssText="line-height:1;opacity:.6;",q(i,"x"),i.style.flexShrink="0",i.addEventListener("click",()=>t.remove()),t.appendChild(o),t.appendChild(n),t.appendChild(l),t.appendChild(a),t.appendChild(i),t}function en(e,t,o){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(ve("Custom Validation"));const l=document.createElement("div");l.className="fb-logic-val-rules",(e||[]).forEach(i=>l.appendChild(ut(i))),n.appendChild(l);const a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.35rem;",re(a,"plus","Add validation rule"),a.addEventListener("click",()=>l.appendChild(ut(null))),n.appendChild(a),n}function tn(e,t,o){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(ve("Cascade Options")),n.lastChild.appendChild(Ie("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const a=document.createElement("span");a.textContent="Source field:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cascade-source",i.style.cssText="flex:1;";const r=document.createElement("option");r.value="",r.textContent="- none -",i.appendChild(r),o.forEach(b=>{const y=document.createElement("option");y.value=b.name,y.textContent=b.label||b.name,b.name===e.sourceField&&(y.selected=!0),i.appendChild(y)}),l.appendChild(a),l.appendChild(i),n.appendChild(l);const c=document.createElement("p");c.textContent='Mapping JSON - {"value":[{"value":"...","label":"..."}]}',c.appendChild(Ie(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),c.style.cssText="font-size:.73rem;color:var(--dm-text-muted);margin:.3rem 0 .2rem;";const s=document.createElement("textarea");s.className="form-input fb-logic-cascade-mapping",s.rows=4,s.style.cssText="font-family:monospace;",s.placeholder='{"uk": [{"value": "london", "label": "London"}]}',s.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(s);const d=document.createElement("p");d.textContent="Default options (one per line: value:Label)",d.appendChild(Ie("Shown before the source field has a value, or when its value has no mapping entry.")),d.style.cssText="font-size:.73rem;color:var(--dm-text-muted);margin:.3rem 0 .2rem;";const u=document.createElement("textarea");return u.className="form-input fb-logic-cascade-defaults",u.rows=3,u.style.cssText="font-family:monospace;",u.placeholder=`option1:Option 1
|
|
4
|
-
option2:Option 2`,u.value=(e.defaultOptions||[]).map(b=>{const y=typeof b=="string"?b:b.value??"",
|
|
5
|
-
`),n.appendChild(d),n.appendChild(u),n}function nn(e,t){const o=e.logic||{},n=C.filter((u,b)=>b!==t&&u.type!=="page-break"&&u.type!=="spacer"),l=document.createElement("div");l.className="fb-field-logic",l.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border);padding-top:.5rem;";const a=document.createElement("div");a.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const i=document.createElement("span");i.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted);",i.textContent="\u26A1 Conditional Logic";const r=dt(o),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",q(c,r?"chevron-down":"chevron-right",12);const s=document.createElement("div");s.className="fb-logic-body",s.style.cssText="padding:.25rem 0 .25rem;"+(r?"":"display:none;"),a.addEventListener("click",()=>{const u=s.style.display==="none";s.style.display=u?"":"none",q(c,u?"chevron-down":"chevron-right",12)}),a.appendChild(i),a.appendChild(c),l.appendChild(a),s.appendChild(Xt(o.visibility||{},t,n)),s.appendChild(Zt(o.requirement||{},t,n)),s.appendChild(en(o.validation||[],t,n));const d=document.getElementById(`fb-type-${t}`)?.value||e.type;return Fe.has(d)&&s.appendChild(tn(o.cascade||{},t,n)),l.appendChild(s),l}const de=[{value:"banner",label:"Show a banner",kind:"state",params:["message","level","position","dismissible"],placeholder:"e.g. You must be 18 or over to continue."},{value:"toast",label:"Show a toast",kind:"event",params:["message","level"],placeholder:"e.g. Thanks - carry on."},{value:"hide-fields",label:"Hide fields",kind:"state",params:["target"]},{value:"disable-fields",label:"Disable fields",kind:"state",params:["target"]},{value:"require-fields",label:"Make fields required",kind:"state",params:["target"]},{value:"block-submit",label:"Block submission",kind:"state",params:["message"],messageLabel:"Reason, shown under the button",placeholder:'Blank: "This form cannot be submitted yet."'},{value:"end-form",label:"End the form here",kind:"state",params:["message","finish","finishLabel","doneMessage","record"],messageLabel:"Message shown in place of the rest",placeholder:"e.g. Sorry - you must be 18 or over to apply."},{value:"goto-step",label:"Jump to a wizard step",kind:"event",params:["step"]},{value:"redirect",label:"Redirect after submit",kind:"event",params:["url"]},{value:"celebrate",label:"Celebrate",kind:"event",params:["theme","intensity"]},{value:"run-action",label:"Run a CMS action",kind:"event",params:["slug"]},{value:"notify",label:"Notify the admins",kind:"event",params:["message","level"],messageLabel:"Notification title",placeholder:"Blank: titled after the form",noOnce:!0,kindText:"when the form is submitted"}],Ce=[{value:"info",label:"Info"},{value:"success",label:"Success"},{value:"warning",label:"Warning"},{value:"error",label:"Error"}],mt=[{value:"field-below",label:"Below this field"},{value:"field-above",label:"Above this field"},{value:"form-top",label:"Top of the form"}],ft=[{value:"rest",label:"The rest of the form"},{value:"all",label:"Every other field"},{value:"listed",label:"Chosen fields"}],ht=[{value:"auto",label:"Whatever is in season"},{value:"christmas",label:"Christmas"},{value:"halloween",label:"Halloween"},{value:"valentines",label:"Valentine's Day"},{value:"guy-fawkes",label:"Guy Fawkes Night"},{value:"st-patricks",label:"St Patrick's Day"},{value:"st-andrews",label:"St Andrew's Day"},{value:"st-davids",label:"St David's Day"},{value:"st-georges",label:"St George's Day"}];function bt(e,t=!1){const o=(i,r)=>({value:i.name,label:`${i.label||i.name}${r?" (this field)":""}`,multi:Me(i)}),n=i=>i&&i.type!=="page-break"&&i.type!=="spacer"&&i.name,l=C.filter((i,r)=>r!==e&&n(i)).map(i=>o(i,!1)),a=C[e];return t&&n(a)?[o(a,!0),...l]:l}function J(e,t,o){const n=document.createElement("select");return n.className=`form-select ${e}`,t.forEach(l=>{const a=document.createElement("option");a.value=l.value,a.textContent=l.label,l.value===o&&(a.selected=!0),n.appendChild(a)}),n}function le(e,t,o){const n=document.createElement("input");return n.type="text",n.className=`form-input ${e}`,n.value=t==null?"":String(t),o&&(n.placeholder=o),n}function ze(e,t){const o=document.createElement("button");return o.type="button",o.className="btn btn-xs btn-ghost fb-trig-remove",t&&(o.title=t,o.setAttribute("aria-label",t)),q(o,"x"),o.addEventListener("click",n=>{n.stopPropagation(),e()}),o}function V(e,t,{full:o=!1,hint:n=""}={}){const l=document.createElement("label");l.className=`fb-trig-f${o?" fb-trig-f--full":""}`;const a=document.createElement("span");if(a.className="fb-trig-f-label",a.textContent=e,l.appendChild(a),l.appendChild(t),n){const i=document.createElement("span");i.className="fb-trig-f-hint",i.textContent=n,l.appendChild(i)}return l}function pe(e,t,o,n){const l=document.createElement("label");l.className="fb-trig-check",n&&(l.title=n);const a=document.createElement("input");return a.type="checkbox",a.className=`form-check ${e}`,a.checked=!!t,l.appendChild(a),l.appendChild(document.createTextNode(o)),l}function gt(e,t){const o=document.createElement("div");o.className="fb-trig-cond-row";const n=J("fb-trig-cond-field",t,e?.field),l=J("fb-trig-cond-op",ye,e?.operator),a=le("fb-trig-cond-val",e?.value,"value"),i=()=>{const c=t.find(s=>s.value===n.value);Array.from(l.options).forEach(s=>{const d=ye.find(u=>u.value===s.value);s.textContent=c?.multi&&pt[s.value]||d?.label||s.value})},r=()=>{a.hidden=Re.has(l.value)};return i(),r(),n.addEventListener("change",i),l.addEventListener("change",r),o.appendChild(n),o.appendChild(l),o.appendChild(a),o.appendChild(ze(()=>o.remove(),"Remove condition")),o}function yt(e,t){const o=document.createElement("div");o.className="fb-trig-params";const n=de.find(c=>c.value===(e.type||"banner"));if(!n)return o;const l=document.createElement("div");l.className="fb-trig-grid";const a=document.createElement("div");a.className="fb-trig-opts";const i=[];for(const c of n.params)switch(c){case"message":l.appendChild(V(n.messageLabel||"Message",le("fb-trig-p-message",e.message,n.placeholder||""),{full:!0}));break;case"finish":a.appendChild(pe("fb-trig-p-finish-on",e.finish!==!1,"Let the visitor finish (send the answers given so far)",'Untick to stop the form dead: the submit button is disabled and nothing is sent, so nothing is saved and "Notify the admins" cannot fire.'));break;case"finishLabel":{const s=V("Button text",le("fb-trig-p-finish",e.finishLabel,"Finish"));i.push(s),l.appendChild(s);break}case"doneMessage":{const s=V("Closing message",le("fb-trig-p-done",e.doneMessage,"Blank: the form's own success message"));i.push(s),l.appendChild(s);break}case"record":{const s=pe("fb-trig-p-record",e.record!==!1,"Save the answers given so far",`Untick to keep nothing: no submission is stored and the form's email, webhook and actions do not run. A "Notify the admins" action still fires.`);i.push(s),a.appendChild(s);break}case"level":l.appendChild(V(n.value==="notify"?"Severity":"Level",J("fb-trig-p-level",Ce,e.level||"info")));break;case"position":l.appendChild(V("Where",J("fb-trig-p-position",mt,e.position||"field-below")));break;case"dismissible":a.appendChild(pe("fb-trig-p-dismissible",e.dismissible!==!1,"The visitor can dismiss it"));break;case"target":{const s=J("fb-trig-p-target",ft,e.target||(e.fields?.length?"listed":"rest")),d=document.createElement("select");d.className="form-select fb-trig-p-fields",d.multiple=!0,d.size=4;const u=new Set(e.fields||[]);bt(t).forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,u.has(m.value)&&(f.selected=!0),d.appendChild(f)});const b=V("Fields",d,{hint:"Ctrl/Cmd-click to pick several"}),y=()=>{b.hidden=s.value!=="listed"};y(),s.addEventListener("change",y),l.appendChild(V("Which fields",s)),l.appendChild(b);break}case"step":{const s=le("fb-trig-p-step",e.step,"0");s.type="number",s.min="0",l.appendChild(V("Step",s,{hint:"Counting from 0"}));break}case"url":l.appendChild(V("Send them to",le("fb-trig-p-url",e.url,"/thank-you"),{full:!0}));break;case"theme":l.appendChild(V("Theme",J("fb-trig-p-theme",ht,e.theme||"auto")));break;case"intensity":l.appendChild(V("Intensity",J("fb-trig-p-intensity",[{value:"low",label:"Low"},{value:"medium",label:"Medium"},{value:"high",label:"High"}],e.intensity||"medium")));break;case"slug":{const s=[{value:"",label:fe.length?"Choose an action":"No actions available"}].concat(fe.map(d=>({value:d.slug,label:d.title||d.slug})));l.appendChild(V("Action",J("fb-trig-p-slug",s,e.slug||""),{full:!0}));break}default:break}n.kind==="event"&&!n.noOnce&&a.appendChild(pe("fb-trig-p-once",!!e.once,"Only once","Fire the first time only, rather than every time the answer swings back."));const r=a.querySelector(".fb-trig-p-finish-on");if(r){const c=()=>i.forEach(s=>{s.hidden=!r.checked});c(),r.addEventListener("change",c)}return l.children.length&&o.appendChild(l),a.children.length&&o.appendChild(a),o}function ln(e){return e?.kindText?e.kindText:e?.kind==="event"?"once, when it becomes true":"while it is true"}const an=(e,t=48)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,ae=(e,t)=>e.find(o=>o.value===t)?.label||t;function on(e){if(!e)return"";const t=[],o=l=>l?`"${an(l)}"`:"",n=()=>e.target==="listed"?e.fields?.length?e.fields.join(", "):"no fields chosen":ae(ft,e.target||"rest").toLowerCase();switch(e.type){case"banner":t.push(o(e.message)||"No message yet",ae(Ce,e.level||"info"),ae(mt,e.position||"field-below").toLowerCase());break;case"toast":t.push(o(e.message)||"No message yet",ae(Ce,e.level||"info"));break;case"hide-fields":case"disable-fields":case"require-fields":t.push(n());break;case"block-submit":t.push(o(e.message)||"the standard reason");break;case"end-form":t.push(o(e.message)||"no message"),e.finish===!1?t.push("submit disabled"):t.push(`button "${e.finishLabel||"Finish"}"`,e.record===!1?"saves nothing":"saves the answers");break;case"goto-step":t.push(Number.isFinite(e.step)?`step ${e.step}`:"no step yet");break;case"redirect":t.push(e.url||"no address yet");break;case"celebrate":t.push(ae(ht,e.theme||"auto"),`${e.intensity||"medium"} intensity`);break;case"run-action":t.push(e.slug?fe.find(l=>l.slug===e.slug)?.title||e.slug:"no action chosen");break;case"notify":t.push(o(e.message)||"titled after the form",ae(Ce,e.level||"info"));break;default:break}return e.once&&t.push("only once"),t.filter(Boolean).join(" \xB7 ")}function De(e,t,{open:o=!1}={}){const n=document.createElement("div");n.className="fb-trig-action-row";const l=document.createElement("div");l.className="fb-trig-action-head",l.tabIndex=0,l.setAttribute("role","button");const a=document.createElement("span");a.className="fb-trig-chevron";const i=document.createElement("span");i.className="fb-trig-action-main";const r=document.createElement("strong"),c=document.createElement("small");c.className="fb-trig-action-summary",i.append(r,c);const s=document.createElement("span");s.className="fb-trig-kind",l.append(a,i,s,ze(()=>n.remove(),"Remove action"));const d=document.createElement("div");d.className="fb-trig-action-body";const u=J("fb-trig-action-type",de,e.type||"banner");d.appendChild(V("Action",u,{full:!0})),d.appendChild(yt(e,t));const b=()=>{const m=de.find(f=>f.value===u.value);r.textContent=m?.label||u.value,s.textContent=ln(m),s.classList.toggle("fb-trig-kind--event",m?.kind==="event"),c.textContent=on(Et(n))},y=m=>{d.hidden=!m,n.classList.toggle("is-open",m),l.setAttribute("aria-expanded",String(m)),q(a,m?"chevron-down":"chevron-right",12)};return l.addEventListener("click",()=>y(d.hidden)),l.addEventListener("keydown",m=>{m.target!==l||m.key!=="Enter"&&m.key!==" "||(m.preventDefault(),y(d.hidden))}),u.addEventListener("change",()=>{const m=yt({type:u.value},t);d.replaceChild(m,d.querySelector(".fb-trig-params")),b(),window.I?.scan?.()}),d.addEventListener("input",b),d.addEventListener("change",b),n.append(l,d),y(o),b(),n}function vt(e,t,o){const n=document.createElement("section");n.className=`fb-trig-sec fb-trig-sec--${e}`;const l=document.createElement("div");l.className="fb-trig-sec-head";const a=document.createElement("span");if(a.className="fb-trig-sec-title",a.textContent=t,l.appendChild(a),o){const i=document.createElement("span");i.className="fb-trig-sec-hint",i.textContent=o,l.appendChild(i)}return n.appendChild(l),n}function Pe(e,t){const o=document.createElement("button");return o.type="button",o.className="btn btn-xs btn-ghost fb-trig-add",re(o,"plus",e,12),o.addEventListener("click",t),o}function xt(e,t,o,n){const a=vt(e,t,e==="then"?"What happens while the answers match.":"What happens while they do not. Leave empty for nothing.");a.classList.add("fb-trig-branch"),a.dataset.branch=e;const i=document.createElement("div");return i.className="fb-trig-action-list",(o||[]).forEach(r=>i.appendChild(De(r,n))),a.appendChild(i),a.appendChild(Pe("Add action",()=>{i.appendChild(De({type:"banner"},n,{open:!0})),window.I?.scan?.()})),a}function Ct(e,t,o){const n=bt(t,!0),l=document.createElement("div");l.className="fb-trig-card",l.dataset.triggerId=e.id||`trg-${Date.now()}-${Math.random().toString(36).slice(2,7)}`;const a=document.createElement("div");a.className="fb-trig-card-head";const i=document.createElement("span");i.className="fb-trig-card-title",i.textContent=`Trigger ${o+1}`,a.appendChild(i);const r=pe("fb-trig-enabled",e.enabled!==!1,"Enabled","Untick to keep the trigger but switch it off.");if(a.appendChild(r),a.appendChild(ze(()=>l.remove(),"Remove trigger")),l.appendChild(a),!n.length){const u=document.createElement("p");return u.className="fb-trig-empty",u.textContent="Give this field a name first - a trigger tests an answer, and it needs something to test.",l.appendChild(u),l}const c=vt("when","When");c.querySelector(".fb-trig-sec-head").appendChild(J("fb-trig-match",[{value:"all",label:"all of these are true"},{value:"any",label:"any of these is true"}],Array.isArray(e.when?.any)?"any":"all"));const s=document.createElement("div");s.className="fb-trig-cond-list";const d=e.when?.all||e.when?.any||[];return(d.length?d:[{}]).forEach(u=>s.appendChild(gt(u,n))),c.appendChild(s),c.appendChild(Pe("Add condition",()=>{s.appendChild(gt({},n)),window.I?.scan?.()})),l.appendChild(c),l.appendChild(xt("then","Then",e.then,t)),l.appendChild(xt("otherwise","Otherwise",e.otherwise,t)),l}function sn(e,t){const o=Array.isArray(e.triggers)?e.triggers:[],n=document.createElement("div");n.className="fb-field-triggers";const l=document.createElement("div");l.className="fb-trig-panel-head";const a=document.createElement("span");a.className="fb-trig-panel-title",a.textContent=`\u{1F3AF} Triggers${o.length?` (${o.length})`:""}`;const i=document.createElement("button");i.type="button",i.className="btn btn-xs btn-ghost",q(i,o.length?"chevron-down":"chevron-right",12);const r=document.createElement("div");r.className="fb-triggers-body",r.hidden=!o.length,l.addEventListener("click",()=>{r.hidden=!r.hidden,q(i,r.hidden?"chevron-right":"chevron-down",12)});const c=document.createElement("p");c.className="fb-trig-blurb",c.textContent="Do something to the form when the answers meet a condition - show a banner or toast, hide, disable or require other fields, refuse the submit, end the form early, notify the admins, jump to a step, or celebrate.",r.appendChild(c);const s=document.createElement("div");return s.className="fb-trig-list",o.forEach((d,u)=>s.appendChild(Ct(d,t,u))),r.appendChild(s),r.appendChild(Pe("Add trigger",()=>{const d=Ct({enabled:!0,then:[]},t,s.children.length);s.appendChild(d),d.querySelector('.fb-trig-branch[data-branch="then"] .fb-trig-action-list')?.appendChild(De({type:"banner"},t,{open:!0})),window.I?.scan?.()})),l.appendChild(a),l.appendChild(i),n.appendChild(l),n.appendChild(r),n}function Et(e){const t=e.querySelector(".fb-trig-action-type")?.value,o=de.find(a=>a.value===t);if(!o)return null;const n={type:t},l=a=>e.querySelector(a)?.value?.trim()||"";for(const a of o.params)switch(a){case"message":n.message=l(".fb-trig-p-message");break;case"finishLabel":{const i=l(".fb-trig-p-finish");i&&(n.finishLabel=i);break}case"doneMessage":{const i=l(".fb-trig-p-done");i&&(n.doneMessage=i);break}case"finish":e.querySelector(".fb-trig-p-finish-on")?.checked||(n.finish=!1);break;case"record":e.querySelector(".fb-trig-p-record")?.checked||(n.record=!1);break;case"level":n.level=l(".fb-trig-p-level")||"info";break;case"position":n.position=l(".fb-trig-p-position")||"field-below";break;case"dismissible":n.dismissible=!!e.querySelector(".fb-trig-p-dismissible")?.checked;break;case"target":{const i=l(".fb-trig-p-target")||"rest";n.target=i,i==="listed"&&(n.fields=Array.from(e.querySelectorAll(".fb-trig-p-fields option")).filter(r=>r.selected).map(r=>r.value));break}case"step":{const i=parseInt(l(".fb-trig-p-step"),10);Number.isFinite(i)&&(n.step=i);break}case"url":n.url=l(".fb-trig-p-url");break;case"theme":n.theme=l(".fb-trig-p-theme")||"auto";break;case"intensity":n.intensity=l(".fb-trig-p-intensity")||"medium";break;case"slug":n.slug=l(".fb-trig-p-slug");break;default:break}return o.kind==="event"&&!o.noOnce&&e.querySelector(".fb-trig-p-once")?.checked&&(n.once=!0),n}function rn(e){const t=Et(e);if(!t)return null;const o=de.find(a=>a.value===t.type),n=t.type,l=["block-submit","end-form","notify"].includes(o.value);return o.params.includes("message")&&!t.message&&!l?(delete t.message,null):(l&&!t.message&&delete t.message,n==="redirect"&&!t.url||n==="run-action"&&!t.slug?null:t)}function cn(e){const o=document.querySelector(`.fb-field-card[data-index="${e}"]`)?.querySelector(".fb-field-triggers");if(!o)return;const n=[];return o.querySelectorAll(".fb-trig-card").forEach(l=>{const a=Array.from(l.querySelectorAll(".fb-trig-cond-row")).map(u=>{const b=u.querySelector(".fb-trig-cond-field")?.value,y=u.querySelector(".fb-trig-cond-op")?.value;if(!b||!y)return null;const m=u.querySelector(".fb-trig-cond-val")?.value??"";return{field:b,operator:y,value:m}}).filter(Boolean);if(!a.length)return;const i=u=>Array.from(l.querySelectorAll(`.fb-trig-branch[data-branch="${u}"] .fb-trig-action-row`)).map(rn).filter(Boolean),r=i("then"),c=i("otherwise");if(!r.length&&!c.length)return;const s=l.querySelector(".fb-trig-match")?.value==="any"?"any":"all",d={id:l.dataset.triggerId,enabled:!!l.querySelector(".fb-trig-enabled")?.checked,when:{[s]:a}};r.length&&(d.then=r),c.length&&(d.otherwise=c),n.push(d)}),n.length?n:void 0}function wt(e,t,o){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(dn(t,o)),n.appendChild(un(t.options||[],o))):(Fe.has(e)&&n.appendChild(gn(t.options||[],o)),e==="textarea"&&n.appendChild(D([P("Rows",`fb-rows-${o}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(D([P("Min Length",`fb-minlength-${o}`,"number",t.minLength||"",""),P("Max Length",`fb-maxlength-${o}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(D([P("Min",`fb-min-${o}`,"number",t.min??"",""),P("Max",`fb-max-${o}`,"number",t.max??"","")]))),n.children.length?n:null}function kt(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border); border-radius:6px; margin-top:.5rem; overflow:hidden;";const o=document.createElement("div");o.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border);",o.textContent=e,t.appendChild(o);const n=document.createElement("div");return n.className="fb-chooser-section-body",n.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",t.appendChild(n),{wrap:t,body:n}}function Ee(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem";const r=document.createElement("select");if(r.id=e,r.className="form-input",n.forEach(([c,s])=>{const d=document.createElement("option");d.value=c,d.textContent=s,String(c)===String(o)&&(d.selected=!0),r.appendChild(d)}),a.appendChild(i),a.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.72rem;margin-top:.2rem;",a.appendChild(c)}return a}function St(e,t,o,n){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const i=document.createElement("input");if(i.id=e,i.type="checkbox",i.checked=!!o,a.appendChild(i),a.appendChild(document.createTextNode(" "+t)),l.appendChild(a),n){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=n,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function _e(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem",a.appendChild(i);const r=document.createElement("div");r.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const c=document.createElement("input");c.id=e,c.type="text",c.className="form-input",c.value=o||"",n&&(c.placeholder=n),c.style.flex="1";const s=document.createElement("input");s.type="color",s.title="Pick a colour",s.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border);border-radius:4px;cursor:pointer;";const d=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(s.value=d?c.value:"#000000",s.addEventListener("input",()=>{c.value=s.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),r.appendChild(c),r.appendChild(s),a.appendChild(r),l){const u=document.createElement("p");u.className="form-hint text-muted",u.textContent=l,u.style.cssText="font-size:.72rem;margin-top:.2rem;",a.appendChild(u)}return a}function dn(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=kt("Layout");n.body.appendChild(D([Ee(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card - rich tiles"],["chip","Chip - compact pills"]],"Card supports description and grid columns; chips wrap."),St(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(D([Ee(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),P("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1-6). Ignored for chips.")])),o.appendChild(n.wrap);const l=kt("Visual");return l.body.appendChild(D([_e(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour - semantic name or any CSS colour."),Ee(`fb-chooser-accent-style-${t}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),l.body.appendChild(D([St(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),_e(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),l.body.appendChild(D([Ee(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),_e(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),o.appendChild(l.wrap),o}function pn(e,t,o,n){const l=document.createElement("div");l.className="fb-chooser-option-row",l.dataset.optIdx=String(o),l.style.cssText="border:1px solid var(--dm-border); border-radius:4px; padding:.5rem; background:var(--dm-background-alt); display:flex; flex-direction:column; gap:.35rem;";const a=document.createElement("div");a.style.cssText="display:flex; gap:.4rem; align-items:center;";const i=document.createElement("div");i.style.cssText="display:flex; flex-direction:column; gap:1px;";const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move up",q(r,"arrow-up"),r.addEventListener("click",I=>{I.preventDefault(),Tt(t,o,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.title="Move down",q(c,"arrow-down"),c.addEventListener("click",I=>{I.preventDefault(),Tt(t,o,1,n)}),i.appendChild(r),i.appendChild(c),a.appendChild(i);const s=document.createElement("input");s.type="text",s.className="form-input fb-chooser-opt-value",s.placeholder="value",s.value=e.value||"",s.style.flex="1",a.appendChild(s);const d=document.createElement("input");d.type="text",d.className="form-input fb-chooser-opt-label",d.placeholder="label (visible to users)",d.value=e.label||"",d.style.flex="2",a.appendChild(d);const u=document.createElement("button");u.type="button",u.className="btn btn-xs btn-ghost",u.style.cssText="line-height:1;opacity:.6;",u.title="Remove option",q(u,"x"),u.addEventListener("click",I=>{I.preventDefault(),hn(t,o,n)}),a.appendChild(u),l.appendChild(a);const b=document.createElement("div");b.style.cssText="display:flex; gap:.4rem;";const y=Mt("icon name (optional)",e.icon||"");y.input.classList.add("fb-chooser-opt-icon"),y.el.style.flex="1",b.appendChild(y.el);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-description",m.placeholder="description (card + comfortable only)",m.value=e.description||"",m.style.flex="2",b.appendChild(m),l.appendChild(b);const f=document.createElement("div");f.style.cssText="display:flex; gap:.4rem;";const k=document.createElement("input");k.type="text",k.className="form-input fb-chooser-opt-tooltip",k.placeholder="tooltip (hover hint)",k.value=e.tooltip||"",k.style.flex="2",f.appendChild(k);const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-badge-text",h.placeholder="badge text",h.value=e.badge?.text||"",h.style.flex="1",f.appendChild(h);const x=document.createElement("select");x.className="form-input fb-chooser-opt-badge-type",x.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([I,X])=>{const H=document.createElement("option");H.value=I,H.textContent=X,(e.badge?.type||"")===I&&(H.selected=!0),x.appendChild(H)}),f.appendChild(x),l.appendChild(f);const $=document.createElement("div");$.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const A=document.createElement("label");A.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const G=document.createElement("input");G.type="checkbox",G.className="fb-chooser-opt-recommended",G.checked=!!e.recommended,A.appendChild(G),A.appendChild(document.createTextNode(" Recommended")),$.appendChild(A);const _=document.createElement("label");_.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const j=document.createElement("input");return j.type="checkbox",j.className="fb-chooser-opt-disabled",j.checked=!!e.disabled,_.appendChild(j),_.appendChild(document.createTextNode(" Disabled")),$.appendChild(_),l.appendChild($),l}function un(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.5rem;";const n=document.createElement("div");n.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const l=document.createElement("p");l.textContent="Options",l.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(l);const a=document.createElement("button");a.type="button",a.className="btn btn-xs btn-ghost",re(a,"plus","Add option"),n.appendChild(a),o.appendChild(n);const i=document.createElement("div");i.className="fb-chooser-options-list",i.id=`fb-chooser-options-${t}`,i.style.cssText="display:flex; flex-direction:column; gap:.4rem;",o.appendChild(i);const r=()=>{i.replaceChildren();const c=mn(t);(c.length?c:e||[]).forEach((d,u)=>{i.appendChild(pn(d,t,u,r))})};return a.addEventListener("click",c=>{c.preventDefault(),fn(t,r)}),r(),o}function mn(e){const t=C[e];return!t||!Array.isArray(t.options)?[]:t.options}function je(e){return C[e]?(Array.isArray(C[e].options)||(C[e].options=[]),C[e].options):[]}function fn(e,t){we(e),je(e).push({value:"new",label:"New option"}),t()}function hn(e,t,o){we(e),je(e).splice(t,1),o()}function Tt(e,t,o,n){we(e);const l=je(e),a=t+o;if(a<0||a>=l.length)return;const[i]=l.splice(t,1);l.splice(a,0,i),n()}function we(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const o=t.querySelectorAll(".fb-chooser-option-row"),n=[];o.forEach(l=>{const a={value:l.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:l.querySelector(".fb-chooser-opt-label")?.value.trim()||""},i=l.querySelector(".fb-chooser-opt-icon")?.value.trim();i&&(a.icon=i);const r=l.querySelector(".fb-chooser-opt-description")?.value.trim();r&&(a.description=r);const c=l.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(a.tooltip=c);const s=l.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),d=l.querySelector(".fb-chooser-opt-badge-type")?.value;(s||d)&&(a.badge={},s&&(a.badge.text=s),d&&(a.badge.type=d)),l.querySelector(".fb-chooser-opt-recommended")?.checked&&(a.recommended=!0),l.querySelector(".fb-chooser-opt-disabled")?.checked&&(a.disabled=!0),n.push(a)}),C[e]&&(C[e].options=n)}function D(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(o=>{o&&t.appendChild(o)}),t}function P(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=t,i.className="form-label",i.textContent=e,i.style.fontSize=".8rem";const r=document.createElement("input");if(r.id=t,r.type=o||"text",r.className="form-input",r.value=n??"",a.appendChild(i),a.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.73rem;margin-top:.35rem;",a.appendChild(c)}return a}function bn(e,t,o,n){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=t,a.className="form-label",a.textContent=e,a.style.fontSize=".8rem";const i=document.createElement("select");return i.id=t,i.className="form-select",o.forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===n&&(c.selected=!0),i.appendChild(c)}),l.appendChild(a),l.appendChild(i),l}function Nt(e,t,o){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const a=document.createElement("input");return a.id=t,a.type="checkbox",a.checked=o,l.appendChild(a),l.appendChild(document.createTextNode(e)),n.appendChild(l),n}function gn(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.4rem;";const n=document.createElement("p");n.textContent="Options (one per line: value or value:Label)",n.style.cssText="font-size:.8rem;font-weight:600;margin-bottom:.3rem;";const l=document.createElement("textarea");return l.id=`fb-options-${t}`,l.className="form-input",l.rows=4,l.placeholder=`yes:Yes
|
|
1
|
+
import{apiRequest as ie}from"/admin/js/api.js";import{makeIconInput as et}from"/admin/js/lib/shortcode-modal.js";import{enhancePickerFields as tt}from"/public/js/forms.js";import{buildWizardSteps as jt}from"/public/js/form-steps.mjs";import{addBannerAction as qe}from"../lib/plugin-chrome.js";import{helpPopovers as Vt}from"../lib/help-popover.js";import{openMenuAt as nt,sidebarCount as Gt}from"../lib/tool-kit.js";import{draftKey as se,draftOf as Ht,duplicateField as Wt,fieldTitle as $e,fingerprint as lt,leavingEditor as at,moveField as ot,restoreDraft as Ut,snapshotOf as Ae}from"../lib/form-editor-arrange.js";import{fmtDateTime as Jt}from"../lib/dates.js";const it=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"chooser",label:"Chooser (visual options)"},{value:"icon",label:"Icon (icon picker)"},{value:"image",label:"Image (media library)"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"password",label:"Password"},{value:"file",label:"File upload"},{value:"hidden",label:"Hidden field"}],Fe=new Set(["select","radio","checkbox-group","chooser"]);let w=[],fe=[],q=null,he=null,ne=null,be={};function A(e,t,o=14){e.textContent="";const n=document.createElement("span");return n.setAttribute("data-icon",t),n.setAttribute("data-icon-size",String(o)),n.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",e.appendChild(n),window.I?.scan?.(e),e}function re(e,t,o,n=14){return A(e,t,n),e.appendChild(document.createTextNode(" "+o)),e}function Ie(e){const t=document.createElement("span");return t.setAttribute("data-icon","help-circle"),t.setAttribute("data-icon-size","12"),t.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;margin-left:.3rem;opacity:.55;cursor:help;",t.title=e,window.I?.scan?.(t),requestAnimationFrame(()=>{try{t.parentNode&&typeof E<"u"&&E.tooltip&&(t.removeAttribute("title"),E.tooltip(t,{content:e,position:"top"}))}catch{}}),t}let ge=null,st=null,Be=null,ce=null;function Oe(){Be?.(),clearTimeout(st),st=setTimeout(()=>{ge&&ge()},350)}function rt(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function ct(e){return it.find(t=>t.value===e)?.label||e}function dt(e){return(e||[]).some(t=>t.logic||Array.isArray(t.triggers)&&t.triggers.length)}function pt(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function Kt(e){const t={...w[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const m=document.getElementById(`fb-pb-label-${e}`),x=document.getElementById(`fb-pb-desc-${e}`);return m&&(t.label=m.value.trim()||t.label),x&&(t.description=x.value.trim()),t}const o=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),l=document.getElementById(`fb-type-${e}`),a=document.getElementById(`fb-required-${e}`),i=document.getElementById(`fb-placeholder-${e}`),r=document.getElementById(`fb-helper-${e}`),c=document.getElementById(`fb-tooltip-${e}`);if(o&&(t.label=o.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),l&&(t.type=l.value||t.type),a&&(t.required=a.checked),i&&(t.placeholder=i.value.trim()),r&&(t.helper=r.value.trim()),c&&(t.tooltip=c.value.trim()),t.type==="chooser"){const m=document.getElementById(`fb-chooser-variant-${e}`)?.value;m&&(t.variant=m);const x=document.getElementById(`fb-chooser-multiple-${e}`);x&&(t.multiple=x.checked);const N=document.getElementById(`fb-chooser-density-${e}`)?.value;N&&(t.density=N);const T=document.getElementById(`fb-chooser-columns-${e}`)?.value;T!==""&&T!==void 0&&(t.columns=parseInt(T,10)||3);const I=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();I?t.accent=I:delete t.accent;const B=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;B?t.accentStyle=B:delete t.accentStyle;const G=document.getElementById(`fb-chooser-glow-${e}`);G&&(t.glow=G.checked);const O=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();O?t.glowColour=O:delete t.glowColour;const X=document.getElementById(`fb-chooser-shadow-${e}`)?.value;X&&X!=="none"?t.shadow=X:delete t.shadow;const W=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();W?t.shadowColour=W:delete t.shadowColour,we(e),w[e]&&Array.isArray(w[e].options)&&(t.options=w[e].options)}else if(Fe.has(t.type)){["variant","density","columns","accent","accentStyle","glow","glowColour","shadow","shadowColour"].forEach(x=>delete t[x]),t.type!=="checkbox-group"&&t.type!=="multiselect"&&delete t.multiple;const m=document.getElementById(`fb-options-${e}`);m&&(t.options=m.value.split(`
|
|
2
|
+
`).filter(x=>x.trim()).map(x=>{const[N,...T]=x.split(":");return{value:N.trim(),label:T.join(":").trim()||N.trim()}}))}for(const m of["prefix","suffix"]){const x=document.getElementById(`fb-${m}-icon-${e}`),N=document.getElementById(`fb-${m}-text-${e}`),T={...t.formConfig||{}};if(x||N){const I=x?.value.trim()||"",B=N?.value.trim()||"";I&&/^[a-z0-9-]+$/i.test(I)?T[m]={icon:I}:B?T[m]={text:B.slice(0,24)}:delete T[m]}else Lt.includes(t.type)||delete T[m];t.formConfig=Object.keys(T).length?T:void 0}if(t.type==="textarea"){const m=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);m>0&&(t.formConfig={...t.formConfig||{},rows:m})}const s=document.getElementById(`fb-span-${e}`),d=document.getElementById(`fb-fullwidth-${e}`);if(s||d){const m={...t.formConfig||{}};if(d?.checked)m.span="full";else{const x=parseInt(s?.value,10);x>1?m.span=x:delete m.span}t.formConfig=Object.keys(m).length?m:void 0}const u=document.getElementById(`fb-minlength-${e}`)?.value;u&&(t.minLength=parseInt(u,10));const b=document.getElementById(`fb-maxlength-${e}`)?.value;b&&(t.maxLength=parseInt(b,10));const y=document.getElementById(`fb-min-${e}`)?.value;y!==""&&y!==void 0&&(t.min=parseFloat(y));const f=document.getElementById(`fb-max-${e}`)?.value;f!==""&&f!==void 0&&(t.max=parseFloat(f));const h=Yt(e);h?t.logic=h:delete t.logic;const k=bn(e);return k?t.triggers=k:delete t.triggers,t}function Yt(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const o=t.querySelector(".fb-field-logic");if(!o)return;const n={};let l=!1;const a=o.querySelector('[data-logic-section="visibility"]');if(a){const s=a.querySelector(".fb-logic-vis-default"),d=a.querySelector(".fb-logic-vis-transition"),u=Array.from(a.querySelectorAll(".fb-logic-cond-row")).map(h=>{const k=h.querySelector(".fb-logic-cond-field"),m=h.querySelector(".fb-logic-cond-op"),x=h.querySelector(".fb-logic-cond-val"),N=h.querySelector(".fb-logic-vis-then");return k?.value?{when:{all:[{field:k.value,operator:m.value,value:x.value}]},then:N.value}:null}).filter(Boolean),b=s?.value||"visible",y=d?.value||"none",f=parseInt(a.querySelector(".fb-logic-vis-duration")?.value||"",10);(b!=="visible"||u.length>0||y!=="none")&&(n.visibility={default:b,conditions:u},y!=="none"&&(n.visibility.transition=y),y!=="none"&&Number.isFinite(f)&&(n.visibility.duration=f),l=!0)}const i=o.querySelector('[data-logic-section="requirement"]');if(i){const s=i.querySelector(".fb-logic-req-default"),d=Array.from(i.querySelectorAll(".fb-logic-cond-row")).map(u=>{const b=u.querySelector(".fb-logic-cond-field"),y=u.querySelector(".fb-logic-cond-op"),f=u.querySelector(".fb-logic-cond-val"),h=u.querySelector(".fb-logic-req-then");return b?.value?{when:{all:[{field:b.value,operator:y.value,value:f.value}]},then:h.value==="true"}:null}).filter(Boolean);d.length>0&&(n.requirement={default:s?.checked===!0,conditions:d},l=!0)}const r=o.querySelector('[data-logic-section="validation"]');if(r){const s=Array.from(r.querySelectorAll(".fb-logic-val-rule")).map(d=>{const u=d.querySelector(".fb-logic-val-type"),b=d.querySelector(".fb-logic-val-pattern"),y=d.querySelector(".fb-logic-val-flags"),f=d.querySelector(".fb-logic-val-message");if(!b?.value.trim())return null;const h=u?.value||"regex",k={type:h,message:f?.value.trim()||"Invalid value."};return h==="regex"?(k.pattern=b.value.trim(),y?.value.trim()&&(k.flags=y.value.trim())):k.field=b.value.trim(),k}).filter(Boolean);s.length>0&&(n.validation=s,l=!0)}const c=o.querySelector('[data-logic-section="cascade"]');if(c){const s=c.querySelector(".fb-logic-cascade-source"),d=c.querySelector(".fb-logic-cascade-mapping"),u=c.querySelector(".fb-logic-cascade-defaults"),b=s?.value?.trim();if(b){let y={};try{y=JSON.parse(d?.value||"{}")}catch{}const f=(u?.value||"").split(`
|
|
3
|
+
`).filter(h=>h.trim()).map(h=>{const[k,...m]=h.split(":");return{value:k.trim(),label:m.join(":").trim()||k.trim()}});n.cascade={sourceField:b,mapping:y,defaultOptions:f},l=!0}}return l?n:void 0}function D(){w=w.map((e,t)=>Kt(t))}function Qt(e){const t=e.firstElementChild;if(!t||t.querySelector(".fb-drag-grip"))return;const o=document.createElement("span");o.className="fb-drag-grip",o.setAttribute("data-icon","grip-vertical"),o.setAttribute("data-icon-size","16"),o.title="Drag to reorder",t.insertBefore(o,t.firstChild),window.I?.scan?.(t)}function Q(e){const t=e.find("#fields-list").get(0),o=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),w.length===0){o&&(o.style.display=""),ce?.(t),Oe();return}o&&(o.style.display="none"),w.forEach((n,l)=>{const a=n.type==="page-break"?Xt(n,l,e):n.type==="spacer"?Zt(n,l,e):en(n,l,e);Qt(a),t.appendChild(a)}),ce?.(t),Oe()}}function Xt(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--dm-border);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const a=document.createElement("span");a.textContent="- Page Break -",a.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted);white-space:nowrap;flex-shrink:0;font-style:italic;";const i=document.createElement("span");i.textContent=e.label||"Untitled Step",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",A(c,"more-horizontal"),c.addEventListener("click",y=>{y.stopPropagation(),d.style.display=d.style.display==="none"?"":"none"}),r.appendChild(c);const s=document.createElement("button");s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove page break",A(s,"x"),s.addEventListener("click",async y=>{y.stopPropagation(),await E.confirm("Remove this page break?")&&(D(),w.splice(t,1),Q(o))}),r.appendChild(s),l.appendChild(a),l.appendChild(i),l.appendChild(r),l.addEventListener("click",()=>{d.style.display=d.style.display==="none"?"":"none"});const d=document.createElement("div");d.className="fb-field-body",d.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border);display:none;";const u=_([V("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),V("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),b=u.querySelector(`#fb-pb-label-${t}`);return b&&b.addEventListener("input",()=>{i.textContent=b.value||"Untitled Step"}),d.appendChild(u),n.appendChild(l),n.appendChild(d),n}function Zt(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--dm-border);border-radius:6px;margin-bottom:.5rem;background:transparent;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const a=document.createElement("div");a.style.cssText="flex:1;height:1px;background:var(--dm-border);";const i=document.createElement("span");i.textContent="Spacer",i.style.cssText="font-size:.7rem;color:var(--dm-text-muted);white-space:nowrap;padding:0 .4rem;font-style:italic;";const r=document.createElement("div");r.style.cssText="flex:1;height:1px;background:var(--dm-border);";const c=document.createElement("div");c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const s=document.createElement("button");return s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove spacer",A(s,"x"),s.addEventListener("click",async d=>{d.stopPropagation(),D(),w.splice(t,1),Q(o)}),c.appendChild(s),l.appendChild(a),l.appendChild(i),l.appendChild(r),l.appendChild(c),n.appendChild(l),n}function en(e,t,o){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--dm-border);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg);cursor:pointer;user-select:none;";const a=document.createElement("span");a.textContent=ct(e.type),a.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary);white-space:nowrap;flex-shrink:0;";const i=document.createElement("span");i.textContent=e.label||"(unlabelled)",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",A(c,"more-horizontal"),c.addEventListener("click",u=>{u.stopPropagation(),d.style.display=d.style.display==="none"?"":"none"}),r.appendChild(c);const s=document.createElement("button");if(s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",s.title="Remove field",A(s,"x"),s.addEventListener("click",async u=>{u.stopPropagation(),await E.confirm("Remove this field?")&&(D(),w.splice(t,1),Q(o))}),r.appendChild(s),l.appendChild(a),l.appendChild(i),e.required){const u=document.createElement("span");u.textContent="required",u.style.cssText="font-size:.7rem;color:var(--dm-danger);flex-shrink:0;",l.appendChild(u)}if(pt(e.logic)){const u=document.createElement("span");A(u,"zap"),u.title="Has conditional logic",u.style.cssText="font-size:.75rem;color:var(--dm-primary);flex-shrink:0;",l.appendChild(u)}l.appendChild(r),l.addEventListener("click",()=>{d.style.display=d.style.display==="none"?"":"none"});const d=tn(e,t,i);return d.style.display="none",n.appendChild(l),n.appendChild(d),n}function tn(e,t,o){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border);";const l=_([V("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),V("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),a=_([Sn("Type",`fb-type-${t}`,it,e.type||"string"),Ft("Required",`fb-required-${t}`,e.required||!1)]),i=_([V("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),r=_([V("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),V("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,s=_([V("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),Ft("Full Width",`fb-fullwidth-${t}`,c==="full")]);s.classList.add("fb-grid-row"),s.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(l),n.appendChild(a),n.appendChild(i),n.appendChild(r),n.appendChild(s);const d=n.querySelector(`#fb-label-${t}`),u=n.querySelector(`#fb-name-${t}`);d&&d.addEventListener("input",()=>{o&&(o.textContent=d.value||"(unlabelled)"),u&&!u.dataset.manuallyEdited&&(u.value=rt(d.value))}),u&&u.addEventListener("input",()=>{u.dataset.manuallyEdited="1"});const b=n.querySelector(`#fb-type-${t}`);b&&b.addEventListener("change",()=>{const f=n.closest(".fb-field-card");if(f){const m=f.querySelector("span");m&&(m.textContent=ct(b.value))}const h=n.querySelector(".fb-field-extras");h&&h.remove();const k=Nt(b.value,e,t);k&&n.appendChild(k)});const y=Nt(e.type,e,t);return y&&n.appendChild(y),n.appendChild(sn(e,t)),n.appendChild(fn(e,t)),n}const ye=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],Re=new Set(["is_empty","is_not_empty"]),ut={equals:"includes",not_equals:"does not include",contains:"any value contains",not_contains:"no value contains",starts_with:"any value starts with",ends_with:"any value ends with",greater_than:"any value is greater than",less_than:"any value is less than",is_empty:"nothing selected",is_not_empty:"something selected",in:"includes one of (comma sep)",not_in:"includes none of (comma sep)",matches_regex:"any value matches regex"};function Me(e){return!!e&&(e.type==="checkbox-group"||!!e.multiple)}function ve(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--dm-text-muted);margin:0 0 .7rem;text-transform:uppercase;letter-spacing:.06em;",t}function xe(e,t,o,n,l){const a=document.createElement("div");a.className="fb-logic-cond-row",a.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const i=document.createElement("span");i.textContent="When",i.style.cssText="font-size:.73rem;color:var(--dm-text-muted);flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-cond-field",r.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(f=>{const h=document.createElement("option");h.value=f.value,h.textContent=f.label,e&&f.value===e.field&&(h.selected=!0),r.appendChild(h)});const c=document.createElement("select");c.className="form-select fb-logic-cond-op",c.style.cssText="flex:1 1 160px;min-width:140px;",ye.forEach(f=>{const h=document.createElement("option");h.value=f.value,h.textContent=f.label,e&&f.value===e.operator&&(h.selected=!0),c.appendChild(h)});const s=()=>{const f=t.find(h=>h.value===r.value);Array.from(c.options).forEach(h=>{const k=ye.find(m=>m.value===h.value);h.textContent=f?.multi&&ut[h.value]||k?.label||h.value})};s(),r.addEventListener("change",s);const d=document.createElement("input");d.type="text",d.className="form-input fb-logic-cond-val",d.placeholder="value",d.style.cssText="flex:1 1 120px;min-width:100px;",d.value=e?.value||"",e&&Re.has(e.operator)&&(d.style.display="none"),c.addEventListener("change",()=>{d.style.display=Re.has(c.value)?"none":""});const u=document.createElement("span");A(u,"arrow-right"),u.style.cssText="font-size:.73rem;color:var(--dm-text-muted);flex-shrink:0;";const b=document.createElement("select");b.className=`form-select ${n}`,b.style.cssText="flex:1 1 140px;min-width:120px;",o.forEach(f=>{const h=document.createElement("option");h.value=f.value,h.textContent=f.label,f.value===l&&(h.selected=!0),b.appendChild(h)});const y=document.createElement("button");return y.type="button",y.className="btn btn-xs btn-ghost",y.style.cssText="line-height:1;opacity:.6;",A(y,"x"),y.style.flexShrink="0",y.addEventListener("click",()=>a.remove()),a.appendChild(i),a.appendChild(r),a.appendChild(c),a.appendChild(d),a.appendChild(u),a.appendChild(b),a.appendChild(y),a}function nn(e,t,o){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(ve("Visibility"));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const a=document.createElement("span");a.textContent="Default:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-vis-default",i.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(m=>{const x=document.createElement("option");x.value=m.value,x.textContent=m.label,m.value===(e.default||"visible")&&(x.selected=!0),i.appendChild(x)}),l.appendChild(a),l.appendChild(i),n.appendChild(l);const r=document.createElement("div");r.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-vis-transition",s.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(m=>{const x=document.createElement("option");x.value=m.value,x.textContent=m.label,m.value===(e.transition||"none")&&(x.selected=!0),s.appendChild(x)}),r.appendChild(c),r.appendChild(s),n.appendChild(r);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const u=document.createElement("span");u.textContent="Speed:",u.style.cssText="font-size:.8rem;flex-shrink:0;";const b=document.createElement("select");b.className="form-select fb-logic-vis-duration",b.style.cssText="max-width:240px;",[{value:"",label:"Default"},{value:"120",label:"Very fast (120ms)"},{value:"200",label:"Fast (200ms)"},{value:"350",label:"Medium (350ms)"},{value:"600",label:"Slow (600ms)"},{value:"1000",label:"Very slow (1s)"}].forEach(m=>{const x=document.createElement("option");x.value=m.value,x.textContent=m.label,m.value===String(e.duration??"")&&(x.selected=!0),b.appendChild(x)}),d.appendChild(u),d.appendChild(b),n.appendChild(d);const y=()=>{d.style.display=s.value==="none"?"none":""};y(),s.addEventListener("change",y);const f=document.createElement("div");f.className="fb-logic-vis-rules";const h=o.map(m=>({value:m.name,label:m.label||m.name,multi:Me(m)})),k=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(m=>{const x=(m.when?.all||m.when?.any||[])[0],N=m.then==="hidden"?"hidden":"visible";h.length>0&&f.appendChild(xe(x,h,k,"fb-logic-vis-then",N))}),n.appendChild(f),h.length>0){const m=document.createElement("button");m.type="button",m.className="btn btn-xs btn-ghost",m.style.cssText="font-size:.73rem;margin-top:.35rem;",re(m,"plus","Add visibility rule"),m.addEventListener("click",()=>f.appendChild(xe(null,h,k,"fb-logic-vis-then","visible"))),n.appendChild(m)}return n}function ln(e,t,o){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(ve("Conditional Requirement"));const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.8rem;cursor:pointer;margin-bottom:.65rem;";const a=document.createElement("input");a.type="checkbox",a.className="fb-logic-req-default",a.checked=e.default===!0,l.appendChild(a),l.appendChild(document.createTextNode("Required by default")),n.appendChild(l);const i=document.createElement("div");i.className="fb-logic-req-rules";const r=o.map(s=>({value:s.name,label:s.label||s.name,multi:Me(s)})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(s=>{const d=(s.when?.all||s.when?.any||[])[0],u=s.then===!0?"true":"false";r.length>0&&i.appendChild(xe(d,r,c,"fb-logic-req-then",u))}),n.appendChild(i),r.length>0){const s=document.createElement("button");s.type="button",s.className="btn btn-xs btn-ghost",s.style.cssText="font-size:.73rem;margin-top:.35rem;",re(s,"plus","Add requirement rule"),s.addEventListener("click",()=>i.appendChild(xe(null,r,c,"fb-logic-req-then","true"))),n.appendChild(s)}return n}function mt(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const o=document.createElement("select");o.className="form-select fb-logic-val-type",o.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===(e?.type||"regex")&&(c.selected=!0),o.appendChild(c)});const n=document.createElement("input");n.type="text",n.className="form-input fb-logic-val-pattern",n.placeholder=e?.type==="match"?"field name":"pattern",n.value=e?.pattern||e?.field||"",n.style.cssText="flex:3;";const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-flags",l.placeholder="flags",l.value=e?.flags||"",l.style.cssText="flex:0 0 55px;",e?.type==="match"&&(l.style.display="none"),o.addEventListener("change",()=>{l.style.display=o.value==="match"?"none":"",n.placeholder=o.value==="match"?"field name":"pattern"});const a=document.createElement("input");a.type="text",a.className="form-input fb-logic-val-message",a.placeholder="Error message",a.value=e?.message||"",a.style.cssText="flex:4;";const i=document.createElement("button");return i.type="button",i.className="btn btn-xs btn-ghost",i.style.cssText="line-height:1;opacity:.6;",A(i,"x"),i.style.flexShrink="0",i.addEventListener("click",()=>t.remove()),t.appendChild(o),t.appendChild(n),t.appendChild(l),t.appendChild(a),t.appendChild(i),t}function an(e,t,o){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(ve("Custom Validation"));const l=document.createElement("div");l.className="fb-logic-val-rules",(e||[]).forEach(i=>l.appendChild(mt(i))),n.appendChild(l);const a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.35rem;",re(a,"plus","Add validation rule"),a.addEventListener("click",()=>l.appendChild(mt(null))),n.appendChild(a),n}function on(e,t,o){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(ve("Cascade Options")),n.lastChild.appendChild(Ie("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const a=document.createElement("span");a.textContent="Source field:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cascade-source",i.style.cssText="flex:1;";const r=document.createElement("option");r.value="",r.textContent="- none -",i.appendChild(r),o.forEach(b=>{const y=document.createElement("option");y.value=b.name,y.textContent=b.label||b.name,b.name===e.sourceField&&(y.selected=!0),i.appendChild(y)}),l.appendChild(a),l.appendChild(i),n.appendChild(l);const c=document.createElement("p");c.textContent='Mapping JSON - {"value":[{"value":"...","label":"..."}]}',c.appendChild(Ie(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),c.style.cssText="font-size:.73rem;color:var(--dm-text-muted);margin:.3rem 0 .2rem;";const s=document.createElement("textarea");s.className="form-input fb-logic-cascade-mapping",s.rows=4,s.style.cssText="font-family:monospace;",s.placeholder='{"uk": [{"value": "london", "label": "London"}]}',s.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(s);const d=document.createElement("p");d.textContent="Default options (one per line: value:Label)",d.appendChild(Ie("Shown before the source field has a value, or when its value has no mapping entry.")),d.style.cssText="font-size:.73rem;color:var(--dm-text-muted);margin:.3rem 0 .2rem;";const u=document.createElement("textarea");return u.className="form-input fb-logic-cascade-defaults",u.rows=3,u.style.cssText="font-family:monospace;",u.placeholder=`option1:Option 1
|
|
4
|
+
option2:Option 2`,u.value=(e.defaultOptions||[]).map(b=>{const y=typeof b=="string"?b:b.value??"",f=typeof b=="string"?b:b.label??y;return y===f?y:`${y}:${f}`}).join(`
|
|
5
|
+
`),n.appendChild(d),n.appendChild(u),n}function sn(e,t){const o=e.logic||{},n=w.filter((u,b)=>b!==t&&u.type!=="page-break"&&u.type!=="spacer"),l=document.createElement("div");l.className="fb-field-logic",l.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border);padding-top:.5rem;";const a=document.createElement("div");a.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const i=document.createElement("span");i.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted);",i.textContent="\u26A1 Conditional Logic";const r=pt(o),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",A(c,r?"chevron-down":"chevron-right",12);const s=document.createElement("div");s.className="fb-logic-body",s.style.cssText="padding:.25rem 0 .25rem;"+(r?"":"display:none;"),a.addEventListener("click",()=>{const u=s.style.display==="none";s.style.display=u?"":"none",A(c,u?"chevron-down":"chevron-right",12)}),a.appendChild(i),a.appendChild(c),l.appendChild(a),s.appendChild(nn(o.visibility||{},t,n)),s.appendChild(ln(o.requirement||{},t,n)),s.appendChild(an(o.validation||[],t,n));const d=document.getElementById(`fb-type-${t}`)?.value||e.type;return Fe.has(d)&&s.appendChild(on(o.cascade||{},t,n)),l.appendChild(s),l}const de=[{value:"banner",label:"Show a banner",kind:"state",params:["message","level","position","dismissible"],placeholder:"e.g. You must be 18 or over to continue."},{value:"toast",label:"Show a toast",kind:"event",params:["message","level"],placeholder:"e.g. Thanks - carry on."},{value:"hide-fields",label:"Hide fields",kind:"state",params:["target"]},{value:"disable-fields",label:"Disable fields",kind:"state",params:["target"]},{value:"require-fields",label:"Make fields required",kind:"state",params:["target"]},{value:"block-submit",label:"Block submission",kind:"state",params:["message"],messageLabel:"Reason, shown under the button",placeholder:'Blank: "This form cannot be submitted yet."'},{value:"end-form",label:"End the form here",kind:"state",params:["message","finish","finishLabel","doneMessage","record"],messageLabel:"Message shown in place of the rest",placeholder:"e.g. Sorry - you must be 18 or over to apply."},{value:"goto-step",label:"Jump to a wizard step",kind:"event",params:["step"]},{value:"redirect",label:"Redirect after submit",kind:"event",params:["url"]},{value:"celebrate",label:"Celebrate",kind:"event",params:["theme","intensity"]},{value:"run-action",label:"Run a CMS action",kind:"event",params:["slug"]},{value:"notify",label:"Notify the admins",kind:"event",params:["message","level"],messageLabel:"Notification title",placeholder:"Blank: titled after the form",noOnce:!0,kindText:"when the form is submitted"}],Ce=[{value:"info",label:"Info"},{value:"success",label:"Success"},{value:"warning",label:"Warning"},{value:"error",label:"Error"}],ft=[{value:"field-below",label:"Below this field"},{value:"field-above",label:"Above this field"},{value:"form-top",label:"Top of the form"}],ht=[{value:"rest",label:"The rest of the form"},{value:"all",label:"Every other field"},{value:"listed",label:"Chosen fields"}],bt=[{value:"auto",label:"Whatever is in season"},{value:"christmas",label:"Christmas"},{value:"halloween",label:"Halloween"},{value:"valentines",label:"Valentine's Day"},{value:"guy-fawkes",label:"Guy Fawkes Night"},{value:"st-patricks",label:"St Patrick's Day"},{value:"st-andrews",label:"St Andrew's Day"},{value:"st-davids",label:"St David's Day"},{value:"st-georges",label:"St George's Day"}];function gt(e,t=!1){const o=(i,r)=>({value:i.name,label:`${i.label||i.name}${r?" (this field)":""}`,multi:Me(i)}),n=i=>i&&i.type!=="page-break"&&i.type!=="spacer"&&i.name,l=w.filter((i,r)=>r!==e&&n(i)).map(i=>o(i,!1)),a=w[e];return t&&n(a)?[o(a,!0),...l]:l}function J(e,t,o){const n=document.createElement("select");return n.className=`form-select ${e}`,t.forEach(l=>{const a=document.createElement("option");a.value=l.value,a.textContent=l.label,l.value===o&&(a.selected=!0),n.appendChild(a)}),n}function le(e,t,o){const n=document.createElement("input");return n.type="text",n.className=`form-input ${e}`,n.value=t==null?"":String(t),o&&(n.placeholder=o),n}function ze(e,t){const o=document.createElement("button");return o.type="button",o.className="btn btn-xs btn-ghost fb-trig-remove",t&&(o.title=t,o.setAttribute("aria-label",t)),A(o,"x"),o.addEventListener("click",n=>{n.stopPropagation(),e()}),o}function j(e,t,{full:o=!1,hint:n=""}={}){const l=document.createElement("label");l.className=`fb-trig-f${o?" fb-trig-f--full":""}`;const a=document.createElement("span");if(a.className="fb-trig-f-label",a.textContent=e,l.appendChild(a),l.appendChild(t),n){const i=document.createElement("span");i.className="fb-trig-f-hint",i.textContent=n,l.appendChild(i)}return l}function pe(e,t,o,n){const l=document.createElement("label");l.className="fb-trig-check",n&&(l.title=n);const a=document.createElement("input");return a.type="checkbox",a.className=`form-check ${e}`,a.checked=!!t,l.appendChild(a),l.appendChild(document.createTextNode(o)),l}function yt(e,t){const o=document.createElement("div");o.className="fb-trig-cond-row";const n=J("fb-trig-cond-field",t,e?.field),l=J("fb-trig-cond-op",ye,e?.operator),a=le("fb-trig-cond-val",e?.value,"value"),i=()=>{const c=t.find(s=>s.value===n.value);Array.from(l.options).forEach(s=>{const d=ye.find(u=>u.value===s.value);s.textContent=c?.multi&&ut[s.value]||d?.label||s.value})},r=()=>{a.hidden=Re.has(l.value)};return i(),r(),n.addEventListener("change",i),l.addEventListener("change",r),o.appendChild(n),o.appendChild(l),o.appendChild(a),o.appendChild(ze(()=>o.remove(),"Remove condition")),o}const vt=["banner","toast","block-submit","end-form"],xt=["password","file","image","signature"],rn="Put an answer in with {{field_name}} - e.g. Hello {{name}}. Pick one from Insert field.";function cn(){try{D()}catch{}return w.filter(e=>e&&e.name&&e.type!=="page-break"&&e.type!=="spacer"&&!xt.includes(e.type))}function Ct(e,t,o){const n=le(e,t,o),l=document.createElement("span");l.className="fb-token-row";const a=document.createElement("select");a.className="form-input fb-token-pick",a.title="Insert an answer into the message",a.setAttribute("aria-label","Insert a field's answer into the message");const i=()=>{a.replaceChildren(new Option("Insert field",""));for(const r of cn())a.appendChild(new Option(`${r.label||r.name} ({{${r.name}}})`,r.name))};return i(),a.addEventListener("focus",i),a.addEventListener("mousedown",i),a.addEventListener("change",()=>{const r=a.value;if(a.value="",!r)return;const c=`{{${r}}}`,s=n.selectionStart??n.value.length,d=n.selectionEnd??s;n.value=n.value.slice(0,s)+c+n.value.slice(d),n.focus(),n.setSelectionRange(s+c.length,s+c.length),n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}),l.append(n,a),l}function dn(e){const t=window.FormLogicEngine;if(!t?.triggerTokens)return[];const o=new Map((e||[]).filter(l=>l&&l.name).map(l=>[l.name,l])),n=new Set;for(const l of e||[])for(const a of l?.triggers||[])for(const i of[...a.then||[],...a.otherwise||[]])if(vt.includes(i?.type))for(const r of[...t.triggerTokens(i.message),...t.triggerTokens(i.doneMessage)]){const c=o.get(r);(!c||xt.includes(c.type))&&n.add(r)}return[...n]}function Et(e,t){const o=document.createElement("div");o.className="fb-trig-params";const n=de.find(c=>c.value===(e.type||"banner"));if(!n)return o;const l=document.createElement("div");l.className="fb-trig-grid";const a=document.createElement("div");a.className="fb-trig-opts";const i=[];for(const c of n.params)switch(c){case"message":l.appendChild(vt.includes(n.value)?j(n.messageLabel||"Message",Ct("fb-trig-p-message",e.message,n.placeholder||""),{full:!0,hint:rn}):j(n.messageLabel||"Message",le("fb-trig-p-message",e.message,n.placeholder||""),{full:!0}));break;case"finish":a.appendChild(pe("fb-trig-p-finish-on",e.finish!==!1,"Let the visitor finish (send the answers given so far)",'Untick to stop the form dead: the submit button is disabled and nothing is sent, so nothing is saved and "Notify the admins" cannot fire.'));break;case"finishLabel":{const s=j("Button text",le("fb-trig-p-finish",e.finishLabel,"Finish"));i.push(s),l.appendChild(s);break}case"doneMessage":{const s=j("Closing message",Ct("fb-trig-p-done",e.doneMessage,"Blank: the form's own success message"));i.push(s),l.appendChild(s);break}case"record":{const s=pe("fb-trig-p-record",e.record!==!1,"Save the answers given so far",`Untick to keep nothing: no submission is stored and the form's email, webhook and actions do not run. A "Notify the admins" action still fires.`);i.push(s),a.appendChild(s);break}case"level":l.appendChild(j(n.value==="notify"?"Severity":"Level",J("fb-trig-p-level",Ce,e.level||"info")));break;case"position":l.appendChild(j("Where",J("fb-trig-p-position",ft,e.position||"field-below")));break;case"dismissible":a.appendChild(pe("fb-trig-p-dismissible",e.dismissible!==!1,"The visitor can dismiss it"));break;case"target":{const s=J("fb-trig-p-target",ht,e.target||(e.fields?.length?"listed":"rest")),d=document.createElement("select");d.className="form-select fb-trig-p-fields",d.multiple=!0,d.size=4;const u=new Set(e.fields||[]);gt(t).forEach(f=>{const h=document.createElement("option");h.value=f.value,h.textContent=f.label,u.has(f.value)&&(h.selected=!0),d.appendChild(h)});const b=j("Fields",d,{hint:"Ctrl/Cmd-click to pick several"}),y=()=>{b.hidden=s.value!=="listed"};y(),s.addEventListener("change",y),l.appendChild(j("Which fields",s)),l.appendChild(b);break}case"step":{const s=le("fb-trig-p-step",e.step,"0");s.type="number",s.min="0",l.appendChild(j("Step",s,{hint:"Counting from 0"}));break}case"url":l.appendChild(j("Send them to",le("fb-trig-p-url",e.url,"/thank-you"),{full:!0}));break;case"theme":l.appendChild(j("Theme",J("fb-trig-p-theme",bt,e.theme||"auto")));break;case"intensity":l.appendChild(j("Intensity",J("fb-trig-p-intensity",[{value:"low",label:"Low"},{value:"medium",label:"Medium"},{value:"high",label:"High"}],e.intensity||"medium")));break;case"slug":{const s=[{value:"",label:fe.length?"Choose an action":"No actions available"}].concat(fe.map(d=>({value:d.slug,label:d.title||d.slug})));l.appendChild(j("Action",J("fb-trig-p-slug",s,e.slug||""),{full:!0}));break}default:break}n.kind==="event"&&!n.noOnce&&a.appendChild(pe("fb-trig-p-once",!!e.once,"Only once","Fire the first time only, rather than every time the answer swings back."));const r=a.querySelector(".fb-trig-p-finish-on");if(r){const c=()=>i.forEach(s=>{s.hidden=!r.checked});c(),r.addEventListener("change",c)}return l.children.length&&o.appendChild(l),a.children.length&&o.appendChild(a),o}function pn(e){return e?.kindText?e.kindText:e?.kind==="event"?"once, when it becomes true":"while it is true"}const un=(e,t=48)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,ae=(e,t)=>e.find(o=>o.value===t)?.label||t;function mn(e){if(!e)return"";const t=[],o=l=>l?`"${un(l)}"`:"",n=()=>e.target==="listed"?e.fields?.length?e.fields.join(", "):"no fields chosen":ae(ht,e.target||"rest").toLowerCase();switch(e.type){case"banner":t.push(o(e.message)||"No message yet",ae(Ce,e.level||"info"),ae(ft,e.position||"field-below").toLowerCase());break;case"toast":t.push(o(e.message)||"No message yet",ae(Ce,e.level||"info"));break;case"hide-fields":case"disable-fields":case"require-fields":t.push(n());break;case"block-submit":t.push(o(e.message)||"the standard reason");break;case"end-form":t.push(o(e.message)||"no message"),e.finish===!1?t.push("submit disabled"):t.push(`button "${e.finishLabel||"Finish"}"`,e.record===!1?"saves nothing":"saves the answers");break;case"goto-step":t.push(Number.isFinite(e.step)?`step ${e.step}`:"no step yet");break;case"redirect":t.push(e.url||"no address yet");break;case"celebrate":t.push(ae(bt,e.theme||"auto"),`${e.intensity||"medium"} intensity`);break;case"run-action":t.push(e.slug?fe.find(l=>l.slug===e.slug)?.title||e.slug:"no action chosen");break;case"notify":t.push(o(e.message)||"titled after the form",ae(Ce,e.level||"info"));break;default:break}return e.once&&t.push("only once"),t.filter(Boolean).join(" \xB7 ")}function De(e,t,{open:o=!1}={}){const n=document.createElement("div");n.className="fb-trig-action-row";const l=document.createElement("div");l.className="fb-trig-action-head",l.tabIndex=0,l.setAttribute("role","button");const a=document.createElement("span");a.className="fb-trig-chevron";const i=document.createElement("span");i.className="fb-trig-action-main";const r=document.createElement("strong"),c=document.createElement("small");c.className="fb-trig-action-summary",i.append(r,c);const s=document.createElement("span");s.className="fb-trig-kind",l.append(a,i,s,ze(()=>n.remove(),"Remove action"));const d=document.createElement("div");d.className="fb-trig-action-body";const u=J("fb-trig-action-type",de,e.type||"banner");d.appendChild(j("Action",u,{full:!0})),d.appendChild(Et(e,t));const b=()=>{const f=de.find(h=>h.value===u.value);r.textContent=f?.label||u.value,s.textContent=pn(f),s.classList.toggle("fb-trig-kind--event",f?.kind==="event"),c.textContent=mn(Tt(n))},y=f=>{d.hidden=!f,n.classList.toggle("is-open",f),l.setAttribute("aria-expanded",String(f)),A(a,f?"chevron-down":"chevron-right",12)};return l.addEventListener("click",()=>y(d.hidden)),l.addEventListener("keydown",f=>{f.target!==l||f.key!=="Enter"&&f.key!==" "||(f.preventDefault(),y(d.hidden))}),u.addEventListener("change",()=>{const f=Et({type:u.value},t);d.replaceChild(f,d.querySelector(".fb-trig-params")),b(),window.I?.scan?.()}),d.addEventListener("input",b),d.addEventListener("change",b),n.append(l,d),y(o),b(),n}function wt(e,t,o){const n=document.createElement("section");n.className=`fb-trig-sec fb-trig-sec--${e}`;const l=document.createElement("div");l.className="fb-trig-sec-head";const a=document.createElement("span");if(a.className="fb-trig-sec-title",a.textContent=t,l.appendChild(a),o){const i=document.createElement("span");i.className="fb-trig-sec-hint",i.textContent=o,l.appendChild(i)}return n.appendChild(l),n}function _e(e,t){const o=document.createElement("button");return o.type="button",o.className="btn btn-xs btn-ghost fb-trig-add",re(o,"plus",e,12),o.addEventListener("click",t),o}function kt(e,t,o,n){const a=wt(e,t,e==="then"?"What happens while the answers match.":"What happens while they do not. Leave empty for nothing.");a.classList.add("fb-trig-branch"),a.dataset.branch=e;const i=document.createElement("div");return i.className="fb-trig-action-list",(o||[]).forEach(r=>i.appendChild(De(r,n))),a.appendChild(i),a.appendChild(_e("Add action",()=>{i.appendChild(De({type:"banner"},n,{open:!0})),window.I?.scan?.()})),a}function St(e,t,o){const n=gt(t,!0),l=document.createElement("div");l.className="fb-trig-card",l.dataset.triggerId=e.id||`trg-${Date.now()}-${Math.random().toString(36).slice(2,7)}`;const a=document.createElement("div");a.className="fb-trig-card-head";const i=document.createElement("span");i.className="fb-trig-card-title",i.textContent=`Trigger ${o+1}`,a.appendChild(i);const r=pe("fb-trig-enabled",e.enabled!==!1,"Enabled","Untick to keep the trigger but switch it off.");if(a.appendChild(r),a.appendChild(ze(()=>l.remove(),"Remove trigger")),l.appendChild(a),!n.length){const u=document.createElement("p");return u.className="fb-trig-empty",u.textContent="Give this field a name first - a trigger tests an answer, and it needs something to test.",l.appendChild(u),l}const c=wt("when","When");c.querySelector(".fb-trig-sec-head").appendChild(J("fb-trig-match",[{value:"all",label:"all of these are true"},{value:"any",label:"any of these is true"}],Array.isArray(e.when?.any)?"any":"all"));const s=document.createElement("div");s.className="fb-trig-cond-list";const d=e.when?.all||e.when?.any||[];return(d.length?d:[{}]).forEach(u=>s.appendChild(yt(u,n))),c.appendChild(s),c.appendChild(_e("Add condition",()=>{s.appendChild(yt({},n)),window.I?.scan?.()})),l.appendChild(c),l.appendChild(kt("then","Then",e.then,t)),l.appendChild(kt("otherwise","Otherwise",e.otherwise,t)),l}function fn(e,t){const o=Array.isArray(e.triggers)?e.triggers:[],n=document.createElement("div");n.className="fb-field-triggers";const l=document.createElement("div");l.className="fb-trig-panel-head";const a=document.createElement("span");a.className="fb-trig-panel-title",a.textContent=`\u{1F3AF} Triggers${o.length?` (${o.length})`:""}`;const i=document.createElement("button");i.type="button",i.className="btn btn-xs btn-ghost",A(i,o.length?"chevron-down":"chevron-right",12);const r=document.createElement("div");r.className="fb-triggers-body",r.hidden=!o.length,l.addEventListener("click",()=>{r.hidden=!r.hidden,A(i,r.hidden?"chevron-right":"chevron-down",12)});const c=document.createElement("p");c.className="fb-trig-blurb",c.textContent="Do something to the form when the answers meet a condition - show a banner or toast, hide, disable or require other fields, refuse the submit, end the form early, notify the admins, jump to a step, or celebrate.",r.appendChild(c);const s=document.createElement("div");return s.className="fb-trig-list",o.forEach((d,u)=>s.appendChild(St(d,t,u))),r.appendChild(s),r.appendChild(_e("Add trigger",()=>{const d=St({enabled:!0,then:[]},t,s.children.length);s.appendChild(d),d.querySelector('.fb-trig-branch[data-branch="then"] .fb-trig-action-list')?.appendChild(De({type:"banner"},t,{open:!0})),window.I?.scan?.()})),l.appendChild(a),l.appendChild(i),n.appendChild(l),n.appendChild(r),n}function Tt(e){const t=e.querySelector(".fb-trig-action-type")?.value,o=de.find(a=>a.value===t);if(!o)return null;const n={type:t},l=a=>e.querySelector(a)?.value?.trim()||"";for(const a of o.params)switch(a){case"message":n.message=l(".fb-trig-p-message");break;case"finishLabel":{const i=l(".fb-trig-p-finish");i&&(n.finishLabel=i);break}case"doneMessage":{const i=l(".fb-trig-p-done");i&&(n.doneMessage=i);break}case"finish":e.querySelector(".fb-trig-p-finish-on")?.checked||(n.finish=!1);break;case"record":e.querySelector(".fb-trig-p-record")?.checked||(n.record=!1);break;case"level":n.level=l(".fb-trig-p-level")||"info";break;case"position":n.position=l(".fb-trig-p-position")||"field-below";break;case"dismissible":n.dismissible=!!e.querySelector(".fb-trig-p-dismissible")?.checked;break;case"target":{const i=l(".fb-trig-p-target")||"rest";n.target=i,i==="listed"&&(n.fields=Array.from(e.querySelectorAll(".fb-trig-p-fields option")).filter(r=>r.selected).map(r=>r.value));break}case"step":{const i=parseInt(l(".fb-trig-p-step"),10);Number.isFinite(i)&&(n.step=i);break}case"url":n.url=l(".fb-trig-p-url");break;case"theme":n.theme=l(".fb-trig-p-theme")||"auto";break;case"intensity":n.intensity=l(".fb-trig-p-intensity")||"medium";break;case"slug":n.slug=l(".fb-trig-p-slug");break;default:break}return o.kind==="event"&&!o.noOnce&&e.querySelector(".fb-trig-p-once")?.checked&&(n.once=!0),n}function hn(e){const t=Tt(e);if(!t)return null;const o=de.find(a=>a.value===t.type),n=t.type,l=["block-submit","end-form","notify"].includes(o.value);return o.params.includes("message")&&!t.message&&!l?(delete t.message,null):(l&&!t.message&&delete t.message,n==="redirect"&&!t.url||n==="run-action"&&!t.slug?null:t)}function bn(e){const o=document.querySelector(`.fb-field-card[data-index="${e}"]`)?.querySelector(".fb-field-triggers");if(!o)return;const n=[];return o.querySelectorAll(".fb-trig-card").forEach(l=>{const a=Array.from(l.querySelectorAll(".fb-trig-cond-row")).map(u=>{const b=u.querySelector(".fb-trig-cond-field")?.value,y=u.querySelector(".fb-trig-cond-op")?.value;if(!b||!y)return null;const f=u.querySelector(".fb-trig-cond-val")?.value??"";return{field:b,operator:y,value:f}}).filter(Boolean);if(!a.length)return;const i=u=>Array.from(l.querySelectorAll(`.fb-trig-branch[data-branch="${u}"] .fb-trig-action-row`)).map(hn).filter(Boolean),r=i("then"),c=i("otherwise");if(!r.length&&!c.length)return;const s=l.querySelector(".fb-trig-match")?.value==="any"?"any":"all",d={id:l.dataset.triggerId,enabled:!!l.querySelector(".fb-trig-enabled")?.checked,when:{[s]:a}};r.length&&(d.then=r),c.length&&(d.otherwise=c),n.push(d)}),n.length?n:void 0}function Nt(e,t,o){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(vn(t,o)),n.appendChild(Cn(t.options||[],o))):(Fe.has(e)&&n.appendChild(Tn(t.options||[],o)),e==="textarea"&&n.appendChild(_([V("Rows",`fb-rows-${o}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(_([V("Min Length",`fb-minlength-${o}`,"number",t.minLength||"",""),V("Max Length",`fb-maxlength-${o}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(_([V("Min",`fb-min-${o}`,"number",t.min??"",""),V("Max",`fb-max-${o}`,"number",t.max??"","")]))),Lt.includes(e)&&n.appendChild(yn(t,o)),n.children.length?n:null}const Lt=["string","email","tel","number","url","date","time","password","select","textarea"];function gn(e){return e?typeof e=="string"?{icon:"",text:e}:{icon:e.icon||"",text:e.icon?"":e.text??""}:{icon:"",text:""}}function yn(e,t){const o=document.createElement("div");o.className="fb-addons";const n=(a,i)=>{const r=gn(e.formConfig?.[a]),c=document.createElement("div");c.style.flex="1";const s=document.createElement("span");s.className="form-label",s.style.fontSize=".8rem",s.textContent=i;const d=et("Icon, e.g. search",r.icon);d.input.id=`fb-${a}-icon-${t}`,d.input.setAttribute("aria-label",`${i}: icon`);const u=document.createElement("input");return u.type="text",u.className="form-input",u.id=`fb-${a}-text-${t}`,u.value=r.text,u.placeholder="or text, e.g. \xA3 or kg",u.maxLength=24,u.setAttribute("aria-label",`${i}: text`),u.style.marginTop=".35rem",d.input.addEventListener("input",()=>{d.input.value.trim()&&(u.value="")}),u.addEventListener("input",()=>{u.value.trim()&&(d.input.value="")}),c.append(s,d.el,u),c};o.appendChild(_([n("prefix","Before the field"),n("suffix","After the field")]));const l=document.createElement("p");return l.className="form-hint text-muted",l.style.cssText="font-size:.73rem;margin:-.4rem 0 .75rem;",l.textContent="An icon or a short text joined to the start or end of the input, like \xA3 before a price or kg after a weight. Leave both empty for none.",o.appendChild(l),o}function qt(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border); border-radius:6px; margin-top:.5rem; overflow:hidden;";const o=document.createElement("div");o.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border);",o.textContent=e,t.appendChild(o);const n=document.createElement("div");return n.className="fb-chooser-section-body",n.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",t.appendChild(n),{wrap:t,body:n}}function Ee(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem";const r=document.createElement("select");if(r.id=e,r.className="form-input",n.forEach(([c,s])=>{const d=document.createElement("option");d.value=c,d.textContent=s,String(c)===String(o)&&(d.selected=!0),r.appendChild(d)}),a.appendChild(i),a.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.72rem;margin-top:.2rem;",a.appendChild(c)}return a}function $t(e,t,o,n){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const i=document.createElement("input");if(i.id=e,i.type="checkbox",i.checked=!!o,a.appendChild(i),a.appendChild(document.createTextNode(" "+t)),l.appendChild(a),n){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=n,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function Pe(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem",a.appendChild(i);const r=document.createElement("div");r.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const c=document.createElement("input");c.id=e,c.type="text",c.className="form-input",c.value=o||"",n&&(c.placeholder=n),c.style.flex="1";const s=document.createElement("input");s.type="color",s.title="Pick a colour",s.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border);border-radius:4px;cursor:pointer;";const d=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(s.value=d?c.value:"#000000",s.addEventListener("input",()=>{c.value=s.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),r.appendChild(c),r.appendChild(s),a.appendChild(r),l){const u=document.createElement("p");u.className="form-hint text-muted",u.textContent=l,u.style.cssText="font-size:.72rem;margin-top:.2rem;",a.appendChild(u)}return a}function vn(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=qt("Layout");n.body.appendChild(_([Ee(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card - rich tiles"],["chip","Chip - compact pills"]],"Card supports description and grid columns; chips wrap."),$t(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(_([Ee(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),V("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1-6). Ignored for chips.")])),o.appendChild(n.wrap);const l=qt("Visual");return l.body.appendChild(_([Pe(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour - semantic name or any CSS colour."),Ee(`fb-chooser-accent-style-${t}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),l.body.appendChild(_([$t(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),Pe(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),l.body.appendChild(_([Ee(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),Pe(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),o.appendChild(l.wrap),o}function xn(e,t,o,n){const l=document.createElement("div");l.className="fb-chooser-option-row",l.dataset.optIdx=String(o),l.style.cssText="border:1px solid var(--dm-border); border-radius:4px; padding:.5rem; background:var(--dm-background-alt); display:flex; flex-direction:column; gap:.35rem;";const a=document.createElement("div");a.style.cssText="display:flex; gap:.4rem; align-items:center;";const i=document.createElement("div");i.style.cssText="display:flex; flex-direction:column; gap:1px;";const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move up",A(r,"arrow-up"),r.addEventListener("click",O=>{O.preventDefault(),At(t,o,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.title="Move down",A(c,"arrow-down"),c.addEventListener("click",O=>{O.preventDefault(),At(t,o,1,n)}),i.appendChild(r),i.appendChild(c),a.appendChild(i);const s=document.createElement("input");s.type="text",s.className="form-input fb-chooser-opt-value",s.placeholder="value",s.value=e.value||"",s.style.flex="1",a.appendChild(s);const d=document.createElement("input");d.type="text",d.className="form-input fb-chooser-opt-label",d.placeholder="label (visible to users)",d.value=e.label||"",d.style.flex="2",a.appendChild(d);const u=document.createElement("button");u.type="button",u.className="btn btn-xs btn-ghost",u.style.cssText="line-height:1;opacity:.6;",u.title="Remove option",A(u,"x"),u.addEventListener("click",O=>{O.preventDefault(),kn(t,o,n)}),a.appendChild(u),l.appendChild(a);const b=document.createElement("div");b.style.cssText="display:flex; gap:.4rem;";const y=et("icon name (optional)",e.icon||"");y.input.classList.add("fb-chooser-opt-icon"),y.el.style.flex="1",b.appendChild(y.el);const f=document.createElement("input");f.type="text",f.className="form-input fb-chooser-opt-description",f.placeholder="description (card + comfortable only)",f.value=e.description||"",f.style.flex="2",b.appendChild(f),l.appendChild(b);const h=document.createElement("div");h.style.cssText="display:flex; gap:.4rem;";const k=document.createElement("input");k.type="text",k.className="form-input fb-chooser-opt-tooltip",k.placeholder="tooltip (hover hint)",k.value=e.tooltip||"",k.style.flex="2",h.appendChild(k);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-badge-text",m.placeholder="badge text",m.value=e.badge?.text||"",m.style.flex="1",h.appendChild(m);const x=document.createElement("select");x.className="form-input fb-chooser-opt-badge-type",x.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([O,X])=>{const W=document.createElement("option");W.value=O,W.textContent=X,(e.badge?.type||"")===O&&(W.selected=!0),x.appendChild(W)}),h.appendChild(x),l.appendChild(h);const N=document.createElement("div");N.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const T=document.createElement("label");T.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const I=document.createElement("input");I.type="checkbox",I.className="fb-chooser-opt-recommended",I.checked=!!e.recommended,T.appendChild(I),T.appendChild(document.createTextNode(" Recommended")),N.appendChild(T);const B=document.createElement("label");B.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const G=document.createElement("input");return G.type="checkbox",G.className="fb-chooser-opt-disabled",G.checked=!!e.disabled,B.appendChild(G),B.appendChild(document.createTextNode(" Disabled")),N.appendChild(B),l.appendChild(N),l}function Cn(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.5rem;";const n=document.createElement("div");n.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const l=document.createElement("p");l.textContent="Options",l.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(l);const a=document.createElement("button");a.type="button",a.className="btn btn-xs btn-ghost",re(a,"plus","Add option"),n.appendChild(a),o.appendChild(n);const i=document.createElement("div");i.className="fb-chooser-options-list",i.id=`fb-chooser-options-${t}`,i.style.cssText="display:flex; flex-direction:column; gap:.4rem;",o.appendChild(i);const r=()=>{i.replaceChildren();const c=En(t);(c.length?c:e||[]).forEach((d,u)=>{i.appendChild(xn(d,t,u,r))})};return a.addEventListener("click",c=>{c.preventDefault(),wn(t,r)}),r(),o}function En(e){const t=w[e];return!t||!Array.isArray(t.options)?[]:t.options}function je(e){return w[e]?(Array.isArray(w[e].options)||(w[e].options=[]),w[e].options):[]}function wn(e,t){we(e),je(e).push({value:"new",label:"New option"}),t()}function kn(e,t,o){we(e),je(e).splice(t,1),o()}function At(e,t,o,n){we(e);const l=je(e),a=t+o;if(a<0||a>=l.length)return;const[i]=l.splice(t,1);l.splice(a,0,i),n()}function we(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const o=t.querySelectorAll(".fb-chooser-option-row"),n=[];o.forEach(l=>{const a={value:l.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:l.querySelector(".fb-chooser-opt-label")?.value.trim()||""},i=l.querySelector(".fb-chooser-opt-icon")?.value.trim();i&&(a.icon=i);const r=l.querySelector(".fb-chooser-opt-description")?.value.trim();r&&(a.description=r);const c=l.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(a.tooltip=c);const s=l.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),d=l.querySelector(".fb-chooser-opt-badge-type")?.value;(s||d)&&(a.badge={},s&&(a.badge.text=s),d&&(a.badge.type=d)),l.querySelector(".fb-chooser-opt-recommended")?.checked&&(a.recommended=!0),l.querySelector(".fb-chooser-opt-disabled")?.checked&&(a.disabled=!0),n.push(a)}),w[e]&&(w[e].options=n)}function _(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(o=>{o&&t.appendChild(o)}),t}function V(e,t,o,n,l){const a=document.createElement("div");a.style.flex="1";const i=document.createElement("label");i.htmlFor=t,i.className="form-label",i.textContent=e,i.style.fontSize=".8rem";const r=document.createElement("input");if(r.id=t,r.type=o||"text",r.className="form-input",r.value=n??"",a.appendChild(i),a.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.73rem;margin-top:.35rem;",a.appendChild(c)}return a}function Sn(e,t,o,n){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=t,a.className="form-label",a.textContent=e,a.style.fontSize=".8rem";const i=document.createElement("select");return i.id=t,i.className="form-select",o.forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===n&&(c.selected=!0),i.appendChild(c)}),l.appendChild(a),l.appendChild(i),l}function Ft(e,t,o){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const a=document.createElement("input");return a.id=t,a.type="checkbox",a.checked=o,l.appendChild(a),l.appendChild(document.createTextNode(e)),n.appendChild(l),n}function Tn(e,t){const o=document.createElement("div");o.style.cssText="margin-top:.4rem;";const n=document.createElement("p");n.textContent="Options (one per line: value or value:Label)",n.style.cssText="font-size:.8rem;font-weight:600;margin-bottom:.3rem;";const l=document.createElement("textarea");return l.id=`fb-options-${t}`,l.className="form-input",l.rows=4,l.placeholder=`yes:Yes
|
|
6
6
|
no:No
|
|
7
7
|
maybe:Maybe`,l.value=(e||[]).map(a=>{const i=typeof a=="string"?a:a.value??"",r=typeof a=="string"?a:a.label??i;return i===r?i:`${i}:${r}`}).join(`
|
|
8
|
-
`),l.style.fontFamily="monospace",o.appendChild(n),o.appendChild(l),o}function ke(e){return parseInt(e,10)===3?3:2}function Se(e){const t=e.find("#field-project").val()||"";return z(),{title:e.find("#field-title").val().trim(),slug:e.find("#field-slug").val().trim(),description:e.find("#field-description").val().trim(),...e.find("#field-bundled").is(":checked")?{bundled:!0}:{},meta:{...be||{},project:t||null},fields:C,settings:{submitText:e.find("#setting-submit-text").val().trim()||"Submit",successMessage:e.find("#setting-success-message").val().trim()||"Thank you.",layout:e.find("#setting-layout").val()||"grid",columns:ke(e.find("#setting-columns").val()),submitSpan:e.find("#setting-submit-span").val()||"",honeypot:e.find("#setting-honeypot").prop("checked"),rateLimitPerMinute:parseInt(e.find("#setting-rate-limit").val(),10)||3,successRedirect:e.find("#setting-success-redirect").val().trim()||null,actionSlug:e.find("#action-cms-slug").val()||null},actions:{email:{enabled:e.find("#action-email-enabled").prop("checked"),recipients:e.find("#action-email-recipients").val().trim(),subjectPrefix:e.find("#action-email-subject-prefix").val().trim()},webhook:{enabled:e.find("#action-webhook-enabled").prop("checked"),url:e.find("#action-webhook-url").val().trim(),method:e.find("#action-webhook-method").val()},...he!==null&&{collection:he}}}}function Lt(e,t){const o={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const l={...n.formConfig||{}};l.span==="full"&&t&&(l.span=t);const a=n.type==="chooser"||!!(n.variant&&["select","radio","checkbox-group"].includes(n.type)),r={type:n.type==="checkbox"?"boolean":n.type==="date"||n.type==="icon"||n.type==="image"?"string":a?"chooser":n.type,label:n.label,required:n.required,options:n.options,formConfig:{...n.placeholder&&{placeholder:n.placeholder},...n.helper&&{helperText:n.helper},...n.tooltip&&{tooltip:n.tooltip},...l}};a&&(n.variant&&(r.variant=n.variant),(n.multiple||n.type==="checkbox-group")&&(r.multiple=!0),n.density&&(r.density=n.density),n.columns&&(r.columns=n.columns),n.accent&&(r.accent=n.accent),n.accentStyle&&(r.accentStyle=n.accentStyle),n.glow&&(r.glow=!0),n.glowColour&&(r.glowColour=n.glowColour),n.shadow&&(r.shadow=n.shadow),n.shadowColour&&(r.shadowColour=n.shadowColour)),o[n.name]=r}),o}function qt(e,t){const o=typeof e=="string"?document.querySelector(e):e;o&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const l=o.querySelector(`[name="${n.name}"]`);l&&l.type!=="date"&&(l.type="date")})}function $t(e,t){const o=e?.querySelector?.(".dm-plugin-banner-name .dm-plugin-banner-sep");if(o){for(;o.nextSibling;)o.nextSibling.remove();o.after(document.createTextNode(String(t||"")))}}const yn=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");let W=null,At=0,Ft=!1;const Te=e=>String(e||"").split("?")[0].replace(/\/$/,"");function vn(){Ft||!window.R?.use||(Ft=!0,R.use((e,t,o)=>!W||!lt(t?.path,e?.path)||W.path!==Te(t?.path)||(W.flush(),!W.dirty())?o():E.confirm("You have unsaved changes to this form. Leave anyway? They are kept as a draft for your next visit to it.").then(n=>{n?o():history.replaceState(null,"",`#${t.path}`)})),M.subscribe("router:afterChange",({to:e,from:t}={})=>{if(W&&W.path===Te(t?.path)&<(t?.path,e?.path)){const o=W;W=null,o.dispose()}}))}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){if(vn(),W){const p=W;W=null,p.dispose()}const t=Te(location.hash.replace(/^#/,"")),o=++At,n=()=>o!==At||!e.get(0)?.isConnected||Te(location.hash.replace(/^#/,""))!==t;C=[],N=null,he=null;const l=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);N=l?l[1]:null;let a=null;const i=E.loader(e.get(0),{type:"dots"});if(N)try{if(a=await ie(`/forms/${N}`),n()){i.destroy();return}C=a.fields||[],he=a.actions?.collection??null}catch{if(n()){i.destroy();return}E.toast("Could not load form.",{type:"error"})}be={};try{const p=await ie("/projects").catch(()=>[]);if(n()){i.destroy();return}const v=e.find("#field-project").get(0);v&&(Array.isArray(p)?p:[]).forEach(g=>{const w=document.createElement("option");w.value=g.slug,w.textContent=g.name||g.slug,v.appendChild(w)})}catch{}if(i.destroy(),$t(e.get(0),a?a.title||a.slug:"New form"),N||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(st(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),a){e.find("#field-title").val(a.title),e.find("#field-slug").val(a.slug),e.find("#field-description").val(a.description||""),e.find("#field-bundled").prop("checked",!!a.bundled),be=a.meta||{},e.find("#field-project").val(a.meta?.project||"");const p=a.settings||{};e.find("#setting-submit-text").val(p.submitText||"Submit"),e.find("#setting-success-message").val(p.successMessage||""),e.find("#setting-layout").val(p.layout||"grid"),e.find("#setting-columns").val(ke(p.columns)),e.find("#setting-submit-span").val(p.submitSpan||""),e.find("#columns-group").get(0).style.display=(p.layout||"grid")==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",p.honeypot!==!1),e.find("#setting-rate-limit").val(p.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(p.successRedirect||"");const v=a.actions?.email||{};e.find("#action-email-enabled").prop("checked",v.enabled||!1),e.find("#action-email-recipients").val(v.recipients||""),e.find("#action-email-subject-prefix").val(v.subjectPrefix||"");const g=a.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",g.enabled||!1),e.find("#action-webhook-url").val(g.url||""),e.find("#action-webhook-method").val(g.method||"POST")}const r=a?.settings?.actionSlug||"";try{const p=await ie("/actions").catch(()=>[]);if(n())return;const v=e.find("#action-cms-slug").get(0);if(fe=Array.isArray(p)?p:[],(Array.isArray(p)?p:[]).forEach(g=>{const w=document.createElement("option");w.value=g.slug,w.textContent=g.title||g.slug,g.slug===r&&(w.selected=!0),v.appendChild(w)}),!p.length){const g=document.createElement("option");g.value="",g.textContent="No actions available",g.disabled=!0,v.appendChild(g)}}catch{}const c=p=>{e.find("#columns-group").get(0).style.display=p?"":"none",document.querySelectorAll(".fb-grid-row").forEach(v=>{v.style.display=p?"flex":"none"})};e.find("#setting-layout").get(0)?.addEventListener("change",function(){c(this.value==="grid")}),c(e.find("#setting-layout").val()==="grid"),Q(e);const s=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",p=>{p.stopPropagation(),s.style.display=s.style.display==="none"?"":"none"}),ne&&document.removeEventListener("click",ne),ne=()=>{s&&(s.style.display="none")},document.addEventListener("click",ne),e.find("#add-field-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),z();const p=C.length;C.push({name:`field_${p+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),Q(e);const v=e.find("#fields-list").get(0)?.lastElementChild;if(v){const g=v.querySelector(".fb-field-body");g&&(g.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),z(),C.push({type:"spacer"}),Q(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),z();const p=C.filter(v=>v.type==="page-break").length+2;C.push({type:"page-break",label:`Step ${p}`,description:""}),Q(e)}),ge=()=>{const p=Se(e),v=e.find("#preview-container").get(0);if(!v)return;const g=e.find("#preview-test-result").get(0),w=e.find("#preview-test-badge").get(0);g&&(g.style.display="none",g.textContent=""),w&&(w.style.display=N?"":"none"),e.find("#preview-card").get(0).style.display="",v.textContent="";const B=document.createElement("div");B.id="fb-preview-form",v.appendChild(B);const U=N?async L=>{g&&(g.style.display="none",g.textContent="");try{const T=await fetch(`/api/forms/submit/${N}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(L)}),K=await T.json();if(!T.ok)throw new Error(K.error||"Submission failed.");g&&(g.textContent=K.message||p.settings?.successMessage||"Submitted successfully.",g.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-success) 12%, transparent);color:var(--dm-success);"),E.toast("Test submission stored.",{type:"success"})}catch(T){g&&(g.textContent=T.message,g.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-danger) 12%, transparent);color:var(--dm-danger);"),E.toast(T.message,{type:"error"})}return!1}:()=>!1,ue=p.fields.some(L=>L.type==="page-break");if(B.addEventListener("dm-form-end",L=>{const T=L.detail?.validation;if(T&&!T.valid){E.toast(`Still to answer: ${T.missing.map(K=>K.label).concat(T.errors).join(", ")}`,{type:"warning"});return}U(L.detail?.values||{})}),typeof F<"u"){const L=ke(p.settings?.columns),T=p.settings?.layout||"grid";if(ue&&F.wizard){const K=zt(p.fields,p.title),me=K.map(te=>({title:te.title,description:te.description,fields:Lt(te.fields,L)}));let O=0,Z=null,Le=null;const Xe=te=>{const Ze=(K[O]||{}).fields||[];qt("#fb-preview-form",Ze),et(B,Ze),te&&window.FormLogicEngine&&ct(p.fields)&&(Z?.destroy?.(),Z=new window.FormLogicEngine.FormLogicRuntime({fields:p.fields},B,{baseValues:Le?.getStepData?Le.getStepData():{}}),Z.init())};Le=F.wizard("#fb-preview-form",{schema:{steps:me},layout:T,columns:L,finishText:p.settings?.submitText||"Finish",onStepChange:te=>{O=te,queueMicrotask(()=>Xe(!0))},onSubmit:U}),Xe(!1)}else if(F.render){const K=Lt(p.fields,L),me={};if(p.fields.forEach(O=>{if(!(!O.name||O.type==="page-break"||O.type==="spacer")&&(O.type==="select"||O.type==="multiselect")&&O.required){const Z=(O.options||[])[0];Z&&(me[O.name]=typeof Z=="object"?Z.value:Z)}}),F.render("#fb-preview-form",K,me,{submitText:p.settings?.submitText||"Submit",layout:T,columns:L,onSubmit:U}),T==="grid"&&p.settings?.submitSpan==="full"){const O=document.querySelector("#fb-preview-form .form-buttons");O&&O.classList.add("col-span-full")}qt("#fb-preview-form",p.fields),et(B,p.fields)}window.FormLogicEngine&&ct(p.fields)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:p.fields},B).init()})}else{const L=document.createElement("p");L.textContent=`${p.fields.filter(T=>T.type!=="page-break").length} field(s): ${p.fields.filter(T=>T.type!=="page-break").map(T=>T.label).join(", ")}`,L.style.cssText="color:var(--dm-text-muted);font-style:italic;",B.appendChild(L)}},ge();const d=e.find("#editor-tabs").get(0),u=e.find("#preview-card").get(0),b=p=>{u&&u.contains(p.target)||Oe()};d&&(d.addEventListener("input",b),d.addEventListener("change",b));const y=e.get(0),m=[],f=p=>(m.push(p),p);let k=!1;const h=M.observable(!1),x=M.observable(!1);let $=N,A=Se(e),G=Ae(A),_=A;const j=()=>{k||!y.isConnected||(_=Se(e),h.value=Ae(_)!==G)};let I=null;Be=()=>{clearTimeout(I),I=setTimeout(j,150)},f(()=>{clearTimeout(I),Be=null,ce=null});const X=p=>{const v=p.settings||{};e.find("#field-title").val(p.title||""),e.find("#field-slug").val(p.slug||""),e.find("#field-description").val(p.description||""),e.find("#field-bundled").prop("checked",!!p.bundled),be={...p.meta||{}},e.find("#field-project").val(p.meta?.project||""),e.find("#setting-submit-text").val(v.submitText||"Submit"),e.find("#setting-success-message").val(v.successMessage||""),e.find("#setting-layout").val(v.layout||"grid"),e.find("#setting-columns").val(ke(v.columns)),e.find("#setting-submit-span").val(v.submitSpan||""),e.find("#setting-honeypot").prop("checked",v.honeypot!==!1),e.find("#setting-rate-limit").val(v.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(v.successRedirect||""),e.find("#action-cms-slug").val(v.actionSlug||"");const g=p.actions?.email||{};e.find("#action-email-enabled").prop("checked",!!g.enabled),e.find("#action-email-recipients").val(g.recipients||""),e.find("#action-email-subject-prefix").val(g.subjectPrefix||"");const w=p.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",!!w.enabled),e.find("#action-webhook-url").val(w.url||""),e.find("#action-webhook-method").val(w.method||"POST"),C=JSON.parse(JSON.stringify(p.fields||[])),c((v.layout||"grid")==="grid"),Q(e)};async function H(){if(x.peek()||k)return;const p=Se(e);if(!p.title){E.toast("Please enter a form title (Form Details).",{type:"error"});return}x.value=!0;const v=N;try{N?(await ie(`/forms/${N}`,{method:"PUT",body:JSON.stringify(p)}),E.toast("Form saved.",{type:"success"})):(N=(await ie("/forms",{method:"POST",body:JSON.stringify(p)})).slug,E.toast("Form created.",{type:"success"})),$=N,A=p,G=Ae(p),_=p,h.value=!1;for(const g of new Set([se(v),se(N)]))try{S.remove?S.remove(g):S.set(g,null)}catch{}$t(y,p.title),Pt("#/forms"),v||R.navigate(`/forms/edit/${N}`)}catch(g){v||(N=null),E.toast(g.message||"Failed to save form.",{type:"error"})}finally{x.value=!1}}async function It(){h.peek()&&(!await E.confirm("Discard the changes you have not saved?")||k||(X(A),j(),Ye()))}const Ve=y.querySelector(".fex-bar");if(Ve){const p=M.applyBindings({dirty:h,saving:x},Ve,{methods:{save:H,discard:It}});f(()=>p.dispose())}f(Dt(y,{position:"bottom"})),qe(e,{id:"form-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>H()}),qe(e,{id:"form-editor-back",icon:"arrow-left",title:"All forms",onClick:()=>{location.hash="#/forms"}}),N&&qe(e,{id:"form-editor-subs",icon:"list",title:"Submissions",onClick:()=>{location.hash=`#/forms/${encodeURIComponent(N)}/submissions`}});const Ge=p=>{n()||!(p.ctrlKey||p.metaKey)||String(p.key).toLowerCase()!=="s"||(p.preventDefault(),p.stopImmediatePropagation(),H())};document.addEventListener("keydown",Ge,!0),f(()=>document.removeEventListener("keydown",Ge,!0));const Y=e.find("#fields-list").get(0),Bt=p=>p?.closest?.(".fb-field-card"),We=p=>Number(p?.dataset.index),He=p=>p?.querySelector(":scope > .fb-field-body"),Ue=p=>{const v=He(p);v&&(v.style.display=v.style.display==="none"?"":"none")},oe=(p,v)=>{C=p,Q(e),v!=null&&Y?.querySelectorAll(".fb-field-card")[v]?.focus()},Ot=async p=>{try{await navigator.clipboard.writeText(p),E.toast(`Copied "${p}".`,{type:"success"})}catch{E.toast(`Copy failed - the name is ${p}.`,{type:"info"})}},Rt=p=>{const v=Bt(p),g=We(v);z();const w=C[g];if(!w)return[];const B=w.type!=="spacer"&&w.type!=="page-break",U=He(v),ue=U&&U.style.display!=="none";return[{header:$e(w)},...U?[{label:ue?"Close":"Edit",icon:ue?"chevron-up":"edit",action:()=>Ue(v)}]:[],...B?[{label:"Logic & triggers",icon:"zap",action:()=>{U&&(U.style.display=""),(U?.querySelector(".fb-field-logic")||U)?.scrollIntoView({block:"center",behavior:"smooth"})}}]:[],{label:"Duplicate",icon:"copy",action:()=>{z(),oe(jt(C,g),g+1)}},{type:"divider"},{label:"Move up",icon:"arrow-up",shortcut:"Alt+\u2191",disabled:g===0,action:()=>{z(),oe(at(C,g,-1),g-1)}},{label:"Move down",icon:"arrow-down",shortcut:"Alt+\u2193",disabled:g>=C.length-1,action:()=>{z(),oe(at(C,g,1),g+1)}},...B&&w.name?[{label:"Copy field name",icon:"clipboard",action:()=>Ot(w.name)}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:async()=>{if(w.type!=="spacer"&&!await E.confirm(`Remove "${yn($e(w))}"?`))return;z();const L=[...C];L.splice(g,1),oe(L,Math.min(g,L.length-1))}}]};if(Y&&typeof E.sortable=="function"){const p=E.sortable(Y,{items:".fb-field-card",handle:".fb-drag-grip",key:null,onSort:({from:v,to:g})=>{z();const w=[...C],[B]=w.splice(v,1);w.splice(g,0,B),oe(w,g)}});f(()=>p.destroy())}let Je=null;if(Y&&E.contextMenu&&(Je=E.contextMenu(Y,{cursor:"glyph",match:".fb-field-card > :first-child",inherit:!1,density:"compact",items:p=>Rt(p)}),f(()=>Je?.destroy?.())),ce=p=>{for(const v of p.querySelectorAll(".fb-field-card")){v.tabIndex=0;const g=v.firstElementChild;if(!g||g.querySelector(".fex-kebab"))continue;const w=document.createElement("button");w.type="button",w.className="fex-kebab",w.textContent="\u22EE",w.title="Actions",z(),w.setAttribute("aria-label",`Actions for ${$e(C[We(v)])}`),w.addEventListener("click",B=>{B.stopPropagation(),tt(w)}),g.appendChild(w)}},Y){ce(Y);const p=v=>{const g=v.target?.classList?.contains("fb-field-card")?v.target:null;g&&(v.key==="F10"&&v.shiftKey?(v.preventDefault(),tt(g.firstElementChild)):v.key==="Enter"&&(v.preventDefault(),Ue(g)))};Y.addEventListener("keydown",p),f(()=>Y.removeEventListener("keydown",p))}const Ye=()=>{try{const p=se($);S.remove?S.remove(p):S.set(p,null)}catch{}},Ke=p=>{if(!h.peek()||!p){Ye();return}try{S.set(se($),{data:_t(p),base:nt(G),at:new Date().toISOString()})}catch{}},Qe=p=>{j(),h.peek()&&(Ke(_),p.preventDefault(),p.returnValue="")};window.addEventListener("beforeunload",Qe),f(()=>window.removeEventListener("beforeunload",Qe)),f(()=>{ne&&(document.removeEventListener("click",ne),ne=null)}),W={path:t,dirty:()=>h.peek(),flush:()=>{clearTimeout(I),j()},dispose(){k||(Ke(_),k=!0,m.splice(0).reverse().forEach(p=>{try{p?.()}catch{}}))}};const Ne=se(N);let ee=null;try{ee=S.get(Ne)}catch{}if(ee?.data){try{S.remove?S.remove(Ne):S.set(Ne,null)}catch{}const p=ee.base&&ee.base!==nt(G),v=ee.at?` (${Gt(ee.at)})`:"";await E.confirm(`You left unsaved changes to this form${v}.${p?" The saved form has changed since then.":""} Pick up where you left off?`)&&!k&&(X(Vt(ee.data,A)),j())}}};
|
|
8
|
+
`),l.style.fontFamily="monospace",o.appendChild(n),o.appendChild(l),o}function ke(e){return parseInt(e,10)===3?3:2}function Se(e){const t=e.find("#field-project").val()||"";return D(),{title:e.find("#field-title").val().trim(),slug:e.find("#field-slug").val().trim(),description:e.find("#field-description").val().trim(),...e.find("#field-bundled").is(":checked")?{bundled:!0}:{},meta:{...be||{},project:t||null},fields:w,settings:{submitText:e.find("#setting-submit-text").val().trim()||"Submit",successMessage:e.find("#setting-success-message").val().trim()||"Thank you.",layout:e.find("#setting-layout").val()||"grid",columns:ke(e.find("#setting-columns").val()),submitSpan:e.find("#setting-submit-span").val()||"",honeypot:e.find("#setting-honeypot").prop("checked"),rateLimitPerMinute:parseInt(e.find("#setting-rate-limit").val(),10)||3,successRedirect:e.find("#setting-success-redirect").val().trim()||null,actionSlug:e.find("#action-cms-slug").val()||null},actions:{email:{enabled:e.find("#action-email-enabled").prop("checked"),recipients:e.find("#action-email-recipients").val().trim(),subjectPrefix:e.find("#action-email-subject-prefix").val().trim()},webhook:{enabled:e.find("#action-webhook-enabled").prop("checked"),url:e.find("#action-webhook-url").val().trim(),method:e.find("#action-webhook-method").val()},...he!==null&&{collection:he}}}}function It(e,t){const o={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const l={...n.formConfig||{}};l.span==="full"&&t&&(l.span=t);const a=n.type==="chooser"||!!(n.variant&&["select","radio","checkbox-group"].includes(n.type)),r={type:n.type==="checkbox"?"boolean":n.type==="date"||n.type==="icon"||n.type==="image"?"string":a?"chooser":n.type,label:n.label,required:n.required,options:n.options,formConfig:{...n.placeholder&&{placeholder:n.placeholder},...n.helper&&{helperText:n.helper},...n.tooltip&&{tooltip:n.tooltip},...l}};a&&(n.variant&&(r.variant=n.variant),(n.multiple||n.type==="checkbox-group")&&(r.multiple=!0),n.density&&(r.density=n.density),n.columns&&(r.columns=n.columns),n.accent&&(r.accent=n.accent),n.accentStyle&&(r.accentStyle=n.accentStyle),n.glow&&(r.glow=!0),n.glowColour&&(r.glowColour=n.glowColour),n.shadow&&(r.shadow=n.shadow),n.shadowColour&&(r.shadowColour=n.shadowColour)),o[n.name]=r}),o}function Bt(e,t){const o=typeof e=="string"?document.querySelector(e):e;o&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const l=o.querySelector(`[name="${n.name}"]`);l&&l.type!=="date"&&(l.type="date")})}function Ot(e,t){const o=e?.querySelector?.(".dm-plugin-banner-name .dm-plugin-banner-sep");if(o){for(;o.nextSibling;)o.nextSibling.remove();o.after(document.createTextNode(String(t||"")))}}const Nn=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");let H=null,Rt=0,Mt=!1;const Te=e=>String(e||"").split("?")[0].replace(/\/$/,"");function Ln(){Mt||!window.R?.use||(Mt=!0,R.use((e,t,o)=>!H||!at(t?.path,e?.path)||H.path!==Te(t?.path)||(H.flush(),!H.dirty())?o():E.confirm("You have unsaved changes to this form. Leave anyway? They are kept as a draft for your next visit to it.").then(n=>{n?o():history.replaceState(null,"",`#${t.path}`)})),M.subscribe("router:afterChange",({to:e,from:t}={})=>{if(H&&H.path===Te(t?.path)&&at(t?.path,e?.path)){const o=H;H=null,o.dispose()}}))}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){if(Ln(),H){const p=H;H=null,p.dispose()}const t=Te(location.hash.replace(/^#/,"")),o=++Rt,n=()=>o!==Rt||!e.get(0)?.isConnected||Te(location.hash.replace(/^#/,""))!==t;w=[],q=null,he=null;const l=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);q=l?l[1]:null;let a=null;const i=E.loader(e.get(0),{type:"dots"});if(q)try{if(a=await ie(`/forms/${q}`),n()){i.destroy();return}w=a.fields||[],he=a.actions?.collection??null}catch{if(n()){i.destroy();return}E.toast("Could not load form.",{type:"error"})}be={};try{const p=await ie("/projects").catch(()=>[]);if(n()){i.destroy();return}const v=e.find("#field-project").get(0);v&&(Array.isArray(p)?p:[]).forEach(g=>{const C=document.createElement("option");C.value=g.slug,C.textContent=g.name||g.slug,v.appendChild(C)})}catch{}if(i.destroy(),Ot(e.get(0),a?a.title||a.slug:"New form"),q||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(rt(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),a){e.find("#field-title").val(a.title),e.find("#field-slug").val(a.slug),e.find("#field-description").val(a.description||""),e.find("#field-bundled").prop("checked",!!a.bundled),be=a.meta||{},e.find("#field-project").val(a.meta?.project||"");const p=a.settings||{};e.find("#setting-submit-text").val(p.submitText||"Submit"),e.find("#setting-success-message").val(p.successMessage||""),e.find("#setting-layout").val(p.layout||"grid"),e.find("#setting-columns").val(ke(p.columns)),e.find("#setting-submit-span").val(p.submitSpan||""),e.find("#columns-group").get(0).style.display=(p.layout||"grid")==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",p.honeypot!==!1),e.find("#setting-rate-limit").val(p.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(p.successRedirect||"");const v=a.actions?.email||{};e.find("#action-email-enabled").prop("checked",v.enabled||!1),e.find("#action-email-recipients").val(v.recipients||""),e.find("#action-email-subject-prefix").val(v.subjectPrefix||"");const g=a.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",g.enabled||!1),e.find("#action-webhook-url").val(g.url||""),e.find("#action-webhook-method").val(g.method||"POST")}const r=a?.settings?.actionSlug||"";try{const p=await ie("/actions").catch(()=>[]);if(n())return;const v=e.find("#action-cms-slug").get(0);if(fe=Array.isArray(p)?p:[],(Array.isArray(p)?p:[]).forEach(g=>{const C=document.createElement("option");C.value=g.slug,C.textContent=g.title||g.slug,g.slug===r&&(C.selected=!0),v.appendChild(C)}),!p.length){const g=document.createElement("option");g.value="",g.textContent="No actions available",g.disabled=!0,v.appendChild(g)}}catch{}const c=p=>{e.find("#columns-group").get(0).style.display=p?"":"none",document.querySelectorAll(".fb-grid-row").forEach(v=>{v.style.display=p?"flex":"none"})};e.find("#setting-layout").get(0)?.addEventListener("change",function(){c(this.value==="grid")}),c(e.find("#setting-layout").val()==="grid"),Q(e);const s=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",p=>{p.stopPropagation(),s.style.display=s.style.display==="none"?"":"none"}),ne&&document.removeEventListener("click",ne),ne=()=>{s&&(s.style.display="none")},document.addEventListener("click",ne),e.find("#add-field-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),D();const p=w.length;w.push({name:`field_${p+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),Q(e);const v=e.find("#fields-list").get(0)?.lastElementChild;if(v){const g=v.querySelector(".fb-field-body");g&&(g.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),D(),w.push({type:"spacer"}),Q(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),D();const p=w.filter(v=>v.type==="page-break").length+2;w.push({type:"page-break",label:`Step ${p}`,description:""}),Q(e)}),ge=()=>{const p=Se(e),v=e.find("#preview-container").get(0);if(!v)return;const g=e.find("#preview-test-result").get(0),C=e.find("#preview-test-badge").get(0);g&&(g.style.display="none",g.textContent=""),C&&(C.style.display=q?"":"none"),e.find("#preview-card").get(0).style.display="",v.textContent="";const z=document.createElement("div");z.id="fb-preview-form",v.appendChild(z);const U=q?async $=>{g&&(g.style.display="none",g.textContent="");try{const L=await fetch(`/api/forms/submit/${q}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify($)}),Y=await L.json();if(!L.ok)throw new Error(Y.error||"Submission failed.");g&&(g.textContent=Y.message||p.settings?.successMessage||"Submitted successfully.",g.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-success) 12%, transparent);color:var(--dm-success);"),E.toast("Test submission stored.",{type:"success"})}catch(L){g&&(g.textContent=L.message,g.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-danger) 12%, transparent);color:var(--dm-danger);"),E.toast(L.message,{type:"error"})}return!1}:()=>!1,ue=p.fields.some($=>$.type==="page-break");if(z.addEventListener("dm-form-end",$=>{const L=$.detail?.validation;if(L&&!L.valid){E.toast(`Still to answer: ${L.missing.map(Y=>Y.label).concat(L.errors).join(", ")}`,{type:"warning"});return}U($.detail?.values||{})}),typeof F<"u"){const $=ke(p.settings?.columns),L=p.settings?.layout||"grid";if(ue&&F.wizard){const Y=jt(p.fields,p.title),me=Y.map(te=>({title:te.title,description:te.description,fields:It(te.fields,$)}));let P=0,Z=null,Le=null;const Xe=te=>{const Ze=(Y[P]||{}).fields||[];Bt("#fb-preview-form",Ze),tt(z,Ze),te&&window.FormLogicEngine&&dt(p.fields)&&(Z?.destroy?.(),Z=new window.FormLogicEngine.FormLogicRuntime({fields:p.fields},z,{baseValues:Le?.getStepData?Le.getStepData():{}}),Z.init())};Le=F.wizard("#fb-preview-form",{schema:{steps:me},layout:L,columns:$,finishText:p.settings?.submitText||"Finish",onStepChange:te=>{P=te,queueMicrotask(()=>Xe(!0))},onSubmit:U}),Xe(!1)}else if(F.render){const Y=It(p.fields,$),me={};if(p.fields.forEach(P=>{if(!(!P.name||P.type==="page-break"||P.type==="spacer")&&(P.type==="select"||P.type==="multiselect")&&P.required){const Z=(P.options||[])[0];Z&&(me[P.name]=typeof Z=="object"?Z.value:Z)}}),F.render("#fb-preview-form",Y,me,{submitText:p.settings?.submitText||"Submit",layout:L,columns:$,onSubmit:U}),L==="grid"&&p.settings?.submitSpan==="full"){const P=document.querySelector("#fb-preview-form .form-buttons");P&&P.classList.add("col-span-full")}Bt("#fb-preview-form",p.fields),tt(z,p.fields)}window.FormLogicEngine&&dt(p.fields)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:p.fields},z).init()})}else{const $=document.createElement("p");$.textContent=`${p.fields.filter(L=>L.type!=="page-break").length} field(s): ${p.fields.filter(L=>L.type!=="page-break").map(L=>L.label).join(", ")}`,$.style.cssText="color:var(--dm-text-muted);font-style:italic;",z.appendChild($)}},ge();const d=e.find("#editor-tabs").get(0),u=e.find("#preview-card").get(0),b=p=>{u&&u.contains(p.target)||Oe()};d&&(d.addEventListener("input",b),d.addEventListener("change",b));const y=e.get(0),f=[],h=p=>(f.push(p),p);let k=!1;const m=M.observable(!1),x=M.observable(!1);let N=q,T=Se(e),I=Ae(T),B=T;const G=()=>{k||!y.isConnected||(B=Se(e),m.value=Ae(B)!==I)};let O=null;Be=()=>{clearTimeout(O),O=setTimeout(G,150)},h(()=>{clearTimeout(O),Be=null,ce=null});const X=p=>{const v=p.settings||{};e.find("#field-title").val(p.title||""),e.find("#field-slug").val(p.slug||""),e.find("#field-description").val(p.description||""),e.find("#field-bundled").prop("checked",!!p.bundled),be={...p.meta||{}},e.find("#field-project").val(p.meta?.project||""),e.find("#setting-submit-text").val(v.submitText||"Submit"),e.find("#setting-success-message").val(v.successMessage||""),e.find("#setting-layout").val(v.layout||"grid"),e.find("#setting-columns").val(ke(v.columns)),e.find("#setting-submit-span").val(v.submitSpan||""),e.find("#setting-honeypot").prop("checked",v.honeypot!==!1),e.find("#setting-rate-limit").val(v.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(v.successRedirect||""),e.find("#action-cms-slug").val(v.actionSlug||"");const g=p.actions?.email||{};e.find("#action-email-enabled").prop("checked",!!g.enabled),e.find("#action-email-recipients").val(g.recipients||""),e.find("#action-email-subject-prefix").val(g.subjectPrefix||"");const C=p.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",!!C.enabled),e.find("#action-webhook-url").val(C.url||""),e.find("#action-webhook-method").val(C.method||"POST"),w=JSON.parse(JSON.stringify(p.fields||[])),c((v.layout||"grid")==="grid"),Q(e)};async function W(){if(x.peek()||k)return;const p=Se(e);if(!p.title){E.toast("Please enter a form title (Form Details).",{type:"error"});return}const v=dn(p.fields);v.length&&E.toast(`Some trigger messages use ${v.map(C=>`{{${C}}}`).join(", ")}, which will show as blank: no field has that name, or it is a password, file or image.`,{type:"warning",duration:9e3}),x.value=!0;const g=q;try{q?(await ie(`/forms/${q}`,{method:"PUT",body:JSON.stringify(p)}),E.toast("Form saved.",{type:"success"})):(q=(await ie("/forms",{method:"POST",body:JSON.stringify(p)})).slug,E.toast("Form created.",{type:"success"})),N=q,T=p,I=Ae(p),B=p,m.value=!1;for(const C of new Set([se(g),se(q)]))try{S.remove?S.remove(C):S.set(C,null)}catch{}Ot(y,p.title),Gt("#/forms"),g||R.navigate(`/forms/edit/${q}`)}catch(C){g||(q=null),E.toast(C.message||"Failed to save form.",{type:"error"})}finally{x.value=!1}}async function zt(){m.peek()&&(!await E.confirm("Discard the changes you have not saved?")||k||(X(T),G(),Ke()))}const Ve=y.querySelector(".fex-bar");if(Ve){const p=M.applyBindings({dirty:m,saving:x},Ve,{methods:{save:W,discard:zt}});h(()=>p.dispose())}h(Vt(y,{position:"bottom"})),qe(e,{id:"form-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>W()}),qe(e,{id:"form-editor-back",icon:"arrow-left",title:"All forms",onClick:()=>{location.hash="#/forms"}}),q&&qe(e,{id:"form-editor-subs",icon:"list",title:"Submissions",onClick:()=>{location.hash=`#/forms/${encodeURIComponent(q)}/submissions`}});const Ge=p=>{n()||!(p.ctrlKey||p.metaKey)||String(p.key).toLowerCase()!=="s"||(p.preventDefault(),p.stopImmediatePropagation(),W())};document.addEventListener("keydown",Ge,!0),h(()=>document.removeEventListener("keydown",Ge,!0));const K=e.find("#fields-list").get(0),Dt=p=>p?.closest?.(".fb-field-card"),He=p=>Number(p?.dataset.index),We=p=>p?.querySelector(":scope > .fb-field-body"),Ue=p=>{const v=We(p);v&&(v.style.display=v.style.display==="none"?"":"none")},oe=(p,v)=>{w=p,Q(e),v!=null&&K?.querySelectorAll(".fb-field-card")[v]?.focus()},_t=async p=>{try{await navigator.clipboard.writeText(p),E.toast(`Copied "${p}".`,{type:"success"})}catch{E.toast(`Copy failed - the name is ${p}.`,{type:"info"})}},Pt=p=>{const v=Dt(p),g=He(v);D();const C=w[g];if(!C)return[];const z=C.type!=="spacer"&&C.type!=="page-break",U=We(v),ue=U&&U.style.display!=="none";return[{header:$e(C)},...U?[{label:ue?"Close":"Edit",icon:ue?"chevron-up":"edit",action:()=>Ue(v)}]:[],...z?[{label:"Logic & triggers",icon:"zap",action:()=>{U&&(U.style.display=""),(U?.querySelector(".fb-field-logic")||U)?.scrollIntoView({block:"center",behavior:"smooth"})}}]:[],{label:"Duplicate",icon:"copy",action:()=>{D(),oe(Wt(w,g),g+1)}},{type:"divider"},{label:"Move up",icon:"arrow-up",shortcut:"Alt+\u2191",disabled:g===0,action:()=>{D(),oe(ot(w,g,-1),g-1)}},{label:"Move down",icon:"arrow-down",shortcut:"Alt+\u2193",disabled:g>=w.length-1,action:()=>{D(),oe(ot(w,g,1),g+1)}},...z&&C.name?[{label:"Copy field name",icon:"clipboard",action:()=>_t(C.name)}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:async()=>{if(C.type!=="spacer"&&!await E.confirm(`Remove "${Nn($e(C))}"?`))return;D();const $=[...w];$.splice(g,1),oe($,Math.min(g,$.length-1))}}]};if(K&&typeof E.sortable=="function"){const p=E.sortable(K,{items:".fb-field-card",handle:".fb-drag-grip",key:null,onSort:({from:v,to:g})=>{D();const C=[...w],[z]=C.splice(v,1);C.splice(g,0,z),oe(C,g)}});h(()=>p.destroy())}let Je=null;if(K&&E.contextMenu&&(Je=E.contextMenu(K,{cursor:"glyph",match:".fb-field-card > :first-child",inherit:!1,density:"compact",items:p=>Pt(p)}),h(()=>Je?.destroy?.())),ce=p=>{for(const v of p.querySelectorAll(".fb-field-card")){v.tabIndex=0;const g=v.firstElementChild;if(!g||g.querySelector(".fex-kebab"))continue;const C=document.createElement("button");C.type="button",C.className="fex-kebab",C.textContent="\u22EE",C.title="Actions",D(),C.setAttribute("aria-label",`Actions for ${$e(w[He(v)])}`),C.addEventListener("click",z=>{z.stopPropagation(),nt(C)}),g.appendChild(C)}},K){ce(K);const p=v=>{const g=v.target?.classList?.contains("fb-field-card")?v.target:null;g&&(v.key==="F10"&&v.shiftKey?(v.preventDefault(),nt(g.firstElementChild)):v.key==="Enter"&&(v.preventDefault(),Ue(g)))};K.addEventListener("keydown",p),h(()=>K.removeEventListener("keydown",p))}const Ke=()=>{try{const p=se(N);S.remove?S.remove(p):S.set(p,null)}catch{}},Ye=p=>{if(!m.peek()||!p){Ke();return}try{S.set(se(N),{data:Ht(p),base:lt(I),at:new Date().toISOString()})}catch{}},Qe=p=>{G(),m.peek()&&(Ye(B),p.preventDefault(),p.returnValue="")};window.addEventListener("beforeunload",Qe),h(()=>window.removeEventListener("beforeunload",Qe)),h(()=>{ne&&(document.removeEventListener("click",ne),ne=null)}),H={path:t,dirty:()=>m.peek(),flush:()=>{clearTimeout(O),G()},dispose(){k||(Ye(B),k=!0,f.splice(0).reverse().forEach(p=>{try{p?.()}catch{}}))}};const Ne=se(q);let ee=null;try{ee=S.get(Ne)}catch{}if(ee?.data){try{S.remove?S.remove(Ne):S.set(Ne,null)}catch{}const p=ee.base&&ee.base!==lt(I),v=ee.at?` (${Jt(ee.at)})`:"";await E.confirm(`You left unsaved changes to this form${v}.${p?" The saved form has changed since then.":""} Pick up where you left off?`)&&!k&&(X(Ut(ee.data,T)),G())}}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.99.0",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@fastify/rate-limit": "^10.3.0",
|
|
84
84
|
"@fastify/static": "9.1.1",
|
|
85
85
|
"bcryptjs": "^3.0.3",
|
|
86
|
-
"domma-js": "0.
|
|
86
|
+
"domma-js": "0.46.1",
|
|
87
87
|
"dotenv": "^17.2.3",
|
|
88
88
|
"fastify": "5.8.5",
|
|
89
89
|
"gray-matter": "^4.0.3",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(l,d){typeof module<"u"&&module.exports?module.exports=d():l.FormLogicEngine=d()})(typeof globalThis<"u"?globalThis:this,function(){"use strict";function l(e){return e===""||e===null||e===void 0?"":String(e)}function d(e){const r=parseFloat(e);return isNaN(r)?null:r}function p(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}const $={not_equals:"equals",not_contains:"contains",not_in:"in"};function q(e,r){const t=l(r),n=l(e.value);switch(e.operator){case"equals":return t===n;case"not_equals":return t!==n;case"contains":return t.toLowerCase().includes(n.toLowerCase());case"not_contains":return!t.toLowerCase().includes(n.toLowerCase());case"starts_with":return t.toLowerCase().startsWith(n.toLowerCase());case"ends_with":return t.toLowerCase().endsWith(n.toLowerCase());case"greater_than":{const i=d(r),s=d(e.value);return i!==null&&s!==null&&i>s}case"less_than":{const i=d(r),s=d(e.value);return i!==null&&s!==null&&i<s}case"is_empty":return p(r);case"is_not_empty":return!p(r);case"in":return(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"not_in":return!(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"matches_regex":try{const i=e.flags||"";return new RegExp(e.value,i).test(t)}catch(i){return typeof console<"u"&&console.warn&&console.warn('[FormLogicEngine] Invalid regex in condition for field "'+e.field+'": '+e.value,i&&i.message),!1}default:return!0}}function y(e,r){if(!e||!e.field||!e.operator)return!0;const t=r[e.field];if(!Array.isArray(t))return q(e,t);if(e.operator==="is_empty")return p(t);if(e.operator==="is_not_empty")return!p(t);const n=$[e.operator];if(n){const i=Object.assign({},e,{operator:n});return!t.some(s=>q(i,s))}return t.some(i=>q(e,i))}function h(e,r){return e?Array.isArray(e.all)?e.all.every(t=>t.all||t.any?h(t,r):y(t,r)):Array.isArray(e.any)?e.any.some(t=>t.all||t.any?h(t,r):y(t,r)):e.field&&e.operator?y(e,r):!0:!0}function A(e,r){const t=e.logic;if(!t||!t.visibility)return"visible";const n=t.visibility,i=n.conditions||[];for(const s of i)if(s.when&&h(s.when,r))return s.then==="hidden"?"hidden":"visible";return n.default==="hidden"?"hidden":"visible"}function _(e,r){const t=e.logic;if(!t||!t.requirement)return e.required||!1;const n=t.requirement,i=n.conditions||[];for(const s of i)if(s.when&&h(s.when,r))return s.then===!0;return n.default===!0}function v(e){if(e==null||e==="")return null;const r=Number(e);return Number.isFinite(r)?r:null}function V(e,r){if(p(r)||Array.isArray(r)||typeof r=="object")return[];const t=e.label||e.name;if(e.type==="number"){const a=Number(r);if(!Number.isFinite(a)||String(r).trim()==="")return[{message:`${t} must be a number.`}];const o=v(e.min),c=v(e.max);return o!==null&&a<o?[{message:`${t} must be at least ${o}.`}]:c!==null&&a>c?[{message:`${t} must be no more than ${c}.`}]:[]}const n=String(r).length,i=v(e.minLength),s=v(e.maxLength);return i!==null&&n<i?[{message:`${t} must be at least ${i} characters.`}]:s!==null&&n>s?[{message:`${t} must be no more than ${s} characters.`}]:[]}function x(e,r,t){const n=V(e,r),i=e.logic;if(!i||!Array.isArray(i.validation))return n;for(const s of i.validation)switch(s.type){case"regex":{if(!p(r))try{new RegExp(s.pattern||"",s.flags||"").test(l(r))||n.push({message:s.message||"Invalid format."})}catch{}break}case"match":{const a=t[s.field];!p(r)&&l(r)!==l(a)&&n.push({message:s.message||"Fields do not match."});break}}return n}function C(e,r){const t=e.logic;if(!t||!t.cascade||!t.cascade.sourceField)return null;const n=t.cascade,i=n.mapping||{},s=r[n.sourceField];if(Array.isArray(s)){const o=new Set,c=[];return s.forEach(function(g){const f=l(g);Object.prototype.hasOwnProperty.call(i,f)&&(i[f]||[]).forEach(function(S){const O=l(S&&S.value);o.has(O)||(o.add(O),c.push(S))})}),c.length?c:n.defaultOptions||null}const a=l(s);return Object.prototype.hasOwnProperty.call(i,a)?i[a]||[]:n.defaultOptions||null}const W={fade:250,slide:300,scale:200};function T(e,r){const t=Number(r);return!isFinite(t)||t<0||t>5e3?W[e]||250:Math.round(t)}function D(e,r,t){const n=T(r,t);if(r==="fade")e.style.transition="opacity "+n+"ms ease",e.style.opacity="1",requestAnimationFrame(function(){e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.opacity=""},n+10)});else if(r==="slide"){const i=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight=e.scrollHeight+"px",e.style.transition="max-height "+n+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight="0",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else r==="scale"&&(e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",e.style.transform="scale(1)",e.style.opacity="1",requestAnimationFrame(function(){e.style.transform="scale(0.95)",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}function H(e,r,t){const n=T(r,t);if(e.classList.remove("fb-field-hidden"),r==="fade")e.style.opacity="0",e.style.transition="opacity "+n+"ms ease",requestAnimationFrame(function(){e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.opacity=""},n+10)});else if(r==="slide"){const i=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight="0",e.style.opacity="0",e.style.transition="max-height "+n+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight=e.scrollHeight+"px",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else r==="scale"&&(e.style.transform="scale(0.95)",e.style.opacity="0",e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",requestAnimationFrame(function(){e.style.transform="scale(1)",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}const N={banner:"state","disable-fields":"state","hide-fields":"state","require-fields":"state","block-submit":"state","end-form":"state",toast:"event",celebrate:"event","goto-step":"event",redirect:"event","run-action":"event",notify:"event"},P=["block-submit","hide-fields","disable-fields","require-fields","end-form","redirect","run-action","notify"];function k(e){return!!e&&!!e.name&&e.type!=="page-break"&&e.type!=="spacer"}function b(e,r,t){const n=(t||[]).filter(k),i=e.target||(e.fields&&e.fields.length?"listed":"rest");if(i==="listed"){const a=new Set(e.fields||[]);return n.filter(o=>a.has(o.name)&&o.name!==r.name).map(o=>o.name)}if(i==="all")return n.filter(a=>a.name!==r.name).map(a=>a.name);const s=n.findIndex(a=>a.name===r.name);return s===-1?[]:n.slice(s+1).map(a=>a.name)}function w(e,r){for(const t of r)e.includes(t)||e.push(t)}function z(e,r,t,n,i){if(!r||!r.type)return;const s=N[r.type];if(s){if(s==="event"){e.events.push({key:i,type:r.type,field:t.name,action:r});return}switch(r.type){case"banner":e.banners.push({key:i,field:t.name,message:r.message||"",level:r.level||"info",position:r.position||"field-below",dismissible:r.dismissible!==!1});break;case"hide-fields":w(e.hidden,b(r,t,n));break;case"disable-fields":w(e.disabled,b(r,t,n));break;case"require-fields":w(e.required,b(r,t,n));break;case"block-submit":e.blocks.push({key:i,message:r.message||"This form cannot be submitted yet."});break;case"end-form":w(e.masked,b({target:"rest"},t,n)),e.ends.push({key:i,field:t.name,message:r.message||"",finishLabel:r.finishLabel||"Finish",doneMessage:r.doneMessage||"",record:r.record!==!1,finish:r.finish!==!1}),r.finish===!1&&e.blocks.push({key:i,quiet:!0,message:r.message||"This form cannot be submitted."});break;default:break}}}function L(e,r){const t={active:[],banners:[],hidden:[],disabled:[],required:[],blocks:[],events:[],masked:[],ends:[]};return(e||[]).forEach(n=>{!k(n)||!Array.isArray(n.triggers)||n.triggers.forEach((i,s)=>{if(!i||i.enabled===!1)return;const a=i.id||`${n.name}::${s}`,o=h(i.when,r),c=`${a}:${o?"then":"else"}`,g=o?i.then:i.otherwise;!Array.isArray(g)||!g.length||(t.active.push(c),g.forEach(f=>z(t,f,n,e,c)))})}),t.required=t.required.filter(n=>!t.hidden.includes(n)&&!t.masked.includes(n)),t}function F(e){return(e||[]).some(r=>Array.isArray(r&&r.triggers)&&r.triggers.length>0)}function u(e,r,t){this._form=e,this._wrapper=r,this._fields=(e.fields||[]).filter(n=>n.type!=="page-break"),this._listeners=[],this._depMap=new Map,this._initialEval=!1,this._baseValues=t&&t.baseValues||null,this._hasTriggers=F(this._fields),this._triggerState=null,this._triggerPrev=new Set,this._triggerFired=new Set,this._triggerApplied={hidden:[],disabled:[],required:[],masked:[]},this._banners=new Map,this._dismissed=new Set,this._end=null}function m(e,r,t){const n=t||"",i=r||"";return n+'[name="'+e+'"]'+i+", "+n+'[name="'+e+'[]"]'+i}function G(e){return String(e||"").replace(/\[\]$/,"")}u.prototype._fieldInputs=function(e){return this._wrapper.querySelectorAll(m(e))},u.prototype._fieldInput=function(e){return this._wrapper.querySelector(m(e))},u.prototype._buildDepMap=function(){this._depMap.clear();const e=this;e._fields.forEach(function(r){const t=r.logic;if(!t)return;function n(i){if(!i)return;(i.all||i.any||[]).forEach(function(a){a.field?(e._depMap.has(a.field)||e._depMap.set(a.field,new Set),e._depMap.get(a.field).add(r.name)):n(a)})}if(t.visibility&&(t.visibility.conditions||[]).forEach(function(i){n(i.when)}),t.requirement&&(t.requirement.conditions||[]).forEach(function(i){n(i.when)}),t.cascade&&t.cascade.sourceField){const i=t.cascade.sourceField;e._depMap.has(i)||e._depMap.set(i,new Set),e._depMap.get(i).add(r.name)}})};function I(e,r){const t={};return(r||[]).forEach(function(n){if(!n||!n.name)return;const i=e.querySelectorAll(m(n.name));if(!i.length)return;const s=i[0];if(s.type==="radio"){const a=e.querySelector(m(n.name,":checked"));t[n.name]=a?a.value:""}else if(s.type==="checkbox")if(i.length>1||/\[\]$/.test(s.name||"")){const a=[];i.forEach(function(o){o.checked&&a.push(o.value)}),t[n.name]=a}else t[n.name]=s.checked?s.value||"true":"";else t[n.name]=s.value}),t}function M(e,r){const t=I(e,r),n=[],i=[];return(r||[]).forEach(function(s){if(!s||!s.name||A(s,t)==="hidden")return;const a=t[s.name],o=Array.isArray(a)?a.length===0:!String(a??"").trim();if(_(s,t)&&o){n.push({name:s.name,label:s.label||s.name});return}if(o)return;const c=x(s,a,t);c.length&&i.push(c[0].message)}),{valid:n.length===0&&i.length===0,missing:n,errors:i,values:t}}u.prototype._getValues=function(){const e=this._baseValues?Object.assign({},this._baseValues):{};return this._fields.forEach(function(r){const t=this._fieldInputs(r.name);if(!t.length)return;const n=t[0];if(n.type==="radio"){const i=this._wrapper.querySelector(m(r.name,":checked"));e[r.name]=i?i.value:""}else if(n.type==="checkbox")if(t.length>1||/\[\]$/.test(n.name||"")){const i=[];t.forEach(function(s){s.checked&&i.push(s.value)}),e[r.name]=i}else e[r.name]=n.checked?n.value||"true":"";else e[r.name]=n.value},this),e},u.prototype._applyVisibility=function(e,r){const n=A(e,r)==="hidden",i=this._findFieldWrapper(e.name);if(!i)return;i.setAttribute("aria-hidden",String(n));const s=this._initialEval?"none":e.logic?.visibility?.transition||"none";if(s==="none"){i.classList.toggle("fb-field-hidden",n);return}const a=e.logic?.visibility?.duration;i.style.transition="",n?D(i,s,a):H(i,s,a)},u.prototype._applyRequirement=function(e,r){const t=_(e,r),n=this._fieldInput(e.name);if(!n)return;if(/\[\]$/.test(n.name||"")||n.closest&&n.closest(".domma-chooser")){const s=this._findFieldWrapper(e.name);n.removeAttribute("required"),s&&(s.setAttribute("aria-required",String(t)),s.classList.toggle("fb-field-required",t));return}t?n.setAttribute("required",""):n.removeAttribute("required");const i=this._findFieldWrapper(e.name);i&&(i.setAttribute("aria-required",String(t)),i.classList.toggle("fb-field-required",t))},u.prototype._applyCascade=function(e,r){const t=C(e,r);if(t===null)return;const n=this._wrapper.querySelector(m(e.name,"","select"));if(!n)return;const i=n.value;n.textContent="",t.forEach(function(s){const a=document.createElement("option");a.value=s.value,a.textContent=s.label||s.value,s.value===i&&(a.selected=!0),n.appendChild(a)})},u.prototype._applyValidation=function(e,r){const t=this._fieldInput(e.name);if(!t)return;const n=x(e,r[e.name],r);let i=t.parentNode.querySelector(".fb-validation-error");n.length?(i||(i=document.createElement("div"),i.className="fb-validation-error",t.parentNode.appendChild(i)),i.textContent=n[0].message):i&&i.remove()},u.prototype._findFieldWrapper=function(e){const r=this._fieldInput(e);if(!r)return null;const t=r.closest("form")||this._wrapper;let n=r;for(;n.parentNode&&n.parentNode!==t;)n=n.parentNode;return n},u.prototype.evaluate=function(e){const r=this._getValues(),t=this;let n;if(e&&this._depMap.has(e)){const i=this._depMap.get(e);n=this._fields.filter(function(s){return i.has(s.name)})}else n=this._fields;n.forEach(function(i){i.logic&&(t._applyVisibility(i,r),t._applyRequirement(i,r),t._applyCascade(i,r))}),this._applyTriggers(r)},u.prototype.init=function(){this._buildDepMap();const e=this;if(this._wrapper.querySelectorAll("input, select, textarea").forEach(function(n){const i=G(n.getAttribute("name"));if(!i)return;const s=function(){e.evaluate(i)};n.addEventListener("input",s),n.addEventListener("change",s),e._listeners.push({el:n,event:"input",fn:s}),e._listeners.push({el:n,event:"change",fn:s});const a=e._fields.find(function(o){return o.name===i});if(a&&a.logic&&Array.isArray(a.logic.validation)&&a.logic.validation.length){const o=function(){const c=e._getValues();e._applyValidation(a,c)};n.addEventListener("blur",o),e._listeners.push({el:n,event:"blur",fn:o})}}),this._wrapper.querySelectorAll("[data-chooser-field]").forEach(function(n){const i=n.getAttribute("data-chooser-field");if(!i)return;const s=function(){setTimeout(function(){e.evaluate(i)},0)};n.addEventListener("click",s),e._listeners.push({el:n,event:"click",fn:s})}),this._hasTriggers){const n=function(i){if(e._end&&!(i.type==="click"&&!(i.target&&i.target.closest&&i.target.closest(j)))){if(!e._end.spec.finish){i.preventDefault(),i.stopImmediatePropagation();return}e._wrapper.dataset.triggerBlocked!=="true"&&(i.preventDefault(),i.stopImmediatePropagation(),e._finishEnded())}};this._wrapper.addEventListener("click",n,!0),this._wrapper.addEventListener("submit",n,!0),this._listeners.push({el:this._wrapper,event:"click",fn:n,capture:!0}),this._listeners.push({el:this._wrapper,event:"submit",fn:n,capture:!0})}this._initialEval=!0,this.evaluate(),this._initialEval=!1},u.prototype.destroy=function(){this._listeners.forEach(function(e){e.el.removeEventListener(e.event,e.fn,!!e.capture)}),this._listeners=[],this._banners.forEach(function(e){e&&e.parentNode&&e.parentNode.removeChild(e)}),this._banners.clear(),this._syncEnd([])},u.prototype._applyTriggers=function(e){if(!this._hasTriggers)return;const r=L(this._fields,e);this._triggerState=r;const t=this._triggerApplied||{hidden:[],disabled:[],required:[]};t.hidden.forEach(n=>{r.hidden.indexOf(n)===-1&&this._setTriggerHidden(n,!1)}),r.hidden.forEach(n=>this._setTriggerHidden(n,!0)),t.disabled.forEach(n=>{r.disabled.indexOf(n)===-1&&this._setTriggerDisabled(n,!1)}),r.disabled.forEach(n=>this._setTriggerDisabled(n,!0)),t.required.forEach(n=>{r.required.indexOf(n)===-1&&this._restoreRequired(n,e)}),r.required.forEach(n=>this._setTriggerRequired(n,!0)),(t.masked||[]).forEach(n=>{r.masked.indexOf(n)===-1&&this._setTriggerMasked(n,!1)}),r.masked.forEach(n=>this._setTriggerMasked(n,!0)),this._triggerApplied={hidden:r.hidden.slice(),disabled:r.disabled.slice(),required:r.required.slice(),masked:r.masked.slice()},this._syncBanners(r.banners),this._syncSubmitBlock(r.blocks),this._syncEnd(r.ends),this._fireTriggerEvents(r)},u.prototype._setTriggerHidden=function(e,r){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-hidden",r),t.setAttribute("aria-hidden",String(r||t.classList.contains("fb-field-hidden"))))},u.prototype._setTriggerMasked=function(e,r){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-masked",r),t.setAttribute("aria-hidden",String(r||t.classList.contains("fb-field-hidden")||t.classList.contains("fb-field-trigger-hidden"))))},u.prototype._setTriggerDisabled=function(e,r){const t=this._findFieldWrapper(e);t&&t.classList.toggle("fb-field-disabled",r),this._fieldInputs(e).forEach(function(i){i.disabled=r});const n=this._wrapper.querySelector('[data-chooser-field="'+e+'"]');n&&(n.classList.toggle("fb-field-disabled",r),n.querySelectorAll('button, [role="radio"], [role="checkbox"]').forEach(function(i){r?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}))},u.prototype._setTriggerRequired=function(e,r){const t=this._fieldInput(e),n=this._findFieldWrapper(e);if(n&&(n.setAttribute("aria-required",String(r)),n.classList.toggle("fb-field-required",r)),!!t){if(/\[\]$/.test(t.name||"")||t.closest&&t.closest(".domma-chooser")){t.removeAttribute("required");return}r?t.setAttribute("required",""):t.removeAttribute("required")}},u.prototype._restoreRequired=function(e,r){const t=this._fields.find(function(n){return n.name===e});t&&this._setTriggerRequired(e,_(t,r))},u.prototype._syncBanners=function(e){const r=new Map;e.forEach(function(t){r.set(t.key,t)}),this._banners.forEach(function(t,n){r.has(n)||(t.parentNode&&t.parentNode.removeChild(t),this._banners.delete(n))},this),this._dismissed.forEach(function(t){r.has(t)||this._dismissed.delete(t)},this),r.forEach(function(t,n){if(this._banners.has(n)){const a=this._banners.get(n).querySelector(".fb-trigger-banner-text");a&&a.textContent!==t.message&&(a.textContent=t.message);return}if(this._dismissed.has(n))return;const i=this._buildBanner(t,n);i&&this._banners.set(n,i)},this)},u.prototype._buildBanner=function(e,r){if(typeof document>"u"||!e.message)return null;const t=document.createElement("div");t.className="fb-trigger-banner fb-trigger-banner--"+(e.level||"info"),t.setAttribute("role",e.level==="error"?"alert":"status"),t.dataset.triggerKey=r;const n=document.createElement("span");if(n.className="fb-trigger-banner-text",n.textContent=e.message,t.appendChild(n),e.dismissible){const a=document.createElement("button");a.type="button",a.className="fb-trigger-banner-close",a.setAttribute("aria-label","Dismiss"),a.textContent="\xD7";const o=this;a.addEventListener("click",function(){o._dismissed.add(r),t.parentNode&&t.parentNode.removeChild(t),o._banners.delete(r)}),t.appendChild(a)}const i=this._findFieldWrapper(e.field),s=this._wrapper.querySelector("form")||this._wrapper;return e.position==="form-top"?s.insertBefore(t,s.firstChild):i&&i.parentNode?e.position==="field-above"?i.parentNode.insertBefore(t,i):i.parentNode.insertBefore(t,i.nextSibling):s.appendChild(t),t},u.prototype._syncSubmitBlock=function(e){const r=this._wrapper.querySelector("form")||this._wrapper,t=r.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit'),n=e.length>0;this._wrapper.dataset.triggerBlocked=n?"true":"false",t&&(t.disabled=n,t.setAttribute("aria-disabled",String(n)));let i=r.querySelector(".fb-trigger-block-note");const s=e.filter(function(a){return!a.quiet}).map(function(a){return a.message});if(!s.length){i&&i.parentNode&&i.parentNode.removeChild(i);return}i||(i=document.createElement("p"),i.className="fb-trigger-block-note",i.setAttribute("role","status"),(t&&t.parentNode?t.parentNode:r).appendChild(i)),i.textContent=s.join(" ")};const j='button[type="submit"], input[type="submit"], .fb-form-submit, .wizard-next, .wizard-submit';u.prototype._primaryButton=function(){return this._wrapper.querySelector(".wizard-next, .wizard-submit")||this._wrapper.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit')};function R(e){return!!(e.matches&&e.matches(".wizard-next, .wizard-submit"))}function B(e,r){e.tagName==="INPUT"?e.value=r:e.textContent=r}return u.prototype._syncEnd=function(e){const r=e&&e.length?e[0]:null,t=this._end;if(t&&(!r||t.spec.key!==r.key)&&(t.panel&&t.panel.parentNode&&t.panel.parentNode.removeChild(t.panel),t.button&&t.button.isConnected&&(t.spec.finish?B(t.button,t.label):R(t.button)&&(t.button.disabled=!1)),this._end=null),this._wrapper.dataset.triggerEnded=r?"true":"false",!r||typeof document>"u")return;if(this._end){const a=this._end.panel&&this._end.panel.querySelector(".fb-trigger-end-text");a&&a.textContent!==r.message&&(a.textContent=r.message),this._end.spec=r;return}let n=null;if(r.message){n=document.createElement("div"),n.className="fb-trigger-end",n.setAttribute("role","status"),n.dataset.triggerKey=r.key;const a=document.createElement("div");a.className="fb-trigger-end-text",a.textContent=r.message,n.appendChild(a);const o=this._findFieldWrapper(r.field);o&&o.parentNode?o.parentNode.insertBefore(n,o.nextSibling):(this._wrapper.querySelector("form")||this._wrapper).appendChild(n)}const i=this._primaryButton();let s="";i&&r.finish?(s=i.tagName==="INPUT"?i.value:i.textContent,B(i,r.finishLabel)):i&&R(i)&&(i.disabled=!0),this._end={spec:r,panel:n,button:i,label:s}},u.prototype._finishEnded=function(){const e=this._triggerState||{},r=new Set([].concat(e.masked||[],e.hidden||[],e.disabled||[])),t=this,n=this._fields.filter(function(a){return k(a)&&!r.has(a.name)&&t._fieldInput(a.name)}),i=this._getValues();(e.masked||[]).forEach(function(a){delete i[a]});const s=M(this._wrapper,n);(e.required||[]).forEach(function(a){const o=n.find(function(f){return f.name===a});if(!o)return;const c=i[a];(Array.isArray(c)?c.length===0:!String(c??"").trim())&&!s.missing.some(function(f){return f.name===a})&&(s.missing.push({name:a,label:o.label||a}),s.valid=!1)}),this._wrapper.dispatchEvent(new CustomEvent("dm-form-end",{bubbles:!0,detail:{values:i,validation:s,end:this._end?this._end.spec:null}}))},u.prototype._fireTriggerEvents=function(e){const r=new Set(e.active),t=this;e.events.forEach(function(n){t._triggerPrev.has(n.key)||n.action.once&&t._triggerFired.has(n.key)||t._initialEval&&n.action.onLoad!==!0||(t._triggerFired.add(n.key),t._runTriggerEvent(n))}),this._triggerPrev=r},u.prototype._runTriggerEvent=function(e){const r=e.action;switch(r.type){case"toast":typeof E<"u"&&E&&typeof E.toast=="function"&&E.toast(r.message||"",{type:r.level||"info",duration:Number(r.duration)||4e3});break;case"celebrate":this._celebrate(r);break;default:break}typeof CustomEvent<"u"&&this._wrapper.dispatchEvent&&this._wrapper.dispatchEvent(new CustomEvent("dm-form-trigger",{bubbles:!0,detail:{type:r.type,field:e.field,action:r}}))},u.prototype._celebrate=function(e){typeof window>"u"||typeof document>"u"||window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches||import("/public/js/celebrations/index.js").then(function(r){const t=r.CelebrationsEffect;let n=e.theme||"auto";if(n==="auto"&&(n=t.getCurrentTheme(),!n))return;const i=new t({theme:n,intensity:e.intensity||"medium",enabled:!0,zIndex:999});i.init().then(function(){i.start()})}).catch(function(){})},u.prototype.submitBlock=function(){const e=this._triggerState&&this._triggerState.blocks||[];return{blocked:e.length>0,reasons:e.map(function(r){return r.message})}},{evaluateCondition:y,evaluateConditionGroup:h,evaluateFieldVisibility:A,evaluateFieldRequirement:_,validateField:x,collectValues:I,validateFields:M,getCascadeOptions:C,resolveTriggerState:L,resolveTriggerTargets:b,hasTriggers:F,TRIGGER_ACTION_KINDS:N,SERVER_TRIGGER_ACTIONS:P,FormLogicRuntime:u}});
|
|
1
|
+
(function(l,d){typeof module<"u"&&module.exports?module.exports=d():l.FormLogicEngine=d()})(typeof globalThis<"u"?globalThis:this,function(){"use strict";function l(e){return e===""||e===null||e===void 0?"":String(e)}function d(e){const r=parseFloat(e);return isNaN(r)?null:r}function p(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}const P={not_equals:"equals",not_contains:"contains",not_in:"in"};function q(e,r){const t=l(r),n=l(e.value);switch(e.operator){case"equals":return t===n;case"not_equals":return t!==n;case"contains":return t.toLowerCase().includes(n.toLowerCase());case"not_contains":return!t.toLowerCase().includes(n.toLowerCase());case"starts_with":return t.toLowerCase().startsWith(n.toLowerCase());case"ends_with":return t.toLowerCase().endsWith(n.toLowerCase());case"greater_than":{const i=d(r),s=d(e.value);return i!==null&&s!==null&&i>s}case"less_than":{const i=d(r),s=d(e.value);return i!==null&&s!==null&&i<s}case"is_empty":return p(r);case"is_not_empty":return!p(r);case"in":return(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"not_in":return!(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"matches_regex":try{const i=e.flags||"";return new RegExp(e.value,i).test(t)}catch(i){return typeof console<"u"&&console.warn&&console.warn('[FormLogicEngine] Invalid regex in condition for field "'+e.field+'": '+e.value,i&&i.message),!1}default:return!0}}function _(e,r){if(!e||!e.field||!e.operator)return!0;const t=r[e.field];if(!Array.isArray(t))return q(e,t);if(e.operator==="is_empty")return p(t);if(e.operator==="is_not_empty")return!p(t);const n=P[e.operator];if(n){const i=Object.assign({},e,{operator:n});return!t.some(s=>q(i,s))}return t.some(i=>q(e,i))}function h(e,r){return e?Array.isArray(e.all)?e.all.every(t=>t.all||t.any?h(t,r):_(t,r)):Array.isArray(e.any)?e.any.some(t=>t.all||t.any?h(t,r):_(t,r)):e.field&&e.operator?_(e,r):!0:!0}function v(e,r){const t=e.logic;if(!t||!t.visibility)return"visible";const n=t.visibility,i=n.conditions||[];for(const s of i)if(s.when&&h(s.when,r))return s.then==="hidden"?"hidden":"visible";return n.default==="hidden"?"hidden":"visible"}function w(e,r){const t=e.logic;if(!t||!t.requirement)return e.required||!1;const n=t.requirement,i=n.conditions||[];for(const s of i)if(s.when&&h(s.when,r))return s.then===!0;return n.default===!0}function A(e){if(e==null||e==="")return null;const r=Number(e);return Number.isFinite(r)?r:null}function z(e,r){if(p(r)||Array.isArray(r)||typeof r=="object")return[];const t=e.label||e.name;if(e.type==="number"){const a=Number(r);if(!Number.isFinite(a)||String(r).trim()==="")return[{message:`${t} must be a number.`}];const o=A(e.min),u=A(e.max);return o!==null&&a<o?[{message:`${t} must be at least ${o}.`}]:u!==null&&a>u?[{message:`${t} must be no more than ${u}.`}]:[]}const n=String(r).length,i=A(e.minLength),s=A(e.maxLength);return i!==null&&n<i?[{message:`${t} must be at least ${i} characters.`}]:s!==null&&n>s?[{message:`${t} must be no more than ${s} characters.`}]:[]}function k(e,r,t){const n=z(e,r),i=e.logic;if(!i||!Array.isArray(i.validation))return n;for(const s of i.validation)switch(s.type){case"regex":{if(!p(r))try{new RegExp(s.pattern||"",s.flags||"").test(l(r))||n.push({message:s.message||"Invalid format."})}catch{}break}case"match":{const a=t[s.field];!p(r)&&l(r)!==l(a)&&n.push({message:s.message||"Fields do not match."});break}}return n}function T(e,r){const t=e.logic;if(!t||!t.cascade||!t.cascade.sourceField)return null;const n=t.cascade,i=n.mapping||{},s=r[n.sourceField];if(Array.isArray(s)){const o=new Set,u=[];return s.forEach(function(g){const f=l(g);Object.prototype.hasOwnProperty.call(i,f)&&(i[f]||[]).forEach(function(x){const H=l(x&&x.value);o.has(H)||(o.add(H),u.push(x))})}),u.length?u:n.defaultOptions||null}const a=l(s);return Object.prototype.hasOwnProperty.call(i,a)?i[a]||[]:n.defaultOptions||null}const j={fade:250,slide:300,scale:200};function N(e,r){const t=Number(r);return!isFinite(t)||t<0||t>5e3?j[e]||250:Math.round(t)}function G(e,r,t){const n=N(r,t);if(r==="fade")e.style.transition="opacity "+n+"ms ease",e.style.opacity="1",requestAnimationFrame(function(){e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.opacity=""},n+10)});else if(r==="slide"){const i=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight=e.scrollHeight+"px",e.style.transition="max-height "+n+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight="0",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else r==="scale"&&(e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",e.style.transform="scale(1)",e.style.opacity="1",requestAnimationFrame(function(){e.style.transform="scale(0.95)",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}function K(e,r,t){const n=N(r,t);if(e.classList.remove("fb-field-hidden"),r==="fade")e.style.opacity="0",e.style.transition="opacity "+n+"ms ease",requestAnimationFrame(function(){e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.opacity=""},n+10)});else if(r==="slide"){const i=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight="0",e.style.opacity="0",e.style.transition="max-height "+n+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight=e.scrollHeight+"px",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else r==="scale"&&(e.style.transform="scale(0.95)",e.style.opacity="0",e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",requestAnimationFrame(function(){e.style.transform="scale(1)",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}const C={banner:"state","disable-fields":"state","hide-fields":"state","require-fields":"state","block-submit":"state","end-form":"state",toast:"event",celebrate:"event","goto-step":"event",redirect:"event","run-action":"event",notify:"event"},U=["block-submit","hide-fields","disable-fields","require-fields","end-form","redirect","run-action","notify"];function y(e){return!!e&&!!e.name&&e.type!=="page-break"&&e.type!=="spacer"}function b(e,r,t){const n=(t||[]).filter(y),i=e.target||(e.fields&&e.fields.length?"listed":"rest");if(i==="listed"){const a=new Set(e.fields||[]);return n.filter(o=>a.has(o.name)&&o.name!==r.name).map(o=>o.name)}if(i==="all")return n.filter(a=>a.name!==r.name).map(a=>a.name);const s=n.findIndex(a=>a.name===r.name);return s===-1?[]:n.slice(s+1).map(a=>a.name)}function S(e,r){for(const t of r)e.includes(t)||e.push(t)}function Y(e,r,t,n,i){if(!r||!r.type)return;const s=C[r.type];if(s){if(s==="event"){e.events.push({key:i,type:r.type,field:t.name,action:r});return}switch(r.type){case"banner":e.banners.push({key:i,field:t.name,message:r.message||"",level:r.level||"info",position:r.position||"field-below",dismissible:r.dismissible!==!1});break;case"hide-fields":S(e.hidden,b(r,t,n));break;case"disable-fields":S(e.disabled,b(r,t,n));break;case"require-fields":S(e.required,b(r,t,n));break;case"block-submit":e.blocks.push({key:i,message:r.message||"This form cannot be submitted yet."});break;case"end-form":S(e.masked,b({target:"rest"},t,n)),e.ends.push({key:i,field:t.name,message:r.message||"",finishLabel:r.finishLabel||"Finish",doneMessage:r.doneMessage||"",record:r.record!==!1,finish:r.finish!==!1}),r.finish===!1&&e.blocks.push({key:i,quiet:!0,message:r.message||"This form cannot be submitted."});break;default:break}}}const L=/\{\{\s*([A-Za-z_][A-Za-z0-9_-]*)\s*\}\}/g,X=["password","file","image","signature"],F=200,Z={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" ",pound:"\xA3",euro:"\u20AC",copy:"\xA9",reg:"\xAE"};function I(e){return String(e).replace(/<[^>]*>/g,"").replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi,(r,t)=>{if(t[0]==="#"){const n=t[1]==="x"||t[1]==="X"?parseInt(t.slice(2),16):parseInt(t.slice(1),10);return Number.isFinite(n)&&n>31?String.fromCodePoint(n):" "}return Z[t.toLowerCase()]??r})}function M(e,r){const t=Array.isArray(e.options)?e.options:[];for(const n of t)if(n&&typeof n=="object"){if(String(n.value)===String(r))return I(n.label!=null?n.label:n.value)}else if(String(n)===String(r))return I(n);return String(r)}function J(e,r){if(!e||X.includes(e.type)||r==null||r===!1)return"";let t;return Array.isArray(r)?t=r.filter(n=>n!==""&&n!=null).map(n=>M(e,n)).join(", "):e.type==="checkbox"?t=r&&r!=="false"?"Yes":"":Array.isArray(e.options)&&e.options.length?t=r===""?"":M(e,r):t=String(r),t=t.replace(/[\u0000-\u001f\u007f]+/g," ").replace(/\s+/g," ").trim(),t.length>F?t.slice(0,F-1).trimEnd()+"\u2026":t}function O(e,r,t,n){if(!e||typeof e!="string"||e.indexOf("{{")===-1)return e||"";const i=n instanceof Set?n:new Set(n||[]),s=new Map((r||[]).filter(y).map(a=>[a.name,a]));return e.replace(L,(a,o)=>{const u=s.get(o);return!u||i.has(o)?"":J(u,(t||{})[o])})}function Q(e){const r=[];return String(e||"").replace(L,(t,n)=>(r.includes(n)||r.push(n),t)),r}function ee(e,r,t){const n=new Set([...e.hidden,...e.disabled,...e.masked]);(r||[]).forEach(s=>{y(s)&&v(s,t)==="hidden"&&n.add(s.name)});const i=s=>O(s,r,t,n);e.banners.forEach(s=>{s.message=i(s.message)}),e.blocks.forEach(s=>{s.message=i(s.message)}),e.ends.forEach(s=>{s.message=i(s.message),s.doneMessage=i(s.doneMessage)}),e.events.forEach(s=>{s.type==="toast"&&s.action&&s.action.message&&(s.action=Object.assign({},s.action,{message:i(s.action.message)}))})}function R(e,r){const t={active:[],banners:[],hidden:[],disabled:[],required:[],blocks:[],events:[],masked:[],ends:[]};return(e||[]).forEach(n=>{!y(n)||!Array.isArray(n.triggers)||n.triggers.forEach((i,s)=>{if(!i||i.enabled===!1)return;const a=i.id||`${n.name}::${s}`,o=h(i.when,r),u=`${a}:${o?"then":"else"}`,g=o?i.then:i.otherwise;!Array.isArray(g)||!g.length||(t.active.push(u),g.forEach(f=>Y(t,f,n,e,u)))})}),t.required=t.required.filter(n=>!t.hidden.includes(n)&&!t.masked.includes(n)),ee(t,e,r||{}),t}function B(e){return(e||[]).some(r=>Array.isArray(r&&r.triggers)&&r.triggers.length>0)}function c(e,r,t){this._form=e,this._wrapper=r,this._fields=(e.fields||[]).filter(n=>n.type!=="page-break"),this._listeners=[],this._depMap=new Map,this._initialEval=!1,this._baseValues=t&&t.baseValues||null,this._hasTriggers=B(this._fields),this._triggerState=null,this._triggerPrev=new Set,this._triggerFired=new Set,this._triggerApplied={hidden:[],disabled:[],required:[],masked:[]},this._banners=new Map,this._dismissed=new Set,this._end=null}function m(e,r,t){const n=t||"",i=r||"";return n+'[name="'+e+'"]'+i+", "+n+'[name="'+e+'[]"]'+i}function te(e){return String(e||"").replace(/\[\]$/,"")}c.prototype._fieldInputs=function(e){return this._wrapper.querySelectorAll(m(e))},c.prototype._fieldInput=function(e){return this._wrapper.querySelector(m(e))},c.prototype._buildDepMap=function(){this._depMap.clear();const e=this;e._fields.forEach(function(r){const t=r.logic;if(!t)return;function n(i){if(!i)return;(i.all||i.any||[]).forEach(function(a){a.field?(e._depMap.has(a.field)||e._depMap.set(a.field,new Set),e._depMap.get(a.field).add(r.name)):n(a)})}if(t.visibility&&(t.visibility.conditions||[]).forEach(function(i){n(i.when)}),t.requirement&&(t.requirement.conditions||[]).forEach(function(i){n(i.when)}),t.cascade&&t.cascade.sourceField){const i=t.cascade.sourceField;e._depMap.has(i)||e._depMap.set(i,new Set),e._depMap.get(i).add(r.name)}})};function $(e,r){const t={};return(r||[]).forEach(function(n){if(!n||!n.name)return;const i=e.querySelectorAll(m(n.name));if(!i.length)return;const s=i[0];if(s.type==="radio"){const a=e.querySelector(m(n.name,":checked"));t[n.name]=a?a.value:""}else if(s.type==="checkbox")if(i.length>1||/\[\]$/.test(s.name||"")){const a=[];i.forEach(function(o){o.checked&&a.push(o.value)}),t[n.name]=a}else t[n.name]=s.checked?s.value||"true":"";else t[n.name]=s.value}),t}function V(e,r){const t=$(e,r),n=[],i=[];return(r||[]).forEach(function(s){if(!s||!s.name||v(s,t)==="hidden")return;const a=t[s.name],o=Array.isArray(a)?a.length===0:!String(a??"").trim();if(w(s,t)&&o){n.push({name:s.name,label:s.label||s.name});return}if(o)return;const u=k(s,a,t);u.length&&i.push(u[0].message)}),{valid:n.length===0&&i.length===0,missing:n,errors:i,values:t}}c.prototype._getValues=function(){const e=this._baseValues?Object.assign({},this._baseValues):{};return this._fields.forEach(function(r){const t=this._fieldInputs(r.name);if(!t.length)return;const n=t[0];if(n.type==="radio"){const i=this._wrapper.querySelector(m(r.name,":checked"));e[r.name]=i?i.value:""}else if(n.type==="checkbox")if(t.length>1||/\[\]$/.test(n.name||"")){const i=[];t.forEach(function(s){s.checked&&i.push(s.value)}),e[r.name]=i}else e[r.name]=n.checked?n.value||"true":"";else e[r.name]=n.value},this),e},c.prototype._applyVisibility=function(e,r){const n=v(e,r)==="hidden",i=this._findFieldWrapper(e.name);if(!i)return;i.setAttribute("aria-hidden",String(n));const s=this._initialEval?"none":e.logic?.visibility?.transition||"none";if(s==="none"){i.classList.toggle("fb-field-hidden",n);return}const a=e.logic?.visibility?.duration;i.style.transition="",n?G(i,s,a):K(i,s,a)},c.prototype._applyRequirement=function(e,r){const t=w(e,r),n=this._fieldInput(e.name);if(!n)return;if(/\[\]$/.test(n.name||"")||n.closest&&n.closest(".domma-chooser")){const s=this._findFieldWrapper(e.name);n.removeAttribute("required"),s&&(s.setAttribute("aria-required",String(t)),s.classList.toggle("fb-field-required",t));return}t?n.setAttribute("required",""):n.removeAttribute("required");const i=this._findFieldWrapper(e.name);i&&(i.setAttribute("aria-required",String(t)),i.classList.toggle("fb-field-required",t))},c.prototype._applyCascade=function(e,r){const t=T(e,r);if(t===null)return;const n=this._wrapper.querySelector(m(e.name,"","select"));if(!n)return;const i=n.value;n.textContent="",t.forEach(function(s){const a=document.createElement("option");a.value=s.value,a.textContent=s.label||s.value,s.value===i&&(a.selected=!0),n.appendChild(a)})},c.prototype._applyValidation=function(e,r){const t=this._fieldInput(e.name);if(!t)return;const n=k(e,r[e.name],r);let i=t.parentNode.querySelector(".fb-validation-error");n.length?(i||(i=document.createElement("div"),i.className="fb-validation-error",t.parentNode.appendChild(i)),i.textContent=n[0].message):i&&i.remove()},c.prototype._findFieldWrapper=function(e){const r=this._fieldInput(e);if(!r)return null;const t=r.closest("form")||this._wrapper;let n=r;for(;n.parentNode&&n.parentNode!==t;)n=n.parentNode;return n},c.prototype.evaluate=function(e){const r=this._getValues(),t=this;let n;if(e&&this._depMap.has(e)){const i=this._depMap.get(e);n=this._fields.filter(function(s){return i.has(s.name)})}else n=this._fields;n.forEach(function(i){i.logic&&(t._applyVisibility(i,r),t._applyRequirement(i,r),t._applyCascade(i,r))}),this._applyTriggers(r)},c.prototype.init=function(){this._buildDepMap();const e=this;if(this._wrapper.querySelectorAll("input, select, textarea").forEach(function(n){const i=te(n.getAttribute("name"));if(!i)return;const s=function(){e.evaluate(i)};n.addEventListener("input",s),n.addEventListener("change",s),e._listeners.push({el:n,event:"input",fn:s}),e._listeners.push({el:n,event:"change",fn:s});const a=e._fields.find(function(o){return o.name===i});if(a&&a.logic&&Array.isArray(a.logic.validation)&&a.logic.validation.length){const o=function(){const u=e._getValues();e._applyValidation(a,u)};n.addEventListener("blur",o),e._listeners.push({el:n,event:"blur",fn:o})}}),this._wrapper.querySelectorAll("[data-chooser-field]").forEach(function(n){const i=n.getAttribute("data-chooser-field");if(!i)return;const s=function(){setTimeout(function(){e.evaluate(i)},0)};n.addEventListener("click",s),e._listeners.push({el:n,event:"click",fn:s})}),this._hasTriggers){const n=function(i){if(e._end&&!(i.type==="click"&&!(i.target&&i.target.closest&&i.target.closest(ne)))){if(!e._end.spec.finish){i.preventDefault(),i.stopImmediatePropagation();return}e._wrapper.dataset.triggerBlocked!=="true"&&(i.preventDefault(),i.stopImmediatePropagation(),e._finishEnded())}};this._wrapper.addEventListener("click",n,!0),this._wrapper.addEventListener("submit",n,!0),this._listeners.push({el:this._wrapper,event:"click",fn:n,capture:!0}),this._listeners.push({el:this._wrapper,event:"submit",fn:n,capture:!0})}this._initialEval=!0,this.evaluate(),this._initialEval=!1},c.prototype.destroy=function(){this._listeners.forEach(function(e){e.el.removeEventListener(e.event,e.fn,!!e.capture)}),this._listeners=[],this._banners.forEach(function(e){e&&e.parentNode&&e.parentNode.removeChild(e)}),this._banners.clear(),this._syncEnd([])},c.prototype._applyTriggers=function(e){if(!this._hasTriggers)return;const r=R(this._fields,e);this._triggerState=r;const t=this._triggerApplied||{hidden:[],disabled:[],required:[]};t.hidden.forEach(n=>{r.hidden.indexOf(n)===-1&&this._setTriggerHidden(n,!1)}),r.hidden.forEach(n=>this._setTriggerHidden(n,!0)),t.disabled.forEach(n=>{r.disabled.indexOf(n)===-1&&this._setTriggerDisabled(n,!1)}),r.disabled.forEach(n=>this._setTriggerDisabled(n,!0)),t.required.forEach(n=>{r.required.indexOf(n)===-1&&this._restoreRequired(n,e)}),r.required.forEach(n=>this._setTriggerRequired(n,!0)),(t.masked||[]).forEach(n=>{r.masked.indexOf(n)===-1&&this._setTriggerMasked(n,!1)}),r.masked.forEach(n=>this._setTriggerMasked(n,!0)),this._triggerApplied={hidden:r.hidden.slice(),disabled:r.disabled.slice(),required:r.required.slice(),masked:r.masked.slice()},this._syncBanners(r.banners),this._syncSubmitBlock(r.blocks),this._syncEnd(r.ends),this._fireTriggerEvents(r)},c.prototype._setTriggerHidden=function(e,r){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-hidden",r),t.setAttribute("aria-hidden",String(r||t.classList.contains("fb-field-hidden"))))},c.prototype._setTriggerMasked=function(e,r){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-masked",r),t.setAttribute("aria-hidden",String(r||t.classList.contains("fb-field-hidden")||t.classList.contains("fb-field-trigger-hidden"))))},c.prototype._setTriggerDisabled=function(e,r){const t=this._findFieldWrapper(e);t&&t.classList.toggle("fb-field-disabled",r),this._fieldInputs(e).forEach(function(i){i.disabled=r});const n=this._wrapper.querySelector('[data-chooser-field="'+e+'"]');n&&(n.classList.toggle("fb-field-disabled",r),n.querySelectorAll('button, [role="radio"], [role="checkbox"]').forEach(function(i){r?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}))},c.prototype._setTriggerRequired=function(e,r){const t=this._fieldInput(e),n=this._findFieldWrapper(e);if(n&&(n.setAttribute("aria-required",String(r)),n.classList.toggle("fb-field-required",r)),!!t){if(/\[\]$/.test(t.name||"")||t.closest&&t.closest(".domma-chooser")){t.removeAttribute("required");return}r?t.setAttribute("required",""):t.removeAttribute("required")}},c.prototype._restoreRequired=function(e,r){const t=this._fields.find(function(n){return n.name===e});t&&this._setTriggerRequired(e,w(t,r))},c.prototype._syncBanners=function(e){const r=new Map;e.forEach(function(t){r.set(t.key,t)}),this._banners.forEach(function(t,n){r.has(n)||(t.parentNode&&t.parentNode.removeChild(t),this._banners.delete(n))},this),this._dismissed.forEach(function(t){r.has(t)||this._dismissed.delete(t)},this),r.forEach(function(t,n){if(this._banners.has(n)){const a=this._banners.get(n).querySelector(".fb-trigger-banner-text");a&&a.textContent!==t.message&&(a.textContent=t.message);return}if(this._dismissed.has(n))return;const i=this._buildBanner(t,n);i&&this._banners.set(n,i)},this)},c.prototype._buildBanner=function(e,r){if(typeof document>"u"||!e.message)return null;const t=document.createElement("div");t.className="fb-trigger-banner fb-trigger-banner--"+(e.level||"info"),t.setAttribute("role",e.level==="error"?"alert":"status"),t.dataset.triggerKey=r;const n=document.createElement("span");if(n.className="fb-trigger-banner-text",n.textContent=e.message,t.appendChild(n),e.dismissible){const a=document.createElement("button");a.type="button",a.className="fb-trigger-banner-close",a.setAttribute("aria-label","Dismiss"),a.textContent="\xD7";const o=this;a.addEventListener("click",function(){o._dismissed.add(r),t.parentNode&&t.parentNode.removeChild(t),o._banners.delete(r)}),t.appendChild(a)}const i=this._findFieldWrapper(e.field),s=this._wrapper.querySelector("form")||this._wrapper;return e.position==="form-top"?s.insertBefore(t,s.firstChild):i&&i.parentNode?e.position==="field-above"?i.parentNode.insertBefore(t,i):i.parentNode.insertBefore(t,i.nextSibling):s.appendChild(t),t},c.prototype._syncSubmitBlock=function(e){const r=this._wrapper.querySelector("form")||this._wrapper,t=r.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit'),n=e.length>0;this._wrapper.dataset.triggerBlocked=n?"true":"false",t&&(t.disabled=n,t.setAttribute("aria-disabled",String(n)));let i=r.querySelector(".fb-trigger-block-note");const s=e.filter(function(a){return!a.quiet}).map(function(a){return a.message});if(!s.length){i&&i.parentNode&&i.parentNode.removeChild(i);return}i||(i=document.createElement("p"),i.className="fb-trigger-block-note",i.setAttribute("role","status"),(t&&t.parentNode?t.parentNode:r).appendChild(i)),i.textContent=s.join(" ")};const ne='button[type="submit"], input[type="submit"], .fb-form-submit, .wizard-next, .wizard-submit';c.prototype._primaryButton=function(){return this._wrapper.querySelector(".wizard-next, .wizard-submit")||this._wrapper.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit')};function W(e){return!!(e.matches&&e.matches(".wizard-next, .wizard-submit"))}function D(e,r){e.tagName==="INPUT"?e.value=r:e.textContent=r}return c.prototype._syncEnd=function(e){const r=e&&e.length?e[0]:null,t=this._end;if(t&&(!r||t.spec.key!==r.key)&&(t.panel&&t.panel.parentNode&&t.panel.parentNode.removeChild(t.panel),t.button&&t.button.isConnected&&(t.spec.finish?D(t.button,t.label):W(t.button)&&(t.button.disabled=!1)),this._end=null),this._wrapper.dataset.triggerEnded=r?"true":"false",!r||typeof document>"u")return;if(this._end){const a=this._end.panel&&this._end.panel.querySelector(".fb-trigger-end-text");a&&a.textContent!==r.message&&(a.textContent=r.message),this._end.spec=r;return}let n=null;if(r.message){n=document.createElement("div"),n.className="fb-trigger-end",n.setAttribute("role","status"),n.dataset.triggerKey=r.key;const a=document.createElement("div");a.className="fb-trigger-end-text",a.textContent=r.message,n.appendChild(a);const o=this._findFieldWrapper(r.field);o&&o.parentNode?o.parentNode.insertBefore(n,o.nextSibling):(this._wrapper.querySelector("form")||this._wrapper).appendChild(n)}const i=this._primaryButton();let s="";i&&r.finish?(s=i.tagName==="INPUT"?i.value:i.textContent,D(i,r.finishLabel)):i&&W(i)&&(i.disabled=!0),this._end={spec:r,panel:n,button:i,label:s}},c.prototype._finishEnded=function(){const e=this._triggerState||{},r=new Set([].concat(e.masked||[],e.hidden||[],e.disabled||[])),t=this,n=this._fields.filter(function(a){return y(a)&&!r.has(a.name)&&t._fieldInput(a.name)}),i=this._getValues();(e.masked||[]).forEach(function(a){delete i[a]});const s=V(this._wrapper,n);(e.required||[]).forEach(function(a){const o=n.find(function(f){return f.name===a});if(!o)return;const u=i[a];(Array.isArray(u)?u.length===0:!String(u??"").trim())&&!s.missing.some(function(f){return f.name===a})&&(s.missing.push({name:a,label:o.label||a}),s.valid=!1)}),this._wrapper.dispatchEvent(new CustomEvent("dm-form-end",{bubbles:!0,detail:{values:i,validation:s,end:this._end?this._end.spec:null}}))},c.prototype._fireTriggerEvents=function(e){const r=new Set(e.active),t=this;e.events.forEach(function(n){t._triggerPrev.has(n.key)||n.action.once&&t._triggerFired.has(n.key)||t._initialEval&&n.action.onLoad!==!0||(t._triggerFired.add(n.key),t._runTriggerEvent(n))}),this._triggerPrev=r},c.prototype._runTriggerEvent=function(e){const r=e.action;switch(r.type){case"toast":typeof E<"u"&&E&&typeof E.toast=="function"&&E.toast(r.message||"",{type:r.level||"info",duration:Number(r.duration)||4e3});break;case"celebrate":this._celebrate(r);break;default:break}typeof CustomEvent<"u"&&this._wrapper.dispatchEvent&&this._wrapper.dispatchEvent(new CustomEvent("dm-form-trigger",{bubbles:!0,detail:{type:r.type,field:e.field,action:r}}))},c.prototype._celebrate=function(e){typeof window>"u"||typeof document>"u"||window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches||import("/public/js/celebrations/index.js").then(function(r){const t=r.CelebrationsEffect;let n=e.theme||"auto";if(n==="auto"&&(n=t.getCurrentTheme(),!n))return;const i=new t({theme:n,intensity:e.intensity||"medium",enabled:!0,zIndex:999});i.init().then(function(){i.start()})}).catch(function(){})},c.prototype.submitBlock=function(){const e=this._triggerState&&this._triggerState.blocks||[];return{blocked:e.length>0,reasons:e.map(function(r){return r.message})}},{evaluateCondition:_,evaluateConditionGroup:h,evaluateFieldVisibility:v,evaluateFieldRequirement:w,validateField:k,collectValues:$,validateFields:V,getCascadeOptions:T,resolveTriggerState:R,resolveTriggerTargets:b,fillTriggerText:O,triggerTokens:Q,hasTriggers:B,TRIGGER_ACTION_KINDS:C,SERVER_TRIGGER_ACTIONS:U,FormLogicRuntime:c}});
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
<script src="/public/js/collection-browser.js?v=20260927-scope-mine"></script>
|
|
151
151
|
|
|
152
152
|
<!-- Core Forms (logic engine must load before renderer) -->
|
|
153
|
-
<script src="/public/js/form-logic-engine.js?v=
|
|
153
|
+
<script src="/public/js/form-logic-engine.js?v=20260927-field-tokens"></script>
|
|
154
154
|
<script src="/public/js/forms.js?v=20260926-form-end" type="module"></script>
|
|
155
155
|
|
|
156
156
|
<!-- Core effects runtime -->
|