domma-cms 0.118.0 → 0.120.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.
@@ -1,4 +1,4 @@
1
- import{api as h}from"../api.js";import{isPathAllowed as J}from"./admin-scope.js";import{colourToCss as U,applyFloat as Y,applyHighlight as Q,itemHighlightAttrs as ee}from"/public/js/menu-decor.mjs?v=20261009-menu-classes";import{placeTools as te,ensureLockedTools as ne,managePluginsItem as se,stripItemByUrl as ie,insertFoldersBeforeSystem as ae,pruneEmptyToolsFolder as oe,sidebarStateKey as re,canAny as B,filterByPermission as le,MANAGE_PLUGINS_URL as z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as ce,canArrange as de,startArrange as ue}from"./sidebar-arrange.js";import{collapseButtonMarkup as me,wireSidebarSize as fe,COLLAPSED_WIDTH as ge}from"./sidebar-size.js";function p(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const pe=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];async function he(e){if(!B(e,"projects"))return[];let s=[];try{s=await h.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const i=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(t=>{const d="#/projects/"+encodeURIComponent(t.slug),c=t.counts||{},o=[{text:"Overview",url:d,icon:t.icon||"folder"}];for(const r of i){const l=Number(c[r.key])||0;l<=0||o.push({text:`${r.text} (${l})`,url:d+r.path,icon:r.icon})}o.push({text:"Settings",url:d+"/settings",icon:"settings"});const m=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:o,...m&&{colour:"neutral",badge:{text:"off"}}}})}function be(e,s){for(const i of e)if(!i.hidden&&(i.key==="projects"||!i.key&&(i.text||"").toLowerCase()==="projects")){i.items=Array.isArray(i.items)?i.items.slice():[],i.items.push(...s);return}}function O(e,s){const i=D.includes(s.tone)?s.tone:"info",t=s.label||String(s.count);s.details?E.set(e,s.details):E.delete(e);const n=!s.inventory&&A.some(d=>d.url===e&&!d.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${i}" data-count-for="${p(e)}"${n} data-badge-label="${p(t)}" aria-label="${p(t)}">${p(String(s.count))}</span>`}function W(e){for(const s of e.querySelectorAll("details")){const i=s.querySelector(":scope > summary");if(!i)continue;let t=0,n=!1;const d=[];for(const l of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const u=Number(l.textContent)||0;u&&(t+=u,n||(n=l.classList.contains("sidebar-badge--danger")),d.push({text:l.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:l.dataset.badgeLabel,tone:l.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const c=`folder:${s.dataset.stateKey||i.textContent.trim()}`;let o=i.querySelector(":scope > .sidebar-badge--sum");if(!t){o?.remove(),E.delete(c);continue}o||(o=document.createElement("span"),i.querySelector(".sidebar-text")?.after(o));const m=i.querySelector(".sidebar-text")?.textContent.trim()||"This folder",r=d.map(l=>`${l.text}: ${l.meta}`).join(" \xB7 ");o.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${n?"danger":"info"}`,o.dataset.countFor=c,o.dataset.badgeLabel=r,o.setAttribute("aria-label",r),o.textContent=String(t),E.set(c,{title:m,items:d})}}const E=new Map;function ye(e,s){if(!e.url||!s)return"";const i=s[e.url];return!i||!(i.count>0)&&!i.showZero?"":O(e.url,i)}const D=["info","attention","danger","live"],ve={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function _(e,{fallbackLabel:s="",tone:i="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const n=typeof e?.label=="string"&&e.label||(s?`${t} - ${s}`:String(t)),d={count:Math.round(t),label:n,tone:D.includes(e?.tone)?e.tone:i};return e?.details&&typeof e.details=="object"&&(d.details=e.details),e?.inventory===!0&&(d.inventory=!0),e?.showZero===!0&&(d.showZero=!0),d}function G(e,s,i=null){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const b=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${p(b)}" aria-hidden="true"></div>`}const t=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",n=e.icon?`<span data-icon="${p(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${p(t)}</span>`,d=p(e.text||""),c=e.url?p(e.url):"",o=Array.isArray(e.items)&&e.items.length>0,m=e.colour?U(e.colour):"",r=ee(e),l=[r.hover&&` data-menu-item-hover="${p(r.hover)}"`,r.active&&` data-menu-item-active="${p(r.active)}"`].filter(Boolean).join(""),u=[m&&`color:${p(m)}`,r.accent&&`--dm-menu-highlight:${p(r.accent)}`].filter(Boolean).join(";"),y=u?` style="${u}"`:"";let v="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const b=e.badge.variant?U(e.badge.variant):"";v=`<span class="dm-menu-badge"${b?` style="background:${p(b)};color:var(--dm-text-inverse,#fff)"`:""}>${p(String(e.badge.text))}</span>`}if(o){const b=re(e,i),C=S.get(b)!==!1,L=e.items.map(j=>G(j,s,b)).join("");return`<details data-state-key="${p(b)}"${C?" open":""}>
1
+ import{api as h}from"../api.js";import{isPathAllowed as J}from"./admin-scope.js";import{colourToCss as U,applyFloat as Y,applyHighlight as Q,itemHighlightAttrs as ee}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{placeTools as te,ensureLockedTools as ne,managePluginsItem as se,stripItemByUrl as ie,insertFoldersBeforeSystem as ae,pruneEmptyToolsFolder as oe,sidebarStateKey as re,canAny as B,filterByPermission as le,MANAGE_PLUGINS_URL as z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as ce,canArrange as de,startArrange as ue}from"./sidebar-arrange.js";import{collapseButtonMarkup as me,wireSidebarSize as fe,COLLAPSED_WIDTH as ge}from"./sidebar-size.js";function p(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const pe=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];async function he(e){if(!B(e,"projects"))return[];let s=[];try{s=await h.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const i=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(t=>{const d="#/projects/"+encodeURIComponent(t.slug),c=t.counts||{},o=[{text:"Overview",url:d,icon:t.icon||"folder"}];for(const r of i){const l=Number(c[r.key])||0;l<=0||o.push({text:`${r.text} (${l})`,url:d+r.path,icon:r.icon})}o.push({text:"Settings",url:d+"/settings",icon:"settings"});const m=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:o,...m&&{colour:"neutral",badge:{text:"off"}}}})}function be(e,s){for(const i of e)if(!i.hidden&&(i.key==="projects"||!i.key&&(i.text||"").toLowerCase()==="projects")){i.items=Array.isArray(i.items)?i.items.slice():[],i.items.push(...s);return}}function O(e,s){const i=D.includes(s.tone)?s.tone:"info",t=s.label||String(s.count);s.details?E.set(e,s.details):E.delete(e);const n=!s.inventory&&A.some(d=>d.url===e&&!d.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${i}" data-count-for="${p(e)}"${n} data-badge-label="${p(t)}" aria-label="${p(t)}">${p(String(s.count))}</span>`}function W(e){for(const s of e.querySelectorAll("details")){const i=s.querySelector(":scope > summary");if(!i)continue;let t=0,n=!1;const d=[];for(const l of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const u=Number(l.textContent)||0;u&&(t+=u,n||(n=l.classList.contains("sidebar-badge--danger")),d.push({text:l.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:l.dataset.badgeLabel,tone:l.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const c=`folder:${s.dataset.stateKey||i.textContent.trim()}`;let o=i.querySelector(":scope > .sidebar-badge--sum");if(!t){o?.remove(),E.delete(c);continue}o||(o=document.createElement("span"),i.querySelector(".sidebar-text")?.after(o));const m=i.querySelector(".sidebar-text")?.textContent.trim()||"This folder",r=d.map(l=>`${l.text}: ${l.meta}`).join(" \xB7 ");o.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${n?"danger":"info"}`,o.dataset.countFor=c,o.dataset.badgeLabel=r,o.setAttribute("aria-label",r),o.textContent=String(t),E.set(c,{title:m,items:d})}}const E=new Map;function ye(e,s){if(!e.url||!s)return"";const i=s[e.url];return!i||!(i.count>0)&&!i.showZero?"":O(e.url,i)}const D=["info","attention","danger","live"],ve={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function _(e,{fallbackLabel:s="",tone:i="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const n=typeof e?.label=="string"&&e.label||(s?`${t} - ${s}`:String(t)),d={count:Math.round(t),label:n,tone:D.includes(e?.tone)?e.tone:i};return e?.details&&typeof e.details=="object"&&(d.details=e.details),e?.inventory===!0&&(d.inventory=!0),e?.showZero===!0&&(d.showZero=!0),d}function G(e,s,i=null){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const b=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${p(b)}" aria-hidden="true"></div>`}const t=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",n=e.icon?`<span data-icon="${p(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${p(t)}</span>`,d=p(e.text||""),c=e.url?p(e.url):"",o=Array.isArray(e.items)&&e.items.length>0,m=e.colour?U(e.colour):"",r=ee(e),l=[r.hover&&` data-menu-item-hover="${p(r.hover)}"`,r.active&&` data-menu-item-active="${p(r.active)}"`].filter(Boolean).join(""),u=[m&&`color:${p(m)}`,r.accent&&`--dm-menu-highlight:${p(r.accent)}`].filter(Boolean).join(";"),y=u?` style="${u}"`:"";let v="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const b=e.badge.variant?U(e.badge.variant):"";v=`<span class="dm-menu-badge"${b?` style="background:${p(b)};color:var(--dm-text-inverse,#fff)"`:""}>${p(String(e.badge.text))}</span>`}if(o){const b=re(e,i),C=S.get(b)!==!1,L=e.items.map(j=>G(j,s,b)).join("");return`<details data-state-key="${p(b)}"${C?" open":""}>
2
2
  <summary${y}>${n} <span class="sidebar-text">${d}</span>${v}</summary>
3
3
  <div class="sidebar-children">${L}</div>
4
4
  </details>`}return`<a href="${c}" class="sidebar-link" data-url="${c}"${y}${l}>${n} <span class="sidebar-text">${d}</span>${ye(e,s)}${v}</a>`}async function xe(e=[],s=[],i=null){const t=g=>g.then(a=>Array.isArray(a)?a.length:Array.isArray(a?.entries)?a.entries.length:0).catch(()=>0);A=Ce(e,i);const n=new Set(A.map(g=>g.url)),d={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},c=(g,a)=>B(s,g)&&!n.has(d[g])?a():Promise.resolve(0),[o,m,r,l,u,y,v,b,C,L,j,F,N,P]=await Promise.all([c("pages",()=>t(h.pages.list())),c("media",()=>t(h.media.list())),c("menus",()=>t(h.menus.list())),c("context-menus",()=>t(h.contextMenus.list())),c("theme",()=>h.theme.get().then(g=>(g?.overrides||[]).length).catch(()=>0)),c("collections",()=>t(h.collections.list())),c("forms",()=>t(h.forms.list())),c("views",()=>t(h.views.list())),c("actions",()=>t(h.actions.list())),c("blocks",()=>t(h.blocks.list())),c("components",()=>t(h.components.list())),c("users",()=>t(h.users.list())),c("plugins",()=>t(h.plugins.list())),c("notifications",()=>h.system?.notifications?.unreadCount?.().then(g=>g?.count??0).catch(()=>0)??Promise.resolve(0))]),q={};await Promise.all(A.map(async g=>{const a=await K(g);a&&(q[g.url]=a)}));const T={"#/pages":o,"#/media":m,"#/menus":r,"#/context-menus":l,"#/theme":u,"#/collections":y,"#/forms":v,"#/views":b,"#/actions":C,"#/blocks":L,"#/components":j,"#/users":F,"#/plugins":N,"#/system/notifications":P},k={};for(const[g,a]of Object.entries(T)){if(n.has(g))continue;const[x,f]=ve[g]??["item","items"],w=_(a);w&&(k[g]={...w,label:`${w.count} ${w.count===1?x:f}`})}return{...k,...q}}let A=[];const $e="dm:sidebar-count",Se=6e4;function Ce(e=[],s=null){const i=[],t=n=>{if(n&&!(s&&typeof n.url=="string"&&n.url.startsWith("#/")&&!J(s,n.url.slice(1)))){n.url&&typeof n.countUrl=="string"&&n.countUrl&&i.push({url:n.url,countUrl:n.countUrl,text:n.text||"",countLabel:n.countLabel||"",inventory:n.inventory===!0});for(const d of[...n.children||[],...n.items||[]])t(d)}};for(const n of e??[])t(n?.item??n);return i}async function K(e){try{const s=new Date,i=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${i}`;return _(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function V(e,s,i){const t=[...e.querySelectorAll("a.sidebar-link")].find(d=>d.dataset.url===s);if(!t)return;const n=t.querySelector(".sidebar-badge[data-count-for]");if(!i||!(i.count>0)&&!i.showZero)n?.remove();else{const d=document.createRange();d.selectNodeContents(t);const c=d.createContextualFragment(O(s,i)).firstElementChild;n?n.replaceWith(c):t.querySelector(".sidebar-text")?.after(c)}R.set(s,i),W(e)}const R=new Map;async function I(e,s=null){const i=s?A.filter(t=>t.url===s):A;await Promise.all(i.map(async t=>{const n=await K(t),d=R.get(t.url);JSON.stringify(d??null)!==JSON.stringify(n)&&V(e,t.url,n)}))}function we(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener($e,s=>{const i=s.detail||{};if(i.url)if(i.count!==void 0){const t=A.find(n=>n.url===i.url);V(e,i.url,_(i,{fallbackLabel:t?.countLabel||t?.text||""}))}else I(e,i.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&I(e)},Se),document.addEventListener("visibilitychange",()=>{document.hidden||I(e)}),ke(e))}function Z(e,s,i){const t=(c,o,m)=>{const r=document.createElement(c);return o&&(r.className=o),m!=null&&(r.textContent=String(m)),r};if(!i){e.textContent=s;return}e.appendChild(t("strong","dm-badge-pop-title",i.title||s)),(i.rows?.length||!i.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",s));const n=Array.isArray(i.rows)?i.rows.slice(0,6):[];if(n.length){const c=t("dl","dm-badge-pop-rows");for(const o of n)c.appendChild(t("dt",null,o.label)),c.appendChild(t("dd",o.tone==="danger"?"is-danger":null,o.value));e.appendChild(c)}const d=Array.isArray(i.items)?i.items.slice(0,6):[];if(d.length){const c=t("ul","dm-badge-pop-items");for(const o of d){const m=t("li",o.tone==="danger"?"is-danger":null);m.appendChild(t("span","dm-badge-pop-text",o.text)),o.meta&&m.appendChild(t("span","dm-badge-pop-meta",o.meta)),c.appendChild(m)}e.appendChild(c)}else i.empty&&e.appendChild(t("p","dm-badge-pop-empty",i.empty))}function ke(e){let s=null,i=null;const t=()=>e.classList.contains("is-collapsed"),n=()=>{clearTimeout(i),s?.remove(),s=null},d=(r,l)=>{n(),i=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),l(s),document.body.appendChild(s);const u=r.getBoundingClientRect(),y=s.getBoundingClientRect(),v=Math.min(u.right+10,innerWidth-y.width-8);s.style.left=`${Math.max(8,v)}px`,s.style.top=`${Math.max(8,Math.min(u.top+u.height/2-y.height/2,innerHeight-y.height-8))}px`},150)},c=r=>{const l=r?.dataset.badgeLabel;if(!l)return n();d(r,u=>Z(u,l,E.get(r.dataset.countFor)))},o=r=>{const l=r.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",u=r.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");d(r,y=>{const v=u&&E.get(u.dataset.countFor);if(v)return Z(y,u.dataset.badgeLabel,{...v,title:v.title||l});if(!u){y.textContent=l;return}const b=document.createElement("strong");b.className="dm-badge-pop-title",b.textContent=l;const C=document.createElement("p");C.className="dm-badge-pop-sub",C.textContent=u.dataset.badgeLabel,y.append(b,C)})},m=r=>{const l=r.closest?.("a.sidebar-link, summary");return l&&e.contains(l)?l:null};e.addEventListener("mouseover",r=>{if(t()){const u=m(r.target);u&&!u.contains(r.relatedTarget)&&o(u);return}const l=r.target.closest?.(".sidebar-badge[data-badge-label]");l&&c(l)}),e.addEventListener("mouseout",r=>{if(t()){const l=m(r.target);l&&!l.contains(r.relatedTarget)&&n();return}r.target.closest?.(".sidebar-badge[data-badge-label]")&&n()}),e.addEventListener("focusin",r=>{const l=r.target.closest?.("a, summary");if(t()&&l&&e.contains(l)&&l.querySelector(":scope > .sidebar-text"))return o(l);const u=l?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");u?c(u):n()}),e.addEventListener("focusout",n),e.addEventListener("click",n),window.addEventListener("scroll",n,!0),window.addEventListener("hashchange",n)}async function Ae(e=[]){if(!B(e,"settings"))return"Admin";try{const s=await h.settings.get();return s?.adminBrand?.title||s?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:s,adminScope:i=null}){const t=$(e).get(0);if(!t)return;let n,d=null,c=null,o=null,m=null,r=null;try{const a=await h.get("/sidebar/menu");n=Array.isArray(a?.items)?a.items:null,d=a?.variant||null,c=a?.position||null,o=a?.style||null,m=a?.float||null,r=a?.highlight||null}catch{n=null}(!n||!n.some(a=>a&&(!a.hidden||a.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=pe.slice());let l=[];try{l=await H.get("/api/sidebar/registered-items")||[]}catch{}n=ie(n,z),n=te(n,l.filter(a=>(a?.item??a)?.url!==z)),n=ne(n),n=ae(n,[se()]);const u=await he(s);u.length&&be(n,u),n=le(n,s,i),n=oe(n);const[y,v]=await Promise.all([xe(l.filter(a=>B(s,a?.item?.permission)),s,i).catch(()=>({})),Ae(s)]),b=d?` dm-admin-sidebar--${p(d)}`:"";function C(a,x="px"){if(a==null)return a;const f=String(a).trim();return/^\d+(\.\d+)?$/.test(f)?`${f}${x}`:f}let L="";if(o){const a=[];o.fontFamily&&a.push(`font-family: ${o.fontFamily}, sans-serif`),o.fontSize&&a.push(`font-size: ${C(o.fontSize)}`),o.fontWeight&&a.push(`font-weight: ${o.fontWeight}`),o.letterSpacing&&a.push(`letter-spacing: ${C(o.letterSpacing,"em")}`);const x=[];if(a.length&&x.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${a.join("; ")} }`),o.iconSize){const f=C(o.iconSize);x.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${f} !important; height: ${f} !important; }`)}x.length&&(L=`<style data-admin-sidebar-style>${x.join(" ")}</style>`)}const j=de(s)?ce():"",F=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span><span class="dm-admin-sidebar-title">${p(v)}</span>${j}${me()}</div>`,N=n.some(a=>a&&a.type==="spacer"&&a.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",P=`${L}<nav class="dm-admin-sidebar${b}${N}">${F}${n.map(a=>G(a,y)).join("")}</nav>`,q=document.createRange();q.selectNodeContents(t);const T=q.createContextualFragment(P);t.replaceChildren(T),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>ue(t));for(const a of A)R.set(a.url,y[a.url]??null);W(t),we(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),Q(t,{highlight:r});const k=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(a=>{t.style[a]=""}),t.classList.remove("dm-menu-floating"),k&&(k.style.paddingLeft="",k.style.paddingRight=""),t.__dmGutter=()=>{if(c!=="floating"||!k)return;const a=m&&m.anchor||"TL",x=Number.isFinite(Number(m?.offsetX))?Number(m.offsetX):16,f=t.classList.contains("is-collapsed")?ge:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,w=Math.round(f+x*2)+"px";a==="R"||a==="TR"||a==="BR"?k.style.paddingRight=w:k.style.paddingLeft=w},c==="floating"&&Y(t,{position:"floating",float:m||{}}),t.querySelectorAll("details").forEach(a=>{a.addEventListener("toggle",function(){const x=this.getAttribute("data-state-key");x&&S.set(x,this.open);const f=this.querySelector(":scope > .sidebar-children");if(f)if(this.open){const w=f.scrollHeight;f.style.maxHeight="0",requestAnimationFrame(()=>{f.style.maxHeight=w+"px",f.addEventListener("transitionend",function X(){f.style.maxHeight="none",f.removeEventListener("transitionend",X)})})}else{const w=f.scrollHeight;f.style.maxHeight=w+"px",requestAnimationFrame(()=>{f.style.maxHeight="0"})}})}),fe(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function g(){const a=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${a}"]`).addClass("active")}g(),t.__dmSetActive=g}
@@ -143,6 +143,11 @@
143
143
  <code>url</code> must be a path, <code>#anchor</code>, <code>?query</code> or an <code>http(s):</code>,
144
144
  <code>mailto:</code> or <code>tel:</code> address - create and update answer <code>400</code> otherwise. Leave
145
145
  either key out for the default button.</p>
146
+ <p>Designer classes (optional): <code>settings.class</code> goes on the <code>&lt;form&gt;</code>,
147
+ <code>settings.submitClass</code> on the submit button (and Finish on a multi-step form), and a field's own
148
+ <code>class</code> on its wrapper (<code>[data-field="name"]</code>). Each is CSS class names separated by spaces -
149
+ letters, digits, hyphens and underscores, not starting with a digit; create and update answer <code>400</code>
150
+ otherwise.</p>
146
151
  <p>The create and update responses carry a <code>warnings</code> array when the form and its target collection
147
152
  disagree - the collection is missing, requires a field the form does not collect, or lacks a field the form
148
153
  sends. Saving still succeeds.</p>
@@ -149,6 +149,11 @@
149
149
  </select>
150
150
  <p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">A grid form lays out in two
151
151
  or three columns. Use a field's Column Span to make one field wider than the rest.</p>
152
+ </div>
153
+ <div class="mb-3" style="margin-top:1rem">
154
+ <label class="form-label" for="setting-form-class">CSS classes <span data-help-title="CSS classes" data-help="Your own class names, separated by spaces, added to the form element so you can style it in Custom CSS. Each field has its own CSS classes box too, and the submit button has one under Submit Button."></span></label>
155
+ <input id="setting-form-class" type="text" class="form-input" placeholder="contact-form is-compact" autocomplete="off" spellcheck="false">
156
+ <p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">Letters, digits, hyphens and underscores; no dots.</p>
152
157
  </div>
153
158
  </div>
154
159
  </div>
@@ -204,6 +209,12 @@
204
209
  </select>
205
210
  </div>
206
211
  </div>
212
+ <div class="row">
213
+ <div class="col-12 mb-3">
214
+ <label class="form-label" for="setting-submit-class">CSS classes <span data-help-title="Submit button CSS classes" data-help="Your own class names, separated by spaces, added to the submit button - and to Finish on a multi-step form - on top of the style chosen above. Style them in Custom CSS."></span></label>
215
+ <input id="setting-submit-class" type="text" class="form-input" placeholder="cta-button" autocomplete="off" spellcheck="false">
216
+ </div>
217
+ </div>
207
218
  <div class="row">
208
219
  <div class="col-6 mb-3">
209
220
  <label class="form-label">Icon before the text <span data-help-title="Button icons" data-help="An icon either side of the button's text - a paper plane after Send, say. Type an icon's name or browse for one; leave both empty for text only. On a multi-step form the icons go on the Finish button, not on Next. If a trigger ends the form early, its Finish keeps them."></span></label>