domma-cms 0.38.3 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +20 -110
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +16 -16
- package/admin/dist/domma/domma-tools.min.js +4 -4
- package/admin/index.html +2 -2
- package/admin/js/lib/sidebar-renderer.js +3 -3
- package/admin/js/templates/menu-editor.html +23 -0
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/menu-editor.js +7 -7
- package/package.json +2 -2
- package/public/css/forms.css +1 -1
- package/public/css/site.css +1 -1
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +1 -1
- package/public/js/menu-decor.mjs +1 -1
- package/public/js/site.js +1 -1
- package/server/services/markdown.js +10 -2
- package/server/services/menus.js +76 -0
- package/server/templates/page.html +4 -4
package/public/js/site.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{makeBadgeEl as _,applyColour as A,applyPill as I}from"/public/js/menu-decor.mjs";import{enhancePickerFields as D}from"/public/js/forms.js?v=20260722-pickers";$(()=>{const C=window.__CMS_NAV__||{},y=window.__CMS_SITE__||{};if(y.autoTheme?.enabled){let o=function(e){const l=(e||"07:00").split(":");return+l[0]*60+(+l[1]||0)},a=function(){const e=new Date,l=e.getHours()*60+e.getMinutes();return l>=o(t.dayStart)&&l<o(t.nightStart)?t.dayTheme:t.nightTheme};const t=y.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const v=$("#site-navbar");if(v.length&&C.brand){let a=function(i,n=0){return Array.isArray(i)?i.filter(c=>!(c&&c.type==="separator"&&n===0)).map(c=>c&&c.type==="separator"?{divider:!0}:Array.isArray(c?.items)?{...c,items:a(c.items,n+1)}:c):i},e=function(i){return Array.isArray(i)?i.filter(n=>!(n&&n.type==="separator")):[]};const t={...C.brand},o=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let i="";t.logo?i+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(i+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(i+=`<span class="navbar-brand-text${o}">${t.text}</span>`),t.tagline&&(i+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=i}else o&&t.text&&(t.html=`<span class="navbar-brand-text${o}">${t.text}</span>`);Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar");const l=v.get(0);if(C.appearOnHover){const i=window.matchMedia("(min-width: 993px)");l.querySelectorAll(".navbar-dropdown").forEach(n=>{n.addEventListener("mouseenter",()=>{i.matches&&n.classList.add("open")}),n.addEventListener("mouseleave",()=>{i.matches&&n.classList.remove("open")})})}const d=l.querySelector(".navbar-toggle"),h=l.querySelector(".navbar-collapse");if(d&&h){let i=null;const n=()=>{h.classList.contains("show")&&d.click()},c=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},s=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",n),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};d.addEventListener("click",()=>{requestAnimationFrame(()=>{h.classList.contains("show")?s():c()})}),h.addEventListener("click",p=>{p.target.closest("a")&&requestAnimationFrame(c)}),document.addEventListener("keydown",p=>{p.key==="Escape"&&h.classList.contains("show")&&n()})}const m=e(C.items||[]);l.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const n=m[parseInt(i.dataset.index,10)];if(!n)return;const c=i.dataset.subindex!=null,s=c&&Array.isArray(n.items)?n.items[parseInt(i.dataset.subindex,10)]:n;if(!s)return;s.colour&&A(i,s.colour),s.pill&&!c&&I(i,s.pill);const p=_(s.badge);p&&i.appendChild(p)})}const b=$("#site-footer");if(b.length){let l=function(r){return String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},d=function(r){return l(r).replace(/'/g,"'")},h=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';const u=d(r.url||"#"),g=l(r.text||""),k=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(h).join("")}</ul>`:"";return`<li><a href="${u}">${g}</a>${k}</li>`},m=function(r,u){return(r||[]).forEach(g=>{g&&g.type==="separator"||(u.push(g),Array.isArray(g.items)&&g.items.length&&m(g.items,u))}),u};const t=y.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${l(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(h).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(h).join("")}</ul></nav>`),y.footer){const r=Object.keys(o).filter(u=>t[u]);r.length&&(a+='<div class="footer-social">',r.forEach(u=>{const{label:g,svg:k}=o[u];a+=`<a href="${t[u]}" target="_blank" rel="noopener noreferrer" aria-label="${g}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",b.html(a);const i=[...m(e.primary,[]),...m(e.legal,[])];b.get(0).querySelectorAll(".footer-nav a").forEach((r,u)=>{const g=i[u];if(!g)return;g.colour&&A(r,g.colour);const k=_(g.badge);k&&r.appendChild(k)});const c=S.get("reduced_motion"),s=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),p=b.get(0).querySelector(".footer-inner");if(p){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=s,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const u=document.createElement("span");u.className="form-switch-label",u.textContent="Reduce motion";const g=document.createElement("label");g.className="form-switch footer-motion-switch",g.title="Reduce motion",g.appendChild(r),g.appendChild(u),p.appendChild(g)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const f=$(".page-body");if(f.length){f.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),f.find(".tabs").each(function(){Domma.elements.tabs(this)}),f.find(".carousel").each(function(){const t={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const o=parseInt(this.dataset.animationDuration,10);Number.isFinite(o)&&(t.animationDuration=o)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(o=>o.cloneNode(!0)),this._dmCarouselOpts=t),this._dmCarousel=Domma.elements.carousel(this,t)}),f.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),f.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),f.find(".dm-progression").each(function(){const t=Array.from(this.querySelectorAll(".dm-progression-item")).map(e=>{const l={title:e.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:e.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return e.dataset.date&&(l.date=e.dataset.date),e.dataset.status&&(l.status=e.dataset.status),e.dataset.entryId&&(l._entryId=e.dataset.entryId),l}),o=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",a=Domma.elements.progression(this,{layout:o,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...t.length?{items:t}:{}});t.length&&(this._dmCtx={instance:a,items:t})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(t=>{const o=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,l=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,h=t.dataset.revealDirection==="rtl",m=Array.from(t.children),i={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};m.forEach((n,c)=>{n.style.opacity="0",n.style.transform=i[a]||"",n.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const s=h?m.length-1-c:c;n.style.transitionDelay=o==="stagger"?`${d+s*l}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const n=new IntersectionObserver(c=>{c.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",n.unobserve(s.target))})},{threshold:.1});m.forEach(c=>n.observe(c))}))}),f.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),f.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,o=document.getElementById(t);if(!o)return;const a=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",a.setContent(o),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const a=atob(o.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const o={};for(const[a,e]of Object.entries(t)){const l=e?.events?.click,{confirm:d,toast:h,alert:m,prompt:i,...n}=l||{};d||h||m||i?($(a).on("click",async function(s){if(s.preventDefault(),d&&!await E.confirm(d))return;let p=null;if(!(i&&(p=await E.prompt(i,{inputPlaceholder:n.promptPlaceholder||"",inputValue:n.promptDefault||""}),p===null))){if(n.target){const r=$(n.target);n.toggleClass&&r.toggleClass(n.toggleClass),n.addClass&&r.addClass(n.addClass),n.removeClass&&r.removeClass(n.removeClass),p!==null&&(n.setText&&r.text(p),n.setVal&&r.val(p),n.setAttr&&r.attr(n.setAttr,p))}n.href&&(window.location.href=n.href),h&&E.toast(h,{type:n.toastType||"success"}),m&&E.alert(m)}}),Object.keys(n).length&&(o[a]={...e,events:{...e.events,click:n}})):o[a]=e}$.setup(o)}}f.length&&x(f.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(y=>{y.__ctaWired||(y.__ctaWired=!0,y.addEventListener("click",async()=>{const v=y.dataset.action,b=y.dataset.entry,w=y.dataset.confirm;let f=S.get("auth_token");if(!f){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(y.childNodes).map(a=>a.cloneNode(!0));y.disabled=!0,y.textContent="Running\u2026";const o=a=>fetch(`/api/actions/${v}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:b})});try{let a=await o(f);if(a.status===401){const l=S.get("auth_refresh_token");if(l){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:l})});if(d.ok){const{token:h}=await d.json();S.set("auth_token",h),f=h,a=await o(f)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{y.disabled=!1,y.textContent="",t.forEach(a=>y.appendChild(a)),Domma.icons.scan(y)}}))})}(function(){const y=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!y.length)return;const v=S.get("auth_token");function b(f,t){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=f;return o.appendChild(a),o}function w(f,t){const o=document.createElement("template");o.innerHTML=t||"",f.replaceWith(o.content)}y.forEach(async f=>{const t=f.dataset.collectionAttrs;if(!t)return;if(!v){f.replaceChildren(b("",!0));return}const o=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=v,e=await o(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const h=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(h.ok){const{token:m}=await h.json();S.set("auth_token",m),a=m,e=await o(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));f.replaceChildren(b(d||"Unable to load entries.",!1));return}const{html:l}=await e.json();w(f,l)}catch{f.replaceChildren(b("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),x(document.body)},100)})(),(function(){const y=document.querySelectorAll("[data-collection-table]");!y.length||typeof T>"u"||!T.create||y.forEach(v=>{let b;try{b=JSON.parse(atob(v.dataset.payload))}catch{return}const{columns:w,rows:f,search:t,sortable:o,exportable:a,pageSize:e,empty:l,ctaConfig:d}=b;if(!w?.length)return;if(d){const i=w.findIndex(n=>n.key==="_cta");i!==-1&&(w[i]={key:"_cta",title:"",render:(n,c)=>{const s=document.createElement("button");if(s.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,s.dataset.action=d.action||"",s.dataset.entry=c._entryId||"",d.confirm&&(s.dataset.confirm=d.confirm),d.icon){const p=document.createElement("span");p.dataset.icon=d.icon,s.appendChild(p),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(d.label||"Run")),s}})}const h="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=h,v.replaceChildren(m),T.create("#"+h,{data:f,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:l}),d&&x(m),v._dmTable={columns:w,rows:f,render(i){T.create("#"+h,{data:i,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:l}),d&&x(m)}}})})(),(function(){const y=document.querySelectorAll("[data-form-inline]");if(!y.length||typeof F>"u")return;function v(t,o){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const l=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),d=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":l?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&o&&(h.span=o);const m={type:d,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...h}};l&&(e.variant&&(m.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(m.multiple=!0),e.density&&(m.density=e.density),e.columns&&(m.columns=e.columns),e.accent&&(m.accent=e.accent),e.accentStyle&&(m.accentStyle=e.accentStyle),e.glow&&(m.glow=!0),e.glowColour&&(m.glowColour=e.glowColour),e.shadow&&(m.shadow=e.shadow),e.shadowColour&&(m.shadowColour=e.shadowColour)),a[e.name]=m}),a}function b(t){const o={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(o[a.name]=typeof e=="object"?e.value:e)}}),o}function w(t,o){(o||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function f(t,o,a){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=o,e.style.cssText=a?"color:var(--dm-danger,#f87171);margin-top:.75rem;":"color:var(--dm-success,#4ade80);margin-top:.75rem;"}y.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=o.fields||[],e=o.settings||{},l=e.columns||1,d=e.layout||"stacked",h=a.some(c=>c.type==="page-break"),m=async c=>{try{const s=t.querySelector('[name="website"]'),p=t.querySelector('[name="_t"]'),r=Object.assign({},c);s!==null&&(r._hp=s.value),p!==null&&(r._t=p.value);const u=await H.post(`/api/forms/submit/${o.slug}`,r);if(u&&u.error){f(t,u.error,!0);const g=new Error(u.error);throw g.formSubmitFailed=!0,g}if(u?.redirect){window.location.href=u.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);f(t,u?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||f(t,s.message||"Submission failed. Please try again.",!0),s}};function i(c){const s=c.querySelector("form");if(!s)return;const p=document.createElement("div");p.className="fb-form-honeypot",p.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name="website",r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",p.appendChild(r);const u=document.createElement("input");u.name="_t",u.type="hidden",u.value=Date.now(),p.appendChild(u),s.appendChild(p)}function n(){if(!window.FormLogicEngine||!a.some(s=>s.logic))return;const c=new window.FormLogicEngine.FormLogicRuntime(o,t);if(c.init(),t._formLogicRuntime=c,t.parentNode&&typeof MutationObserver<"u"){const s=new MutationObserver(function(p){for(const r of p)for(const u of r.removedNodes)if(u===t||u.nodeType===1&&u.contains&&u.contains(t)){c.destroy(),s.disconnect();return}});s.observe(t.parentNode,{childList:!0,subtree:!1})}}if(h&&F.wizard){const c=[];let s=[],p=o.title||"Step 1",r="";a.forEach(g=>{g.type==="page-break"?(c.push({title:p,description:r,fields:v(s,l)}),s=[],p=g.label||`Step ${c.length+1}`,r=g.description||""):g.type!=="spacer"&&s.push(g)}),c.push({title:p,description:r,fields:v(s,l)});const u=F.wizard(t,{schema:{steps:c},onSubmit:m});Promise.resolve(u).then(function(){w(t,a),D(t,a),e.honeypot!==!1&&i(t),n()})}else if(F.render){const c=F.render(t,v(a,l),b(a),{submitText:e.submitText||"Submit",layout:d,columns:l,onSubmit:m});Promise.resolve(c).then(function(){if(d==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}w(t,a),D(t,a),e.honeypot!==!1&&i(t),n()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
1
|
+
import{makeBadgeEl as _,applyColour as A,applyPill as I,applyFloat as M}from"/public/js/menu-decor.mjs?v=20260817-floating-menus";import{enhancePickerFields as D}from"/public/js/forms.js?v=20260817-multi-chooser";$(()=>{const C=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let n=function(e){const l=(e||"07:00").split(":");return+l[0]*60+(+l[1]||0)},a=function(){const e=new Date,l=e.getHours()*60+e.getMinutes();return l>=n(t.dayStart)&&l<n(t.nightStart)?t.dayTheme:t.nightTheme};const t=g.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const v=$("#site-navbar");if(v.length&&C.brand){let a=function(o,i=0){return Array.isArray(o)?o.filter(s=>!(s&&s.type==="separator"&&i===0)).map(s=>s&&s.type==="separator"?{divider:!0}:Array.isArray(s?.items)?{...s,items:a(s.items,i+1)}:s):o},e=function(o){return Array.isArray(o)?o.filter(i=>!(i&&i.type==="separator")):[]};const t={...C.brand},n=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let o="";t.logo?o+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(o+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(o+=`<span class="navbar-brand-text${n}">${t.text}</span>`),t.tagline&&(o+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=o}else n&&t.text&&(t.html=`<span class="navbar-brand-text${n}">${t.text}</span>`);const l=C.position==="floating";Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:l?"static":C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar"),l&&M(v.get(0),C);const d=v.get(0);if(C.appearOnHover){const o=window.matchMedia("(min-width: 993px)");d.querySelectorAll(".navbar-dropdown").forEach(i=>{i.addEventListener("mouseenter",()=>{o.matches&&i.classList.add("open")}),i.addEventListener("mouseleave",()=>{o.matches&&i.classList.remove("open")})})}const h=d.querySelector(".navbar-toggle"),c=d.querySelector(".navbar-collapse");if(h&&c){let o=null;const i=()=>{c.classList.contains("show")&&h.click()},s=()=>{o&&(o.remove(),o=null),document.body.classList.remove("dm-drawer-open")},m=()=>{o||(o=document.createElement("div"),o.className="dm-navbar-backdrop",o.addEventListener("click",i),document.body.appendChild(o),document.body.classList.add("dm-drawer-open"))};h.addEventListener("click",()=>{requestAnimationFrame(()=>{c.classList.contains("show")?m():s()})}),c.addEventListener("click",r=>{r.target.closest("a")&&requestAnimationFrame(s)}),document.addEventListener("keydown",r=>{r.key==="Escape"&&c.classList.contains("show")&&i()})}const y=e(C.items||[]);d.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(o=>{const i=y[parseInt(o.dataset.index,10)];if(!i)return;const s=o.dataset.subindex!=null,m=s&&Array.isArray(i.items)?i.items[parseInt(o.dataset.subindex,10)]:i;if(!m)return;m.colour&&A(o,m.colour),m.pill&&!s&&I(o,m.pill);const r=_(m.badge);r&&o.appendChild(r)})}const b=$("#site-footer");if(b.length){let l=function(r){return String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},d=function(r){return l(r).replace(/'/g,"'")},h=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';const u=d(r.url||"#"),f=l(r.text||""),k=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(h).join("")}</ul>`:"";return`<li><a href="${u}">${f}</a>${k}</li>`},c=function(r,u){return(r||[]).forEach(f=>{f&&f.type==="separator"||(u.push(f),Array.isArray(f.items)&&f.items.length&&c(f.items,u))}),u};const t=g.social||{},n={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${l(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(h).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(h).join("")}</ul></nav>`),g.footer){const r=Object.keys(n).filter(u=>t[u]);r.length&&(a+='<div class="footer-social">',r.forEach(u=>{const{label:f,svg:k}=n[u];a+=`<a href="${t[u]}" target="_blank" rel="noopener noreferrer" aria-label="${f}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",b.html(a);const y=[...c(e.primary,[]),...c(e.legal,[])];b.get(0).querySelectorAll(".footer-nav a").forEach((r,u)=>{const f=y[u];if(!f)return;f.colour&&A(r,f.colour);const k=_(f.badge);k&&r.appendChild(k)});const i=S.get("reduced_motion"),s=i!==null?!!i:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),m=b.get(0).querySelector(".footer-inner");if(m){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=s,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const u=document.createElement("span");u.className="form-switch-label",u.textContent="Reduce motion";const f=document.createElement("label");f.className="form-switch footer-motion-switch",f.title="Reduce motion",f.appendChild(r),f.appendChild(u),m.appendChild(f)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const p=$(".page-body");if(p.length){p.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),p.find(".tabs").each(function(){Domma.elements.tabs(this)}),p.find(".carousel").each(function(){const t={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const n=parseInt(this.dataset.animationDuration,10);Number.isFinite(n)&&(t.animationDuration=n)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(n=>n.cloneNode(!0)),this._dmCarouselOpts=t),this._dmCarousel=Domma.elements.carousel(this,t)}),p.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),p.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),p.find(".dm-progression").each(function(){const t=Array.from(this.querySelectorAll(".dm-progression-item")).map(e=>{const l={title:e.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:e.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return e.dataset.date&&(l.date=e.dataset.date),e.dataset.status&&(l.status=e.dataset.status),e.dataset.entryId&&(l._entryId=e.dataset.entryId),l}),n=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",a=Domma.elements.progression(this,{layout:n,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...t.length?{items:t}:{}});t.length&&(this._dmCtx={instance:a,items:t})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(t=>{const n=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,l=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,h=t.dataset.revealDirection==="rtl",c=Array.from(t.children),y={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};c.forEach((o,i)=>{o.style.opacity="0",o.style.transform=y[a]||"",o.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const s=h?c.length-1-i:i;o.style.transitionDelay=n==="stagger"?`${d+s*l}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const o=new IntersectionObserver(i=>{i.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",o.unobserve(s.target))})},{threshold:.1});c.forEach(i=>o.observe(i))}))}),p.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),p.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,n=document.getElementById(t);if(!n)return;const a=E.slideover({title:n.dataset.soTitle||"",size:n.dataset.soSize||"md",position:n.dataset.soPosition||"right"});n.style.display="",a.setContent(n),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(n=>{try{const a=atob(n.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const n={};for(const[a,e]of Object.entries(t)){const l=e?.events?.click,{confirm:d,toast:h,alert:c,prompt:y,...o}=l||{};d||h||c||y?($(a).on("click",async function(s){if(s.preventDefault(),d&&!await E.confirm(d))return;let m=null;if(!(y&&(m=await E.prompt(y,{inputPlaceholder:o.promptPlaceholder||"",inputValue:o.promptDefault||""}),m===null))){if(o.target){const r=$(o.target);o.toggleClass&&r.toggleClass(o.toggleClass),o.addClass&&r.addClass(o.addClass),o.removeClass&&r.removeClass(o.removeClass),m!==null&&(o.setText&&r.text(m),o.setVal&&r.val(m),o.setAttr&&r.attr(o.setAttr,m))}o.href&&(window.location.href=o.href),h&&E.toast(h,{type:o.toastType||"success"}),c&&E.alert(c)}}),Object.keys(o).length&&(n[a]={...e,events:{...e.events,click:o}})):n[a]=e}$.setup(n)}}p.length&&x(p.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const v=g.dataset.action,b=g.dataset.entry,w=g.dataset.confirm;let p=S.get("auth_token");if(!p){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(g.childNodes).map(a=>a.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const n=a=>fetch(`/api/actions/${v}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:b})});try{let a=await n(p);if(a.status===401){const l=S.get("auth_refresh_token");if(l){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:l})});if(d.ok){const{token:h}=await d.json();S.set("auth_token",h),p=h,a=await n(p)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",t.forEach(a=>g.appendChild(a)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const v=S.get("auth_token");function b(p,t){const n=document.createElement("div");n.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=p;return n.appendChild(a),n}function w(p,t){const n=document.createElement("template");n.innerHTML=t||"",p.replaceWith(n.content)}g.forEach(async p=>{const t=p.dataset.collectionAttrs;if(!t)return;if(!v){p.replaceChildren(b("",!0));return}const n=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=v,e=await n(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const h=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(h.ok){const{token:c}=await h.json();S.set("auth_token",c),a=c,e=await n(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));p.replaceChildren(b(d||"Unable to load entries.",!1));return}const{html:l}=await e.json();w(p,l)}catch{p.replaceChildren(b("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),x(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(v=>{let b;try{b=JSON.parse(atob(v.dataset.payload))}catch{return}const{columns:w,rows:p,search:t,sortable:n,exportable:a,pageSize:e,empty:l,ctaConfig:d}=b;if(!w?.length)return;if(d){const y=w.findIndex(o=>o.key==="_cta");y!==-1&&(w[y]={key:"_cta",title:"",render:(o,i)=>{const s=document.createElement("button");if(s.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,s.dataset.action=d.action||"",s.dataset.entry=i._entryId||"",d.confirm&&(s.dataset.confirm=d.confirm),d.icon){const m=document.createElement("span");m.dataset.icon=d.icon,s.appendChild(m),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(d.label||"Run")),s}})}const h="col-table-"+Math.random().toString(36).slice(2,7),c=document.createElement("div");c.id=h,v.replaceChildren(c),T.create("#"+h,{data:p,columns:w,search:t,sortable:n,exportable:a,pageSize:e,emptyMessage:l}),d&&x(c),v._dmTable={columns:w,rows:p,render(y){T.create("#"+h,{data:y,columns:w,search:t,sortable:n,exportable:a,pageSize:e,emptyMessage:l}),d&&x(c)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function v(t,n){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const l=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),d=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":l?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&n&&(h.span=n);const c={type:d,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...h}};l&&(e.variant&&(c.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(c.multiple=!0),e.density&&(c.density=e.density),e.columns&&(c.columns=e.columns),e.accent&&(c.accent=e.accent),e.accentStyle&&(c.accentStyle=e.accentStyle),e.glow&&(c.glow=!0),e.glowColour&&(c.glowColour=e.glowColour),e.shadow&&(c.shadow=e.shadow),e.shadowColour&&(c.shadowColour=e.shadowColour)),a[e.name]=c}),a}function b(t){const n={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(n[a.name]=typeof e=="object"?e.value:e)}}),n}function w(t,n){(n||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function p(t,n,a){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=n,e.style.cssText=a?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let n;try{n=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=n.fields||[],e=n.settings||{},l=e.columns||1,d=e.layout||"stacked",h=a.some(i=>i.type==="page-break"),c=async i=>{try{const s=t.querySelector('[name="website"]'),m=t.querySelector('[name="_t"]'),r=Object.assign({},i);s!==null&&(r._hp=s.value),m!==null&&(r._t=m.value);const u=await H.post(`/api/forms/submit/${n.slug}`,r);if(u&&u.error){p(t,u.error,!0);const f=new Error(u.error);throw f.formSubmitFailed=!0,f}if(u?.redirect){window.location.href=u.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);p(t,u?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||p(t,s.message||"Submission failed. Please try again.",!0),s}};function y(i){const s=i.querySelector("form");if(!s)return;const m=document.createElement("div");m.className="fb-form-honeypot",m.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name="website",r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",m.appendChild(r);const u=document.createElement("input");u.name="_t",u.type="hidden",u.value=Date.now(),m.appendChild(u),s.appendChild(m)}function o(){if(!window.FormLogicEngine||!a.some(s=>s.logic))return;const i=new window.FormLogicEngine.FormLogicRuntime(n,t);if(i.init(),t._formLogicRuntime=i,t.parentNode&&typeof MutationObserver<"u"){const s=new MutationObserver(function(m){for(const r of m)for(const u of r.removedNodes)if(u===t||u.nodeType===1&&u.contains&&u.contains(t)){i.destroy(),s.disconnect();return}});s.observe(t.parentNode,{childList:!0,subtree:!1})}}if(h&&F.wizard){const i=[];let s=[],m=n.title||"Step 1",r="";a.forEach(f=>{f.type==="page-break"?(i.push({title:m,description:r,fields:v(s,l)}),s=[],m=f.label||`Step ${i.length+1}`,r=f.description||""):f.type!=="spacer"&&s.push(f)}),i.push({title:m,description:r,fields:v(s,l)});const u=F.wizard(t,{schema:{steps:i},onSubmit:c});Promise.resolve(u).then(function(){w(t,a),D(t,a),e.honeypot!==!1&&y(t),o()})}else if(F.render){const i=F.render(t,v(a,l),b(a),{submitText:e.submitText||"Submit",layout:d,columns:l,onSubmit:c});Promise.resolve(i).then(function(){if(d==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}w(t,a),D(t,a),e.honeypot!==!1&&y(t),o()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
@@ -11,7 +11,7 @@ import {fileURLToPath} from 'url';
|
|
|
11
11
|
import {applyTransforms, getSanitizeExtensions, getShortcodeProcessors} from './hooks.js';
|
|
12
12
|
import {getConfig} from '../config.js';
|
|
13
13
|
import {getCollection, listEntries} from './collections.js';
|
|
14
|
-
import {getMenu, resolveLocation, resolveMenuDecorations, colourToCss} from './menus.js';
|
|
14
|
+
import {getMenu, resolveLocation, resolveMenuDecorations, colourToCss, floatToCss} from './menus.js';
|
|
15
15
|
import {checkVisibility} from '../middleware/auth.js';
|
|
16
16
|
|
|
17
17
|
const __dirname_md = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -3860,7 +3860,15 @@ async function processMenuBlocks(markdown, user) {
|
|
|
3860
3860
|
const klass = attrs.class ? ` ${escapeAttr(attrs.class)}` : '';
|
|
3861
3861
|
const variantClass = attrs.variant ? ` dm-menu--variant-${escapeAttr(attrs.variant)}` : '';
|
|
3862
3862
|
const decorated = await resolveMenuDecorations(capped);
|
|
3863
|
-
|
|
3863
|
+
// A floating menu pins itself to a viewport corner/edge. `float="no"`
|
|
3864
|
+
// opts a single placement out, so the same menu can be floated in its
|
|
3865
|
+
// slot yet rendered inline where the shortcode drops it.
|
|
3866
|
+
const floatCss = attrs.float === 'no' ? '' : floatToCss(menu);
|
|
3867
|
+
const floatClass = floatCss ? ' dm-menu-floating' : '';
|
|
3868
|
+
const floatAttrs = floatCss
|
|
3869
|
+
? ` style="${escapeAttr(floatCss)}" data-float-anchor="${escapeAttr(menu.float?.anchor || 'TL')}"`
|
|
3870
|
+
: '';
|
|
3871
|
+
const html = `<nav class="dm-menu dm-menu--${escapeAttr(menu.slug)}${variantClass}${floatClass}${klass}" data-menu="${escapeAttr(menu.slug)}"${floatAttrs}>${renderMenuItemsAsUl(decorated)}</nav>`;
|
|
3864
3872
|
out = out.replace(m[0], html);
|
|
3865
3873
|
}
|
|
3866
3874
|
return out;
|
package/server/services/menus.js
CHANGED
|
@@ -81,6 +81,65 @@ export function colourToCss(value) {
|
|
|
81
81
|
return '';
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Floating menus — a menu with position "floating" detaches from the document
|
|
86
|
+
// flow and pins itself to a viewport corner or edge, the same way the
|
|
87
|
+
// breadcrumb trail does. The anchor/offset shape deliberately mirrors
|
|
88
|
+
// site.json's `breadcrumbs` config so the two read alike in the admin.
|
|
89
|
+
//
|
|
90
|
+
// Keep FLOAT_ANCHORS and floatToCss in sync with the copies in
|
|
91
|
+
// public/js/menu-decor.mjs (tests/services/menu-decor-client.test.js asserts it).
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
export const FLOAT_ANCHORS = new Set(['TL', 'TR', 'BL', 'BR', 'L', 'R']);
|
|
94
|
+
|
|
95
|
+
export const FLOAT_DEFAULTS = {anchor: 'TL', offsetX: 16, offsetY: 16, zIndex: 150};
|
|
96
|
+
|
|
97
|
+
function floatNumber(value, fallback, min, max) {
|
|
98
|
+
const n = Number(value);
|
|
99
|
+
if (!Number.isFinite(n)) return fallback;
|
|
100
|
+
return Math.min(max, Math.max(min, Math.round(n)));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build the inline CSS for a floating menu. Returns '' for any menu that is
|
|
105
|
+
* not floating, so callers can apply it unconditionally.
|
|
106
|
+
*
|
|
107
|
+
* `L` and `R` anchor to the middle of that edge; the four corner anchors use
|
|
108
|
+
* both offsets. Width is optional and only meaningful for panel-shaped menus
|
|
109
|
+
* (the admin sidebar); it is ignored when absent.
|
|
110
|
+
*
|
|
111
|
+
* @param {object} menu A menu record (uses .position and .float)
|
|
112
|
+
* @returns {string} CSS declarations, semicolon-separated, no trailing ';'
|
|
113
|
+
*/
|
|
114
|
+
export function floatToCss(menu) {
|
|
115
|
+
if (!menu || menu.position !== 'floating') return '';
|
|
116
|
+
|
|
117
|
+
const f = (menu.float && typeof menu.float === 'object' && !Array.isArray(menu.float)) ? menu.float : {};
|
|
118
|
+
const anchor = FLOAT_ANCHORS.has(f.anchor) ? f.anchor : FLOAT_DEFAULTS.anchor;
|
|
119
|
+
const x = floatNumber(f.offsetX, FLOAT_DEFAULTS.offsetX, 0, 4000);
|
|
120
|
+
const y = floatNumber(f.offsetY, FLOAT_DEFAULTS.offsetY, 0, 4000);
|
|
121
|
+
const z = floatNumber(f.zIndex, FLOAT_DEFAULTS.zIndex, 0, 2147483647);
|
|
122
|
+
|
|
123
|
+
const decls = ['position:fixed', `z-index:${z}`];
|
|
124
|
+
|
|
125
|
+
if (anchor === 'L' || anchor === 'R') {
|
|
126
|
+
decls.push('top:50%', 'transform:translateY(-50%)');
|
|
127
|
+
decls.push(anchor === 'L' ? `left:${x}px` : `right:${x}px`);
|
|
128
|
+
} else {
|
|
129
|
+
decls.push(anchor[0] === 'T' ? `top:${y}px` : `bottom:${y}px`);
|
|
130
|
+
decls.push(anchor[1] === 'L' ? `left:${x}px` : `right:${x}px`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (f.width != null && f.width !== '') {
|
|
134
|
+
decls.push(`width:${floatNumber(f.width, 260, 120, 600)}px`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// A pinned panel must never grow past the viewport — it scrolls instead.
|
|
138
|
+
decls.push(`max-height:calc(100vh - ${(anchor === 'L' || anchor === 'R') ? 32 : y * 2}px)`);
|
|
139
|
+
|
|
140
|
+
return decls.join(';');
|
|
141
|
+
}
|
|
142
|
+
|
|
84
143
|
/**
|
|
85
144
|
* Walk a menu tree and resolve live badge counts. For any item whose
|
|
86
145
|
* `badge.countFrom` names a collection, the entry count replaces `badge.text`.
|
|
@@ -215,6 +274,7 @@ export async function createMenu(input) {
|
|
|
215
274
|
...(input.variant != null && {variant: input.variant}),
|
|
216
275
|
...(input.position != null && {position: input.position}),
|
|
217
276
|
...(input.appearOnHover != null && {appearOnHover: !!input.appearOnHover}),
|
|
277
|
+
...(input.float != null && {float: input.float}),
|
|
218
278
|
...(input.style != null && {style: input.style}),
|
|
219
279
|
items: Array.isArray(input.items) ? input.items : [],
|
|
220
280
|
meta: {
|
|
@@ -253,6 +313,7 @@ export async function updateMenu(slug, input) {
|
|
|
253
313
|
...(input.variant != null && {variant: input.variant}),
|
|
254
314
|
...(input.position != null && {position: input.position}),
|
|
255
315
|
...(input.appearOnHover != null && {appearOnHover: !!input.appearOnHover}),
|
|
316
|
+
...(input.float != null && {float: input.float}),
|
|
256
317
|
...(input.style != null && {style: input.style}),
|
|
257
318
|
items: Array.isArray(input.items) ? input.items : [],
|
|
258
319
|
meta: {
|
|
@@ -333,6 +394,21 @@ export function validateMenu(menu) {
|
|
|
333
394
|
if (menu.position != null && !POSITIONS.has(menu.position)) {
|
|
334
395
|
errors.push(`Invalid position "${menu.position}" — expected one of: ${[...POSITIONS].join(', ')}`);
|
|
335
396
|
}
|
|
397
|
+
if (menu.float != null) {
|
|
398
|
+
if (typeof menu.float !== 'object' || Array.isArray(menu.float)) {
|
|
399
|
+
errors.push('float must be an object');
|
|
400
|
+
} else {
|
|
401
|
+
if (menu.float.anchor != null && !FLOAT_ANCHORS.has(menu.float.anchor)) {
|
|
402
|
+
errors.push(`float.anchor must be one of: ${[...FLOAT_ANCHORS].join(', ')}`);
|
|
403
|
+
}
|
|
404
|
+
for (const key of ['offsetX', 'offsetY', 'width', 'zIndex']) {
|
|
405
|
+
const v = menu.float[key];
|
|
406
|
+
if (v != null && v !== '' && !Number.isFinite(Number(v))) {
|
|
407
|
+
errors.push(`float.${key} must be a number`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
336
412
|
if (!Array.isArray(menu.items)) {
|
|
337
413
|
errors.push('items must be an array');
|
|
338
414
|
} else {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
<!-- Site CSS -->
|
|
20
20
|
<link rel="stylesheet" href="/public/css/site.css">
|
|
21
|
-
<link rel="stylesheet" href="/public/css/forms.css">
|
|
21
|
+
<link rel="stylesheet" href="/public/css/forms.css?v=20260817-multi-chooser">
|
|
22
22
|
|
|
23
23
|
<!-- Font overrides -->
|
|
24
24
|
{{fontStyleTag}}
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
</script>
|
|
117
117
|
|
|
118
118
|
<!-- Site initialisation -->
|
|
119
|
-
<script src="/public/js/site.js?v=
|
|
119
|
+
<script src="/public/js/site.js?v=20260817-floating-menus" type="module"></script>
|
|
120
120
|
<script src="/public/js/collection-context.js?v=20260722-ctx-cursor" defer></script>
|
|
121
121
|
|
|
122
122
|
<!-- Interactive Collection Browser (used by [collection] shortcodes with
|
|
@@ -124,8 +124,8 @@
|
|
|
124
124
|
<script src="/public/js/collection-browser.js?v=20260523-browser"></script>
|
|
125
125
|
|
|
126
126
|
<!-- Core Forms (logic engine must load before renderer) -->
|
|
127
|
-
<script src="/public/js/form-logic-engine.js?v=
|
|
128
|
-
<script src="/public/js/forms.js?v=
|
|
127
|
+
<script src="/public/js/form-logic-engine.js?v=20260817-transition-speed"></script>
|
|
128
|
+
<script src="/public/js/forms.js?v=20260817-multi-chooser" type="module"></script>
|
|
129
129
|
|
|
130
130
|
<!-- Core effects runtime -->
|
|
131
131
|
<script src="/public/js/effects.js"></script>
|