Skip to content

Commit 7c7846a

Browse files
authoredNov 8, 2024··
Merge pull request #246 from nuxt-modules/chore/4.0.0
Chore/4.0.0
2 parents 81b1dbd + f9982b4 commit 7c7846a

25 files changed

+18363
-16401
lines changed
 

‎.github/workflows/ci.yml

+12-15
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,23 @@ jobs:
2525
- name: checkout
2626
uses: actions/checkout@master
2727

28+
- name: Setup pnpm
29+
uses: pnpm/action-setup@v4
30+
- name: Use Node.js ${{ matrix.node-version }}
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: ${{ matrix.node-version }}
34+
cache: pnpm
35+
2836
- name: cache node_modules
29-
uses: actions/cache@v2
37+
uses: actions/cache@v3
3038
with:
3139
path: node_modules
32-
key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
40+
key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/pnpm-lock.yaml')) }}
3341

3442
- name: Install dependencies
3543
if: steps.cache.outputs.cache-hit != 'true'
36-
run: yarn
44+
run: pnpm install
3745

3846
- name: Lint
39-
run: yarn lint
40-
41-
- name: Prepare
42-
run: yarn dev:prepare
43-
44-
# - name: Test
45-
# run: yarn test
46-
47-
# - name: Coverage
48-
# run: yarn codecov
49-
# env:
50-
# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
47+
run: pnpm run lint

‎.stackblitz/app.vue

+91-36
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<script lang="ts" setup>
2-
// Usage of `useCldImageUrl` composable
2+
import type {
3+
CloudinaryUploadWidgetError,
4+
CloudinaryUploadWidgetResults,
5+
} from '@cloudinary-util/types'
6+
import type { MediaType } from '../src/runtime/components/CldProductGallery.vue'
7+
38
const { url } = useCldImageUrl({ options: { src: '/cld-sample-5.jpg' } })
49
console.log(url)
510
@@ -8,38 +13,84 @@ const { url: videoUrl } = useCldVideoUrl({
813
})
914
console.log(videoUrl)
1015
11-
const mediaAssets = [
16+
const mediaAssets: { tag: string, mediaType?: MediaType }[] = [
1217
{ tag: 'electric_car_product_gallery_demo' }, // by default mediaType: "image"
1318
{ tag: 'electric_car_product_gallery_demo', mediaType: 'video' },
1419
{ tag: 'electric_car_360_product_gallery_demo', mediaType: 'spin' },
1520
]
1621
1722
const buttonId = 'open-btn'
23+
24+
const cldVideoRef = ref()
25+
26+
const chapters = {
27+
0: 'Chapter 1',
28+
6: 'Chapter 2',
29+
9: 'Chapter 3',
30+
}
31+
32+
const colors = {
33+
accent: '#ff0000',
34+
base: '#00ff00',
35+
text: '#0000ff',
36+
}
37+
38+
const onResult = (results: CloudinaryUploadWidgetResults) => {
39+
console.log('results', results)
40+
}
41+
const onError = (
42+
error: CloudinaryUploadWidgetError,
43+
results: CloudinaryUploadWidgetResults,
44+
) => {
45+
console.log('error', error)
46+
console.log('results', results)
47+
}
1848
</script>
1949

2050
<template>
21-
<!-- Usage of `CldImage.vue` component -->
22-
<CldImage
23-
src="cld-sample-5"
51+
<button :id="buttonId">
52+
Select Image or Video
53+
</button>
54+
<CldMediaLibrary
55+
api-key="12345"
56+
:button-id="buttonId"
57+
style="height: 600px"
58+
/>
59+
<CldProductGallery
60+
:media-assets="mediaAssets"
61+
cloud-name="demo"
62+
:button-id="buttonId"
63+
/>
64+
<CldOgImage
65+
src="cld-sample-2"
66+
twitter-title="test"
2467
width="987"
2568
height="987"
26-
alt="Sample Product"
69+
alt="twitter-title"
2770
/>
2871
<CldVideoPlayer
72+
ref="cldVideoRef"
73+
auto-play
74+
autoplay-mode="on-scroll"
75+
loop
76+
muted
77+
playsinline
2978
width="1620"
3079
height="1080"
31-
src="videos/mountain-stars"
80+
src="videos/dog-running-snow"
81+
:config="{ url: { cname: 'test' } }"
3282
picture-in-picture-toggle
83+
chapters-button
84+
:chapters="chapters"
85+
:colors="colors"
86+
:transformation="{ raw_transformation: 'e_fade:2000,e_fade:-2000' }"
3387
/>
34-
<!-- Usage of `CldUploadWidget.vue` component -->
3588
<CldUploadWidget
3689
v-slot="{ open }"
3790
upload-preset="nuxt-cloudinary-unsigned"
38-
:on-upload="
39-
(result, w) => {
40-
console.log(result, w);
41-
}
42-
"
91+
:tags="['sad', 'music']"
92+
:on-error="onError"
93+
:on-result="onResult"
4394
>
4495
<button
4596
type="button"
@@ -48,23 +99,41 @@ const buttonId = 'open-btn'
4899
Upload an Image
49100
</button>
50101
</CldUploadWidget>
51-
<!-- Usage of `CldUploadButton.vue` component -->
52-
<CldUploadButton upload-preset="nuxt-cloudinary-unsigned">
102+
<CldUploadButton
103+
upload-preset="nuxt-cloudinary-unsigned"
104+
:on-error="onError"
105+
:on-result="onResult"
106+
>
53107
Upload
54108
</CldUploadButton>
55-
<p>CldOgImage is here. Inspect the html meta to see the result</p>
56-
<CldOgImage
57-
src="cld-sample-2"
58-
twitter-title="test"
59-
width="300"
60-
height="300"
61-
alt="test"
109+
<CldImage
110+
src="cld-sample-5"
111+
width="987"
112+
height="987"
113+
alt="Sample Product"
114+
crop="fill"
115+
priority
116+
sizes="(max-width: 600px) 480px,
117+
800px"
118+
:replace-background="{ prompt: 'fish tank', seed: 3 }"
119+
:extract="{
120+
prompt: 'space jellyfish',
121+
multiple: true,
122+
mode: 'mask',
123+
invert: true,
124+
}"
62125
/>
63126
<CldImage
64127
src="cld-sample-5"
65128
width="987"
66129
height="987"
67130
alt="Sample Product"
131+
:crop="{
132+
type: 'thumb',
133+
width: 600,
134+
height: 600,
135+
source: true,
136+
}"
68137
:overlays="[
69138
{
70139
position: {
@@ -102,18 +171,4 @@ const buttonId = 'open-btn'
102171
},
103172
]"
104173
/>
105-
106-
<button :id="buttonId">
107-
Select Image or Video
108-
</button>
109-
<CldMediaLibrary
110-
api-key="12345"
111-
:button-id="buttonId"
112-
style="height: 600px"
113-
/>
114-
<CldProductGallery
115-
:media-assets="mediaAssets"
116-
cloud-name="demo"
117-
:button-id="buttonId"
118-
/>
119174
</template>

