@teamnovu/nuxt-image 0.5.4 → 1.0.0-beta.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 +229 -24
- package/dist/module.d.mts +70 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +67 -0
- package/dist/runtime/components/NovuBunnyImage.d.vue.ts +11 -0
- package/dist/runtime/components/NovuBunnyImage.vue +67 -0
- package/dist/runtime/components/NovuBunnyImage.vue.d.ts +11 -0
- package/dist/runtime/components/NovuCloudinaryImage.d.vue.ts +19 -0
- package/dist/runtime/components/NovuCloudinaryImage.vue +79 -0
- package/dist/runtime/components/NovuCloudinaryImage.vue.d.ts +19 -0
- package/dist/runtime/components/NovuImage.d.vue.ts +28 -0
- package/dist/runtime/components/NovuImage.vue +90 -0
- package/dist/runtime/components/NovuImage.vue.d.ts +28 -0
- package/dist/runtime/components/NovuImgproxyImage.d.vue.ts +12 -0
- package/dist/runtime/components/NovuImgproxyImage.vue +68 -0
- package/dist/runtime/components/NovuImgproxyImage.vue.d.ts +12 -0
- package/dist/runtime/components/NovuStatamicImage.d.vue.ts +17 -0
- package/dist/runtime/components/NovuStatamicImage.vue +87 -0
- package/dist/runtime/components/NovuStatamicImage.vue.d.ts +17 -0
- package/dist/runtime/composables/useResponsiveImage.d.ts +42 -0
- package/dist/runtime/composables/useResponsiveImage.js +115 -0
- package/dist/runtime/providers/imgproxy.d.ts +68 -0
- package/dist/runtime/providers/imgproxy.js +161 -0
- package/dist/runtime/types.d.ts +56 -0
- package/dist/runtime/types.js +0 -0
- package/dist/runtime/utils/focal.d.ts +11 -0
- package/dist/runtime/utils/focal.js +33 -0
- package/dist/runtime/utils/i18n.d.ts +1 -0
- package/dist/runtime/utils/i18n.js +10 -0
- package/dist/runtime/utils/numbers.d.ts +1 -0
- package/dist/runtime/utils/numbers.js +6 -0
- package/dist/runtime/utils/providerModifiers.d.ts +4 -0
- package/dist/runtime/utils/providerModifiers.js +36 -0
- package/dist/runtime/utils/screens.d.ts +5 -0
- package/dist/runtime/utils/screens.js +19 -0
- package/dist/runtime/utils/statamic.d.ts +3 -0
- package/dist/runtime/utils/statamic.js +143 -0
- package/dist/types.d.mts +3 -0
- package/package.json +65 -68
- package/CHANGELOG.md +0 -371
- package/LICENSE +0 -21
- package/dist/module.js +0 -482
- package/dist/runtime/components/image.mixin.d.ts +0 -46
- package/dist/runtime/components/image.mixin.js +0 -58
- package/dist/runtime/components/nuxt-img.vue +0 -49
- package/dist/runtime/components/nuxt-img.vue.d.ts +0 -12
- package/dist/runtime/components/nuxt-picture.vue +0 -86
- package/dist/runtime/components/nuxt-picture.vue.d.ts +0 -15
- package/dist/runtime/image.d.ts +0 -2
- package/dist/runtime/image.js +0 -194
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/index.js +0 -2
- package/dist/runtime/ipx.d.ts +0 -3
- package/dist/runtime/ipx.js +0 -3
- package/dist/runtime/plugin.d.ts +0 -1
- package/dist/runtime/plugin.js +0 -31
- package/dist/runtime/providers/cloudinary.d.ts +0 -2
- package/dist/runtime/providers/cloudinary.js +0 -96
- package/dist/runtime/providers/fastly.d.ts +0 -2
- package/dist/runtime/providers/fastly.js +0 -21
- package/dist/runtime/providers/glide.d.ts +0 -2
- package/dist/runtime/providers/glide.js +0 -49
- package/dist/runtime/providers/imagekit.d.ts +0 -2
- package/dist/runtime/providers/imagekit.js +0 -172
- package/dist/runtime/providers/imgix.d.ts +0 -3
- package/dist/runtime/providers/imgix.js +0 -153
- package/dist/runtime/providers/ipx.d.ts +0 -4
- package/dist/runtime/providers/ipx.js +0 -28
- package/dist/runtime/providers/netlify.d.ts +0 -3
- package/dist/runtime/providers/netlify.js +0 -40
- package/dist/runtime/providers/prismic.d.ts +0 -2
- package/dist/runtime/providers/prismic.js +0 -10
- package/dist/runtime/providers/sanity.d.ts +0 -2
- package/dist/runtime/providers/sanity.js +0 -87
- package/dist/runtime/providers/static.d.ts +0 -3
- package/dist/runtime/providers/static.js +0 -6
- package/dist/runtime/providers/storyblok.d.ts +0 -2
- package/dist/runtime/providers/storyblok.js +0 -27
- package/dist/runtime/providers/twicpics.d.ts +0 -2
- package/dist/runtime/providers/twicpics.js +0 -58
- package/dist/runtime/providers/vercel.d.ts +0 -3
- package/dist/runtime/providers/vercel.js +0 -28
- package/dist/runtime/utils/index.d.ts +0 -17
- package/dist/runtime/utils/index.js +0 -72
- package/dist/runtime/utils/meta.d.ts +0 -2
- package/dist/runtime/utils/meta.js +0 -67
- package/dist/runtime/utils/static-map.d.ts +0 -2
- package/dist/runtime/utils/static-map.js +0 -20
- package/dist/types.d.ts +0 -172
- package/vetur/attributes.json +0 -32
- package/vetur/tags.json +0 -32
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, useTemplateRef } from "vue";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
import { useResponsiveImage } from "../composables/useResponsiveImage";
|
|
5
|
+
import { toFiniteNumber } from "../utils/numbers";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
src: { type: String, required: true },
|
|
8
|
+
alt: { type: String, required: false, default: void 0 },
|
|
9
|
+
width: { type: [Number, String], required: false, default: void 0 },
|
|
10
|
+
height: { type: [Number, String], required: false, default: void 0 },
|
|
11
|
+
sourceWidth: { type: [Number, String], required: false, default: void 0 },
|
|
12
|
+
sourceHeight: { type: [Number, String], required: false, default: void 0 },
|
|
13
|
+
aspectRatio: { type: Number, required: false, default: void 0 },
|
|
14
|
+
quality: { type: [Number, String], required: false, default: void 0 },
|
|
15
|
+
format: { type: String, required: false, default: void 0 },
|
|
16
|
+
fit: { type: String, required: false, default: void 0 },
|
|
17
|
+
focal: { type: [String, Array], required: false, default: void 0 },
|
|
18
|
+
modifiers: { type: Object, required: false, default: void 0 },
|
|
19
|
+
provider: { type: String, required: false, default: void 0 },
|
|
20
|
+
sizes: { type: String, required: false, default: void 0 },
|
|
21
|
+
loading: { type: String, required: false, default: "lazy" },
|
|
22
|
+
decoding: { type: String, required: false, default: "async" },
|
|
23
|
+
priority: { type: Boolean, required: false, default: false },
|
|
24
|
+
fallbackWidth: { type: Number, required: false, default: void 0 },
|
|
25
|
+
usePlaceholder: { type: Boolean, required: false, default: void 0 },
|
|
26
|
+
placeholderQuality: { type: Number, required: false, default: void 0 },
|
|
27
|
+
placeholderWidth: { type: Number, required: false, default: void 0 },
|
|
28
|
+
placeholderSrc: { type: String, required: false, default: void 0 }
|
|
29
|
+
});
|
|
30
|
+
const runtimeConfig = useRuntimeConfig().public.novuImage;
|
|
31
|
+
const effectiveProvider = computed(() => props.provider ?? runtimeConfig.provider);
|
|
32
|
+
const effectiveQuality = computed(() => props.quality ?? runtimeConfig.defaultQuality);
|
|
33
|
+
const effectiveFormat = computed(() => props.format ?? runtimeConfig.defaultFormat);
|
|
34
|
+
const effectiveFallbackWidth = computed(() => props.fallbackWidth ?? runtimeConfig.fallbackWidth);
|
|
35
|
+
const effectiveUsePlaceholder = computed(() => props.usePlaceholder ?? runtimeConfig.usePlaceholder);
|
|
36
|
+
const effectivePlaceholderQuality = computed(() => props.placeholderQuality ?? runtimeConfig.placeholderQuality);
|
|
37
|
+
const effectivePlaceholderWidth = computed(() => props.placeholderWidth ?? runtimeConfig.placeholderWidth);
|
|
38
|
+
const imgRef = useTemplateRef("imgRef");
|
|
39
|
+
const { srcset, sizes, fallbackUrl } = useResponsiveImage({
|
|
40
|
+
src: () => props.src,
|
|
41
|
+
width: () => props.width,
|
|
42
|
+
height: () => props.height,
|
|
43
|
+
sourceWidth: () => props.sourceWidth,
|
|
44
|
+
sourceHeight: () => props.sourceHeight,
|
|
45
|
+
aspectRatio: () => props.aspectRatio,
|
|
46
|
+
quality: effectiveQuality,
|
|
47
|
+
format: effectiveFormat,
|
|
48
|
+
fit: () => props.fit,
|
|
49
|
+
modifiers: () => props.modifiers,
|
|
50
|
+
provider: effectiveProvider,
|
|
51
|
+
focal: () => props.focal,
|
|
52
|
+
sizes: () => props.sizes,
|
|
53
|
+
fallbackWidth: effectiveFallbackWidth,
|
|
54
|
+
usePlaceholder: effectiveUsePlaceholder,
|
|
55
|
+
placeholderQuality: effectivePlaceholderQuality,
|
|
56
|
+
placeholderWidth: effectivePlaceholderWidth,
|
|
57
|
+
placeholderSrc: () => props.placeholderSrc,
|
|
58
|
+
screens: () => runtimeConfig.screens,
|
|
59
|
+
imgRef
|
|
60
|
+
});
|
|
61
|
+
const aspectRatioStyle = computed(() => {
|
|
62
|
+
const explicit = props.aspectRatio;
|
|
63
|
+
if (explicit && Number.isFinite(explicit) && explicit > 0) {
|
|
64
|
+
return { aspectRatio: String(explicit) };
|
|
65
|
+
}
|
|
66
|
+
const w = toFiniteNumber(props.width);
|
|
67
|
+
const h = toFiniteNumber(props.height);
|
|
68
|
+
if (w && h) return { aspectRatio: `${w} / ${h}` };
|
|
69
|
+
return void 0;
|
|
70
|
+
});
|
|
71
|
+
const effectiveLoading = computed(() => props.priority ? "eager" : props.loading);
|
|
72
|
+
const fetchPriority = computed(() => props.priority ? "high" : "auto");
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<img
|
|
77
|
+
v-if="src"
|
|
78
|
+
ref="imgRef"
|
|
79
|
+
:src="fallbackUrl"
|
|
80
|
+
:srcset="srcset"
|
|
81
|
+
:sizes="sizes"
|
|
82
|
+
:alt="alt ?? ''"
|
|
83
|
+
:width="width"
|
|
84
|
+
:height="height"
|
|
85
|
+
:loading="effectiveLoading"
|
|
86
|
+
:decoding="decoding"
|
|
87
|
+
:fetchpriority="fetchPriority"
|
|
88
|
+
:style="aspectRatioStyle"
|
|
89
|
+
>
|
|
90
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FocalPoint } from '../utils/focal.js';
|
|
2
|
+
export interface NovuImageProps {
|
|
3
|
+
src: string;
|
|
4
|
+
alt?: string;
|
|
5
|
+
width?: number | string;
|
|
6
|
+
height?: number | string;
|
|
7
|
+
sourceWidth?: number | string;
|
|
8
|
+
sourceHeight?: number | string;
|
|
9
|
+
aspectRatio?: number;
|
|
10
|
+
quality?: number | string;
|
|
11
|
+
format?: string;
|
|
12
|
+
fit?: string;
|
|
13
|
+
focal?: FocalPoint;
|
|
14
|
+
modifiers?: Record<string, unknown>;
|
|
15
|
+
provider?: string;
|
|
16
|
+
sizes?: string;
|
|
17
|
+
loading?: 'lazy' | 'eager';
|
|
18
|
+
decoding?: 'async' | 'sync' | 'auto';
|
|
19
|
+
priority?: boolean;
|
|
20
|
+
fallbackWidth?: number;
|
|
21
|
+
usePlaceholder?: boolean;
|
|
22
|
+
placeholderQuality?: number;
|
|
23
|
+
placeholderWidth?: number;
|
|
24
|
+
placeholderSrc?: string;
|
|
25
|
+
}
|
|
26
|
+
declare const __VLS_export: import("vue").DefineComponent<NovuImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NovuImageProps } from './NovuImage.vue.js';
|
|
2
|
+
import type { ImgproxyResizingType } from '../providers/imgproxy.js';
|
|
3
|
+
export interface NovuImgproxyImageProps extends Omit<NovuImageProps, 'provider'> {
|
|
4
|
+
preset?: string;
|
|
5
|
+
/** Raw imgproxy gravity string (e.g. `'sm'`, `'ce'`, `'fp:0.5:0.5'`). Overrides `focal`. */
|
|
6
|
+
gravity?: string;
|
|
7
|
+
resizingType?: ImgproxyResizingType;
|
|
8
|
+
extend?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<NovuImgproxyImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuImgproxyImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import NovuImage from "./NovuImage.vue";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
preset: { type: String, required: false, default: void 0 },
|
|
6
|
+
gravity: { type: String, required: false, default: void 0 },
|
|
7
|
+
resizingType: { type: String, required: false, default: void 0 },
|
|
8
|
+
extend: { type: Boolean, required: false, default: void 0 },
|
|
9
|
+
src: { type: String, required: true },
|
|
10
|
+
alt: { type: String, required: false },
|
|
11
|
+
width: { type: [Number, String], required: false },
|
|
12
|
+
height: { type: [Number, String], required: false },
|
|
13
|
+
sourceWidth: { type: [Number, String], required: false },
|
|
14
|
+
sourceHeight: { type: [Number, String], required: false },
|
|
15
|
+
aspectRatio: { type: Number, required: false },
|
|
16
|
+
quality: { type: [Number, String], required: false },
|
|
17
|
+
format: { type: String, required: false },
|
|
18
|
+
fit: { type: String, required: false },
|
|
19
|
+
focal: { type: [String, Array], required: false },
|
|
20
|
+
modifiers: { type: Object, required: false },
|
|
21
|
+
sizes: { type: String, required: false },
|
|
22
|
+
loading: { type: String, required: false },
|
|
23
|
+
decoding: { type: String, required: false },
|
|
24
|
+
priority: { type: Boolean, required: false },
|
|
25
|
+
fallbackWidth: { type: Number, required: false },
|
|
26
|
+
usePlaceholder: { type: Boolean, required: false },
|
|
27
|
+
placeholderQuality: { type: Number, required: false },
|
|
28
|
+
placeholderWidth: { type: Number, required: false },
|
|
29
|
+
placeholderSrc: { type: String, required: false }
|
|
30
|
+
});
|
|
31
|
+
const imgproxyModifiers = computed(() => {
|
|
32
|
+
const out = { ...props.modifiers ?? {} };
|
|
33
|
+
if (props.preset !== void 0) out.preset = props.preset;
|
|
34
|
+
if (props.gravity !== void 0) out.gravity = props.gravity;
|
|
35
|
+
if (props.resizingType !== void 0) out.resizingType = props.resizingType;
|
|
36
|
+
if (props.extend !== void 0) out.extend = props.extend;
|
|
37
|
+
return out;
|
|
38
|
+
});
|
|
39
|
+
const effectiveFocal = computed(() => props.gravity !== void 0 ? void 0 : props.focal);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<NovuImage
|
|
44
|
+
v-bind="$attrs"
|
|
45
|
+
:src="src"
|
|
46
|
+
:alt="alt"
|
|
47
|
+
:width="width"
|
|
48
|
+
:height="height"
|
|
49
|
+
:source-width="sourceWidth"
|
|
50
|
+
:source-height="sourceHeight"
|
|
51
|
+
:aspect-ratio="aspectRatio"
|
|
52
|
+
:quality="quality"
|
|
53
|
+
:format="format"
|
|
54
|
+
:fit="fit"
|
|
55
|
+
:focal="effectiveFocal"
|
|
56
|
+
:modifiers="imgproxyModifiers"
|
|
57
|
+
:sizes="sizes"
|
|
58
|
+
:loading="loading"
|
|
59
|
+
:decoding="decoding"
|
|
60
|
+
:priority="priority"
|
|
61
|
+
:fallback-width="fallbackWidth"
|
|
62
|
+
:use-placeholder="usePlaceholder"
|
|
63
|
+
:placeholder-quality="placeholderQuality"
|
|
64
|
+
:placeholder-width="placeholderWidth"
|
|
65
|
+
:placeholder-src="placeholderSrc"
|
|
66
|
+
provider="imgproxy"
|
|
67
|
+
/>
|
|
68
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NovuImageProps } from './NovuImage.vue.js';
|
|
2
|
+
import type { ImgproxyResizingType } from '../providers/imgproxy.js';
|
|
3
|
+
export interface NovuImgproxyImageProps extends Omit<NovuImageProps, 'provider'> {
|
|
4
|
+
preset?: string;
|
|
5
|
+
/** Raw imgproxy gravity string (e.g. `'sm'`, `'ce'`, `'fp:0.5:0.5'`). Overrides `focal`. */
|
|
6
|
+
gravity?: string;
|
|
7
|
+
resizingType?: ImgproxyResizingType;
|
|
8
|
+
extend?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<NovuImgproxyImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuImgproxyImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FocalPoint } from '../utils/focal.js';
|
|
2
|
+
import type { StatamicAsset } from '../types.js';
|
|
3
|
+
export interface NovuStatamicImageProps {
|
|
4
|
+
src?: string | StatamicAsset | null;
|
|
5
|
+
provider?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
fallbackAlt?: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
focal?: FocalPoint;
|
|
10
|
+
width?: number | string;
|
|
11
|
+
height?: number | string;
|
|
12
|
+
aspectRatio?: number;
|
|
13
|
+
placeholderField?: string | string[];
|
|
14
|
+
}
|
|
15
|
+
declare const __VLS_export: import("vue").DefineComponent<NovuStatamicImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuStatamicImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, useAttrs } from "vue";
|
|
3
|
+
import { useNuxtApp, useRuntimeConfig } from "#imports";
|
|
4
|
+
import NovuImage from "./NovuImage.vue";
|
|
5
|
+
import NovuCloudinaryImage from "./NovuCloudinaryImage.vue";
|
|
6
|
+
import NovuBunnyImage from "./NovuBunnyImage.vue";
|
|
7
|
+
import NovuImgproxyImage from "./NovuImgproxyImage.vue";
|
|
8
|
+
import { resolveStatamicAsset } from "../utils/statamic";
|
|
9
|
+
import { getI18nLocale } from "../utils/i18n";
|
|
10
|
+
import { toFiniteNumber } from "../utils/numbers";
|
|
11
|
+
defineOptions({ inheritAttrs: false });
|
|
12
|
+
const props = defineProps({
|
|
13
|
+
src: { type: [String, Object, null], required: false, default: void 0 },
|
|
14
|
+
provider: { type: String, required: false, default: void 0 },
|
|
15
|
+
alt: { type: String, required: false, default: void 0 },
|
|
16
|
+
fallbackAlt: { type: String, required: false, default: void 0 },
|
|
17
|
+
locale: { type: String, required: false, default: void 0 },
|
|
18
|
+
focal: { type: [String, Array], required: false, default: void 0 },
|
|
19
|
+
width: { type: [Number, String], required: false, default: void 0 },
|
|
20
|
+
height: { type: [Number, String], required: false, default: void 0 },
|
|
21
|
+
aspectRatio: { type: Number, required: false, default: void 0 },
|
|
22
|
+
placeholderField: { type: [String, Array], required: false, default: void 0 }
|
|
23
|
+
});
|
|
24
|
+
const runtimeConfig = useRuntimeConfig().public.novuImage;
|
|
25
|
+
const nuxtApp = useNuxtApp();
|
|
26
|
+
const attrs = useAttrs();
|
|
27
|
+
const i18nLocale = computed(() => getI18nLocale(nuxtApp));
|
|
28
|
+
const effectiveLocale = computed(() => props.locale ?? i18nLocale.value);
|
|
29
|
+
const effectiveProvider = computed(() => props.provider ?? runtimeConfig.provider);
|
|
30
|
+
const resolved = computed(
|
|
31
|
+
() => resolveStatamicAsset(
|
|
32
|
+
props.src,
|
|
33
|
+
{
|
|
34
|
+
alt: props.alt,
|
|
35
|
+
fallbackAlt: props.fallbackAlt,
|
|
36
|
+
focal: props.focal,
|
|
37
|
+
width: toFiniteNumber(props.width),
|
|
38
|
+
height: toFiniteNumber(props.height),
|
|
39
|
+
aspectRatio: props.aspectRatio,
|
|
40
|
+
placeholderField: props.placeholderField
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
locale: effectiveLocale.value,
|
|
44
|
+
provider: effectiveProvider.value,
|
|
45
|
+
statamicPlaceholderFields: runtimeConfig.statamicPlaceholderFields
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
const wrapperByProvider = {
|
|
50
|
+
cloudinary: NovuCloudinaryImage,
|
|
51
|
+
bunny: NovuBunnyImage,
|
|
52
|
+
imgproxy: NovuImgproxyImage
|
|
53
|
+
};
|
|
54
|
+
const resolvedComponent = computed(() => {
|
|
55
|
+
const provider = effectiveProvider.value;
|
|
56
|
+
if (provider && provider in wrapperByProvider) {
|
|
57
|
+
return wrapperByProvider[provider];
|
|
58
|
+
}
|
|
59
|
+
return NovuImage;
|
|
60
|
+
});
|
|
61
|
+
const imageProps = computed(() => {
|
|
62
|
+
const base = {
|
|
63
|
+
src: resolved.value.src ?? "",
|
|
64
|
+
alt: resolved.value.alt,
|
|
65
|
+
focal: resolved.value.focal,
|
|
66
|
+
width: resolved.value.width,
|
|
67
|
+
height: resolved.value.height,
|
|
68
|
+
sourceWidth: resolved.value.sourceWidth,
|
|
69
|
+
sourceHeight: resolved.value.sourceHeight,
|
|
70
|
+
aspectRatio: resolved.value.aspectRatio,
|
|
71
|
+
placeholderSrc: resolved.value.placeholderSrc
|
|
72
|
+
};
|
|
73
|
+
if (resolvedComponent.value === NovuImage) {
|
|
74
|
+
return { ...base, provider: effectiveProvider.value };
|
|
75
|
+
}
|
|
76
|
+
return base;
|
|
77
|
+
});
|
|
78
|
+
const componentBindings = computed(() => ({ ...attrs, ...imageProps.value }));
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template>
|
|
82
|
+
<component
|
|
83
|
+
:is="resolvedComponent"
|
|
84
|
+
v-if="resolved.src"
|
|
85
|
+
v-bind="componentBindings"
|
|
86
|
+
/>
|
|
87
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FocalPoint } from '../utils/focal.js';
|
|
2
|
+
import type { StatamicAsset } from '../types.js';
|
|
3
|
+
export interface NovuStatamicImageProps {
|
|
4
|
+
src?: string | StatamicAsset | null;
|
|
5
|
+
provider?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
fallbackAlt?: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
focal?: FocalPoint;
|
|
10
|
+
width?: number | string;
|
|
11
|
+
height?: number | string;
|
|
12
|
+
aspectRatio?: number;
|
|
13
|
+
placeholderField?: string | string[];
|
|
14
|
+
}
|
|
15
|
+
declare const __VLS_export: import("vue").DefineComponent<NovuStatamicImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuStatamicImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter, Ref } from 'vue';
|
|
2
|
+
import type { FocalPoint } from '../utils/focal.js';
|
|
3
|
+
export interface UseResponsiveImageOptions {
|
|
4
|
+
src: MaybeRefOrGetter<string | undefined | null>;
|
|
5
|
+
width?: MaybeRefOrGetter<number | string | undefined>;
|
|
6
|
+
height?: MaybeRefOrGetter<number | string | undefined>;
|
|
7
|
+
sourceWidth?: MaybeRefOrGetter<number | string | undefined>;
|
|
8
|
+
sourceHeight?: MaybeRefOrGetter<number | string | undefined>;
|
|
9
|
+
aspectRatio?: MaybeRefOrGetter<number | undefined>;
|
|
10
|
+
quality?: MaybeRefOrGetter<number | string | undefined>;
|
|
11
|
+
format?: MaybeRefOrGetter<string | undefined>;
|
|
12
|
+
fit?: MaybeRefOrGetter<string | undefined>;
|
|
13
|
+
modifiers?: MaybeRefOrGetter<Record<string, unknown> | undefined>;
|
|
14
|
+
provider?: MaybeRefOrGetter<string | undefined>;
|
|
15
|
+
focal?: MaybeRefOrGetter<FocalPoint | undefined>;
|
|
16
|
+
sizes?: MaybeRefOrGetter<string | undefined>;
|
|
17
|
+
fallbackWidth: MaybeRefOrGetter<number>;
|
|
18
|
+
usePlaceholder: MaybeRefOrGetter<boolean>;
|
|
19
|
+
placeholderQuality: MaybeRefOrGetter<number>;
|
|
20
|
+
placeholderWidth: MaybeRefOrGetter<number>;
|
|
21
|
+
placeholderSrc?: MaybeRefOrGetter<string | undefined>;
|
|
22
|
+
screens: MaybeRefOrGetter<Record<string, number | string>>;
|
|
23
|
+
imgRef: Readonly<Ref<HTMLImageElement | null>>;
|
|
24
|
+
}
|
|
25
|
+
interface ElementSize {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function useResponsiveImage(options: UseResponsiveImageOptions): {
|
|
30
|
+
srcset: import("vue").ComputedRef<string | undefined>;
|
|
31
|
+
sizes: import("vue").ComputedRef<string>;
|
|
32
|
+
placeholderSrc: import("vue").ComputedRef<string | undefined>;
|
|
33
|
+
fallbackUrl: import("vue").ComputedRef<string | undefined>;
|
|
34
|
+
elementSize: Ref<{
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
} | null, ElementSize | {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
} | null>;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { computed, ref, toValue } from "vue";
|
|
2
|
+
import { useResizeObserver } from "@vueuse/core";
|
|
3
|
+
import { useImage } from "#imports";
|
|
4
|
+
import { parseScreens } from "../utils/screens.js";
|
|
5
|
+
import { applyFocal, defaultFocalForProvider } from "../utils/focal.js";
|
|
6
|
+
import { transformProviderModifiers } from "../utils/providerModifiers.js";
|
|
7
|
+
import { toFiniteNumber } from "../utils/numbers.js";
|
|
8
|
+
const PLACEHOLDER_DESCRIPTOR = "32w";
|
|
9
|
+
export function useResponsiveImage(options) {
|
|
10
|
+
const $img = useImage();
|
|
11
|
+
const breakpointWidths = computed(() => parseScreens(toValue(options.screens)));
|
|
12
|
+
const intrinsicAspectRatio = computed(() => {
|
|
13
|
+
return resolveImageAspectRatio(
|
|
14
|
+
toValue(options.aspectRatio),
|
|
15
|
+
toFiniteNumber(toValue(options.width)),
|
|
16
|
+
toFiniteNumber(toValue(options.height))
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
const baseModifiers = computed(() => {
|
|
20
|
+
const explicitFocal = toValue(options.focal);
|
|
21
|
+
const provider = toValue(options.provider);
|
|
22
|
+
const focal = explicitFocal ?? defaultFocalForProvider(provider);
|
|
23
|
+
const sourceW = toFiniteNumber(toValue(options.sourceWidth)) ?? toFiniteNumber(toValue(options.width));
|
|
24
|
+
const sourceH = toFiniteNumber(toValue(options.sourceHeight)) ?? toFiniteNumber(toValue(options.height));
|
|
25
|
+
const quality = toValue(options.quality);
|
|
26
|
+
const format = toValue(options.format);
|
|
27
|
+
const fit = toValue(options.fit);
|
|
28
|
+
const extra = toValue(options.modifiers) ?? {};
|
|
29
|
+
const focalModifiers = applyFocal(provider, focal, { width: sourceW, height: sourceH });
|
|
30
|
+
return {
|
|
31
|
+
...explicitFocal === void 0 ? focalModifiers : {},
|
|
32
|
+
...extra,
|
|
33
|
+
...quality !== void 0 ? { quality } : {},
|
|
34
|
+
...format !== void 0 ? { format } : {},
|
|
35
|
+
...explicitFocal !== void 0 ? focalModifiers : {},
|
|
36
|
+
...fit !== void 0 ? { fit } : {}
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const renderUrl = (src, modifiers) => {
|
|
40
|
+
const provider = toValue(options.provider);
|
|
41
|
+
const callOptions = provider ? { provider } : {};
|
|
42
|
+
return $img(src, modifiers, callOptions);
|
|
43
|
+
};
|
|
44
|
+
const candidateModifiers = (width) => {
|
|
45
|
+
const ar = intrinsicAspectRatio.value;
|
|
46
|
+
const base = {
|
|
47
|
+
...baseModifiers.value,
|
|
48
|
+
width,
|
|
49
|
+
...ar !== void 0 ? { height: Math.round(width / ar) } : {}
|
|
50
|
+
};
|
|
51
|
+
return transformProviderModifiers(toValue(options.provider), base);
|
|
52
|
+
};
|
|
53
|
+
const placeholderSrc = computed(() => {
|
|
54
|
+
const custom = toValue(options.placeholderSrc);
|
|
55
|
+
if (custom) return custom;
|
|
56
|
+
if (!toValue(options.usePlaceholder)) return void 0;
|
|
57
|
+
const src = toValue(options.src);
|
|
58
|
+
if (!src) return void 0;
|
|
59
|
+
return renderUrl(src, {
|
|
60
|
+
...candidateModifiers(toValue(options.placeholderWidth)),
|
|
61
|
+
quality: toValue(options.placeholderQuality)
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const srcset = computed(() => {
|
|
65
|
+
const src = toValue(options.src);
|
|
66
|
+
if (!src) return void 0;
|
|
67
|
+
const widths = breakpointWidths.value;
|
|
68
|
+
if (!widths.length) return void 0;
|
|
69
|
+
const entries = widths.map((w) => `${renderUrl(src, candidateModifiers(w))} ${w}w`);
|
|
70
|
+
const placeholder = placeholderSrc.value;
|
|
71
|
+
if (placeholder) entries.push(`${placeholder} ${PLACEHOLDER_DESCRIPTOR}`);
|
|
72
|
+
return entries.join(", ");
|
|
73
|
+
});
|
|
74
|
+
const fallbackUrl = computed(() => {
|
|
75
|
+
const src = toValue(options.src);
|
|
76
|
+
if (!src) return void 0;
|
|
77
|
+
return renderUrl(src, candidateModifiers(toValue(options.fallbackWidth)));
|
|
78
|
+
});
|
|
79
|
+
const elementSize = ref(null);
|
|
80
|
+
useResizeObserver(options.imgRef, ([entry]) => {
|
|
81
|
+
if (!entry) return;
|
|
82
|
+
const { width, height } = entry.contentRect;
|
|
83
|
+
elementSize.value = { width, height };
|
|
84
|
+
});
|
|
85
|
+
const sizes = computed(() => {
|
|
86
|
+
const override = toValue(options.sizes);
|
|
87
|
+
if (override) return override;
|
|
88
|
+
const size = elementSize.value;
|
|
89
|
+
if (!size || size.width <= 0) return "1px";
|
|
90
|
+
const el = options.imgRef.value;
|
|
91
|
+
const objectFit = el ? readObjectFit(el) : "fill";
|
|
92
|
+
const containerAspect = size.height > 0 ? size.width / size.height : 0;
|
|
93
|
+
const imgAspect = intrinsicAspectRatio.value;
|
|
94
|
+
if (objectFit === "cover" && imgAspect !== void 0 && containerAspect > 0 && imgAspect > containerAspect) {
|
|
95
|
+
return `${Math.ceil(size.height * imgAspect)}px`;
|
|
96
|
+
}
|
|
97
|
+
return `${Math.ceil(size.width)}px`;
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
srcset,
|
|
101
|
+
sizes,
|
|
102
|
+
placeholderSrc,
|
|
103
|
+
fallbackUrl,
|
|
104
|
+
elementSize
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function readObjectFit(el) {
|
|
108
|
+
if (typeof window === "undefined") return "fill";
|
|
109
|
+
return window.getComputedStyle(el).objectFit || "fill";
|
|
110
|
+
}
|
|
111
|
+
function resolveImageAspectRatio(explicitRatio, width, height) {
|
|
112
|
+
if (explicitRatio && Number.isFinite(explicitRatio) && explicitRatio > 0) return explicitRatio;
|
|
113
|
+
if (width && height && width > 0 && height > 0) return width / height;
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ImageModifiers, ProviderGetImage } from '@nuxt/image';
|
|
2
|
+
export type ImgproxyResizingType = 'fit' | 'fill' | 'fill-down' | 'force' | 'auto';
|
|
3
|
+
export type ImgproxyGravityType = 'ce' | 'no' | 'so' | 'ea' | 'we' | 'noea' | 'nowe' | 'soea' | 'sowe';
|
|
4
|
+
export interface ImgproxyCrop {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
gravity?: ImgproxyGravityType;
|
|
8
|
+
}
|
|
9
|
+
export type ImgproxyFormat = 'webp' | 'png' | 'jpg' | 'jpeg' | 'jxl' | 'avif' | 'gif' | 'ico' | 'svg' | 'heic' | 'bmp' | 'tiff' | 'pdf' | 'psd' | 'mp4';
|
|
10
|
+
export type ImgproxyBooleanPrimitive = string | number | boolean;
|
|
11
|
+
export interface ImgproxyModifiers extends Omit<ImageModifiers, 'fit' | 'format' | 'gravity'> {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
format: ImgproxyFormat;
|
|
15
|
+
fit: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
|
16
|
+
resizingType: ImgproxyResizingType;
|
|
17
|
+
resize: string;
|
|
18
|
+
size: string;
|
|
19
|
+
minWidth: number;
|
|
20
|
+
minHeight: number;
|
|
21
|
+
zoom: string | number;
|
|
22
|
+
dpr: number;
|
|
23
|
+
enlarge: boolean;
|
|
24
|
+
extend: boolean;
|
|
25
|
+
extendAspectRatio: string;
|
|
26
|
+
gravity: ImgproxyGravityType | string;
|
|
27
|
+
crop: ImgproxyCrop | string;
|
|
28
|
+
autoRotate: boolean;
|
|
29
|
+
rotate: number;
|
|
30
|
+
background: string;
|
|
31
|
+
blur: number;
|
|
32
|
+
sharpen: number;
|
|
33
|
+
pixelate: number;
|
|
34
|
+
stripMetadata: boolean;
|
|
35
|
+
keepCopyright: boolean;
|
|
36
|
+
stripColorProfile: boolean;
|
|
37
|
+
enforceThumbnail: boolean;
|
|
38
|
+
maxBytes: number;
|
|
39
|
+
raw: boolean;
|
|
40
|
+
cachebuster: string;
|
|
41
|
+
expires: number;
|
|
42
|
+
filename: string;
|
|
43
|
+
returnAttachment: boolean;
|
|
44
|
+
preset: string;
|
|
45
|
+
maxSrcResolution: number;
|
|
46
|
+
maxSrcFileSize: number;
|
|
47
|
+
maxAnimationFrames: number;
|
|
48
|
+
maxAnimationFrameResolution: string;
|
|
49
|
+
maxResultDimension: string;
|
|
50
|
+
}
|
|
51
|
+
interface ImgproxyOptions {
|
|
52
|
+
baseURL: string;
|
|
53
|
+
salt: string;
|
|
54
|
+
key: string;
|
|
55
|
+
modifiers?: Partial<ImgproxyModifiers>;
|
|
56
|
+
}
|
|
57
|
+
interface ImgproxyContext {
|
|
58
|
+
baseURL?: string;
|
|
59
|
+
key?: string;
|
|
60
|
+
salt?: string;
|
|
61
|
+
modifiers?: Partial<ImgproxyModifiers>;
|
|
62
|
+
}
|
|
63
|
+
export declare const getImage: ProviderGetImage<ImgproxyContext>;
|
|
64
|
+
declare const _default: () => {
|
|
65
|
+
getImage: ProviderGetImage<ImgproxyContext>;
|
|
66
|
+
};
|
|
67
|
+
export default _default;
|
|
68
|
+
export type { ImgproxyOptions };
|