@prismicio/vue 3.0.0-alpha.5 → 3.0.0-beta.2

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.
@@ -3,15 +3,24 @@ import {
3
3
  ComponentCustomProps,
4
4
  computed,
5
5
  ConcreteComponent,
6
+ ComputedRef,
6
7
  defineComponent,
7
8
  h,
8
9
  PropType,
9
10
  VNodeProps,
11
+ unref,
10
12
  } from "vue";
11
13
 
12
14
  import { ImageField } from "@prismicio/types";
15
+ import {
16
+ asImageSrc,
17
+ asImageWidthSrcSet,
18
+ asImagePixelDensitySrcSet,
19
+ isFilled,
20
+ } from "@prismicio/helpers";
13
21
 
14
22
  import { usePrismic } from "../usePrismic";
23
+ import { VueUseOptions } from "../types";
15
24
  import { simplyResolveComponent } from "../lib/simplyResolveComponent";
16
25
 
17
26
  /**
@@ -33,17 +42,165 @@ export type PrismicImageProps = {
33
42
  *
34
43
  * @remarks
35
44
  * HTML tag names and components will be rendered using the `img` tag
36
- * interface (`src` and `alt` attribute). Components will also receive an
37
- * additional `copyright` props.
45
+ * interface (`src`, `srcset`, and `alt` attribute). Components will also
46
+ * receive an additional `copyright` props.
38
47
  * @defaultValue The one provided to `@prismicio/vue` plugin if configured, `"img"` otherwise.
39
48
  */
40
49
  imageComponent?: string | ConcreteComponent;
41
50
 
42
51
  /**
43
- * A map of additional props to pass to the component used to render images
44
- * when using one.
52
+ * An object of Imgix URL API parameters.
53
+ *
54
+ * @see Imgix URL parameters reference: https://docs.imgix.com/apis/rendering
55
+ */
56
+ imgixParams?: Parameters<typeof asImageSrc>[1];
57
+
58
+ /**
59
+ * Adds an additional `srcset` attribute to the image following given widths.
60
+ *
61
+ * @remarks
62
+ * A special value of `"auto"` is accepted to automatically use image widths
63
+ * coming from the API.
64
+ * @remarks
65
+ * A special value of `"defaults"` is accepted to automatically use image
66
+ * widths coming from `@prismicio/helpers`
67
+ * @remarks
68
+ * This prop is not compatible with the `pixelDensities` prop. When both are
69
+ * used the `pixelDensities` prop will be ignored.
70
+ */
71
+ widths?:
72
+ | NonNullable<Parameters<typeof asImageWidthSrcSet>[1]>["widths"]
73
+ | "auto"
74
+ | "defaults";
75
+
76
+ /**
77
+ * Adds an additional `srcset` attribute to the image following giving pixel densities.
78
+ *
79
+ * @remarks
80
+ * A special value of `"defaults"` is accepted to automatically use image
81
+ * pixel densities coming from `@prismicio/helpers`
82
+ * @remarks
83
+ * This prop is not compatible with the `widths` prop. When both are used, the
84
+ * `pixelDensities` prop will be ignored.
85
+ */
86
+ pixelDensities?:
87
+ | NonNullable<
88
+ Parameters<typeof asImagePixelDensitySrcSet>[1]
89
+ >["pixelDensities"]
90
+ | "defaults";
91
+ };
92
+
93
+ /**
94
+ * Options for {@link usePrismicImage}.
95
+ */
96
+ export type UsePrismicImageOptions = VueUseOptions<
97
+ Omit<PrismicImageProps, "imageComponent">
98
+ >;
99
+
100
+ /**
101
+ * Return type of {@link usePrismicImage}.
102
+ */
103
+ export type UsePrismicImageReturnType = {
104
+ /**
105
+ * Resolved image `src` value.
106
+ */
107
+ src: ComputedRef<string | null>;
108
+
109
+ /**
110
+ * Resolved image `srcset` value.
111
+ */
112
+ srcset: ComputedRef<string | null>;
113
+
114
+ /**
115
+ * Resolved image `alt` value.
45
116
  */
46
- imageComponentAdditionalProps?: Record<string, unknown>;
117
+ alt: ComputedRef<string | null>;
118
+
119
+ /**
120
+ * Resolved image `copyright` value.
121
+ */
122
+ copyright: ComputedRef<string | null>;
123
+ };
124
+
125
+ /**
126
+ * A low level composable that returns a resolved information about a Prismic image field.
127
+ *
128
+ * @param props - {@link UsePrismicImageOptions}
129
+ *
130
+ * @returns - Resolved image information {@link UsePrismicImageReturnType}
131
+ */
132
+ export const usePrismicImage = (
133
+ props: UsePrismicImageOptions,
134
+ ): UsePrismicImageReturnType => {
135
+ const asImage = computed(() => {
136
+ const field = unref(props.field);
137
+
138
+ if (!isFilled.image(field)) {
139
+ return {
140
+ src: null,
141
+ srcset: null,
142
+ };
143
+ }
144
+
145
+ const imgixParams = unref(props.imgixParams);
146
+ const widths = unref(props.widths);
147
+ const pixelDensities = unref(props.pixelDensities);
148
+
149
+ if (widths) {
150
+ if (pixelDensities) {
151
+ console.warn(
152
+ "[PrismicImage] `widths` and `pixelDensities` props should not be use alongside each others, only `widths` will be applied",
153
+ props,
154
+ );
155
+ }
156
+
157
+ if (widths === "auto") {
158
+ return asImageWidthSrcSet(field, imgixParams);
159
+ } else {
160
+ // Remove potential thumbnails when using manual widths
161
+ const { url, dimensions, alt, copyright } = field;
162
+
163
+ return asImageWidthSrcSet(
164
+ { url, dimensions, alt, copyright },
165
+ {
166
+ ...imgixParams,
167
+ widths: widths === "defaults" ? undefined : widths,
168
+ },
169
+ );
170
+ }
171
+ } else if (pixelDensities) {
172
+ return asImagePixelDensitySrcSet(field, {
173
+ ...imgixParams,
174
+ pixelDensities:
175
+ pixelDensities === "defaults" ? undefined : pixelDensities,
176
+ });
177
+ } else {
178
+ return {
179
+ src: asImageSrc(field, imgixParams),
180
+ srcset: null,
181
+ };
182
+ }
183
+ });
184
+
185
+ const src = computed(() => {
186
+ return asImage.value.src;
187
+ });
188
+ const srcset = computed(() => {
189
+ return asImage.value.srcset;
190
+ });
191
+ const alt = computed(() => {
192
+ return unref(props.field).alt || null;
193
+ });
194
+ const copyright = computed(() => {
195
+ return unref(props.field).copyright || null;
196
+ });
197
+
198
+ return {
199
+ src,
200
+ srcset,
201
+ alt,
202
+ copyright,
203
+ };
47
204
  };
