@pro-laico/payload-images 0.0.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/LICENSE.md +22 -0
- package/README.md +11 -0
- package/dist/access.d.ts +4 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +4 -0
- package/dist/access.js.map +1 -0
- package/dist/collections/generatedImages.d.ts +26 -0
- package/dist/collections/generatedImages.d.ts.map +1 -0
- package/dist/collections/generatedImages.js +91 -0
- package/dist/collections/generatedImages.js.map +1 -0
- package/dist/collections/images.d.ts +60 -0
- package/dist/collections/images.d.ts.map +1 -0
- package/dist/collections/images.js +223 -0
- package/dist/collections/images.js.map +1 -0
- package/dist/components/admin/focalPreview.d.ts +8 -0
- package/dist/components/admin/focalPreview.d.ts.map +1 -0
- package/dist/components/admin/focalPreview.js +234 -0
- package/dist/components/admin/focalPreview.js.map +1 -0
- package/dist/components/admin/purgeVariants.d.ts +16 -0
- package/dist/components/admin/purgeVariants.d.ts.map +1 -0
- package/dist/components/admin/purgeVariants.js +66 -0
- package/dist/components/admin/purgeVariants.js.map +1 -0
- package/dist/components/image.d.ts +90 -0
- package/dist/components/image.d.ts.map +1 -0
- package/dist/components/image.js +162 -0
- package/dist/components/image.js.map +1 -0
- package/dist/components/inlineLqip.d.ts +36 -0
- package/dist/components/inlineLqip.d.ts.map +1 -0
- package/dist/components/inlineLqip.js +54 -0
- package/dist/components/inlineLqip.js.map +1 -0
- package/dist/endpoints/transform.d.ts +40 -0
- package/dist/endpoints/transform.d.ts.map +1 -0
- package/dist/endpoints/transform.js +181 -0
- package/dist/endpoints/transform.js.map +1 -0
- package/dist/fields/virtualUrls.d.ts +26 -0
- package/dist/fields/virtualUrls.d.ts.map +1 -0
- package/dist/fields/virtualUrls.js +185 -0
- package/dist/fields/virtualUrls.js.map +1 -0
- package/dist/hooks/purge.d.ts +33 -0
- package/dist/hooks/purge.d.ts.map +1 -0
- package/dist/hooks/purge.js +63 -0
- package/dist/hooks/purge.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/configStash.d.ts +6 -0
- package/dist/lib/configStash.d.ts.map +1 -0
- package/dist/lib/configStash.js +10 -0
- package/dist/lib/configStash.js.map +1 -0
- package/dist/lib/getServerSideURL.d.ts +8 -0
- package/dist/lib/getServerSideURL.d.ts.map +1 -0
- package/dist/lib/getServerSideURL.js +12 -0
- package/dist/lib/getServerSideURL.js.map +1 -0
- package/dist/lib/mergeCollection.d.ts +17 -0
- package/dist/lib/mergeCollection.d.ts.map +1 -0
- package/dist/lib/mergeCollection.js +55 -0
- package/dist/lib/mergeCollection.js.map +1 -0
- package/dist/plugin.d.ts +128 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +184 -0
- package/dist/plugin.js.map +1 -0
- package/dist/transform/coalesce.d.ts +16 -0
- package/dist/transform/coalesce.d.ts.map +1 -0
- package/dist/transform/coalesce.js +25 -0
- package/dist/transform/coalesce.js.map +1 -0
- package/dist/transform/geometry.d.ts +46 -0
- package/dist/transform/geometry.d.ts.map +1 -0
- package/dist/transform/geometry.js +64 -0
- package/dist/transform/geometry.js.map +1 -0
- package/dist/transform/getVariantBytes.d.ts +60 -0
- package/dist/transform/getVariantBytes.d.ts.map +1 -0
- package/dist/transform/getVariantBytes.js +163 -0
- package/dist/transform/getVariantBytes.js.map +1 -0
- package/dist/transform/limit.d.ts +5 -0
- package/dist/transform/limit.d.ts.map +1 -0
- package/dist/transform/limit.js +41 -0
- package/dist/transform/limit.js.map +1 -0
- package/dist/transform/lqip.d.ts +21 -0
- package/dist/transform/lqip.d.ts.map +1 -0
- package/dist/transform/lqip.js +32 -0
- package/dist/transform/lqip.js.map +1 -0
- package/dist/transform/params.d.ts +100 -0
- package/dist/transform/params.d.ts.map +1 -0
- package/dist/transform/params.js +144 -0
- package/dist/transform/params.js.map +1 -0
- package/dist/transform/sharp.d.ts +28 -0
- package/dist/transform/sharp.d.ts.map +1 -0
- package/dist/transform/sharp.js +85 -0
- package/dist/transform/sharp.js.map +1 -0
- package/dist/transform/sharpInstance.d.ts +21 -0
- package/dist/transform/sharpInstance.d.ts.map +1 -0
- package/dist/transform/sharpInstance.js +38 -0
- package/dist/transform/sharpInstance.js.map +1 -0
- package/dist/transform/source.d.ts +32 -0
- package/dist/transform/source.d.ts.map +1 -0
- package/dist/transform/source.js +121 -0
- package/dist/transform/source.js.map +1 -0
- package/dist/utils/urls.d.ts +108 -0
- package/dist/utils/urls.d.ts.map +1 -0
- package/dist/utils/urls.js +113 -0
- package/dist/utils/urls.js.map +1 -0
- package/dist/variants/key.d.ts +9 -0
- package/dist/variants/key.d.ts.map +1 -0
- package/dist/variants/key.js +29 -0
- package/dist/variants/key.js.map +1 -0
- package/package.json +98 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Pro Laico LLC <contact@prolaico.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @pro-laico/payload-images
|
|
2
|
+
|
|
3
|
+
On-demand image optimization for [Payload CMS](https://payloadcms.com/) — an Images collection that stores only the original and generates, crops, and caches each rendered size on first request, with a responsive `<ResponsiveImage>` component and admin focal-point picker.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @pro-laico/payload-images
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
**[Documentation →](https://payload-plugins.prolaico.com/docs/plugins/payload-images)**
|
|
10
|
+
|
|
11
|
+
Ported from [`@pro-laico/images`](https://github.com/pro-laico/atomic-payload) (Atomic Payload, MIT).
|
package/dist/access.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,eAAO,MAAM,MAAM,EAAE,MAAmB,CAAA;AACxC,eAAO,MAAM,KAAK,EAAE,MAAuC,CAAA"}
|
package/dist/access.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/access.ts"],"sourcesContent":["import type { Access } from 'payload'\n\nexport const anyone: Access = () => true\nexport const authd: Access = ({ req }) => Boolean(req.user)\n"],"names":["anyone","authd","req","Boolean","user"],"mappings":"AAEA,OAAO,MAAMA,SAAiB,IAAM,KAAI;AACxC,OAAO,MAAMC,QAAgB,CAAC,EAAEC,GAAG,EAAE,GAAKC,QAAQD,IAAIE,IAAI,EAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
export declare const GENERATED_IMAGES_SLUG = "generated-images";
|
|
3
|
+
export interface CreateGeneratedImagesOptions {
|
|
4
|
+
/** Slug for this collection. Default `generated-images`. */
|
|
5
|
+
slug?: string;
|
|
6
|
+
/** Slug of the source image collection the variants point back to. Default `images`. */
|
|
7
|
+
sourceSlug?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The hidden, durable cache of on-demand image variants. The transform endpoint
|
|
11
|
+
* writes one upload doc here per (source, settings, focal) combination; the source
|
|
12
|
+
* `images` collection surfaces them through a `join` field and purges them when the
|
|
13
|
+
* source changes or is deleted.
|
|
14
|
+
*
|
|
15
|
+
* It is an UPLOAD collection so variant bytes flow through whatever storage adapter
|
|
16
|
+
* is configured (local disk, S3, Vercel Blob, …) — keeping the feature
|
|
17
|
+
* platform-agnostic. With a cloud storage adapter, register it with a SERVER-upload
|
|
18
|
+
* storage instance, since the endpoint creates docs server-side via the Local API.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately carries NO revalidation hooks: variants are derived and disposable,
|
|
21
|
+
* so busting cache tags on every cache-miss create would be pure churn.
|
|
22
|
+
*/
|
|
23
|
+
export declare const createGeneratedImagesCollection: (opts?: CreateGeneratedImagesOptions) => CollectionConfig;
|
|
24
|
+
/** The default generated-images collection. */
|
|
25
|
+
export declare const GeneratedImages: CollectionConfig;
|
|
26
|
+
//# sourceMappingURL=generatedImages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generatedImages.d.ts","sourceRoot":"","sources":["../../src/collections/generatedImages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,SAAS,CAAA;AAK/D,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD,MAAM,WAAW,4BAA4B;IAC3C,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,+BAA+B,GAAI,OAAM,4BAAiC,KAAG,gBA6BzF,CAAA;AAED,+CAA+C;AAC/C,eAAO,MAAM,eAAe,EAAE,gBAAoD,CAAA"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { authd } from "../access.js";
|
|
2
|
+
import { IMAGE_MIME_TYPES } from "../transform/params.js";
|
|
3
|
+
export const GENERATED_IMAGES_SLUG = 'generated-images';
|
|
4
|
+
/**
|
|
5
|
+
* The hidden, durable cache of on-demand image variants. The transform endpoint
|
|
6
|
+
* writes one upload doc here per (source, settings, focal) combination; the source
|
|
7
|
+
* `images` collection surfaces them through a `join` field and purges them when the
|
|
8
|
+
* source changes or is deleted.
|
|
9
|
+
*
|
|
10
|
+
* It is an UPLOAD collection so variant bytes flow through whatever storage adapter
|
|
11
|
+
* is configured (local disk, S3, Vercel Blob, …) — keeping the feature
|
|
12
|
+
* platform-agnostic. With a cloud storage adapter, register it with a SERVER-upload
|
|
13
|
+
* storage instance, since the endpoint creates docs server-side via the Local API.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately carries NO revalidation hooks: variants are derived and disposable,
|
|
16
|
+
* so busting cache tags on every cache-miss create would be pure churn.
|
|
17
|
+
*/ export const createGeneratedImagesCollection = (opts = {})=>{
|
|
18
|
+
const slug = opts.slug || GENERATED_IMAGES_SLUG;
|
|
19
|
+
const sourceSlug = opts.sourceSlug || 'images';
|
|
20
|
+
return {
|
|
21
|
+
slug,
|
|
22
|
+
access: {
|
|
23
|
+
create: authd,
|
|
24
|
+
delete: authd,
|
|
25
|
+
read: authd,
|
|
26
|
+
update: authd
|
|
27
|
+
},
|
|
28
|
+
admin: {
|
|
29
|
+
hidden: true,
|
|
30
|
+
group: 'Assets',
|
|
31
|
+
useAsTitle: 'cacheKey',
|
|
32
|
+
defaultColumns: [
|
|
33
|
+
'cacheKey',
|
|
34
|
+
'width',
|
|
35
|
+
'height',
|
|
36
|
+
'format'
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
fields: [
|
|
40
|
+
{
|
|
41
|
+
name: 'source',
|
|
42
|
+
type: 'relationship',
|
|
43
|
+
relationTo: sourceSlug,
|
|
44
|
+
required: true,
|
|
45
|
+
index: true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'cacheKey',
|
|
49
|
+
type: 'text',
|
|
50
|
+
required: true,
|
|
51
|
+
unique: true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'row',
|
|
55
|
+
fields: [
|
|
56
|
+
{
|
|
57
|
+
name: 'fit',
|
|
58
|
+
type: 'text'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'format',
|
|
62
|
+
type: 'text'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'quality',
|
|
66
|
+
type: 'number'
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'row',
|
|
72
|
+
fields: [
|
|
73
|
+
{
|
|
74
|
+
name: 'focalX',
|
|
75
|
+
type: 'number'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'focalY',
|
|
79
|
+
type: 'number'
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
upload: {
|
|
85
|
+
mimeTypes: IMAGE_MIME_TYPES
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** The default generated-images collection. */ export const GeneratedImages = createGeneratedImagesCollection();
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=generatedImages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/generatedImages.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport { authd } from '../access'\nimport { IMAGE_MIME_TYPES } from '../transform/params'\n\nexport const GENERATED_IMAGES_SLUG = 'generated-images'\n\nexport interface CreateGeneratedImagesOptions {\n /** Slug for this collection. Default `generated-images`. */\n slug?: string\n /** Slug of the source image collection the variants point back to. Default `images`. */\n sourceSlug?: string\n}\n\n/**\n * The hidden, durable cache of on-demand image variants. The transform endpoint\n * writes one upload doc here per (source, settings, focal) combination; the source\n * `images` collection surfaces them through a `join` field and purges them when the\n * source changes or is deleted.\n *\n * It is an UPLOAD collection so variant bytes flow through whatever storage adapter\n * is configured (local disk, S3, Vercel Blob, …) — keeping the feature\n * platform-agnostic. With a cloud storage adapter, register it with a SERVER-upload\n * storage instance, since the endpoint creates docs server-side via the Local API.\n *\n * Deliberately carries NO revalidation hooks: variants are derived and disposable,\n * so busting cache tags on every cache-miss create would be pure churn.\n */\nexport const createGeneratedImagesCollection = (opts: CreateGeneratedImagesOptions = {}): CollectionConfig => {\n const slug = opts.slug || GENERATED_IMAGES_SLUG\n const sourceSlug = (opts.sourceSlug || 'images') as CollectionSlug\n\n return {\n slug,\n access: { create: authd, delete: authd, read: authd, update: authd },\n admin: { hidden: true, group: 'Assets', useAsTitle: 'cacheKey', defaultColumns: ['cacheKey', 'width', 'height', 'format'] },\n fields: [\n { name: 'source', type: 'relationship', relationTo: sourceSlug, required: true, index: true },\n { name: 'cacheKey', type: 'text', required: true, unique: true },\n {\n type: 'row',\n fields: [\n { name: 'fit', type: 'text' },\n { name: 'format', type: 'text' },\n { name: 'quality', type: 'number' },\n ],\n },\n {\n type: 'row',\n fields: [\n { name: 'focalX', type: 'number' },\n { name: 'focalY', type: 'number' },\n ],\n },\n ],\n upload: { mimeTypes: IMAGE_MIME_TYPES },\n }\n}\n\n/** The default generated-images collection. */\nexport const GeneratedImages: CollectionConfig = createGeneratedImagesCollection()\n"],"names":["authd","IMAGE_MIME_TYPES","GENERATED_IMAGES_SLUG","createGeneratedImagesCollection","opts","slug","sourceSlug","access","create","delete","read","update","admin","hidden","group","useAsTitle","defaultColumns","fields","name","type","relationTo","required","index","unique","upload","mimeTypes","GeneratedImages"],"mappings":"AAEA,SAASA,KAAK,QAAQ,eAAW;AACjC,SAASC,gBAAgB,QAAQ,yBAAqB;AAEtD,OAAO,MAAMC,wBAAwB,mBAAkB;AASvD;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMC,kCAAkC,CAACC,OAAqC,CAAC,CAAC;IACrF,MAAMC,OAAOD,KAAKC,IAAI,IAAIH;IAC1B,MAAMI,aAAcF,KAAKE,UAAU,IAAI;IAEvC,OAAO;QACLD;QACAE,QAAQ;YAAEC,QAAQR;YAAOS,QAAQT;YAAOU,MAAMV;YAAOW,QAAQX;QAAM;QACnEY,OAAO;YAAEC,QAAQ;YAAMC,OAAO;YAAUC,YAAY;YAAYC,gBAAgB;gBAAC;gBAAY;gBAAS;gBAAU;aAAS;QAAC;QAC1HC,QAAQ;YACN;gBAAEC,MAAM;gBAAUC,MAAM;gBAAgBC,YAAYd;gBAAYe,UAAU;gBAAMC,OAAO;YAAK;YAC5F;gBAAEJ,MAAM;gBAAYC,MAAM;gBAAQE,UAAU;gBAAME,QAAQ;YAAK;YAC/D;gBACEJ,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAOC,MAAM;oBAAO;oBAC5B;wBAAED,MAAM;wBAAUC,MAAM;oBAAO;oBAC/B;wBAAED,MAAM;wBAAWC,MAAM;oBAAS;iBACnC;YACH;YACA;gBACEA,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAUC,MAAM;oBAAS;oBACjC;wBAAED,MAAM;wBAAUC,MAAM;oBAAS;iBAClC;YACH;SACD;QACDK,QAAQ;YAAEC,WAAWxB;QAAiB;IACxC;AACF,EAAC;AAED,6CAA6C,GAC7C,OAAO,MAAMyB,kBAAoCvB,kCAAiC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
/** Admin component subpaths (referenced by the Payload import map). */
|
|
3
|
+
export declare const FocalPreviewFieldPath = "@pro-laico/payload-images/admin/focalPreview";
|
|
4
|
+
export declare const PurgeVariantsFieldPath = "@pro-laico/payload-images/admin/purgeVariants";
|
|
5
|
+
export interface CreateImagesOptions {
|
|
6
|
+
/** Render the focal-point + ratio-preview field and the purge-variants button. Default true. */
|
|
7
|
+
focalUI?: boolean;
|
|
8
|
+
/** Aspect ratios shown in the focal preview tiles. */
|
|
9
|
+
previewRatios?: string[];
|
|
10
|
+
/** Slug of the generated-images collection the `variants` join targets. */
|
|
11
|
+
variantSlug?: string;
|
|
12
|
+
/** Purge route (under the API base) the purge button POSTs to. Default `/img/purge`. */
|
|
13
|
+
purgePath?: string;
|
|
14
|
+
/** Admin list/preview thumbnail width (px), served on-demand via the transform endpoint so the
|
|
15
|
+
* admin never loads full-res originals. Default 160; pass `false` to use Payload's default. */
|
|
16
|
+
adminThumbnail?: number | false;
|
|
17
|
+
/** The app's API route base (`config.routes.api`), used to build the admin thumbnail URL. Default `/api`. */
|
|
18
|
+
apiRoute?: string;
|
|
19
|
+
/** Whether the transform + purge endpoints are registered (`transform !== false`). When false, the
|
|
20
|
+
* purge button and the `variants` join — UI that targets those endpoints — are skipped. Default true. */
|
|
21
|
+
endpointsEnabled?: boolean;
|
|
22
|
+
/** Add virtual `src`/`srcset`/`placeholderURL`/`thumbnailURL` fields (computed on read), so
|
|
23
|
+
* optimized URLs ride along in every REST/GraphQL/Local-API response. Default true. */
|
|
24
|
+
virtualFields?: boolean;
|
|
25
|
+
/** Mark the `alt` field `localized: true` (requires Payload localization). Default false. */
|
|
26
|
+
localizeAlt?: boolean;
|
|
27
|
+
/** Accepted upload mime types. Defaults to the raster formats the transform pipeline can
|
|
28
|
+
* process ({@link IMAGE_MIME_TYPES}: avif/webp/jpeg/png). Override to widen or narrow what
|
|
29
|
+
* the collection accepts — but the endpoint only meaningfully transforms raster images. */
|
|
30
|
+
mimeTypes?: string[];
|
|
31
|
+
/** Enable Payload's native folder organization on the collection (adds a folder relationship). Default false. */
|
|
32
|
+
folders?: boolean;
|
|
33
|
+
/** Cap the *stored* original's longest edge (px) via `upload.resizeOptions`, applied once on upload.
|
|
34
|
+
* Off by default — the original is kept untouched. Set it only to bound storage. */
|
|
35
|
+
maxOriginalSize?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The image-pipeline additions, as a partial config to deep-merge onto a collection: the admin
|
|
39
|
+
* image-management UI (gated by `focalUI`), the purge hooks, `upload.focalPoint`, the on-demand
|
|
40
|
+
* admin thumbnail, and the lean `defaultPopulate` / `forceSelect` that keep the virtual URLs
|
|
41
|
+
* riding through select/populated reads. The factory below folds these into the default `images`
|
|
42
|
+
* collection; the plugin folds them onto an existing collection when `extendCollection` is set
|
|
43
|
+
* (so a project's own `media` collection gains the pipeline without a second collection).
|
|
44
|
+
* Deliberately omits `alt`/`access`/`admin`/`slug` and the mime whitelist so it never clobbers a
|
|
45
|
+
* collection it's merged onto (the plugin also re-merges the target's own
|
|
46
|
+
* `defaultPopulate`/`forceSelect`/thumbnail on top).
|
|
47
|
+
*/
|
|
48
|
+
export declare const imageEnhancements: (opts?: CreateImagesOptions) => Partial<CollectionConfig>;
|
|
49
|
+
/**
|
|
50
|
+
* The source image upload collection. Stores only the original (no pre-generated sizes by
|
|
51
|
+
* default); keeps Payload's built-in `focalPoint` (the focal component enhances it). The
|
|
52
|
+
* on-demand transform endpoint serves every rendered variant and records it in the
|
|
53
|
+
* generated-images collection, surfaced here via the `variants` join and purged by the
|
|
54
|
+
* change/delete hooks. The LQIP placeholder is generated on demand by `<ResponsiveImage>` (a tiny
|
|
55
|
+
* inline base64 via the shared variant cache) — there's no stored placeholder field.
|
|
56
|
+
*/
|
|
57
|
+
export declare const createImagesCollection: (opts?: CreateImagesOptions) => CollectionConfig;
|
|
58
|
+
/** The default Images collection (on-demand transforms, no pre-generated sizes). */
|
|
59
|
+
export declare const Images: CollectionConfig;
|
|
60
|
+
//# sourceMappingURL=images.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/collections/images.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA4C,MAAM,SAAS,CAAA;AAQzF,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,iDAAiD,CAAA;AACnF,eAAO,MAAM,sBAAsB,kDAAkD,CAAA;AAErF,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;oGACgG;IAChG,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC/B,6GAA6G;IAC7G,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;8GAC0G;IAC1G,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;4FACwF;IACxF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,6FAA6F;IAC7F,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;gGAE4F;IAC5F,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,iHAAiH;IACjH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;yFACqF;IACrF,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAuDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAM,mBAAwB,KAAG,OAAO,CAAC,gBAAgB,CA2B1F,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAM,mBAAwB,KAAG,gBA0CvE,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,MAAM,EAAE,gBAA2C,CAAA"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { anyone, authd } from "../access.js";
|
|
2
|
+
import { VIRTUAL_URL_FIELDS, VIRTUAL_URL_INPUTS, virtualUrlFields } from "../fields/virtualUrls.js";
|
|
3
|
+
import { purgeStaleVariantsAfterChange, purgeVariantsBeforeDelete } from "../hooks/purge.js";
|
|
4
|
+
import { IMAGE_MIME_TYPES } from "../transform/params.js";
|
|
5
|
+
import { GENERATED_IMAGES_SLUG } from "./generatedImages.js";
|
|
6
|
+
/** Admin component subpaths (referenced by the Payload import map). */ export const FocalPreviewFieldPath = '@pro-laico/payload-images/admin/focalPreview';
|
|
7
|
+
export const PurgeVariantsFieldPath = '@pro-laico/payload-images/admin/purgeVariants';
|
|
8
|
+
/**
|
|
9
|
+
* A focal-cropped admin thumbnail served by the transform endpoint, so the Images list view
|
|
10
|
+
* loads tiny WebP thumbnails instead of full-resolution originals. Returns `undefined` when
|
|
11
|
+
* disabled (`adminThumbnail: false`, or the transform endpoint isn't registered). The API base
|
|
12
|
+
* comes from the app's `routes.api` (threaded through as `apiRoute`).
|
|
13
|
+
*/ const resolveAdminThumbnail = (adminThumbnail, apiRoute = '/api')=>{
|
|
14
|
+
if (adminThumbnail === false) return undefined;
|
|
15
|
+
const w = typeof adminThumbnail === 'number' ? adminThumbnail : 160;
|
|
16
|
+
return ({ doc })=>doc?.id ? `${apiRoute}/img/${String(doc.id)}?w=${w}&h=${w}&fit=cover&fmt=auto` : null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The admin "image management" fields, shown only when `focalUI` is on: the focal-point picker,
|
|
20
|
+
* the ratio preview, the purge-variants button, and the `variants` join listing the cached
|
|
21
|
+
* variants (it pairs with the purge button). With `focalUI: false` the collection is a clean
|
|
22
|
+
* upload — just `alt` + the file — and the import map isn't needed.
|
|
23
|
+
*/ const adminUIFields = (focalUI, variantSlug, previewRatios, purgePath, endpoints = true)=>focalUI ? [
|
|
24
|
+
{
|
|
25
|
+
name: 'focalPreview',
|
|
26
|
+
type: 'ui',
|
|
27
|
+
admin: {
|
|
28
|
+
components: {
|
|
29
|
+
Field: {
|
|
30
|
+
path: FocalPreviewFieldPath,
|
|
31
|
+
...previewRatios ? {
|
|
32
|
+
clientProps: {
|
|
33
|
+
previewRatios
|
|
34
|
+
}
|
|
35
|
+
} : {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
// The purge button POSTs to the purge endpoint and the join lists endpoint-generated
|
|
41
|
+
// variants — with the endpoints unregistered, both would be dead UI.
|
|
42
|
+
...endpoints ? [
|
|
43
|
+
{
|
|
44
|
+
name: 'purgeVariants',
|
|
45
|
+
type: 'ui',
|
|
46
|
+
admin: {
|
|
47
|
+
components: {
|
|
48
|
+
Field: {
|
|
49
|
+
path: PurgeVariantsFieldPath,
|
|
50
|
+
...purgePath ? {
|
|
51
|
+
clientProps: {
|
|
52
|
+
purgePath
|
|
53
|
+
}
|
|
54
|
+
} : {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'variants',
|
|
61
|
+
type: 'join',
|
|
62
|
+
collection: variantSlug,
|
|
63
|
+
on: 'source',
|
|
64
|
+
admin: {
|
|
65
|
+
defaultColumns: [
|
|
66
|
+
'filename',
|
|
67
|
+
'width',
|
|
68
|
+
'height',
|
|
69
|
+
'format'
|
|
70
|
+
],
|
|
71
|
+
allowCreate: false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
] : []
|
|
75
|
+
] : [];
|
|
76
|
+
/**
|
|
77
|
+
* The image-pipeline additions, as a partial config to deep-merge onto a collection: the admin
|
|
78
|
+
* image-management UI (gated by `focalUI`), the purge hooks, `upload.focalPoint`, the on-demand
|
|
79
|
+
* admin thumbnail, and the lean `defaultPopulate` / `forceSelect` that keep the virtual URLs
|
|
80
|
+
* riding through select/populated reads. The factory below folds these into the default `images`
|
|
81
|
+
* collection; the plugin folds them onto an existing collection when `extendCollection` is set
|
|
82
|
+
* (so a project's own `media` collection gains the pipeline without a second collection).
|
|
83
|
+
* Deliberately omits `alt`/`access`/`admin`/`slug` and the mime whitelist so it never clobbers a
|
|
84
|
+
* collection it's merged onto (the plugin also re-merges the target's own
|
|
85
|
+
* `defaultPopulate`/`forceSelect`/thumbnail on top).
|
|
86
|
+
*/ export const imageEnhancements = (opts = {})=>{
|
|
87
|
+
const { focalUI = true, virtualFields = true, previewRatios, purgePath, folders, endpointsEnabled = true } = opts;
|
|
88
|
+
const variantSlug = opts.variantSlug || GENERATED_IMAGES_SLUG;
|
|
89
|
+
const adminThumbnail = resolveAdminThumbnail(opts.adminThumbnail, opts.apiRoute);
|
|
90
|
+
// Lean relationship population: when an image is referenced (e.g. `page.heroImage`), populate the
|
|
91
|
+
// renderable fields + the virtual URLs, and skip the `variants` join (which would run an extra
|
|
92
|
+
// query per populated image). `forceSelect` keeps the virtual fields' inputs present under `select`.
|
|
93
|
+
const renderableFields = {
|
|
94
|
+
alt: true,
|
|
95
|
+
url: true,
|
|
96
|
+
filename: true,
|
|
97
|
+
width: true,
|
|
98
|
+
height: true,
|
|
99
|
+
focalX: true,
|
|
100
|
+
focalY: true
|
|
101
|
+
};
|
|
102
|
+
const defaultPopulate = virtualFields ? {
|
|
103
|
+
...renderableFields,
|
|
104
|
+
...Object.fromEntries(VIRTUAL_URL_FIELDS.map((f)=>[
|
|
105
|
+
f,
|
|
106
|
+
true
|
|
107
|
+
]))
|
|
108
|
+
} : renderableFields;
|
|
109
|
+
const forceSelect = virtualFields ? Object.fromEntries(VIRTUAL_URL_INPUTS.map((f)=>[
|
|
110
|
+
f,
|
|
111
|
+
true
|
|
112
|
+
])) : undefined;
|
|
113
|
+
return {
|
|
114
|
+
// Admin UI is gated by focalUI; the virtual URL fields are for API consumers, so they're
|
|
115
|
+
// added independently (hidden in the admin).
|
|
116
|
+
fields: [
|
|
117
|
+
...adminUIFields(focalUI, variantSlug, previewRatios, purgePath, endpointsEnabled),
|
|
118
|
+
...virtualFields ? virtualUrlFields() : []
|
|
119
|
+
],
|
|
120
|
+
hooks: {
|
|
121
|
+
afterChange: [
|
|
122
|
+
purgeStaleVariantsAfterChange({
|
|
123
|
+
variantSlug
|
|
124
|
+
})
|
|
125
|
+
],
|
|
126
|
+
beforeDelete: [
|
|
127
|
+
purgeVariantsBeforeDelete({
|
|
128
|
+
variantSlug
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
defaultPopulate: defaultPopulate,
|
|
133
|
+
...forceSelect ? {
|
|
134
|
+
forceSelect: forceSelect
|
|
135
|
+
} : {},
|
|
136
|
+
upload: {
|
|
137
|
+
focalPoint: true,
|
|
138
|
+
...adminThumbnail ? {
|
|
139
|
+
adminThumbnail
|
|
140
|
+
} : {}
|
|
141
|
+
},
|
|
142
|
+
...folders ? {
|
|
143
|
+
folders: true
|
|
144
|
+
} : {}
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* The source image upload collection. Stores only the original (no pre-generated sizes by
|
|
149
|
+
* default); keeps Payload's built-in `focalPoint` (the focal component enhances it). The
|
|
150
|
+
* on-demand transform endpoint serves every rendered variant and records it in the
|
|
151
|
+
* generated-images collection, surfaced here via the `variants` join and purged by the
|
|
152
|
+
* change/delete hooks. The LQIP placeholder is generated on demand by `<ResponsiveImage>` (a tiny
|
|
153
|
+
* inline base64 via the shared variant cache) — there's no stored placeholder field.
|
|
154
|
+
*/ export const createImagesCollection = (opts = {})=>{
|
|
155
|
+
const { localizeAlt = false, folders, maxOriginalSize } = opts;
|
|
156
|
+
const enh = imageEnhancements(opts);
|
|
157
|
+
const adminThumbnail = enh.upload.adminThumbnail;
|
|
158
|
+
return {
|
|
159
|
+
slug: 'images',
|
|
160
|
+
access: {
|
|
161
|
+
create: authd,
|
|
162
|
+
delete: authd,
|
|
163
|
+
read: anyone,
|
|
164
|
+
update: authd
|
|
165
|
+
},
|
|
166
|
+
admin: {
|
|
167
|
+
group: 'Assets',
|
|
168
|
+
description: 'Upload images here. Display sizes are generated on demand and cached — store the original once, render any size.',
|
|
169
|
+
enableListViewSelectAPI: true,
|
|
170
|
+
useAsTitle: 'alt',
|
|
171
|
+
// `filename` first: the list view renders the (on-demand, adminThumbnail) preview on that cell.
|
|
172
|
+
defaultColumns: [
|
|
173
|
+
'filename',
|
|
174
|
+
'alt',
|
|
175
|
+
'updatedAt'
|
|
176
|
+
],
|
|
177
|
+
listSearchableFields: [
|
|
178
|
+
'alt',
|
|
179
|
+
'filename'
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
defaultPopulate: enh.defaultPopulate,
|
|
183
|
+
...enh.forceSelect ? {
|
|
184
|
+
forceSelect: enh.forceSelect
|
|
185
|
+
} : {},
|
|
186
|
+
...folders ? {
|
|
187
|
+
folders: true
|
|
188
|
+
} : {},
|
|
189
|
+
fields: [
|
|
190
|
+
{
|
|
191
|
+
name: 'alt',
|
|
192
|
+
type: 'text',
|
|
193
|
+
required: true,
|
|
194
|
+
localized: localizeAlt,
|
|
195
|
+
admin: {
|
|
196
|
+
description: 'Describe the image for screen readers and SEO.'
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
...enh.fields ?? []
|
|
200
|
+
],
|
|
201
|
+
hooks: enh.hooks,
|
|
202
|
+
upload: {
|
|
203
|
+
focalPoint: true,
|
|
204
|
+
displayPreview: true,
|
|
205
|
+
mimeTypes: opts.mimeTypes ?? IMAGE_MIME_TYPES,
|
|
206
|
+
...adminThumbnail ? {
|
|
207
|
+
adminThumbnail
|
|
208
|
+
} : {},
|
|
209
|
+
// Off by default — the stored original is kept untouched; only downsize when asked, to bound storage.
|
|
210
|
+
...maxOriginalSize ? {
|
|
211
|
+
resizeOptions: {
|
|
212
|
+
width: maxOriginalSize,
|
|
213
|
+
height: maxOriginalSize,
|
|
214
|
+
fit: 'inside',
|
|
215
|
+
withoutEnlargement: true
|
|
216
|
+
}
|
|
217
|
+
} : {}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
/** The default Images collection (on-demand transforms, no pre-generated sizes). */ export const Images = createImagesCollection();
|
|
222
|
+
|
|
223
|
+
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/images.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, Field, GetAdminThumbnail } from 'payload'\n\nimport { anyone, authd } from '../access'\nimport { VIRTUAL_URL_FIELDS, VIRTUAL_URL_INPUTS, virtualUrlFields } from '../fields/virtualUrls'\nimport { purgeStaleVariantsAfterChange, purgeVariantsBeforeDelete } from '../hooks/purge'\nimport { IMAGE_MIME_TYPES } from '../transform/params'\nimport { GENERATED_IMAGES_SLUG } from './generatedImages'\n\n/** Admin component subpaths (referenced by the Payload import map). */\nexport const FocalPreviewFieldPath = '@pro-laico/payload-images/admin/focalPreview'\nexport const PurgeVariantsFieldPath = '@pro-laico/payload-images/admin/purgeVariants'\n\nexport interface CreateImagesOptions {\n /** Render the focal-point + ratio-preview field and the purge-variants button. Default true. */\n focalUI?: boolean\n /** Aspect ratios shown in the focal preview tiles. */\n previewRatios?: string[]\n /** Slug of the generated-images collection the `variants` join targets. */\n variantSlug?: string\n /** Purge route (under the API base) the purge button POSTs to. Default `/img/purge`. */\n purgePath?: string\n /** Admin list/preview thumbnail width (px), served on-demand via the transform endpoint so the\n * admin never loads full-res originals. Default 160; pass `false` to use Payload's default. */\n adminThumbnail?: number | false\n /** The app's API route base (`config.routes.api`), used to build the admin thumbnail URL. Default `/api`. */\n apiRoute?: string\n /** Whether the transform + purge endpoints are registered (`transform !== false`). When false, the\n * purge button and the `variants` join — UI that targets those endpoints — are skipped. Default true. */\n endpointsEnabled?: boolean\n /** Add virtual `src`/`srcset`/`placeholderURL`/`thumbnailURL` fields (computed on read), so\n * optimized URLs ride along in every REST/GraphQL/Local-API response. Default true. */\n virtualFields?: boolean\n /** Mark the `alt` field `localized: true` (requires Payload localization). Default false. */\n localizeAlt?: boolean\n /** Accepted upload mime types. Defaults to the raster formats the transform pipeline can\n * process ({@link IMAGE_MIME_TYPES}: avif/webp/jpeg/png). Override to widen or narrow what\n * the collection accepts — but the endpoint only meaningfully transforms raster images. */\n mimeTypes?: string[]\n /** Enable Payload's native folder organization on the collection (adds a folder relationship). Default false. */\n folders?: boolean\n /** Cap the *stored* original's longest edge (px) via `upload.resizeOptions`, applied once on upload.\n * Off by default — the original is kept untouched. Set it only to bound storage. */\n maxOriginalSize?: number\n}\n\n/**\n * A focal-cropped admin thumbnail served by the transform endpoint, so the Images list view\n * loads tiny WebP thumbnails instead of full-resolution originals. Returns `undefined` when\n * disabled (`adminThumbnail: false`, or the transform endpoint isn't registered). The API base\n * comes from the app's `routes.api` (threaded through as `apiRoute`).\n */\nconst resolveAdminThumbnail = (adminThumbnail: number | false | undefined, apiRoute = '/api'): GetAdminThumbnail | undefined => {\n if (adminThumbnail === false) return undefined\n const w = typeof adminThumbnail === 'number' ? adminThumbnail : 160\n return ({ doc }) => (doc?.id ? `${apiRoute}/img/${String(doc.id)}?w=${w}&h=${w}&fit=cover&fmt=auto` : null)\n}\n\n/**\n * The admin \"image management\" fields, shown only when `focalUI` is on: the focal-point picker,\n * the ratio preview, the purge-variants button, and the `variants` join listing the cached\n * variants (it pairs with the purge button). With `focalUI: false` the collection is a clean\n * upload — just `alt` + the file — and the import map isn't needed.\n */\nconst adminUIFields = (\n focalUI: boolean,\n variantSlug: CollectionSlug,\n previewRatios?: string[],\n purgePath?: string,\n endpoints = true,\n): Field[] =>\n focalUI\n ? [\n {\n name: 'focalPreview',\n type: 'ui',\n admin: { components: { Field: { path: FocalPreviewFieldPath, ...(previewRatios ? { clientProps: { previewRatios } } : {}) } } },\n },\n // The purge button POSTs to the purge endpoint and the join lists endpoint-generated\n // variants — with the endpoints unregistered, both would be dead UI.\n ...(endpoints\n ? ([\n {\n name: 'purgeVariants',\n type: 'ui',\n admin: { components: { Field: { path: PurgeVariantsFieldPath, ...(purgePath ? { clientProps: { purgePath } } : {}) } } },\n },\n {\n name: 'variants',\n type: 'join',\n collection: variantSlug,\n on: 'source',\n admin: { defaultColumns: ['filename', 'width', 'height', 'format'], allowCreate: false },\n },\n ] as Field[])\n : []),\n ]\n : []\n\n/**\n * The image-pipeline additions, as a partial config to deep-merge onto a collection: the admin\n * image-management UI (gated by `focalUI`), the purge hooks, `upload.focalPoint`, the on-demand\n * admin thumbnail, and the lean `defaultPopulate` / `forceSelect` that keep the virtual URLs\n * riding through select/populated reads. The factory below folds these into the default `images`\n * collection; the plugin folds them onto an existing collection when `extendCollection` is set\n * (so a project's own `media` collection gains the pipeline without a second collection).\n * Deliberately omits `alt`/`access`/`admin`/`slug` and the mime whitelist so it never clobbers a\n * collection it's merged onto (the plugin also re-merges the target's own\n * `defaultPopulate`/`forceSelect`/thumbnail on top).\n */\nexport const imageEnhancements = (opts: CreateImagesOptions = {}): Partial<CollectionConfig> => {\n const { focalUI = true, virtualFields = true, previewRatios, purgePath, folders, endpointsEnabled = true } = opts\n const variantSlug = (opts.variantSlug || GENERATED_IMAGES_SLUG) as CollectionSlug\n const adminThumbnail = resolveAdminThumbnail(opts.adminThumbnail, opts.apiRoute)\n\n // Lean relationship population: when an image is referenced (e.g. `page.heroImage`), populate the\n // renderable fields + the virtual URLs, and skip the `variants` join (which would run an extra\n // query per populated image). `forceSelect` keeps the virtual fields' inputs present under `select`.\n const renderableFields = { alt: true, url: true, filename: true, width: true, height: true, focalX: true, focalY: true }\n const defaultPopulate = virtualFields\n ? { ...renderableFields, ...Object.fromEntries(VIRTUAL_URL_FIELDS.map((f) => [f, true])) }\n : renderableFields\n const forceSelect = virtualFields ? Object.fromEntries(VIRTUAL_URL_INPUTS.map((f) => [f, true])) : undefined\n\n return {\n // Admin UI is gated by focalUI; the virtual URL fields are for API consumers, so they're\n // added independently (hidden in the admin).\n fields: [...adminUIFields(focalUI, variantSlug, previewRatios, purgePath, endpointsEnabled), ...(virtualFields ? virtualUrlFields() : [])],\n hooks: {\n afterChange: [purgeStaleVariantsAfterChange({ variantSlug })],\n beforeDelete: [purgeVariantsBeforeDelete({ variantSlug })],\n },\n defaultPopulate: defaultPopulate as CollectionConfig['defaultPopulate'],\n ...(forceSelect ? { forceSelect: forceSelect as CollectionConfig['forceSelect'] } : {}),\n upload: { focalPoint: true, ...(adminThumbnail ? { adminThumbnail } : {}) },\n ...(folders ? { folders: true } : {}),\n }\n}\n\n/**\n * The source image upload collection. Stores only the original (no pre-generated sizes by\n * default); keeps Payload's built-in `focalPoint` (the focal component enhances it). The\n * on-demand transform endpoint serves every rendered variant and records it in the\n * generated-images collection, surfaced here via the `variants` join and purged by the\n * change/delete hooks. The LQIP placeholder is generated on demand by `<ResponsiveImage>` (a tiny\n * inline base64 via the shared variant cache) — there's no stored placeholder field.\n */\nexport const createImagesCollection = (opts: CreateImagesOptions = {}): CollectionConfig => {\n const { localizeAlt = false, folders, maxOriginalSize } = opts\n const enh = imageEnhancements(opts)\n const adminThumbnail = (enh.upload as { adminThumbnail?: GetAdminThumbnail }).adminThumbnail\n\n return {\n slug: 'images',\n access: { create: authd, delete: authd, read: anyone, update: authd },\n admin: {\n group: 'Assets',\n description: 'Upload images here. Display sizes are generated on demand and cached — store the original once, render any size.',\n enableListViewSelectAPI: true,\n useAsTitle: 'alt',\n // `filename` first: the list view renders the (on-demand, adminThumbnail) preview on that cell.\n defaultColumns: ['filename', 'alt', 'updatedAt'],\n listSearchableFields: ['alt', 'filename'],\n },\n defaultPopulate: enh.defaultPopulate,\n ...(enh.forceSelect ? { forceSelect: enh.forceSelect } : {}),\n ...(folders ? { folders: true } : {}),\n fields: [\n {\n name: 'alt',\n type: 'text',\n required: true,\n localized: localizeAlt,\n admin: { description: 'Describe the image for screen readers and SEO.' },\n },\n ...(enh.fields ?? []),\n ],\n hooks: enh.hooks,\n upload: {\n focalPoint: true,\n displayPreview: true, // show image thumbnails in upload/relationship fields that target this collection\n mimeTypes: opts.mimeTypes ?? IMAGE_MIME_TYPES,\n ...(adminThumbnail ? { adminThumbnail } : {}),\n // Off by default — the stored original is kept untouched; only downsize when asked, to bound storage.\n ...(maxOriginalSize\n ? { resizeOptions: { width: maxOriginalSize, height: maxOriginalSize, fit: 'inside', withoutEnlargement: true } }\n : {}),\n },\n }\n}\n\n/** The default Images collection (on-demand transforms, no pre-generated sizes). */\nexport const Images: CollectionConfig = createImagesCollection()\n"],"names":["anyone","authd","VIRTUAL_URL_FIELDS","VIRTUAL_URL_INPUTS","virtualUrlFields","purgeStaleVariantsAfterChange","purgeVariantsBeforeDelete","IMAGE_MIME_TYPES","GENERATED_IMAGES_SLUG","FocalPreviewFieldPath","PurgeVariantsFieldPath","resolveAdminThumbnail","adminThumbnail","apiRoute","undefined","w","doc","id","String","adminUIFields","focalUI","variantSlug","previewRatios","purgePath","endpoints","name","type","admin","components","Field","path","clientProps","collection","on","defaultColumns","allowCreate","imageEnhancements","opts","virtualFields","folders","endpointsEnabled","renderableFields","alt","url","filename","width","height","focalX","focalY","defaultPopulate","Object","fromEntries","map","f","forceSelect","fields","hooks","afterChange","beforeDelete","upload","focalPoint","createImagesCollection","localizeAlt","maxOriginalSize","enh","slug","access","create","delete","read","update","group","description","enableListViewSelectAPI","useAsTitle","listSearchableFields","required","localized","displayPreview","mimeTypes","resizeOptions","fit","withoutEnlargement","Images"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focalPreview.d.ts","sourceRoot":"","sources":["../../../src/components/admin/focalPreview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0B9B,UAAU,iBAAiB;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAID,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6IpD,CAAA;AAED,eAAe,YAAY,CAAA"}
|