domma-cms 1.10.14 → 1.10.16
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/CHANGELOG.md +14 -0
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +4 -4
- package/admin/index.html +2 -2
- package/admin/js/api.js +1 -1
- package/admin/js/lib/hotkeys-ui.js +7 -16
- package/admin/js/lib/hotkeys.js +1 -1
- package/admin/js/lib/media-arrange.js +1 -1
- package/admin/js/lib/palette-commands.js +1 -0
- package/admin/js/templates/media.html +19 -10
- package/admin/js/views/form-editor.js +6 -6
- package/admin/js/views/media.js +3 -3
- package/docs/configuration.md +7 -3
- package/docs/markdown-shortcodes.md +3 -2
- package/package.json +2 -2
- package/public/css/forms.css +1 -1
- package/public/css/site.css +1 -1
- package/public/js/collection-browser.js +2 -2
- package/public/js/collection-range.mjs +1 -0
- package/public/js/form-files.mjs +1 -0
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/site.js +1 -1
- package/server/routes/api/forms.js +63 -38
- package/server/routes/api/media.js +25 -2
- package/server/server.js +8 -2
- package/server/services/mediaTypes.js +22 -0
- package/server/templates/page.html +8 -6
package/admin/js/views/media.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as N,getToken as ot,refreshAccessToken as it}from"../api.js";import{addBannerAction as rt}from"../lib/plugin-chrome.js";import{SHAPES as lt,whileLoading as dt}from"../lib/loading.js";import{helpPopovers as Ie}from"../lib/help-popover.js";import{onceClosed as ct,openMenuAt as Fe,settingsStore as mt,sidebarCount as le,viewLifetime as ut}from"../lib/tool-kit.js";import{openImageEditor as pt}from"../lib/image-editor.js";import{KIND_LABEL as Ue,arrangeMedia as ft,formatBytes as Pe,isEditable as de,isPreviewable as vt,kindCounts as ht,kindOf as bt,markdownFor as ce,mediaTile as gt,nameRefusal as yt,normaliseLimits as _e,normalisePrefs as wt,dropzoneRules as Et,rejectText as kt,safeName as w,shortName as Be,uploadErrorText as xt,uploadHint as $t}from"../lib/media-arrange.js";import{fmtDateTime as Mt}from"../lib/dates.js";const He=/^#\/media\/?(\?.*)?$/,me="#/media",K="media.draft",Oe=mt("media.prefs",wt),St=`
|
|
2
2
|
<div class="mdx-tile" data-name="{{name}}" tabindex="0" title="{{name}}">
|
|
3
3
|
<div class="mdx-thumb">
|
|
4
4
|
<img data-bind-hidden="!preview" data-bind-src="thumb" alt="" loading="lazy" decoding="async">
|
|
@@ -7,7 +7,7 @@ import{api as z}from"../api.js";import{addBannerAction as Be}from"../lib/plugin-
|
|
|
7
7
|
<span class="mdx-chip">{{kindLabel}}</span>
|
|
8
8
|
<button type="button" class="mdx-kebab" aria-label="Actions for {{name}}" title="Actions">\u22EE</button>
|
|
9
9
|
<div class="mdx-info"><span class="mdx-name">{{name}}</span><span class="mdx-meta">{{meta}}</span></div>
|
|
10
|
-
</div>`,q=
|
|
10
|
+
</div>`,q=p=>String(p??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Dt(p){const r=document.createElement("template");return r.innerHTML=p.trim(),r.content.firstElementChild}const d=ut(He);let ue=null;async function j(p,r){try{await navigator.clipboard.writeText(p),E.toast(`${r} copied.`,{type:"success"})}catch{E.toast("Copy failed - the browser refused the clipboard.",{type:"error"})}}export const mediaView={templateUrl:"/admin/js/templates/media.html",async onMount(p){d.reset();const r=p.get(0).querySelector(".mdx")||p.get(0),m=()=>He.test(location.hash)&&r.isConnected,pe=Oe.read(),v=M.observable([]),fe=M.observable(!0),V=M.observable(""),b=M.observable(pe.kind),Y=M.observable(pe.sort),ve=M.observable(""),F=M.observable(!1),he=M.observable("");let k=_e(null),G=String(Date.now());d.add(M.effect(()=>Oe.write({kind:b.value,sort:Y.value})));const U=M.observable([]);d.add(M.effect(()=>{const e=Date.now();U.value=ft(v.value,{kind:b.value,query:ve.value,sort:Y.value}).map(t=>gt(t,{now:e,stamp:G}))}));const be=M.computed(()=>ht(v.value));d.add(M.effect(()=>{b.value!=="all"&&!be.value[b.value]&&v.value.length&&(b.value="all")}));const Ke=M.computed(()=>{const e=v.value,t=U.value.length,s=Pe(e.reduce((n,o)=>n+(Number(o.size)||0),0));return t===e.length?`${e.length} file${e.length===1?"":"s"} \xB7 ${s}`:`${t} of ${e.length} \xB7 ${s} in all`}),je=M.computed(()=>v.value.length?"No files match.":"Nothing uploaded yet - drop files here, or Upload in the banner."),x=e=>v.peek().find(t=>t.name===e);async function $(){try{const e=await dt(r.querySelector(".dm-loading-slot"),N.media.list(),lt.tiles);if(!m())return;G=String(Date.now()),v.value=Array.isArray(e)?e:[],V.value=""}catch(e){V.value=`Could not load the media: ${e.message||e}`}finally{fe.value=!1}}async function W(e){if(!await E.confirm(`Delete "${q(e.name)}"? Pages that show it will show a broken image or link. This cannot be undone.`))return!1;try{return await N.media.delete(e.name),E.toast(`Deleted "${e.name}".`,{type:"success"}),le(me),m()&&await $(),!0}catch(t){return E.toast(`Not deleted: ${t.message||t}`,{type:"error"}),!1}}async function ge(e){const t=await pt(e);return t&&m()&&(le(me),await $()),t}const J=e=>window.location.origin+e.url;let g=null,ye=!1;const Q=new Set;function D(e,{focusRename:t=!1,draftName:s=null}={}){const n=x(e);if(!n){E.toast(`"${e}" is not here any more.`,{type:"warning"});return}const o=M.observable(s??n.name),H=M.observable(!1),Ce=M.computed(()=>{const a=o.value.trim();if(!a)return"A file needs a name.";const i=w(a);return i!==n.name&&x(i)?`"${i}" is already here.`:""}),Ze=M.computed(()=>{const a=o.value.trim(),i=w(a);return a&&i!==a?`Saved as "${i}" - only letters, numbers, dots, hyphens and underscores are kept.`:""}),Te=()=>o.peek().trim()!==n.name,ze={newName:o,saving:H,renameErr:Ce,renameHint:Ze,cannotRename:M.computed(()=>H.value||!!Ce.value||w(o.value.trim())===n.name),editable:de(n.name)},u=E.slideover({title:q(n.name),size:"md",position:"right"}),c=Dt(`
|
|
11
11
|
<div class="mdx-panel">
|
|
12
12
|
<div class="mdx-preview"></div>
|
|
13
13
|
<dl class="mdx-facts"></dl>
|
|
@@ -37,4 +37,4 @@ import{api as z}from"../api.js";import{addBannerAction as Be}from"../lib/plugin-
|
|
|
37
37
|
<a class="btn btn-sm btn-ghost mdx-open" target="_blank" rel="noopener"><span data-icon="external-link" data-icon-size="13"></span> Open</a>
|
|
38
38
|
<button type="button" class="btn btn-sm btn-outline" data-on-click="edit" data-bind-hidden="!editable"><span data-icon="edit" data-icon-size="13"></span> Edit image</button>
|
|
39
39
|
</div>
|
|
40
|
-
</div>`),
|
|
40
|
+
</div>`),se=c.querySelector(".mdx-preview"),A=bt(n.name);if(vt(n.name)){const a=document.createElement("img");a.src=`${n.url}?t=${encodeURIComponent(G)}`,a.alt=n.name,se.appendChild(a)}else if(A==="video"||A==="audio"){const a=document.createElement(A);a.src=n.url,a.controls=!0,a.preload="metadata",se.appendChild(a)}else{const a=document.createElement("span");a.setAttribute("data-icon",A==="document"?"file-text":"paperclip"),a.setAttribute("data-icon-size","40"),se.appendChild(a)}const et=c.querySelector(".mdx-facts"),O=(a,i)=>{const qe=document.createElement("dt");qe.textContent=a;const re=document.createElement("dd");return re.textContent=i,et.append(qe,re),re};if(O("Type",`${Ue[A]==="Images"?"Image":Ue[A].replace(/s$/,"")} (${(n.name.split(".").pop()||"").toUpperCase()})`),O("Size",Pe(n.size)),O("Uploaded",n.createdAt?Mt(n.createdAt):"-"),de(n.name)){const a=O("Dimensions","\u2026");N.media.info(n.name).then(i=>{a.textContent=i?.width?`${i.width} \xD7 ${i.height} px`:"-"}).catch(()=>{a.textContent="-"})}c.querySelector(".mdx-url").value=J(n),c.querySelector(".mdx-md").value=ce(n),c.querySelector(".mdx-open").href=n.url;let R=!1;async function Re(){if(!ze.cannotRename.peek()){H.value=!0;try{const a=await N.media.rename(n.name,o.peek().trim());E.toast(`Renamed to "${a?.name||w(o.peek().trim())}".`,{type:"success"}),R=!0,u.close(),m()&&await $()}catch(a){E.toast(`Not renamed: ${a.message||a}`,{type:"error"})}finally{H.value=!1}}}u.element.appendChild(c);const tt=Ie(c),nt=M.applyBindings(ze,c,{methods:{rename:Re,copyUrl:()=>j(J(n),"Address"),copyMd:()=>j(ce(n),"Markdown"),async del(){await W(n)&&(R=!0,u.close())},async edit(){R=!0,u.close(),await ge(n)}}}),oe=c.querySelector("#mdx-rename"),at=a=>{a.key==="Enter"&&a.target===oe&&(a.preventDefault(),Re())};c.addEventListener("keydown",at);const Ne={name:n.name,dirty:Te,newName:()=>o.peek()};g=Ne,Q.add(u),ct(u,()=>{nt.dispose(),tt(),c.querySelectorAll("video, audio").forEach(a=>{try{a.pause()}catch{}}),Q.delete(u),g===Ne&&(g=null)});const st=u.close.bind(u);let ie=!1;u.close=async a=>{if(!R&&!ye&&a!==!0&&Te()){if(ie)return;ie=!0;const i=await E.confirm("Discard the new name you typed?");if(ie=!1,!i)return}return R=!0,st()},I.scan(c),u.open(),(t||s)&&(oe.focus(),oe.select())}function Ve(e){const t=x(e);return t?[{header:t.name},{label:"Details\u2026",icon:"info",action:()=>D(t.name)},{label:"Copy address",icon:"link",action:()=>j(J(t),"Address")},{label:"Copy for a page",icon:"copy",action:()=>j(ce(t),"Markdown")},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(t.url,"_blank","noopener")},{type:"divider"},{label:"Rename\u2026",icon:"type",action:()=>D(t.name,{focusRename:!0})},...de(t.name)?[{label:"Edit image\u2026",icon:"edit",action:()=>ge(t)}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>W(t)}]:[]}const h=r.querySelector(".mdx-grid");h.innerHTML=St;const L=e=>()=>{b.value=e},Ye=M.applyBindings({files:v,loading:fe,loadError:V,kind:b,sort:Y,query:ve,tiles:U,counts:be,totalText:Ke,emptyText:je,hintText:he,dragging:F},r,{methods:{kindAll:L("all"),kindImage:L("image"),kindVideo:L("video"),kindAudio:L("audio"),kindDocument:L("document"),kindOther:L("other")}});d.add(()=>Ye.dispose()),d.add(Ie(r,{position:"bottom"})),d.add(M.effect(()=>{U.value,requestAnimationFrame(()=>{h.isConnected&&I.scan(h)})}));const Ge=E.contextMenu(h,{cursor:"glyph",match:".mdx-tile",inherit:!1,density:"compact",items:e=>Ve(e.dataset.name)});d.add(()=>Ge?.destroy?.());const we=e=>{const t=e.target.closest(".mdx-tile");if(!t)return;const s=e.target.closest(".mdx-kebab");if(s){Fe(s);return}D(t.dataset.name)},Ee=e=>{const t=e.target.closest(".mdx-tile");if(!t||e.target!==t)return;const s=x(t.dataset.name);s&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),Fe(t)):e.key==="Enter"?D(s.name):e.key==="F2"?(e.preventDefault(),D(s.name,{focusRename:!0})):e.key==="Delete"&&(e.preventDefault(),W(s)))};h.addEventListener("click",we),h.addEventListener("keydown",Ee),d.add(()=>{h.removeEventListener("click",we),h.removeEventListener("keydown",Ee)});const X=new WeakSet;let P=[],C=[],Z=0,ee=0;const ke=e=>setTimeout(e,0),We=()=>{if(!C.length)return;const e=C;C=[],E.toast(`Not added - ${e.slice(0,4).join("; ")}${e.length>4?` and ${e.length-4} more`:""}`,{type:"error",duration:9e3})},te=e=>{C.length||ke(We),C.push(e)};async function Je(){const e=P;if(P=[],!e.length||!m())return;const t=[...new Set(e.map(o=>w(o.name)))],s=t.slice(0,5).map(o=>`"${q(o)}"`).join(", ")+(t.length>5?` and ${t.length-5} more`:""),n=t.length===1;await E.confirm(`${n?"A file":`${t.length} files`} with the same name ${n?"is":"are"} already here: ${s}. Replace ${n?"it":"them"}?`)&&(T||(e.forEach(o=>X.add(o)),l.addFiles(e)))}const Qe=r.querySelector(".mdx-file-input"),Xe=r.querySelector(".mdx-zone");let T=!1,_=0,ne=null;const l=E.dropzone(Xe,{input:Qe,multiple:!0,previews:!0,upload:{url:"/api/media",field:"file",concurrency:3,headers:()=>{const e=ot();return e?{Authorization:`Bearer ${e}`}:{}}},labels:{cancel:"Cancel the upload of {name}",remove:"Remove {name} from the list"},onBeforeAdd(e){const t=yt(e.name,k);if(t)return te(`${Be(e.name)}: ${t}`),!1;if(X.has(e.file))return X.delete(e.file),!0;const s=w(e.name),n=l.getEntries().filter(o=>w(o.name)===s);return n.some(o=>o.status==="uploading"||o.status==="queued")?(te(`${Be(e.name)}: a file of that name is uploading already`),!1):(n.forEach(o=>{o.status!=="done"&&l.removeFile(o.id)}),x(s)?(P.length||ke(Je),P.push(e.file),!1):!0)},onReject(e,t){te(kt(e.name,t,k))},onSuccess(){Z++},onError(e,t){if(t.status===401&&!e._mdxRefreshed&&S.get("auth_refresh_token")){e._mdxRefreshed=!0,t.message="signing in again\u2026",_++,it().then(()=>{T||l.retry(e.id)}).catch(()=>{}).finally(()=>{_--,ne?.()});return}ee++,t.message=xt(t,k)},onComplete(){const e=Z,t=ee;if(Z=0,ee=0,e&&E.toast(`Uploaded ${e} file${e===1?"":"s"}.`,{type:"success"}),t){const s=`${t} file${t===1?"":"s"} not uploaded`;E.toast(m()?`${s} - Retry or remove ${t===1?"it":"them"} in the list.`:`${s}.`,{type:"error",duration:9e3})}l.getEntries().filter(s=>s.status==="done").forEach(s=>l.removeFile(s.id)),e&&(le(me),m()?$():ue?.())}}),ae=()=>l.getEntries().some(e=>e.status==="uploading"||e.status==="queued");ue=()=>{m()&&$()};const xe=()=>{T||(T=!0,l.destroy())};d.add(()=>{ue=null,ne=()=>{!ae()&&!_&&xe()},ae()||_?l.on("complete",ne):xe()}),N.media.limits().then(e=>{T||!m()||(k=_e(e),l.setOptions(Et(k)),he.value=$t(k))}).catch(()=>{}),rt(p,{id:"media-upload",icon:"upload",title:"Upload files",onClick:()=>l.browse()});const B=e=>[...e.dataTransfer?.types||[]].includes("Files");let z=0;const $e=e=>{B(e)&&(e.preventDefault(),z++,F.value=!0)},Me=e=>{B(e)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")},Se=e=>{B(e)&&(z=Math.max(0,z-1),z||(F.value=!1))},De=e=>{B(e)&&(z=0,F.value=!1,!e.defaultPrevented&&(e.preventDefault(),l.addFiles(e.dataTransfer.files)))},Le=e=>{const t=[...e.clipboardData?.files||[]];e.defaultPrevented||!t.length||e.target?.closest?.('input, textarea, [contenteditable=""], [contenteditable="true"]')&&[...e.clipboardData.types||[]].includes("text/plain")||(e.preventDefault(),l.addFiles(t))},f=p.get(0);f.addEventListener("dragenter",$e),f.addEventListener("dragover",Me),f.addEventListener("dragleave",Se),f.addEventListener("drop",De),f.addEventListener("paste",Le),d.add(()=>{f.removeEventListener("dragenter",$e),f.removeEventListener("dragover",Me),f.removeEventListener("dragleave",Se),f.removeEventListener("drop",De),f.removeEventListener("paste",Le)});const Ae=e=>{(ae()||g?.dirty())&&(e.preventDefault(),e.returnValue="")};if(window.addEventListener("beforeunload",Ae),d.add(()=>window.removeEventListener("beforeunload",Ae)),d.add(()=>{if(g?.dirty())try{S.set(K,{name:g.name,newName:g.newName(),at:new Date().toISOString()})}catch{}ye=!0,[...Q].forEach(e=>{try{e.close(!0)}catch{}})}),I.scan(r),await $(),!m())return;let y=null;try{y=S.get(K)}catch{}if(y?.name){try{S.remove?S.remove(K):S.set(K,null)}catch{}x(y.name)&&await E.confirm(`You were renaming "${q(y.name)}" to "${q(y.newName)}" and left before saving. Pick up where you left off?`)&&m()&&D(y.name,{draftName:y.newName})}}};
|
package/docs/configuration.md
CHANGED
|
@@ -1702,9 +1702,13 @@ uploads of the same name don't collide).
|
|
|
1702
1702
|
| `accept` | no | Comma-separated mime types or wildcards (`image/*`, `application/pdf,image/jpeg`) |
|
|
1703
1703
|
| `maxSize` | no | Maximum bytes (default 5 MB) |
|
|
1704
1704
|
|
|
1705
|
-
**Form rendering** -
|
|
1706
|
-
|
|
1707
|
-
|
|
1705
|
+
**Form rendering** - a drop area (`E.dropzone`) over the field's file input: drop,
|
|
1706
|
+
browse (click, Enter or Space) or paste, with image previews. `accept`, `maxSize`
|
|
1707
|
+
and the count (one file unless `multiple: true`) are checked as files are added,
|
|
1708
|
+
and a refused file is named under the area and announced. The rules are
|
|
1709
|
+
`public/js/form-files.mjs`, which the server imports for its own check. Setting
|
|
1710
|
+
`multiple: true` lets several files be added (the server keeps the last one per
|
|
1711
|
+
field).
|
|
1708
1712
|
|
|
1709
1713
|
**Submission** - the form switches automatically to `multipart/form-data`
|
|
1710
1714
|
when any file input has a selected file. The server-side handler:
|
|
@@ -1115,8 +1115,9 @@ all entries inline.
|
|
|
1115
1115
|
Subsequent pages append rather than replace; filter/sort changes reset to page 1 normally.
|
|
1116
1116
|
- **Server-mode debounce + abort** - in-flight fetches are cancelled when a new state change supersedes them, so
|
|
1117
1117
|
fast typing in the search box doesn't fight for response order.
|
|
1118
|
-
- **Polished controls** - multiselect renders as toggleable chips with counts; numeric ranges add a
|
|
1119
|
-
slider
|
|
1118
|
+
- **Polished controls** - multiselect renders as toggleable chips with counts; numeric ranges add a two-thumb
|
|
1119
|
+
slider (Domma's `E.rangeSlider`: arrow keys, Page Up/Down, Home/End, read out by screen readers, the field's
|
|
1120
|
+
prefix/suffix text in its labels) under the min/max inputs, in client mode when the loaded entries give the bounds.
|
|
1120
1121
|
|
|
1121
1122
|
##### State-machine transitions
|
|
1122
1123
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.16",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"bcryptjs": "^3.0.3",
|
|
87
87
|
"css-select": "^7.0.0",
|
|
88
88
|
"dom-serializer": "^3.1.1",
|
|
89
|
-
"domma-js": "0.
|
|
89
|
+
"domma-js": "0.58.1",
|
|
90
90
|
"dotenv": "^17.2.3",
|
|
91
91
|
"fastify": "5.12.7",
|
|
92
92
|
"gray-matter": "^4.0.3",
|
package/public/css/forms.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.fb-form-wrapper{max-width:600px;margin:2rem auto}.fb-form-honeypot{position:absolute;left:-9999px;top:-9999px;width:0;height:0;overflow:hidden;opacity:0;z-index:-1}.fb-form-success{padding:1rem 1.25rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-success) 12%,transparent);border:1px solid color-mix(in srgb,var(--dm-success) 30%,transparent);color:var(--dm-success);font-size:.95rem;margin-top:.5rem}.fb-form-error{padding:.75rem 1rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-danger) 10%,transparent);border:1px solid color-mix(in srgb,var(--dm-danger) 25%,transparent);color:var(--dm-danger);font-size:.875rem;margin-top:.75rem}.fb-form-loading{opacity:.6;pointer-events:none}.wizard-progress{height:4px;background:var(--dm-border);border-radius:2px;margin-bottom:1rem}.wizard-progress-bar{height:100%;background:var(--dm-primary);border-radius:2px;transition:width .3s ease}.wizard-step-title{font-size:1.1rem;font-weight:600;margin-bottom:.25rem}.wizard-step-description{font-size:.875rem;color:var(--dm-text-muted);margin-bottom:1rem}.wizard-nav{display:flex;justify-content:space-between;gap:.5rem;margin-top:1rem}.fb-field-hidden{display:none!important}.fb-validation-error{color:var(--dm-danger);font-size:.8rem;margin-top:.25rem}.fb-field-trigger-hidden{display:none!important}.fb-field-disabled{opacity:.55;pointer-events:none}.fb-trigger-banner{display:flex;align-items:flex-start;gap:.75rem;margin:.75rem 0;padding:.7rem .9rem;border-radius:var(--dm-radius-md);border-left:3px solid var(--dm-info);background:var(--dm-surface-raised);color:var(--dm-text);font-size:.9rem}.fb-trigger-banner--success{border-left-color:var(--dm-success)}.fb-trigger-banner--warning{border-left-color:var(--dm-warning)}.fb-trigger-banner--error{border-left-color:var(--dm-danger)}.fb-trigger-banner-text{flex:1 1 auto}.fb-trigger-banner-close{flex:0 0 auto;background:none;border:0;color:var(--dm-text-muted);font-size:1.1rem;line-height:1;cursor:pointer;padding:0 .15rem}.fb-trigger-banner-close:hover{color:var(--dm-text)}.fb-trigger-block-note{color:var(--dm-danger);font-size:.85rem;margin:.5rem 0 0}.fb-field-trigger-masked{display:none!important}.fb-trigger-end{margin:1rem 0;padding:1rem 1.1rem;border-radius:var(--dm-radius-md);border:1px solid var(--dm-border);border-left:3px solid var(--dm-warning);background:var(--dm-surface-raised);color:var(--dm-text)}.fb-trigger-end-text{margin:0}.fb-form-wrapper .grid>.fb-trigger-banner,.cms-form-embed .grid>.fb-trigger-banner,.fb-form-wrapper .grid>.fb-trigger-end,.cms-form-embed .grid>.fb-trigger-end{grid-column:1 / -1}.fb-form-wrapper .domma-form-field{margin-bottom:2rem}.fb-form-wrapper .form-group:not(.form-buttons){margin-bottom:2rem}.fb-spacer{height:2rem}.fb-field-required .picker-label:after,.fb-field-required>.form-label:after{content:" *";color:var(--dm-danger)}.fb-field-required .picker-label .picker-required{display:none}.fb-field-invalid>.form-label,.fb-field-invalid .picker-label{color:var(--dm-danger)}.fb-field-invalid .form-input,.fb-field-invalid .form-select,.fb-field-invalid textarea,.fb-field-invalid .picker-options{border-color:var(--dm-danger)}@media(max-width:700px){.fb-form-wrapper form.grid,[data-form-inline] form.grid,.fb-form-wrapper .wizard form.grid,[data-form-inline] .wizard form.grid{grid-template-columns:1fr!important}.fb-form-wrapper form.grid>[class*=col-span-],[data-form-inline] form.grid>[class*=col-span-]{grid-column:span 1 / span 1!important}}.wizard-nav{gap:1rem;flex-wrap:wrap}.wizard-nav .btn{min-width:7.5rem}@media(max-width:480px){.wizard-nav{flex-direction:column-reverse}.wizard-nav .btn{width:100%}}.fb-form-wrapper .form-buttons,[data-form-inline] .form-buttons,#fb-preview-form .form-buttons{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.25rem}.form-buttons.fb-submit{display:flex;gap:1rem;flex-wrap:wrap}.form-buttons.fb-submit--left{justify-content:flex-start;text-align:left}.form-buttons.fb-submit--center{justify-content:center;text-align:center}.form-buttons.fb-submit--right{justify-content:flex-end;text-align:right}.form-buttons.fb-submit--full>button[type=submit],.form-buttons.fb-submit--full>input[type=submit]{flex:1 1 100%;width:100%}.form-buttons.fb-submit>.fb-trigger-block-note{flex-basis:100%}.btn.fb-btn{gap:.45em}.btn.fb-btn>.fb-btn-icon{flex-shrink:0}.form-buttons.fb-submit--pair{display:flex;gap:1rem;flex-wrap:wrap;align-items:center}.form-buttons.fb-submit--full.fb-submit--pair:not(.fb-submit--stack)>.btn{flex:1 1 0;width:auto}.form-buttons.fb-submit--stack{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;gap:.75rem}.form-buttons.fb-submit--stack.fb-submit--center{align-items:center}.form-buttons.fb-submit--stack.fb-submit--right{align-items:flex-end}.form-buttons.fb-submit--stack.fb-submit--full{align-items:stretch}.form-buttons.fb-submit--stack>.btn,.form-buttons.fb-submit--stack>button[type=submit]{flex:0 0 auto}.form-buttons.fb-submit--stack>.fb-trigger-block-note{flex-basis:auto;align-self:stretch}a.btn.fb-secondary-btn.btn-primary:not(:hover){color:var(--dm-primary-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-secondary:not(:hover){color:var(--dm-secondary-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-success:not(:hover){color:var(--dm-success-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-danger:not(:hover){color:var(--dm-danger-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-warning:not(:hover){color:var(--dm-warning-text)}a.btn.fb-secondary-btn.btn-info:not(:hover){color:var(--dm-info-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-outline:not(:hover){color:var(--dm-text)}a.btn.fb-secondary-btn.btn-outline-success:not(:hover){color:var(--dm-success)}a.btn.fb-secondary-btn.btn-outline-danger:not(:hover){color:var(--dm-danger)}a.btn.fb-secondary-btn.btn-outline-warning:not(:hover){color:var(--dm-warning-text)}a.btn.fb-secondary-btn.btn-outline-info:not(:hover){color:var(--dm-info)}a.btn.fb-secondary-btn.btn-ghost:not(:hover){color:var(--dm-text-secondary)}a.btn.fb-secondary-btn:hover{text-decoration:none}
|
|
1
|
+
.fb-form-wrapper{max-width:600px;margin:2rem auto}.fb-form-honeypot{position:absolute;left:-9999px;top:-9999px;width:0;height:0;overflow:hidden;opacity:0;z-index:-1}.fb-form-success{padding:1rem 1.25rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-success) 12%,transparent);border:1px solid color-mix(in srgb,var(--dm-success) 30%,transparent);color:var(--dm-success);font-size:.95rem;margin-top:.5rem}.fb-form-error{padding:.75rem 1rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-danger) 10%,transparent);border:1px solid color-mix(in srgb,var(--dm-danger) 25%,transparent);color:var(--dm-danger);font-size:.875rem;margin-top:.75rem}.fb-form-loading{opacity:.6;pointer-events:none}.wizard-progress{height:4px;background:var(--dm-border);border-radius:2px;margin-bottom:1rem}.wizard-progress-bar{height:100%;background:var(--dm-primary);border-radius:2px;transition:width .3s ease}.wizard-step-title{font-size:1.1rem;font-weight:600;margin-bottom:.25rem}.wizard-step-description{font-size:.875rem;color:var(--dm-text-muted);margin-bottom:1rem}.wizard-nav{display:flex;justify-content:space-between;gap:.5rem;margin-top:1rem}.fb-field-hidden{display:none!important}.fb-validation-error{color:var(--dm-danger);font-size:.8rem;margin-top:.25rem}.fb-field-trigger-hidden{display:none!important}.fb-field-disabled{opacity:.55;pointer-events:none}.fb-trigger-banner{display:flex;align-items:flex-start;gap:.75rem;margin:.75rem 0;padding:.7rem .9rem;border-radius:var(--dm-radius-md);border-left:3px solid var(--dm-info);background:var(--dm-surface-raised);color:var(--dm-text);font-size:.9rem}.fb-trigger-banner--success{border-left-color:var(--dm-success)}.fb-trigger-banner--warning{border-left-color:var(--dm-warning)}.fb-trigger-banner--error{border-left-color:var(--dm-danger)}.fb-trigger-banner-text{flex:1 1 auto}.fb-trigger-banner-close{flex:0 0 auto;background:none;border:0;color:var(--dm-text-muted);font-size:1.1rem;line-height:1;cursor:pointer;padding:0 .15rem}.fb-trigger-banner-close:hover{color:var(--dm-text)}.fb-trigger-block-note{color:var(--dm-danger);font-size:.85rem;margin:.5rem 0 0}.fb-field-trigger-masked{display:none!important}.fb-trigger-end{margin:1rem 0;padding:1rem 1.1rem;border-radius:var(--dm-radius-md);border:1px solid var(--dm-border);border-left:3px solid var(--dm-warning);background:var(--dm-surface-raised);color:var(--dm-text)}.fb-trigger-end-text{margin:0}.fb-form-wrapper .grid>.fb-trigger-banner,.cms-form-embed .grid>.fb-trigger-banner,.fb-form-wrapper .grid>.fb-trigger-end,.cms-form-embed .grid>.fb-trigger-end{grid-column:1 / -1}.fb-form-wrapper .domma-form-field{margin-bottom:2rem}.fb-form-wrapper .form-group:not(.form-buttons){margin-bottom:2rem}.fb-spacer{height:2rem}.fb-field-required .picker-label:after,.fb-field-required>.form-label:after{content:" *";color:var(--dm-danger)}.fb-field-required .picker-label .picker-required{display:none}.fb-field-invalid>.form-label,.fb-field-invalid .picker-label{color:var(--dm-danger)}.fb-field-invalid .form-input,.fb-field-invalid .form-select,.fb-field-invalid textarea,.fb-field-invalid .picker-options{border-color:var(--dm-danger)}.fb-file-refusal{margin:.4rem 0 0;font-size:.875rem;color:var(--dm-danger)}.fb-file-refusal[hidden]{display:none}.fb-field-invalid .dm-dropzone{border-color:var(--dm-danger)}.page-body ul.dm-dropzone-files{margin:var(--dm-space-3, .75rem) 0;padding-left:0}@media(max-width:700px){.fb-form-wrapper form.grid,[data-form-inline] form.grid,.fb-form-wrapper .wizard form.grid,[data-form-inline] .wizard form.grid{grid-template-columns:1fr!important}.fb-form-wrapper form.grid>[class*=col-span-],[data-form-inline] form.grid>[class*=col-span-]{grid-column:span 1 / span 1!important}}.wizard-nav{gap:1rem;flex-wrap:wrap}.wizard-nav .btn{min-width:7.5rem}@media(max-width:480px){.wizard-nav{flex-direction:column-reverse}.wizard-nav .btn{width:100%}}.fb-form-wrapper .form-buttons,[data-form-inline] .form-buttons,#fb-preview-form .form-buttons{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.25rem}.form-buttons.fb-submit{display:flex;gap:1rem;flex-wrap:wrap}.form-buttons.fb-submit--left{justify-content:flex-start;text-align:left}.form-buttons.fb-submit--center{justify-content:center;text-align:center}.form-buttons.fb-submit--right{justify-content:flex-end;text-align:right}.form-buttons.fb-submit--full>button[type=submit],.form-buttons.fb-submit--full>input[type=submit]{flex:1 1 100%;width:100%}.form-buttons.fb-submit>.fb-trigger-block-note{flex-basis:100%}.btn.fb-btn{gap:.45em}.btn.fb-btn>.fb-btn-icon{flex-shrink:0}.form-buttons.fb-submit--pair{display:flex;gap:1rem;flex-wrap:wrap;align-items:center}.form-buttons.fb-submit--full.fb-submit--pair:not(.fb-submit--stack)>.btn{flex:1 1 0;width:auto}.form-buttons.fb-submit--stack{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;gap:.75rem}.form-buttons.fb-submit--stack.fb-submit--center{align-items:center}.form-buttons.fb-submit--stack.fb-submit--right{align-items:flex-end}.form-buttons.fb-submit--stack.fb-submit--full{align-items:stretch}.form-buttons.fb-submit--stack>.btn,.form-buttons.fb-submit--stack>button[type=submit]{flex:0 0 auto}.form-buttons.fb-submit--stack>.fb-trigger-block-note{flex-basis:auto;align-self:stretch}a.btn.fb-secondary-btn.btn-primary:not(:hover){color:var(--dm-primary-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-secondary:not(:hover){color:var(--dm-secondary-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-success:not(:hover){color:var(--dm-success-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-danger:not(:hover){color:var(--dm-danger-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-warning:not(:hover){color:var(--dm-warning-text)}a.btn.fb-secondary-btn.btn-info:not(:hover){color:var(--dm-info-text, var(--dm-white))}a.btn.fb-secondary-btn.btn-outline:not(:hover){color:var(--dm-text)}a.btn.fb-secondary-btn.btn-outline-success:not(:hover){color:var(--dm-success)}a.btn.fb-secondary-btn.btn-outline-danger:not(:hover){color:var(--dm-danger)}a.btn.fb-secondary-btn.btn-outline-warning:not(:hover){color:var(--dm-warning-text)}a.btn.fb-secondary-btn.btn-outline-info:not(:hover){color:var(--dm-info)}a.btn.fb-secondary-btn.btn-ghost:not(:hover){color:var(--dm-text-secondary)}a.btn.fb-secondary-btn:hover{text-decoration:none}
|