‎.stackblitz/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
"nuxt": "^3.11.2"
1212
},
1313
"dependencies": {
14-
"@nuxtjs/cloudinary": "^3.1.0"
14+
"@nuxtjs/cloudinary": "^4.0.0"
1515
}
1616
}

‎.stackblitz/pnpm-lock.yaml

+6,473
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.stackblitz/yarn.lock

-6,482
This file was deleted.
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<CldImage
3+
src="cld-sample-5"
4+
width="987"
5+
height="987"
6+
alt="Sample Product"
7+
:crop="{
8+
type: 'thumb',
9+
width: 600,
10+
height: 600,
11+
source: true,
12+
}"
13+
/>
14+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<CldImage
3+
src="cld-sample-5"
4+
width="987"
5+
height="987"
6+
alt="Sample Product"
7+
:extract="{
8+
prompt: 'space jellyfish',
9+
multiple: true,
10+
mode: 'mask',
11+
invert: true,
12+
}"
13+
/>
14+
</template>

‎docs/content/2.components/CldImage/1.usage.md

-18
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,6 @@ The version number is required due to the variable nature of Cloudinary URLs. Th
105105
</template>
106106
```
107107

108-
## Preserving URL Transformations
109-
110-
If using a full Cloudinary URL, you might already have transformations applied to your image.
111-
112-
To preserve those transformations, you can apply the `preserveTransformations` prop:
113-
114-
```vue
115-
<template>
116-
<CldImage
117-
src="https://res.cloudinary.com/mycloud/image/upload/w_100,h_200,c_fill/v1234/myimage"
118-
width="400"
119-
height="400"
120-
alt="My Awesome Image"
121-
preserveTransformations
122-
/>
123-
</template>
124-
```
125-
126108
## Watch & Learn
127109

128110
<iframe width="100%" height="420"

‎docs/content/2.components/CldImage/2.configuration.md

-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ Following props can be passed to the CldImage component to make it work in more
5656
| gravity | string | `"auto"` | `"faces"` |
5757
| height | number/string | - | `600` |
5858
| overlays | array | - | See Below |
59-
| preserveTransformations | string | `false` | `true` |
6059
| quality | string | `"auto"` | `"90"` |
6160
| rawTransformations | array | - | `['e_blur:2000']` |
6261
| removeBackground | bool/string | `false` | `true` |

‎docs/content/2.components/CldImage/3.examples.md

+30
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,36 @@ crop
5454

5555
:cld-image{src="images/woman-headphones" width="1200" height="1200" alt="test" crop="thumb" style="text-align: center; margin: 0 auto"}
5656

57+
Cloudinary additionally supports dynamic crop modes like thumb that may provide a different result based on the given width and height. To help provide more options for controlling cropping images, you can specify and object or array of objects.
58+
59+
For instance, to crop the original source image, which will then later resize it to the initial width and height, you can use:
60+
61+
```html
62+
:crop="{
63+
type: 'thumb',
64+
width: 600,
65+
height: 600,
66+
source: true,
67+
}"
68+
```
69+
70+
:image-with-crop{style="text-align: center; margin: 0 auto"}
71+
72+
#### Extract
73+
74+
`extract`: Extracts an area or multiple areas of an image, described in natural language.
75+
76+
```html
77+
:extract="{
78+
prompt: 'space jellyfish',
79+
multiple: true,
80+
mode: 'mask',
81+
invert: true,
82+
}"
83+
```
84+
85+
:image-with-extract{style="text-align: center; margin: 0 auto"}
86+
5787
#### Fill
5888

5989
`fillBackground`: Fills the background of an image using Generative AI

‎docs/content/2.components/CldVideoPlayer.md

+77-29
Large diffs are not rendered by default.

‎docs/content/3.composables/1.useCldImageUrl.md

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ Apart from the image options, `useCldImageUrl` composable accepts two additional
5454
| gravity | string | `"auto"` | `"faces"` |
5555
| height | number/string | - | `600` |
5656
| overlays | array | - | See Below |
57-
| preserveTransformations | string | `false` | `true` |
5857
| quality | string | `"auto"` | `"90"` |
5958
| rawTransformations | array | - | `['e_blur:2000']` |
6059
| removeBackground | bool/string | `false` | `true` |

‎docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"typescript": "^5.4.5"
1818
},
1919
"dependencies": {
20-
"@nuxtjs/cloudinary": "^3.1.0"
20+
"@nuxtjs/cloudinary": "^4.0.0"
2121
}
2222
}

‎docs/pnpm-lock.yaml

+5,947-4,750
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nuxtjs/cloudinary",
3-
"version": "3.1.0",
3+
"version": "4.0.0",
44
"description": "Cloudinary module for Nuxt",
55
"license": "MIT",
66
"type": "module",
@@ -45,11 +45,11 @@
4545
"lint": "eslint .",
4646
"test": "vitest run",
4747
"test:watch": "vitest watch",
48-
"stackblitz": "cd .stackblitz && yarn && yarn dev"
48+
"stackblitz": "cd .stackblitz && pnpm install && pnpm run dev"
4949
},
5050
"dependencies": {
51-
"@cloudinary-util/url-loader": "^5.6.0",
52-
"@cloudinary-util/types": "1.2.0",
51+
"@cloudinary-util/url-loader": "^6.0.0",
52+
"@cloudinary-util/types": "2.0.0-beta.1",
5353
"@nuxt/kit": "^3.11.2",
5454
"@unpic/vue": "^0.0.48",
5555
"defu": "^6.1.4"
@@ -68,5 +68,6 @@
6868
"stackblitz": {
6969
"installDependencies": false,
7070
"startCommand": "yarn stackblitz"
71-
}
71+
},
72+
"packageManager": "pnpm@8.11.0+sha512.9df87c16c98db27b4e051d787e67f2207ec47e809ccb07bf7b5ec4acdcd1613355839a38d0b900144923d6a7057700b74d2a0ccb1558beb241647a1206a9a7ab"
7273
}

‎playground/app.vue

+46-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<script lang="ts" setup>
2-
// Usage of `useCldImageUrl` composable
2+
import type {
3+
CloudinaryUploadWidgetError,
4+
CloudinaryUploadWidgetResults,
5+
} from '@cloudinary-util/types'
6+
import type { MediaType } from '../src/runtime/components/CldProductGallery.vue'
7+
38
const { url } = useCldImageUrl({ options: { src: '/cld-sample-5.jpg' } })
49
console.log(url)
510
@@ -8,7 +13,7 @@ const { url: videoUrl } = useCldVideoUrl({
813
})
914
console.log(videoUrl)
1015
11-
const mediaAssets = [
16+
const mediaAssets: { tag: string, mediaType?: MediaType }[] = [
1217
{ tag: 'electric_car_product_gallery_demo' }, // by default mediaType: "image"
1318
{ tag: 'electric_car_product_gallery_demo', mediaType: 'video' },
1419
{ tag: 'electric_car_360_product_gallery_demo', mediaType: 'spin' },
@@ -29,6 +34,17 @@ const colors = {
2934
base: '#00ff00',
3035
text: '#0000ff',
3136
}
37+
38+
const onResult = (results: CloudinaryUploadWidgetResults) => {
39+
console.log('results', results)
40+
}
41+
const onError = (
42+
error: CloudinaryUploadWidgetError,
43+
results: CloudinaryUploadWidgetResults,
44+
) => {
45+
console.log('error', error)
46+
console.log('results', results)
47+
}
3248
</script>
3349

3450
<template>
@@ -48,9 +64,17 @@ const colors = {
4864
<CldOgImage
4965
src="cld-sample-2"
5066
twitter-title="test"
67+
width="987"
68+
height="987"
69+
alt="twitter-title"
5170
/>
5271
<CldVideoPlayer
5372
ref="cldVideoRef"
73+
auto-play
74+
autoplay-mode="on-scroll"
75+
loop
76+
muted
77+
playsinline
5478
width="1620"
5579
height="1080"
5680
src="videos/dog-running-snow"
@@ -59,11 +83,14 @@ const colors = {
5983
chapters-button
6084
:chapters="chapters"
6185
:colors="colors"
86+
:transformation="{ raw_transformation: 'e_fade:2000,e_fade:-2000' }"
6287
/>
6388
<CldUploadWidget
6489
v-slot="{ open }"
6590
upload-preset="nuxt-cloudinary-unsigned"
6691
:tags="['sad', 'music']"
92+
:on-error="onError"
93+
:on-result="onResult"
6794
>
6895
<button
6996
type="button"
@@ -72,7 +99,11 @@ const colors = {
7299
Upload an Image
73100
</button>
74101
</CldUploadWidget>
75-
<CldUploadButton upload-preset="nuxt-cloudinary-unsigned">
102+
<CldUploadButton
103+
upload-preset="nuxt-cloudinary-unsigned"
104+
:on-error="onError"
105+
:on-result="onResult"
106+
>
76107
Upload
77108
</CldUploadButton>
78109
<CldImage
@@ -85,13 +116,24 @@ const colors = {
85116
sizes="(max-width: 600px) 480px,
86117
800px"
87118
:replace-background="{ prompt: 'fish tank', seed: 3 }"
119+
:extract="{
120+
prompt: 'space jellyfish',
121+
multiple: true,
122+
mode: 'mask',
123+
invert: true,
124+
}"
88125
/>
89126
<CldImage
90127
src="cld-sample-5"
91128
width="987"
92129
height="987"
93130
alt="Sample Product"
94-
crop="fill"
131+
:crop="{
132+
type: 'thumb',
133+
width: 600,
134+
height: 600,
135+
source: true,
136+
}"
95137
:overlays="[
96138
{
97139
position: {

‎pnpm-lock.yaml

+5,563-4,508
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/runtime/components/CldImage.vue

+20-112
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,31 @@
11
<script setup lang="ts">
2-
// This is working in dev playground but does not work in the published package
3-
// Come back to this after https://github.com/nuxt/nuxt/issues/20936 is fixed
4-
// import type { AssetOptions } from "@cloudinary-util/url-loader";
5-
// import type { ConfigOptions } from "@cloudinary-util/url-loader";
62
import { ref } from 'vue'
73
import { Image } from '@unpic/vue'
8-
import type { ConstructUrlProps } from '@cloudinary-util/url-loader'
4+
import type {
5+
ConstructUrlProps,
6+
ImageOptions,
7+
ConfigOptions,
8+
} from '@cloudinary-util/url-loader'
9+
import { pollForProcessingImage } from '@cloudinary-util/util'
910
import { useCldImageUrl } from '../composables/useCldImageUrl'
1011
11-
interface ImageOptionsFillBackground {
12-
crop?: string
13-
gravity?: string
14-
prompt?: string
15-
}
16-
17-
interface AssetOptions {
18-
assetType?: string
19-
crop?: string
20-
deliveryType?: string
21-
effects?: Array<any>
22-
flags?: Array<string> | object
23-
format?: string
24-
gravity?: string
25-
height?: string | number
26-
overlays?: Array<any>
27-
quality?: number | string
28-
rawTransformations?: string[]
29-
removeBackground?: boolean
30-
sanitize?: boolean
31-
seoSuffix?: string
32-
src: string
33-
text?: any
34-
namedTransformations?: Array<string>
35-
underlay?: string
36-
underlays?: Array<any>
37-
version?: number | string
38-
width?: string | number
39-
zoom?: string
40-
}
41-
42-
type ImageOptionsRecolorPrompt = string | Array<string>
43-
44-
interface ImageOptionsRecolor {
45-
prompt?: ImageOptionsRecolorPrompt
46-
to?: string
47-
multiple?: boolean
48-
}
49-
50-
interface ImageOptionsZoomPan {
51-
loop: string | boolean
52-
options: string
53-
}
54-
55-
type ImageOptionsRemovePrompt = string | Array<string>
56-
57-
interface ImageOptionsRemove {
58-
prompt?: ImageOptionsRemovePrompt
59-
region?: [300, 200, 1900, 3500]
60-
multiple?: boolean
61-
removeShadow?: boolean
62-
}
63-
64-
interface ImageOptionsGenerativeReplace {
65-
to: string
66-
from: string
67-
preserveGeometry?: boolean
68-
}
69-
interface ImageOptions extends AssetOptions {
70-
zoompan?: string | boolean | ImageOptionsZoomPan
71-
fillBackground?: boolean | ImageOptionsFillBackground
72-
recolor?: ImageOptionsRecolorPrompt | ImageOptionsRecolor
73-
remove?: ImageOptionsRemovePrompt | ImageOptionsRemove
74-
replace?: Array<string | boolean> | ImageOptionsGenerativeReplace
75-
restore?: boolean
76-
replaceBackground?: boolean | string | { prompt: string, seed: number }
77-
}
78-
7912
export interface CldImageProps extends ImageOptions {
8013
loading?: 'eager' | 'lazy'
8114
fetchPriority?: 'high' | 'low' | 'auto'
8215
// Adding below as required props to promote good patterns in developing images
8316
alt: string
8417
width: string | number
8518
height: string | number
86-
// Cloudinary URL Loader props
87-
// Cannot use `ConfigOptions` due to the same issue as mentioned at the top
88-
config?: any
19+
config?: ConfigOptions
8920
// Unpic props
9021
layout?: 'constrained' | 'fullWidth' | 'fixed'
9122
priority?: boolean
9223
background?: 'auto' | string
93-
// Cloudinary missing effect props
94-
blur?: string | number
95-
pixelate?: boolean
96-
grayscale?: boolean
97-
tint?: string | number
98-
opacity?: string | number
99-
shear?: string
100-
border?: string
101-
loaderOptions?: {
102-
width: number | string
103-
}
10424
}
10525
10626
const props = defineProps<CldImageProps>()
10727
108-
const { config, loaderOptions, ...options } = props
28+
const { config, ...options } = props
10929
11030
const { url } = useCldImageUrl({ options, config } as ConstructUrlProps)
11131
@@ -131,31 +51,19 @@ const transformUrl = () => {
13151
const imgKey = ref('image-key')
13252
13353
const handleError = async (payload: Event) => {
134-
const result = await pollForProcessingImage(payload)
135-
136-
if (result) imgKey.value = `${imgKey.value}-${Math.random()}`
137-
}
138-
139-
const pollForProcessingImage = async (options: Event): Promise<boolean> => {
140-
const { src } = options.target as EventTarget & { src: string }
141-
try {
142-
await new Promise((resolve, reject) => {
143-
fetch(src).then((res) => {
144-
if (!res.ok) {
145-
reject(res)
146-
return
147-
}
148-
resolve(res)
149-
})
150-
})
54+
const eventPayload = payload.target as EventTarget & { src: string }
55+
const result = await pollForProcessingImage(eventPayload)
56+
57+
if (
58+
typeof result.error === 'string'
59+
&& process.env.NODE_ENV === 'development'
60+
) {
61+
console.error(
62+
`[CldImage] Failed to load image ${props.src}: ${result.error}`,
63+
)
15164
}
152-
catch (e: any) {
153-
if (e.status === 423) {
154-
return await pollForProcessingImage(options)
155-
}
156-
return false
157-
}
158-
return true
65+
66+
if (result.success) imgKey.value = `${imgKey.value}-${Math.random()}`
15967
}
16068
</script>
16169

‎src/runtime/components/CldMediaLibrary.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const handleOnLoad = () => {
4848
useHead({
4949
script: [
5050
{
51-
id: `cloudinary-media-library-${Math.floor(Math.random() * 100)}`,
51+
id: 'cloudinary-media-library',
5252
src: `https://media-library.cloudinary.com/global/all.js`,
5353
onload: handleOnLoad,
5454
onerror: e =>

