@nonphoto/sanity-image 3.2.0 → 5.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/dist/index.d.ts +516 -68
- package/dist/index.js +278 -114
- package/package.json +5 -2
- package/src/asset.ts +120 -9
- package/src/constants.ts +7 -0
- package/src/crop.ts +14 -0
- package/src/hotspot.ts +14 -0
- package/src/image.ts +69 -0
- package/src/index.ts +6 -5
- package/src/rect.ts +28 -0
- package/src/transformations.ts +155 -0
- package/src/imageObject.ts +0 -15
- package/src/params.ts +0 -118
- package/src/reference.ts +0 -12
- package/src/stub.ts +0 -44
- package/src/url.ts +0 -76
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./asset";
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
2
|
+
export * from "./constants";
|
|
3
|
+
export * from "./crop";
|
|
4
|
+
export * from "./hotspot";
|
|
5
|
+
export * from "./image";
|
|
6
|
+
export * from "./rect";
|
|
7
|
+
export * from "./transformations";
|
package/src/rect.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InferOutput, is, number, object, tuple } from "valibot";
|
|
2
|
+
import { ImageAsset } from "./asset";
|
|
3
|
+
import { Crop } from "./crop";
|
|
4
|
+
|
|
5
|
+
export const rectSchema = object({
|
|
6
|
+
pos: tuple([number(), number()]),
|
|
7
|
+
size: tuple([number(), number()]),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type Rect = InferOutput<typeof rectSchema>;
|
|
11
|
+
|
|
12
|
+
export function isRect(input: unknown): input is Rect {
|
|
13
|
+
return is(rectSchema, input);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function rectFromCrop(
|
|
17
|
+
asset: Pick<ImageAsset, "width" | "height">,
|
|
18
|
+
crop: Crop,
|
|
19
|
+
): Rect {
|
|
20
|
+
const left = Math.max(crop.left ?? 0, 0);
|
|
21
|
+
const right = Math.max(crop.right ?? 0, 0);
|
|
22
|
+
const top = Math.max(crop.top ?? 0, 0);
|
|
23
|
+
const bottom = Math.max(crop.bottom ?? 0, 0);
|
|
24
|
+
return {
|
|
25
|
+
pos: [left * asset.width, top * asset.height],
|
|
26
|
+
size: [(1 - left - right) * asset.width, (1 - top - bottom) * asset.height],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
boolean,
|
|
3
|
+
InferOutput,
|
|
4
|
+
is,
|
|
5
|
+
literal,
|
|
6
|
+
number,
|
|
7
|
+
object,
|
|
8
|
+
partial,
|
|
9
|
+
string,
|
|
10
|
+
tuple,
|
|
11
|
+
union,
|
|
12
|
+
} from "valibot";
|
|
13
|
+
import { rectSchema } from "./rect";
|
|
14
|
+
|
|
15
|
+
export const transformationsSchema = partial(
|
|
16
|
+
object({
|
|
17
|
+
auto: literal("format"),
|
|
18
|
+
background: string(),
|
|
19
|
+
blur: number(),
|
|
20
|
+
crop: union([
|
|
21
|
+
literal("top"),
|
|
22
|
+
literal("bottom"),
|
|
23
|
+
literal("left"),
|
|
24
|
+
literal("right"),
|
|
25
|
+
literal("center"),
|
|
26
|
+
literal("focalpoint"),
|
|
27
|
+
literal("entropy"),
|
|
28
|
+
]),
|
|
29
|
+
download: union([string(), boolean()]),
|
|
30
|
+
dpr: union([literal(1), literal(2), literal(3)]),
|
|
31
|
+
fit: union([
|
|
32
|
+
literal("clip"),
|
|
33
|
+
literal("crop"),
|
|
34
|
+
literal("fill"),
|
|
35
|
+
literal("fillmax"),
|
|
36
|
+
literal("max"),
|
|
37
|
+
literal("scale"),
|
|
38
|
+
literal("min"),
|
|
39
|
+
]),
|
|
40
|
+
flipHorizontal: boolean(),
|
|
41
|
+
flipVertical: boolean(),
|
|
42
|
+
focalPoint: tuple([number(), number()]),
|
|
43
|
+
format: union([
|
|
44
|
+
literal("jpg"),
|
|
45
|
+
literal("pjpg"),
|
|
46
|
+
literal("png"),
|
|
47
|
+
literal("webp"),
|
|
48
|
+
]),
|
|
49
|
+
frame: number(),
|
|
50
|
+
height: number(),
|
|
51
|
+
invert: boolean(),
|
|
52
|
+
maxHeight: number(),
|
|
53
|
+
maxWidth: number(),
|
|
54
|
+
minHeight: number(),
|
|
55
|
+
minWidth: number(),
|
|
56
|
+
orientation: union([literal(0), literal(90), literal(180), literal(270)]),
|
|
57
|
+
pad: number(),
|
|
58
|
+
quality: number(),
|
|
59
|
+
rect: rectSchema,
|
|
60
|
+
saturation: number(),
|
|
61
|
+
sharpen: number(),
|
|
62
|
+
width: number(),
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export type Transformations = InferOutput<typeof transformationsSchema>;
|
|
67
|
+
|
|
68
|
+
export function isTransformations(input: unknown): input is Transformations {
|
|
69
|
+
return is(transformationsSchema, input);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function entry(
|
|
73
|
+
key: string,
|
|
74
|
+
value: string | number | boolean | null | undefined,
|
|
75
|
+
): [string, string] | undefined {
|
|
76
|
+
return value == null || value === false
|
|
77
|
+
? undefined
|
|
78
|
+
: [key, String(typeof value === "number" ? Math.round(value) : value)];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function transformationsToURLSearch({
|
|
82
|
+
auto,
|
|
83
|
+
background,
|
|
84
|
+
blur,
|
|
85
|
+
crop,
|
|
86
|
+
download,
|
|
87
|
+
dpr,
|
|
88
|
+
fit,
|
|
89
|
+
flipHorizontal,
|
|
90
|
+
flipVertical,
|
|
91
|
+
focalPoint,
|
|
92
|
+
format,
|
|
93
|
+
frame,
|
|
94
|
+
height,
|
|
95
|
+
invert,
|
|
96
|
+
maxHeight,
|
|
97
|
+
maxWidth,
|
|
98
|
+
minHeight,
|
|
99
|
+
minWidth,
|
|
100
|
+
orientation,
|
|
101
|
+
pad,
|
|
102
|
+
quality,
|
|
103
|
+
rect,
|
|
104
|
+
saturation,
|
|
105
|
+
sharpen,
|
|
106
|
+
width,
|
|
107
|
+
}: Transformations): string {
|
|
108
|
+
return (
|
|
109
|
+
"?" +
|
|
110
|
+
[
|
|
111
|
+
entry("auto", auto),
|
|
112
|
+
entry("bg", background),
|
|
113
|
+
entry("blur", blur),
|
|
114
|
+
entry("crop", crop),
|
|
115
|
+
entry("dl", download),
|
|
116
|
+
entry("dpr", dpr),
|
|
117
|
+
entry("fit", fit),
|
|
118
|
+
entry(
|
|
119
|
+
"flip",
|
|
120
|
+
flipHorizontal || flipVertical
|
|
121
|
+
? [flipHorizontal && "h", flipVertical && "v"]
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.join("")
|
|
124
|
+
: undefined,
|
|
125
|
+
),
|
|
126
|
+
entry("fm", format),
|
|
127
|
+
entry("fp-x", focalPoint?.[0]),
|
|
128
|
+
entry("fp-y", focalPoint?.[1]),
|
|
129
|
+
entry("frame", frame),
|
|
130
|
+
entry("h", height),
|
|
131
|
+
entry("invert", invert),
|
|
132
|
+
entry("max-h", maxHeight),
|
|
133
|
+
entry("max-w", maxWidth),
|
|
134
|
+
entry("min-h", minHeight),
|
|
135
|
+
entry("min-w", minWidth),
|
|
136
|
+
entry("or", orientation),
|
|
137
|
+
entry("pad", pad),
|
|
138
|
+
entry("q", quality),
|
|
139
|
+
entry(
|
|
140
|
+
"rect",
|
|
141
|
+
rect
|
|
142
|
+
? [rect.pos[0], rect.pos[1], rect.size[0], rect.size[1]]
|
|
143
|
+
.map(Math.round)
|
|
144
|
+
.join(",")
|
|
145
|
+
: undefined,
|
|
146
|
+
),
|
|
147
|
+
entry("sat", saturation),
|
|
148
|
+
entry("sharp", sharpen),
|
|
149
|
+
entry("w", width),
|
|
150
|
+
]
|
|
151
|
+
.filter((entry) => entry != null)
|
|
152
|
+
.map((entry) => entry.join("="))
|
|
153
|
+
.join("&")
|
|
154
|
+
);
|
|
155
|
+
}
|
package/src/imageObject.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { isSanityImageAssetLike, SanityImageAssetLike } from "./asset";
|
|
2
|
-
import { isSanityReference, SanityReference } from "./reference";
|
|
3
|
-
|
|
4
|
-
export interface SanityImageObject {
|
|
5
|
-
asset: SanityImageAssetLike | SanityReference;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function isSanityImageObject(x: any): x is SanityImageObject {
|
|
9
|
-
return (
|
|
10
|
-
x != null &&
|
|
11
|
-
typeof x === "object" &&
|
|
12
|
-
"asset" in x &&
|
|
13
|
-
(isSanityImageAssetLike(x.asset) || isSanityReference(x.asset))
|
|
14
|
-
);
|
|
15
|
-
}
|
package/src/params.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
export type ImageFormat = "jpg" | "pjpg" | "png" | "webp";
|
|
2
|
-
|
|
3
|
-
export type FitMode =
|
|
4
|
-
| "clip"
|
|
5
|
-
| "crop"
|
|
6
|
-
| "fill"
|
|
7
|
-
| "fillmax"
|
|
8
|
-
| "max"
|
|
9
|
-
| "scale"
|
|
10
|
-
| "min";
|
|
11
|
-
|
|
12
|
-
export type CropMode =
|
|
13
|
-
| "top"
|
|
14
|
-
| "bottom"
|
|
15
|
-
| "left"
|
|
16
|
-
| "right"
|
|
17
|
-
| "center"
|
|
18
|
-
| "focalpoint"
|
|
19
|
-
| "entropy";
|
|
20
|
-
|
|
21
|
-
export type AutoMode = "format";
|
|
22
|
-
|
|
23
|
-
export type Orientation = 0 | 90 | 180 | 270;
|
|
24
|
-
|
|
25
|
-
export type Dpr = 1 | 2 | 3;
|
|
26
|
-
|
|
27
|
-
export type SanityImageParams = {
|
|
28
|
-
auto?: AutoMode;
|
|
29
|
-
background?: string;
|
|
30
|
-
blur?: number;
|
|
31
|
-
crop?: CropMode;
|
|
32
|
-
download?: boolean | string;
|
|
33
|
-
dpr?: Dpr;
|
|
34
|
-
fit?: FitMode;
|
|
35
|
-
flipHorizontal?: boolean;
|
|
36
|
-
flipVertical?: boolean;
|
|
37
|
-
focalPoint?: { x: number; y: number };
|
|
38
|
-
format?: ImageFormat;
|
|
39
|
-
frame?: number;
|
|
40
|
-
height?: number;
|
|
41
|
-
invert?: boolean;
|
|
42
|
-
maxHeight?: number;
|
|
43
|
-
maxWidth?: number;
|
|
44
|
-
minHeight?: number;
|
|
45
|
-
minWidth?: number;
|
|
46
|
-
orientation?: Orientation;
|
|
47
|
-
pad?: number;
|
|
48
|
-
quality?: number;
|
|
49
|
-
rect?: { left: number; top: number; width: number; height: number };
|
|
50
|
-
saturation?: number;
|
|
51
|
-
sharpen?: number;
|
|
52
|
-
width?: number;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export function sanityImageParamsToSearchParamEntries({
|
|
56
|
-
auto,
|
|
57
|
-
background,
|
|
58
|
-
blur,
|
|
59
|
-
crop,
|
|
60
|
-
download,
|
|
61
|
-
dpr,
|
|
62
|
-
fit,
|
|
63
|
-
flipHorizontal,
|
|
64
|
-
flipVertical,
|
|
65
|
-
focalPoint,
|
|
66
|
-
format,
|
|
67
|
-
frame,
|
|
68
|
-
height,
|
|
69
|
-
invert,
|
|
70
|
-
maxHeight,
|
|
71
|
-
maxWidth,
|
|
72
|
-
minHeight,
|
|
73
|
-
minWidth,
|
|
74
|
-
orientation,
|
|
75
|
-
pad,
|
|
76
|
-
quality,
|
|
77
|
-
rect,
|
|
78
|
-
saturation,
|
|
79
|
-
sharpen,
|
|
80
|
-
width,
|
|
81
|
-
}: SanityImageParams): string[][] {
|
|
82
|
-
return Object.entries({
|
|
83
|
-
auto,
|
|
84
|
-
bg: background,
|
|
85
|
-
blur,
|
|
86
|
-
crop,
|
|
87
|
-
dl: download,
|
|
88
|
-
dpr,
|
|
89
|
-
fit,
|
|
90
|
-
flip: [flipHorizontal && "h", flipVertical && "v"].filter(Boolean).join(""),
|
|
91
|
-
fm: format,
|
|
92
|
-
"fp-x": focalPoint?.x,
|
|
93
|
-
"fp-y": focalPoint?.y,
|
|
94
|
-
frame,
|
|
95
|
-
h: height,
|
|
96
|
-
invert,
|
|
97
|
-
"max-h": maxHeight,
|
|
98
|
-
"max-w": maxWidth,
|
|
99
|
-
"min-h": minHeight,
|
|
100
|
-
"min-w": minWidth,
|
|
101
|
-
or: orientation,
|
|
102
|
-
pad,
|
|
103
|
-
q: quality,
|
|
104
|
-
rect: rect
|
|
105
|
-
? [rect.left, rect.top, rect.width, rect.height].map(Math.round).join(",")
|
|
106
|
-
: undefined,
|
|
107
|
-
sat: saturation,
|
|
108
|
-
sharp: sharpen,
|
|
109
|
-
w: width,
|
|
110
|
-
})
|
|
111
|
-
.filter(([, value]) => typeof value === "number" || Boolean(value))
|
|
112
|
-
.map(([key, value]) => [
|
|
113
|
-
key,
|
|
114
|
-
encodeURIComponent(
|
|
115
|
-
typeof value === "number" ? Math.round(value) : (value as string)
|
|
116
|
-
),
|
|
117
|
-
]);
|
|
118
|
-
}
|
package/src/reference.ts
DELETED
package/src/stub.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { isSanityImageAssetLike, SanityImageAssetLike } from "./asset";
|
|
2
|
-
import { SanityImageObject } from "./imageObject";
|
|
3
|
-
import { isSanityReference, SanityReference } from "./reference";
|
|
4
|
-
|
|
5
|
-
export type SanityImageSource =
|
|
6
|
-
| SanityImageObject
|
|
7
|
-
| SanityReference
|
|
8
|
-
| SanityImageAssetLike
|
|
9
|
-
| string;
|
|
10
|
-
|
|
11
|
-
export interface SanityImageAssetStub {
|
|
12
|
-
id: string;
|
|
13
|
-
width: number;
|
|
14
|
-
height: number;
|
|
15
|
-
format: string;
|
|
16
|
-
vanityName?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function parseSanityImageAssetId(
|
|
20
|
-
assetId: string
|
|
21
|
-
): SanityImageAssetStub | undefined {
|
|
22
|
-
const matches = assetId.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
|
|
23
|
-
if (matches) {
|
|
24
|
-
const [, id, width, height, format] = matches;
|
|
25
|
-
return { id, width: Number(width), height: Number(height), format };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function sanityImageAssetId(source: SanityImageSource): string {
|
|
30
|
-
return typeof source === "string"
|
|
31
|
-
? source
|
|
32
|
-
: isSanityReference(source)
|
|
33
|
-
? source._ref
|
|
34
|
-
: isSanityImageAssetLike(source)
|
|
35
|
-
? source._id
|
|
36
|
-
: sanityImageAssetId(source.asset);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function sanityImageAssetStub(
|
|
40
|
-
source: SanityImageSource
|
|
41
|
-
): SanityImageAssetStub | undefined {
|
|
42
|
-
const id = sanityImageAssetId(source);
|
|
43
|
-
return id ? parseSanityImageAssetId(id) : undefined;
|
|
44
|
-
}
|
package/src/url.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SanityImageParams,
|
|
3
|
-
sanityImageParamsToSearchParamEntries,
|
|
4
|
-
} from "./params";
|
|
5
|
-
import { SanityImageAssetStub } from "./stub";
|
|
6
|
-
|
|
7
|
-
export const defaultSrcsetWidths = [
|
|
8
|
-
6016, // 6K
|
|
9
|
-
5120, // 5K
|
|
10
|
-
4480, // 4.5K
|
|
11
|
-
3840, // 4K
|
|
12
|
-
3200, // QHD+
|
|
13
|
-
2560, // WQXGA
|
|
14
|
-
2048, // QXGA
|
|
15
|
-
1920, // 1080p
|
|
16
|
-
1668, // iPad
|
|
17
|
-
1280, // 720p
|
|
18
|
-
1080, // iPhone 6-8 Plus
|
|
19
|
-
960,
|
|
20
|
-
720, // iPhone 6-8
|
|
21
|
-
640, // 480p
|
|
22
|
-
480,
|
|
23
|
-
360,
|
|
24
|
-
240,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
export interface SanityClientLike {
|
|
28
|
-
projectId: string;
|
|
29
|
-
dataset: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function sanityImageUrl(
|
|
33
|
-
client: SanityClientLike,
|
|
34
|
-
image: SanityImageAssetStub,
|
|
35
|
-
params?: SanityImageParams
|
|
36
|
-
): string {
|
|
37
|
-
const url = new URL(
|
|
38
|
-
[
|
|
39
|
-
`https://cdn.sanity.io/images`,
|
|
40
|
-
client.projectId,
|
|
41
|
-
client.dataset,
|
|
42
|
-
`${image.id}-${image.width}x${image.height}.${image.format}`,
|
|
43
|
-
image.vanityName,
|
|
44
|
-
]
|
|
45
|
-
.filter(Boolean)
|
|
46
|
-
.join("/")
|
|
47
|
-
);
|
|
48
|
-
if (params) {
|
|
49
|
-
url.search = new URLSearchParams(
|
|
50
|
-
sanityImageParamsToSearchParamEntries(params)
|
|
51
|
-
).toString();
|
|
52
|
-
}
|
|
53
|
-
return url.href;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function sanityImageSrcset(
|
|
57
|
-
client: SanityClientLike,
|
|
58
|
-
image: SanityImageAssetStub,
|
|
59
|
-
params?: Omit<SanityImageParams, "width">,
|
|
60
|
-
widths: number[] = defaultSrcsetWidths
|
|
61
|
-
): string {
|
|
62
|
-
const aspectRatio = image.height / image.width;
|
|
63
|
-
return [
|
|
64
|
-
...widths.sort((a, b) => a - b).filter((width) => width < image.width),
|
|
65
|
-
image.width,
|
|
66
|
-
]
|
|
67
|
-
.map((width) => {
|
|
68
|
-
const url = sanityImageUrl(client, image, {
|
|
69
|
-
...params,
|
|
70
|
-
width,
|
|
71
|
-
height: width * aspectRatio,
|
|
72
|
-
});
|
|
73
|
-
return `${url} ${width}w`;
|
|
74
|
-
})
|
|
75
|
-
.join(",");
|
|
76
|
-
}
|