48
205
 
49
206
  /**
@@ -63,8 +220,27 @@ export const PrismicImageImpl = /*#__PURE__*/ defineComponent({
63
220
  default: undefined,
64
221
  required: false,
65
222
  },
66
- imageComponentAdditionalProps: {
67
- type: Object as PropType<Record<string, unknown>>,
223
+ imgixParams: {
224
+ type: Object as PropType<Parameters<typeof asImageSrc>[1]>,
225
+ default: undefined,
226
+ required: false,
227
+ },
228
+ widths: {
229
+ type: [String, Object] as PropType<
230
+ | NonNullable<Parameters<typeof asImageWidthSrcSet>[1]>["widths"]
231
+ | "auto"
232
+ | "defaults"
233
+ >,
234
+ default: undefined,
235
+ required: false,
236
+ },
237
+ pixelDensities: {
238
+ type: [String, Object] as PropType<
239
+ | NonNullable<
240
+ Parameters<typeof asImagePixelDensitySrcSet>[1]
241
+ >["pixelDensities"]
242
+ | "defaults"
243
+ >,
68
244
  default: undefined,
69
245
  required: false,
70
246
  },
@@ -85,10 +261,13 @@ export const PrismicImageImpl = /*#__PURE__*/ defineComponent({
85
261
  );
86
262
  });
87
263
 
264
+ const { src, srcset, alt, copyright } = usePrismicImage(props);
265
+
88
266
  return () => {
89
267
  const attributes = {
90
- src: props.field.url || null,
91
- alt: props.field.alt || null,
268
+ src: src.value,
269
+ srcset: srcset.value,
270
+ alt: alt.value,
92
271
  };
93
272
 
94
273
  switch (type.value) {
@@ -99,8 +278,7 @@ export const PrismicImageImpl = /*#__PURE__*/ defineComponent({
99
278
  default:
100
279
  return h(simplyResolveComponent(type.value), {
101
280
  ...attributes,
102
- copyright: props.field.copyright || null,
103
- ...props.imageComponentAdditionalProps,
281
+ copyright: copyright.value,
104
282
  });
105
283
  }
106
284
  };
@@ -50,6 +50,7 @@ export type PrismicLinkProps = {
50
50
  * resolver parameter with `@prismicio/client`.
51
51
  *
52
52
  * @defaultValue The link resolver provided to `@prismicio/vue` plugin if configured.
53
+ *
53
54
  * @see Link resolver documentation {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver#link-resolver}
54
55
  */
55
56
  linkResolver?: LinkResolverFunction;
@@ -57,19 +58,19 @@ export type PrismicLinkProps = {
57
58
  /**
58
59
  * An explicit `target` attribute to apply to the rendered link.
59
60
  */
60
- target?: string;
61
+ target?: string | null;
61
62
 
62
63
  /**
63
64
  * An explicit `rel` attribute to apply to the rendered link.
64
65
  */
65
- rel?: string;
66
+ rel?: string | null;
66
67
 
67
68
  /**
68
69
  * Value of the `rel` attribute to use on links rendered with `target="_blank"`.
69
70
  *
70
71
  * @defaultValue The one provided to `@prismicio/vue` plugin if configured, `"noopener noreferrer"` otherwise.
71
72
  */
72
- blankTargetRelAttribute?: string;
73
+ blankTargetRelAttribute?: string | null;
73
74
 
74
75
  /**
75
76
  * An HTML tag name, a component, or a functional component used to render
@@ -165,21 +166,33 @@ export const usePrismicLink = (
165
166
  });
166
167
  const target = computed(() => {
167
168
  const field = unref(props.field);
169
+ const target = unref(props.target);
168
170
 
169
- return (
170
- unref(props.target) ||
171
- (field && "target" in field && field.target ? field.target : null)
172
- );
171
+ if (typeof target !== "undefined") {
172
+ return target;
173
+ } else {
174
+ return field && "target" in field && field.target ? field.target : null;
175
+ }
173
176
  });
174
177
  const rel = computed(() => {
175
- return (
176
- unref(props.rel) ||
177
- (target.value === "_blank"
178
- ? unref(props.blankTargetRelAttribute) ||
179
- options.components?.linkBlankTargetRelAttribute ||
180
- defaultBlankTargetRelAttribute
181
- : null)
182
- );
178
+ const rel = unref(props.rel);
179
+
180
+ if (typeof rel !== "undefined") {
181
+ return rel;
182
+ } else if (target.value === "_blank") {
183
+ const blankTargetRelAttribute = unref(props.blankTargetRelAttribute);
184
+
185
+ if (typeof blankTargetRelAttribute !== "undefined") {
186
+ return blankTargetRelAttribute;
187
+ } else {
188
+ return typeof options.components?.linkBlankTargetRelAttribute !==
189
+ "undefined"
190
+ ? options.components.linkBlankTargetRelAttribute
191
+ : defaultBlankTargetRelAttribute;
192
+ }
193
+ } else {
194
+ return null;
195
+ }
183
196
  });
184
197
 
185
198
  return {
@@ -208,17 +221,17 @@ export const PrismicLinkImpl = /*#__PURE__*/ defineComponent({
208
221
  required: false,
209
222
  },
210
223
  target: {
211
- type: String as PropType<string>,
224
+ type: String as PropType<string | null>,
212
225
  default: undefined,
213
226
  required: false,
214
227
  },
215
228
  rel: {
216
- type: String as PropType<string>,
229
+ type: String as PropType<string | null>,
217
230
  default: undefined,
218
231
  required: false,
219
232
  },
220
233
  blankTargetRelAttribute: {
221
- type: String as PropType<string>,
234
+ type: String as PropType<string | null>,
222
235
  default: undefined,
223
236
  required: false,
224
237
  },
@@ -50,6 +50,7 @@ export type PrismicRichTextProps = {
50
50
  * resolver parameter with `@prismicio/client`.
51
51
  *
52
52
  * @defaultValue The link resolver provided to `@prismicio/vue` plugin if configured.
53
+ *
53
54
  * @see Link resolver documentation {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver#link-resolver}
54
55
  */
55
56
  linkResolver?: LinkResolverFunction;
@@ -58,6 +59,7 @@ export type PrismicRichTextProps = {
58
59
  * An HTML serializer to customize the way rich text fields are rendered.
59
60
  *
60
61
  * @defaultValue The HTML serializer provided to `@prismicio/vue` plugin if configured.
62
+ *
61
63
  * @see HTML serializer documentation {@link https://prismic.io/docs/core-concepts/html-serializer}
62
64
  */
63
65
  htmlSerializer?: HTMLFunctionSerializer | HTMLMapSerializer;
@@ -104,7 +106,8 @@ export const usePrismicRichText = (
104
106
  const htmlSerializer =
105
107
  unref(props.htmlSerializer) ?? options.htmlSerializer;
106
108
 
107
- return asHTML(unref(props.field), linkResolver, htmlSerializer);
109
+ // TODO: Update after https://github.com/prismicio/prismic-helpers/pull/43
110
+ return asHTML(unref(props.field), linkResolver, htmlSerializer) || "";
108
111
  });
109
112
 
110
113
  return {
@@ -22,7 +22,6 @@ import { simplyResolveComponent } from "../lib/simplyResolveComponent";
22
22
  * The default component rendered to wrap the text output.
23
23
  */
24
24
  const defaultWrapper = "div";
25
-
26
25
  /**
27
26
  * Props for `<PrismicText />`.
28
27
  */
@@ -75,7 +74,8 @@ export const usePrismicText = (
75
74
  props: UsePrismicTextOptions,
76
75
  ): UsePrismicTextReturnType => {
77
76
  const text = computed(() => {
78
- return asText(unref(props.field), unref(props.separator));
77
+ // TODO: Update after https://github.com/prismicio/prismic-helpers/pull/43
78
+ return asText(unref(props.field), unref(props.separator)) || "";
79
79
  });
80
80
 
81
81
  return {
@@ -149,7 +149,7 @@ export const getSliceComponentProps = <
149
149
  TContext = unknown,
150
150
  >(
151
151
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
152
- propsHint?: string[],
152
+ propsHint?: ["slice", "index", "slices", "context"],
153
153
  ): DefineComponentSliceComponentProps<TSlice, TContext> => ({
154
154
  slice: {
155
155
  type: Object as PropType<SliceComponentProps<TSlice, TContext>["slice"]>,
@@ -254,11 +254,11 @@ export type SliceZoneComponents<
254
254
  * @example Defining a slice components:
255
255
  *
256
256
  * ```javascript
257
- * import { getSliceZoneComponents } from "@prismicio/vue";
257
+ * import { defineSliceZoneComponents } from "@prismicio/vue";
258
258
  *
259
259
  * export default {
260
260
  * data() {
261
- * components: getSliceZoneComponents({
261
+ * components: defineSliceZoneComponents({
262
262
  * foo: Foo,
263
263
  * bar: defineAsyncComponent(
264
264
  * () => new Promise((res) => res(Bar)),
@@ -271,11 +271,12 @@ export type SliceZoneComponents<
271
271
  *
272
272
  * @typeParam TSlice - The type(s) of slices in the Slice Zone
273
273
  * @typeParam TContext - Arbitrary data made available to all Slice components
274
+ *
274
275
  * @param components - {@link SliceZoneComponents}
275
276
  *
276
277
  * @returns A new optimized record of {@link SliceZoneComponents}
277
278
  */
278
- export const getSliceZoneComponents = <
279
+ export const defineSliceZoneComponents = <
279
280
  TSlice extends SliceLike = SliceLike,
280
281
  TContext = unknown,
281
282
  >(
@@ -300,6 +301,44 @@ export const getSliceZoneComponents = <
300
301
  return result;
301
302
  };
302
303
 
304
+ /**
305
+ * Arguments for a `<SliceZone>` `resolver` function.
306
+ */
307
+ export type SliceZoneResolverArgs<TSlice extends SliceLike = SliceLike> = {
308
+ /**
309
+ * The Slice to resolve to a Vue component..
310
+ */
311
+ slice: TSlice;
312
+
313
+ /**
314
+ * The name of the Slice.
315
+ */
316
+ sliceName: TSlice["slice_type"];
317
+
318
+ /**
319
+ * The index of the Slice in the Slice Zone.
320
+ */
321
+ i: number;
322
+ };
323
+
324
+ /**
325
+ * A function that determines the rendered Vue component for each Slice in the
326
+ * Slice Zone. If a nullish value is returned, the component will fallback to
327
+ * the `components` or `defaultComponent` props to determine the rendered component.
328
+ *
329
+ * @deprecated Use the `components` prop instead.
330
+ *
331
+ * @param args - Arguments for the resolver function.
332
+ *
333
+ * @returns The Vue component to render for a Slice.
334
+ */
335
+ export type SliceZoneResolver<
336
+ TSlice extends SliceLike = SliceLike,
337
+ TContext = unknown,
338
+ > = (
339
+ args: SliceZoneResolverArgs<TSlice>,
340
+ ) => SliceComponentType<TSlice, TContext> | string | undefined | null;
341
+
303
342
  /**
304
343
  * Props for `<SliceZone />`.
305
344
  *
@@ -318,7 +357,19 @@ export type SliceZoneProps<
318
357
  /**
319
358
  * A record mapping Slice types to Vue components.
320
359
  */
321
- components: SliceZoneComponents;
360
+ components?: SliceZoneComponents;
361
+
362
+ /**
363
+ * A function that determines the rendered Vue component for each Slice in the
364
+ * Slice Zone.
365
+ *
366
+ * @deprecated Use the `components` prop instead.
367
+ *
368
+ * @param args - Arguments for the resolver function.
369
+ *
370
+ * @returns The Vue component to render for a Slice.
371
+ */
372
+ resolver?: SliceZoneResolver<TSlice, TContext>;
322
373
 
323
374
  /**
324
375
  * Arbitrary data made available to all Slice components.
@@ -331,6 +382,7 @@ export type SliceZoneProps<
331
382
  *
332
383
  * @remarks
333
384
  * Components will be rendered using the {@link SliceComponentProps} interface.
385
+ *
334
386
  * @defaultValue The Slice Zone default component provided to `@prismicio/vue` plugin if configured, otherwise `null` when `process.env.NODE_ENV === "production"` else {@link TODOSliceComponent}.
335
387
  */
336
388
  defaultComponent?: SliceComponentType<TSlice, TContext>;
@@ -356,7 +408,13 @@ export const SliceZoneImpl = /*#__PURE__*/ defineComponent({
356
408
  },
357
409
  components: {
358
410
  type: Object as PropType<SliceZoneComponents>,
359
- required: true,
411
+ default: undefined,
412
+ required: false,
413
+ },
414
+ resolver: {
415
+ type: Function as PropType<SliceZoneResolver>,
416
+ default: undefined,
417
+ required: false,
360
418
  },
361
419
  context: {
362
420
  type: null,
@@ -384,14 +442,28 @@ export const SliceZoneImpl = /*#__PURE__*/ defineComponent({
384
442
 
385
443
  const renderedSlices = computed(() => {
386
444
  return props.slices.map((slice, index) => {
387
- const component =
388
- slice.slice_type in props.components
445
+ let component =
446
+ props.components && slice.slice_type in props.components
389
447
  ? props.components[slice.slice_type]
390
448
  : props.defaultComponent ||
391
449
  options.components?.sliceZoneDefaultComponent ||
392
450
  TODOSliceComponent;
393
451
 
452
+ // TODO: Remove `resolver` in v3 in favor of `components`.
453
+ if (props.resolver) {
454
+ const resolvedComponent = props.resolver({
455
+ slice,
456
+ sliceName: slice.slice_type,
457
+ i: index,
458
+ });
459
+
460
+ if (resolvedComponent) {
461
+ component = resolvedComponent;
462
+ }
463
+ }
464
+
394
465
  const p = {
466
+ key: `${slice.slice_type}-${index}`,
395
467
  slice,
396
468
  index,
397
469
  context: props.context,
@@ -1,8 +1,12 @@
1
1
  export { PrismicEmbedImpl, PrismicEmbed } from "./PrismicEmbed";
2
2
  export type { PrismicEmbedProps } from "./PrismicEmbed";
3
3
 
4
- export { PrismicImageImpl, PrismicImage } from "./PrismicImage";
5
- export type { PrismicImageProps } from "./PrismicImage";
4
+ export {
5
+ usePrismicImage,
6
+ PrismicImageImpl,
7
+ PrismicImage,
8
+ } from "./PrismicImage";
9
+ export type { UsePrismicImageOptions, PrismicImageProps } from "./PrismicImage";
6
10
 
7
11
  export { usePrismicLink, PrismicLinkImpl, PrismicLink } from "./PrismicLink";
8
12
  export type { UsePrismicLinkOptions, PrismicLinkProps } from "./PrismicLink";
@@ -23,7 +27,7 @@ export type {
23
27
  export {
24
28
  getSliceComponentProps,
25
29
  TODOSliceComponent,
26
- getSliceZoneComponents,
30
+ defineSliceZoneComponents,
27
31
  SliceZoneImpl,
28
32
  SliceZone,
29
33
  } from "./SliceZone";
@@ -33,6 +37,8 @@ export type {
33
37
  SliceComponentType,
34
38
  SliceLike,
35
39
  SliceZoneComponents,
40
+ SliceZoneResolverArgs,
41
+ SliceZoneResolver,
36
42
  SliceZoneLike,
37
43
  SliceZoneProps,
38
44
  } from "./SliceZone";