‎src/runtime/components/CldOgImage.vue

+1-50
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { computed } from 'vue'
33
import type { ConstructUrlProps } from '@cloudinary-util/url-loader'
44
import { useCldImageUrl } from '../composables/useCldImageUrl'
5+
import type { CldImageProps } from './CldImage.vue'
56
import { useRouter } from '#imports'
67
78
const TWITTER_CARD = 'summary_large_image'
@@ -11,56 +12,6 @@ const OG_IMAGE_HEIGHT = 1254
1112
1213
const { currentRoute } = useRouter()
1314
14-
interface AssetOptions {
15-
assetType?: string
16-
crop?: string
17-
deliveryType?: string
18-
effects?: Array<any>
19-
flags?: Array<string> | object
20-
format?: string
21-
gravity?: string
22-
height?: string | number
23-
overlays?: Array<any>
24-
quality?: number | string
25-
rawTransformations?: string[]
26-
removeBackground?: boolean
27-
sanitize?: boolean
28-
seoSuffix?: string
29-
src: string
30-
text?: any
31-
namedTransformations?: Array<string>
32-
underlay?: string
33-
underlays?: Array<any>
34-
version?: number | string
35-
width?: string | number
36-
widthResize?: string | number
37-
zoom?: string
38-
}
39-
40-
interface ImageOptionsZoomPan {
41-
loop: string | boolean
42-
options: string
43-
}
44-
interface ImageOptions extends AssetOptions {
45-
zoompan?: string | boolean | ImageOptionsZoomPan
46-
}
47-
48-
export interface CldImageProps extends ImageOptions {
49-
loading?: 'eager' | 'lazy'
50-
fetchPriority?: 'high' | 'low' | 'auto'
51-
// Adding below as required props to promote good patterns in developing images
52-
alt: string
53-
width: string | number
54-
height: string | number
55-
// Cloudinary URL Loader props
56-
// Cannot use `ConfigOptions` due to the same issue as mentioned at the top
57-
config?: any
58-
// Unpic props
59-
layout?: 'constrained' | 'fullWidth' | 'fixed'
60-
priority?: boolean
61-
background?: 'auto' | string
62-
}
63-
6415
export type CldOgImageProps = CldImageProps & {
6516
excludeTags?: Array<string>
6617
twitterTitle?: string

‎src/runtime/components/CldProductGallery.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useRuntimeConfig } from '#imports'
55
66
const cloudinaryRef = ref()
77
8-
type MediaType = 'image' | 'video' | 'spin'
8+
export type MediaType = 'image' | 'video' | 'spin'
99
1010
type ProductGalleryProps = {
1111
cloudName?: string

‎src/runtime/components/CldUploadButton.vue

+3-148
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,5 @@
11
<script setup lang="ts">
2-
export interface CldUploadWidgetPropsOptions {
3-
// Widget
4-
5-
encryption?: {
6-
key: string
7-
iv: string
8-
}
9-
defaultSource?: string
10-
maxFiles?: number
11-
multiple?: boolean
12-
sources?: Array<
13-
| 'camera'
14-
| 'dropbox'
15-
| 'facebook'
16-
| 'gettyimages'
17-
| 'google_drive'
18-
| 'image_search'
19-
| 'instagram'
20-
| 'istock'
21-
| 'local'
22-
| 'shutterstock'
23-
| 'unsplash'
24-
| 'url'
25-
>
26-
27-
// Cropping
28-
29-
cropping?: boolean
30-
croppingAspectRatio?: number
31-
croppingCoordinatesMode?: string
32-
croppingDefaultSelectionRatio?: number
33-
croppingShowBackButton?: boolean
34-
croppingShowDimensions?: boolean
35-
showSkipCropButton?: boolean
36-
37-
// Sources
38-
39-
dropboxAppKey?: string
40-
facebookAppId?: string
41-
googleApiKey?: string
42-
googleDriveClientId?: string
43-
instagramClientId?: string
44-
searchByRights?: boolean
45-
searchBySites?: Array<string>
46-
47-
// Upload
48-
49-
context?: object
50-
folder?: string
51-
publicId?: string
52-
resourceType?: string
53-
tags?: Array<string>
54-
// eslint-disable-next-line @typescript-eslint/ban-types
55-
uploadSignature?: string | Function
56-
uploadSignatureTimestamp?: number
57-
58-
// Client Side
59-
60-
clientAllowedFormats?: Array<string>
61-
croppingValidateDimensions?: boolean
62-
maxChunkSize?: number
63-
maxImageFileSize?: number
64-
maxImageHeight?: number
65-
maxImageWidth?: number
66-
maxFileSize?: number
67-
maxRawFileSize?: number
68-
maxVideoFileSize?: number
69-
minImageHeight?: number
70-
minImageWidth?: number
71-
validateMaxWidthHeight?: boolean
72-
73-
// Containing Page
74-
75-
fieldName?: string
76-
form?: string
77-
thumbnails?: string
78-
thumbnailTransformation?: string | Array<object>
79-
80-
// Customization
81-
82-
buttonCaption?: string
83-
buttonClass?: string
84-
text?: object
85-
theme?: string
86-
styles?: object
87-
88-
// Advanced
89-
90-
autoMinimize?: boolean
91-
// eslint-disable-next-line @typescript-eslint/ban-types
92-
getTags?: Function
93-
// eslint-disable-next-line @typescript-eslint/ban-types
94-
getUploadPresets?: Function
95-
inlineContainer?: any // string or DOM element
96-
language?: string
97-
// eslint-disable-next-line @typescript-eslint/ban-types
98-
preBatch?: Function
99-
// eslint-disable-next-line @typescript-eslint/ban-types
100-
prepareUploadParams?: Function
101-
queueViewPosition?: string
102-
showAdvancedOptions?: boolean
103-
showCompletedButton?: boolean
104-
showInsecurePreview?: boolean
105-
showPoweredBy?: boolean
106-
showUploadMoreButton?: boolean
107-
singleUploadAutoClose?: boolean
108-
}
109-
110-
export interface CldUploadWidgetProps {
111-
// eslint-disable-next-line @typescript-eslint/ban-types
112-
onClose?: Function
113-
// eslint-disable-next-line @typescript-eslint/ban-types
114-
onError?: Function
115-
// eslint-disable-next-line @typescript-eslint/ban-types
116-
onOpen?: Function
117-
// eslint-disable-next-line @typescript-eslint/ban-types
118-
onUpload?: Function
119-
// eslint-disable-next-line @typescript-eslint/ban-types
120-
onClick?: Function
121-
// eslint-disable-next-line @typescript-eslint/ban-types
122-
onAbort?: Function
123-
// eslint-disable-next-line @typescript-eslint/ban-types
124-
onBatchCancelled?: Function
125-
// eslint-disable-next-line @typescript-eslint/ban-types
126-
onDisplayChanged?: Function
127-
// eslint-disable-next-line @typescript-eslint/ban-types
128-
onPublicId?: Function
129-
// eslint-disable-next-line @typescript-eslint/ban-types
130-
onQueuesEnd?: Function
131-
// eslint-disable-next-line @typescript-eslint/ban-types
132-
onQueuesStart?: Function
133-
// eslint-disable-next-line @typescript-eslint/ban-types
134-
onRetry?: Function
135-
// eslint-disable-next-line @typescript-eslint/ban-types
136-
onShowCompleted?: Function
137-
// eslint-disable-next-line @typescript-eslint/ban-types
138-
onSourceChanged?: Function
139-
// eslint-disable-next-line @typescript-eslint/ban-types
140-
onSuccess?: Function
141-
// eslint-disable-next-line @typescript-eslint/ban-types
142-
onTags?: Function
143-
// eslint-disable-next-line @typescript-eslint/ban-types
144-
onUploadAdded?: Function
145-
options?: CldUploadWidgetPropsOptions
146-
signatureEndpoint?: URL | RequestInfo
147-
uploadPreset?: string
148-
}
2+
import type { CldUploadWidgetProps } from './CldUploadWidget.vue'
1493
1504
defineProps<CldUploadWidgetProps>()
1515
</script>
@@ -154,6 +8,8 @@ defineProps<CldUploadWidgetProps>()
1548
<CldUploadWidget
1559
v-slot="{ open, isLoading }"
15610
:upload-preset="uploadPreset"
11+
:on-error="onError"
12+
:on-result="onResult"
15713
>
15814
<button
15915
type="button"
@@ -162,7 +18,6 @@ defineProps<CldUploadWidgetProps>()
16218
@click="(e: Event) => {
16319
e.preventDefault();
16420
open();
165-
if (typeof onClick === 'function') onClick(e)
16621
}"
16722
>
16823
<slot />

