vue-datocms 3.0.3 → 4.0.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/dist/index.cjs.js CHANGED
@@ -190,6 +190,36 @@ const imageShowStrategy = ({ lazyLoad, loaded }) => {
190
190
  }
191
191
  return true;
192
192
  };
193
+ const buildSrcSet = (src, width, candidateMultipliers) => {
194
+ if (!src || !width) {
195
+ return void 0;
196
+ }
197
+ return candidateMultipliers.map((multiplier) => {
198
+ const url = new URL(src);
199
+ if (multiplier !== 1) {
200
+ url.searchParams.set("dpr", `${multiplier}`);
201
+ const maxH = url.searchParams.get("max-h");
202
+ const maxW = url.searchParams.get("max-w");
203
+ if (maxH) {
204
+ url.searchParams.set(
205
+ "max-h",
206
+ `${Math.floor(parseInt(maxH) * multiplier)}`
207
+ );
208
+ }
209
+ if (maxW) {
210
+ url.searchParams.set(
211
+ "max-w",
212
+ `${Math.floor(parseInt(maxW) * multiplier)}`
213
+ );
214
+ }
215
+ }
216
+ const finalWidth = Math.floor(width * multiplier);
217
+ if (finalWidth < 50) {
218
+ return null;
219
+ }
220
+ return `${url.toString()} ${finalWidth}w`;
221
+ }).filter(Boolean).join(",");
222
+ };
193
223
  const Image = vueDemi.defineComponent({
194
224
  name: "DatocmsImage",
195
225
  props: {
@@ -239,6 +269,24 @@ const Image = vueDemi.defineComponent({
239
269
  },
240
270
  objectPosition: {
241
271
  type: String
272
+ },
273
+ usePlaceholder: {
274
+ type: Boolean,
275
+ default: true
276
+ },
277
+ sizes: {
278
+ type: String
279
+ },
280
+ priority: {
281
+ type: Boolean,
282
+ default: false
283
+ },
284
+ srcSetCandidates: {
285
+ type: Array,
286
+ validator: (values) => values.every((value) => {
287
+ return typeof value === "number";
288
+ }),
289
+ default: () => [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]
242
290
  }
243
291
  },
244
292
  setup(props) {
@@ -250,21 +298,33 @@ const Image = vueDemi.defineComponent({
250
298
  threshold: props.intersectionThreshold || props.intersectionTreshold || 0,
251
299
  rootMargin: props.intersectionMargin || "0px 0px 0px 0px"
252
300
  });
301
+ const computedLazyLoad = vueDemi.ref(props.priority ? false : props.lazyLoad);
302
+ const imageRef = vueDemi.ref();
303
+ vueDemi.watchEffect(() => {
304
+ if (!imageRef.value) {
305
+ return;
306
+ }
307
+ if (imageRef.value.complete && imageRef.value.naturalWidth) {
308
+ handleLoad();
309
+ }
310
+ });
253
311
  return {
254
312
  inView,
255
313
  elRef,
256
314
  loaded,
257
- handleLoad
315
+ handleLoad,
316
+ computedLazyLoad,
317
+ imageRef
258
318
  };
259
319
  },
260
320
  render() {
261
321
  const addImage = imageAddStrategy({
262
- lazyLoad: this.lazyLoad,
322
+ lazyLoad: this.computedLazyLoad,
263
323
  inView: this.inView,
264
324
  loaded: this.loaded
265
325
  });
266
326
  const showImage = imageShowStrategy({
267
- lazyLoad: this.lazyLoad,
327
+ lazyLoad: this.computedLazyLoad,
268
328
  inView: this.inView,
269
329
  loaded: this.loaded
270
330
  });
@@ -272,30 +332,30 @@ const Image = vueDemi.defineComponent({
272
332
  ...vueDemi.isVue2 && {
273
333
  props: {
274
334
  srcset: this.data.webpSrcSet,
275
- sizes: this.data.sizes,
335
+ sizes: this.sizes ?? this.data.sizes ?? void 0,
276
336
  type: "image/webp"
277
337
  }
278
338
  },
279
339
  ...vueDemi.isVue3 && {
280
340
  srcset: this.data.webpSrcSet,
281
- sizes: this.data.sizes,
341
+ sizes: this.sizes ?? this.data.sizes ?? void 0,
282
342
  type: "image/webp"
283
343
  }
284
344
  });
285
345
  const regularSource = this.data.srcSet && vueDemi.h(Source, {
286
346
  ...vueDemi.isVue2 && {
287
347
  props: {
288
- srcset: this.data.srcSet,
289
- sizes: this.data.sizes
348
+ srcset: this.data.srcSet ?? buildSrcSet(this.data.src, this.data.width, this.srcSetCandidates),
349
+ sizes: this.sizes ?? this.data.sizes ?? void 0
290
350
  }
291
351
  },
292
352
  ...vueDemi.isVue3 && {
293
- srcset: this.data.srcSet,
294
- sizes: this.data.sizes
353
+ srcset: this.data.srcSet ?? buildSrcSet(this.data.src, this.data.width, this.srcSetCandidates),
354
+ sizes: this.sizes ?? this.data.sizes ?? void 0
295
355
  }
296
356
  });
297
357
  const transition = typeof this.fadeInDuration === "undefined" || this.fadeInDuration > 0 ? `opacity ${this.fadeInDuration || 500}ms ${this.fadeInDuration || 500}ms` : void 0;
298
- const placeholder = vueDemi.h("div", {
358
+ const placeholder = this.usePlaceholder && (this.data.bgColor || this.data.base64) ? vueDemi.h("div", {
299
359
  style: {
300
360
  backgroundImage: this.data.base64 ? `url(${this.data.base64})` : null,
301
361
  backgroundColor: this.data.bgColor,
@@ -304,11 +364,15 @@ const Image = vueDemi.defineComponent({
304
364
  objectFit: this.objectFit,
305
365
  objectPosition: this.objectPosition,
306
366
  transition,
307
- ...absolutePositioning
367
+ position: "absolute",
368
+ left: "-5%",
369
+ top: "-5%",
370
+ width: "110%",
371
+ height: "110%"
308
372
  }
309
- });
373
+ }) : null;
310
374
  const { width, aspectRatio } = this.data;
311
- const height = this.data.height || width / aspectRatio;
375
+ const height = this.data.height ?? (aspectRatio ? width / aspectRatio : 0);
312
376
  const sizer = this.layout !== "fill" ? vueDemi.h(Sizer, {
313
377
  ...vueDemi.isVue2 && {
314
378
  props: {
@@ -349,7 +413,8 @@ const Image = vueDemi.defineComponent({
349
413
  attrs: {
350
414
  src: this.data.src,
351
415
  alt: this.data.alt,
352
- title: this.data.title
416
+ title: this.data.title,
417
+ fetchpriority: this.priority ? "high" : void 0
353
418
  },
354
419
  on: {
355
420
  load: this.handleLoad
@@ -359,16 +424,18 @@ const Image = vueDemi.defineComponent({
359
424
  src: this.data.src,
360
425
  alt: this.data.alt,
361
426
  title: this.data.title,
427
+ fetchpriority: this.priority ? "high" : void 0,
362
428
  onLoad: this.handleLoad
363
429
  },
430
+ ref: "imageRef",
364
431
  class: this.pictureClass,
365
432
  style: {
366
- ...absolutePositioning,
367
- ...this.pictureStyle,
368
433
  opacity: showImage ? 1 : 0,
369
434
  transition,
435
+ ...absolutePositioning,
370
436
  objectFit: this.objectFit,
371
- objectPosition: this.objectPosition
437
+ objectPosition: this.objectPosition,
438
+ ...this.pictureStyle
372
439
  }
373
440
  })
374
441
  ]),
@@ -390,8 +457,14 @@ const Image = vueDemi.defineComponent({
390
457
  alt: this.data.alt,
391
458
  title: this.data.title,
392
459
  class: this.pictureClass,
393
- style: toCss({ ...this.pictureStyle, ...absolutePositioning }),
394
- loading: "lazy"
460
+ style: toCss({
461
+ ...absolutePositioning,
462
+ objectFit: this.objectFit,
463
+ objectPosition: this.objectPosition,
464
+ ...this.pictureStyle
465
+ }),
466
+ loading: this.computedLazyLoad ? "lazy" : void 0,
467
+ fetchpriority: this.priority ? "high" : void 0
395
468
  })
396
469
  ])
397
470
  }
@@ -413,7 +486,8 @@ const Image = vueDemi.defineComponent({
413
486
  title: this.data.title,
414
487
  class: this.pictureClass,
415
488
  style: toCss({ ...this.pictureStyle, ...absolutePositioning }),
416
- loading: "lazy"
489
+ loading: this.computedLazyLoad ? "lazy" : void 0,
490
+ fetchpriority: this.priority ? "high" : void 0
417
491
  })
418
492
  ])
419
493
  }
@@ -589,46 +663,34 @@ const DatocmsStructuredTextPlugin = {
589
663
  }
590
664
  };
591
665
 
592
- function useQuerySubscription(options) {
593
- const { enabled, initialData, ...other } = options;
666
+ const useQuerySubscription = ({ enabled = true, initialData, query, token, ...other }) => {
594
667
  const error = vueDemi.ref(null);
595
- const data = vueDemi.ref(null);
596
- const status = vueDemi.ref(
597
- enabled ? "connecting" : "closed"
598
- );
668
+ const data = vueDemi.ref(vueDemi.unref(initialData) || null);
669
+ const status = vueDemi.ref(vueDemi.unref(enabled) ? "connecting" : "closed");
599
670
  const subscribeToQueryOptions = other;
600
- vueDemi.watchEffect((onCleanup) => {
601
- if (enabled === false) {
602
- status.value = "closed";
603
- return () => {
604
- };
605
- }
606
- let unsubscribe;
607
- async function subscribe() {
608
- unsubscribe = await datocmsListen.subscribeToQuery({
671
+ vueDemi.watchEffect(async (onCleanup) => {
672
+ if (query && token && vueDemi.unref(enabled)) {
673
+ const unsubscribe = await datocmsListen.subscribeToQuery({
609
674
  ...subscribeToQueryOptions,
675
+ query,
676
+ token,
610
677
  onStatusChange: (connectionStatus) => {
611
678
  status.value = connectionStatus;
612
679
  },
613
- onUpdate: (updateData) => {
680
+ onUpdate: ({ response }) => {
614
681
  error.value = null;
615
- data.value = updateData.response.data;
682
+ data.value = response.data;
616
683
  },
617
684
  onChannelError: (errorData) => {
618
685
  data.value = null;
619
686
  error.value = errorData;
620
687
  }
621
688
  });
689
+ onCleanup(unsubscribe);
622
690
  }
623
- subscribe();
624
- onCleanup(() => {
625
- if (unsubscribe) {
626
- unsubscribe();
627
- }
628
- });
629
691
  });
630
- return { error, status, data: data || initialData };
631
- }
692
+ return { data, status, error };
693
+ };
632
694
 
633
695
  const highlightPieces = (textWithHighlightMarker) => {
634
696
  return textWithHighlightMarker.split(/\[h\](.+?)\[\/h\]/g).map((text, index) => ({
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { Options, ConnectionStatus } from 'datocms-listen';
8
8
 
9
9
  declare type ResponsiveImageType = {
10
10
  /** The aspect ratio (width/height) of the image */
11
- aspectRatio: number;
11
+ aspectRatio?: number;
12
12
  /** A base64-encoded thumbnail to offer during image loading */
13
13
  base64?: string;
14
14
  /** The height of the image */
@@ -100,11 +100,47 @@ declare const Image: vue_demi.DefineComponent<{
100
100
  objectPosition: {
101
101
  type: StringConstructor;
102
102
  };
103
+ /** Whether the component should use a blurred image placeholder */
104
+ usePlaceholder: {
105
+ type: BooleanConstructor;
106
+ default: boolean;
107
+ };
108
+ /**
109
+ * The HTML5 `sizes` attribute for the image
110
+ *
111
+ * Learn more about srcset and sizes:
112
+ * -> https://web.dev/learn/design/responsive-images/#sizes
113
+ * -> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes
114
+ **/
115
+ sizes: {
116
+ type: StringConstructor;
117
+ };
118
+ /**
119
+ * When true, the image will be considered high priority. Lazy loading is automatically disabled, and fetchpriority="high" is added to the image.
120
+ * You should use the priority property on any image detected as the Largest Contentful Paint (LCP) element. It may be appropriate to have multiple priority images, as different images may be the LCP element for different viewport sizes.
121
+ * Should only be used when the image is visible above the fold.
122
+ **/
123
+ priority: {
124
+ type: BooleanConstructor;
125
+ default: boolean;
126
+ };
127
+ /**
128
+ * If `data` does not contain `srcSet`, the candidates for the `srcset` of the image will be auto-generated based on these width multipliers
129
+ *
130
+ * Default candidate multipliers are [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]
131
+ **/
132
+ srcSetCandidates: {
133
+ type: ArrayConstructor;
134
+ validator: (values: any[]) => values is number[];
135
+ default: () => number[];
136
+ };
103
137
  }, {
104
138
  inView: vue_demi.Ref<boolean>;
105
139
  elRef: vue_demi.Ref<HTMLElement | null>;
106
140
  loaded: vue_demi.Ref<boolean>;
107
141
  handleLoad: () => void;
142
+ computedLazyLoad: vue_demi.Ref<boolean>;
143
+ imageRef: vue_demi.Ref<HTMLImageElement | undefined>;
108
144
  }, unknown, {}, {}, vue_demi.ComponentOptionsMixin, vue_demi.ComponentOptionsMixin, {}, string, vue_demi.VNodeProps & vue_demi.AllowedComponentProps & vue_demi.ComponentCustomProps, Readonly<vue_demi.ExtractPropTypes<{
109
145
  /** The actual response you get from a DatoCMS `responsiveImage` GraphQL query */
110
146
  data: {
@@ -175,6 +211,40 @@ declare const Image: vue_demi.DefineComponent<{
175
211
  objectPosition: {
176
212
  type: StringConstructor;
177
213
  };
214
+ /** Whether the component should use a blurred image placeholder */
215
+ usePlaceholder: {
216
+ type: BooleanConstructor;
217
+ default: boolean;
218
+ };
219
+ /**
220
+ * The HTML5 `sizes` attribute for the image
221
+ *
222
+ * Learn more about srcset and sizes:
223
+ * -> https://web.dev/learn/design/responsive-images/#sizes
224
+ * -> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes
225
+ **/
226
+ sizes: {
227
+ type: StringConstructor;
228
+ };
229
+ /**
230
+ * When true, the image will be considered high priority. Lazy loading is automatically disabled, and fetchpriority="high" is added to the image.
231
+ * You should use the priority property on any image detected as the Largest Contentful Paint (LCP) element. It may be appropriate to have multiple priority images, as different images may be the LCP element for different viewport sizes.
232
+ * Should only be used when the image is visible above the fold.
233
+ **/
234
+ priority: {
235
+ type: BooleanConstructor;
236
+ default: boolean;
237
+ };
238
+ /**
239
+ * If `data` does not contain `srcSet`, the candidates for the `srcset` of the image will be auto-generated based on these width multipliers
240
+ *
241
+ * Default candidate multipliers are [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]
242
+ **/
243
+ srcSetCandidates: {
244
+ type: ArrayConstructor;
245
+ validator: (values: any[]) => values is number[];
246
+ default: () => number[];
247
+ };
178
248
  }>>, {
179
249
  explicitWidth: boolean;
180
250
  lazyLoad: boolean;
@@ -183,6 +253,9 @@ declare const Image: vue_demi.DefineComponent<{
183
253
  rootStyle: Record<string, any>;
184
254
  pictureStyle: Record<string, any>;
185
255
  layout: string;
256
+ usePlaceholder: boolean;
257
+ priority: boolean;
258
+ srcSetCandidates: unknown[];
186
259
  }>;
187
260
  declare const DatocmsImagePlugin: {
188
261
  install: (Vue: any) => void;
@@ -308,26 +381,26 @@ declare const DatocmsStructuredTextPlugin: {
308
381
  declare type SubscribeToQueryOptions<QueryResult, QueryVariables> = Omit<Options<QueryResult, QueryVariables>, 'onStatusChange' | 'onUpdate' | 'onChannelError'>;
309
382
  declare type EnabledQueryListenerOptions<QueryResult, QueryVariables> = {
310
383
  /** Whether the subscription has to be performed or not */
311
- enabled?: true;
384
+ enabled?: true | Ref<boolean>;
312
385
  /** The initial data to use while the initial request is being performed */
313
386
  initialData?: QueryResult;
314
387
  } & SubscribeToQueryOptions<QueryResult, QueryVariables>;
315
388
  declare type DisabledQueryListenerOptions<QueryResult, QueryVariables> = {
316
389
  /** Whether the subscription has to be performed or not */
317
- enabled: false;
390
+ enabled: false | Ref<boolean>;
318
391
  /** The initial data to use while the initial request is being performed */
319
392
  initialData?: QueryResult;
320
393
  } & Partial<SubscribeToQueryOptions<QueryResult, QueryVariables>>;
321
394
  declare type QueryListenerOptions<QueryResult, QueryVariables> = EnabledQueryListenerOptions<QueryResult, QueryVariables> | DisabledQueryListenerOptions<QueryResult, QueryVariables>;
322
- declare function useQuerySubscription<QueryResult = any, QueryVariables = Record<string, any>>(options: QueryListenerOptions<QueryResult, QueryVariables>): {
395
+ declare const useQuerySubscription: <QueryResult = any, QueryVariables = Record<string, any>>({ enabled, initialData, query, token, ...other }: QueryListenerOptions<QueryResult, QueryVariables>) => {
396
+ data: Ref<vue_demi.UnwrapRef<NonNullable<QueryResult>> | null>;
397
+ status: Ref<ConnectionStatus>;
323
398
  error: Ref<{
324
399
  code: string;
325
400
  message: string;
326
401
  fatal: boolean;
327
402
  response?: any;
328
403
  } | null>;
329
- status: Ref<ConnectionStatus>;
330
- data: Ref<QueryResult | null>;
331
404
  };
332
405
 
333
406
  declare type SearchResultInstancesHrefSchema = {
@@ -1,5 +1,5 @@
1
1
  import hypenateStyleName from 'hyphenate-style-name';
2
- import { ref, onMounted, onBeforeUnmount, defineComponent, h, isVue2, isVue3, watchEffect, reactive, computed, toRaw } from 'vue-demi';
2
+ import { ref, onMounted, onBeforeUnmount, defineComponent, h, isVue2, isVue3, watchEffect, unref, reactive, computed, toRaw } from 'vue-demi';
3
3
  import { render, renderNodeRule, defaultMetaTransformer } from 'datocms-structured-text-generic-html-renderer';
4
4
  export { renderMarkRule, renderNodeRule, renderNodeRule as renderRule } from 'datocms-structured-text-generic-html-renderer';
5
5
  import { isRoot, isInlineItem, RenderError, isStructuredText, isItemLink, isBlock } from 'datocms-structured-text-utils';
@@ -184,6 +184,36 @@ const imageShowStrategy = ({ lazyLoad, loaded }) => {
184
184
  }
185
185
  return true;
186
186
  };
187
+ const buildSrcSet = (src, width, candidateMultipliers) => {
188
+ if (!src || !width) {
189
+ return void 0;
190
+ }
191
+ return candidateMultipliers.map((multiplier) => {
192
+ const url = new URL(src);
193
+ if (multiplier !== 1) {
194
+ url.searchParams.set("dpr", `${multiplier}`);
195
+ const maxH = url.searchParams.get("max-h");
196
+ const maxW = url.searchParams.get("max-w");
197
+ if (maxH) {
198
+ url.searchParams.set(
199
+ "max-h",
200
+ `${Math.floor(parseInt(maxH) * multiplier)}`
201
+ );
202
+ }
203
+ if (maxW) {
204
+ url.searchParams.set(
205
+ "max-w",
206
+ `${Math.floor(parseInt(maxW) * multiplier)}`
207
+ );
208
+ }
209
+ }
210
+ const finalWidth = Math.floor(width * multiplier);
211
+ if (finalWidth < 50) {
212
+ return null;
213
+ }
214
+ return `${url.toString()} ${finalWidth}w`;
215
+ }).filter(Boolean).join(",");
216
+ };
187
217
  const Image = defineComponent({
188
218
  name: "DatocmsImage",
189
219
  props: {
@@ -233,6 +263,24 @@ const Image = defineComponent({
233
263
  },
234
264
  objectPosition: {
235
265
  type: String
266
+ },
267
+ usePlaceholder: {
268
+ type: Boolean,
269
+ default: true
270
+ },
271
+ sizes: {
272
+ type: String
273
+ },
274
+ priority: {
275
+ type: Boolean,
276
+ default: false
277
+ },
278
+ srcSetCandidates: {
279
+ type: Array,
280
+ validator: (values) => values.every((value) => {
281
+ return typeof value === "number";
282
+ }),
283
+ default: () => [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]
236
284
  }
237
285
  },
238
286
  setup(props) {
@@ -244,21 +292,33 @@ const Image = defineComponent({
244
292
  threshold: props.intersectionThreshold || props.intersectionTreshold || 0,
245
293
  rootMargin: props.intersectionMargin || "0px 0px 0px 0px"
246
294
  });
295
+ const computedLazyLoad = ref(props.priority ? false : props.lazyLoad);
296
+ const imageRef = ref();
297
+ watchEffect(() => {
298
+ if (!imageRef.value) {
299
+ return;
300
+ }
301
+ if (imageRef.value.complete && imageRef.value.naturalWidth) {
302
+ handleLoad();
303
+ }
304
+ });
247
305
  return {
248
306
  inView,
249
307
  elRef,
250
308
  loaded,
251
- handleLoad
309
+ handleLoad,
310
+ computedLazyLoad,
311
+ imageRef
252
312
  };
253
313
  },
254
314
  render() {
255
315
  const addImage = imageAddStrategy({
256
- lazyLoad: this.lazyLoad,
316
+ lazyLoad: this.computedLazyLoad,
257
317
  inView: this.inView,
258
318
  loaded: this.loaded
259
319
  });
260
320
  const showImage = imageShowStrategy({
261
- lazyLoad: this.lazyLoad,
321
+ lazyLoad: this.computedLazyLoad,
262
322
  inView: this.inView,
263
323
  loaded: this.loaded
264
324
  });
@@ -266,30 +326,30 @@ const Image = defineComponent({
266
326
  ...isVue2 && {
267
327
  props: {
268
328
  srcset: this.data.webpSrcSet,
269
- sizes: this.data.sizes,
329
+ sizes: this.sizes ?? this.data.sizes ?? void 0,
270
330
  type: "image/webp"
271
331
  }
272
332
  },
273
333
  ...isVue3 && {
274
334
  srcset: this.data.webpSrcSet,
275
- sizes: this.data.sizes,
335
+ sizes: this.sizes ?? this.data.sizes ?? void 0,
276
336
  type: "image/webp"
277
337
  }
278
338
  });
279
339
  const regularSource = this.data.srcSet && h(Source, {
280
340
  ...isVue2 && {
281
341
  props: {
282
- srcset: this.data.srcSet,
283
- sizes: this.data.sizes
342
+ srcset: this.data.srcSet ?? buildSrcSet(this.data.src, this.data.width, this.srcSetCandidates),
343
+ sizes: this.sizes ?? this.data.sizes ?? void 0
284
344
  }
285
345
  },
286
346
  ...isVue3 && {
287
- srcset: this.data.srcSet,
288
- sizes: this.data.sizes
347
+ srcset: this.data.srcSet ?? buildSrcSet(this.data.src, this.data.width, this.srcSetCandidates),
348
+ sizes: this.sizes ?? this.data.sizes ?? void 0
289
349
  }
290
350
  });
291
351
  const transition = typeof this.fadeInDuration === "undefined" || this.fadeInDuration > 0 ? `opacity ${this.fadeInDuration || 500}ms ${this.fadeInDuration || 500}ms` : void 0;
292
- const placeholder = h("div", {
352
+ const placeholder = this.usePlaceholder && (this.data.bgColor || this.data.base64) ? h("div", {
293
353
  style: {
294
354
  backgroundImage: this.data.base64 ? `url(${this.data.base64})` : null,
295
355
  backgroundColor: this.data.bgColor,
@@ -298,11 +358,15 @@ const Image = defineComponent({
298
358
  objectFit: this.objectFit,
299
359
  objectPosition: this.objectPosition,
300
360
  transition,
301
- ...absolutePositioning
361
+ position: "absolute",
362
+ left: "-5%",
363
+ top: "-5%",
364
+ width: "110%",
365
+ height: "110%"
302
366
  }
303
- });
367
+ }) : null;
304
368
  const { width, aspectRatio } = this.data;
305
- const height = this.data.height || width / aspectRatio;
369
+ const height = this.data.height ?? (aspectRatio ? width / aspectRatio : 0);
306
370
  const sizer = this.layout !== "fill" ? h(Sizer, {
307
371
  ...isVue2 && {
308
372
  props: {
@@ -343,7 +407,8 @@ const Image = defineComponent({
343
407
  attrs: {
344
408
  src: this.data.src,
345
409
  alt: this.data.alt,
346
- title: this.data.title
410
+ title: this.data.title,
411
+ fetchpriority: this.priority ? "high" : void 0
347
412
  },
348
413
  on: {
349
414
  load: this.handleLoad
@@ -353,16 +418,18 @@ const Image = defineComponent({
353
418
  src: this.data.src,
354
419
  alt: this.data.alt,
355
420
  title: this.data.title,
421
+ fetchpriority: this.priority ? "high" : void 0,
356
422
  onLoad: this.handleLoad
357
423
  },
424
+ ref: "imageRef",
358
425
  class: this.pictureClass,
359
426
  style: {
360
- ...absolutePositioning,
361
- ...this.pictureStyle,
362
427
  opacity: showImage ? 1 : 0,
363
428
  transition,
429
+ ...absolutePositioning,
364
430
  objectFit: this.objectFit,
365
- objectPosition: this.objectPosition
431
+ objectPosition: this.objectPosition,
432
+ ...this.pictureStyle
366
433
  }
367
434
  })
368
435
  ]),
@@ -384,8 +451,14 @@ const Image = defineComponent({
384
451
  alt: this.data.alt,
385
452
  title: this.data.title,
386
453
  class: this.pictureClass,
387
- style: toCss({ ...this.pictureStyle, ...absolutePositioning }),
388
- loading: "lazy"
454
+ style: toCss({
455
+ ...absolutePositioning,
456
+ objectFit: this.objectFit,
457
+ objectPosition: this.objectPosition,
458
+ ...this.pictureStyle
459
+ }),
460
+ loading: this.computedLazyLoad ? "lazy" : void 0,
461
+ fetchpriority: this.priority ? "high" : void 0
389
462
  })
390
463
  ])
391
464
  }
@@ -407,7 +480,8 @@ const Image = defineComponent({
407
480
  title: this.data.title,
408
481
  class: this.pictureClass,
409
482
  style: toCss({ ...this.pictureStyle, ...absolutePositioning }),
410
- loading: "lazy"
483
+ loading: this.computedLazyLoad ? "lazy" : void 0,
484
+ fetchpriority: this.priority ? "high" : void 0
411
485
  })
412
486
  ])
413
487
  }
@@ -583,46 +657,34 @@ const DatocmsStructuredTextPlugin = {
583
657
  }
584
658
  };
585
659
 
586
- function useQuerySubscription(options) {
587
- const { enabled, initialData, ...other } = options;
660
+ const useQuerySubscription = ({ enabled = true, initialData, query, token, ...other }) => {
588
661
  const error = ref(null);
589
- const data = ref(null);
590
- const status = ref(
591
- enabled ? "connecting" : "closed"
592
- );
662
+ const data = ref(unref(initialData) || null);
663
+ const status = ref(unref(enabled) ? "connecting" : "closed");
593
664
  const subscribeToQueryOptions = other;
594
- watchEffect((onCleanup) => {
595
- if (enabled === false) {
596
- status.value = "closed";
597
- return () => {
598
- };
599
- }
600
- let unsubscribe;
601
- async function subscribe() {
602
- unsubscribe = await subscribeToQuery({
665
+ watchEffect(async (onCleanup) => {
666
+ if (query && token && unref(enabled)) {
667
+ const unsubscribe = await subscribeToQuery({
603
668
  ...subscribeToQueryOptions,
669
+ query,
670
+ token,
604
671
  onStatusChange: (connectionStatus) => {
605
672
  status.value = connectionStatus;
606
673
  },
607
- onUpdate: (updateData) => {
674
+ onUpdate: ({ response }) => {
608
675
  error.value = null;
609
- data.value = updateData.response.data;
676
+ data.value = response.data;
610
677
  },
611
678
  onChannelError: (errorData) => {
612
679
  data.value = null;
613
680
  error.value = errorData;
614
681
  }
615
682
  });
683
+ onCleanup(unsubscribe);
616
684
  }
617
- subscribe();
618
- onCleanup(() => {
619
- if (unsubscribe) {
620
- unsubscribe();
621
- }
622
- });
623
685
  });
624
- return { error, status, data: data || initialData };
625
- }
686
+ return { data, status, error };
687
+ };
626
688
 
627
689
  const highlightPieces = (textWithHighlightMarker) => {
628
690
  return textWithHighlightMarker.split(/\[h\](.+?)\[\/h\]/g).map((text, index) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-datocms",
3
- "version": "3.0.3",
3
+ "version": "4.0.1",
4
4
  "description": "A set of components and utilities to work faster with DatoCMS in Vue.js environments",
5
5
  "keywords": [
6
6
  "datocms",
@@ -11,7 +11,16 @@
11
11
  "main": "./dist/index.cjs.js",
12
12
  "module": "./dist/index.esm.mjs",
13
13
  "types": "./dist/index.d.ts",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git://github.com/datocms/vue-datocms.git"
17
+ },
14
18
  "license": "MIT",
19
+ "author": "Stefano Verna <s.verna@datocms.com>",
20
+ "contributors": [
21
+ "Silvano Stralla <silvano@datocms.com>"
22
+ ],
23
+ "homepage": "https://github.com/datocms/vue-datocms",
15
24
  "exports": {
16
25
  ".": {
17
26
  "import": "./dist/index.esm.mjs",