domma-cms 0.25.4 → 0.25.6
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/css/admin.css +1 -1
- package/admin/js/templates/actions-list.html +1 -1
- package/admin/js/templates/collection-entries.html +1 -1
- package/admin/js/templates/form-submissions.html +1 -1
- package/admin/js/templates/views-list.html +1 -1
- package/admin/js/views/action-editor.js +1 -1
- package/admin/js/views/actions-list.js +1 -1
- package/admin/js/views/blocks.js +5 -11
- package/admin/js/views/collection-editor.js +4 -4
- package/admin/js/views/collection-entries.js +1 -1
- package/admin/js/views/collections.js +1 -1
- package/admin/js/views/components.js +4 -10
- package/admin/js/views/dashboard/widgets/health-detail.js +1 -1
- package/admin/js/views/form-editor.js +3 -3
- package/admin/js/views/form-submissions.js +1 -1
- package/admin/js/views/forms.js +1 -1
- package/admin/js/views/menu-editor.js +15 -11
- package/admin/js/views/page-editor.js +18 -18
- package/admin/js/views/pages.js +5 -5
- package/admin/js/views/plugins.js +2 -2
- package/admin/js/views/roles.js +3 -3
- package/admin/js/views/users.js +4 -4
- package/admin/js/views/view-editor.js +1 -1
- package/admin/js/views/views-list.js +1 -1
- package/package.json +1 -1
- package/public/css/site.css +1 -1
- package/public/js/site.js +1 -1
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import{api as i}from"../api.js";function c(t){return String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function m(t){for(;t.firstChild;)t.removeChild(t.firstChild)}export const componentsView={templateUrl:"/admin/js/templates/components.html",async onMount(t){u(),await
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
<a href="#/components/edit/${o}" class="btn btn-sm btn-primary">
|
|
5
|
-
<span data-icon="edit"></span> Edit
|
|
6
|
-
</a>
|
|
7
|
-
<button class="btn btn-sm btn-secondary js-export-component" data-name="${o}" title="Export as .dmcomponent.json">
|
|
8
|
-
<span data-icon="download"></span> Export
|
|
9
|
-
</button>
|
|
1
|
+
import{api as i}from"../api.js";function c(t){return String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function m(t){for(;t.firstChild;)t.removeChild(t.firstChild)}export const componentsView={templateUrl:"/admin/js/templates/components.html",async onMount(t){u(),await p(t),g(t),Domma.icons.scan(t.get(0))}};let d=!1;function u(){d||(d=!0,I.register("download",{viewBox:"0 0 24 24",paths:["M12 3v12","M7 10l5 5 5-5","M5 19h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("upload",{viewBox:"0 0 24 24",paths:["M12 21V9","M7 14l5-5 5 5","M5 5h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("plus",{viewBox:"0 0 24 24",paths:["M12 5v14","M5 12h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}))}async function p(t){const r=t.find("#components-table-container").get(0);if(!r)return;let a=[];try{a=await i.components.list()}catch(e){m(r);const n=document.createElement("p");n.className="text-muted",n.textContent=`Failed to load components: ${e.message}`,r.appendChild(n);return}T.create(r,{data:a,emptyMessage:'No components yet. Click "New Component" to create your first one.',columns:[{key:"name",title:"Name",render:(e,n)=>{const o=n.origin==="plugin"?' <span class="badge badge-secondary" title="Provided by a plugin \u2014 read-only">plugin</span>':"";return`<a href="#/components/edit/${c(e)}">${c(e)}.dmc</a>${o}`}},{key:"props",title:"Props",render:e=>{const n=Object.keys(e||{});return n.length===0?'<span class="text-muted">\u2014</span>':n.map(o=>`<span class="badge badge-secondary" style="margin-right:.25rem;">${c(o)}</span>`).join("")}},{key:"size",title:"Size",render:e=>`${e} B`},{key:"updatedAt",title:"Updated",render:e=>e?new Date(e).toLocaleString():"\u2014"},{key:"name",title:"Actions",render:(e,n)=>{const o=c(e),s=n.origin==="plugin"?"":`<button class="btn btn-sm btn-danger js-delete-component" data-name="${o}" data-tooltip="Delete"><span data-icon="trash"></span></button>`;return`<div style="display:flex;gap:.4rem;justify-content:flex-end;">
|
|
2
|
+
<a href="#/components/edit/${o}" class="btn btn-sm btn-ghost" data-tooltip="Edit"><span data-icon="edit"></span></a>
|
|
3
|
+
<button class="btn btn-sm btn-ghost js-export-component" data-name="${o}" data-tooltip="Export as .dmcomponent.json"><span data-icon="download"></span></button>
|
|
10
4
|
${s}
|
|
11
|
-
</div>`}}]}),r.querySelectorAll(".js-delete-component").forEach(e=>{e.addEventListener("click",async()=>{await f(e.dataset.name,t)})}),r.querySelectorAll(".js-export-component").forEach(e=>{e.addEventListener("click",async()=>{try{await i.components.exportBundle(e.dataset.name)}catch(n){E.toast(n.message||"Export failed.",{type:"error"})}})}),Domma.icons.scan(r)}async function f(t,r){if(await E.confirm(`Delete component "${t}"? This cannot be undone.`))try{await i.components.delete(t),E.toast("Component deleted.",{type:"success"}),await
|
|
5
|
+
</div>`}}]}),r.querySelectorAll(".js-delete-component").forEach(e=>{e.addEventListener("click",async()=>{await f(e.dataset.name,t)})}),r.querySelectorAll(".js-export-component").forEach(e=>{e.addEventListener("click",async()=>{try{await i.components.exportBundle(e.dataset.name)}catch(n){E.toast(n.message||"Export failed.",{type:"error"})}})}),Domma.icons.scan(r),r.querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})})}async function f(t,r){if(await E.confirm(`Delete component "${t}"? This cannot be undone.`))try{await i.components.delete(t),E.toast("Component deleted.",{type:"success"}),await p(r)}catch(e){E.toast(e.message||"Failed to delete component.",{type:"error"})}}function g(t){const r=t.find("#import-component-btn").get(0),a=t.find("#import-component-file").get(0);!r||!a||(r.addEventListener("click",()=>a.click()),a.addEventListener("change",async()=>{const e=a.files?.[0];if(!e)return;a.value="";let n;try{const o=await e.text();n=JSON.parse(o)}catch{E.toast("Not a valid .dmcomponent.json file (could not parse JSON).",{type:"error"});return}if(!n||typeof n!="object"||!n.name||typeof n.source!="string"){E.toast("Bundle is missing a name or source field.",{type:"error"});return}try{const o=await i.components.importBundle(n);E.toast(`Imported "${o.name}".`,{type:"success"}),await p(t)}catch(o){if(o.code==="CONFLICT"){if(!await E.confirm(`A component named "${o.name}" already exists. Overwrite it with the imported version?`)){E.toast("Import cancelled.",{type:"info"});return}try{const s=await i.components.importBundle(n,{overwrite:!0});E.toast(`Overwrote "${s.name}".`,{type:"success"}),await p(t)}catch(s){E.toast(s.message||"Import failed.",{type:"error"})}return}E.toast(o.message||"Import failed.",{type:"error"})}}))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const p={ok:"check-circle",warn:"
|
|
1
|
+
const p={ok:"check-circle",warn:"warning",fail:"x-circle"};export default function h(o,s){const r=s?.health?.checks||[],l=o.find('[data-field="rows"]');l.empty();const a=l.get(0);if(a){for(const e of r){const n=document.createElement("div");n.className="dash-spike-row dash-health-pill",n.setAttribute("data-level",e.level);const c=document.createElement("span"),i=document.createElement("span");i.setAttribute("data-icon",p[e.level]||"circle"),c.appendChild(i),c.appendChild(document.createTextNode(" "+(e.label||e.id)));const d=document.createElement("span");if(d.textContent=String(e.value),n.appendChild(c),n.appendChild(d),a.appendChild(n),Array.isArray(e.detail)&&e.detail.length>0){const t=document.createElement("div");t.className="dash-health-detail",t.style.fontSize="12px",t.style.opacity="0.75",t.style.padding="4px 0 8px",t.textContent=e.detail.slice(0,5).join(", "),a.appendChild(t)}}window.Domma&&Domma.icons&&Domma.icons.scan()}}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{apiRequest as z}from"/admin/js/api.js";import{makeIconInput as ie}from"/admin/js/lib/shortcode-modal.js";const G=[{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:"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"}],
|
|
1
|
+
import{apiRequest as z}from"/admin/js/api.js";import{makeIconInput as ie}from"/admin/js/lib/shortcode-modal.js";const G=[{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:"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"}],H=new Set(["select","radio","checkbox-group","chooser"]);let b=[],T=null,O=null,M=null,V={};function W(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function Y(e){return G.find(t=>t.value===e)?.label||e}function K(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function ce(e){const t={...b[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const h=document.getElementById(`fb-pb-label-${e}`),y=document.getElementById(`fb-pb-desc-${e}`);return h&&(t.label=h.value.trim()||t.label),y&&(t.description=y.value.trim()),t}const s=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),o=document.getElementById(`fb-type-${e}`),l=document.getElementById(`fb-required-${e}`),a=document.getElementById(`fb-placeholder-${e}`),i=document.getElementById(`fb-helper-${e}`),c=document.getElementById(`fb-tooltip-${e}`);if(s&&(t.label=s.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),o&&(t.type=o.value||t.type),l&&(t.required=l.checked),a&&(t.placeholder=a.value.trim()),i&&(t.helper=i.value.trim()),c&&(t.tooltip=c.value.trim()),t.type==="chooser"){const h=document.getElementById(`fb-chooser-variant-${e}`)?.value;h&&(t.variant=h);const y=document.getElementById(`fb-chooser-multiple-${e}`);y&&(t.multiple=y.checked);const k=document.getElementById(`fb-chooser-density-${e}`)?.value;k&&(t.density=k);const N=document.getElementById(`fb-chooser-columns-${e}`)?.value;N!==""&&N!==void 0&&(t.columns=parseInt(N,10)||3);const q=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();q?t.accent=q:delete t.accent;const $=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;$?t.accentStyle=$:delete t.accentStyle;const L=document.getElementById(`fb-chooser-glow-${e}`);L&&(t.glow=L.checked);const B=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();B?t.glowColour=B:delete t.glowColour;const S=document.getElementById(`fb-chooser-shadow-${e}`)?.value;S&&S!=="none"?t.shadow=S:delete t.shadow;const I=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();I?t.shadowColour=I:delete t.shadowColour,D(e),b[e]&&Array.isArray(b[e].options)&&(t.options=b[e].options)}else if(H.has(t.type)){const h=document.getElementById(`fb-options-${e}`);h&&(t.options=h.value.split(`
|
|
2
2
|
`).filter(y=>y.trim()).map(y=>{const[k,...N]=y.split(":");return{value:k.trim(),label:N.join(":").trim()||k.trim()}}))}if(t.type==="textarea"){const h=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);h>0&&(t.formConfig={...t.formConfig||{},rows:h})}const r=document.getElementById(`fb-span-${e}`),m=document.getElementById(`fb-fullwidth-${e}`);if(r||m){const h={...t.formConfig||{}};if(m?.checked)h.span="full";else{const y=parseInt(r?.value,10);y>1?h.span=y:delete h.span}t.formConfig=Object.keys(h).length?h:void 0}const p=document.getElementById(`fb-minlength-${e}`)?.value;p&&(t.minLength=parseInt(p,10));const u=document.getElementById(`fb-maxlength-${e}`)?.value;u&&(t.maxLength=parseInt(u,10));const d=document.getElementById(`fb-min-${e}`)?.value;d!==""&&d!==void 0&&(t.min=parseFloat(d));const f=document.getElementById(`fb-max-${e}`)?.value;f!==""&&f!==void 0&&(t.max=parseFloat(f));const g=re(e);return g?t.logic=g:delete t.logic,t}function re(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const s=t.querySelector(".fb-field-logic");if(!s)return;const n={};let o=!1;const l=s.querySelector('[data-logic-section="visibility"]');if(l){const r=l.querySelector(".fb-logic-vis-default"),m=l.querySelector(".fb-logic-vis-transition"),p=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(f=>{const g=f.querySelector(".fb-logic-cond-field"),h=f.querySelector(".fb-logic-cond-op"),y=f.querySelector(".fb-logic-cond-val"),k=f.querySelector(".fb-logic-vis-then");return g?.value?{when:{all:[{field:g.value,operator:h.value,value:y.value}]},then:k.value}:null}).filter(Boolean),u=r?.value||"visible",d=m?.value||"none";(u!=="visible"||p.length>0||d!=="none")&&(n.visibility={default:u,conditions:p},d!=="none"&&(n.visibility.transition=d),o=!0)}const a=s.querySelector('[data-logic-section="requirement"]');if(a){const r=a.querySelector(".fb-logic-req-default"),m=Array.from(a.querySelectorAll(".fb-logic-cond-row")).map(p=>{const u=p.querySelector(".fb-logic-cond-field"),d=p.querySelector(".fb-logic-cond-op"),f=p.querySelector(".fb-logic-cond-val"),g=p.querySelector(".fb-logic-req-then");return u?.value?{when:{all:[{field:u.value,operator:d.value,value:f.value}]},then:g.value==="true"}:null}).filter(Boolean);m.length>0&&(n.requirement={default:r?.checked===!0,conditions:m},o=!0)}const i=s.querySelector('[data-logic-section="validation"]');if(i){const r=Array.from(i.querySelectorAll(".fb-logic-val-rule")).map(m=>{const p=m.querySelector(".fb-logic-val-type"),u=m.querySelector(".fb-logic-val-pattern"),d=m.querySelector(".fb-logic-val-flags"),f=m.querySelector(".fb-logic-val-message");if(!u?.value.trim())return null;const g=p?.value||"regex",h={type:g,message:f?.value.trim()||"Invalid value."};return g==="regex"?(h.pattern=u.value.trim(),d?.value.trim()&&(h.flags=d.value.trim())):h.field=u.value.trim(),h}).filter(Boolean);r.length>0&&(n.validation=r,o=!0)}const c=s.querySelector('[data-logic-section="cascade"]');if(c){const r=c.querySelector(".fb-logic-cascade-source"),m=c.querySelector(".fb-logic-cascade-mapping"),p=c.querySelector(".fb-logic-cascade-defaults"),u=r?.value?.trim();if(u){let d={};try{d=JSON.parse(m?.value||"{}")}catch{}const f=(p?.value||"").split(`
|
|
3
3
|
`).filter(g=>g.trim()).map(g=>{const[h,...y]=g.split(":");return{value:h.trim(),label:y.join(":").trim()||h.trim()}});n.cascade={sourceField:u,mapping:d,defaultOptions:f},o=!0}}return o?n:void 0}function C(){b=b.map((e,t)=>ce(t))}function w(e){const t=e.find("#fields-list").get(0),s=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),b.length===0){s&&(s.style.display="");return}s&&(s.style.display="none"),b.forEach((n,o)=>{const l=n.type==="page-break"?de(n,o,e):n.type==="spacer"?pe(n,o,e):me(n,o,e);t.appendChild(l)})}}function de(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--border-color,#444);border-radius:6px;overflow:hidden;margin-bottom:.5rem;background:var(--card-bg,rgba(255,255,255,.02));";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent="\u2014 Page Break \u2014",l.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:rgba(120,120,120,.2);color:var(--text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const a=document.createElement("span");a.textContent=e.label||"Untitled Step",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted,#888);";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move up",d.textContent="\u2191",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(d)}if(t<b.length-1){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move down",d.textContent="\u2193",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(d)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",c.textContent="\u22EF",c.addEventListener("click",d=>{d.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");r.className="btn btn-xs btn-danger",r.title="Remove page break",r.textContent="\u2715",r.addEventListener("click",async d=>{d.stopPropagation(),await E.confirm("Remove this page break?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=document.createElement("div");m.className="fb-field-body",m.style.cssText="padding:.8rem;border-top:1px dashed var(--border-color,#444);display:none;";const p=v([x("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),x("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),u=p.querySelector(`#fb-pb-label-${t}`);return u&&u.addEventListener("input",()=>{a.textContent=u.value||"Untitled Step"}),m.appendChild(p),n.appendChild(o),n.appendChild(m),n}function pe(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--border-color,#444);border-radius:6px;margin-bottom:.5rem;background:transparent;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const l=document.createElement("div");l.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const a=document.createElement("span");a.textContent="Spacer",a.style.cssText="font-size:.7rem;color:var(--text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const i=document.createElement("div");i.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const c=document.createElement("div");if(c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;",t>0){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move up",m.textContent="\u2191",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),c.appendChild(m)}if(t<b.length-1){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move down",m.textContent="\u2193",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),c.appendChild(m)}const r=document.createElement("button");return r.className="btn btn-xs btn-danger",r.title="Remove spacer",r.textContent="\u2715",r.addEventListener("click",async m=>{m.stopPropagation(),C(),b.splice(t,1),w(s)}),c.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.appendChild(c),n.appendChild(o),n}function me(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--border-color,#333);border-radius:6px;overflow:hidden;margin-bottom:.5rem;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;background:var(--card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=Y(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:var(--primary-soft,rgba(99,102,241,.15));color:var(--primary,#6366f1);white-space:nowrap;flex-shrink:0;";const a=document.createElement("span");a.textContent=e.label||"(unlabelled)",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move up",p.textContent="\u2191",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(p)}if(t<b.length-1){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move down",p.textContent="\u2193",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(p)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",c.textContent="\u22EF",c.addEventListener("click",p=>{p.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");if(r.className="btn btn-xs btn-danger",r.title="Remove field",r.textContent="\u2715",r.addEventListener("click",async p=>{p.stopPropagation(),await E.confirm("Remove this field?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),e.required){const p=document.createElement("span");p.textContent="required",p.style.cssText="font-size:.7rem;color:var(--danger,#ef4444);flex-shrink:0;",o.appendChild(p)}if(K(e.logic)){const p=document.createElement("span");p.textContent="\u26A1",p.title="Has conditional logic",p.style.cssText="font-size:.75rem;color:var(--primary,#6366f1);flex-shrink:0;",o.appendChild(p)}o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=ue(e,t,a);return m.style.display="none",n.appendChild(o),n.appendChild(m),n}function ue(e,t,s){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:.8rem;border-top:1px solid var(--border-color,#333);";const o=v([x("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),x("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),l=v([Te("Type",`fb-type-${t}`,G,e.type||"string"),le("Required",`fb-required-${t}`,e.required||!1)]),a=v([x("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),i=v([x("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),x("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,r=v([x("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),le("Full Width",`fb-fullwidth-${t}`,c==="full")]);r.classList.add("fb-grid-row"),r.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(o),n.appendChild(l),n.appendChild(a),n.appendChild(i),n.appendChild(r);const m=n.querySelector(`#fb-label-${t}`),p=n.querySelector(`#fb-name-${t}`);m&&m.addEventListener("input",()=>{s&&(s.textContent=m.value||"(unlabelled)"),p&&!p.dataset.manuallyEdited&&(p.value=W(m.value))}),p&&p.addEventListener("input",()=>{p.dataset.manuallyEdited="1"});const u=n.querySelector(`#fb-type-${t}`);u&&u.addEventListener("change",()=>{const f=n.closest(".fb-field-card");if(f){const y=f.querySelector("span");y&&(y.textContent=Y(u.value))}const g=n.querySelector(".fb-field-extras");g&&g.remove();const h=Z(u.value,e,t);h&&n.appendChild(h)});const d=Z(e.type,e,t);return d&&n.appendChild(d),n.appendChild(ve(e,t)),n}const fe=[{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"}],Q=new Set(["is_empty","is_not_empty"]);function j(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--text-muted,#888);margin:.6rem 0 .3rem;text-transform:uppercase;letter-spacing:.04em;",t}function P(e,t,s,n,o){const l=document.createElement("div");l.className="fb-logic-cond-row",l.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const a=document.createElement("span");a.textContent="When",a.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cond-field",i.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.field&&(f.selected=!0),i.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;",fe.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.operator&&(f.selected=!0),c.appendChild(f)});const r=document.createElement("input");r.type="text",r.className="form-input fb-logic-cond-val",r.placeholder="value",r.style.cssText="flex:1 1 120px;min-width:100px;",r.value=e?.value||"",e&&Q.has(e.operator)&&(r.style.display="none"),c.addEventListener("change",()=>{r.style.display=Q.has(c.value)?"none":""});const m=document.createElement("span");m.textContent="\u2192",m.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const p=document.createElement("select");p.className=`form-select ${n}`,p.style.cssText="flex:1 1 140px;min-width:120px;",s.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===o&&(f.selected=!0),p.appendChild(f)});const u=document.createElement("button");return u.type="button",u.className="btn btn-xs btn-danger",u.textContent="\u2715",u.style.flexShrink="0",u.addEventListener("click",()=>l.remove()),l.appendChild(a),l.appendChild(i),l.appendChild(c),l.appendChild(r),l.appendChild(m),l.appendChild(p),l.appendChild(u),l}function be(e,t,s){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(j("Visibility"));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const l=document.createElement("span");l.textContent="Default:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const a=document.createElement("select");a.className="form-select fb-logic-vis-default",a.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.default||"visible")&&(f.selected=!0),a.appendChild(f)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-vis-transition",r.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.transition||"none")&&(f.selected=!0),r.appendChild(f)}),i.appendChild(c),i.appendChild(r),n.appendChild(i);const m=document.createElement("div");m.className="fb-logic-vis-rules";const p=s.map(d=>({value:d.name,label:d.label||d.name})),u=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(d=>{const f=(d.when?.all||d.when?.any||[])[0],g=d.then==="hidden"?"hidden":"visible";p.length>0&&m.appendChild(P(f,p,u,"fb-logic-vis-then",g))}),n.appendChild(m),p.length>0){const d=document.createElement("button");d.type="button",d.className="btn btn-xs btn-ghost",d.style.cssText="font-size:.73rem;margin-top:.2rem;",d.textContent="+ Add visibility rule",d.addEventListener("click",()=>m.appendChild(P(null,p,u,"fb-logic-vis-then","visible"))),n.appendChild(d)}return n}function he(e,t,s){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(j("Conditional Requirement"));const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;font-size:.8rem;cursor:pointer;margin-bottom:.4rem;";const l=document.createElement("input");l.type="checkbox",l.className="fb-logic-req-default",l.checked=e.default===!0,o.appendChild(l),o.appendChild(document.createTextNode("Required by default")),n.appendChild(o);const a=document.createElement("div");a.className="fb-logic-req-rules";const i=s.map(r=>({value:r.name,label:r.label||r.name})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(r=>{const m=(r.when?.all||r.when?.any||[])[0],p=r.then===!0?"true":"false";i.length>0&&a.appendChild(P(m,i,c,"fb-logic-req-then",p))}),n.appendChild(a),i.length>0){const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.style.cssText="font-size:.73rem;margin-top:.2rem;",r.textContent="+ Add requirement rule",r.addEventListener("click",()=>a.appendChild(P(null,i,c,"fb-logic-req-then","true"))),n.appendChild(r)}return n}function X(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("select");s.className="form-select fb-logic-val-type",s.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===(e?.type||"regex")&&(c.selected=!0),s.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 o=document.createElement("input");o.type="text",o.className="form-input fb-logic-val-flags",o.placeholder="flags",o.value=e?.flags||"",o.style.cssText="flex:0 0 55px;",e?.type==="match"&&(o.style.display="none"),s.addEventListener("change",()=>{o.style.display=s.value==="match"?"none":"",n.placeholder=s.value==="match"?"field name":"pattern"});const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-message",l.placeholder="Error message",l.value=e?.message||"",l.style.cssText="flex:4;";const a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-danger",a.textContent="\u2715",a.style.flexShrink="0",a.addEventListener("click",()=>t.remove()),t.appendChild(s),t.appendChild(n),t.appendChild(o),t.appendChild(l),t.appendChild(a),t}function ge(e,t,s){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(j("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(a=>o.appendChild(X(a))),n.appendChild(o);const l=document.createElement("button");return l.type="button",l.className="btn btn-xs btn-ghost",l.style.cssText="font-size:.73rem;margin-top:.2rem;",l.textContent="+ Add validation rule",l.addEventListener("click",()=>o.appendChild(X(null))),n.appendChild(l),n}function ye(e,t,s){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(j("Cascade Options"));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const l=document.createElement("span");l.textContent="Source field:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const a=document.createElement("select");a.className="form-select fb-logic-cascade-source",a.style.cssText="flex:1;";const i=document.createElement("option");i.value="",i.textContent="\u2014 none \u2014",a.appendChild(i),s.forEach(u=>{const d=document.createElement("option");d.value=u.name,d.textContent=u.label||u.name,u.name===e.sourceField&&(d.selected=!0),a.appendChild(d)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const c=document.createElement("p");c.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',c.style.cssText="font-size:.73rem;color:var(--text-muted,#888);margin:.3rem 0 .2rem;";const r=document.createElement("textarea");r.className="form-input fb-logic-cascade-mapping",r.rows=4,r.style.cssText="font-family:monospace;",r.placeholder='{"uk": [{"value": "london", "label": "London"}]}',r.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(r);const m=document.createElement("p");m.textContent="Default options (one per line: value:Label)",m.style.cssText="font-size:.73rem;color:var(--text-muted,#888);margin:.3rem 0 .2rem;";const p=document.createElement("textarea");return p.className="form-input fb-logic-cascade-defaults",p.rows=3,p.style.cssText="font-family:monospace;",p.placeholder=`option1:Option 1
|
|
4
4
|
option2:Option 2`,p.value=(e.defaultOptions||[]).map(u=>{const d=typeof u=="string"?u:u.value??"",f=typeof u=="string"?u:u.label??d;return d===f?d:`${d}:${f}`}).join(`
|
|
5
|
-
`),n.appendChild(m),n.appendChild(p),n}function ve(e,t){const s=e.logic||{},n=b.filter((p,u)=>u!==t&&p.type!=="page-break"&&p.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--border-color,#333);padding-top:.5rem;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const a=document.createElement("span");a.style.cssText="font-size:.8rem;font-weight:600;color:var(--text-muted,#888);",a.textContent="\u26A1 Conditional Logic";const i=K(s),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.textContent=i?"\u25BE":"\u25B8";const r=document.createElement("div");r.className="fb-logic-body",r.style.cssText="padding:.25rem 0 .25rem;"+(i?"":"display:none;"),l.addEventListener("click",()=>{const p=r.style.display==="none";r.style.display=p?"":"none",c.textContent=p?"\u25BE":"\u25B8"}),l.appendChild(a),l.appendChild(c),o.appendChild(l),r.appendChild(be(s.visibility||{},t,n)),r.appendChild(he(s.requirement||{},t,n)),r.appendChild(ge(s.validation||[],t,n));const m=document.getElementById(`fb-type-${t}`)?.value||e.type;return V.has(m)&&r.appendChild(ye(s.cascade||{},t,n)),o.appendChild(r),o}function Z(e,t,s){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(xe(t,s)),n.appendChild(Ee(t.options||[],s))):(V.has(e)&&n.appendChild(Ne(t.options||[],s)),e==="textarea"&&n.appendChild(v([x("Rows",`fb-rows-${s}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(v([x("Min Length",`fb-minlength-${s}`,"number",t.minLength||"",""),x("Max Length",`fb-maxlength-${s}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(v([x("Min",`fb-min-${s}`,"number",t.min??"",""),x("Max",`fb-max-${s}`,"number",t.max??"","")]))),n.children.length?n:null}function ee(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const s=document.createElement("div");s.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-bg, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",s.textContent=e,t.appendChild(s);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 _(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem";const i=document.createElement("select");if(i.id=e,i.className="form-input",n.forEach(([c,r])=>{const m=document.createElement("option");m.value=c,m.textContent=r,String(c)===String(s)&&(m.selected=!0),i.appendChild(m)}),l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(c)}return l}function te(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const a=document.createElement("input");if(a.id=e,a.type="checkbox",a.checked=!!s,l.appendChild(a),l.appendChild(document.createTextNode(" "+t)),o.appendChild(l),n){const i=document.createElement("p");i.className="form-hint text-muted",i.textContent=n,i.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(i)}return o}function U(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem",l.appendChild(a);const i=document.createElement("div");i.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=s||"",n&&(c.placeholder=n),c.style.flex="1";const r=document.createElement("input");r.type="color",r.title="Pick a colour",r.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const m=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(r.value=m?c.value:"#000000",r.addEventListener("input",()=>{c.value=r.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),i.appendChild(c),i.appendChild(r),l.appendChild(i),o){const p=document.createElement("p");p.className="form-hint text-muted",p.textContent=o,p.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(p)}return l}function xe(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=ee("Layout");n.body.appendChild(v([_(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),te(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(v([_(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),x("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),s.appendChild(n.wrap);const o=ee("Visual");return o.body.appendChild(v([U(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),_(`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.")])),o.body.appendChild(v([te(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),U(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(v([_(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),U(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),s.appendChild(o.wrap),s}function Ce(e,t,s,n){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(s),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-bg-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const l=document.createElement("div");l.style.cssText="display:flex; gap:.4rem; align-items:center;";const a=document.createElement("div");a.style.cssText="display:flex; flex-direction:column; gap:1px;";const i=document.createElement("button");i.type="button",i.className="btn btn-secondary btn-sm",i.style.padding=".1rem .3rem",i.title="Move up",i.textContent="\u25B2",i.addEventListener("click",S=>{S.preventDefault(),ne(t,s,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-secondary btn-sm",c.style.padding=".1rem .3rem",c.title="Move down",c.textContent="\u25BC",c.addEventListener("click",S=>{S.preventDefault(),ne(t,s,1,n)}),a.appendChild(i),a.appendChild(c),l.appendChild(a);const r=document.createElement("input");r.type="text",r.className="form-input fb-chooser-opt-value",r.placeholder="value",r.value=e.value||"",r.style.flex="1",l.appendChild(r);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-label",m.placeholder="label (visible to users)",m.value=e.label||"",m.style.flex="2",l.appendChild(m);const p=document.createElement("button");p.type="button",p.className="btn btn-secondary btn-sm",p.title="Remove option",p.textContent="\u2715",p.addEventListener("click",S=>{S.preventDefault(),Se(t,s,n)}),l.appendChild(p),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const d=ie("icon name (optional)",e.icon||"");d.input.classList.add("fb-chooser-opt-icon"),d.el.style.flex="1",u.appendChild(d.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",u.appendChild(f),o.appendChild(u);const g=document.createElement("div");g.style.cssText="display:flex; gap:.4rem;";const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-tooltip",h.placeholder="tooltip (hover hint)",h.value=e.tooltip||"",h.style.flex="2",g.appendChild(h);const y=document.createElement("input");y.type="text",y.className="form-input fb-chooser-opt-badge-text",y.placeholder="badge text",y.value=e.badge?.text||"",y.style.flex="1",g.appendChild(y);const k=document.createElement("select");k.className="form-input fb-chooser-opt-badge-type",k.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([S,I])=>{const A=document.createElement("option");A.value=S,A.textContent=I,(e.badge?.type||"")===S&&(A.selected=!0),k.appendChild(A)}),g.appendChild(k),o.appendChild(g);const N=document.createElement("div");N.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const q=document.createElement("label");q.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const $=document.createElement("input");$.type="checkbox",$.className="fb-chooser-opt-recommended",$.checked=!!e.recommended,q.appendChild($),q.appendChild(document.createTextNode(" Recommended")),N.appendChild(q);const L=document.createElement("label");L.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const B=document.createElement("input");return B.type="checkbox",B.className="fb-chooser-opt-disabled",B.checked=!!e.disabled,L.appendChild(B),L.appendChild(document.createTextNode(" Disabled")),N.appendChild(L),o.appendChild(N),o}function Ee(e,t){const s=document.createElement("div");s.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 o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-secondary btn-sm",l.textContent="+ Add option",n.appendChild(l),s.appendChild(n);const a=document.createElement("div");a.className="fb-chooser-options-list",a.id=`fb-chooser-options-${t}`,a.style.cssText="display:flex; flex-direction:column; gap:.4rem;",s.appendChild(a);const i=()=>{a.replaceChildren();const c=we(t);(c.length?c:e||[]).forEach((m,p)=>{a.appendChild(Ce(m,t,p,i))})};return l.addEventListener("click",c=>{c.preventDefault(),ke(t,i)}),i(),s}function we(e){const t=b[e];return!t||!Array.isArray(t.options)?[]:t.options}function J(e){return b[e]?(Array.isArray(b[e].options)||(b[e].options=[]),b[e].options):[]}function ke(e,t){D(e),J(e).push({value:"new",label:"New option"}),t()}function Se(e,t,s){D(e),J(e).splice(t,1),s()}function ne(e,t,s,n){D(e);const o=J(e),l=t+s;if(l<0||l>=o.length)return;const[a]=o.splice(t,1);o.splice(l,0,a),n()}function D(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const s=t.querySelectorAll(".fb-chooser-option-row"),n=[];s.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},a=o.querySelector(".fb-chooser-opt-icon")?.value.trim();a&&(l.icon=a);const i=o.querySelector(".fb-chooser-opt-description")?.value.trim();i&&(l.description=i);const c=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(l.tooltip=c);const r=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),m=o.querySelector(".fb-chooser-opt-badge-type")?.value;(r||m)&&(l.badge={},r&&(l.badge.text=r),m&&(l.badge.type=m)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),n.push(l)}),b[e]&&(b[e].options=n)}function v(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.75rem;margin-bottom:.6rem;",e.forEach(s=>{s&&t.appendChild(s)}),t}function x(e,t,s,n,o){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("input");if(i.id=t,i.type=s||"text",i.className="form-input",i.value=n??"",l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(c)}return l}function Te(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const a=document.createElement("select");return a.id=t,a.className="form-select",s.forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===n&&(c.selected=!0),a.appendChild(c)}),o.appendChild(l),o.appendChild(a),o}function le(e,t,s){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const l=document.createElement("input");return l.id=t,l.type="checkbox",l.checked=s,o.appendChild(l),o.appendChild(document.createTextNode(e)),n.appendChild(o),n}function Ne(e,t){const s=document.createElement("div");s.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 o=document.createElement("textarea");return o.id=`fb-options-${t}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
|
|
5
|
+
`),n.appendChild(m),n.appendChild(p),n}function ve(e,t){const s=e.logic||{},n=b.filter((p,u)=>u!==t&&p.type!=="page-break"&&p.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--border-color,#333);padding-top:.5rem;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const a=document.createElement("span");a.style.cssText="font-size:.8rem;font-weight:600;color:var(--text-muted,#888);",a.textContent="\u26A1 Conditional Logic";const i=K(s),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.textContent=i?"\u25BE":"\u25B8";const r=document.createElement("div");r.className="fb-logic-body",r.style.cssText="padding:.25rem 0 .25rem;"+(i?"":"display:none;"),l.addEventListener("click",()=>{const p=r.style.display==="none";r.style.display=p?"":"none",c.textContent=p?"\u25BE":"\u25B8"}),l.appendChild(a),l.appendChild(c),o.appendChild(l),r.appendChild(be(s.visibility||{},t,n)),r.appendChild(he(s.requirement||{},t,n)),r.appendChild(ge(s.validation||[],t,n));const m=document.getElementById(`fb-type-${t}`)?.value||e.type;return H.has(m)&&r.appendChild(ye(s.cascade||{},t,n)),o.appendChild(r),o}function Z(e,t,s){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(xe(t,s)),n.appendChild(Ee(t.options||[],s))):(H.has(e)&&n.appendChild(Ne(t.options||[],s)),e==="textarea"&&n.appendChild(v([x("Rows",`fb-rows-${s}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(v([x("Min Length",`fb-minlength-${s}`,"number",t.minLength||"",""),x("Max Length",`fb-maxlength-${s}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(v([x("Min",`fb-min-${s}`,"number",t.min??"",""),x("Max",`fb-max-${s}`,"number",t.max??"","")]))),n.children.length?n:null}function ee(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const s=document.createElement("div");s.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-bg, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",s.textContent=e,t.appendChild(s);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 _(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem";const i=document.createElement("select");if(i.id=e,i.className="form-input",n.forEach(([c,r])=>{const m=document.createElement("option");m.value=c,m.textContent=r,String(c)===String(s)&&(m.selected=!0),i.appendChild(m)}),l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(c)}return l}function te(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const a=document.createElement("input");if(a.id=e,a.type="checkbox",a.checked=!!s,l.appendChild(a),l.appendChild(document.createTextNode(" "+t)),o.appendChild(l),n){const i=document.createElement("p");i.className="form-hint text-muted",i.textContent=n,i.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(i)}return o}function U(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem",l.appendChild(a);const i=document.createElement("div");i.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=s||"",n&&(c.placeholder=n),c.style.flex="1";const r=document.createElement("input");r.type="color",r.title="Pick a colour",r.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const m=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(r.value=m?c.value:"#000000",r.addEventListener("input",()=>{c.value=r.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),i.appendChild(c),i.appendChild(r),l.appendChild(i),o){const p=document.createElement("p");p.className="form-hint text-muted",p.textContent=o,p.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(p)}return l}function xe(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=ee("Layout");n.body.appendChild(v([_(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),te(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(v([_(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),x("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),s.appendChild(n.wrap);const o=ee("Visual");return o.body.appendChild(v([U(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),_(`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.")])),o.body.appendChild(v([te(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),U(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(v([_(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),U(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),s.appendChild(o.wrap),s}function Ce(e,t,s,n){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(s),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-bg-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const l=document.createElement("div");l.style.cssText="display:flex; gap:.4rem; align-items:center;";const a=document.createElement("div");a.style.cssText="display:flex; flex-direction:column; gap:1px;";const i=document.createElement("button");i.type="button",i.className="btn btn-secondary btn-sm",i.style.padding=".1rem .3rem",i.title="Move up",i.textContent="\u25B2",i.addEventListener("click",S=>{S.preventDefault(),ne(t,s,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-secondary btn-sm",c.style.padding=".1rem .3rem",c.title="Move down",c.textContent="\u25BC",c.addEventListener("click",S=>{S.preventDefault(),ne(t,s,1,n)}),a.appendChild(i),a.appendChild(c),l.appendChild(a);const r=document.createElement("input");r.type="text",r.className="form-input fb-chooser-opt-value",r.placeholder="value",r.value=e.value||"",r.style.flex="1",l.appendChild(r);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-label",m.placeholder="label (visible to users)",m.value=e.label||"",m.style.flex="2",l.appendChild(m);const p=document.createElement("button");p.type="button",p.className="btn btn-ghost btn-sm",p.title="Remove option",p.innerHTML='<span data-icon="x"></span>',p.addEventListener("click",S=>{S.preventDefault(),Se(t,s,n)}),l.appendChild(p),Domma.icons.scan(p),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const d=ie("icon name (optional)",e.icon||"");d.input.classList.add("fb-chooser-opt-icon"),d.el.style.flex="1",u.appendChild(d.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",u.appendChild(f),o.appendChild(u);const g=document.createElement("div");g.style.cssText="display:flex; gap:.4rem;";const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-tooltip",h.placeholder="tooltip (hover hint)",h.value=e.tooltip||"",h.style.flex="2",g.appendChild(h);const y=document.createElement("input");y.type="text",y.className="form-input fb-chooser-opt-badge-text",y.placeholder="badge text",y.value=e.badge?.text||"",y.style.flex="1",g.appendChild(y);const k=document.createElement("select");k.className="form-input fb-chooser-opt-badge-type",k.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([S,I])=>{const A=document.createElement("option");A.value=S,A.textContent=I,(e.badge?.type||"")===S&&(A.selected=!0),k.appendChild(A)}),g.appendChild(k),o.appendChild(g);const N=document.createElement("div");N.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const q=document.createElement("label");q.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const $=document.createElement("input");$.type="checkbox",$.className="fb-chooser-opt-recommended",$.checked=!!e.recommended,q.appendChild($),q.appendChild(document.createTextNode(" Recommended")),N.appendChild(q);const L=document.createElement("label");L.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const B=document.createElement("input");return B.type="checkbox",B.className="fb-chooser-opt-disabled",B.checked=!!e.disabled,L.appendChild(B),L.appendChild(document.createTextNode(" Disabled")),N.appendChild(L),o.appendChild(N),o}function Ee(e,t){const s=document.createElement("div");s.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 o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-secondary btn-sm",l.innerHTML='<span data-icon="plus"></span> Add option',n.appendChild(l),s.appendChild(n),Domma.icons.scan(l);const a=document.createElement("div");a.className="fb-chooser-options-list",a.id=`fb-chooser-options-${t}`,a.style.cssText="display:flex; flex-direction:column; gap:.4rem;",s.appendChild(a);const i=()=>{a.replaceChildren();const c=we(t);(c.length?c:e||[]).forEach((m,p)=>{a.appendChild(Ce(m,t,p,i))})};return l.addEventListener("click",c=>{c.preventDefault(),ke(t,i)}),i(),s}function we(e){const t=b[e];return!t||!Array.isArray(t.options)?[]:t.options}function J(e){return b[e]?(Array.isArray(b[e].options)||(b[e].options=[]),b[e].options):[]}function ke(e,t){D(e),J(e).push({value:"new",label:"New option"}),t()}function Se(e,t,s){D(e),J(e).splice(t,1),s()}function ne(e,t,s,n){D(e);const o=J(e),l=t+s;if(l<0||l>=o.length)return;const[a]=o.splice(t,1);o.splice(l,0,a),n()}function D(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const s=t.querySelectorAll(".fb-chooser-option-row"),n=[];s.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},a=o.querySelector(".fb-chooser-opt-icon")?.value.trim();a&&(l.icon=a);const i=o.querySelector(".fb-chooser-opt-description")?.value.trim();i&&(l.description=i);const c=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(l.tooltip=c);const r=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),m=o.querySelector(".fb-chooser-opt-badge-type")?.value;(r||m)&&(l.badge={},r&&(l.badge.text=r),m&&(l.badge.type=m)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),n.push(l)}),b[e]&&(b[e].options=n)}function v(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.75rem;margin-bottom:.6rem;",e.forEach(s=>{s&&t.appendChild(s)}),t}function x(e,t,s,n,o){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("input");if(i.id=t,i.type=s||"text",i.className="form-input",i.value=n??"",l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(c)}return l}function Te(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const a=document.createElement("select");return a.id=t,a.className="form-select",s.forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===n&&(c.selected=!0),a.appendChild(c)}),o.appendChild(l),o.appendChild(a),o}function le(e,t,s){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const l=document.createElement("input");return l.id=t,l.type="checkbox",l.checked=s,o.appendChild(l),o.appendChild(document.createTextNode(e)),n.appendChild(o),n}function Ne(e,t){const s=document.createElement("div");s.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 o=document.createElement("textarea");return o.id=`fb-options-${t}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
|
|
6
6
|
no:No
|
|
7
7
|
maybe:Maybe`,o.value=(e||[]).map(l=>{const a=typeof l=="string"?l:l.value??"",i=typeof l=="string"?l:l.label??a;return a===i?a:`${a}:${i}`}).join(`
|
|
8
|
-
`),o.style.fontFamily="monospace",s.appendChild(n),s.appendChild(o),s}function oe(e){const t=e.find("#field-project").val()||"";return C(),{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:{...
|
|
8
|
+
`),o.style.fontFamily="monospace",s.appendChild(n),s.appendChild(o),s}function oe(e){const t=e.find("#field-project").val()||"";return C(),{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:{...V||{},project:t||null},fields:b,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()||"stacked",columns:parseInt(e.find("#setting-columns").val(),10)||2,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()},...O!==null&&{collection:O}}}}function qe(e){const t=[];let s=[],n="Step 1",o="";return e.forEach(l=>{l.type==="page-break"?(t.push({title:n,description:o,fields:s}),s=[],n=l.label||`Step ${t.length+1}`,o=l.description||""):l.type!=="spacer"&&s.push(l)}),(s.length||t.length===0)&&t.push({title:n,description:o,fields:s}),t}function ae(e,t){const s={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const o={...n.formConfig||{}};o.span==="full"&&t&&(o.span=t);const a={type:n.type==="checkbox"?"boolean":n.type==="date"?"string":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},...o}};n.type==="chooser"&&(n.variant&&(a.variant=n.variant),n.multiple&&(a.multiple=!0),n.density&&(a.density=n.density),n.columns&&(a.columns=n.columns),n.accent&&(a.accent=n.accent),n.accentStyle&&(a.accentStyle=n.accentStyle),n.glow&&(a.glow=!0),n.glowColour&&(a.glowColour=n.glowColour),n.shadow&&(a.shadow=n.shadow),n.shadowColour&&(a.shadowColour=n.shadowColour)),s[n.name]=a}),s}function se(e,t){const s=typeof e=="string"?document.querySelector(e):e;s&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const o=s.querySelector(`[name="${n.name}"]`);o&&o.type!=="date"&&(o.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){b=[],T=null,O=null;const t=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);T=t?t[1]:null;let s=null;if(T)try{s=await z(`/forms/${T}`),b=s.fields||[],O=s.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}V={};try{const l=await z("/projects").catch(()=>[]),a=e.find("#field-project").get(0);a&&(Array.isArray(l)?l:[]).forEach(i=>{const c=document.createElement("option");c.value=i.slug,c.textContent=i.name||i.slug,a.appendChild(c)})}catch{}if(s?e.find("#editor-title").get(0).textContent=`Edit: ${s.title}`:e.find("#editor-title").get(0).textContent="New Form",T||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(W(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),s){e.find("#field-title").val(s.title),e.find("#field-slug").val(s.slug),e.find("#field-description").val(s.description||""),e.find("#field-bundled").prop("checked",!!s.bundled),V=s.meta||{},e.find("#field-project").val(s.meta?.project||"");const l=s.settings||{};e.find("#setting-submit-text").val(l.submitText||"Submit"),e.find("#setting-success-message").val(l.successMessage||""),e.find("#setting-layout").val(l.layout||"stacked"),e.find("#setting-columns").val(l.columns||2),e.find("#setting-submit-span").val(l.submitSpan||""),e.find("#columns-group").get(0).style.display=l.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",l.honeypot!==!1),e.find("#setting-rate-limit").val(l.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(l.successRedirect||"");const a=s.actions?.email||{};e.find("#action-email-enabled").prop("checked",a.enabled||!1),e.find("#action-email-recipients").val(a.recipients||""),e.find("#action-email-subject-prefix").val(a.subjectPrefix||"");const i=s.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",i.enabled||!1),e.find("#action-webhook-url").val(i.url||""),e.find("#action-webhook-method").val(i.method||"POST")}const n=s?.settings?.actionSlug||"";try{const l=await z("/actions").catch(()=>[]),a=e.find("#action-cms-slug").get(0);if((Array.isArray(l)?l:[]).forEach(i=>{const c=document.createElement("option");c.value=i.slug,c.textContent=i.title||i.slug,i.slug===n&&(c.selected=!0),a.appendChild(c)}),!l.length){const i=document.createElement("option");i.value="",i.textContent="No actions available",i.disabled=!0,a.appendChild(i)}}catch{}e.find("#setting-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(a=>{a.style.display=l?"flex":"none"})}),w(e);const o=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",l=>{l.stopPropagation(),o.style.display=o.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{o&&(o.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.length;b.push({name:`field_${l+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),w(e);const a=e.find("#fields-list").get(0)?.lastElementChild;if(a){const i=a.querySelector(".fb-field-body");i&&(i.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C(),b.push({type:"spacer"}),w(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.filter(a=>a.type==="page-break").length+2;b.push({type:"page-break",label:`Step ${l}`,description:""}),w(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const l=oe(e);if(!l.title){E.toast("Please enter a form title.",{type:"error"});return}try{T?(await z(`/forms/${T}`,{method:"PUT",body:JSON.stringify(l)}),E.toast("Form saved.",{type:"success"})):(T=(await z("/forms",{method:"POST",body:JSON.stringify(l)})).slug,R.navigate(`/forms/edit/${T}`),E.toast("Form created.",{type:"success"}))}catch(a){E.toast(a.message||"Failed to save form.",{type:"error"})}}),e.find("#preview-btn").get(0).addEventListener("click",()=>{const l=oe(e),a=e.find("#preview-container").get(0);if(!a)return;const i=e.find("#preview-test-result").get(0),c=e.find("#preview-test-badge").get(0);i&&(i.style.display="none",i.textContent=""),c&&(c.style.display=T?"":"none"),e.find("#preview-card").get(0).style.display="",a.textContent="";const r=document.createElement("div");r.id="fb-preview-form",a.appendChild(r);const m=T?async u=>{i&&(i.style.display="none",i.textContent="");try{const d=await fetch(`/api/forms/submit/${T}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),f=await d.json();if(!d.ok)throw new Error(f.error||"Submission failed.");i&&(i.textContent=f.message||l.settings?.successMessage||"Submitted successfully.",i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(34,197,94,.12);color:var(--success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(d){i&&(i.textContent=d.message,i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(239,68,68,.12);color:var(--danger,#ef4444);"),E.toast(d.message,{type:"error"})}return!1}:()=>!1,p=l.fields.some(u=>u.type==="page-break");if(typeof F<"u"){const u=l.settings?.columns||2,d=l.settings?.layout||"stacked";if(p&&F.wizard){const f=qe(l.fields).map(g=>({title:g.title,description:g.description,fields:ae(g.fields,u)}));F.wizard("#fb-preview-form",{schema:{steps:f},onSubmit:m}),se("#fb-preview-form",l.fields)}else if(F.render){const f=ae(l.fields,u),g={};if(l.fields.forEach(h=>{if(!(!h.name||h.type==="page-break"||h.type==="spacer")&&(h.type==="select"||h.type==="multiselect")&&h.required){const y=(h.options||[])[0];y&&(g[h.name]=typeof y=="object"?y.value:y)}}),F.render("#fb-preview-form",f,g,{submitText:l.settings?.submitText||"Submit",layout:d,columns:u,onSubmit:m}),d==="grid"&&l.settings?.submitSpan==="full"){const h=document.querySelector("#fb-preview-form .form-buttons");h&&h.classList.add("col-span-full")}se("#fb-preview-form",l.fields)}window.FormLogicEngine&&l.fields.some(f=>f.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:l.fields},r).init()})}else{const u=document.createElement("p");u.textContent=`${l.fields.filter(d=>d.type!=="page-break").length} field(s): ${l.fields.filter(d=>d.type!=="page-break").map(d=>d.label).join(", ")}`,u.style.cssText="color:var(--muted);font-style:italic;",r.appendChild(u)}e.find("#preview-card").get(0).scrollIntoView({behavior:"smooth",block:"start"})})}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{apiRequest as
|
|
1
|
+
import{apiRequest as g}from"/admin/js/api.js";let c=null,w=[],i=[];export const formSubmissionsView={templateUrl:"/admin/js/templates/form-submissions.html",async onMount(o){const r=window.location.hash.match(/\/forms\/([^/?#]+)\/submissions/);if(c=r?r[1]:null,!c){E.toast("No form selected.",{type:"error"});return}try{const t=await g(`/forms/${c}`);w=(t.fields||[]).filter(n=>n.type!=="page-break"),o.find("#submissions-title").get(0).textContent=`${t.title} \u2014 Submissions`}catch{E.toast("Could not load form definition.",{type:"error"})}await L(o),o.find("#export-btn").off("click").on("click",()=>{const t=E.slideover({title:"Export Submissions",size:"sm",position:"right"}),n=document.createElement("div");n.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;";function a(l,u,f,y){const v=document.createElement("div");v.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const b=document.createElement("button");b.className="btn btn-ghost",b.style.cssText="justify-content:flex-start;gap:.5rem;";const k=document.createElement("span");return k.setAttribute("data-icon",u),b.appendChild(k),b.appendChild(document.createTextNode(" "+l)),b.addEventListener("click",()=>{fetch(f,{headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}}).then(C=>C.blob()).then(C=>{const h=document.createElement("a");h.href=URL.createObjectURL(C),h.download=y,document.body.appendChild(h),h.click(),document.body.removeChild(h),URL.revokeObjectURL(h.href),t.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),v.appendChild(b),v}n.appendChild(a("Export as CSV","file-text",`/api/forms/${c}/submissions/export`,`${c}-submissions.csv`)),n.appendChild(a("Export as JSON","code",`/api/forms/${c}/submissions/export/json`,`${c}-submissions.json`)),t.setContent(n),Domma.icons.scan(n),t.open()}),o.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete all submissions? This cannot be undone."))try{await g(`/forms/${c}/submissions`,{method:"DELETE"}),i=[],E.toast("All submissions cleared.",{type:"success"}),x([],o)}catch{E.toast("Failed to clear submissions.",{type:"error"})}});const d=o.find("#sub-search").get(0),m=o.find("#sub-date-from").get(0),s=o.find("#sub-date-to").get(0);function e(){const t=d?.value.toLowerCase()||"",n=m?.value?new Date(m.value):null,a=s?.value?new Date(s.value+"T23:59:59"):null,l=i.filter(u=>{if(t&&!Object.values(u.data||{}).map(y=>String(y).toLowerCase()).some(y=>y.includes(t)))return!1;if(n||a){const f=u.meta?.createdAt?new Date(u.meta.createdAt):null;if(!f||n&&f<n||a&&f>a)return!1}return!0});x(l,o)}d&&d.addEventListener("input",e),m&&m.addEventListener("change",e),s&&s.addEventListener("change",e),Domma.icons.scan()}};async function L(o){try{i=await g(`/forms/${c}/submissions`)}catch{i=[],E.toast("Could not load submissions.",{type:"error"})}x(i,o)}function x(o,p){const r=p.find("#sub-count").get(0);r&&(o.length===i.length?r.textContent=`${i.length} submission${i.length!==1?"s":""}`:r.textContent=`Showing ${o.length} of ${i.length}`);const m=[...w.map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(t,n)=>{const a=n.data?.[e.name]??"",l=String(a),u=document.createElement("span");return u.title=l,u.textContent=l.length>80?l.slice(0,80)+"\u2026":l,u.outerHTML}})),{key:"meta",title:"Date",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const t=document.createElement("button");return t.className="btn btn-sm btn-danger js-delete-submission",t.dataset.id=e,t.setAttribute("data-tooltip","Delete"),t.innerHTML='<span data-icon="trash"></span>',t.outerHTML}}];T.create("#submissions-table",{data:o,columns:m,emptyMessage:"No submissions yet."});const s=document.querySelector("#submissions-table");s&&(s.querySelectorAll(".js-delete-submission").forEach(e=>{e.addEventListener("click",async t=>{t.stopPropagation();const n=e.dataset.id;if(await E.confirm("Delete this submission?"))try{await g(`/forms/${c}/submissions/${n}`,{method:"DELETE"}),i=i.filter(l=>l.id!==n),E.toast("Submission deleted.",{type:"success"}),x(i,p)}catch{E.toast("Failed to delete submission.",{type:"error"})}})}),s.querySelectorAll("tbody tr").forEach((e,t)=>{const n=o[t];n&&(e.style.cursor="pointer",e.addEventListener("click",a=>{a.target.closest(".js-delete-submission")||A(n)}))}),Domma.icons.scan(s),s.querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})}))}function A(o){const p=document.createElement("div"),r=document.createElement("div");r.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",w.forEach(s=>{const e=o.data?.[s.name];if(e==null||e==="")return;const t=document.createElement("div");t.style.cssText="border-bottom:1px solid var(--border-color,#333);padding-bottom:.6rem;";const n=document.createElement("strong");n.textContent=s.label||s.name,n.style.cssText="display:block;font-size:.8rem;color:var(--text-muted,#888);margin-bottom:.2rem;";const a=document.createElement("p");a.textContent=String(e),a.style.cssText="margin:0;word-break:break-word;",t.appendChild(n),t.appendChild(a),r.appendChild(t)});const d=document.createElement("div");if(d.style.cssText="font-size:.8rem;color:var(--text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--border-color,#333);padding-top:.75rem;",o.meta?.createdAt){const s=document.createElement("span");s.textContent=`Submitted: ${D(o.meta.createdAt).format("DD MMM YYYY HH:mm")}`,d.appendChild(s)}if(o.meta?.ip){const s=document.createElement("span");s.textContent=`IP: ${o.meta.ip}`,d.appendChild(s)}p.appendChild(r),p.appendChild(d);const m=E.modal({title:"Submission Details",size:"md"});m.element.appendChild(p),m.open()}
|
package/admin/js/views/forms.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{apiRequest as p}from"/admin/js/api.js";import{filterByProject as b,getProjectFromHash as h}from"/admin/js/lib/project-context.js";function u(d){return String(d).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}export const formsView={templateUrl:"/admin/js/templates/forms.html",async onMount(d){await g(d),d.find("#create-form-btn").off("click").on("click",()=>{const r=E.modal({title:"Create Form",size:"sm"}),l=document.createElement("div");l.style.cssText="padding:.25rem 0 .5rem;";const e=document.createElement("div");F.create({title:{type:"string",label:"Form Title",placeholder:"e.g. Contact, Feedback\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(e),l.appendChild(e);const
|
|
1
|
+
import{apiRequest as p}from"/admin/js/api.js";import{filterByProject as b,getProjectFromHash as h}from"/admin/js/lib/project-context.js";function u(d){return String(d).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}export const formsView={templateUrl:"/admin/js/templates/forms.html",async onMount(d){await g(d),d.find("#create-form-btn").off("click").on("click",()=>{const r=E.modal({title:"Create Form",size:"sm"}),l=document.createElement("div");l.style.cssText="padding:.25rem 0 .5rem;";const e=document.createElement("div");F.create({title:{type:"string",label:"Form Title",placeholder:"e.g. Contact, Feedback\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(e),l.appendChild(e);const n=document.createElement("div");n.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const a=document.createElement("button");a.className="btn btn-ghost",a.textContent="Cancel";const s=document.createElement("button");s.className="btn btn-primary",s.textContent="Create",n.appendChild(a),n.appendChild(s),l.appendChild(n),r.element.appendChild(l),r.open();const t=e.querySelector('input[name="title"]');setTimeout(()=>t?.focus(),50);async function o(){const i=t?.value.trim();if(i)try{const c=await p("/forms",{method:"POST",body:JSON.stringify({title:i})});r.close(),R.navigate(`/forms/edit/${c.slug}`)}catch(c){E.toast(c.message||"Failed to create form.",{type:"error"})}}a.addEventListener("click",()=>r.close()),s.addEventListener("click",o),t?.addEventListener("keydown",i=>{i.key==="Enter"&&o()})}),d.find("#test-email-btn").off("click").on("click",async()=>{const l=(window.__CMS_SITE__?.smtp||{}).fromAddress||"",e=E.modal({title:"Send Test Email",size:"sm"}),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;";const a=document.createElement("div");a.className="mb-3";const s=document.createElement("label");s.className="form-label",s.textContent="Send to";const t=document.createElement("input");t.type="email",t.className="form-input",t.value=l,t.placeholder="test@example.com",a.appendChild(s),a.appendChild(t),n.appendChild(a);const o=document.createElement("p");o.className="text-muted",o.style.cssText="font-size:.8rem;margin-bottom:.75rem;",o.textContent="SMTP settings are configured in Site Settings.",n.appendChild(o);const i=document.createElement("div");i.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const c=document.createElement("button");c.className="btn btn-ghost",c.textContent="Cancel";const m=document.createElement("button");m.className="btn btn-primary",m.textContent="Send",i.appendChild(c),i.appendChild(m),n.appendChild(i),e.element.appendChild(n),e.open(),setTimeout(()=>t?.focus(),50),c.addEventListener("click",()=>e.close()),m.addEventListener("click",async()=>{const f=t.value.trim();if(f){m.disabled=!0;try{await p("/forms/test-email",{method:"POST",body:JSON.stringify({to:f})}),e.close(),E.toast("Test email sent.",{type:"success"})}catch(y){E.toast(y.message||"Failed to send test email.",{type:"error"})}finally{m.disabled=!1}}})}),Domma.icons.scan()}};async function g(d){let r=[];try{r=await p("/forms")}catch{E.toast("Could not load forms.",{type:"error"})}const l=h();l&&(r=b(r,l)),T.create("#forms-table",{data:r,columns:[{key:"title",title:"Title",render:(e,n)=>{const a=document.createElement("span");a.style.cssText="display:inline-flex;align-items:center;gap:.4rem;flex-wrap:wrap;";const s=document.createElement("a");if(s.href=`#/forms/edit/${u(n.slug)}`,s.textContent=e,s.style.fontWeight="600",a.appendChild(s),n.plugin){const t=document.createElement("span");t.className="badge badge-outline",t.textContent=n.plugin,t.title=`Managed by the ${n.plugin} plugin`,t.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",a.appendChild(t)}return a.outerHTML}},{key:"slug",title:"Slug",render:e=>{const n=document.createElement("code");return n.textContent=e,n.outerHTML}},{key:"fields",title:"Field Count",render:e=>String(e?.length??0)},{key:"submissionCount",title:"Submission Count",render:e=>String(e??0)},{key:"slug",title:"Actions",render:(e,n)=>{const a=document.createElement("div");a.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;";const s=document.createElement("a");s.href=`#/forms/edit/${u(e)}`,s.className="btn btn-sm btn-ghost",s.setAttribute("data-tooltip","Edit"),s.innerHTML='<span data-icon="edit"></span>';const t=document.createElement("a");t.href=`#/forms/${u(e)}/submissions`,t.className="btn btn-sm btn-ghost",t.setAttribute("data-tooltip","Submissions"),t.innerHTML='<span data-icon="inbox"></span>';const o=document.createElement("button");return o.className="btn btn-sm btn-danger js-delete-form",o.dataset.slug=e,o.dataset.plugin=n.plugin||"",o.setAttribute("data-tooltip","Delete"),o.innerHTML='<span data-icon="trash"></span>',a.appendChild(s),a.appendChild(t),a.appendChild(o),a.outerHTML}}],emptyMessage:'No forms yet. Click "Create Form" to get started.'}),document.querySelectorAll(".js-delete-form").forEach(e=>{e.addEventListener("click",async()=>{const n=e.dataset.slug,a=e.dataset.plugin,s=a?`This form is managed by the <strong>${a}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete form "${n}" and all its submissions? This cannot be undone.`;if(await E.confirm(s))try{await p(`/forms/${n}`,{method:"DELETE"}),E.toast("Form deleted.",{type:"success"}),await g(d)}catch{E.toast("Failed to delete form.",{type:"error"})}})}),Domma.icons.scan(),document.querySelectorAll("#forms-table [data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})})}
|
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import{api as
|
|
2
|
-
<div class="menu-tree-row menu-tree-row--separator" data-id="${
|
|
3
|
-
<span class="menu-tree-grip" data-icon="
|
|
4
|
-
<
|
|
1
|
+
import{api as k}from"../api.js";import{colourToCss as U}from"/public/js/menu-decor.mjs";import{openIconPicker as V}from"../lib/card-builder.js";let G=1;const O=()=>"i_"+G++,K=760;function q(t,l=null,a=[]){for(const n of t||[]){const e=O();if(n&&n.type==="separator"){a.push({id:e,parentId:l,type:"separator"});continue}a.push({id:e,parentId:l,text:n.text||"",url:n.url||"",icon:n.icon||"",visibility:n.visibility||"",permission:n.permission||"",hidden:!!n.hidden,bundled:!!n.bundled,badge:n.badge?{...n.badge}:null,pill:n.pill?{...n.pill}:null,colour:n.colour||""}),Array.isArray(n.items)&&n.items.length&&q(n.items,e,a)}return a}function X(t){const l=new Map;for(const n of t)l.has(n.parentId)||l.set(n.parentId,[]),l.get(n.parentId).push(n);function a(n){return(l.get(n)||[]).map(e=>{if(e.type==="separator")return{type:"separator"};const d=a(e.id);return{text:(e.text||"").trim(),url:(e.url||"").trim(),...e.icon&&{icon:e.icon.trim()},...e.visibility&&{visibility:e.visibility},...e.permission&&{permission:e.permission},...e.hidden&&{hidden:!0},...e.bundled&&{bundled:!0},...e.badge&&(e.badge.text||e.badge.countFrom)?{badge:{...e.badge.text&&{text:e.badge.text},...e.badge.countFrom&&{countFrom:e.badge.countFrom},...e.badge.variant&&{variant:e.badge.variant}}}:{},...e.pill&&e.pill.variant?{pill:{style:e.pill.style||"filled",variant:e.pill.variant}}:{},...e.colour&&{colour:e.colour},...d.length&&{items:d}}})}return a(null)}function A(t){return String(t||"").replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<")}function J(t,l){let a=0,n=l.find(e=>e.id===t);for(;n&&n.parentId;)a++,n=l.find(e=>e.id===n.parentId);return a}function x(t,l,a=()=>""){t.html("");function n(e){for(const d of l.filter(f=>f.parentId===e)){const f=J(d.id,l);if(d.type==="separator"){t.append(`
|
|
2
|
+
<div class="menu-tree-row menu-tree-row--separator" data-id="${d.id}" style="margin-left:${f*28}px">
|
|
3
|
+
<span class="menu-tree-grip" data-icon="move"></span>
|
|
4
|
+
<span class="me-sep-line"><span class="me-sep-label">Separator</span></span>
|
|
5
|
+
<button class="btn btn-sm btn-ghost me-ins-item" data-tooltip="Add item below"><span data-icon="plus"></span></button>
|
|
6
|
+
<button class="btn btn-sm btn-ghost me-ins-sep" data-tooltip="Add separator below"><span data-icon="minus"></span></button>
|
|
5
7
|
<button class="btn btn-sm btn-ghost me-outdent" data-tooltip="Outdent"><span data-icon="chevron-left"></span></button>
|
|
6
8
|
<button class="btn btn-sm btn-ghost me-indent" data-tooltip="Indent"><span data-icon="chevron-right"></span></button>
|
|
7
9
|
<button class="btn btn-sm btn-ghost me-up" data-tooltip="Move up"><span data-icon="arrow-up"></span></button>
|
|
8
10
|
<button class="btn btn-sm btn-ghost me-down" data-tooltip="Move down"><span data-icon="arrow-down"></span></button>
|
|
9
11
|
<button class="btn btn-sm btn-danger me-remove" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
10
12
|
</div>
|
|
11
|
-
`);continue}const
|
|
12
|
-
<div class="menu-tree-row menu-tree-row--slim${
|
|
13
|
-
<span class="menu-tree-grip" data-icon="
|
|
14
|
-
<span class="me-row-label"${_}>${
|
|
15
|
-
<span class="me-row-url">${
|
|
13
|
+
`);continue}const F=d.badge&&(d.badge.text||d.badge.countFrom)?`<span class="dm-menu-badge">${A(String(d.badge.text||"#"))}</span>`:"",v=U(d.colour),_=v?` style="color:${A(v)}"`:"";t.append(`
|
|
14
|
+
<div class="menu-tree-row menu-tree-row--slim${d.hidden?" menu-tree-row--hidden":""}" data-id="${d.id}" style="margin-left:${f*28}px">
|
|
15
|
+
<span class="menu-tree-grip" data-icon="move"></span>
|
|
16
|
+
<span class="me-row-label"${_}>${A(d.text||"(untitled)")}${F}</span>
|
|
17
|
+
<span class="me-row-url">${A(d.url||"")}</span>
|
|
18
|
+
<button class="btn btn-sm btn-ghost me-ins-item" data-tooltip="Add item below"><span data-icon="plus"></span></button>
|
|
19
|
+
<button class="btn btn-sm btn-ghost me-ins-sep" data-tooltip="Add separator below"><span data-icon="minus"></span></button>
|
|
16
20
|
<button class="btn btn-sm btn-ghost me-outdent" data-tooltip="Outdent"><span data-icon="chevron-left"></span></button>
|
|
17
21
|
<button class="btn btn-sm btn-ghost me-indent" data-tooltip="Indent"><span data-icon="chevron-right"></span></button>
|
|
18
22
|
<button class="btn btn-sm btn-ghost me-up" data-tooltip="Move up"><span data-icon="arrow-up"></span></button>
|
|
19
23
|
<button class="btn btn-sm btn-ghost me-down" data-tooltip="Move down"><span data-icon="arrow-down"></span></button>
|
|
20
|
-
<button class="btn btn-sm btn-ghost me-hidden ${
|
|
24
|
+
<button class="btn btn-sm btn-ghost me-hidden ${d.hidden?"active":""}" data-tooltip="${d.hidden?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
|
|
21
25
|
<button class="btn btn-sm btn-secondary me-edit" data-tooltip="Edit"><span data-icon="edit"></span></button>
|
|
22
26
|
<button class="btn btn-sm btn-danger me-remove" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
23
27
|
</div>
|
|
24
|
-
`),n(r.id)}}n(null),Domma.icons.scan(t.get(0)),t.get(0).querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})})}function F(t,l){t.find(".menu-tree-row").each(function(){const o=$(this).data("id"),n=l.find(_=>_.id===o);if(!n||n.type==="separator")return;const e=$(this).find(".me-text");e.length&&(n.text=e.val());const r=$(this).find(".me-url");r.length&&(n.url=r.val());const f=$(this).find(".me-icon");f.length&&(n.icon=f.val());const S=$(this).find(".me-vis");S.length&&(n.visibility=S.val());const y=$(this).find(".me-perm");y.length&&(n.permission=y.val())})}function it(t,l){const o={};for(const n of l){const e=t.querySelector(`[name="${n}"]`);e&&(o[n]=e.type==="checkbox"?e.checked:e.value)}return o}function b(t,l={},...o){const n=document.createElement(t);Object.assign(n,l);for(const e of o)e!=null&&n.append(e);return n}function I(t,l,o){const n=b("label",{className:"form-field"},b("span",{className:"form-label",textContent:t}),l);return o&&n.append(b("small",{className:"form-hint",textContent:o,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),n}function Q(t,l){const o=[["","\u2014 Everyone \u2014"],["public","Public \u2014 anyone, incl. logged-out"],...(l||[]).map(e=>[e.name,`${e.label||e.name} and above`]),["private","Private \u2014 super-admin only"]];t&&!o.some(([e])=>e===t)&&o.push([t,`${t} (custom)`]);const n=j(o,t||"");return n.name="visibility",n}function j(t,l){const o=b("select",{className:"form-select"});return t.forEach(([n,e])=>o.add(new Option(e,n))),o.value=l??"",o}function B(t,l){const o=typeof l=="string"&&l.startsWith("#"),n=j([["","\u2014 none \u2014"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],o?"__custom__":l||""),e=b("input",{type:"color",className:"form-input me-colour-hex",value:o?l:"#000000"});e.style.display=o?"":"none",n.addEventListener("change",()=>{e.style.display=n.value==="__custom__"?"":"none"});const r=I(t,n);return r.append(e),r._read=()=>n.value==="__custom__"?e.value:n.value,r}function Y(t,l){const o=b("input",{className:"form-input me-icon",name:"icon",value:l||""}),n=b("span",{className:"me-icon-preview"});l&&n.setAttribute("data-icon",l);const e=b("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),r=f=>{f?n.setAttribute("data-icon",f):n.removeAttribute("data-icon"),Domma.icons.scan(n.parentNode||n)};return e.addEventListener("click",()=>V(f=>{o.value=f,r(f)})),o.addEventListener("input",()=>r(o.value.trim())),I(t,b("div",{className:"me-icon-field"},o,e,n))}function W(...t){return b("div",{className:"me-field-grid"},...t)}async function Z(t,l,o,n,e){const r=v=>b("h4",{className:"me-form-section",textContent:v}),f=I("Label",b("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),S=I("URL",b("input",{className:"form-input",name:"url",value:t.url||""}),"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items."),y=Y("Icon",t.icon||""),_=I("Visibility",Q(t.visibility||"",n),"Who can see this item, by role seniority. Picking a role shows it to that role and anyone more senior. Blank = everyone."),C=j([["","\u2014 no permission gate \u2014"],...(l||[]).map(v=>[v.key,v.label||v.key])],t.permission||"");C.name="permission";const z=I("Permission",C,"Hide unless the viewer holds this specific permission. Independent of Visibility \u2014 both must pass."),h=I("Badge text",b("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),i=j([["","\u2014 none \u2014"],...(o||[]).map(v=>[v.slug,v.title||v.slug])],t.badge?.countFrom||"");i.name="badgeCount";const m=I("Badge count from",i,"Show a live count of this collection\u2019s entries as the badge."),O=B("Badge colour",t.badge?.variant||""),P=j([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");P.name="renderAs";const A=I("Render as",P,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),s=I("Pill style",j([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));s.querySelector("select").name="pillStyle";const a=B("Pill colour",t.pill?.variant||""),c=b("div",{className:"me-pill-wrap"},W(s,a));c.style.display=t.pill?"":"none",P.addEventListener("change",()=>{c.style.display=P.value==="pill"?"":"none"});const u=B("Text colour",t.colour||""),d=b("button",{className:"btn btn-primary",textContent:"Apply"}),p=b("div",{className:"me-item-form"},r("Link"),f,S,y,r("Access"),W(_,z),r("Badge"),W(h,O),m,r("Appearance"),A,c,u,d),g=E.slideover({title:"Edit menu item",size:"lg",position:"right"});g.element.appendChild(p),g.open();const L=g.element.closest(".dm-slideover")||g.element.querySelector(".dm-slideover")||g.element;L.style.setProperty("width",K+"px","important"),L.style.setProperty("max-width","95vw","important"),Domma.icons.scan(p);const x=v=>p.querySelector(`[name="${v}"]`)?.value||"";d.addEventListener("click",()=>{t.text=x("text"),t.url=x("url"),t.icon=x("icon"),t.visibility=x("visibility"),t.permission=x("permission");const v=x("badgeText"),R=x("badgeCount"),M=O._read();if(t.badge=v||R?{...v&&{text:v},...R&&{countFrom:R},...M&&{variant:M}}:null,x("renderAs")==="pill"){const D=a._read();t.pill={style:x("pillStyle")||"filled",...D&&{variant:D}}}else t.pill=null;t.colour=u._read(),g.close(),e()})}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){const l=location.hash.match(/^#\/menus\/edit\/(.+)$/),o=l?decodeURIComponent(l[1]):null,n=!o;let e;if(n)e={slug:"",name:"",description:"",items:[]};else try{e=await w.menus.get(o)}catch(s){E.toast(`Failed to load menu: ${s.message||s}`,{type:"error"}),location.hash="#/menus";return}const r=await w.projects.list().catch(()=>[]),f=await H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),S=await w.collections.list().catch(()=>[]),y=await w.get("/collections/roles/entries?limit=100").catch(()=>null),C=(Array.isArray(y)?y:y?.entries||[]).map(s=>({name:s.data?.name,label:s.data?.label,level:s.data?.level??99})).filter(s=>s.name).sort((s,a)=>a.level-s.level),z='<option value="">\u2014 no permission gate \u2014</option>',h=s=>z+(f.resources||[]).map(a=>{const c=(s||"")===a.key?" selected":"";return`<option value="${a.key}"${c}>${a.label||a.key}</option>`}).join("");t.find("#me-title").text(n?"New menu":`Edit "${e.slug}"`);let i=q(e.items);const m=t.find("#me-items-list");k(m,i,h),t.find("#me-add-item").on("click",()=>{F(m,i),i.push({id:T(),parentId:null,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}),k(m,i,h)}),t.find("#me-add-separator").on("click",()=>{F(m,i),i.push({id:T(),parentId:null,type:"separator"}),k(m,i,h)}),t.off("click",".me-remove").on("click",".me-remove",function(){const s=$(this).closest(".menu-tree-row").data("id");i=i.filter(a=>a.id!==s&&a.parentId!==s),k(m,i,h)}),t.off("click",".me-indent").on("click",".me-indent",function(){F(m,i);const s=$(this).closest(".menu-tree-row").data("id"),a=i.find(d=>d.id===s);if(!a)return;const c=i.filter(d=>d.parentId===a.parentId),u=c.findIndex(d=>d.id===s);u<=0||c[u-1].type!=="separator"&&(a.parentId=c[u-1].id,k(m,i,h))}),t.off("click",".me-outdent").on("click",".me-outdent",function(){F(m,i);const s=$(this).closest(".menu-tree-row").data("id"),a=i.find(u=>u.id===s);if(!a||a.parentId==null)return;const c=i.find(u=>u.id===a.parentId);a.parentId=c?c.parentId:null,k(m,i,h)}),t.off("click",".me-up").on("click",".me-up",function(){F(m,i);const s=$(this).closest(".menu-tree-row").data("id"),a=i.filter(p=>p.parentId===i.find(g=>g.id===s)?.parentId),c=a.findIndex(p=>p.id===s);if(c<=0)return;const u=i.indexOf(a[c]),d=i.indexOf(a[c-1]);[i[u],i[d]]=[i[d],i[u]],k(m,i,h)}),t.off("click",".me-down").on("click",".me-down",function(){F(m,i);const s=$(this).closest(".menu-tree-row").data("id"),a=i.filter(p=>p.parentId===i.find(g=>g.id===s)?.parentId),c=a.findIndex(p=>p.id===s);if(c===-1||c>=a.length-1)return;const u=i.indexOf(a[c]),d=i.indexOf(a[c+1]);[i[u],i[d]]=[i[d],i[u]],k(m,i,h)}),t.off("click",".me-hidden").on("click",".me-hidden",function(){F(m,i);const s=$(this).closest(".menu-tree-row").data("id"),a=i.find(c=>c.id===s);a&&(a.hidden=!a.hidden),k(m,i,h)}),t.off("click",".me-edit").on("click",".me-edit",function(){const s=$(this).closest(".menu-tree-row").data("id"),a=i.find(c=>c.id===s);!a||a.type==="separator"||Z(a,f.resources||[],S,C,()=>k(m,i,h))}),E.tabs(t.find("#me-tabs").get(0)),t.find("#me-variant").val(e.variant||""),t.find("#me-position").val(e.position||""),t.find("#me-fontFamily").val(e.style?.fontFamily||""),t.find("#me-fontSize").val(e.style?.fontSize||""),t.find("#me-fontWeight").val(e.style?.fontWeight||""),t.find("#me-letterSpacing").val(e.style?.letterSpacing||""),t.find("#me-iconSize").val(e.style?.iconSize||"");const{getProjectFromHash:O}=await import("../lib/project-context.js"),P=n?O():null,A=t.find("#me-project");A.html('<option value="">\u2014 none \u2014</option>'+r.map(s=>`<option value="${N(s.slug)}">${N(s.name||s.slug)}</option>`).join("")),t.find("#me-slug").val(e.slug||""),t.find("#me-name").val(e.name||""),t.find("#me-description").val(e.description||""),A.val(e.meta?.project||P||""),Domma.icons.scan(t.get(0)),t.find("#me-save").on("click",async()=>{F(m,i);const s={variant:t.find("#me-variant").val(),position:t.find("#me-position").val(),fontFamily:t.find("#me-fontFamily").val(),fontSize:t.find("#me-fontSize").val(),fontWeight:t.find("#me-fontWeight").val(),letterSpacing:t.find("#me-letterSpacing").val(),iconSize:t.find("#me-iconSize").val()},a={slug:t.find("#me-slug").val().trim(),name:t.find("#me-name").val().trim(),description:t.find("#me-description").val().trim(),project:t.find("#me-project").val()},c=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"],u=Object.fromEntries(Object.entries(s).filter(([p,g])=>c.includes(p)&&g)),d={slug:a.slug,name:a.name,description:a.description,...s.variant&&{variant:s.variant},...s.position&&{position:s.position},...Object.keys(u).length&&{style:u},items:X(i),meta:{...e.meta||{},project:a.project||null}};try{if(n)await w.menus.create(d);else if(d.slug!==e.slug){await w.menus.create(d);const p=await w.menuLocations.get();let g=!1;for(const[L,x]of Object.entries(p))x===e.slug&&(p[L]=d.slug,g=!0);g&&await w.menuLocations.save(p),await w.menus.remove(e.slug)}else await w.menus.update(e.slug,d);E.toast("Saved.",{type:"success"}),location.hash="#/menus/edit/"+encodeURIComponent(d.slug)}catch(p){E.toast(`Save failed: ${p.message||p}`,{type:"error"})}})}};
|
|
28
|
+
`),n(d.id)}}n(null),Domma.icons.scan(t.get(0)),t.get(0).querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})})}function S(t,l){t.find(".menu-tree-row").each(function(){const a=$(this).data("id"),n=l.find(_=>_.id===a);if(!n||n.type==="separator")return;const e=$(this).find(".me-text");e.length&&(n.text=e.val());const d=$(this).find(".me-url");d.length&&(n.url=d.val());const f=$(this).find(".me-icon");f.length&&(n.icon=f.val());const F=$(this).find(".me-vis");F.length&&(n.visibility=F.val());const v=$(this).find(".me-perm");v.length&&(n.permission=v.val())})}function it(t,l){const a={};for(const n of l){const e=t.querySelector(`[name="${n}"]`);e&&(a[n]=e.type==="checkbox"?e.checked:e.value)}return a}function g(t,l={},...a){const n=document.createElement(t);Object.assign(n,l);for(const e of a)e!=null&&n.append(e);return n}function I(t,l,a){const n=g("label",{className:"form-field"},g("span",{className:"form-label",textContent:t}),l);return a&&n.append(g("small",{className:"form-hint",textContent:a,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),n}function Q(t,l){const a=[["","\u2014 Everyone \u2014"],["public","Public \u2014 anyone, incl. logged-out"],...(l||[]).map(e=>[e.name,`${e.label||e.name} and above`]),["private","Private \u2014 super-admin only"]];t&&!a.some(([e])=>e===t)&&a.push([t,`${t} (custom)`]);const n=N(a,t||"");return n.name="visibility",n}function N(t,l){const a=g("select",{className:"form-select"});return t.forEach(([n,e])=>a.add(new Option(e,n))),a.value=l??"",a}function B(t,l){const a=typeof l=="string"&&l.startsWith("#"),n=N([["","\u2014 none \u2014"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],a?"__custom__":l||""),e=g("input",{type:"color",className:"form-input me-colour-hex",value:a?l:"#000000"});e.style.display=a?"":"none",n.addEventListener("change",()=>{e.style.display=n.value==="__custom__"?"":"none"});const d=I(t,n);return d.append(e),d._read=()=>n.value==="__custom__"?e.value:n.value,d}function Y(t,l){const a=g("input",{className:"form-input me-icon",name:"icon",value:l||""}),n=g("span",{className:"me-icon-preview"});l&&n.setAttribute("data-icon",l);const e=g("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),d=f=>{f?n.setAttribute("data-icon",f):n.removeAttribute("data-icon"),Domma.icons.scan(n.parentNode||n)};return e.addEventListener("click",()=>V(f=>{a.value=f,d(f)})),a.addEventListener("input",()=>d(a.value.trim())),I(t,g("div",{className:"me-icon-field"},a,e,n))}function W(...t){return g("div",{className:"me-field-grid"},...t)}async function Z(t,l,a,n,e){const d=b=>g("h4",{className:"me-form-section",textContent:b}),f=I("Label",g("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),F=I("URL",g("input",{className:"form-input",name:"url",value:t.url||""}),"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items."),v=Y("Icon",t.icon||""),_=I("Visibility",Q(t.visibility||"",n),"Who can see this item, by role seniority. Picking a role shows it to that role and anyone more senior. Blank = everyone."),L=N([["","\u2014 no permission gate \u2014"],...(l||[]).map(b=>[b.key,b.label||b.key])],t.permission||"");L.name="permission";const R=I("Permission",L,"Hide unless the viewer holds this specific permission. Independent of Visibility \u2014 both must pass."),h=I("Badge text",g("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),i=N([["","\u2014 none \u2014"],...(a||[]).map(b=>[b.slug,b.title||b.slug])],t.badge?.countFrom||"");i.name="badgeCount";const p=I("Badge count from",i,"Show a live count of this collection\u2019s entries as the badge."),j=B("Badge colour",t.badge?.variant||""),P=N([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");P.name="renderAs";const z=I("Render as",P,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),C=I("Pill style",N([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));C.querySelector("select").name="pillStyle";const s=B("Pill colour",t.pill?.variant||""),o=g("div",{className:"me-pill-wrap"},W(C,s));o.style.display=t.pill?"":"none",P.addEventListener("change",()=>{o.style.display=P.value==="pill"?"":"none"});const c=B("Text colour",t.colour||""),u=g("button",{className:"btn btn-primary",textContent:"Apply"}),m=g("div",{className:"me-item-form"},d("Link"),f,F,v,d("Access"),W(_,R),d("Badge"),W(h,j),p,d("Appearance"),z,o,c,u),r=E.slideover({title:"Edit menu item",size:"lg",position:"right"});r.element.appendChild(m),r.open();const y=r.element.closest(".dm-slideover")||r.element.querySelector(".dm-slideover")||r.element;y.style.setProperty("width",K+"px","important"),y.style.setProperty("max-width","95vw","important"),Domma.icons.scan(m);const w=b=>m.querySelector(`[name="${b}"]`)?.value||"";u.addEventListener("click",()=>{t.text=w("text"),t.url=w("url"),t.icon=w("icon"),t.visibility=w("visibility"),t.permission=w("permission");const b=w("badgeText"),T=w("badgeCount"),M=j._read();if(t.badge=b||T?{...b&&{text:b},...T&&{countFrom:T},...M&&{variant:M}}:null,w("renderAs")==="pill"){const D=s._read();t.pill={style:w("pillStyle")||"filled",...D&&{variant:D}}}else t.pill=null;t.colour=c._read(),r.close(),e()})}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){const l=location.hash.match(/^#\/menus\/edit\/(.+)$/),a=l?decodeURIComponent(l[1]):null,n=!a;let e;if(n)e={slug:"",name:"",description:"",items:[]};else try{e=await k.menus.get(a)}catch(s){E.toast(`Failed to load menu: ${s.message||s}`,{type:"error"}),location.hash="#/menus";return}const d=await k.projects.list().catch(()=>[]),f=await H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),F=await k.collections.list().catch(()=>[]),v=await k.get("/collections/roles/entries?limit=100").catch(()=>null),L=(Array.isArray(v)?v:v?.entries||[]).map(s=>({name:s.data?.name,label:s.data?.label,level:s.data?.level??99})).filter(s=>s.name).sort((s,o)=>o.level-s.level),R='<option value="">\u2014 no permission gate \u2014</option>',h=s=>R+(f.resources||[]).map(o=>{const c=(s||"")===o.key?" selected":"";return`<option value="${o.key}"${c}>${o.label||o.key}</option>`}).join("");t.find("#me-title").text(n?"New menu":`Edit "${e.slug}"`);let i=q(e.items);const p=t.find("#me-items-list");x(p,i,h),t.find("#me-add-item").on("click",()=>{S(p,i),i.push({id:O(),parentId:null,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}),x(p,i,h)}),t.find("#me-add-separator").on("click",()=>{S(p,i),i.push({id:O(),parentId:null,type:"separator"}),x(p,i,h)});function j(s){return function(){S(p,i);const o=$(this).closest(".menu-tree-row").data("id"),c=i.findIndex(r=>r.id===o);if(c===-1)return;const u=i[c],m=s==="separator"?{id:O(),parentId:u.parentId,type:"separator"}:{id:O(),parentId:u.parentId,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1};i.splice(c+1,0,m),x(p,i,h)}}t.off("click",".me-ins-item").on("click",".me-ins-item",j("item")),t.off("click",".me-ins-sep").on("click",".me-ins-sep",j("separator")),t.off("click",".me-remove").on("click",".me-remove",function(){const s=$(this).closest(".menu-tree-row").data("id");i=i.filter(o=>o.id!==s&&o.parentId!==s),x(p,i,h)}),t.off("click",".me-indent").on("click",".me-indent",function(){S(p,i);const s=$(this).closest(".menu-tree-row").data("id"),o=i.find(m=>m.id===s);if(!o)return;const c=i.filter(m=>m.parentId===o.parentId),u=c.findIndex(m=>m.id===s);u<=0||c[u-1].type!=="separator"&&(o.parentId=c[u-1].id,x(p,i,h))}),t.off("click",".me-outdent").on("click",".me-outdent",function(){S(p,i);const s=$(this).closest(".menu-tree-row").data("id"),o=i.find(u=>u.id===s);if(!o||o.parentId==null)return;const c=i.find(u=>u.id===o.parentId);o.parentId=c?c.parentId:null,x(p,i,h)}),t.off("click",".me-up").on("click",".me-up",function(){S(p,i);const s=$(this).closest(".menu-tree-row").data("id"),o=i.filter(r=>r.parentId===i.find(y=>y.id===s)?.parentId),c=o.findIndex(r=>r.id===s);if(c<=0)return;const u=i.indexOf(o[c]),m=i.indexOf(o[c-1]);[i[u],i[m]]=[i[m],i[u]],x(p,i,h)}),t.off("click",".me-down").on("click",".me-down",function(){S(p,i);const s=$(this).closest(".menu-tree-row").data("id"),o=i.filter(r=>r.parentId===i.find(y=>y.id===s)?.parentId),c=o.findIndex(r=>r.id===s);if(c===-1||c>=o.length-1)return;const u=i.indexOf(o[c]),m=i.indexOf(o[c+1]);[i[u],i[m]]=[i[m],i[u]],x(p,i,h)}),t.off("click",".me-hidden").on("click",".me-hidden",function(){S(p,i);const s=$(this).closest(".menu-tree-row").data("id"),o=i.find(c=>c.id===s);o&&(o.hidden=!o.hidden),x(p,i,h)}),t.off("click",".me-edit").on("click",".me-edit",function(){const s=$(this).closest(".menu-tree-row").data("id"),o=i.find(c=>c.id===s);!o||o.type==="separator"||Z(o,f.resources||[],F,L,()=>x(p,i,h))}),E.tabs(t.find("#me-tabs").get(0)),t.find("#me-variant").val(e.variant||""),t.find("#me-position").val(e.position||""),t.find("#me-fontFamily").val(e.style?.fontFamily||""),t.find("#me-fontSize").val(e.style?.fontSize||""),t.find("#me-fontWeight").val(e.style?.fontWeight||""),t.find("#me-letterSpacing").val(e.style?.letterSpacing||""),t.find("#me-iconSize").val(e.style?.iconSize||"");const{getProjectFromHash:P}=await import("../lib/project-context.js"),z=n?P():null,C=t.find("#me-project");C.html('<option value="">\u2014 none \u2014</option>'+d.map(s=>`<option value="${A(s.slug)}">${A(s.name||s.slug)}</option>`).join("")),t.find("#me-slug").val(e.slug||""),t.find("#me-name").val(e.name||""),t.find("#me-description").val(e.description||""),C.val(e.meta?.project||z||""),Domma.icons.scan(t.get(0)),t.find("#me-save").on("click",async()=>{S(p,i);const s={variant:t.find("#me-variant").val(),position:t.find("#me-position").val(),fontFamily:t.find("#me-fontFamily").val(),fontSize:t.find("#me-fontSize").val(),fontWeight:t.find("#me-fontWeight").val(),letterSpacing:t.find("#me-letterSpacing").val(),iconSize:t.find("#me-iconSize").val()},o={slug:t.find("#me-slug").val().trim(),name:t.find("#me-name").val().trim(),description:t.find("#me-description").val().trim(),project:t.find("#me-project").val()},c=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"],u=Object.fromEntries(Object.entries(s).filter(([r,y])=>c.includes(r)&&y)),m={slug:o.slug,name:o.name,description:o.description,...s.variant&&{variant:s.variant},...s.position&&{position:s.position},...Object.keys(u).length&&{style:u},items:X(i),meta:{...e.meta||{},project:o.project||null}};try{if(n)await k.menus.create(m);else if(m.slug!==e.slug){await k.menus.create(m);const r=await k.menuLocations.get();let y=!1;for(const[w,b]of Object.entries(r))b===e.slug&&(r[w]=m.slug,y=!0);y&&await k.menuLocations.save(r),await k.menus.remove(e.slug)}else await k.menus.update(e.slug,m);E.toast("Saved.",{type:"success"}),location.hash="#/menus/edit/"+encodeURIComponent(m.slug)}catch(r){E.toast(`Save failed: ${r.message||r}`,{type:"error"})}})}};
|