‎src/runtime/components/CldUploadWidget.vue

+22-170
Original file line numberDiff line numberDiff line change
@@ -3,176 +3,31 @@ import { useHead } from '@unhead/vue'
33
import { ref, watch } from 'vue'
44
import {
55
type ConfigOptions,
6+
type GetUploadWidgetOptions,
7+
type GenerateUploadWidgetResultCallback,
68
generateSignatureCallback,
79
generateUploadWidgetResultCallback,
810
getUploadWidgetOptions,
911
UPLOAD_WIDGET_EVENTS,
1012
} from '@cloudinary-util/url-loader'
1113
import type { CloudinaryUploadWidgetResults } from '@cloudinary-util/types'
14+
import { triggerOnIdle } from '../util/triggerOnIdle'
1215
import { useRuntimeConfig } from '#imports'
1316
14-
export interface CldUploadWidgetProps {
15-
// eslint-disable-next-line @typescript-eslint/ban-types
16-
onClose?: Function
17-
// eslint-disable-next-line @typescript-eslint/ban-types
18-
onError?: Function
19-
// eslint-disable-next-line @typescript-eslint/ban-types
20-
onOpen?: Function
21-
// eslint-disable-next-line @typescript-eslint/ban-types
22-
onUpload?: Function
23-
// eslint-disable-next-line @typescript-eslint/ban-types
24-
onAbort?: Function
25-
// eslint-disable-next-line @typescript-eslint/ban-types
26-
onBatchCancelled?: Function
27-
// eslint-disable-next-line @typescript-eslint/ban-types
28-
onDisplayChanged?: Function
29-
// eslint-disable-next-line @typescript-eslint/ban-types
30-
onPublicId?: Function
31-
// eslint-disable-next-line @typescript-eslint/ban-types
32-
onQueuesEnd?: Function
33-
// eslint-disable-next-line @typescript-eslint/ban-types
34-
onQueuesStart?: Function
35-
// eslint-disable-next-line @typescript-eslint/ban-types
36-
onRetry?: Function
37-
// eslint-disable-next-line @typescript-eslint/ban-types
38-
onShowCompleted?: Function
39-
// eslint-disable-next-line @typescript-eslint/ban-types
40-
onSourceChanged?: Function
41-
// eslint-disable-next-line @typescript-eslint/ban-types
42-
onSuccess?: Function
43-
// eslint-disable-next-line @typescript-eslint/ban-types
44-
onTags?: Function
45-
// eslint-disable-next-line @typescript-eslint/ban-types
46-
onUploadAdded?: Function
47-
options?: CldUploadWidgetPropsOptions
17+
export interface CldUploadWidgetProps
18+
extends GenerateUploadWidgetResultCallback {
19+
options?: GetUploadWidgetOptions
4820
signatureEndpoint?: URL | RequestInfo
4921
uploadPreset?: string
5022
config?: ConfigOptions
5123
tags?: Array<string>
5224
}
5325
54-
// Parameters sourced from:
55-
// https://cloudinary.com/documentation/upload_widget_reference#parameters
56-
57-
export interface CldUploadWidgetPropsOptions {
58-
// Widget
59-
60-
encryption?: {
61-
key: string
62-
iv: string
63-
}
64-
defaultSource?: string
65-
maxFiles?: number
66-
multiple?: boolean
67-
sources?: Array<
68-
| 'camera'
69-
| 'dropbox'
70-
| 'facebook'
71-
| 'gettyimages'
72-
| 'google_drive'
73-
| 'image_search'
74-
| 'instagram'
75-
| 'istock'
76-
| 'local'
77-
| 'shutterstock'
78-
| 'unsplash'
79-
| 'url'
80-
>
81-
82-
// Cropping
83-
84-
cropping?: boolean
85-
croppingAspectRatio?: number
86-
croppingCoordinatesMode?: string
87-
croppingDefaultSelectionRatio?: number
88-
croppingShowBackButton?: boolean
89-
croppingShowDimensions?: boolean
90-
showSkipCropButton?: boolean
91-
92-
// Sources
93-
94-
dropboxAppKey?: string
95-
facebookAppId?: string
96-
googleApiKey?: string
97-
googleDriveClientId?: string
98-
instagramClientId?: string
99-
searchByRights?: boolean
100-
searchBySites?: Array<string>
101-
102-
// Upload
103-
104-
context?: object
105-
folder?: string
106-
publicId?: string
107-
resourceType?: string
108-
tags?: Array<string>
109-
// eslint-disable-next-line @typescript-eslint/ban-types
110-
uploadSignature?: string | Function
111-
uploadSignatureTimestamp?: number
112-
113-
// Client Side
114-
115-
clientAllowedFormats?: Array<string>
116-
croppingValidateDimensions?: boolean
117-
maxChunkSize?: number
118-
maxImageFileSize?: number
119-
maxImageHeight?: number
120-
maxImageWidth?: number
121-
maxFileSize?: number
122-
maxRawFileSize?: number
123-
maxVideoFileSize?: number
124-
minImageHeight?: number
125-
minImageWidth?: number
126-
validateMaxWidthHeight?: boolean
127-
128-
// Containing Page
129-
130-
fieldName?: string
131-
form?: string
132-
thumbnails?: string
133-
thumbnailTransformation?: string | Array<object>
134-
135-
// Customization
136-
137-
buttonCaption?: string
138-
buttonClass?: string
139-
text?: object
140-
theme?: string
141-
styles?: object
142-
143-
// Advanced
144-
145-
autoMinimize?: boolean
146-
// eslint-disable-next-line @typescript-eslint/ban-types
147-
getTags?: Function
148-
// eslint-disable-next-line @typescript-eslint/ban-types
149-
getUploadPresets?: Function
150-
inlineContainer?: any // string or DOM element
151-
language?: string
152-
// eslint-disable-next-line @typescript-eslint/ban-types
153-
preBatch?: Function
154-
// eslint-disable-next-line @typescript-eslint/ban-types
155-
prepareUploadParams?: Function
156-
queueViewPosition?: string
157-
showAdvancedOptions?: boolean
158-
showCompletedButton?: boolean
159-
showInsecurePreview?: boolean
160-
showPoweredBy?: boolean
161-
showUploadMoreButton?: boolean
162-
singleUploadAutoClose?: boolean
163-
}
164-
165-
export interface CldUploadWidgetResults {
166-
event: string
167-
info: string
168-
}
169-
170-
function triggerOnIdle(callback: any) {
171-
if (window && 'requestIdleCallback' in window) {
172-
return requestIdleCallback(callback)
173-
}
174-
return setTimeout(() => callback(), 1)
175-
}
26+
type UploadActionFunction = (
27+
results: CloudinaryUploadWidgetResults,
28+
data?: any
29+
) => object
30+
type CallbackFunction = (data?: string[]) => object
17631
17732
const props = defineProps<CldUploadWidgetProps>()
17833
@@ -254,6 +109,7 @@ const uploadOptions = getUploadWidgetOptions(
254109
{
255110
cloud: {
256111
cloudName: useRuntimeConfig().public.cloudinary.cloudName,
112+
apiKey: useRuntimeConfig().public.cloudinary.apiKey,
257113
},
258114
...options,
259115
...instanceMethods,
@@ -265,11 +121,8 @@ const resultsCallback = generateUploadWidgetResultCallback({
265121
onError: (uploadError) => {
266122
error.value = uploadError
267123
268-
if (typeof onError === 'function') {
269-
onError(uploadError, {
270-
widget: widget.value.current,
271-
...instanceMethods,
272-
})
124+
if (typeof onError === 'function' && results.value) {
125+
onError(uploadError, results.value)
273126
}
274127
},
275128
onResult: (uploadResult) => {
@@ -285,8 +138,7 @@ const resultsCallback = generateUploadWidgetResultCallback({
285138
typeof widgetEvent === 'string'
286139
&& typeof props[widgetEvent] === 'function'
287140
) {
288-
// eslint-disable-next-line @typescript-eslint/ban-types
289-
const callback = props[widgetEvent] as Function
141+
const callback = props[widgetEvent] as UploadActionFunction
290142
callback(uploadResult, {
291143
widget: widget.value.current,
292144
...instanceMethods,
@@ -296,8 +148,7 @@ const resultsCallback = generateUploadWidgetResultCallback({
296148
const widgetEventAction = `${widgetEvent}Action` as keyof typeof props
297149
298150
if (widgetEventAction && typeof props[widgetEventAction] === 'function') {
299-
// eslint-disable-next-line @typescript-eslint/ban-types
300-
const action = props[widgetEventAction] as Function
151+
const action = props[widgetEventAction] as UploadActionFunction
301152
action(uploadResult)
302153
}
303154
},
@@ -306,8 +157,7 @@ const resultsCallback = generateUploadWidgetResultCallback({
306157
if (props.tags?.length) {
307158
uploadOptions.showAdvancedOptions = true
308159
309-
// eslint-disable-next-line @typescript-eslint/ban-types
310-
uploadOptions.getTags = (cb: Function, prefix: string) =>
160+
uploadOptions.getTags = (cb: CallbackFunction, prefix: string) =>
311161
cb(prefix ? props.tags?.filter(t => !t.indexOf(prefix)) : props.tags)
312162
}
313163
@@ -322,7 +172,7 @@ watch(results, () => {
322172
&& results.value.info === 'hidden'
323173
324174
if (isSuccess && typeof onUpload === 'function') {
325-
onUpload(results, widget.value)
175+
onUpload(results.value)
326176
}
327177
328178
if (isClosed && typeof onClose === 'function') {
@@ -332,7 +182,7 @@ watch(results, () => {
332182
333183
watch(error, () => {
334184
if (error.value && typeof onError === 'function') {
335-
onError(error, widget.value)
185+
onError(error.value, widget.value)
336186
}
337187
})
338188
@@ -344,7 +194,9 @@ watch(error, () => {
344194
function handleOnLoad() {
345195
isScriptLoading.value = false
346196
if (!cloudinary.value) {
347-
cloudinary.value = (window as any).cloudinary
197+
if ('cloudinary' in window) {
198+
cloudinary.value = window.cloudinary
199+
}
348200
}
349201
350202
// To help improve load time of the widget on first instance, use requestIdleCallback

‎src/runtime/components/CldVideoPlayer.vue

+34-68
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,18 @@ import {
99
} from '@cloudinary-util/url-loader'
1010
import { useRuntimeConfig } from '#imports'
1111
12-
export interface CloudinaryVideoPlayer {
13-
// eslint-disable-next-line @typescript-eslint/ban-types
14-
on: Function
15-
}
12+
type HandleEventFunction = (event: {
13+
player: CldVideoPlayerProps['videoRef']
14+
video: CldVideoPlayerProps['videoRef']
15+
}) => object
1616
17-
export interface CloudinaryVideoPlayerOptions {
18-
autoplayMode?: string
19-
cloud_name?: string
20-
colors?: CloudinaryVideoPlayerOptionsColors
21-
controls?: boolean
22-
fontFace?: string
23-
loop?: boolean
24-
muted?: boolean
25-
publicId: string
26-
secure?: boolean
27-
transformation?: Array<object> | object
28-
hideContextMenu?: boolean
29-
config?: ConfigOptions
30-
pictureInPictureToggle?: boolean
31-
chapters?: Record<string | number, string> | boolean
32-
chaptersButton?: boolean
33-
disableRemotePlayback?: boolean
17+
type CallbackFunction = (
18+
key: string,
19+
handleEvent: HandleEventFunction
20+
) => object
21+
22+
export interface CloudinaryVideoPlayer {
23+
on: CallbackFunction
3424
}
3525
3626
export interface CloudinaryVideoPlayerOptionsColors {
@@ -39,50 +29,22 @@ export interface CloudinaryVideoPlayerOptionsColors {
3929
text?: string
4030
}
4131
42-
export interface CloudinaryVideoPlayerOptionsLogo {
43-
logoImageUrl?: string
44-
logoOnclickUrl?: string
45-
showLogo?: boolean
46-
}
47-
48-
export interface CldVideoPlayerPropsLogo {
49-
imageUrl?: CloudinaryVideoPlayerOptionsLogo['logoImageUrl']
50-
logo?: boolean
51-
onClickUrl?: CloudinaryVideoPlayerOptionsLogo['logoOnclickUrl']
52-
}
53-
54-
export type CldVideoPlayerProps = Pick<
55-
CloudinaryVideoPlayerOptions,
56-
| 'colors'
57-
| 'controls'
58-
| 'fontFace'
59-
| 'loop'
60-
| 'muted'
61-
| 'transformation'
62-
| 'hideContextMenu'
63-
> & {
64-
autoPlay?: string
32+
export type CldVideoPlayerProps = GetVideoPlayerOptions & {
33+
autoPlay?: boolean
34+
autoplayMode?: 'never' | 'always' | 'on-scroll'
6535
className?: string
36+
colors?: CloudinaryVideoPlayerOptionsColors
6637
height: string | number
6738
id?: string
68-
logo?: boolean | CldVideoPlayerPropsLogo
69-
// eslint-disable-next-line @typescript-eslint/ban-types
70-
onDataLoad?: Function
71-
// eslint-disable-next-line @typescript-eslint/ban-types
72-
onError?: Function
73-
// eslint-disable-next-line @typescript-eslint/ban-types
74-
onMetadataLoad?: Function
75-
// eslint-disable-next-line @typescript-eslint/ban-types
76-
onPause?: Function
77-
// eslint-disable-next-line @typescript-eslint/ban-types
78-
onPlay?: Function
79-
// eslint-disable-next-line @typescript-eslint/ban-types
80-
onEnded?: Function
39+
onDataLoad?: HandleEventFunction
40+
onError?: HandleEventFunction
41+
onMetadataLoad?: HandleEventFunction
42+
onPause?: HandleEventFunction
43+
onPlay?: HandleEventFunction
44+
onEnded?: HandleEventFunction
8145
playerRef?: { value: CloudinaryVideoPlayer | null }
82-
src: string
8346
version?: string
8447
videoRef?: { value: HTMLVideoElement | null }
85-
quality?: string | number
8648
width: string | number
8749
config?: ConfigOptions
8850
pictureInPictureToggle?: boolean
@@ -92,7 +54,9 @@ export type CldVideoPlayerProps = Pick<
9254
}
9355
9456
const props = withDefaults(defineProps<CldVideoPlayerProps>(), {
95-
autoPlay: 'never',
57+
autoPlay: false,
58+
autoplayMode: 'always',
59+
playsinline: false,
9660
controls: true,
9761
logo: true,
9862
loop: false,
@@ -117,22 +81,22 @@ const {
11781
quality,
11882
width,
11983
config,
120-
} = props as CldVideoPlayerProps
84+
} = props
12185
12286
const playerTransformations = Array.isArray(transformation)
12387
? transformation
12488
: [transformation]
12589
126-
let publicId = src
90+
let localPublicId = src
12791
12892
// If the publicId/src is a URL, attempt to parse it as a Cloudinary URL
12993
// to get the public ID alone
13094
131-
if (publicId.startsWith('http')) {
95+
if (localPublicId.startsWith('http')) {
13296
try {
13397
const parts = parseUrl(src)
13498
if (typeof parts?.publicId === 'string') {
135-
publicId = parts?.publicId
99+
localPublicId = parts?.publicId
136100
}
137101
}
138102
catch (e) {
@@ -150,15 +114,14 @@ const videoRef = props.videoRef || defaultVideoRef
150114
const defaultPlayerRef = ref()
151115
const playerRef = props.playerRef || defaultPlayerRef
152116
153-
const playerId = id || `player-${publicId.replace('/', '-')}`
117+
const playerId = id || `player-${localPublicId.replace('/', '-')}`
154118
let playerClassName = 'cld-video-player cld-fluid'
155119
156120
if (className) {
157121
playerClassName = `${playerClassName} ${className}`
158122
}
159123
160-
// eslint-disable-next-line @typescript-eslint/ban-types
161-
const events: Record<string, Function | undefined> = {
124+
const events: Record<string, HandleEventFunction | undefined> = {
162125
error: onError,
163126
loadeddata: onDataLoad,
164127
loadedmetadata: onMetadataLoad,
@@ -186,6 +149,9 @@ const handleOnLoad = () => {
186149
...props,
187150
colors: props.colors || {},
188151
fontFace: props.fontFace || '',
152+
publicId: localPublicId,
153+
playedEventPercents: props.playedEventPercents || [25, 50, 75, 100],
154+
playedEventTimes: props.playedEventTimes || [],
189155
} as GetVideoPlayerOptions,
190156
{
191157
cloud: {
@@ -199,7 +165,7 @@ const handleOnLoad = () => {
199165
200166
playerRef.value = cloudinaryRef.value.videoPlayer(
201167
videoRef.value,
202-
playerOptions,
168+
JSON.parse(JSON.stringify(playerOptions)),
203169
)
204170
205171
Object.keys(events).forEach((key) => {

‎src/runtime/util/triggerOnIdle.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export function triggerOnIdle(callback: any) {
2+
if (window && 'requestIdleCallback' in window) {
3+
return requestIdleCallback(callback)
4+
}
5+
return setTimeout(() => callback(), 1)
6+
}

0 commit comments

Comments
 (0)
Please sign in to comment.