domma-cms 1.6.5 → 1.6.7
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/README.md +1 -1
- package/admin/js/lib/tool-kit.js +1 -1
- package/admin/js/lib/tool-recovery.js +12 -0
- package/admin/js/templates/analytics.html +5 -0
- package/admin/js/templates/contacts.html +4 -0
- package/admin/js/templates/docs/usage-tools.html +12 -0
- package/admin/js/templates/notes.html +4 -0
- package/admin/js/templates/todo.html +4 -0
- package/admin/js/views/analytics.js +7 -7
- package/admin/js/views/contacts.js +22 -22
- package/admin/js/views/notes.js +8 -8
- package/admin/js/views/todo.js +9 -9
- package/config/menus/admin-sidebar.json +1 -1
- package/package.json +1 -1
- package/public/js/collection-context.js +2 -2
- package/public/js/context-menus.js +1 -1
- package/public/js/effects.js +1 -1
- package/public/js/site.js +1 -1
- package/public/js/utf8-base64.mjs +1 -0
- package/server/routes/api/contacts.js +12 -14
- package/server/routes/api/notes.js +7 -6
- package/server/routes/api/todo.js +7 -5
- package/server/services/collections.js +11 -2
- package/server/services/markdown.js +10 -10
- package/server/services/renderer.js +2 -2
- package/server/services/sidebar-migration.js +15 -4
- package/server/services/tool-trash.js +39 -0
- package/server/templates/page.html +4 -4
package/admin/js/views/notes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{apiRequest as
|
|
1
|
+
import{protectToolEditor as se,mountToolTrash as ne}from"../lib/tool-recovery.js";import{apiRequest as le}from"../api.js";import{addBannerAction as ie}from"../lib/plugin-chrome.js";import{helpPopovers as R}from"../lib/help-popover.js";import{mountRelated as re,relatedMenuItems as ce,relatedProviders as de}from"../lib/related.js";import{onceClosed as K,openMenuAt as ue,settingsStore as pe,sidebarCount as ve,viewLifetime as me}from"../lib/tool-kit.js";import{arrangeNotes as be,collectCategories as fe,DEFAULT_SETTINGS as ye,normaliseSettings as j,parseCategories as he,preview as ge,PREVIEW_MAX as we,PREVIEW_MIN as ke}from"../lib/notes-arrange.js";const z=pe("notes.settings",j),N=(m,r="GET",g)=>le(m,{method:r,...g!==void 0?{body:JSON.stringify(g)}:{}});function W(m){const r=document.createElement("template");return r.innerHTML=m.trim(),r.content.firstElementChild}const Ee=`
|
|
2
2
|
<article class="note-card" data-id="{{id}}" tabindex="0">
|
|
3
3
|
<h3 class="note-title">{{title}}</h3>
|
|
4
4
|
<p class="note-preview">{{preview}}</p>
|
|
@@ -7,9 +7,9 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
7
7
|
<time class="note-time" title="{{updatedFull}}">{{updatedRel}}</time>
|
|
8
8
|
</footer>
|
|
9
9
|
<button type="button" class="note-kebab" aria-label="Note actions" title="Actions">\u22EE</button>
|
|
10
|
-
</article>`,
|
|
10
|
+
</article>`,c=me(/^#\/notes\b/);export const notesView={templateUrl:"/admin/js/templates/notes.html",async onMount(m){c.reset();const r=m.get(0);let g=[];de("note").then(e=>{g=e});const d=M.observableArray([]),S=M.observable(""),p=M.observable(""),b=M.observable(!0),C=M.observable(""),$=M.observable(!1),s=M.observable(z.read()),x=M.computed(()=>fe(d.value));c.add(M.effect(()=>{d.value,b.peek()||ve("#/notes")}));const _=M.computed(()=>be(d.value,{query:S.value,category:p.value,sort:s.value.sort})),T=M.observable([]);c.add(M.effect(()=>{const e=s.value.previewLength;T.value=_.value.map(t=>({id:t.id,title:t.title||"Untitled",preview:ge(t.content,e),cats:(t.categories||[]).map(a=>({name:a})),updatedRel:t.updatedAt?D(t.updatedAt).fromNow():"",updatedFull:t.updatedAt?"Updated "+D(t.updatedAt).format("DD MMM YYYY HH:mm"):""}))}));const F=M.computed(()=>!!(S.value.trim()||p.value)),G={query:S,category:p,loading:b,loadError:C,searchFocus:$,categories:x,visible:T,isCards:M.computed(()=>s.value.layout==="cards"),listClass:M.computed(()=>(s.value.layout==="cards"?"is-cards":"is-list")+(s.value.showCategories?"":" hide-cats")),isEmpty:M.computed(()=>!b.value&&!C.value&&T.value.length===0),emptyText:M.computed(()=>F.value?"No notes match. Clear the search or pick another category.":"No notes yet. Click New to write your first."),countText:M.computed(()=>{if(b.value)return"";const e=d.value.length,t=T.value.length;return F.value?`${t} of ${e}`:`${e} note${e===1?"":"s"}`})};c.add(M.effect(()=>{const e=p.value.toLowerCase();e&&!x.value.some(t=>t.toLowerCase()===e)&&(p.value="")})),c.add(M.effect(()=>z.write(s.value)));const L=(e,t)=>{s.value=j({...s.peek(),[e]:t})};r.querySelector(".notes-items").innerHTML=Ee;const J=M.applyBindings(G,r,{methods:{create:()=>w(null),retry:A,useCards:()=>L("layout","cards"),useList:()=>L("layout","list")}});c.add(()=>J.dispose()),ne({root:r,toolbar:r.querySelector(".notes-bar-end"),tool:"notes",endpoint:"/notes",itemPath:"/items",label:"Notes",titleOf:e=>e.title||e.content,reload:A,lifetime:c}),c.add(R(r,{position:"bottom"}));const f=e=>d.peek().find(t=>t.id===e);function X(e){const t=d.peek(),a=t.findIndex(o=>o.id===e.id);d.value=a===-1?[e,...t]:t.map(o=>o.id===e.id?e:o)}async function A(){b.value=!0;try{const e=await N("/notes/items");d.value=Array.isArray(e)?e:e.data??[],C.value=""}catch{C.value="Could not load this list. Your items have not been removed.",E.toast("Failed to load notes",{type:"error"})}finally{b.value=!1}}async function U(e,t){const a=e?await N("/notes/items/"+e,"PUT",t):await N("/notes/items","POST",t);return X(a),a}async function Y(e){if(s.peek().confirmDelete&&!await E.confirm("Move this note to Trash? You can restore it later."))return!1;try{return await N("/notes/items/"+e,"DELETE"),d.value=d.peek().filter(t=>t.id!==e),E.toast("Note moved to Trash",{type:"success"}),!0}catch{return E.toast("Failed to delete note",{type:"error"}),!1}}async function Z(e){const t=f(e);if(t)try{await U(null,{title:`${t.title||"Untitled"} (copy)`,content:t.content||"",categories:t.categories||[]}),E.toast("Note duplicated",{type:"success"})}catch{E.toast("Failed to duplicate note",{type:"error"})}}async function Q(e){const t=f(e);if(t)try{await navigator.clipboard.writeText([t.title,t.content].filter(Boolean).join(`
|
|
11
11
|
|
|
12
|
-
`)),E.toast("Copied to clipboard",{type:"success"})}catch{E.toast("The browser would not allow copying",{type:"error"})}}let
|
|
12
|
+
`)),E.toast("Copied to clipboard",{type:"success"})}catch{E.toast("The browser would not allow copying",{type:"error"})}}let y=null;async function w(e){if(y&&!await y.close())return;const t=!e,a=E.slideover({title:t?"New note":"Edit note",size:"md",position:"right"});y=a;const o=W(`
|
|
13
13
|
<div class="notes-panel">
|
|
14
14
|
<div>
|
|
15
15
|
<label class="form-label" for="note-title-input">Title</label>
|
|
@@ -26,7 +26,7 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
26
26
|
<div class="notes-field-grow">
|
|
27
27
|
<label class="form-label" for="note-content-input">Content
|
|
28
28
|
<span data-help-title="Writing"
|
|
29
|
-
data-help="Plain text. Line breaks are kept. Ctrl+Enter (Cmd+Enter on a Mac) saves; Escape
|
|
29
|
+
data-help="Plain text. Line breaks are kept. Ctrl+Enter (Cmd+Enter on a Mac) saves; Escape offers to keep unsaved changes as a browser draft."></span>
|
|
30
30
|
</label>
|
|
31
31
|
<textarea id="note-content-input" class="form-input" placeholder="Write your note\u2026"
|
|
32
32
|
data-model="content.value"></textarea>
|
|
@@ -47,7 +47,7 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
47
47
|
<span data-icon="save" data-icon-size="13"></span> Save
|
|
48
48
|
</button>
|
|
49
49
|
</div>
|
|
50
|
-
</div>`),
|
|
50
|
+
</div>`),l={title:e?.title||"",content:e?.content||"",cats:t?s.peek().defaultCategories:e.categories||[]},v=M.observable(l.title),u=M.observable(l.content),i=M.observable([...l.cats]),h=M.observable(!1),k={title:v,content:u,saving:h,canDelete:!t,stats:M.computed(()=>{const n=u.value.trim(),B=n?n.split(/\s+/).length:0;return`${B} word${B===1?"":"s"} \xB7 ${u.value.length} characters`}),dirty:M.computed(()=>v.value!==l.title||u.value!==l.content||i.value.join("\0")!==l.cats.join("\0"))};async function O(){if(!h.peek()){h.value=!0;try{await U(e?.id,{title:v.peek().trim(),content:u.peek(),categories:i.peek()}),E.toast(t?"Note created":"Note saved",{type:"success"}),V.saved(),a.close()}catch{E.toast("Failed to save note",{type:"error"})}finally{h.value=!1}}}a.element.appendChild(o),t||re(o.querySelector(".related-box"),"note",e.id,{onOpen:()=>a.close()});const ae=M.applyBindings(k,o,{methods:{submit:O,cancel:()=>a.close(),del:async()=>{await Y(e.id)&&(V.discard(),a.close())}}}),P=E.pillbox(o.querySelector(".notes-cats-input"),{data:x.peek().map(n=>({value:n,label:n})),value:i.peek(),creatable:!0,placeholder:"Add a category\u2026",onChange:()=>{i.value=he(P.getValue())}}),oe=R(o);o.addEventListener("keydown",n=>{(n.ctrlKey||n.metaKey)&&n.key==="Enter"&&(n.preventDefault(),O())}),K(a,()=>{ae.dispose(),oe(),P.destroy?.(),y===a&&(y=null)});const V=se({so:a,panel:o,tool:"notes",id:e?.id,lifetime:c,read:()=>({title:v.peek(),content:u.peek(),cats:[...i.peek()]}),write:n=>{v.value=n.title??"",u.value=n.content??"",i.value=Array.isArray(n.cats)?n.cats:[],P.setValue(i.peek())}});a.open(),setTimeout(()=>o.querySelector(t?"#note-title-input":"#note-content-input")?.focus(),60)}function ee(){const e=E.slideover({title:"Notes settings",size:"sm",position:"right"}),t=W(`
|
|
51
51
|
<div class="notes-panel">
|
|
52
52
|
<div class="notes-setting">
|
|
53
53
|
<label class="form-label" for="notes-set-layout">Layout
|
|
@@ -71,7 +71,7 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
71
71
|
<span data-help="How much of each note's content shows on its card. 0 shows titles only."></span>
|
|
72
72
|
</label>
|
|
73
73
|
<div class="notes-range">
|
|
74
|
-
<input id="notes-set-preview" type="range" min="${
|
|
74
|
+
<input id="notes-set-preview" type="range" min="${ke}" max="${we}" step="20"
|
|
75
75
|
data-model="previewLength.value">
|
|
76
76
|
<output data-bind-text="previewText.value"></output>
|
|
77
77
|
</div>
|
|
@@ -85,7 +85,7 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
85
85
|
<div class="notes-setting">
|
|
86
86
|
<div class="notes-setting-row">
|
|
87
87
|
<label class="form-label" for="notes-set-confirm" style="margin:0">Confirm before deleting
|
|
88
|
-
<span data-help="Off
|
|
88
|
+
<span data-help="Off moves items straight to Trash. You can restore them."></span>
|
|
89
89
|
</label>
|
|
90
90
|
<input id="notes-set-confirm" type="checkbox" class="form-switch" data-model="confirmDelete.value">
|
|
91
91
|
</div>
|
|
@@ -102,4 +102,4 @@ import{apiRequest as ae}from"../api.js";import{addBannerAction as se}from"../lib
|
|
|
102
102
|
<span class="spacer"></span>
|
|
103
103
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="done">Done</button>
|
|
104
104
|
</div>
|
|
105
|
-
</div>`),a=(
|
|
105
|
+
</div>`),a=(i,h=k=>k)=>M.computed({read:()=>s.value[i],write:k=>L(i,h(k))}),o={layout:a("layout"),sort:a("sort"),previewLength:a("previewLength",Number),showCategories:a("showCategories"),confirmDelete:a("confirmDelete"),previewText:M.computed(()=>s.value.previewLength?`${s.value.previewLength} ch`:"Off")};e.element.appendChild(t);let l;const v=M.applyBindings(o,t,{methods:{done:()=>e.close(),reset:()=>{s.value={...ye},l?.setValue([])}}});l=E.pillbox(t.querySelector(".notes-set-defaults"),{data:x.peek().map(i=>({value:i,label:i})),value:s.peek().defaultCategories,creatable:!0,placeholder:"None",onChange:()=>L("defaultCategories",l.getValue())});const u=R(t);K(e,()=>{v.dispose(),u(),l.destroy?.()}),e.open()}ie(m,{id:"notes-settings",icon:"settings",title:"Notes settings",onClick:ee});const q=r.querySelector(".notes-list"),te=E.contextMenu(q,{cursor:"glyph",match:".note-card",inherit:!1,density:"compact",items:e=>{const t=e.dataset.id,o=f(t)?.categories||[];return[{label:"Edit",icon:"edit",action:()=>w(f(t))},...ce(g,t),{label:"Duplicate",icon:"copy",action:()=>Z(t)},{label:"Copy text",icon:"clipboard",action:()=>Q(t)},{label:"Show only\u2026",icon:"filter",disabled:o.length===0,submenu:o.map(l=>({label:l,icon:"tag",action:()=>{p.value=l}}))},{type:"divider"},{label:"Delete",icon:"trash",danger:!0,action:()=>Y(t)}]}});c.add(()=>te?.destroy?.()),q.addEventListener("click",e=>{const t=e.target.closest(".note-card");if(!t)return;const a=e.target.closest(".note-cat");if(a){p.value=a.dataset.cat;return}const o=e.target.closest(".note-kebab");if(o){ue(o);return}w(f(t.dataset.id))}),q.addEventListener("keydown",e=>{const t=e.target.closest?.(".note-card");t&&e.key==="Enter"&&e.target===t&&w(f(t.dataset.id))});const H=e=>{e.ctrlKey||e.metaKey||e.altKey||e.target.closest?.("input, textarea, select, [contenteditable], .dm-slideover")||(e.key==="/"?(e.preventDefault(),$.value=!0):(e.key==="n"||e.key==="N")&&(e.preventDefault(),w(null)))};document.addEventListener("keydown",H),c.add(()=>document.removeEventListener("keydown",H)),c.add(()=>y?.close()),I.scan(r),await A()}};
|
package/admin/js/views/todo.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{apiRequest as
|
|
1
|
+
import{protectToolEditor as dt,mountToolTrash as rt,toolDraftKey as ct}from"../lib/tool-recovery.js";import{apiRequest as ut}from"../api.js";import{addBannerAction as pt}from"../lib/plugin-chrome.js";import{helpPopovers as j}from"../lib/help-popover.js";import{onceClosed as G,openMenuAt as bt,settingsStore as mt,sidebarCount as vt,viewLifetime as ft}from"../lib/tool-kit.js";import{arrangeTodos as yt,countTodos as ht,daysUntil as kt,DEFAULT_SETTINGS as W,dueDay as gt,dueLabel as Mt,isoDay as A,isOverdue as wt,normaliseSettings as _,PRIORITIES as C,STATUSES as J}from"../lib/todo-arrange.js";const p={"not-started":"Not started","in-progress":"In progress",completed:"Completed",highest:"Highest",high:"High",medium:"Medium",low:"Low",lowest:"Lowest",open:"Open",all:"All",overdue:"Overdue"},V=mt("todo.settings",_),g=(m,d="GET",l)=>ut(m,{method:d,...l!==void 0?{body:JSON.stringify(l)}:{}}),Tt=`
|
|
2
2
|
<div class="todo-row{{doneClass}}" data-id="{{id}}">
|
|
3
3
|
<input type="checkbox" class="todo-check" aria-label="Done" data-bind-checked="isDone">
|
|
4
4
|
<span class="todo-text" title="Click to edit">{{text}}</span>
|
|
@@ -6,7 +6,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
6
6
|
<button type="button" class="todo-pill" data-kind="status" data-value="{{status}}" title="Change status">{{statusLabel}}</button>
|
|
7
7
|
<button type="button" class="todo-pill" data-kind="priority" data-value="{{priority}}" title="Change priority">{{priorityLabel}}</button>
|
|
8
8
|
<button type="button" class="todo-kebab" aria-label="Task actions" title="Actions">\u22EE</button>
|
|
9
|
-
</div>`;function
|
|
9
|
+
</div>`;function Q(m){const d=document.createElement("template");return d.innerHTML=m.trim(),d.content.firstElementChild}const r=ft(/^#\/todo\b/);export const todoView={templateUrl:"/admin/js/templates/todo.html",async onMount(m){r.reset();const d=m.get(0),l=M.observable(V.read()),n=M.observable([]),v=M.observable(!0),w=M.observable(""),L=M.observable(""),$=M.observable(l.peek().defaultFilter),f=M.observable(""),y=M.observable(l.peek().defaultPriority),h=M.observable(""),O=M.observable(!1),R=M.observable(!1),k=ct("todo","quick-add");try{const t=k&&S.get(k);t?.text&&(f.value=t.text,y.value=C.includes(t.priority)?t.priority:l.peek().defaultPriority,h.value=t.due||"")}catch{}r.add(M.effect(()=>{const t={text:f.value,priority:y.value,due:h.value};try{k&&(t.text?S.set(k,t):S.remove(k))}catch{}}));const P=M.computed(()=>ht(n.value)),X=M.computed(()=>["open","all","not-started","in-progress","completed","overdue"].map(t=>({value:t,label:`${p[t]} (${P.value[t]})`}))),T=M.observable([]);r.add(M.effect(()=>{const t=l.value.relativeDue;T.value=yt(n.value,{filter:$.value,query:L.value,sort:l.value.sort}).map(e=>{const o=kt(e.dueAt),s=wt(e);return{id:e.id,text:e.text,status:e.status,priority:e.priority,statusLabel:p[e.status]??e.status,priorityLabel:p[e.priority]??e.priority,doneClass:e.status==="completed"?" is-done":"",isDone:e.status==="completed",due:Mt(e.dueAt,{relative:t,format:i=>D(i).format("D MMM")}),dueClass:s?" is-overdue":o===0&&e.status!=="completed"?" is-today":"",dueTitle:o===null?"":`Due ${D(gt(e.dueAt)).format("dddd D MMMM YYYY")}`}})}));const Z={rows:T,loading:v,loadError:w,query:L,filter:$,filterOptions:X,counts:P,draft:f,draftPriority:y,draftDue:h,addFocus:O,searchFocus:R,cannotAdd:M.computed(()=>!f.value.trim()),isEmpty:M.computed(()=>!v.value&&!w.value&&T.value.length===0),emptyText:M.computed(()=>n.value.length?$.value==="open"&&!L.value.trim()?"Nothing open - everything is done.":"No tasks match. Change the filter or clear the search.":"No tasks yet. Add one above."),countText:M.computed(()=>{if(v.value)return"";const t=T.value.length,e=n.value.length;return t===e?`${e} task${e===1?"":"s"}`:`${t} of ${e}`})};r.add(M.effect(()=>V.write(l.value))),r.add(M.effect(()=>{n.value,v.peek()||vt("#/todo")}));const tt=(t,e)=>{l.value=_({...l.peek(),[t]:e})},q=t=>n.peek().find(e=>e.id===t),et=t=>{n.value=n.peek().map(e=>e.id===t.id?t:e)};async function N(){v.value=!0;try{const t=await g("/todo/items");n.value=Array.isArray(t)?t:t.data??[],w.value=""}catch{w.value="Could not load this list. Your items have not been removed.",E.toast("Failed to load tasks",{type:"error"})}finally{v.value=!1}}async function u(t,e,o){try{return et(await g(`/todo/items/${t}`,"PUT",e)),o&&E.toast(o,{type:"success"}),!0}catch{return E.toast("Failed to update task",{type:"error"}),!1}}async function ot(){const t=f.peek().trim();if(t)try{const e=await g("/todo/items","POST",{text:t,priority:y.peek(),status:"not-started",dueAt:h.peek()||null});n.value=[...n.peek(),e],f.value="",h.value="",y.value=l.peek().defaultPriority,O.value=!0}catch{E.toast("Failed to add task",{type:"error"})}}const at=()=>(ot(),!1);async function H(t){if(l.peek().confirmDelete&&!await E.confirm("Move this task to Trash?"))return!1;try{return await g(`/todo/items/${t}`,"DELETE"),n.value=n.peek().filter(e=>e.id!==t),E.toast("Task moved to Trash",{type:"success"}),!0}catch{return E.toast("Failed to delete task",{type:"error"}),!1}}async function st(){const t=P.peek().completed;if(!(!t||!await E.confirm(`Delete ${t} completed task${t===1?"":"s"}?`)))try{await g("/todo/completed","DELETE"),n.value=n.peek().filter(e=>e.status!=="completed"),E.toast("Completed tasks moved to Trash",{type:"success"})}catch{E.toast("Failed to clear completed tasks",{type:"error"})}}const B=t=>{const e=q(t);if(!e)return;const o=e.status==="completed"?"not-started":"completed";u(t,{status:o},o==="completed"?"Task completed":"Task reopened")};function U(t){const e=q(t);if(!e)return;const o=E.slideover({title:"Edit task",size:"md",position:"right"}),s=Q(`
|
|
10
10
|
<div class="todo-panel">
|
|
11
11
|
<div>
|
|
12
12
|
<label class="form-label" for="todo-edit-text">Task</label>
|
|
@@ -16,13 +16,13 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
16
16
|
<div>
|
|
17
17
|
<label class="form-label" for="todo-edit-status">Status</label>
|
|
18
18
|
<select id="todo-edit-status" class="form-input" data-model="status.value">
|
|
19
|
-
${
|
|
19
|
+
${J.map(a=>`<option value="${a}">${p[a]}</option>`).join("")}
|
|
20
20
|
</select>
|
|
21
21
|
</div>
|
|
22
22
|
<div>
|
|
23
23
|
<label class="form-label" for="todo-edit-priority">Priority</label>
|
|
24
24
|
<select id="todo-edit-priority" class="form-input" data-model="priority.value">
|
|
25
|
-
${
|
|
25
|
+
${C.map(a=>`<option value="${a}">${p[a]}</option>`).join("")}
|
|
26
26
|
</select>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
@@ -48,14 +48,14 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
48
48
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="save"
|
|
49
49
|
data-bind-disabled="cannotSave.value">Save</button>
|
|
50
50
|
</div>
|
|
51
|
-
</div>`),i={text:M.observable(
|
|
51
|
+
</div>`),i={text:M.observable(e.text||""),status:M.observable(e.status),priority:M.observable(e.priority),due:M.observable(e.dueAt?String(e.dueAt).slice(0,10):""),created:e.createdAt?D(e.createdAt).format("D MMM YYYY"):"earlier"};i.cannotSave=M.computed(()=>!i.text.value.trim());const x=async()=>{await u(e.id,{text:i.text.peek().trim(),status:i.status.peek(),priority:i.priority.peek(),dueAt:i.due.peek()||null},"Task saved")&&(b.saved(),o.close())};o.element.appendChild(s);const Y=j(s),c=M.applyBindings(i,s,{methods:{save:x,cancel:()=>o.close(),del:async()=>{await H(e.id)&&(b.discard(),o.close())},dueIn:a=>{i.due.value=A(Number(a.currentTarget.dataset.days))},clearDue:()=>{i.due.value=""}}});s.addEventListener("keydown",a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),x())}),G(o,()=>{c.dispose(),Y()});const b=dt({so:o,panel:s,tool:"todo",id:t,lifetime:r,read:()=>Object.fromEntries(["text","status","priority","due"].map(a=>[a,i[a].peek()])),write:a=>{for(const K of["text","status","priority","due"])K in a&&(i[K].value=a[K])}});o.open(),setTimeout(()=>s.querySelector("#todo-edit-text")?.focus(),60)}function lt(){const t=E.slideover({title:"Todo settings",size:"sm",position:"right"}),e=c=>c.map(b=>`<option value="${b}">${p[b]}</option>`).join(""),o=Q(`
|
|
52
52
|
<div class="todo-panel">
|
|
53
53
|
<div>
|
|
54
54
|
<label class="form-label" for="todo-set-filter">Show on opening
|
|
55
55
|
<span data-help="Which tasks the list shows when you open Todo. The filter in the toolbar changes it for this visit only."></span>
|
|
56
56
|
</label>
|
|
57
57
|
<select id="todo-set-filter" class="form-input" data-model="defaultFilter.value">
|
|
58
|
-
${
|
|
58
|
+
${e(["open","all","not-started","in-progress","completed","overdue"])}
|
|
59
59
|
</select>
|
|
60
60
|
</div>
|
|
61
61
|
<div>
|
|
@@ -72,7 +72,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
72
72
|
<div>
|
|
73
73
|
<label class="form-label" for="todo-set-priority">Priority for new tasks</label>
|
|
74
74
|
<select id="todo-set-priority" class="form-input" data-model="defaultPriority.value">
|
|
75
|
-
${
|
|
75
|
+
${e(C)}
|
|
76
76
|
</select>
|
|
77
77
|
</div>
|
|
78
78
|
<div class="todo-setting-row">
|
|
@@ -83,7 +83,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
83
83
|
</div>
|
|
84
84
|
<div class="todo-setting-row">
|
|
85
85
|
<label class="form-label" for="todo-set-confirm" style="margin:0">Confirm before deleting
|
|
86
|
-
<span data-help="Off
|
|
86
|
+
<span data-help="Off moves items straight to Trash. You can restore them."></span>
|
|
87
87
|
</label>
|
|
88
88
|
<input id="todo-set-confirm" type="checkbox" class="form-switch" data-model="confirmDelete.value">
|
|
89
89
|
</div>
|
|
@@ -93,4 +93,4 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
|
|
|
93
93
|
<span class="spacer"></span>
|
|
94
94
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="done">Done</button>
|
|
95
95
|
</div>
|
|
96
|
-
</div>`),s=
|
|
96
|
+
</div>`),s=c=>M.computed({read:()=>l.value[c],write:b=>tt(c,b)}),i=Object.fromEntries(Object.keys(W).map(c=>[c,s(c)]));t.element.appendChild(o);const x=j(o),Y=M.applyBindings(i,o,{methods:{done:()=>t.close(),reset:()=>{l.value={...W}}}});G(t,()=>{Y.dispose(),x()}),t.open()}pt(m,{id:"todo-settings",icon:"settings",title:"Todo settings",onClick:lt}),d.querySelector(".todo-rows").innerHTML=Tt,r.add(j(d,{position:"bottom"}));const nt=M.applyBindings(Z,d,{methods:{retry:N,add:at,clearCompleted:st}});r.add(()=>nt.dispose()),rt({root:d,toolbar:d.querySelector(".todo-bar"),tool:"todo",endpoint:"/todo",itemPath:"/items",label:"Todo",titleOf:t=>t.text,reload:N,lifetime:r});const F=d.querySelector(".todo-list"),it=E.contextMenu(F,{cursor:"glyph",match:".todo-row",inherit:!1,density:"compact",items:t=>{const e=t.dataset.id,o=q(e);return o?[{label:"Edit\u2026",icon:"edit",action:()=>U(e)},{label:o.status==="completed"?"Reopen":"Complete",icon:"check",action:()=>B(e)},{type:"divider"},{label:"Status",icon:"circle",submenu:J.map(s=>({label:p[s],disabled:o.status===s,action:()=>u(e,{status:s})}))},{label:"Priority",icon:"flag",submenu:C.map(s=>({label:p[s],disabled:o.priority===s,action:()=>u(e,{priority:s})}))},{label:"Due",icon:"calendar",submenu:[{label:"Today",action:()=>u(e,{dueAt:A(0)})},{label:"Tomorrow",action:()=>u(e,{dueAt:A(1)})},{label:"Next week",action:()=>u(e,{dueAt:A(7)})},{type:"divider"},{label:"No due date",disabled:!o.dueAt,action:()=>u(e,{dueAt:null})}]},{type:"divider"},{label:"Delete",icon:"trash",danger:!0,action:()=>H(e)}]:[]}});r.add(()=>it?.destroy?.()),F.addEventListener("click",t=>{const e=t.target.closest(".todo-row");if(e){if(t.target.closest(".todo-kebab, .todo-pill"))return bt(t.target.closest(".todo-kebab, .todo-pill"));t.target.closest(".todo-text")&&U(e.dataset.id)}}),F.addEventListener("change",t=>{t.target.matches(".todo-check")&&B(t.target.closest(".todo-row").dataset.id)});const z=t=>{t.ctrlKey||t.metaKey||t.altKey||t.target.closest?.("input, textarea, select, [contenteditable], .dm-slideover")||(t.key==="/"?(t.preventDefault(),R.value=!0):(t.key==="n"||t.key==="N")&&(t.preventDefault(),O.value=!0))};document.addEventListener("keydown",z),r.add(()=>document.removeEventListener("keydown",z)),I.scan(d),await N()}};
|
package/package.json
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{compareValues as
|
|
2
|
-
`),document.head.appendChild(P);function g(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,o)=>({id:String(o),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=JSON.parse(atob(t.dataset.ctx))}catch{t._dmCtxModel=null}return t._dmCtxModel}function $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=mt(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=H(o.entries.filter(a=>e.every(r=>$(a,r))),n),l=new Set(d.map(a=>a.id));if(t._dmTable)t._dmTable.render(d.map(a=>a.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(a=>l.has(String(a._entryId))));else if(t._dmCtxSlides)W(t,l);else{const a=Array.from(t.querySelectorAll("[data-entry-id]"));a.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const r=new Map(a.map(h=>[h.dataset.entryId,h])),u=a.length?a[0].parentNode:null;n&&u&&d.forEach(h=>{const x=r.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const i=document.createElement("div");i.className="dm-ctx-group-heading",i.textContent=b;const s=r.get(x.id);s&&u.insertBefore(i,s)})}}w(t,e),tt(t,d.length,e.length),D()}const A="dmf";function M(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function U(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function J(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function D(){const t={};M().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(A+"="));Object.keys(t).length&&e.push(A+"="+U(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function V(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return J(t[1])}catch{return null}}function N(t=0){const e=V();if(!e)return;const o=M();let n=!1;for(const[d,l]of Object.entries(e)){const a=o[Number(d)];if(!a||!l)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};a._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),a._dmCtxSort=u.s||null,a._dmCtxGroup=u.g||null,p(a)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function W(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function Y(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function X(){return new Date().toISOString().slice(0,10)}let L=null;function Z(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function K(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await Z();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const Q={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function w(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const r=String(d.value),u=r.length>24?r.slice(0,24)+"\u2026":r;a.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${Q[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),a.appendChild(c),o.appendChild(a)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function tt(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",O,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",G,!0))}const O=t=>{C&&!C.contains(t.target)&&f()},j=t=>{t.key==="Escape"&&f()},G=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function I(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function R(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function B(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),R(t)}function et(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),B(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&B(t,o.build,o.title)},t}function nt(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function ot(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function dt(t,e,o){o.fields.forEach(n=>{const[d,l]=ot(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function at(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function q(t,e){const o=t._dmCtxFilters||[];return H(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function it(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const a=T(q(e,o),o.fields,l);Y(a,`${d}-${X()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function lt(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(q(e,o),o.fields,"markdown"),"Table copied"),f()}))}function rt(t,e,o,n,d){const l=(i,s,k,ct)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:i,label:s,op:k,value:ct}),p(e),f()};if(n&&o){const i=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"eq",i))),t.appendChild(m(`Filter: \u2260 \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"ne",i)));const s=String(i);(s!==""&&!Number.isNaN(parseFloat(s))||!Number.isNaN(Date.parse(s)))&&(t.appendChild(m(`Filter: > \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"gt",i))),t.appendChild(m(`Filter: < \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"lt",i))))}else if(o){t.appendChild(v("Filter by"));for(const i of d.fields){const s=o.data?.[i.name];s==null||String(s)===""||t.appendChild(m(`${i.label||i.name}: \u201C${y(s,20)}\u201D`,()=>l(i.name,i.label,"eq",s)))}}const a=document.createElement("div");a.style.padding=".35rem .6rem";const r=document.createElement("input");r.type="text",r.className="dm-ctx-input",r.placeholder="Search\u2026 or name: value",r.addEventListener("mousedown",i=>i.stopPropagation()),r.addEventListener("keydown",i=>{if(i.key!=="Enter"||!r.value.trim())return;const s=ut(r.value,d.fields);s?l(s.field,s.label,s.op,s.value):l("*","search","contains",r.value.trim())}),a.appendChild(r),t.appendChild(a),t.appendChild(I()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(i=>dt(i,e,d),"Sort"))),nt(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(i=>at(i,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(i=>lt(i,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const i=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",i)};window.addEventListener("afterprint",i),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{D(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;K(c).then(i=>{!i||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(s=>it(s,e,d,c),"Export")),u),R(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const i=[];x&&i.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&i.push("sort"),e._dmCtxGroup&&i.push("grouping"),t.appendChild(I()),t.appendChild(m(`Clear all (${i.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const a=et(document.createElement("div"));a.className="dm-ctx-menu",a._dmX=t,a._dmY=e,C=a,document.body.appendChild(a),a._dmPush(r=>rt(r,o,n,d,l),null),document.addEventListener("mousedown",O,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",G,!0),setTimeout(()=>{const r=a.querySelector(".dm-ctx-input");r&&r.focus()},0)}function F(t,e,o){if(t._dmTable){const r=e.closest("tbody tr");if(!r)return{entry:null,field:null};const u=e.closest("td"),c=r.querySelectorAll("td"),h=Array.from(c).map(i=>i.textContent.trim()),x=o.entries.find(i=>o.fields.every((s,k)=>String(i.data?.[s.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(r=>r.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let a=null;if(l){const r=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});r.length===1&&(a=r[0])}return{entry:d,field:a}}function st(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}st(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=F(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{cursor:"glyph",exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=F(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
|
|
1
|
+
import{decodeBase64Json as H}from"/public/js/utf8-base64.mjs";import{compareValues as ut,sortEntries as U}from"/public/js/collection-sort.mjs";import{serialise as T}from"/public/js/collection-export.mjs";import{parseQuery as ft}from"/public/js/collection-query.mjs";(function(){"use strict";const P=document.createElement("style");P.textContent=["[data-ctx] a, [data-ctx] button, [data-collection-table] a, [data-collection-table] button,","[data-ctx] input, [data-collection-table] input { cursor: pointer; }",'[data-ctx] input[type="text"], [data-collection-table] input[type="text"] { cursor: text; }',".dm-ctx-hidden { display: none !important; }",".dm-ctx-menu { position: fixed; z-index: 10000; min-width: 200px; max-width: 300px; max-height: 60vh; overflow-y: auto;background: var(--dm-surface, #1c1c30); border: 1px solid var(--dm-border, #2a2a40); border-radius: 6px;box-shadow: var(--dm-shadow-lg, 0 8px 24px rgba(0,0,0,.4)); padding: .3rem; font-size: .83rem;color: var(--dm-text, #e5e5e5); }",".dm-ctx-menu button.dm-ctx-row { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;background: none; border: none; color: inherit; font: inherit; padding: .35rem .6rem; border-radius: 4px; cursor: pointer; }",".dm-ctx-menu button.dm-ctx-row:hover { background: var(--dm-hover-bg, rgba(128,128,128,.14)); }",".dm-ctx-heading { padding: .3rem .6rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-input { width: 100%; font-size: .8rem; padding: .3rem .5rem; border-radius: 4px;border: 1px solid var(--dm-border, #2a2a40); background: var(--dm-input-bg, rgba(128,128,128,.08)); color: inherit; cursor: text; }",".dm-ctx-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0 0 .6rem; }",".dm-ctx-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; padding: .2rem .6rem;border-radius: 999px; background: var(--dm-hover-bg, rgba(128,128,128,.14)); border: 1px solid var(--dm-border, #2a2a40); }",".dm-ctx-chip button { background: none; border: none; cursor: pointer; color: inherit; font-size: .9rem; line-height: 1; padding: 0; }",".dm-ctx-empty-note { padding: .75rem; font-size: .85rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem;border-bottom: 1px solid var(--dm-border, #2a2a40); margin-bottom: .25rem; }",".dm-ctx-menu button.dm-ctx-back { width: auto; padding: .3rem .5rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-group-heading { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); padding: .5rem 0 .25rem; grid-column: 1 / -1; }"].join(`
|
|
2
|
+
`),document.head.appendChild(P);function g(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,o)=>({id:String(o),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=H(t.dataset.ctx)}catch{t._dmCtxModel=null}return t._dmCtxModel}function $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=ut(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=U(o.entries.filter(i=>e.every(r=>$(i,r))),n),l=new Set(d.map(i=>i.id));if(t._dmTable)t._dmTable.render(d.map(i=>i.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(i=>l.has(String(i._entryId))));else if(t._dmCtxSlides)J(t,l);else{const i=Array.from(t.querySelectorAll("[data-entry-id]"));i.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const r=new Map(i.map(h=>[h.dataset.entryId,h])),u=i.length?i[0].parentNode:null;n&&u&&d.forEach(h=>{const x=r.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const a=document.createElement("div");a.className="dm-ctx-group-heading",a.textContent=b;const s=r.get(x.id);s&&u.insertBefore(a,s)})}}tt(t,e),et(t,d.length,e.length),D()}const A="dmf";function M(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function V(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function W(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return H(e)}function D(){const t={};M().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(A+"="));Object.keys(t).length&&e.push(A+"="+V(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function Y(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return W(t[1])}catch{return null}}function N(t=0){const e=Y();if(!e)return;const o=M();let n=!1;for(const[d,l]of Object.entries(e)){const i=o[Number(d)];if(!i||!l)continue;if(i.hasAttribute("data-collection-table")?!i._dmTable:i.classList.contains("dm-progression")?!i._dmCtx:i.classList.contains("carousel")?!i._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};i._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),i._dmCtxSort=u.s||null,i._dmCtxGroup=u.g||null,p(i)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function J(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function X(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function Z(){return new Date().toISOString().slice(0,10)}let L=null;function K(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function Q(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await K();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const w={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function tt(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const i=document.createElement("span");i.className="dm-ctx-chip";const r=String(d.value),u=r.length>24?r.slice(0,24)+"\u2026":r;i.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${w[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),i.appendChild(c),o.appendChild(i)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function et(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",j,!0),document.removeEventListener("keydown",B,!0),window.removeEventListener("scroll",G,!0))}const j=t=>{C&&!C.contains(t.target)&&f()},B=t=>{t.key==="Escape"&&f()},G=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function O(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function I(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function R(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),I(t)}function nt(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),R(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&R(t,o.build,o.title)},t}function ot(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function dt(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function it(t,e,o){o.fields.forEach(n=>{const[d,l]=dt(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function at(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function q(t,e){const o=t._dmCtxFilters||[];return U(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function lt(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const i=T(q(e,o),o.fields,l);X(i,`${d}-${Z()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function rt(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(q(e,o),o.fields,"markdown"),"Table copied"),f()}))}function st(t,e,o,n,d){const l=(a,s,k,mt)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:a,label:s,op:k,value:mt}),p(e),f()};if(n&&o){const a=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"eq",a))),t.appendChild(m(`Filter: \u2260 \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"ne",a)));const s=String(a);(s!==""&&!Number.isNaN(parseFloat(s))||!Number.isNaN(Date.parse(s)))&&(t.appendChild(m(`Filter: > \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"gt",a))),t.appendChild(m(`Filter: < \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"lt",a))))}else if(o){t.appendChild(v("Filter by"));for(const a of d.fields){const s=o.data?.[a.name];s==null||String(s)===""||t.appendChild(m(`${a.label||a.name}: \u201C${y(s,20)}\u201D`,()=>l(a.name,a.label,"eq",s)))}}const i=document.createElement("div");i.style.padding=".35rem .6rem";const r=document.createElement("input");r.type="text",r.className="dm-ctx-input",r.placeholder="Search\u2026 or name: value",r.addEventListener("mousedown",a=>a.stopPropagation()),r.addEventListener("keydown",a=>{if(a.key!=="Enter"||!r.value.trim())return;const s=ft(r.value,d.fields);s?l(s.field,s.label,s.op,s.value):l("*","search","contains",r.value.trim())}),i.appendChild(r),t.appendChild(i),t.appendChild(O()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(a=>it(a,e,d),"Sort"))),ot(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(a=>at(a,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(a=>rt(a,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const a=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",a)};window.addEventListener("afterprint",a),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{D(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;Q(c).then(a=>{!a||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(s=>lt(s,e,d,c),"Export")),u),I(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const a=[];x&&a.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&a.push("sort"),e._dmCtxGroup&&a.push("grouping"),t.appendChild(O()),t.appendChild(m(`Clear all (${a.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const i=nt(document.createElement("div"));i.className="dm-ctx-menu",i._dmX=t,i._dmY=e,C=i,document.body.appendChild(i),i._dmPush(r=>st(r,o,n,d,l),null),document.addEventListener("mousedown",j,!0),document.addEventListener("keydown",B,!0),window.addEventListener("scroll",G,!0),setTimeout(()=>{const r=i.querySelector(".dm-ctx-input");r&&r.focus()},0)}function F(t,e,o){if(t._dmTable){const r=e.closest("tbody tr");if(!r)return{entry:null,field:null};const u=e.closest("td"),c=r.querySelectorAll("td"),h=Array.from(c).map(a=>a.textContent.trim()),x=o.entries.find(a=>o.fields.every((s,k)=>String(a.data?.[s.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(r=>r.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let i=null;if(l){const r=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});r.length===1&&(i=r[0])}return{entry:d,field:i}}function ct(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}ct(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=F(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{cursor:"glyph",exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=F(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},m=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function p(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),p(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&p(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(m);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(m)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(
|
|
1
|
+
import{decodeBase64Json as S}from"/public/js/utf8-base64.mjs";(function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},m=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function p(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),p(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&p(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(m);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(m)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(S(o.dataset.ctx)||{}).slug||"")}catch{}if(!r||!c){s("Right-click an entry in a collection display to mark it","error");return}try{const a=await fetch(`/api/collections/${encodeURIComponent(c)}/entries/${encodeURIComponent(r)}/spam`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({field:e.field||void 0})});if(a.status===401||a.status===403){s("You do not have permission to do that","error");return}if(!a.ok)throw new Error(`HTTP ${a.status}`);s("Marked as spam","success"),n&&n.setAttribute("data-spam","true")}catch(a){s(`Could not mark as spam: ${a.message}`,"error")}};case"open-modal":return e.block?async()=>{try{const t=await fetch(`/api/blocks/${encodeURIComponent(e.block)}/render`);if(!t.ok)throw new Error(`HTTP ${t.status}`);const{html:n}=await t.json(),r=E.modal({title:e.title||"",closeOnBackdrop:!0}),o=document.createElement("div");o.innerHTML=typeof DOMPurify<"u"?DOMPurify.sanitize(n):n,r.element.appendChild(o),r.open()}catch(t){s(`Could not open: ${t.message}`,"error")}}:null;case"search":return t=>{const n=f(t);n&&n.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:0,clientY:0}))};case"filter":case"sort":case"export":return t=>{const n=f(t);n&&n.dispatchEvent(new CustomEvent("dm-ctx-request",{bubbles:!0,detail:{...e}}))};default:return null}}const b=new Set(["search","filter","sort","export"]);function g(e){return l[e]&&l[e].disabled?l[e].disabled:b.has(e)?t=>!f(t):null}function h(e){const t=[];for(const n of e||[]){const r=n.type||"item";if(r==="divider"){t.push({type:"divider"});continue}if(r==="header"){t.push({type:"header",header:n.text||""});continue}if(Array.isArray(n.submenu)&&n.submenu.length){const a=h(n.submenu);a.length&&t.push({label:n.text,icon:n.icon,danger:!!n.danger,className:n.class||void 0,submenu:a});continue}const o=w(n.action);if(!o)continue;const c=g(n.action&&n.action.type);t.push({label:n.text,icon:n.icon||void 0,danger:!!n.danger,className:n.class||void 0,shortcut:n.shortcut||void 0,...c?{disabled:c}:{},action:o})}return t.some(n=>n.label)?t:[]}for(const e of d){const t=h(e.items);if(!t.length)continue;const n=e.target||{};if(!n.selector)continue;const r=e.theme||{};try{Domma.elements.contextMenu(n.selector,{cursor:"glyph",match:n.match||null,exclude:n.exclude||null,inherit:e.inherit===void 0?"append":e.inherit,priority:e.priority||0,items:t,accent:r.accent,surface:r.surface,radius:r.radius,shadow:r.shadow,opacity:r.opacity,density:r.density,transition:r.transition,ariaLabel:e.name||"Context menu",className:e.class||""})}catch(o){console.warn(`[context-menus] Skipped "${e.slug}": ${o.message}`)}}})();
|
package/public/js/effects.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";fetch("/api/effects/settings").then(function(
|
|
1
|
+
(function(){"use strict";fetch("/api/effects/settings").then(function(x){return x.json()}).then(function(x){var q=x.respectMotion!==!1,E=x.defaultDuration||600,C=x.defaultAnimation||"fade",k=x.defaultThreshold!=null?x.defaultThreshold:.1,u=q&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,c=document.querySelector(".page-body");if(!c)return;var b=c.querySelector?c.querySelector(".dm-fx-demo-controls"):null;if(b){b.hidden=!1;var g=b.querySelector("[data-fx-demo-status]"),w=b.querySelector("[data-fx-demo-play]");g.textContent=u?"Animations are disabled by your Reduce motion preference. The demos show static content.":"Animations are enabled. Scroll to each demo to see it, or replay the page.",w.textContent=u?"Enable animations on this site":"Replay demos",w.addEventListener("click",function(){if(u)try{localStorage.setItem("domma:reduced_motion","false")}catch{g.textContent="Your browser blocked saving the motion preference. Use its accessibility settings to allow animations.";return}window.location.reload()})}function r(t,e){return t.getAttribute("data-fx-"+e)}function n(t,e,i){var o=r(t,e),a=o!==null?parseFloat(o):NaN;return Number.isFinite(a)?a:i}function v(t,e,i){var o=r(t,e);return o===null?i:o==="true"||o==="1"}function z(t,e,i){var o=new IntersectionObserver(function(a){a[0].isIntersecting&&(o.unobserve(t),i())},{threshold:e});o.observe(t)}c.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||C,i=n(t,"duration",E),o=n(t,"delay",0),a=n(t,"threshold",k),s=v(t,"once",!0);u||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+i+"ms ease "+o+"ms, transform "+i+"ms ease "+o+"ms";var f=new IntersectionObserver(function(d){d.forEach(function(y){y.isIntersecting?(t.style.opacity="1",t.style.transform="",s&&f.unobserve(t)):s||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:a});f.observe(t)})}))}),c.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=n(t,"to",0),i=n(t,"from",0),o=n(t,"duration",2e3),a=r(t,"prefix")||"",s=r(t,"suffix")||"",f=n(t,"decimals",0),d=r(t,"separator")||"";function y(h){var m=h.toFixed(f);if(d){var l=m.split(".");l[0]=l[0].replace(/\B(?=(\d{3})+(?!\d))/g,d),m=l.join(".")}return a+m+s}if(u){t.textContent=y(e);return}z(t,.5,function(){var h=null;requestAnimationFrame(function m(l){h||(h=l);var S=Math.min((l-h)/o,1),I=1-Math.pow(1-S,3);t.textContent=y(i+(e-i)*I),S<1&&requestAnimationFrame(m)})})}),u||c.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=n(t,"speed",50),i=t.textContent,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",a=0;z(t,.3,function(){var s=setInterval(function(){for(var f="",d=0;d<i.length;d++)d<a||i[d]===" "?f+=i[d]:f+=o[Math.floor(Math.random()*o.length)];t.textContent=f,a<i.length?a++:clearInterval(s)},e)})}),u||c.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=n(t,"intensity",5),i=n(t,"duration",500),o=r(t,"direction")||"horizontal",a=o==="vertical"?"translateY":"translateX";t.animate([{transform:a+"(0)"},{transform:a+"(-"+e+"px)"},{transform:a+"("+e+"px)"},{transform:a+"(-"+e+"px)"},{transform:a+"("+e+"px)"},{transform:a+"(0)"}],{duration:i,easing:"ease-in-out"})}),c.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var i=r(t,"colour")||"rgba(255,255,255,0.3)",o=n(t,"duration",600),a=t.getBoundingClientRect(),s=Math.max(a.width,a.height)*2,f=document.createElement("span");f.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+s+"px;height:"+s+"px;left:"+(e.clientX-a.left-s/2)+"px;top:"+(e.clientY-a.top-s/2)+"px;background:"+i+";transform:scale(0);opacity:1;transition:transform "+o+"ms ease,opacity "+o+"ms ease;",t.appendChild(f),requestAnimationFrame(function(){f.style.transform="scale(1)",f.style.opacity="0"}),setTimeout(function(){f.remove()},o)})});var p=window.Domma&&window.Domma.effects;p&&(c.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!u){var e={};r(t,"amplitude")&&(e.amplitude=n(t,"amplitude",6)),r(t,"duration")&&(e.duration=n(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=n(t,"stagger",0)),p.breathe(t,e)}}),c.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!u){var e={};r(t,"scale")&&(e.scale=n(t,"scale",1.05)),r(t,"duration")&&(e.duration=n(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),p.pulse(t,e)}}),c.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=n(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=n(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=v(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=v(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=n(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=v(t,"pause-on-hover",!1));var i=r(t,"actions");if(i)try{e.actions=JSON.parse(i)}catch{return}else{var o=t.textContent.trim();if(!o)return;u||(t.textContent=""),e.actions=[{render:o}]}if(u){var a=(e.actions||[]).find(function(s){return s.render!=null});a&&(t.textContent=String(a.render));return}p.scribe(t,e)}),c.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=n(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=n(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",3)),p.twinkle(t,e)}),typeof p.tickerTape=="function"&&c.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!u){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",50)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"sway")&&(e.sway=n(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=n(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=n(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=n(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=n(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=n(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=n(t,"fade-start",.55)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?p.tickerTape(null,e):p.tickerTape(t,e)}}),typeof p.butterflies=="function"&&c.querySelectorAll(".dm-fx-butterflies").forEach(function(t){if(!u){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",18)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"wander")&&(e.wander=n(t,"wander",1)),r(t,"rise-speed")&&(e.riseSpeed=n(t,"rise-speed",.4)),r(t,"flap-speed")&&(e.flapSpeed=n(t,"flap-speed",1)),r(t,"min-size")&&(e.minSize=n(t,"min-size",14)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",26)),r(t,"two-tone")&&(e.twoTone=v(t,"two-tone",!0)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",40)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?p.butterflies(null,e):p.butterflies(t,e)}}),typeof p.strobe=="function"&&c.querySelectorAll(".dm-fx-strobe").forEach(function(t){if(u)return;function e(f){var d=r(t,f);if(!d)return null;var y=d.split(",").map(function(h){return h.trim()}).filter(Boolean);return y.length?y:null}var i={};r(t,"preset")&&(i.preset=r(t,"preset")),r(t,"motion")&&(i.motion=r(t,"motion")),r(t,"speed")&&(i.speed=n(t,"speed",1)),r(t,"sweep-arc")&&(i.sweepArc=n(t,"sweep-arc",100)),r(t,"beam-width")&&(i.beamWidth=n(t,"beam-width",16)),r(t,"intensity")&&(i.intensity=n(t,"intensity",.7)),r(t,"flicker")&&(i.flicker=v(t,"flicker",!0)),r(t,"hz")&&(i.hz=n(t,"hz",10));var o=e("origins");o&&(i.origins=o);var a=e("colours");a&&(i.colours=a),r(t,"duration")&&(i.duration=n(t,"duration",null)),r(t,"z-index")&&(i.zIndex=n(t,"z-index",9999));var s=r(t,"mode");s==="page"?p.strobe(null,i):p.strobe(t,i)}));var A=c.querySelectorAll(".dm-fx-celebrate");A.length&&import("/public/js/celebrations/index.js?v=20261003-characters").then(function(t){var e=t.CelebrationsEffect;A.forEach(function(i){var o=r(i,"character");if(o){if(i.querySelector("canvas"))return;t.mountCharacter(i,{character:o,size:r(i,"size")||"medium",facing:r(i,"facing")||"right",animate:!u&&v(i,"animate",!0)}).then(function(m){var l=new MutationObserver(function(){i.isConnected||(m.destroy(),l.disconnect())});i.isConnected?l.observe(document.body,{childList:!0,subtree:!0}):m.destroy()}).catch(function(m){console.error("[celebrate]",m)});return}if(!u){var a=i.getAttribute("data-fx-theme")||"auto",s=i.getAttribute("data-fx-intensity")||"medium",f=i.getAttribute("data-fx-z-index")?parseInt(i.getAttribute("data-fx-z-index"),10):999;if(!(a==="auto"&&(a=e.getCurrentTheme(),!a))){var d={},y=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(i.attributes).forEach(function(m){m.name.startsWith("data-fx-")&&!y.includes(m.name)&&(d[m.name.slice(8)]=m.value)});var h=new e({theme:a,intensity:s,enabled:!0,zIndex:f,overrides:d});h.init().then(function(){h.start()})}}})}).catch(function(){})}).catch(function(){})})();
|