directus-extension-csi-gallery-thumbnails 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -25
- package/dist/index.js +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
# CSI
|
|
1
|
+
# CSI Image Thumbnails display
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
("3 items").
|
|
3
|
+
A Directus display for single-image fields and complete galleries. Both follow
|
|
4
|
+
the same Compact, Cozy, and Comfortable table spacing. Gallery cells show
|
|
5
|
+
pictures instead of a count ("3 items").
|
|
6
6
|
|
|
7
7
|
Directus's built-in Related Values display can only show a count for a
|
|
8
8
|
multi-image field; its thumbnails appear after clicking the count. This display
|
|
9
|
-
draws
|
|
9
|
+
draws every thumbnail in the cell itself, wrapping onto additional lines when the column is narrow.
|
|
10
10
|
Non-image files show a short type label such as `PDF`. Empty galleries show `--`.
|
|
11
11
|
|
|
12
|
-
Thumbnails
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Thumbnails follow Directus 12.3.1 table spacing: **Compact 32px**, **Cozy
|
|
13
|
+
56px**, **Comfortable 80px**. Rows containing this display expand to fit the
|
|
14
|
+
images, including wrapped galleries. Other tables keep their native row height.
|
|
15
|
+
The display matches the native row's 31/45/60px CSS variable values, without
|
|
16
|
+
relying on their generated names; verify these values when upgrading Directus.
|
|
17
|
+
|
|
18
|
+
Images use 160px contain transformations for crisp 80px previews without
|
|
19
|
+
cropping, retain the modified-on cache buster, and load with the editor's
|
|
20
|
+
session so private files work too. Directus must allow these asset
|
|
21
|
+
transformations. Existing saved `limit` options are ignored; every photo is
|
|
22
|
+
shown. Empty and non-image file behavior is preserved.
|
|
15
23
|
|
|
16
24
|
## Fields
|
|
17
25
|
|
|
18
|
-
| Field
|
|
19
|
-
|
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
26
|
+
| Field | Path to the file |
|
|
27
|
+
| ------------------------------------------- | ----------------------------- |
|
|
28
|
+
| `products.thumbnail` (single file) | No option needed |
|
|
29
|
+
| `product_families.hero_image` (single file) | No option needed |
|
|
30
|
+
| `product_families.gallery` (Files) | `directus_files_id` (default) |
|
|
31
|
+
| `products.gallery_assets` (Asset Library) | `asset_library_id.file` |
|
|
22
32
|
|
|
23
|
-
Set the field's display to **
|
|
24
|
-
file**: the dot path from one gallery row (the junction item) to its
|
|
33
|
+
Set the field's display to **Image Thumbnails**. Single-file fields need no
|
|
34
|
+
options. For galleries, fill in **Path to the file**: the dot path from one gallery row (the junction item) to its
|
|
25
35
|
`directus_files` record.
|
|
26
36
|
|
|
27
37
|
## Build and test
|
|
@@ -34,18 +44,48 @@ pnpm --filter directus-extension-csi-gallery-thumbnails build
|
|
|
34
44
|
|
|
35
45
|
The build creates `dist/index.js`.
|
|
36
46
|
|
|
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.
|
|
55
|
+
|
|
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.
|
|
59
|
+
|
|
37
60
|
## Installation
|
|
38
61
|
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
66
|
+
|
|
67
|
+
## Single-image activation
|
|
68
|
+
|
|
69
|
+
After 0.2.0 is installed, change only the display metadata on
|
|
70
|
+
`products.thumbnail` and `product_families.hero_image` to
|
|
71
|
+
`display=csi-gallery-thumbnails`, with `display_options=null`. Preserve their
|
|
72
|
+
interfaces and file references. The extension uses Directus's field type to
|
|
73
|
+
fetch the related file directly; it does not change the stored image.
|
|
74
|
+
|
|
75
|
+
## Publishing without browser authentication
|
|
76
|
+
|
|
77
|
+
Configure an npm trusted publisher for this package with GitHub owner
|
|
78
|
+
`Nuwave-Marketing`, repository `csi-big-commerce-catalyst`, workflow filename
|
|
79
|
+
`publish-gallery-thumbnails.yml`, and **Allow npm publish** enabled. Leave the
|
|
80
|
+
environment name empty; dist-tag management is not needed.
|
|
41
81
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
82
|
+
After merging a version change to `main`, run **Publish Directus Image
|
|
83
|
+
Thumbnails** in GitHub Actions on `main` and enter the package version. The
|
|
84
|
+
workflow checks that version, tests and builds the extension using locked
|
|
85
|
+
dependencies, and publishes using GitHub's short-lived OIDC identity. It needs
|
|
86
|
+
no npm login, saved publishing token, or passkey prompt for routine releases.
|
|
87
|
+
An npm owner still authenticates when initially configuring the connection.
|
|
88
|
+
The workflow refuses releases from other branches.
|
|
46
89
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
until the hosting team bakes the CSI extensions into the Directus image. While
|
|
50
|
-
it is missing, the two fields show no thumbnails; switching their display back
|
|
51
|
-
to Related Values restores the click-to-open list.
|
|
90
|
+
The repository is private, so npm currently does not generate public provenance
|
|
91
|
+
for this workflow. Trusted publishing still authenticates the release.
|
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 d,renderList as u,unref as c,withKeys as p,withModifiers as f,createElementVNode as m,toDisplayString as g,createBlock as h,Teleport as v,normalizeStyle as b,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=["src","alt"],O={key:1,role:"status"};var F=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),F=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({}),K=a(!1),N=a(null),R=a(!1),U=a(null);let W;function q(){clearTimeout(W),B.value=null,window.removeEventListener("scroll",q,!0),window.removeEventListener("resize",q)}function H(e,n){if(q(),N.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`])),K.value=!1,B.value=n,window.addEventListener("scroll",q,!0),window.addEventListener("resize",q)}function V(){U.value?.close(),N.value=null}function G(e){e.target===U.value&&V()}return r(F,()=>{q(),V()}),o(()=>{q(),V()}),(e,n)=>(l(),s(d,null,[0===F.value.length?(l(),s("span",P,"--")):(l(),s("span",T,[(l(!0),s(d,null,u(F.value,(e,t)=>(l(),s(d,{key:`${e.id}-${t}`},[c(_)(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;q();const t=e.currentTarget;W=setTimeout(()=>H(t,n),150)}(n,e),onPointerleave:q,onFocus:n=>function(e,n){H(e.currentTarget,n)}(n,e),onBlur:q,onClick:f(n=>async function(e){q(),R.value=!1,N.value=e,await x(),U.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(q,["stop"]),["esc"])]},[m("img",{alt:e.title??"",src:c(j)(c(w),e),class:"thumb",loading:"lazy",onError:n=>{return t=e.id,k.add(t),void(B.value?.id===t&&q());var t}},null,40,M)],40,$)):(l(),s("span",S,g(c(E)(e)),1))],64))),128))])),(l(),h(v,{to:"body"},[B.value?(l(),s("div",{key:0,class:"csi-image-preview",style:b(D.value),"aria-hidden":"true"},[K.value?(l(),s("span",L,"Image preview unavailable")):(l(),s("img",{key:B.value.id,src:c(C)(c(w),B.value,800),alt:"",onError:n[3]||(n[3]=e=>K.value=!0)},null,40,A))],4)):y("v-if",!0),m("dialog",{ref_key:"dialog",ref:U,class:"csi-image-dialog","aria-label":"Image preview",onClick:f(G,["stop"]),onClose:n[5]||(n[5]=e=>N.value=null),onCancel:n[6]||(n[6]=e=>N.value=null)},[N.value?(l(),s(d,{key:0},[m("header",null,[m("span",null,g(N.value.title||"Image preview"),1),m("button",{type:"button",autofocus:"","aria-label":"Close image preview",onClick:V}," × ")]),R.value?(l(),s("p",O,"Image preview unavailable")):(l(),s("img",{key:N.value.id,src:c(C)(c(w),N.value,1600),alt:N.value.title??"",onError:n[4]||(n[4]=e=>R.value=!0)},null,40,I))],64)):y("v-if",!0)],544)]))],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-dad23590] {\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-dad23590] {\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-dad23590]:focus-visible {\n outline: 2px solid var(--theme--primary, #6644ff);\n outline-offset: 2px;\n}\n.thumb[data-v-dad23590] {\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-dad23590] {\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-dad23590] {\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-dad23590] {\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-dad23590] {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n.csi-image-dialog[data-v-dad23590] {\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-dad23590]::backdrop {\n background: #0009;\n}\n.csi-image-dialog header[data-v-dad23590] {\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-dad23590] {\n overflow-wrap: anywhere;\n}\n.csi-image-dialog button[data-v-dad23590] {\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-dad23590] {\n display: block;\n width: 100%;\n height: min(700px, 70dvh);\n object-fit: contain;\n}\n',{});var K=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})(F,[["__scopeId","data-v-dad23590"]]),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{K as default};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus-extension-csi-gallery-thumbnails",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Directus display
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Directus display for single images and complete galleries, sized to table spacing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
7
7
|
"directus-extension",
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
],
|
|
12
12
|
"icon": "photo_library",
|
|
13
13
|
"license": "UNLICENSED",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/Nuwave-Marketing/csi-big-commerce-catalyst.git",
|
|
17
|
+
"directory": "directus/extensions/csi-gallery-thumbnails"
|
|
18
|
+
},
|
|
14
19
|
"publishConfig": {
|
|
15
20
|
"access": "public"
|
|
16
21
|
},
|