@ethlete/contentful 4.0.0-next.10 → 4.0.0-next.12
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/CHANGELOG.md +27 -0
- package/README.md +6 -1
- package/fesm2022/ethlete-contentful.mjs +365 -399
- package/fesm2022/ethlete-contentful.mjs.map +1 -1
- package/generators/generators.json +5 -0
- package/generators/migrate-contentful-default-components/contentful-default-components.js +59 -0
- package/generators/migrate-contentful-default-components/migration.js +57 -0
- package/generators/migrate-contentful-default-components/schema.json +26 -0
- package/migrations.json +8 -0
- package/package.json +1 -1
- package/types/ethlete-contentful.d.ts +75 -12
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { NgClass, DOCUMENT } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input, computed, ViewEncapsulation, Component,
|
|
4
|
-
import { normalizePictureSizes, PictureComponent } from '@ethlete/components';
|
|
3
|
+
import { input, computed, ViewEncapsulation, Component, InjectionToken, inject, numberAttribute, booleanAttribute, isDevMode, ViewContainerRef, ElementRef, effect, untracked, signal, reflectComponentType, inputBinding } from '@angular/core';
|
|
4
|
+
import { formatFileSize, normalizePictureSizes, PictureComponent } from '@ethlete/components';
|
|
5
|
+
import { defineLabels, toProvideFn, toInjectFn, toToken, injectUrl, RuntimeError, isObject, injectRenderer, getObjectProperty } from '@ethlete/core';
|
|
5
6
|
import { Router, RouterLink } from '@angular/router';
|
|
6
|
-
import { RuntimeError, isObject, injectRenderer, getObjectProperty } from '@ethlete/core';
|
|
7
7
|
|
|
8
8
|
const GQL_FRAGMENT_CONTENTFUL_ASSET = `
|
|
9
9
|
fragment ContentfulAssetData on Asset {
|
|
@@ -12,6 +12,7 @@ const GQL_FRAGMENT_CONTENTFUL_ASSET = `
|
|
|
12
12
|
}
|
|
13
13
|
url
|
|
14
14
|
title
|
|
15
|
+
fileName
|
|
15
16
|
width
|
|
16
17
|
height
|
|
17
18
|
description
|
|
@@ -42,13 +43,13 @@ class ContentfulAudioComponent {
|
|
|
42
43
|
if (isContentfulGqlAsset(asset) && asset.url) {
|
|
43
44
|
return {
|
|
44
45
|
url: asset.url,
|
|
45
|
-
title: asset.title,
|
|
46
|
+
title: asset.title || asset.fileName || null,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
|
-
if (!isContentfulGqlAsset(asset) && asset.fields.file
|
|
49
|
+
if (!isContentfulGqlAsset(asset) && asset.fields.file?.url) {
|
|
49
50
|
return {
|
|
50
51
|
url: asset.fields.file.url,
|
|
51
|
-
title: asset.fields.title,
|
|
52
|
+
title: asset.fields.title || asset.fields.file.fileName || null,
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
return null;
|
|
@@ -85,8 +86,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
85
86
|
}]
|
|
86
87
|
}], propDecorators: { asset: [{ type: i0.Input, args: [{ isSignal: true, alias: "asset", required: true }] }], audioClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "audioClass", required: false }] }], figureClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "figureClass", required: false }] }], figcaptionClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "figcaptionClass", required: false }] }] } });
|
|
87
88
|
|
|
89
|
+
/** The built-in English labels. */
|
|
90
|
+
const DEFAULT_CONTENTFUL_FILE_LABELS = {
|
|
91
|
+
fileSize: (bytes) => `(${formatFileSize(bytes)})`,
|
|
92
|
+
};
|
|
93
|
+
const CONTENTFUL_FILE_LABELS_DEF = /* @__PURE__ */ defineLabels('CONTENTFUL_FILE_LABELS', DEFAULT_CONTENTFUL_FILE_LABELS);
|
|
94
|
+
/**
|
|
95
|
+
* Localize `et-contentful-file` for everything below this injector, and read the set in effect here as a signal.
|
|
96
|
+
* Partial - whatever you leave out keeps its {@link DEFAULT_CONTENTFUL_FILE_LABELS} value.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* provideContentfulFileLabels({ fileSize: (bytes) => `(${new Intl.NumberFormat('de').format(bytes)} Byte)` });
|
|
100
|
+
*/
|
|
101
|
+
const provideContentfulFileLabels = /* @__PURE__ */ toProvideFn(CONTENTFUL_FILE_LABELS_DEF);
|
|
102
|
+
const injectContentfulFileLabels = /* @__PURE__ */ toInjectFn(CONTENTFUL_FILE_LABELS_DEF);
|
|
103
|
+
const CONTENTFUL_FILE_LABELS = /* @__PURE__ */ toToken(CONTENTFUL_FILE_LABELS_DEF);
|
|
104
|
+
|
|
88
105
|
class ContentfulFileComponent {
|
|
89
106
|
constructor() {
|
|
107
|
+
this.labels = injectContentfulFileLabels();
|
|
90
108
|
this.asset = input.required(/* @ts-ignore */
|
|
91
109
|
...(ngDevMode ? [{ debugName: "asset" }] : /* istanbul ignore next */ []));
|
|
92
110
|
this.fileClass = input(null, /* @ts-ignore */
|
|
@@ -100,14 +118,14 @@ class ContentfulFileComponent {
|
|
|
100
118
|
return {
|
|
101
119
|
url: asset.url,
|
|
102
120
|
size: asset.size,
|
|
103
|
-
title: asset.title,
|
|
121
|
+
title: asset.title || asset.fileName || null,
|
|
104
122
|
};
|
|
105
123
|
}
|
|
106
|
-
if (!isContentfulGqlAsset(asset) && asset.fields.file
|
|
124
|
+
if (!isContentfulGqlAsset(asset) && asset.fields.file?.url) {
|
|
107
125
|
return {
|
|
108
126
|
url: asset.fields.file.url,
|
|
109
127
|
size: asset.fields.file.details.size,
|
|
110
|
-
title: asset.fields.title,
|
|
128
|
+
title: asset.fields.title || asset.fields.file.fileName || null,
|
|
111
129
|
};
|
|
112
130
|
}
|
|
113
131
|
return null;
|
|
@@ -120,7 +138,7 @@ class ContentfulFileComponent {
|
|
|
120
138
|
<a [href]="data.url" [ngClass]="fileClass()" target="_blank" rel="noopener noreferrer">
|
|
121
139
|
{{ data.title }}
|
|
122
140
|
@if (data.size !== null) {
|
|
123
|
-
|
|
141
|
+
{{ labels().fileSize(data.size) }}
|
|
124
142
|
}
|
|
125
143
|
</a>
|
|
126
144
|
}
|
|
@@ -135,7 +153,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
135
153
|
<a [href]="data.url" [ngClass]="fileClass()" target="_blank" rel="noopener noreferrer">
|
|
136
154
|
{{ data.title }}
|
|
137
155
|
@if (data.size !== null) {
|
|
138
|
-
|
|
156
|
+
{{ labels().fileSize(data.size) }}
|
|
139
157
|
}
|
|
140
158
|
</a>
|
|
141
159
|
}
|
|
@@ -148,160 +166,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
148
166
|
}]
|
|
149
167
|
}], propDecorators: { asset: [{ type: i0.Input, args: [{ isSignal: true, alias: "asset", required: true }] }], fileClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileClass", required: false }] }] } });
|
|
150
168
|
|
|
151
|
-
const parseWebUrl = (href) => {
|
|
152
|
-
if (!/^(?:https?:)?\/\//i.test(href)) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
return new URL(href.startsWith('//') ? 'https:' + href : href);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
const normalizeHostname = (host) => {
|
|
163
|
-
const value = host.trim().toLowerCase();
|
|
164
|
-
try {
|
|
165
|
-
return new URL(value.includes('://') ? value : 'https://' + value).hostname;
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return value.split(':')[0] ?? value;
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
const matchesHostname = (hostname, configuredHost) => {
|
|
172
|
-
const normalizedHost = normalizeHostname(configuredHost);
|
|
173
|
-
return hostname === normalizedHost || hostname.endsWith('.' + normalizedHost);
|
|
174
|
-
};
|
|
175
|
-
class ContentfulLinkComponent {
|
|
176
|
-
constructor() {
|
|
177
|
-
this.document = inject(DOCUMENT);
|
|
178
|
-
this.router = inject(Router);
|
|
179
|
-
this.config = injectContentfulConfig();
|
|
180
|
-
this.href = input.required(/* @ts-ignore */
|
|
181
|
-
...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
|
|
182
|
-
this.text = input.required(/* @ts-ignore */
|
|
183
|
-
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
184
|
-
this.textClass = input('', /* @ts-ignore */
|
|
185
|
-
...(ngDevMode ? [{ debugName: "textClass" }] : /* istanbul ignore next */ []));
|
|
186
|
-
this.usesRouterLink = computed(() => {
|
|
187
|
-
const href = this.href();
|
|
188
|
-
const absoluteUrl = parseWebUrl(href);
|
|
189
|
-
if (absoluteUrl) {
|
|
190
|
-
const internalHosts = [this.document.location.hostname, ...this.config.internalHosts];
|
|
191
|
-
return internalHosts.some((host) => matchesHostname(absoluteUrl.hostname, host));
|
|
192
|
-
}
|
|
193
|
-
return !href.startsWith('#') && !/^[a-z][a-z\d+.-]*:/i.test(href);
|
|
194
|
-
}, /* @ts-ignore */
|
|
195
|
-
...(ngDevMode ? [{ debugName: "usesRouterLink" }] : /* istanbul ignore next */ []));
|
|
196
|
-
this.openInNewTab = computed(() => Boolean(parseWebUrl(this.href())) && !this.usesRouterLink(), /* @ts-ignore */
|
|
197
|
-
...(ngDevMode ? [{ debugName: "openInNewTab" }] : /* istanbul ignore next */ []));
|
|
198
|
-
this.internalPath = computed(() => {
|
|
199
|
-
const href = this.href();
|
|
200
|
-
const url = parseWebUrl(href);
|
|
201
|
-
if (!url) {
|
|
202
|
-
return href;
|
|
203
|
-
}
|
|
204
|
-
return url.pathname + url.search + url.hash;
|
|
205
|
-
}, /* @ts-ignore */
|
|
206
|
-
...(ngDevMode ? [{ debugName: "internalPath" }] : /* istanbul ignore next */ []));
|
|
207
|
-
this.internalUrlTree = computed(() => this.router.parseUrl(this.internalPath()), /* @ts-ignore */
|
|
208
|
-
...(ngDevMode ? [{ debugName: "internalUrlTree" }] : /* istanbul ignore next */ []));
|
|
209
|
-
this.linkClass = computed(() => {
|
|
210
|
-
const base = 'et-contentful-rich-text-default-element et-contentful-rich-text-default-a';
|
|
211
|
-
const extra = this.textClass();
|
|
212
|
-
return extra ? `${base} ${extra}` : base;
|
|
213
|
-
}, /* @ts-ignore */
|
|
214
|
-
...(ngDevMode ? [{ debugName: "linkClass" }] : /* istanbul ignore next */ []));
|
|
215
|
-
}
|
|
216
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
217
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ContentfulLinkComponent, isStandalone: true, selector: "et-contentful-link", inputs: { href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, textClass: { classPropertyName: "textClass", publicName: "textClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-contentful-link" }, ngImport: i0, template: `
|
|
218
|
-
@if (usesRouterLink()) {
|
|
219
|
-
<a [class]="linkClass()" [routerLink]="internalUrlTree()">{{ text() }}</a>
|
|
220
|
-
} @else {
|
|
221
|
-
<a
|
|
222
|
-
[class]="linkClass()"
|
|
223
|
-
[href]="href()"
|
|
224
|
-
[target]="openInNewTab() ? '_blank' : null"
|
|
225
|
-
[rel]="openInNewTab() ? 'noopener noreferrer' : null"
|
|
226
|
-
>{{ text() }}</a
|
|
227
|
-
>
|
|
228
|
-
}
|
|
229
|
-
`, isInline: true, styles: ["@layer components{.et-contentful-link{display:contents}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
230
|
-
}
|
|
231
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulLinkComponent, decorators: [{
|
|
232
|
-
type: Component,
|
|
233
|
-
args: [{ selector: 'et-contentful-link', template: `
|
|
234
|
-
@if (usesRouterLink()) {
|
|
235
|
-
<a [class]="linkClass()" [routerLink]="internalUrlTree()">{{ text() }}</a>
|
|
236
|
-
} @else {
|
|
237
|
-
<a
|
|
238
|
-
[class]="linkClass()"
|
|
239
|
-
[href]="href()"
|
|
240
|
-
[target]="openInNewTab() ? '_blank' : null"
|
|
241
|
-
[rel]="openInNewTab() ? 'noopener noreferrer' : null"
|
|
242
|
-
>{{ text() }}</a
|
|
243
|
-
>
|
|
244
|
-
}
|
|
245
|
-
`, encapsulation: ViewEncapsulation.None, imports: [RouterLink], host: {
|
|
246
|
-
class: 'et-contentful-link',
|
|
247
|
-
}, styles: ["@layer components{.et-contentful-link{display:contents}}\n"] }]
|
|
248
|
-
}], propDecorators: { href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], textClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "textClass", required: false }] }] } });
|
|
249
|
-
|
|
250
|
-
class ContentfulVideoComponent {
|
|
251
|
-
constructor() {
|
|
252
|
-
this.asset = input.required(/* @ts-ignore */
|
|
253
|
-
...(ngDevMode ? [{ debugName: "asset" }] : /* istanbul ignore next */ []));
|
|
254
|
-
this.videoClass = input(null, /* @ts-ignore */
|
|
255
|
-
...(ngDevMode ? [{ debugName: "videoClass" }] : /* istanbul ignore next */ []));
|
|
256
|
-
this.data = computed(() => {
|
|
257
|
-
const asset = this.asset();
|
|
258
|
-
if (!asset) {
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
if (isContentfulGqlAsset(asset) && asset.url) {
|
|
262
|
-
return {
|
|
263
|
-
url: asset.url,
|
|
264
|
-
contentType: asset.contentType,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
if (!isContentfulGqlAsset(asset) && asset.fields.file.url) {
|
|
268
|
-
return {
|
|
269
|
-
url: asset.fields.file.url,
|
|
270
|
-
contentType: asset.fields.file.contentType,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}, /* @ts-ignore */
|
|
275
|
-
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
276
|
-
}
|
|
277
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
278
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ContentfulVideoComponent, isStandalone: true, selector: "et-contentful-video", inputs: { asset: { classPropertyName: "asset", publicName: "asset", isSignal: true, isRequired: true, transformFunction: null }, videoClass: { classPropertyName: "videoClass", publicName: "videoClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-contentful-video" }, ngImport: i0, template: `
|
|
279
|
-
@if (data(); as data) {
|
|
280
|
-
<video [ngClass]="videoClass()" controls>
|
|
281
|
-
<source [src]="data.url" [type]="data.contentType" />
|
|
282
|
-
</video>
|
|
283
|
-
}
|
|
284
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
285
|
-
}
|
|
286
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulVideoComponent, decorators: [{
|
|
287
|
-
type: Component,
|
|
288
|
-
args: [{
|
|
289
|
-
selector: 'et-contentful-video',
|
|
290
|
-
template: `
|
|
291
|
-
@if (data(); as data) {
|
|
292
|
-
<video [ngClass]="videoClass()" controls>
|
|
293
|
-
<source [src]="data.url" [type]="data.contentType" />
|
|
294
|
-
</video>
|
|
295
|
-
}
|
|
296
|
-
`,
|
|
297
|
-
encapsulation: ViewEncapsulation.None,
|
|
298
|
-
imports: [NgClass],
|
|
299
|
-
host: {
|
|
300
|
-
class: 'et-contentful-video',
|
|
301
|
-
},
|
|
302
|
-
}]
|
|
303
|
-
}], propDecorators: { asset: [{ type: i0.Input, args: [{ isSignal: true, alias: "asset", required: true }] }], videoClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "videoClass", required: false }] }] } });
|
|
304
|
-
|
|
305
169
|
const CONTENTFUL_CONFIG = new InjectionToken('ContentfulConfig');
|
|
306
170
|
|
|
307
171
|
/**
|
|
@@ -319,25 +183,15 @@ const CONTENTFUL_FALLBACK_CONFIG = {
|
|
|
319
183
|
backgroundColor: null,
|
|
320
184
|
},
|
|
321
185
|
};
|
|
322
|
-
const createContentfulConfig = (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
},
|
|
332
|
-
customComponents: {},
|
|
333
|
-
imageOptions: {
|
|
334
|
-
srcsetSizes: ['375w', '1280w', '1920w', '2560w'],
|
|
335
|
-
sizes: ['100vw'],
|
|
336
|
-
backgroundColor: null,
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
...(config ?? {}),
|
|
340
|
-
});
|
|
186
|
+
const createContentfulConfig = (options) => {
|
|
187
|
+
const config = options ?? {};
|
|
188
|
+
return {
|
|
189
|
+
...CONTENTFUL_FALLBACK_CONFIG,
|
|
190
|
+
...config,
|
|
191
|
+
components: config.components ?? CONTENTFUL_FALLBACK_CONFIG.components,
|
|
192
|
+
imageOptions: { ...CONTENTFUL_FALLBACK_CONFIG.imageOptions, ...config.imageOptions },
|
|
193
|
+
};
|
|
194
|
+
};
|
|
341
195
|
/**
|
|
342
196
|
* Reads the contentful config from the current injector. Falls back to a config without
|
|
343
197
|
* any `components` when none was provided via `provideContentfulConfig()`.
|
|
@@ -357,15 +211,16 @@ const generateDefaultContentfulImageSource = (data) => {
|
|
|
357
211
|
srcset: data.url,
|
|
358
212
|
};
|
|
359
213
|
}
|
|
360
|
-
|
|
214
|
+
const file = data.fields.file;
|
|
215
|
+
if (!file?.contentType || !file.url) {
|
|
361
216
|
return {
|
|
362
217
|
type: '',
|
|
363
218
|
srcset: '',
|
|
364
219
|
};
|
|
365
220
|
}
|
|
366
221
|
return {
|
|
367
|
-
type:
|
|
368
|
-
srcset:
|
|
222
|
+
type: file.contentType,
|
|
223
|
+
srcset: file.url,
|
|
369
224
|
};
|
|
370
225
|
};
|
|
371
226
|
/**
|
|
@@ -392,13 +247,20 @@ const parseContentfulImageSize = (size) => {
|
|
|
392
247
|
return width ? { width: Number(width[1]), height: null } : { width: null, height: null };
|
|
393
248
|
};
|
|
394
249
|
const SOURCE_TYPES = ['image/avif', 'image/webp'];
|
|
395
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Builds AVIF and WebP `<source>` entries for a Contentful asset through the Contentful Images API.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* generateContentfulImageSources(asset, { srcsetSizes: ['400w', '800w'], quality: 80 });
|
|
255
|
+
*/
|
|
256
|
+
const generateContentfulImageSources = (data, options = {}) => {
|
|
257
|
+
const { srcsetSizes = [], backgroundColor = null, quality = null, focusArea = null, resizeBehavior = null } = options;
|
|
396
258
|
const isGqlAsset = isContentfulGqlAsset(data);
|
|
397
|
-
const baseUrl = isGqlAsset ? data.url : data.fields.file
|
|
259
|
+
const baseUrl = isGqlAsset ? data.url : data.fields.file?.url;
|
|
398
260
|
if (!baseUrl) {
|
|
399
261
|
return [];
|
|
400
262
|
}
|
|
401
|
-
const imageDimensions = isGqlAsset ? data : data.fields.file
|
|
263
|
+
const imageDimensions = isGqlAsset ? data : data.fields.file?.details.image;
|
|
402
264
|
const sources = [];
|
|
403
265
|
for (const type of SOURCE_TYPES) {
|
|
404
266
|
const sourceSets = [];
|
|
@@ -416,7 +278,7 @@ const generateContentfulImageSources = (data, srcsetSizes, backgroundColor, qual
|
|
|
416
278
|
if (resizeBehavior) {
|
|
417
279
|
queryParams.push(`fit=${resizeBehavior}`);
|
|
418
280
|
}
|
|
419
|
-
if (srcsetSizes
|
|
281
|
+
if (srcsetSizes.length) {
|
|
420
282
|
const urlWithParams = `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${queryParams.join('&')}`;
|
|
421
283
|
for (const size of srcsetSizes) {
|
|
422
284
|
const { width, height } = parseContentfulImageSize(size);
|
|
@@ -484,15 +346,17 @@ class ContentfulImageComponent {
|
|
|
484
346
|
...(ngDevMode ? [{ debugName: "normalizedAsset" }] : /* istanbul ignore next */ []));
|
|
485
347
|
this.sourcesValue = computed(() => {
|
|
486
348
|
const asset = this.asset();
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
349
|
+
const options = {
|
|
350
|
+
srcsetSizes: this.srcsetSizes(),
|
|
351
|
+
backgroundColor: this.backgroundColor(),
|
|
352
|
+
quality: this.quality(),
|
|
353
|
+
focusArea: this.focusArea(),
|
|
354
|
+
resizeBehavior: this.resizeBehavior(),
|
|
355
|
+
};
|
|
492
356
|
if (!asset) {
|
|
493
357
|
return [];
|
|
494
358
|
}
|
|
495
|
-
return generateContentfulImageSources(asset,
|
|
359
|
+
return generateContentfulImageSources(asset, options);
|
|
496
360
|
}, /* @ts-ignore */
|
|
497
361
|
...(ngDevMode ? [{ debugName: "sourcesValue" }] : /* istanbul ignore next */ []));
|
|
498
362
|
this.defaultSrcValue = computed(() => {
|
|
@@ -539,6 +403,117 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
539
403
|
}]
|
|
540
404
|
}], propDecorators: { asset: [{ type: i0.Input, args: [{ isSignal: true, alias: "asset", required: true }] }], backgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundColor", required: false }] }], srcsetSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "srcsetSizes", required: false }] }], quality: [{ type: i0.Input, args: [{ isSignal: true, alias: "quality", required: false }] }], focusArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusArea", required: false }] }], resizeBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeBehavior", required: false }] }], priority: [{ type: i0.Input, args: [{ isSignal: true, alias: "priority", required: false }] }], sizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizes", required: false }] }] } });
|
|
541
405
|
|
|
406
|
+
const parseWebUrl = (href) => {
|
|
407
|
+
if (!/^(?:https?:)?\/\//i.test(href)) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
return new URL(href.startsWith('//') ? 'https:' + href : href);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
const normalizeHostname = (host) => {
|
|
418
|
+
const value = host.trim().toLowerCase();
|
|
419
|
+
try {
|
|
420
|
+
return new URL(value.includes('://') ? value : 'https://' + value).hostname;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return value.split(':')[0] ?? value;
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
const matchesHostname = (hostname, configuredHost) => {
|
|
427
|
+
const normalizedHost = normalizeHostname(configuredHost);
|
|
428
|
+
return hostname === normalizedHost || hostname.endsWith('.' + normalizedHost);
|
|
429
|
+
};
|
|
430
|
+
const isInternalWebUrl = (url, { location, internalHosts }) => (url.hostname === location.hostname && url.port === location.port) ||
|
|
431
|
+
internalHosts.some((host) => matchesHostname(url.hostname, host));
|
|
432
|
+
const isExternalWebHref = (href, context) => {
|
|
433
|
+
const url = parseWebUrl(href);
|
|
434
|
+
return url !== null && !isInternalWebUrl(url, context);
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
class ContentfulLinkComponent {
|
|
438
|
+
constructor() {
|
|
439
|
+
this.document = inject(DOCUMENT);
|
|
440
|
+
this.router = inject(Router);
|
|
441
|
+
this.config = injectContentfulConfig();
|
|
442
|
+
this.url = injectUrl();
|
|
443
|
+
this.href = input.required(/* @ts-ignore */
|
|
444
|
+
...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
|
|
445
|
+
this.text = input.required(/* @ts-ignore */
|
|
446
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
447
|
+
this.textClass = input('', /* @ts-ignore */
|
|
448
|
+
...(ngDevMode ? [{ debugName: "textClass" }] : /* istanbul ignore next */ []));
|
|
449
|
+
this.anchorClass = input('', /* @ts-ignore */
|
|
450
|
+
...(ngDevMode ? [{ debugName: "anchorClass" }] : /* istanbul ignore next */ []));
|
|
451
|
+
this.usesRouterLink = computed(() => {
|
|
452
|
+
const href = this.href();
|
|
453
|
+
const absoluteUrl = parseWebUrl(href);
|
|
454
|
+
if (absoluteUrl) {
|
|
455
|
+
return isInternalWebUrl(absoluteUrl, {
|
|
456
|
+
location: this.document.location,
|
|
457
|
+
internalHosts: this.config.internalHosts,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return !href.startsWith('#') && !/^[a-z][a-z\d+.-]*:/i.test(href);
|
|
461
|
+
}, /* @ts-ignore */
|
|
462
|
+
...(ngDevMode ? [{ debugName: "usesRouterLink" }] : /* istanbul ignore next */ []));
|
|
463
|
+
this.openInNewTab = computed(() => Boolean(parseWebUrl(this.href())) && !this.usesRouterLink(), /* @ts-ignore */
|
|
464
|
+
...(ngDevMode ? [{ debugName: "openInNewTab" }] : /* istanbul ignore next */ []));
|
|
465
|
+
this.internalPath = computed(() => {
|
|
466
|
+
const href = this.href();
|
|
467
|
+
const url = parseWebUrl(href);
|
|
468
|
+
if (url) {
|
|
469
|
+
return url.pathname + url.search + url.hash;
|
|
470
|
+
}
|
|
471
|
+
if (href.startsWith('/')) {
|
|
472
|
+
return href;
|
|
473
|
+
}
|
|
474
|
+
const resolved = new URL(href, 'https://contentful.invalid' + this.url());
|
|
475
|
+
return resolved.pathname + resolved.search + resolved.hash;
|
|
476
|
+
}, /* @ts-ignore */
|
|
477
|
+
...(ngDevMode ? [{ debugName: "internalPath" }] : /* istanbul ignore next */ []));
|
|
478
|
+
this.internalUrlTree = computed(() => this.router.parseUrl(this.internalPath()), /* @ts-ignore */
|
|
479
|
+
...(ngDevMode ? [{ debugName: "internalUrlTree" }] : /* istanbul ignore next */ []));
|
|
480
|
+
this.linkClass = computed(() => [this.anchorClass(), this.textClass()].filter(Boolean).join(' ') || null, /* @ts-ignore */
|
|
481
|
+
...(ngDevMode ? [{ debugName: "linkClass" }] : /* istanbul ignore next */ []));
|
|
482
|
+
}
|
|
483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
484
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ContentfulLinkComponent, isStandalone: true, selector: "et-contentful-link", inputs: { href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, textClass: { classPropertyName: "textClass", publicName: "textClass", isSignal: true, isRequired: false, transformFunction: null }, anchorClass: { classPropertyName: "anchorClass", publicName: "anchorClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-contentful-link" }, ngImport: i0, template: `
|
|
485
|
+
@if (usesRouterLink()) {
|
|
486
|
+
<a [class]="linkClass()" [routerLink]="internalUrlTree()">{{ text() }}</a>
|
|
487
|
+
} @else {
|
|
488
|
+
<a
|
|
489
|
+
[class]="linkClass()"
|
|
490
|
+
[href]="href()"
|
|
491
|
+
[target]="openInNewTab() ? '_blank' : null"
|
|
492
|
+
[rel]="openInNewTab() ? 'noopener noreferrer' : null"
|
|
493
|
+
>{{ text() }}</a
|
|
494
|
+
>
|
|
495
|
+
}
|
|
496
|
+
`, isInline: true, styles: ["@layer components{.et-contentful-link{display:contents}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
497
|
+
}
|
|
498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulLinkComponent, decorators: [{
|
|
499
|
+
type: Component,
|
|
500
|
+
args: [{ selector: 'et-contentful-link', template: `
|
|
501
|
+
@if (usesRouterLink()) {
|
|
502
|
+
<a [class]="linkClass()" [routerLink]="internalUrlTree()">{{ text() }}</a>
|
|
503
|
+
} @else {
|
|
504
|
+
<a
|
|
505
|
+
[class]="linkClass()"
|
|
506
|
+
[href]="href()"
|
|
507
|
+
[target]="openInNewTab() ? '_blank' : null"
|
|
508
|
+
[rel]="openInNewTab() ? 'noopener noreferrer' : null"
|
|
509
|
+
>{{ text() }}</a
|
|
510
|
+
>
|
|
511
|
+
}
|
|
512
|
+
`, encapsulation: ViewEncapsulation.None, imports: [RouterLink], host: {
|
|
513
|
+
class: 'et-contentful-link',
|
|
514
|
+
}, styles: ["@layer components{.et-contentful-link{display:contents}}\n"] }]
|
|
515
|
+
}], propDecorators: { href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], textClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "textClass", required: false }] }], anchorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorClass", required: false }] }] } });
|
|
516
|
+
|
|
542
517
|
/**
|
|
543
518
|
* The `BLOCKS` node type strings of `@contentful/rich-text-types`, as a plain literal map.
|
|
544
519
|
* Keep the values in sync with the enum - they are part of the Contentful Delivery API.
|
|
@@ -587,22 +562,18 @@ const RICH_TEXT_RENDERER_ERRORS = {
|
|
|
587
562
|
rich_text_wrong_type: 'The rich text object does not satisfy the RichTextResponse interface. It should contain a property named "nodeType" with the value "document".',
|
|
588
563
|
asset_id_not_found: 'The asset ID was not found. This node is not supported.',
|
|
589
564
|
entry_id_not_found: 'The entry ID was not found. This node is not supported.',
|
|
590
|
-
asset_not_found: 'The asset was not found. This node is not supported.',
|
|
591
|
-
entry_not_found: 'The entry was not found. This node is not supported.',
|
|
592
|
-
custom_component_not_found: 'No custom component found for entry type. Please provide one for this type.',
|
|
593
565
|
text_parent_not_found: 'The parent node is not found. This structure is not supported.',
|
|
594
|
-
|
|
566
|
+
cached_command_not_found: 'No rendered node was found for a render command. The renderer state is out of sync.',
|
|
567
|
+
cached_command_not_component: 'The rendered node for a component command is not a component. The renderer state is out of sync.',
|
|
595
568
|
};
|
|
596
569
|
const RICH_TEXT_RENDERER_ERROR_CODES = {
|
|
597
570
|
rich_text_undefined: 0,
|
|
598
571
|
rich_text_wrong_type: 1,
|
|
599
572
|
asset_id_not_found: 2,
|
|
600
573
|
entry_id_not_found: 3,
|
|
601
|
-
asset_not_found: 4,
|
|
602
|
-
entry_not_found: 5,
|
|
603
|
-
custom_component_not_found: 6,
|
|
604
574
|
text_parent_not_found: 7,
|
|
605
|
-
|
|
575
|
+
cached_command_not_found: 9,
|
|
576
|
+
cached_command_not_component: 10,
|
|
606
577
|
};
|
|
607
578
|
const richTextRendererError = (code, data) => {
|
|
608
579
|
const message = `<et-contentful-rich-text-renderer>: ${RICH_TEXT_RENDERER_ERRORS[code]}`;
|
|
@@ -612,60 +583,43 @@ const richTextRendererError = (code, data) => {
|
|
|
612
583
|
const isRichTextRootNode = (node) => {
|
|
613
584
|
return isObject(node) && 'nodeType' in node && node['nodeType'] === 'document';
|
|
614
585
|
};
|
|
586
|
+
const NODE_TYPE_TAGS = /* @__PURE__ */ (() => ({
|
|
587
|
+
[CF_BLOCKS.HEADING_1]: 'h1',
|
|
588
|
+
[CF_BLOCKS.HEADING_2]: 'h2',
|
|
589
|
+
[CF_BLOCKS.HEADING_3]: 'h3',
|
|
590
|
+
[CF_BLOCKS.HEADING_4]: 'h4',
|
|
591
|
+
[CF_BLOCKS.HEADING_5]: 'h5',
|
|
592
|
+
[CF_BLOCKS.HEADING_6]: 'h6',
|
|
593
|
+
[CF_BLOCKS.PARAGRAPH]: 'p',
|
|
594
|
+
[CF_BLOCKS.UL_LIST]: 'ul',
|
|
595
|
+
[CF_BLOCKS.OL_LIST]: 'ol',
|
|
596
|
+
[CF_BLOCKS.LIST_ITEM]: 'li',
|
|
597
|
+
[CF_BLOCKS.HR]: 'hr',
|
|
598
|
+
[CF_BLOCKS.QUOTE]: 'blockquote',
|
|
599
|
+
[CF_BLOCKS.TABLE]: 'table',
|
|
600
|
+
[CF_BLOCKS.TABLE_ROW]: 'tr',
|
|
601
|
+
[CF_BLOCKS.TABLE_CELL]: 'td',
|
|
602
|
+
[CF_BLOCKS.TABLE_HEADER_CELL]: 'th',
|
|
603
|
+
}))();
|
|
604
|
+
const INLINE_NODE_TYPES = /* @__PURE__ */ (() => new Set(Object.values(CF_INLINES)))();
|
|
605
|
+
/**
|
|
606
|
+
* The html element a structural rich text node renders as. A node type without an element
|
|
607
|
+
* (an unsupported inline or block) falls back to `span` for inlines and `div` otherwise,
|
|
608
|
+
* with a dev-mode warning.
|
|
609
|
+
*/
|
|
615
610
|
const translateContentfulNodeTypeToHtmlTag = (nodeType) => {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
case CF_BLOCKS.HEADING_4:
|
|
624
|
-
return 'h4';
|
|
625
|
-
case CF_BLOCKS.HEADING_5:
|
|
626
|
-
return 'h5';
|
|
627
|
-
case CF_BLOCKS.HEADING_6:
|
|
628
|
-
return 'h6';
|
|
629
|
-
case CF_BLOCKS.PARAGRAPH:
|
|
630
|
-
return 'p';
|
|
631
|
-
case CF_BLOCKS.UL_LIST:
|
|
632
|
-
return 'ul';
|
|
633
|
-
case CF_BLOCKS.OL_LIST:
|
|
634
|
-
return 'ol';
|
|
635
|
-
case CF_BLOCKS.LIST_ITEM:
|
|
636
|
-
return 'li';
|
|
637
|
-
case CF_BLOCKS.HR:
|
|
638
|
-
return 'hr';
|
|
639
|
-
case CF_BLOCKS.QUOTE:
|
|
640
|
-
return 'blockquote';
|
|
641
|
-
case CF_BLOCKS.TABLE:
|
|
642
|
-
return 'table';
|
|
643
|
-
case CF_BLOCKS.TABLE_ROW:
|
|
644
|
-
return 'tr';
|
|
645
|
-
case CF_BLOCKS.TABLE_CELL:
|
|
646
|
-
return 'td';
|
|
647
|
-
case CF_BLOCKS.TABLE_HEADER_CELL:
|
|
648
|
-
return 'th';
|
|
649
|
-
case CF_BLOCKS.EMBEDDED_ASSET:
|
|
650
|
-
return 'div';
|
|
651
|
-
case CF_INLINES.ENTRY_HYPERLINK:
|
|
652
|
-
return 'a';
|
|
653
|
-
case CF_INLINES.ASSET_HYPERLINK:
|
|
654
|
-
return 'a';
|
|
655
|
-
case 'text':
|
|
656
|
-
return 'span';
|
|
657
|
-
// Will be ignored by the renderer
|
|
658
|
-
case 'document':
|
|
659
|
-
return 'div';
|
|
660
|
-
case CF_BLOCKS.EMBEDDED_ENTRY:
|
|
661
|
-
return 'div';
|
|
662
|
-
case CF_INLINES.EMBEDDED_ENTRY:
|
|
663
|
-
return 'div';
|
|
664
|
-
default:
|
|
665
|
-
return 'div';
|
|
611
|
+
const tag = NODE_TYPE_TAGS[nodeType];
|
|
612
|
+
if (tag) {
|
|
613
|
+
return tag;
|
|
614
|
+
}
|
|
615
|
+
const fallback = INLINE_NODE_TYPES.has(nodeType) ? 'span' : 'div';
|
|
616
|
+
if (isDevMode()) {
|
|
617
|
+
console.warn(`Unsupported rich text node type "${nodeType}"! Its content is rendered inside a <${fallback}>.`);
|
|
666
618
|
}
|
|
619
|
+
return fallback;
|
|
667
620
|
};
|
|
668
621
|
|
|
622
|
+
const DEFAULT_ANCHOR_CLASS = 'et-contentful-rich-text-default-element et-contentful-rich-text-default-a';
|
|
669
623
|
const MARK_TAG_MAP = {
|
|
670
624
|
bold: 'strong',
|
|
671
625
|
italic: 'em',
|
|
@@ -727,7 +681,7 @@ const createContentfulIncludeMap = (config) => {
|
|
|
727
681
|
const entry = entryMap.get(id);
|
|
728
682
|
if (!entry) {
|
|
729
683
|
if (isDevMode()) {
|
|
730
|
-
console.warn('Entry not found! Will return null. Is the include query param
|
|
684
|
+
console.warn('Entry not found! Will return null. Is the include query param too low?', { id, entryMap });
|
|
731
685
|
}
|
|
732
686
|
return null;
|
|
733
687
|
}
|
|
@@ -772,6 +726,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
772
726
|
this.renderer = injectRenderer();
|
|
773
727
|
this.elementRef = inject(ElementRef);
|
|
774
728
|
this.config = injectContentfulConfig();
|
|
729
|
+
this.document = inject(DOCUMENT);
|
|
775
730
|
/**
|
|
776
731
|
* The contentful response gotten via their REST api.
|
|
777
732
|
* @see https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/entries/entries-collection
|
|
@@ -825,18 +780,6 @@ class ContentfulRichTextRendererComponent {
|
|
|
825
780
|
const instructions = [];
|
|
826
781
|
const preserved = new Set();
|
|
827
782
|
const needsReattach = new Set();
|
|
828
|
-
const parentIdOf = (command) => {
|
|
829
|
-
if (command.nestingLevel === 0) {
|
|
830
|
-
return null;
|
|
831
|
-
}
|
|
832
|
-
for (let i = command.index - 1; i >= 0; i--) {
|
|
833
|
-
const cmd = commands[i];
|
|
834
|
-
if (cmd && cmd.nestingLevel === command.nestingLevel - 1 && cmd.kind === 'htmlOpen') {
|
|
835
|
-
return cmd.id;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
return null;
|
|
839
|
-
};
|
|
840
783
|
const markTagsEqual = (a, b) => a.length === b.length && a.every((tag, index) => tag === b[index]);
|
|
841
784
|
const attributesEqual = (a, b) => {
|
|
842
785
|
const aKeys = Object.keys(a);
|
|
@@ -853,8 +796,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
853
796
|
if (previous.kind === 'component' && command.kind === 'component' && previous.component !== command.component) {
|
|
854
797
|
continue;
|
|
855
798
|
}
|
|
856
|
-
const
|
|
857
|
-
const parentPreserved = parentId === null || preserved.has(parentId);
|
|
799
|
+
const parentPreserved = previous.parentId === command.parentId && (command.parentId === null || preserved.has(command.parentId));
|
|
858
800
|
if (command.kind === 'component') {
|
|
859
801
|
preserved.add(command.id);
|
|
860
802
|
const samePlace = parentPreserved &&
|
|
@@ -913,7 +855,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
913
855
|
let nestingLevel = 0;
|
|
914
856
|
let domPosition = 0;
|
|
915
857
|
let textId = 0;
|
|
916
|
-
|
|
858
|
+
const openElementIds = [];
|
|
917
859
|
const traverse = (node) => {
|
|
918
860
|
switch (node.nodeType) {
|
|
919
861
|
case 'text': {
|
|
@@ -927,7 +869,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
927
869
|
kind: 'text',
|
|
928
870
|
nestingLevel,
|
|
929
871
|
domPosition,
|
|
930
|
-
|
|
872
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
931
873
|
attributes,
|
|
932
874
|
markTags: node.marks.length ? marksToTags(node.marks) : [],
|
|
933
875
|
text,
|
|
@@ -943,11 +885,14 @@ class ContentfulRichTextRendererComponent {
|
|
|
943
885
|
}
|
|
944
886
|
const asset = this.contentIncludesMap().getAsset(assetId);
|
|
945
887
|
if (!asset) {
|
|
946
|
-
|
|
888
|
+
if (isDevMode()) {
|
|
889
|
+
console.warn('Embedded asset is missing from the includes! The asset will be skipped. Is it unpublished or deleted?', { assetId, node });
|
|
890
|
+
}
|
|
891
|
+
break;
|
|
947
892
|
}
|
|
948
|
-
const contentType = asset.fields.file
|
|
893
|
+
const contentType = asset.fields.file?.contentType;
|
|
949
894
|
const assetComponents = this.config.components;
|
|
950
|
-
const isMissing = !contentType && !asset.fields.file
|
|
895
|
+
const isMissing = !contentType && !asset.fields.file?.url;
|
|
951
896
|
if (isMissing) {
|
|
952
897
|
if (isDevMode()) {
|
|
953
898
|
console.warn('Asset is missing file data! Asset will be skipped. Did you forget to upload a file for the current translation in Contentful?', { asset });
|
|
@@ -978,7 +923,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
978
923
|
kind: 'component',
|
|
979
924
|
nestingLevel,
|
|
980
925
|
domPosition,
|
|
981
|
-
|
|
926
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
982
927
|
component,
|
|
983
928
|
inputs: { asset },
|
|
984
929
|
id,
|
|
@@ -996,52 +941,42 @@ class ContentfulRichTextRendererComponent {
|
|
|
996
941
|
else if (node.nodeType === CF_INLINES.ASSET_HYPERLINK) {
|
|
997
942
|
const assetId = node.data['target']?.sys?.id;
|
|
998
943
|
const asset = assetId ? this.contentIncludesMap().getAsset(assetId) : null;
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
let linkText = '';
|
|
1002
|
-
const linkMarks = [];
|
|
1003
|
-
for (const child of node.content) {
|
|
1004
|
-
if (child.nodeType === 'text') {
|
|
1005
|
-
linkText += child.value;
|
|
1006
|
-
linkMarks.push(...child.marks);
|
|
1007
|
-
}
|
|
944
|
+
const url = asset?.fields.file?.url;
|
|
945
|
+
href = url ? normalizeHref(url) : null;
|
|
1008
946
|
}
|
|
947
|
+
const linkTexts = node.content.filter((child) => child.nodeType === 'text' && Boolean(child.value));
|
|
948
|
+
const linkText = linkTexts.map((child) => child.value).join('');
|
|
1009
949
|
const linkComponent = href ? this.config.components.link : null;
|
|
1010
950
|
if (href && !linkComponent) {
|
|
951
|
+
const attributes = { class: DEFAULT_ANCHOR_CLASS, href };
|
|
952
|
+
if (isExternalWebHref(href, { location: this.document.location, internalHosts: this.config.internalHosts })) {
|
|
953
|
+
attributes['target'] = '_blank';
|
|
954
|
+
attributes['rel'] = 'noopener noreferrer';
|
|
955
|
+
}
|
|
1011
956
|
rootCommands.push({
|
|
1012
957
|
kind: 'htmlOpen',
|
|
1013
958
|
nestingLevel,
|
|
1014
959
|
domPosition,
|
|
1015
|
-
|
|
1016
|
-
attributes
|
|
1017
|
-
class: 'et-contentful-rich-text-default-element et-contentful-rich-text-default-a',
|
|
1018
|
-
href,
|
|
1019
|
-
},
|
|
960
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
961
|
+
attributes,
|
|
1020
962
|
tagName: 'a',
|
|
1021
963
|
id: 'e-o' + elementOpenId++,
|
|
1022
964
|
});
|
|
1023
965
|
const anchorDomPosition = domPosition;
|
|
966
|
+
openElementIds.push('e-o' + (elementOpenId - 1));
|
|
1024
967
|
nestingLevel++;
|
|
1025
968
|
domPosition = 0;
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
index: commandIndex++,
|
|
1031
|
-
attributes: {
|
|
1032
|
-
class: 'et-contentful-rich-text-default-element et-contentful-rich-text-default-span',
|
|
1033
|
-
},
|
|
1034
|
-
markTags: marksToTags(linkMarks),
|
|
1035
|
-
text: linkText,
|
|
1036
|
-
id: 't' + textId++,
|
|
1037
|
-
});
|
|
969
|
+
for (const child of linkTexts) {
|
|
970
|
+
traverse(child);
|
|
971
|
+
}
|
|
972
|
+
openElementIds.pop();
|
|
1038
973
|
nestingLevel--;
|
|
1039
974
|
domPosition = anchorDomPosition;
|
|
1040
975
|
rootCommands.push({
|
|
1041
976
|
kind: 'htmlClose',
|
|
1042
977
|
nestingLevel,
|
|
1043
978
|
domPosition,
|
|
1044
|
-
|
|
979
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
1045
980
|
tagName: 'a',
|
|
1046
981
|
id: 'e-c' + elementCloseId++,
|
|
1047
982
|
});
|
|
@@ -1049,25 +984,16 @@ class ContentfulRichTextRendererComponent {
|
|
|
1049
984
|
break;
|
|
1050
985
|
}
|
|
1051
986
|
if (!href) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
domPosition,
|
|
1056
|
-
index: commandIndex++,
|
|
1057
|
-
attributes: {
|
|
1058
|
-
class: 'et-contentful-rich-text-default-element et-contentful-rich-text-default-span',
|
|
1059
|
-
},
|
|
1060
|
-
markTags: marksToTags(linkMarks),
|
|
1061
|
-
text: linkText,
|
|
1062
|
-
id: 't' + textId++,
|
|
1063
|
-
});
|
|
1064
|
-
domPosition++;
|
|
987
|
+
for (const child of linkTexts) {
|
|
988
|
+
traverse(child);
|
|
989
|
+
}
|
|
1065
990
|
break;
|
|
1066
991
|
}
|
|
1067
992
|
if (!linkComponent) {
|
|
1068
993
|
break;
|
|
1069
994
|
}
|
|
1070
|
-
const
|
|
995
|
+
const sharedMarks = linkTexts[0]?.marks.filter((mark) => linkTexts.every((child) => child.marks.some((other) => other.type === mark.type))) ?? [];
|
|
996
|
+
const textClass = marksToClass(sharedMarks);
|
|
1071
997
|
let linkComponentId = componentIdMap.get(LINK_COMPONENT_TYPE) ?? -1;
|
|
1072
998
|
const linkId = LINK_COMPONENT_TYPE + ++linkComponentId;
|
|
1073
999
|
componentIdMap.set(LINK_COMPONENT_TYPE, linkComponentId);
|
|
@@ -1075,9 +1001,9 @@ class ContentfulRichTextRendererComponent {
|
|
|
1075
1001
|
kind: 'component',
|
|
1076
1002
|
nestingLevel,
|
|
1077
1003
|
domPosition,
|
|
1078
|
-
|
|
1004
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
1079
1005
|
component: linkComponent,
|
|
1080
|
-
inputs: { href, text: linkText, textClass },
|
|
1006
|
+
inputs: { href, text: linkText, textClass, anchorClass: DEFAULT_ANCHOR_CLASS },
|
|
1081
1007
|
id: linkId,
|
|
1082
1008
|
});
|
|
1083
1009
|
domPosition++;
|
|
@@ -1091,16 +1017,18 @@ class ContentfulRichTextRendererComponent {
|
|
|
1091
1017
|
}
|
|
1092
1018
|
const entry = this.contentIncludesMap().getEntry(entryId, ET_CONTENTFUL_ANY_ENTRY_CONTENT_TYPE_SYS_ID);
|
|
1093
1019
|
if (!entry) {
|
|
1094
|
-
|
|
1020
|
+
if (isDevMode()) {
|
|
1021
|
+
console.warn('Embedded entry is missing from the includes! The entry will be skipped. Is it unpublished or deleted?', { entryId, node });
|
|
1022
|
+
}
|
|
1023
|
+
break;
|
|
1095
1024
|
}
|
|
1096
1025
|
const componentType = entry.sys.contentType.sys.id;
|
|
1097
1026
|
const component = this.config.customComponents[componentType];
|
|
1098
1027
|
if (!component) {
|
|
1099
|
-
|
|
1100
|
-
componentType,
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
});
|
|
1028
|
+
if (isDevMode()) {
|
|
1029
|
+
console.warn('No custom component registered for this embedded entry type! The entry will be skipped. Provide one via provideContentfulConfig({ customComponents: … }).', { componentType, customComponents: this.config.customComponents, entry });
|
|
1030
|
+
}
|
|
1031
|
+
break;
|
|
1104
1032
|
}
|
|
1105
1033
|
const occurrenceKey = 'entry:' + entryId;
|
|
1106
1034
|
const occurrence = (componentIdMap.get(occurrenceKey) ?? -1) + 1;
|
|
@@ -1110,7 +1038,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
1110
1038
|
kind: 'component',
|
|
1111
1039
|
nestingLevel,
|
|
1112
1040
|
domPosition,
|
|
1113
|
-
|
|
1041
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
1114
1042
|
component,
|
|
1115
1043
|
inputs: {
|
|
1116
1044
|
fields: entry.fields,
|
|
@@ -1132,17 +1060,19 @@ class ContentfulRichTextRendererComponent {
|
|
|
1132
1060
|
kind: 'htmlOpen',
|
|
1133
1061
|
nestingLevel,
|
|
1134
1062
|
domPosition,
|
|
1135
|
-
|
|
1063
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
1136
1064
|
attributes,
|
|
1137
1065
|
tagName: tag,
|
|
1138
1066
|
id: 'e-o' + elementOpenId++,
|
|
1139
1067
|
});
|
|
1140
1068
|
const domPositionAtThisLevel = domPosition;
|
|
1069
|
+
openElementIds.push('e-o' + (elementOpenId - 1));
|
|
1141
1070
|
nestingLevel++;
|
|
1142
1071
|
domPosition = 0;
|
|
1143
1072
|
for (const child of node.content) {
|
|
1144
1073
|
traverse(child);
|
|
1145
1074
|
}
|
|
1075
|
+
openElementIds.pop();
|
|
1146
1076
|
nestingLevel--;
|
|
1147
1077
|
domPosition = domPositionAtThisLevel;
|
|
1148
1078
|
const lastCommand = rootCommands[rootCommands.length - 1];
|
|
@@ -1152,14 +1082,13 @@ class ContentfulRichTextRendererComponent {
|
|
|
1152
1082
|
lastCommand.tagName !== 'hr') {
|
|
1153
1083
|
rootCommands.pop();
|
|
1154
1084
|
elementOpenId--;
|
|
1155
|
-
commandIndex--;
|
|
1156
1085
|
}
|
|
1157
1086
|
else {
|
|
1158
1087
|
rootCommands.push({
|
|
1159
1088
|
kind: 'htmlClose',
|
|
1160
1089
|
nestingLevel,
|
|
1161
1090
|
domPosition,
|
|
1162
|
-
|
|
1091
|
+
parentId: openElementIds.at(-1) ?? null,
|
|
1163
1092
|
tagName: tag,
|
|
1164
1093
|
id: 'e-c' + elementCloseId++,
|
|
1165
1094
|
});
|
|
@@ -1175,26 +1104,32 @@ class ContentfulRichTextRendererComponent {
|
|
|
1175
1104
|
return rootCommands;
|
|
1176
1105
|
}
|
|
1177
1106
|
execInstructions(instructions) {
|
|
1178
|
-
|
|
1179
|
-
|
|
1107
|
+
const lastPlacedChild = new Map();
|
|
1108
|
+
for (const { type, command } of instructions) {
|
|
1109
|
+
if (type === 'delete') {
|
|
1110
|
+
this.runDeleteInstruction(command);
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
const previousSibling = lastPlacedChild.get(command.parentId) ?? null;
|
|
1114
|
+
switch (type) {
|
|
1180
1115
|
case 'create':
|
|
1181
|
-
this.runCreateInstruction(
|
|
1116
|
+
this.runCreateInstruction(command, previousSibling);
|
|
1182
1117
|
break;
|
|
1183
1118
|
case 'update':
|
|
1184
|
-
this.runUpdateInstruction(
|
|
1119
|
+
this.runUpdateInstruction(command);
|
|
1185
1120
|
break;
|
|
1186
1121
|
case 'move':
|
|
1187
|
-
this.runMoveInstruction(
|
|
1188
|
-
break;
|
|
1189
|
-
case 'delete':
|
|
1190
|
-
this.runDeleteInstruction(instruction.command);
|
|
1122
|
+
this.runMoveInstruction(command, previousSibling);
|
|
1191
1123
|
break;
|
|
1192
1124
|
}
|
|
1125
|
+
const element = this.executedCommandsCache.get(command.id)?.element;
|
|
1126
|
+
if (element) {
|
|
1127
|
+
lastPlacedChild.set(command.parentId, element);
|
|
1128
|
+
}
|
|
1193
1129
|
}
|
|
1194
1130
|
}
|
|
1195
|
-
runCreateInstruction(command) {
|
|
1131
|
+
runCreateInstruction(command, previousSibling) {
|
|
1196
1132
|
const parentElement = this.findParent(command);
|
|
1197
|
-
const nextElement = this.findFollowingElement(command);
|
|
1198
1133
|
if (command.kind === 'component') {
|
|
1199
1134
|
const inputs = signal(command.inputs, /* @ts-ignore */
|
|
1200
1135
|
...(ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []));
|
|
@@ -1204,7 +1139,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
1204
1139
|
.map((declared) => inputBinding(declared.templateName, () => inputs()[declared.propName]));
|
|
1205
1140
|
const componentRef = this.viewContainerRef.createComponent(command.component, { bindings });
|
|
1206
1141
|
const rootNode = this.getComponentRootNode(componentRef);
|
|
1207
|
-
this.
|
|
1142
|
+
this.renderInsertAfter(rootNode, { parentElement, previousSibling });
|
|
1208
1143
|
this.executedCommandsCache.set(command.id, {
|
|
1209
1144
|
command,
|
|
1210
1145
|
componentRef,
|
|
@@ -1234,7 +1169,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
1234
1169
|
this.renderer.appendChild(textContainer, textNode);
|
|
1235
1170
|
}
|
|
1236
1171
|
}
|
|
1237
|
-
this.
|
|
1172
|
+
this.renderInsertAfter(span, { parentElement, previousSibling });
|
|
1238
1173
|
this.executedCommandsCache.set(command.id, {
|
|
1239
1174
|
command,
|
|
1240
1175
|
element: span,
|
|
@@ -1245,7 +1180,7 @@ class ContentfulRichTextRendererComponent {
|
|
|
1245
1180
|
for (const [key, value] of Object.entries(command.attributes)) {
|
|
1246
1181
|
this.renderer.setAttribute(element, key, value);
|
|
1247
1182
|
}
|
|
1248
|
-
this.
|
|
1183
|
+
this.renderInsertAfter(element, { parentElement, previousSibling });
|
|
1249
1184
|
this.executedCommandsCache.set(command.id, {
|
|
1250
1185
|
command,
|
|
1251
1186
|
element,
|
|
@@ -1255,11 +1190,11 @@ class ContentfulRichTextRendererComponent {
|
|
|
1255
1190
|
runUpdateInstruction(command) {
|
|
1256
1191
|
const cached = this.executedCommandsCache.get(command.id);
|
|
1257
1192
|
if (!cached) {
|
|
1258
|
-
throw
|
|
1193
|
+
throw richTextRendererError('cached_command_not_found', { command });
|
|
1259
1194
|
}
|
|
1260
1195
|
if (command.kind === 'component') {
|
|
1261
1196
|
if (!isExecutedComponentCommandCacheItem(cached)) {
|
|
1262
|
-
throw
|
|
1197
|
+
throw richTextRendererError('cached_command_not_component', { command });
|
|
1263
1198
|
}
|
|
1264
1199
|
cached.inputs.set(command.inputs);
|
|
1265
1200
|
}
|
|
@@ -1268,23 +1203,21 @@ class ContentfulRichTextRendererComponent {
|
|
|
1268
1203
|
command,
|
|
1269
1204
|
});
|
|
1270
1205
|
}
|
|
1271
|
-
runMoveInstruction(command) {
|
|
1206
|
+
runMoveInstruction(command, previousSibling) {
|
|
1272
1207
|
const cached = this.executedCommandsCache.get(command.id);
|
|
1273
1208
|
if (!cached) {
|
|
1274
|
-
throw
|
|
1209
|
+
throw richTextRendererError('cached_command_not_found', { command });
|
|
1275
1210
|
}
|
|
1276
1211
|
if (command.kind === 'component') {
|
|
1277
1212
|
if (!isExecutedComponentCommandCacheItem(cached)) {
|
|
1278
|
-
throw
|
|
1213
|
+
throw richTextRendererError('cached_command_not_component', { command });
|
|
1279
1214
|
}
|
|
1280
1215
|
const rootNode = cached.element;
|
|
1281
1216
|
const oldParentElement = cached.element.parentElement;
|
|
1282
1217
|
if (oldParentElement) {
|
|
1283
1218
|
this.renderer.removeChild(oldParentElement, rootNode);
|
|
1284
1219
|
}
|
|
1285
|
-
|
|
1286
|
-
const nextElement = this.findFollowingElement(command);
|
|
1287
|
-
this.renderInsertOrAppend(rootNode, newParentElement, nextElement);
|
|
1220
|
+
this.renderInsertAfter(rootNode, { parentElement: this.findParent(command), previousSibling });
|
|
1288
1221
|
cached.inputs.set(command.inputs);
|
|
1289
1222
|
this.executedCommandsCache.set(command.id, {
|
|
1290
1223
|
...cached,
|
|
@@ -1298,11 +1231,11 @@ class ContentfulRichTextRendererComponent {
|
|
|
1298
1231
|
if (command.kind === 'htmlClose') {
|
|
1299
1232
|
return;
|
|
1300
1233
|
}
|
|
1301
|
-
throw
|
|
1234
|
+
throw richTextRendererError('cached_command_not_found', { command });
|
|
1302
1235
|
}
|
|
1303
1236
|
if (command.kind === 'component') {
|
|
1304
1237
|
if (!isExecutedComponentCommandCacheItem(cached)) {
|
|
1305
|
-
throw
|
|
1238
|
+
throw richTextRendererError('cached_command_not_component', { command });
|
|
1306
1239
|
}
|
|
1307
1240
|
cached.componentRef.destroy();
|
|
1308
1241
|
}
|
|
@@ -1317,57 +1250,17 @@ class ContentfulRichTextRendererComponent {
|
|
|
1317
1250
|
return componentRef.hostView.rootNodes[0];
|
|
1318
1251
|
}
|
|
1319
1252
|
findParent(command) {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
if (command.nestingLevel === 0) {
|
|
1323
|
-
parentElement = hostElement;
|
|
1324
|
-
}
|
|
1325
|
-
else {
|
|
1326
|
-
const allCommands = this.renderCommands();
|
|
1327
|
-
let parentCommand = null;
|
|
1328
|
-
for (let i = command.index - 1; i >= 0; i--) {
|
|
1329
|
-
const cmd = allCommands[i];
|
|
1330
|
-
if (!cmd) {
|
|
1331
|
-
throw new Error('Command not found!');
|
|
1332
|
-
}
|
|
1333
|
-
if (cmd.nestingLevel === command.nestingLevel - 1 && cmd.kind === 'htmlOpen') {
|
|
1334
|
-
parentCommand = cmd;
|
|
1335
|
-
break;
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
if (!parentCommand) {
|
|
1339
|
-
throw richTextRendererError('text_parent_not_found', { command });
|
|
1340
|
-
}
|
|
1341
|
-
parentElement = this.executedCommandsCache.get(parentCommand.id)?.element;
|
|
1253
|
+
if (command.parentId === null) {
|
|
1254
|
+
return this.elementRef.nativeElement;
|
|
1342
1255
|
}
|
|
1256
|
+
const parentElement = this.executedCommandsCache.get(command.parentId)?.element;
|
|
1343
1257
|
if (!parentElement) {
|
|
1344
|
-
throw richTextRendererError('
|
|
1258
|
+
throw richTextRendererError('text_parent_not_found', { command });
|
|
1345
1259
|
}
|
|
1346
1260
|
return parentElement;
|
|
1347
1261
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
let nextElement;
|
|
1351
|
-
let nextDomPosition = Infinity;
|
|
1352
|
-
for (const cached of this.executedCommandsCache.values()) {
|
|
1353
|
-
if (cached.command.domPosition > command.domPosition &&
|
|
1354
|
-
cached.command.domPosition < nextDomPosition &&
|
|
1355
|
-
cached.command.nestingLevel === command.nestingLevel &&
|
|
1356
|
-
cached.element.parentElement === parentElement &&
|
|
1357
|
-
cached.command.id !== command.id) {
|
|
1358
|
-
nextElement = cached.element;
|
|
1359
|
-
nextDomPosition = cached.command.domPosition;
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
return nextElement;
|
|
1363
|
-
}
|
|
1364
|
-
renderInsertOrAppend(nodeToRender, parentElement, nextElement) {
|
|
1365
|
-
if (nextElement) {
|
|
1366
|
-
this.renderer.insertBefore(parentElement, nodeToRender, nextElement);
|
|
1367
|
-
}
|
|
1368
|
-
else {
|
|
1369
|
-
this.renderer.appendChild(parentElement, nodeToRender);
|
|
1370
|
-
}
|
|
1262
|
+
renderInsertAfter(node, { parentElement, previousSibling }) {
|
|
1263
|
+
this.renderer.insertBefore(parentElement, node, previousSibling ? previousSibling.nextSibling : parentElement.firstChild);
|
|
1371
1264
|
}
|
|
1372
1265
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulRichTextRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1373
1266
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: ContentfulRichTextRendererComponent, isStandalone: true, selector: "et-contentful-rich-text-renderer", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: true, transformFunction: null }, richTextPath: { classPropertyName: "richTextPath", publicName: "richTextPath", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-contentful-rich-text-renderer" }, ngImport: i0, template: ``, isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
|
|
@@ -1384,6 +1277,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
1384
1277
|
}]
|
|
1385
1278
|
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: true }] }], richTextPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "richTextPath", required: true }] }] } });
|
|
1386
1279
|
|
|
1280
|
+
class ContentfulVideoComponent {
|
|
1281
|
+
constructor() {
|
|
1282
|
+
this.asset = input.required(/* @ts-ignore */
|
|
1283
|
+
...(ngDevMode ? [{ debugName: "asset" }] : /* istanbul ignore next */ []));
|
|
1284
|
+
this.videoClass = input(null, /* @ts-ignore */
|
|
1285
|
+
...(ngDevMode ? [{ debugName: "videoClass" }] : /* istanbul ignore next */ []));
|
|
1286
|
+
this.data = computed(() => {
|
|
1287
|
+
const asset = this.asset();
|
|
1288
|
+
if (!asset) {
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
if (isContentfulGqlAsset(asset) && asset.url) {
|
|
1292
|
+
return {
|
|
1293
|
+
url: asset.url,
|
|
1294
|
+
contentType: asset.contentType,
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
if (!isContentfulGqlAsset(asset) && asset.fields.file?.url) {
|
|
1298
|
+
return {
|
|
1299
|
+
url: asset.fields.file.url,
|
|
1300
|
+
contentType: asset.fields.file.contentType,
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
return null;
|
|
1304
|
+
}, /* @ts-ignore */
|
|
1305
|
+
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
1306
|
+
}
|
|
1307
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1308
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ContentfulVideoComponent, isStandalone: true, selector: "et-contentful-video", inputs: { asset: { classPropertyName: "asset", publicName: "asset", isSignal: true, isRequired: true, transformFunction: null }, videoClass: { classPropertyName: "videoClass", publicName: "videoClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-contentful-video" }, ngImport: i0, template: `
|
|
1309
|
+
@if (data(); as data) {
|
|
1310
|
+
<video [ngClass]="videoClass()" controls>
|
|
1311
|
+
<source [src]="data.url" [type]="data.contentType" />
|
|
1312
|
+
</video>
|
|
1313
|
+
}
|
|
1314
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
1315
|
+
}
|
|
1316
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ContentfulVideoComponent, decorators: [{
|
|
1317
|
+
type: Component,
|
|
1318
|
+
args: [{
|
|
1319
|
+
selector: 'et-contentful-video',
|
|
1320
|
+
template: `
|
|
1321
|
+
@if (data(); as data) {
|
|
1322
|
+
<video [ngClass]="videoClass()" controls>
|
|
1323
|
+
<source [src]="data.url" [type]="data.contentType" />
|
|
1324
|
+
</video>
|
|
1325
|
+
}
|
|
1326
|
+
`,
|
|
1327
|
+
encapsulation: ViewEncapsulation.None,
|
|
1328
|
+
imports: [NgClass],
|
|
1329
|
+
host: {
|
|
1330
|
+
class: 'et-contentful-video',
|
|
1331
|
+
},
|
|
1332
|
+
}]
|
|
1333
|
+
}], propDecorators: { asset: [{ type: i0.Input, args: [{ isSignal: true, alias: "asset", required: true }] }], videoClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "videoClass", required: false }] }] } });
|
|
1334
|
+
|
|
1387
1335
|
const ContentfulImports = [
|
|
1388
1336
|
ContentfulAudioComponent,
|
|
1389
1337
|
ContentfulFileComponent,
|
|
@@ -1392,6 +1340,24 @@ const ContentfulImports = [
|
|
|
1392
1340
|
ContentfulRichTextRendererComponent,
|
|
1393
1341
|
];
|
|
1394
1342
|
|
|
1343
|
+
/**
|
|
1344
|
+
* The shipped audio, file, image, video and link components, to spread into `provideContentfulConfig`.
|
|
1345
|
+
* An app that brings its own components, or renders text only, leaves this out and bundles none of them.
|
|
1346
|
+
* To replace one, spread the map instead: `components: { ...CONTENTFUL_DEFAULT_COMPONENTS.components, image: MyImage }`.
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* provideContentfulConfig({ ...CONTENTFUL_DEFAULT_COMPONENTS });
|
|
1350
|
+
*/
|
|
1351
|
+
const CONTENTFUL_DEFAULT_COMPONENTS = {
|
|
1352
|
+
components: {
|
|
1353
|
+
audio: ContentfulAudioComponent,
|
|
1354
|
+
file: ContentfulFileComponent,
|
|
1355
|
+
image: ContentfulImageComponent,
|
|
1356
|
+
video: ContentfulVideoComponent,
|
|
1357
|
+
link: ContentfulLinkComponent,
|
|
1358
|
+
},
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1395
1361
|
const provideContentfulConfig = (contentfulConfig) => {
|
|
1396
1362
|
return { provide: CONTENTFUL_CONFIG, useValue: createContentfulConfig(contentfulConfig) };
|
|
1397
1363
|
};
|
|
@@ -1401,5 +1367,5 @@ const isContentfulEntryType = (entry, type) => entry.sys.contentType.sys.id ===
|
|
|
1401
1367
|
* Generated bundle index. Do not edit.
|
|
1402
1368
|
*/
|
|
1403
1369
|
|
|
1404
|
-
export { CONTENTFUL_CONFIG, ContentfulAudioComponent, ContentfulFileComponent, ContentfulImageComponent, ContentfulImports, ContentfulLinkComponent, ContentfulRichTextRendererComponent, ContentfulVideoComponent, ET_CONTENTFUL_ANY_ENTRY_CONTENT_TYPE_SYS_ID, GQL_FRAGMENT_CONTENTFUL_ASSET, createContentfulConfig, createContentfulIncludeMap, generateContentfulImageSources, generateDefaultContentfulImageSource, injectContentfulConfig, isContentfulEntryType, isContentfulGqlAsset, isRichTextRootNode, marksToClass, marksToTags, parseContentfulImageSize, provideContentfulConfig, translateContentfulNodeTypeToHtmlTag };
|
|
1370
|
+
export { CONTENTFUL_CONFIG, CONTENTFUL_DEFAULT_COMPONENTS, CONTENTFUL_FILE_LABELS, ContentfulAudioComponent, ContentfulFileComponent, ContentfulImageComponent, ContentfulImports, ContentfulLinkComponent, ContentfulRichTextRendererComponent, ContentfulVideoComponent, DEFAULT_CONTENTFUL_FILE_LABELS, ET_CONTENTFUL_ANY_ENTRY_CONTENT_TYPE_SYS_ID, GQL_FRAGMENT_CONTENTFUL_ASSET, createContentfulConfig, createContentfulIncludeMap, generateContentfulImageSources, generateDefaultContentfulImageSource, injectContentfulConfig, injectContentfulFileLabels, isContentfulEntryType, isContentfulGqlAsset, isRichTextRootNode, marksToClass, marksToTags, parseContentfulImageSize, provideContentfulConfig, provideContentfulFileLabels, translateContentfulNodeTypeToHtmlTag };
|
|
1405
1371
|
//# sourceMappingURL=ethlete-contentful.mjs.map
|