directus-extension-csi-gallery-thumbnails 0.2.0 → 0.3.1
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 +16 -12
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,21 +44,25 @@ pnpm --filter directus-extension-csi-gallery-thumbnails build
|
|
|
44
44
|
|
|
45
45
|
The build creates `dist/index.js`.
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## Image enlargement (0.3.0)
|
|
48
|
+
|
|
49
|
+
Hover over a thumbnail for a 400px preview after a short delay. Keyboard focus
|
|
50
|
+
also previews an image. The preview stays within the viewport and closes when
|
|
51
|
+
scrolling, resizing, leaving the thumbnail, or pressing Escape. Click a thumbnail
|
|
52
|
+
(or press Enter/Space) to open a persistent, larger image dialog. Close with its
|
|
53
|
+
button, Escape, or the backdrop; focus returns to the thumbnail. Touch users can
|
|
54
|
+
tap to open the dialog. Neither action navigates to the product record.
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
Larger images load only on interaction: 800px for hover and 1600px for the dialog,
|
|
57
|
+
using uncropped transformations and the existing Directus session. Empty galleries,
|
|
58
|
+
non-image files, thumbnail spacing and wrapping retain their existing behavior.
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
2. Once the Marketplace lists it, install it with
|
|
54
|
-
`POST /extensions/registry/install {"extension": "<marketplace id>"}`.
|
|
55
|
-
3. Set the field displays above.
|
|
60
|
+
## Installation
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
to Related Values restores the click-to-open list.
|
|
62
|
+
Publish the package using the workflow below, then upgrade the pinned version
|
|
63
|
+
in `directus/deploy/Dockerfile` and deploy the Beanstalk bundle following
|
|
64
|
+
[the deployment runbook](../../deploy/README.md). The production extension is
|
|
65
|
+
baked into the image; a Marketplace install does not replace that local package.
|
|
62
66
|
|
|
63
67
|
## Single-image activation
|
|
64
68
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineDisplay as e}from"@directus/extensions-sdk";import{defineComponent as
|
|
1
|
+
import{defineDisplay as e}from"@directus/extensions-sdk";import{defineComponent as n,reactive as t,computed as i,ref as a,watch as r,onBeforeUnmount as o,openBlock as l,createElementBlock as s,Fragment as c,renderList as u,unref as d,withKeys as p,withModifiers as f,createElementVNode as m,toDisplayString as g,createBlock as b,Teleport as h,normalizeStyle as v,createCommentVNode as y,nextTick as x}from"vue";const w="directus_files_id",k=["id","type","title","modified_on"];function z(e){return("string"==typeof e?e.trim().replace(/^\.+|\.+$/g,""):"")||w}function _(e){return e.type?.startsWith("image/")??!1}function j(e,n){return C(e,n,160,"contain")}function C(e,n,t,i="inside"){const a=new URL(`assets/${n.id}`,e);return a.searchParams.set("width",String(t)),a.searchParams.set("height",String(t)),a.searchParams.set("fit",i),a.searchParams.set("format","auto"),n.modified_on&&a.searchParams.set("v",n.modified_on),a.href}function E(e){const n=e.type?.split("/")[1]?.split(/[+.;-]/)[0];return(n||"file").slice(0,4).toUpperCase()}const P={key:0,class:"csi-gallery-thumbnails null"},T={key:1,class:"csi-gallery-thumbnails"},$=["aria-label","onPointerenter","onFocus","onClick","onKeydown"],M=["alt","src","onError"],S={key:1,class:"thumb file"},A=["src"],L={key:1},I=["onKeydown"],O=["src","alt"],F={key:1,role:"status"};var K=n({__name:"display",props:{value:{},type:{},filePath:{}},setup(e){const n=e,w=function(e){const n=e.split("/"),t=n.indexOf("admin");return`${n.slice(0,t).join("/")}/`}(window.location.href),k=t(new Set),K=i(()=>function(e,n,t){const i="uuid"===t?[e]:Array.isArray(e)?e:[],a=z(n);return i.flatMap(e=>{const n="uuid"===t?e:function(e,n){return n.split(".").reduce((e,n)=>{if(e&&"object"==typeof e)return e[n]},e)}(e,a);if(!n||"object"!=typeof n)return[];const{id:i,type:r,title:o,modified_on:l}=n;return"string"==typeof i&&i?[{id:i,type:"string"==typeof r?r:null,title:"string"==typeof o?o:null,modified_on:"string"==typeof l?l:null}]:[]})}(n.value,n.filePath,n.type)),B=a(null),D=a({}),N=a(!1),R=a(null),U=a(!1),W=a(null);let q;function H(){clearTimeout(q),B.value=null,window.removeEventListener("scroll",H,!0),window.removeEventListener("resize",H)}function V(e,n){if(H(),R.value||!e.isConnected)return;const t=function(e,n,t){const i=12,a=Math.max(1,Math.min(400,n-24,t-24)),r=e.right+i+a<=n-i?e.right+i:e.left-i-a;return{left:Math.max(i,Math.min(r,n-a-i)),top:Math.max(i,Math.min(e.top,t-a-i)),width:a,height:a}}(e.getBoundingClientRect(),window.innerWidth,window.innerHeight);D.value=Object.fromEntries(Object.entries(t).map(([e,n])=>[e,`${n}px`])),N.value=!1,B.value=n,window.addEventListener("scroll",H,!0),window.addEventListener("resize",H)}function G(){W.value?.close(),R.value=null}function J(e){e.target===W.value&&G()}return r(K,()=>{H(),G()}),o(()=>{H(),G()}),(e,n)=>(l(),s(c,null,[0===K.value.length?(l(),s("span",P,"--")):(l(),s("span",T,[(l(!0),s(c,null,u(K.value,(e,t)=>(l(),s(c,{key:`${e.id}-${t}`},[d(_)(e)&&!k.has(e.id)?(l(),s("button",{key:0,type:"button",class:"image-button","aria-label":`Enlarge ${e.title||`image ${t+1}`}`,"aria-haspopup":"dialog",onPointerenter:n=>function(e,n){if("touch"===e.pointerType)return;H();const t=e.currentTarget;q=setTimeout(()=>V(t,n),150)}(n,e),onPointerleave:H,onFocus:n=>function(e,n){V(e.currentTarget,n)}(n,e),onBlur:H,onClick:f(n=>async function(e){H(),U.value=!1,R.value=e,await x(),W.value?.showModal()}(e),["stop"]),onDblclick:n[0]||(n[0]=f(()=>{},["stop"])),onKeydown:[n[1]||(n[1]=p(f(()=>{},["stop"]),["enter"])),n[2]||(n[2]=p(f(()=>{},["stop"]),["space"])),p(f(H,["stop"]),["esc"])]},[m("img",{alt:e.title??"",src:d(j)(d(w),e),class:"thumb",loading:"lazy",onError:n=>{return t=e.id,k.add(t),void(B.value?.id===t&&H());var t}},null,40,M)],40,$)):(l(),s("span",S,g(d(E)(e)),1))],64))),128))])),(l(),b(h,{to:"body"},[B.value?(l(),s("div",{key:0,class:"csi-image-preview",style:v(D.value),"aria-hidden":"true"},[N.value?(l(),s("span",L,"Image preview unavailable")):(l(),s("img",{key:B.value.id,src:d(C)(d(w),B.value,800),alt:"",onError:n[3]||(n[3]=e=>N.value=!0)},null,40,A))],4)):y("v-if",!0),m("dialog",{ref_key:"dialog",ref:W,class:"csi-image-dialog","aria-label":"Image preview",onClick:f(J,["stop"]),onKeydown:p(f(G,["stop","prevent"]),["esc"]),onClose:n[5]||(n[5]=e=>R.value=null),onCancel:n[6]||(n[6]=e=>R.value=null)},[R.value?(l(),s(c,{key:0},[m("header",null,[m("span",null,g(R.value.title||"Image preview"),1),m("button",{type:"button",autofocus:"","aria-label":"Close image preview",onClick:G}," × ")]),U.value?(l(),s("p",F,"Image preview unavailable")):(l(),s("img",{key:R.value.id,src:d(C)(d(w),R.value,1600),alt:R.value.title??"",onError:n[4]||(n[4]=e=>U.value=!0)},null,40,O))],64)):y("v-if",!0)],40,I)]))],64))}}),B=[],D=[];!function(e,n){if(e&&"undefined"!=typeof document){var t,i=!0===n.prepend?"prepend":"append",a=!0===n.singleTag,r="string"==typeof n.container?document.querySelector(n.container):document.getElementsByTagName("head")[0];if(a){var o=B.indexOf(r);-1===o&&(o=B.push(r)-1,D[o]={}),t=D[o]&&D[o][i]?D[o][i]:D[o][i]=l()}else t=l();65279===e.charCodeAt(0)&&(e=e.substring(1)),t.styleSheet?t.styleSheet.cssText+=e:t.appendChild(document.createTextNode(e))}function l(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),n.attributes)for(var t=Object.keys(n.attributes),a=0;a<t.length;a++)e.setAttribute(t[a],n.attributes[t[a]]);var o="prepend"===i?"afterbegin":"beforeend";return r.insertAdjacentElement(o,e),e}}('\n.csi-gallery-thumbnails[data-v-71b7e5c1] {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n max-inline-size: 100%;\n min-inline-size: 0;\n vertical-align: middle;\n}\n.image-button[data-v-71b7e5c1] {\n display: inline-flex;\n flex: none;\n padding: 0;\n border: 0;\n border-radius: var(--theme--border-radius);\n background: transparent;\n cursor: zoom-in;\n}\n.image-button[data-v-71b7e5c1]:focus-visible {\n outline: 2px solid var(--theme--primary, #6644ff);\n outline-offset: 2px;\n}\n.thumb[data-v-71b7e5c1] {\n flex: none;\n inline-size: var(--csi-gallery-thumbnail-size, 32px);\n block-size: var(--csi-gallery-thumbnail-size, 32px);\n border-radius: var(--theme--border-radius);\n object-fit: contain;\n background-color: var(--theme--background-normal);\n}\n.file[data-v-71b7e5c1] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--theme--foreground-subdued);\n font-size: 9px;\n font-weight: 600;\n}\n.null[data-v-71b7e5c1] {\n color: var(--theme--foreground-subdued);\n}\n\n/* Directus 12.3 table rows expose their native spacing as Vue CSS variables:\n 29/43/58px plus a 2px border. Match those values without depending on the\n generated variable name. Leave the native variables intact so switching\n spacing continues to work; only rows containing this display grow. */\n.table-row:has(.csi-gallery-thumbnails) {\n --csi-gallery-thumbnail-size: 32px;\n block-size: auto;\n min-block-size: calc(var(--csi-gallery-thumbnail-size) + 16px);\n}\n.table-row[style*=": 45px;"]:has(.csi-gallery-thumbnails) {\n --csi-gallery-thumbnail-size: 56px;\n}\n.table-row[style*=": 60px;"]:has(.csi-gallery-thumbnails) {\n --csi-gallery-thumbnail-size: 80px;\n}\n.csi-image-preview[data-v-71b7e5c1] {\n position: fixed;\n z-index: 10000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 8px;\n box-sizing: border-box;\n border: 1px solid var(--theme--border-color, #dde4e8);\n border-radius: var(--theme--border-radius, 6px);\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #26333f);\n box-shadow: 0 8px 32px #0003;\n pointer-events: none;\n}\n.csi-image-preview img[data-v-71b7e5c1] {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n.csi-image-dialog[data-v-71b7e5c1] {\n width: min(960px, calc(100vw - 32px));\n max-height: calc(100dvh - 32px);\n padding: 16px;\n box-sizing: border-box;\n border: 1px solid var(--theme--border-color, #dde4e8);\n border-radius: var(--theme--border-radius, 6px);\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #26333f);\n}\n.csi-image-dialog[data-v-71b7e5c1]::backdrop {\n background: #0009;\n}\n.csi-image-dialog header[data-v-71b7e5c1] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 12px;\n}\n.csi-image-dialog header span[data-v-71b7e5c1] {\n overflow-wrap: anywhere;\n}\n.csi-image-dialog button[data-v-71b7e5c1] {\n flex: none;\n width: 36px;\n height: 36px;\n border: 0;\n border-radius: 6px;\n font: 28px/1 sans-serif;\n cursor: pointer;\n color: inherit;\n background: var(--theme--background-normal, #f1f4f6);\n}\n.csi-image-dialog > img[data-v-71b7e5c1] {\n display: block;\n width: 100%;\n height: min(700px, 70dvh);\n object-fit: contain;\n}\n',{});var N=e({id:"csi-gallery-thumbnails",name:"Image Thumbnails",icon:"photo_library",description:"Show every image with hover previews and click-to-enlarge.",component:((e,n)=>{const t=e.__vccOpts||e;for(const[e,i]of n)t[e]=i;return t})(K,[["__scopeId","data-v-71b7e5c1"]]),types:["alias","uuid"],localTypes:["files","m2m","o2m","file"],fields:(e,n)=>function(e,n){if("uuid"===n)return[...k];const t=z(e);return k.map(e=>`${t}.${e}`)}(e?.filePath,n.type),options:e=>"uuid"===e.field?.type?[]:[{field:"filePath",name:"Path to the file",type:"string",meta:{interface:"input",width:"half",note:"From one gallery row to its file. Files field: directus_files_id. Asset Library: asset_library_id.file",options:{placeholder:w,font:"monospace"}},schema:{default_value:w}}]});export{N as default};
|