@dotcms/angular 0.0.1-beta.9 → 1.0.0

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.
Files changed (126) hide show
  1. package/README.md +646 -167
  2. package/dotcms-angular.d.ts.map +1 -1
  3. package/esm2022/dotcms-angular.mjs +2 -2
  4. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/code.component.mjs +49 -0
  5. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/dot-contentlet.component.mjs +125 -0
  6. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/image.component.mjs +25 -0
  7. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/list.component.mjs +66 -0
  8. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/table.component.mjs +97 -0
  9. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/text.component.mjs +231 -0
  10. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/unknown.component.mjs +65 -0
  11. package/esm2022/lib/components/dotcms-block-editor-renderer/blocks/video.component.mjs +48 -0
  12. package/esm2022/lib/components/dotcms-block-editor-renderer/dotcms-block-editor-renderer.component.mjs +50 -0
  13. package/esm2022/lib/components/dotcms-block-editor-renderer/item/dotcms-block-editor-item.component.mjs +45 -0
  14. package/esm2022/lib/components/dotcms-editable-text/dotcms-editable-text.component.mjs +240 -0
  15. package/esm2022/lib/components/dotcms-editable-text/utils.mjs +20 -0
  16. package/esm2022/lib/components/dotcms-layout-body/components/column/column.component.mjs +45 -0
  17. package/esm2022/lib/components/dotcms-layout-body/components/container/components/container-not-found/container-not-found.component.mjs +52 -0
  18. package/esm2022/lib/components/dotcms-layout-body/components/container/components/empty-container/empty-container.component.mjs +47 -0
  19. package/esm2022/lib/components/dotcms-layout-body/components/container/container.component.mjs +99 -0
  20. package/esm2022/lib/components/dotcms-layout-body/components/contentlet/contentlet.component.mjs +145 -0
  21. package/esm2022/lib/components/dotcms-layout-body/components/fallback-component/fallback-component.component.mjs +47 -0
  22. package/esm2022/lib/components/dotcms-layout-body/components/page-error-message/page-error-message.component.mjs +55 -0
  23. package/esm2022/lib/components/dotcms-layout-body/components/row/row.component.mjs +46 -0
  24. package/esm2022/lib/components/dotcms-layout-body/dotcms-layout-body.component.mjs +69 -0
  25. package/esm2022/lib/directives/dotcms-show-when/dotcms-show-when.directive.mjs +49 -0
  26. package/esm2022/lib/models/index.mjs +2 -2
  27. package/esm2022/lib/providers/dotcms-client/dotcms-client.provider.mjs +52 -0
  28. package/esm2022/lib/providers/dotcms-image-loader/dotcms-image_loader.mjs +74 -0
  29. package/esm2022/lib/services/dotcms-editable-page.service.mjs +93 -0
  30. package/esm2022/lib/store/dotcms.store.mjs +61 -0
  31. package/esm2022/public_api.mjs +8 -0
  32. package/fesm2022/dotcms-angular.mjs +1578 -612
  33. package/fesm2022/dotcms-angular.mjs.map +1 -1
  34. package/index.d.ts +6 -6
  35. package/lib/components/dotcms-block-editor-renderer/blocks/code.component.d.ts +10 -0
  36. package/lib/components/dotcms-block-editor-renderer/blocks/code.component.d.ts.map +1 -0
  37. package/lib/components/dotcms-block-editor-renderer/blocks/dot-contentlet.component.d.ts +34 -0
  38. package/lib/components/dotcms-block-editor-renderer/blocks/dot-contentlet.component.d.ts.map +1 -0
  39. package/lib/components/dotcms-block-editor-renderer/blocks/image.component.d.ts +9 -0
  40. package/lib/components/dotcms-block-editor-renderer/blocks/image.component.d.ts.map +1 -0
  41. package/lib/components/dotcms-block-editor-renderer/blocks/list.component.d.ts +14 -0
  42. package/lib/components/dotcms-block-editor-renderer/blocks/list.component.d.ts.map +1 -0
  43. package/lib/components/dotcms-block-editor-renderer/blocks/table.component.d.ts +10 -0
  44. package/lib/components/dotcms-block-editor-renderer/blocks/table.component.d.ts.map +1 -0
  45. package/lib/components/dotcms-block-editor-renderer/blocks/text.component.d.ts +27 -0
  46. package/lib/components/dotcms-block-editor-renderer/blocks/text.component.d.ts.map +1 -0
  47. package/lib/components/dotcms-block-editor-renderer/blocks/unknown.component.d.ts +18 -0
  48. package/lib/components/dotcms-block-editor-renderer/blocks/unknown.component.d.ts.map +1 -0
  49. package/lib/components/dotcms-block-editor-renderer/blocks/video.component.d.ts +10 -0
  50. package/lib/components/dotcms-block-editor-renderer/blocks/video.component.d.ts.map +1 -0
  51. package/lib/components/dotcms-block-editor-renderer/dotcms-block-editor-renderer.component.d.ts +39 -0
  52. package/lib/components/dotcms-block-editor-renderer/dotcms-block-editor-renderer.component.d.ts.map +1 -0
  53. package/lib/components/dotcms-block-editor-renderer/item/dotcms-block-editor-item.component.d.ts +12 -0
  54. package/lib/components/dotcms-block-editor-renderer/item/dotcms-block-editor-item.component.d.ts.map +1 -0
  55. package/lib/components/{dot-editable-text/dot-editable-text.component.d.ts → dotcms-editable-text/dotcms-editable-text.component.d.ts} +30 -30
  56. package/lib/components/dotcms-editable-text/dotcms-editable-text.component.d.ts.map +1 -0
  57. package/lib/components/dotcms-editable-text/utils.d.ts.map +1 -0
  58. package/lib/components/dotcms-layout-body/components/column/column.component.d.ts +21 -0
  59. package/lib/components/dotcms-layout-body/components/column/column.component.d.ts.map +1 -0
  60. package/lib/components/dotcms-layout-body/components/container/components/container-not-found/container-not-found.component.d.ts +27 -0
  61. package/lib/components/dotcms-layout-body/components/container/components/container-not-found/container-not-found.component.d.ts.map +1 -0
  62. package/lib/components/dotcms-layout-body/components/container/components/empty-container/empty-container.component.d.ts +23 -0
  63. package/lib/components/dotcms-layout-body/components/container/components/empty-container/empty-container.component.d.ts.map +1 -0
  64. package/lib/components/dotcms-layout-body/components/container/container.component.d.ts +32 -0
  65. package/lib/components/dotcms-layout-body/components/container/container.component.d.ts.map +1 -0
  66. package/lib/components/dotcms-layout-body/components/contentlet/contentlet.component.d.ts +48 -0
  67. package/lib/components/dotcms-layout-body/components/contentlet/contentlet.component.d.ts.map +1 -0
  68. package/lib/components/dotcms-layout-body/components/fallback-component/fallback-component.component.d.ts +16 -0
  69. package/lib/components/dotcms-layout-body/components/fallback-component/fallback-component.component.d.ts.map +1 -0
  70. package/lib/components/dotcms-layout-body/components/page-error-message/page-error-message.component.d.ts +13 -0
  71. package/lib/components/dotcms-layout-body/components/page-error-message/page-error-message.component.d.ts.map +1 -0
  72. package/lib/components/dotcms-layout-body/components/row/row.component.d.ts +22 -0
  73. package/lib/components/dotcms-layout-body/components/row/row.component.d.ts.map +1 -0
  74. package/lib/components/dotcms-layout-body/dotcms-layout-body.component.d.ts +30 -0
  75. package/lib/components/dotcms-layout-body/dotcms-layout-body.component.d.ts.map +1 -0
  76. package/lib/directives/dotcms-show-when/dotcms-show-when.directive.d.ts +21 -0
  77. package/lib/directives/dotcms-show-when/dotcms-show-when.directive.d.ts.map +1 -0
  78. package/lib/models/index.d.ts +9 -10
  79. package/lib/models/index.d.ts.map +1 -1
  80. package/lib/providers/dotcms-client/dotcms-client.provider.d.ts +60 -0
  81. package/lib/providers/dotcms-client/dotcms-client.provider.d.ts.map +1 -0
  82. package/lib/{utils/image_loader.d.ts → providers/dotcms-image-loader/dotcms-image_loader.d.ts} +1 -1
  83. package/lib/providers/dotcms-image-loader/dotcms-image_loader.d.ts.map +1 -0
  84. package/lib/services/dotcms-editable-page.service.d.ts +40 -0
  85. package/lib/services/dotcms-editable-page.service.d.ts.map +1 -0
  86. package/lib/store/dotcms.store.d.ts +36 -0
  87. package/lib/store/dotcms.store.d.ts.map +1 -0
  88. package/package.json +9 -9
  89. package/public_api.d.ts +9 -0
  90. package/public_api.d.ts.map +1 -0
  91. package/esm2022/index.mjs +0 -6
  92. package/esm2022/lib/components/dot-editable-text/dot-editable-text.component.mjs +0 -225
  93. package/esm2022/lib/components/dot-editable-text/utils.mjs +0 -43
  94. package/esm2022/lib/components/no-component/no-component.component.mjs +0 -32
  95. package/esm2022/lib/layout/column/column.component.mjs +0 -45
  96. package/esm2022/lib/layout/container/container.component.mjs +0 -126
  97. package/esm2022/lib/layout/contentlet/contentlet.component.mjs +0 -120
  98. package/esm2022/lib/layout/dotcms-layout/dotcms-layout.component.mjs +0 -101
  99. package/esm2022/lib/layout/row/row.component.mjs +0 -29
  100. package/esm2022/lib/models/dotcms.model.mjs +0 -3
  101. package/esm2022/lib/services/dotcms-context/page-context.service.mjs +0 -75
  102. package/esm2022/lib/utils/image_loader.mjs +0 -75
  103. package/esm2022/lib/utils/index.mjs +0 -84
  104. package/index.d.ts.map +0 -1
  105. package/lib/components/dot-editable-text/dot-editable-text.component.d.ts.map +0 -1
  106. package/lib/components/dot-editable-text/utils.d.ts.map +0 -1
  107. package/lib/components/no-component/no-component.component.d.ts +0 -22
  108. package/lib/components/no-component/no-component.component.d.ts.map +0 -1
  109. package/lib/layout/column/column.component.d.ts +0 -29
  110. package/lib/layout/column/column.component.d.ts.map +0 -1
  111. package/lib/layout/container/container.component.d.ts +0 -88
  112. package/lib/layout/container/container.component.d.ts.map +0 -1
  113. package/lib/layout/contentlet/contentlet.component.d.ts +0 -86
  114. package/lib/layout/contentlet/contentlet.component.d.ts.map +0 -1
  115. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts +0 -68
  116. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts.map +0 -1
  117. package/lib/layout/row/row.component.d.ts +0 -20
  118. package/lib/layout/row/row.component.d.ts.map +0 -1
  119. package/lib/models/dotcms.model.d.ts +0 -416
  120. package/lib/models/dotcms.model.d.ts.map +0 -1
  121. package/lib/services/dotcms-context/page-context.service.d.ts +0 -49
  122. package/lib/services/dotcms-context/page-context.service.d.ts.map +0 -1
  123. package/lib/utils/image_loader.d.ts.map +0 -1
  124. package/lib/utils/index.d.ts +0 -63
  125. package/lib/utils/index.d.ts.map +0 -1
  126. /package/lib/components/{dot-editable-text → dotcms-editable-text}/utils.d.ts +0 -0
@@ -1,55 +1,200 @@
1
- import { TINYMCE_SCRIPT_SRC, EditorComponent } from '@tinymce/tinymce-angular';
2
1
  import * as i0 from '@angular/core';
3
- import { inject, Renderer2, ElementRef, SecurityContext, Component, ViewChild, Input, HostListener, Injectable, ChangeDetectionStrategy, HostBinding, signal, computed, DestroyRef } from '@angular/core';
2
+ import { inject, ViewContainerRef, TemplateRef, Input, Directive, makeEnvironmentProviders, Renderer2, ElementRef, SecurityContext, HostListener, ViewChild, Component, ChangeDetectionStrategy, computed, signal, Injectable, HostBinding } from '@angular/core';
3
+ import { UVE_MODE, DotCMSUVEAction, UVEEventType } from '@dotcms/types';
4
+ import { getUVEState, sendMessageToUVE, initUVE, updateNavigation, createUVESubscription } from '@dotcms/uve';
5
+ import { IMAGE_LOADER, NgComponentOutlet, AsyncPipe, NgTemplateOutlet, NgStyle } from '@angular/common';
6
+ import { createDotCMSClient } from '@dotcms/client';
7
+ import { EditorComponent, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
4
8
  import { DomSanitizer } from '@angular/platform-browser';
5
- import { NOTIFY_CLIENT, isInsideEditor, DotCmsClient, postMessageToEditor, CLIENT_ACTIONS, initEditor, updateNavigation } from '@dotcms/client';
6
- import { AsyncPipe, NgComponentOutlet, IMAGE_LOADER } from '@angular/common';
7
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
- import { ActivatedRoute } from '@angular/router';
9
- import { createUVESubscription } from '@dotcms/uve';
10
- import { BehaviorSubject } from 'rxjs';
11
- import { map } from 'rxjs/operators';
9
+ import { __DOTCMS_UVE_EVENT__, BlockEditorDefaultBlocks } from '@dotcms/types/internal';
10
+ import { __DEFAULT_TINYMCE_CONFIG__, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__, __TINYMCE_PATH_ON_DOTCMS__, isValidBlocks, PRODUCTION_MODE, DEVELOPMENT_MODE, EMPTY_CONTAINER_STYLE_ANGULAR, getDotContentletAttributes, CUSTOM_NO_COMPONENT, getDotContainerAttributes, getContainersData, getContentletsInContainer, getColumnPositionClasses, combineClasses } from '@dotcms/uve/internal';
11
+ import { Subject, of } from 'rxjs';
12
+ import { finalize } from 'rxjs/operators';
13
+
14
+ /**
15
+ * Directive to show a template when the UVE is in a specific mode.
16
+ *
17
+ * @example
18
+ * <div *dotCMSShowWhen="UVE_MODE.EDIT">
19
+ * This will be shown when the UVE is in edit mode.
20
+ * </div>
21
+ *
22
+ * @export
23
+ * @class DotCMSShowWhenDirective
24
+ */
25
+ class DotCMSShowWhenDirective {
26
+ #when = UVE_MODE.EDIT;
27
+ #hasView = false;
28
+ set dotCMSShowWhen(value) {
29
+ this.#when = value;
30
+ this.updateViewContainer();
31
+ }
32
+ #viewContainerRef = inject(ViewContainerRef);
33
+ #templateRef = inject(TemplateRef);
34
+ updateViewContainer() {
35
+ const state = getUVEState();
36
+ const shouldShow = state?.mode === this.#when;
37
+ if (shouldShow && !this.#hasView) {
38
+ this.#viewContainerRef.createEmbeddedView(this.#templateRef);
39
+ this.#hasView = true;
40
+ }
41
+ else if (!shouldShow && this.#hasView) {
42
+ this.#viewContainerRef.clear();
43
+ this.#hasView = false;
44
+ }
45
+ }
46
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSShowWhenDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
47
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.3", type: DotCMSShowWhenDirective, isStandalone: true, selector: "[dotCMSShowWhen]", inputs: { dotCMSShowWhen: "dotCMSShowWhen" }, ngImport: i0 }); }
48
+ }
49
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSShowWhenDirective, decorators: [{
50
+ type: Directive,
51
+ args: [{
52
+ selector: '[dotCMSShowWhen]',
53
+ standalone: true
54
+ }]
55
+ }], propDecorators: { dotCMSShowWhen: [{
56
+ type: Input
57
+ }] } });
58
+
59
+ /**
60
+ * Validates if a given path is a valid URL string
61
+ *
62
+ * @param path - The path to validate
63
+ * @returns boolean indicating if the path is valid
64
+ */
65
+ function isValidPath(path) {
66
+ if (typeof path !== 'string' || path.trim() === '') {
67
+ return false;
68
+ }
69
+ try {
70
+ new URL(path);
71
+ return true;
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }
77
+ /**
78
+ * Provides a DotCMS image loader configuration for the Angular Image directive
79
+ *
80
+ * @param path - The base URL path to the DotCMS instance, or empty to use current site
81
+ * @returns An array of providers for the IMAGE_LOADER token
82
+ * @throws Error if the provided path is invalid
83
+ * @example
84
+ * ```typescript
85
+ * // In your app.config.ts
86
+ * export const appConfig: ApplicationConfig = {
87
+ * providers: [
88
+ * provideDotCMSImageLoader('https://demo.dotcms.com')
89
+ * // Or use current site:
90
+ * // provideDotCMSImageLoader()
91
+ * ]
92
+ * };
93
+ * ```
94
+ */
95
+ function provideDotCMSImageLoader(path) {
96
+ // If path is provided, validate it
97
+ if (path && !isValidPath(path)) {
98
+ throw new Error(`Image loader has detected an invalid path (\`${path}\`). ` +
99
+ `To fix this, supply either the full URL to the dotCMS site, or leave it empty to use the current site.`);
100
+ }
101
+ return [
102
+ {
103
+ provide: IMAGE_LOADER,
104
+ useValue: (config) => createDotCMSURL(config, path)
105
+ }
106
+ ];
107
+ }
108
+ /**
109
+ * Creates a DotCMS-compatible URL for image loading
110
+ *
111
+ * @param config - The image loader configuration
112
+ * @param path - The base URL path to the DotCMS instance
113
+ * @returns A fully qualified URL for the image
114
+ * @internal
115
+ */
116
+ function createDotCMSURL(config, path) {
117
+ const { loaderParams, src, width } = config;
118
+ const params = loaderParams;
119
+ if (params?.isOutsideSRC) {
120
+ return src;
121
+ }
122
+ // Use empty string as fallback to support using current site
123
+ const dotcmsHost = path ? new URL(path).origin : '';
124
+ const imageSRC = src.includes('/dA/') ? src : `/dA/${src}`;
125
+ const languageId = params?.languageId ?? '1';
126
+ if (width) {
127
+ return `${dotcmsHost}${imageSRC}/${width}w?language_id=${languageId}`;
128
+ }
129
+ return `${dotcmsHost}${imageSRC}?language_id=${languageId}`;
130
+ }
131
+
132
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
133
+ class DotCMSClient {
134
+ constructor(client) {
135
+ return client;
136
+ }
137
+ }
138
+ /**
139
+ * Creates environment providers for the DotCMS client to be used in Angular applications.
140
+ * This function configures the DI container to provide a DotCMSClient instance
141
+ * throughout the application using the specified configuration.
142
+ *
143
+ * The provider should be registered at the application level (typically in main.ts)
144
+ * to ensure a single instance is shared across the entire application.
145
+ *
146
+ * @param options - Configuration object for the DotCMS client
147
+ * @param options.apiUrl - The base URL for the DotCMS API
148
+ * @param options.authToken - Authentication token for API requests (optional)
149
+ * @param options.siteId - The site identifier (optional)
150
+ * @returns Environment providers array that can be used with bootstrapApplication
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * // main.ts
155
+ * import { bootstrapApplication } from '@angular/platform-browser';
156
+ * import { AppComponent } from './app/app.component';
157
+ * import { provideDotCMSClient } from '@dotcms/angular';
158
+ *
159
+ * bootstrapApplication(AppComponent, {
160
+ * providers: [
161
+ * provideDotCMSClient({
162
+ * apiUrl: 'https://demo.dotcms.com',
163
+ * authToken: 'your-auth-token',
164
+ * siteId: 'your-site-id'
165
+ * }),
166
+ * // other providers...
167
+ * ]
168
+ * });
169
+ * ```
170
+ *
171
+ */
172
+ function provideDotCMSClient(options) {
173
+ const dotCMSClient = createDotCMSClient(options);
174
+ return makeEnvironmentProviders([
175
+ {
176
+ provide: DotCMSClient,
177
+ useFactory: () => new DotCMSClient(dotCMSClient)
178
+ }
179
+ ]);
180
+ }
12
181
 
13
182
  const DEFAULT_TINYMCE_CONFIG = {
14
- menubar: false,
15
- inline: true,
16
- valid_styles: {
17
- '*': 'font-size,font-family,color,text-decoration,text-align'
18
- },
19
- powerpaste_word_import: 'clean',
20
- powerpaste_html_import: 'clean',
21
- suffix: '.min', // Suffix to use when loading resources
22
- license_key: 'gpl'
183
+ ...__DEFAULT_TINYMCE_CONFIG__,
184
+ license_key: 'gpl' // Using self-hosted license key
23
185
  };
24
186
  const TINYMCE_CONFIG = {
25
187
  minimal: {
26
188
  ...DEFAULT_TINYMCE_CONFIG,
27
- plugins: 'link autolink',
28
- toolbar: 'bold italic underline | link',
29
- valid_elements: 'strong,em,span[style],a[href]'
189
+ ...__BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.minimal
30
190
  },
31
191
  full: {
32
192
  ...DEFAULT_TINYMCE_CONFIG,
33
- plugins: 'link lists autolink charmap',
34
- style_formats: [
35
- { title: 'Paragraph', format: 'p' },
36
- { title: 'Header 1', format: 'h1' },
37
- { title: 'Header 2', format: 'h2' },
38
- { title: 'Header 3', format: 'h3' },
39
- { title: 'Header 4', format: 'h4' },
40
- { title: 'Header 5', format: 'h5' },
41
- { title: 'Header 6', format: 'h6' },
42
- { title: 'Pre', format: 'pre' },
43
- { title: 'Code', format: 'code' }
44
- ],
45
- toolbar: [
46
- 'styleselect undo redo | bold italic underline | forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent | hr charmap removeformat | link'
47
- ]
193
+ ...__BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.full
48
194
  },
49
195
  plain: {
50
196
  ...DEFAULT_TINYMCE_CONFIG,
51
- plugins: '',
52
- toolbar: ''
197
+ ...__BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.plain
53
198
  }
54
199
  };
55
200
 
@@ -58,43 +203,39 @@ const TINYMCE_CONFIG = {
58
203
  * This component is responsible to render a text field that can be edited inline.
59
204
  *
60
205
  * @export
61
- * @class DotEditableTextComponent
206
+ * @class DotCMSEditableTextComponent
62
207
  * @implements {OnInit}
63
208
  * @implements {OnChanges}
64
209
  */
65
- class DotEditableTextComponent {
210
+ class DotCMSEditableTextComponent {
66
211
  constructor() {
67
212
  /**
68
213
  * Represents the mode of the editor which can be `plain`, `minimal`, or `full`
69
214
  *
70
215
  * @type {DOT_EDITABLE_TEXT_MODE}
71
- * @memberof DotEditableTextComponent
216
+ * @memberof DotCMSEditableTextComponent
72
217
  */
73
218
  this.mode = 'plain';
74
219
  /**
75
220
  * Represents the format of the editor which can be `text` or `html`
76
221
  *
77
222
  * @type {DOT_EDITABLE_TEXT_FORMAT}
78
- * @memberof DotEditableTextComponent
223
+ * @memberof DotCMSEditableTextComponent
79
224
  */
80
225
  this.format = 'text';
81
- /**
82
- * Represents the field name of the `contentlet` that can be edited
83
- *
84
- * @memberof DotEditableTextComponent
85
- */
86
- this.fieldName = '';
87
226
  /**
88
227
  * Represents the content of the `contentlet` that can be edited
89
228
  *
90
229
  * @protected
91
- * @memberof DotEditableTextComponent
230
+ * @memberof DotCMSEditableTextComponent
92
231
  */
93
232
  this.content = '';
233
+ this.#NotDotCMSHostMessage = 'The `dotCMSHost` parameter is not defined. Check that the UVE is sending the correct parameters.';
94
234
  this.#sanitizer = inject(DomSanitizer);
95
235
  this.#renderer = inject(Renderer2);
96
236
  this.#elementRef = inject(ElementRef);
97
237
  }
238
+ #NotDotCMSHostMessage;
98
239
  #sanitizer;
99
240
  #renderer;
100
241
  #elementRef;
@@ -102,16 +243,27 @@ class DotEditableTextComponent {
102
243
  * The TinyMCE editor
103
244
  *
104
245
  * @readonly
105
- * @memberof DotEditableTextComponent
246
+ * @memberof DotCMSEditableTextComponent
106
247
  */
107
248
  get editor() {
108
249
  return this.editorComponent?.editor;
109
250
  }
251
+ /**
252
+ * Represents if the component is inside the editor
253
+ *
254
+ * @protected
255
+ * @type {boolean}
256
+ * @memberof DotCMSEditableTextComponent
257
+ */
258
+ get isEditMode() {
259
+ const { mode, dotCMSHost } = getUVEState() || {};
260
+ return mode === UVE_MODE.EDIT && dotCMSHost;
261
+ }
110
262
  /**
111
263
  * Returns the number of pages the contentlet is on
112
264
  *
113
265
  * @readonly
114
- * @memberof DotEditableTextComponent
266
+ * @memberof DotCMSEditableTextComponent
115
267
  */
116
268
  get onNumberOfPages() {
117
269
  return this.contentlet['onNumberOfPages'] || 1;
@@ -121,11 +273,11 @@ class DotEditableTextComponent {
121
273
  *
122
274
  * @param {MessageEvent} { data }
123
275
  * @return {*}
124
- * @memberof DotEditableTextComponent
276
+ * @memberof DotCMSEditableTextComponent
125
277
  */
126
278
  onMessage({ data }) {
127
279
  const { name, payload } = data;
128
- if (name !== NOTIFY_CLIENT.UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS) {
280
+ if (name !== __DOTCMS_UVE_EVENT__.UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS) {
129
281
  return;
130
282
  }
131
283
  const { oldInode, inode } = payload;
@@ -136,14 +288,17 @@ class DotEditableTextComponent {
136
288
  }
137
289
  }
138
290
  ngOnInit() {
139
- this.isInsideEditor = isInsideEditor();
140
- if (!this.isInsideEditor) {
291
+ const { dotCMSHost } = getUVEState() || {};
292
+ if (!this.isEditMode) {
141
293
  this.innerHTMLToElement();
294
+ if (!dotCMSHost) {
295
+ console.warn(this.#NotDotCMSHostMessage);
296
+ }
142
297
  return;
143
298
  }
144
299
  this.init = {
145
300
  ...TINYMCE_CONFIG[this.mode],
146
- base_url: `${DotCmsClient.dotcmsUrl}/ext/tinymcev7`
301
+ base_url: `${dotCMSHost}/ext/tinymcev7`
147
302
  };
148
303
  }
149
304
  ngOnChanges() {
@@ -157,18 +312,18 @@ class DotEditableTextComponent {
157
312
  *
158
313
  * @param {EventObj<MouseEvent>} { event }
159
314
  * @return {*}
160
- * @memberof DotEditableTextComponent
315
+ * @memberof DotCMSEditableTextComponent
161
316
  */
162
317
  onMouseDown({ event }) {
163
- if (this.onNumberOfPages <= 1 || this.editorComponent.editor.hasFocus()) {
318
+ if (Number(this.onNumberOfPages) <= 1 || this.editorComponent.editor.hasFocus()) {
164
319
  return;
165
320
  }
166
321
  const { inode, languageId: language } = this.contentlet;
167
322
  event.stopPropagation();
168
323
  event.preventDefault();
169
324
  try {
170
- postMessageToEditor({
171
- action: CLIENT_ACTIONS.COPY_CONTENTLET_INLINE_EDITING,
325
+ sendMessageToUVE({
326
+ action: DotCMSUVEAction.COPY_CONTENTLET_INLINE_EDITING,
172
327
  payload: {
173
328
  dataset: {
174
329
  inode,
@@ -186,7 +341,7 @@ class DotEditableTextComponent {
186
341
  * Handle focus out event
187
342
  *
188
343
  * @return {*}
189
- * @memberof DotEditableTextComponent
344
+ * @memberof DotCMSEditableTextComponent
190
345
  */
191
346
  onFocusOut() {
192
347
  const content = this.editor.getContent({ format: this.format });
@@ -195,8 +350,8 @@ class DotEditableTextComponent {
195
350
  }
196
351
  const { inode, languageId: langId } = this.contentlet;
197
352
  try {
198
- postMessageToEditor({
199
- action: CLIENT_ACTIONS.UPDATE_CONTENTLET_INLINE_EDITING,
353
+ sendMessageToUVE({
354
+ action: DotCMSUVEAction.UPDATE_CONTENTLET_INLINE_EDITING,
200
355
  payload: {
201
356
  content,
202
357
  dataset: {
@@ -217,7 +372,7 @@ class DotEditableTextComponent {
217
372
  * @private
218
373
  * @param {string} editedContent
219
374
  * @return {*}
220
- * @memberof DotEditableTextComponent
375
+ * @memberof DotCMSEditableTextComponent
221
376
  */
222
377
  innerHTMLToElement() {
223
378
  const element = this.#elementRef.nativeElement;
@@ -231,31 +386,33 @@ class DotEditableTextComponent {
231
386
  * @private
232
387
  * @param {string} editedContent
233
388
  * @return {*}
234
- * @memberof DotEditableTextComponent
389
+ * @memberof DotCMSEditableTextComponent
235
390
  */
236
391
  didContentChange(editedContent) {
237
392
  return this.content !== editedContent;
238
393
  }
239
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotEditableTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
240
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotEditableTextComponent, isStandalone: true, selector: "dot-editable-text", inputs: { mode: "mode", format: "format", contentlet: "contentlet", fieldName: "fieldName" }, host: { listeners: { "window:message": "onMessage($event)" } }, providers: [
394
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSEditableTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
395
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotCMSEditableTextComponent, isStandalone: true, selector: "dotcms-editable-text", inputs: { mode: "mode", format: "format", contentlet: "contentlet", fieldName: "fieldName" }, host: { listeners: { "window:message": "onMessage($event)" } }, providers: [
241
396
  {
242
397
  provide: TINYMCE_SCRIPT_SRC,
243
398
  useFactory: () => {
244
- return `${DotCmsClient.dotcmsUrl}/ext/tinymcev7/tinymce.min.js`;
399
+ const { dotCMSHost } = getUVEState() || {};
400
+ return `${dotCMSHost || ''}${__TINYMCE_PATH_ON_DOTCMS__}`;
245
401
  }
246
402
  }
247
- ], viewQueries: [{ propertyName: "editorComponent", first: true, predicate: EditorComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (isInsideEditor) {\n <editor\n #tinyEditor\n [init]=\"init\"\n [initialValue]=\"content\"\n (onMouseDown)=\"onMouseDown($event)\"\n (onFocusOut)=\"onFocusOut()\" />\n}\n", styles: [":host ::ng-deep .mce-content-body:not(.mce-edit-focus):hover{outline:2px solid #006ce7;border-radius:4px}\n"], dependencies: [{ kind: "component", type: EditorComponent, selector: "editor", inputs: ["cloudChannel", "apiKey", "init", "id", "initialValue", "outputFormat", "inline", "tagName", "plugins", "toolbar", "modelEvents", "allowedEvents", "ignoreEvents", "disabled"] }] }); }
403
+ ], viewQueries: [{ propertyName: "editorComponent", first: true, predicate: EditorComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (isEditMode) {\n <editor\n #tinyEditor\n [init]=\"init\"\n [initialValue]=\"content\"\n (onMouseDown)=\"onMouseDown($event)\"\n (onFocusOut)=\"onFocusOut()\" />\n}\n", styles: [":host ::ng-deep .mce-content-body:not(.mce-edit-focus){outline:2px solid #006ce7;border-radius:4px}\n"], dependencies: [{ kind: "component", type: EditorComponent, selector: "editor", inputs: ["cloudChannel", "apiKey", "init", "id", "initialValue", "outputFormat", "inline", "tagName", "plugins", "toolbar", "modelEvents", "allowedEvents", "ignoreEvents", "disabled"] }] }); }
248
404
  }
249
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotEditableTextComponent, decorators: [{
405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSEditableTextComponent, decorators: [{
250
406
  type: Component,
251
- args: [{ selector: 'dot-editable-text', standalone: true, imports: [EditorComponent], providers: [
407
+ args: [{ selector: 'dotcms-editable-text', standalone: true, imports: [EditorComponent], providers: [
252
408
  {
253
409
  provide: TINYMCE_SCRIPT_SRC,
254
410
  useFactory: () => {
255
- return `${DotCmsClient.dotcmsUrl}/ext/tinymcev7/tinymce.min.js`;
411
+ const { dotCMSHost } = getUVEState() || {};
412
+ return `${dotCMSHost || ''}${__TINYMCE_PATH_ON_DOTCMS__}`;
256
413
  }
257
414
  }
258
- ], template: "@if (isInsideEditor) {\n <editor\n #tinyEditor\n [init]=\"init\"\n [initialValue]=\"content\"\n (onMouseDown)=\"onMouseDown($event)\"\n (onFocusOut)=\"onFocusOut()\" />\n}\n", styles: [":host ::ng-deep .mce-content-body:not(.mce-edit-focus):hover{outline:2px solid #006ce7;border-radius:4px}\n"] }]
415
+ ], template: "@if (isEditMode) {\n <editor\n #tinyEditor\n [init]=\"init\"\n [initialValue]=\"content\"\n (onMouseDown)=\"onMouseDown($event)\"\n (onFocusOut)=\"onFocusOut()\" />\n}\n", styles: [":host ::ng-deep .mce-content-body:not(.mce-edit-focus){outline:2px solid #006ce7;border-radius:4px}\n"] }]
259
416
  }], propDecorators: { editorComponent: [{
260
417
  type: ViewChild,
261
418
  args: [EditorComponent]
@@ -272,404 +429,1225 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImpor
272
429
  args: ['window:message', ['$event']]
273
430
  }] } });
274
431
 
275
- /**
276
- * @author dotCMS
277
- * @description This service is responsible for managing the page context.
278
- * @export
279
- * @class PageContextService
280
- */
281
- class PageContextService {
282
- constructor() {
283
- this.context$ = new BehaviorSubject(null);
284
- }
285
- /**
286
- * @description Get the context
287
- * @readonly
288
- * @type {DotCMSPageContext}
289
- * @memberof PageContextService
290
- */
291
- get context() {
292
- return this.context$.getValue();
293
- }
294
- /**
295
- * @description Get the context as an observable
296
- * @readonly
297
- * @memberof PageContextService
298
- */
299
- get contextObs$() {
300
- return this.context$.asObservable();
301
- }
302
- /**
303
- * @description Get the current page asset
304
- * @readonly
305
- * @type {(Observable<DotCMSPageAsset | null>)}
306
- * @memberof PageContextService
307
- */
308
- get currentPage$() {
309
- return this.contextObs$.pipe(map((context) => context?.pageAsset || null));
310
- }
311
- /**
312
- *
313
- * @description Set the context
314
- * @param {DotCMSPageAsset} value
315
- * @memberof DotcmsContextService
316
- */
317
- setContext(pageAsset, components) {
318
- this.context$.next({
319
- pageAsset,
320
- components,
321
- isInsideEditor: isInsideEditor()
322
- });
323
- }
324
- /**
325
- * @description Set the page asset in the context
326
- * @param {DotCMSPageAsset} pageAsset
327
- * @memberof PageContextService
328
- */
329
- setPageAsset(pageAsset) {
330
- this.context$.next({
331
- ...this.context,
332
- pageAsset
333
- });
334
- }
335
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: PageContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
336
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: PageContextService, providedIn: 'root' }); }
432
+ class DotCodeBlock {
433
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCodeBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
434
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotCodeBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-code-block", ngImport: i0, template: `
435
+ <pre>
436
+ <code>
437
+ <ng-content />
438
+ </code>
439
+ </pre>
440
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
337
441
  }
338
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: PageContextService, decorators: [{
339
- type: Injectable,
442
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCodeBlock, decorators: [{
443
+ type: Component,
340
444
  args: [{
341
- providedIn: 'root'
445
+ selector: 'dotcms-block-editor-renderer-code-block',
446
+ standalone: true,
447
+ template: `
448
+ <pre>
449
+ <code>
450
+ <ng-content />
451
+ </code>
452
+ </pre>
453
+ `,
454
+ changeDetection: ChangeDetectionStrategy.OnPush
455
+ }]
456
+ }] });
457
+ class DotBlockQuote {
458
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotBlockQuote, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
459
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotBlockQuote, isStandalone: true, selector: "dotcms-block-editor-renderer-block-quote", ngImport: i0, template: `
460
+ <blockquote>
461
+ <ng-content />
462
+ </blockquote>
463
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
464
+ }
465
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotBlockQuote, decorators: [{
466
+ type: Component,
467
+ args: [{
468
+ selector: 'dotcms-block-editor-renderer-block-quote',
469
+ standalone: true,
470
+ template: `
471
+ <blockquote>
472
+ <ng-content />
473
+ </blockquote>
474
+ `,
475
+ changeDetection: ChangeDetectionStrategy.OnPush
342
476
  }]
343
477
  }] });
344
478
 
345
- //Changed the type, to avoid SQ issue.
346
- //This should be put inside a lib
347
- /**
348
- * Represents a mapping of numbers to corresponding CSS class names for column end values.
349
- * @typedef {Record<number, string | null>} EndClassMap
350
- */
351
- const endClassMap = {
352
- 1: 'col-end-1',
353
- 2: 'col-end-2',
354
- 3: 'col-end-3',
355
- 4: 'col-end-4',
356
- 5: 'col-end-5',
357
- 6: 'col-end-6',
358
- 7: 'col-end-7',
359
- 8: 'col-end-8',
360
- 9: 'col-end-9',
361
- 10: 'col-end-10',
362
- 11: 'col-end-11',
363
- 12: 'col-end-12',
364
- 13: 'col-end-13'
365
- };
366
- //Changed the type, to avoid SQ issue.
367
- //This should be put inside a lib
368
- /**
369
- * Represents a mapping of numbers to CSS class names for starting columns.
370
- * @typedef {Record<number, string | null>} StartClassMap
371
- */
372
- const startClassMap = {
373
- 1: 'col-start-1',
374
- 2: 'col-start-2',
375
- 3: 'col-start-3',
376
- 4: 'col-start-4',
377
- 5: 'col-start-5',
378
- 6: 'col-start-6',
379
- 7: 'col-start-7',
380
- 8: 'col-start-8',
381
- 9: 'col-start-9',
382
- 10: 'col-start-10',
383
- 11: 'col-start-11',
384
- 12: 'col-start-12'
385
- };
386
- /**
387
- * Retrieves the data for a set of containers.
388
- *
389
- * @param containers - The DotCMSPageAssetContainer object containing the containers.
390
- * @param containerRef - The DotCMSContainer object representing the container reference.
391
- * @returns An object containing the container data, accept types, contentlets, and variant ID.
392
- */
393
- const getContainersData = (containers, containerRef) => {
394
- const { identifier, uuid } = containerRef;
395
- const { containerStructures, container } = containers[identifier];
396
- const { variantId } = container?.parentPermissionable || {};
397
- const acceptTypes = containerStructures
398
- .map((structure) => structure.contentTypeVar)
399
- .join(',');
400
- // Get the contentlets for "this" container
401
- const contentlets = containers[identifier].contentlets[`uuid-${uuid}`] ??
402
- containers[identifier].contentlets[`uuid-dotParser_${uuid}`];
403
- if (!contentlets) {
404
- console.warn(`We couldn't find the contentlets for the container with the identifier ${identifier} and the uuid ${uuid} becareful by adding content to this container.\nWe recommend to change the container in the layout and add the content again.`);
405
- }
406
- return {
407
- ...containers[identifier].container,
408
- acceptTypes,
409
- contentlets: contentlets ?? [],
410
- variantId
411
- };
412
- };
413
- /**
414
- * Returns the position style classes based on the start and end values.
415
- * Used to set the grid column start and end values.
416
- * @param start - The start value.
417
- * @param end - The end value.
418
- * @returns An object containing the startClass and endClass.
419
- */
420
- const getPositionStyleClasses = (start, end) => {
421
- const startClass = startClassMap[start];
422
- const endClass = endClassMap[end];
423
- return {
424
- startClass,
425
- endClass
426
- };
427
- };
428
-
429
- /**
430
- * This component is responsible to display a message when there is no component for a contentlet.
431
- *
432
- * @export
433
- * @class NoComponent
434
- */
435
- class NoComponent {
479
+ class NoComponentProvided {
436
480
  constructor() {
437
- /**
438
- * The data-testid attribute used for identifying the component during testing.
439
- */
440
- this.testId = 'no-component';
481
+ this.style = {
482
+ backgroundColor: '#fffaf0',
483
+ color: '#333',
484
+ padding: '1rem',
485
+ borderRadius: '0.5rem',
486
+ marginBottom: '1rem',
487
+ marginTop: '1rem',
488
+ border: '1px solid #ed8936'
489
+ };
441
490
  }
442
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
443
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: NoComponent, isStandalone: true, selector: "dotcms-no-component", inputs: { contentlet: "contentlet" }, host: { properties: { "attr.data-testid": "this.testId" } }, ngImport: i0, template: `
444
- No Component for {{ contentlet.contentType }}
445
- `, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
491
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NoComponentProvided, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
492
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: NoComponentProvided, isStandalone: true, selector: "dotcms-no-component-provided", inputs: { contentType: "contentType" }, ngImport: i0, template: `
493
+ <div data-testid="no-component-provided" [style]="style">
494
+ <strong style="color: #c05621">Dev Warning</strong>
495
+ : No component or custom renderer provided for content type
496
+ <strong style="color: #c05621">{{ contentType || 'Unknown' }}</strong>
497
+ .
498
+ <br />
499
+ Please refer to the
500
+ <a
501
+ href="https://dev.dotcms.com/docs/block-editor"
502
+ target="_blank"
503
+ rel="noopener noreferrer"
504
+ style="color: #c05621">
505
+ Block Editor Custom Renderers Documentation
506
+ </a>
507
+ for guidance.
508
+ </div>
509
+ `, isInline: true }); }
446
510
  }
447
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NoComponent, decorators: [{
511
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NoComponentProvided, decorators: [{
448
512
  type: Component,
449
- args: [{ selector: 'dotcms-no-component', standalone: true, template: `
450
- No Component for {{ contentlet.contentType }}
451
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
452
- }], propDecorators: { contentlet: [{
513
+ args: [{
514
+ selector: 'dotcms-no-component-provided',
515
+ standalone: true,
516
+ template: `
517
+ <div data-testid="no-component-provided" [style]="style">
518
+ <strong style="color: #c05621">Dev Warning</strong>
519
+ : No component or custom renderer provided for content type
520
+ <strong style="color: #c05621">{{ contentType || 'Unknown' }}</strong>
521
+ .
522
+ <br />
523
+ Please refer to the
524
+ <a
525
+ href="https://dev.dotcms.com/docs/block-editor"
526
+ target="_blank"
527
+ rel="noopener noreferrer"
528
+ style="color: #c05621">
529
+ Block Editor Custom Renderers Documentation
530
+ </a>
531
+ for guidance.
532
+ </div>
533
+ `
534
+ }]
535
+ }], propDecorators: { contentType: [{
453
536
  type: Input
454
- }], testId: [{
455
- type: HostBinding,
456
- args: ['attr.data-testid']
457
537
  }] } });
458
-
459
538
  /**
460
- * This component is responsible to display a contentlet.
461
- *
462
- * @export
463
- * @class ContentletComponent
464
- * @implements {OnChanges}
539
+ * DotContent component that renders content based on content type
465
540
  */
466
- class ContentletComponent {
541
+ class DotContentletBlock {
467
542
  constructor() {
468
- /**
469
- * The identifier of contentlet component.
470
- *
471
- * @type {(string | null)}
472
- * @memberof ContentletComponent
473
- */
474
- this.identifier = null;
475
- /**
476
- * The base type of contentlet component.
477
- *
478
- * @type {(string | null)}
479
- * @memberof ContentletComponent
480
- */
481
- this.baseType = null;
482
- /**
483
- * The title of contentlet component.
484
- *
485
- * @type {(string | null)}
486
- * @memberof ContentletComponent
487
- */
488
- this.title = null;
489
- /**
490
- * The inode of contentlet component.
491
- *
492
- * @type {(string | null)}
493
- * @memberof ContentletComponent
494
- */
495
- this.inode = null;
496
- /**
497
- * The type of contentlet component.
498
- *
499
- * @type {(string | null)}
500
- * @memberof ContentletComponent
501
- */
502
- this.dotType = null;
503
- /**
504
- * The container of contentlet component.
505
- *
506
- * @type {(string | null)}
507
- * @memberof ContentletComponent
508
- */
509
- this.dotContainer = null;
510
- /**
511
- * The number of pages where the contentlet appears
512
- *
513
- * @type {(string | null)}
514
- * @memberof ContentletComponent
515
- */
516
- this.numberOfPages = null;
517
- /**
518
- * The content of contentlet component.
519
- *
520
- * @type {(string | null)}
521
- * @memberof ContentletComponent
522
- */
523
- this.dotContent = null;
543
+ this.$data = computed(() => this.attrs?.['data']);
544
+ this.DOT_CONTENT_NO_DATA_MESSAGE = '[DotCMSBlockEditorRenderer]: No data provided for Contentlet Block. Try to add a contentlet to the block editor. If the error persists, please contact the DotCMS support team.';
545
+ this.DOT_CONTENT_NO_MATCHING_COMPONENT_MESSAGE = (contentType) => `[DotCMSBlockEditorRenderer]: No matching component found for content type: ${contentType}. Provide a custom renderer for this content type to fix this error.`;
524
546
  }
525
- ngOnChanges() {
526
- this.identifier = this.contentlet.identifier;
527
- this.baseType = this.contentlet.baseType;
528
- this.title = this.contentlet.title;
529
- this.inode = this.contentlet.inode;
530
- this.dotType = this.contentlet.contentType;
531
- this.dotContainer = this.container;
532
- this.numberOfPages = this.contentlet['onNumberOfPages'];
533
- this.dotContent = 'contentlet';
547
+ get isDevMode() {
548
+ return getUVEState()?.mode === UVE_MODE.EDIT;
534
549
  }
535
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContentletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
536
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: ContentletComponent, isStandalone: true, selector: "dotcms-contentlet-wrapper", inputs: { contentlet: "contentlet", container: "container" }, host: { properties: { "attr.data-dot-identifier": "this.identifier", "attr.data-dot-basetype": "this.baseType", "attr.data-dot-title": "this.title", "attr.data-dot-inode": "this.inode", "attr.data-dot-type": "this.dotType", "attr.data-dot-container": "this.dotContainer", "attr.data-dot-on-number-of-pages": "this.numberOfPages", "attr.data-dot-object": "this.dotContent" } }, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
550
+ ngOnInit() {
551
+ if (!this.$data()) {
552
+ console.error(this.DOT_CONTENT_NO_DATA_MESSAGE);
553
+ return;
554
+ }
555
+ const contentType = this.$data()?.contentType || '';
556
+ this.contentComponent = this.customRenderers?.[contentType];
557
+ if (!this.contentComponent) {
558
+ console.warn(this.DOT_CONTENT_NO_MATCHING_COMPONENT_MESSAGE(contentType));
559
+ }
560
+ }
561
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotContentletBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
562
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotContentletBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-contentlet", inputs: { customRenderers: "customRenderers", attrs: "attrs" }, ngImport: i0, template: `
563
+ @if (contentComponent) {
564
+ <ng-container
565
+ *ngComponentOutlet="
566
+ contentComponent | async;
567
+ inputs: { contentlet: $data() }
568
+ "></ng-container>
569
+ } @else if (isDevMode) {
570
+ <dotcms-no-component-provided [contentType]="$data()?.contentType" />
571
+ }
572
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: NoComponentProvided, selector: "dotcms-no-component-provided", inputs: ["contentType"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
537
573
  }
538
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContentletComponent, decorators: [{
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotContentletBlock, decorators: [{
539
575
  type: Component,
540
576
  args: [{
541
- selector: 'dotcms-contentlet-wrapper',
577
+ selector: 'dotcms-block-editor-renderer-contentlet',
542
578
  standalone: true,
543
- template: '<ng-content></ng-content>',
544
- changeDetection: ChangeDetectionStrategy.OnPush
579
+ imports: [NgComponentOutlet, AsyncPipe, NoComponentProvided],
580
+ changeDetection: ChangeDetectionStrategy.OnPush,
581
+ template: `
582
+ @if (contentComponent) {
583
+ <ng-container
584
+ *ngComponentOutlet="
585
+ contentComponent | async;
586
+ inputs: { contentlet: $data() }
587
+ "></ng-container>
588
+ } @else if (isDevMode) {
589
+ <dotcms-no-component-provided [contentType]="$data()?.contentType" />
590
+ }
591
+ `
545
592
  }]
546
- }], propDecorators: { contentlet: [{
547
- type: Input,
548
- args: [{ required: true }]
549
- }], container: [{
593
+ }], propDecorators: { customRenderers: [{
594
+ type: Input
595
+ }], attrs: [{
550
596
  type: Input
551
- }], identifier: [{
552
- type: HostBinding,
553
- args: ['attr.data-dot-identifier']
554
- }], baseType: [{
555
- type: HostBinding,
556
- args: ['attr.data-dot-basetype']
557
- }], title: [{
558
- type: HostBinding,
559
- args: ['attr.data-dot-title']
560
- }], inode: [{
561
- type: HostBinding,
562
- args: ['attr.data-dot-inode']
563
- }], dotType: [{
564
- type: HostBinding,
565
- args: ['attr.data-dot-type']
566
- }], dotContainer: [{
567
- type: HostBinding,
568
- args: ['attr.data-dot-container']
569
- }], numberOfPages: [{
570
- type: HostBinding,
571
- args: ['attr.data-dot-on-number-of-pages']
572
- }], dotContent: [{
573
- type: HostBinding,
574
- args: ['attr.data-dot-object']
575
597
  }] } });
576
598
 
577
- /**
578
- * This component is responsible to display a container with contentlets.
579
- *
580
- * @export
581
- * @class ContainerComponent
582
- * @implements {OnChanges}
583
- */
584
- class ContainerComponent {
599
+ class DotImageBlock {
585
600
  constructor() {
586
- this.pageContextService = inject(PageContextService);
587
- this.NoComponent = NoComponent;
588
- this.$isInsideEditor = signal(false);
589
- this.$contentlets = signal([]);
590
- this.$dotContainer = signal(null);
591
- this.$dotContainerAsString = computed(() => JSON.stringify(this.$dotContainer()));
601
+ this.$srcURL = computed(() => this.attrs?.['src']);
602
+ }
603
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotImageBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
604
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotImageBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-image", inputs: { attrs: "attrs" }, ngImport: i0, template: `
605
+ <img [alt]="attrs?.['alt']" [src]="$srcURL()" />
606
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
607
+ }
608
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotImageBlock, decorators: [{
609
+ type: Component,
610
+ args: [{
611
+ selector: 'dotcms-block-editor-renderer-image',
612
+ standalone: true,
613
+ template: `
614
+ <img [alt]="attrs?.['alt']" [src]="$srcURL()" />
615
+ `,
616
+ changeDetection: ChangeDetectionStrategy.OnPush
617
+ }]
618
+ }], propDecorators: { attrs: [{
619
+ type: Input
620
+ }] } });
621
+
622
+ class DotBulletList {
623
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotBulletList, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
624
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotBulletList, isStandalone: true, selector: "dotcms-block-editor-renderer-bullet-list", ngImport: i0, template: `
625
+ <ul>
626
+ <ng-content />
627
+ </ul>
628
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
629
+ }
630
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotBulletList, decorators: [{
631
+ type: Component,
632
+ args: [{
633
+ selector: 'dotcms-block-editor-renderer-bullet-list',
634
+ standalone: true,
635
+ changeDetection: ChangeDetectionStrategy.OnPush,
636
+ template: `
637
+ <ul>
638
+ <ng-content />
639
+ </ul>
640
+ `
641
+ }]
642
+ }] });
643
+ class DotOrdererList {
644
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotOrdererList, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
645
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotOrdererList, isStandalone: true, selector: "dotcms-block-editor-renderer-ordered-list", ngImport: i0, template: `
646
+ <ol>
647
+ <ng-content />
648
+ </ol>
649
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
650
+ }
651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotOrdererList, decorators: [{
652
+ type: Component,
653
+ args: [{
654
+ selector: 'dotcms-block-editor-renderer-ordered-list',
655
+ standalone: true,
656
+ changeDetection: ChangeDetectionStrategy.OnPush,
657
+ template: `
658
+ <ol>
659
+ <ng-content />
660
+ </ol>
661
+ `
662
+ }]
663
+ }] });
664
+ class DotListItem {
665
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotListItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
666
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotListItem, isStandalone: true, selector: "dotcms-block-editor-renderer-list-item", ngImport: i0, template: `
667
+ <li>
668
+ <ng-content />
669
+ </li>
670
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
671
+ }
672
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotListItem, decorators: [{
673
+ type: Component,
674
+ args: [{
675
+ selector: 'dotcms-block-editor-renderer-list-item',
676
+ standalone: true,
677
+ changeDetection: ChangeDetectionStrategy.OnPush,
678
+ template: `
679
+ <li>
680
+ <ng-content />
681
+ </li>
682
+ `
683
+ }]
684
+ }] });
685
+
686
+ class DotTableBlock {
687
+ constructor() {
688
+ this.blockEditorItem = DotCMSBlockEditorItemComponent;
689
+ }
690
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotTableBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
691
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotTableBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-table", inputs: { content: "content" }, ngImport: i0, template: `
692
+ <table>
693
+ <thead>
694
+ @for (rowNode of content?.slice(0, 1); track rowNode.type) {
695
+ <tr>
696
+ @for (cellNode of rowNode.content; track cellNode.type) {
697
+ <th
698
+ [attr.colspan]="cellNode.attrs?.['colspan'] || 1"
699
+ [attr.rowspan]="cellNode.attrs?.['rowspan'] || 1">
700
+ <ng-container
701
+ *ngComponentOutlet="
702
+ blockEditorItem;
703
+ inputs: { content: cellNode.content }
704
+ "></ng-container>
705
+ </th>
706
+ }
707
+ </tr>
708
+ }
709
+ </thead>
710
+ <tbody>
711
+ @for (rowNode of content?.slice(1); track rowNode.type) {
712
+ <tr>
713
+ @for (cellNode of rowNode.content; track cellNode.type) {
714
+ <td
715
+ [attr.colspan]="cellNode.attrs?.['colspan'] || 1"
716
+ [attr.rowspan]="cellNode.attrs?.['rowspan'] || 1">
717
+ <ng-container
718
+ *ngComponentOutlet="
719
+ blockEditorItem;
720
+ inputs: { content: cellNode.content }
721
+ "></ng-container>
722
+ </td>
723
+ }
724
+ </tr>
725
+ }
726
+ </tbody>
727
+ </table>
728
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); }
729
+ }
730
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotTableBlock, decorators: [{
731
+ type: Component,
732
+ args: [{
733
+ selector: 'dotcms-block-editor-renderer-table',
734
+ standalone: true,
735
+ imports: [NgComponentOutlet],
736
+ template: `
737
+ <table>
738
+ <thead>
739
+ @for (rowNode of content?.slice(0, 1); track rowNode.type) {
740
+ <tr>
741
+ @for (cellNode of rowNode.content; track cellNode.type) {
742
+ <th
743
+ [attr.colspan]="cellNode.attrs?.['colspan'] || 1"
744
+ [attr.rowspan]="cellNode.attrs?.['rowspan'] || 1">
745
+ <ng-container
746
+ *ngComponentOutlet="
747
+ blockEditorItem;
748
+ inputs: { content: cellNode.content }
749
+ "></ng-container>
750
+ </th>
751
+ }
752
+ </tr>
753
+ }
754
+ </thead>
755
+ <tbody>
756
+ @for (rowNode of content?.slice(1); track rowNode.type) {
757
+ <tr>
758
+ @for (cellNode of rowNode.content; track cellNode.type) {
759
+ <td
760
+ [attr.colspan]="cellNode.attrs?.['colspan'] || 1"
761
+ [attr.rowspan]="cellNode.attrs?.['rowspan'] || 1">
762
+ <ng-container
763
+ *ngComponentOutlet="
764
+ blockEditorItem;
765
+ inputs: { content: cellNode.content }
766
+ "></ng-container>
767
+ </td>
768
+ }
769
+ </tr>
770
+ }
771
+ </tbody>
772
+ </table>
773
+ `
774
+ }]
775
+ }], propDecorators: { content: [{
776
+ type: Input
777
+ }] } });
778
+
779
+ class DotParagraphBlock {
780
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotParagraphBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
781
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotParagraphBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-paragraph", ngImport: i0, template: `
782
+ <p>
783
+ <ng-content />
784
+ </p>
785
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
786
+ }
787
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotParagraphBlock, decorators: [{
788
+ type: Component,
789
+ args: [{
790
+ selector: 'dotcms-block-editor-renderer-paragraph',
791
+ standalone: true,
792
+ changeDetection: ChangeDetectionStrategy.OnPush,
793
+ template: `
794
+ <p>
795
+ <ng-content />
796
+ </p>
797
+ `
798
+ }]
799
+ }] });
800
+ class DotHeadingBlock {
801
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotHeadingBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
802
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotHeadingBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-heading", inputs: { level: "level" }, ngImport: i0, template: `
803
+ @switch (level) {
804
+ @case ('1') {
805
+ <h1>
806
+ <ng-content />
807
+ </h1>
808
+ }
809
+ @case ('2') {
810
+ <h2>
811
+ <ng-content />
812
+ </h2>
813
+ }
814
+ @case ('3') {
815
+ <h3>
816
+ <ng-content />
817
+ </h3>
818
+ }
819
+ @case ('4') {
820
+ <h4>
821
+ <ng-content />
822
+ </h4>
823
+ }
824
+ @case ('5') {
825
+ <h5>
826
+ <ng-content />
827
+ </h5>
828
+ }
829
+ @case ('6') {
830
+ <h6>
831
+ <ng-content />
832
+ </h6>
833
+ }
834
+ @default {
835
+ <h1>
836
+ <ng-content />
837
+ </h1>
838
+ }
839
+ }
840
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
841
+ }
842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotHeadingBlock, decorators: [{
843
+ type: Component,
844
+ args: [{
845
+ selector: 'dotcms-block-editor-renderer-heading',
846
+ standalone: true,
847
+ changeDetection: ChangeDetectionStrategy.OnPush,
848
+ template: `
849
+ @switch (level) {
850
+ @case ('1') {
851
+ <h1>
852
+ <ng-content />
853
+ </h1>
854
+ }
855
+ @case ('2') {
856
+ <h2>
857
+ <ng-content />
858
+ </h2>
859
+ }
860
+ @case ('3') {
861
+ <h3>
862
+ <ng-content />
863
+ </h3>
864
+ }
865
+ @case ('4') {
866
+ <h4>
867
+ <ng-content />
868
+ </h4>
869
+ }
870
+ @case ('5') {
871
+ <h5>
872
+ <ng-content />
873
+ </h5>
874
+ }
875
+ @case ('6') {
876
+ <h6>
877
+ <ng-content />
878
+ </h6>
879
+ }
880
+ @default {
881
+ <h1>
882
+ <ng-content />
883
+ </h1>
884
+ }
885
+ }
886
+ `
887
+ }]
888
+ }], propDecorators: { level: [{
889
+ type: Input
890
+ }] } });
891
+ class DotTextBlock {
892
+ constructor() {
893
+ this.marks = [];
894
+ this.text = '';
895
+ this.$remainingMarks = computed(() => this.marks?.slice(1));
896
+ this.$currentAttrs = computed(() => {
897
+ const attrs = { ...(this.marks?.[0]?.attrs || {}) };
898
+ if (attrs['class']) {
899
+ attrs['className'] = attrs['class'];
900
+ delete attrs['class'];
901
+ }
902
+ return attrs;
903
+ });
904
+ }
905
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotTextBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
906
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotTextBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-text", inputs: { marks: "marks", text: "text" }, ngImport: i0, template: `
907
+ @switch (marks?.[0]?.type) {
908
+ @case ('link') {
909
+ <a
910
+ [attr.href]="$currentAttrs()['href'] || ''"
911
+ [attr.target]="$currentAttrs()['target'] || ''">
912
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
913
+ </a>
914
+ }
915
+ @case ('bold') {
916
+ <strong>
917
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
918
+ </strong>
919
+ }
920
+ @case ('underline') {
921
+ <u>
922
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
923
+ </u>
924
+ }
925
+ @case ('italic') {
926
+ <em>
927
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
928
+ </em>
929
+ }
930
+ @case ('strike') {
931
+ <s>
932
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
933
+ </s>
934
+ }
935
+ @case ('superscript') {
936
+ <sup>
937
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
938
+ </sup>
939
+ }
940
+ @case ('subscript') {
941
+ <sub>
942
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
943
+ </sub>
944
+ }
945
+ @default {
946
+ {{ text }}
947
+ }
948
+ }
949
+ `, isInline: true, dependencies: [{ kind: "component", type: DotTextBlock, selector: "dotcms-block-editor-renderer-text", inputs: ["marks", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
950
+ }
951
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotTextBlock, decorators: [{
952
+ type: Component,
953
+ args: [{
954
+ selector: 'dotcms-block-editor-renderer-text',
955
+ standalone: true,
956
+ changeDetection: ChangeDetectionStrategy.OnPush,
957
+ template: `
958
+ @switch (marks?.[0]?.type) {
959
+ @case ('link') {
960
+ <a
961
+ [attr.href]="$currentAttrs()['href'] || ''"
962
+ [attr.target]="$currentAttrs()['target'] || ''">
963
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
964
+ </a>
965
+ }
966
+ @case ('bold') {
967
+ <strong>
968
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
969
+ </strong>
970
+ }
971
+ @case ('underline') {
972
+ <u>
973
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
974
+ </u>
975
+ }
976
+ @case ('italic') {
977
+ <em>
978
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
979
+ </em>
980
+ }
981
+ @case ('strike') {
982
+ <s>
983
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
984
+ </s>
985
+ }
986
+ @case ('superscript') {
987
+ <sup>
988
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
989
+ </sup>
990
+ }
991
+ @case ('subscript') {
992
+ <sub>
993
+ <dotcms-block-editor-renderer-text [marks]="$remainingMarks()" [text]="text" />
994
+ </sub>
995
+ }
996
+ @default {
997
+ {{ text }}
998
+ }
999
+ }
1000
+ `
1001
+ }]
1002
+ }], propDecorators: { marks: [{
1003
+ type: Input
1004
+ }], text: [{
1005
+ type: Input
1006
+ }] } });
1007
+
1008
+ class DotUnknownBlockComponent {
1009
+ constructor() {
1010
+ this.style = {
1011
+ backgroundColor: '#fff5f5',
1012
+ color: '#333',
1013
+ padding: '1rem',
1014
+ borderRadius: '0.5rem',
1015
+ marginBottom: '1rem',
1016
+ marginTop: '1rem',
1017
+ border: '1px solid #fc8181'
1018
+ };
1019
+ }
1020
+ get isEditMode() {
1021
+ return getUVEState()?.mode === UVE_MODE.EDIT;
1022
+ }
1023
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotUnknownBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1024
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotUnknownBlockComponent, isStandalone: true, selector: "dotcms-block-editor-renderer-unknown", inputs: { node: "node" }, ngImport: i0, template: `
1025
+ @if (isEditMode) {
1026
+ <div [style]="style" data-testid="unknown-block-type">
1027
+ <strong style="color: #c53030">Warning:</strong>
1028
+ The block type
1029
+ <strong>{{ node.type }}</strong>
1030
+ is not recognized. Please check your
1031
+ <a
1032
+ href="https://dev.dotcms.com/docs/block-editor"
1033
+ target="_blank"
1034
+ rel="noopener noreferrer">
1035
+ configuration
1036
+ </a>
1037
+ or contact support for assistance.
1038
+ </div>
1039
+ }
1040
+ `, isInline: true }); }
1041
+ }
1042
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotUnknownBlockComponent, decorators: [{
1043
+ type: Component,
1044
+ args: [{
1045
+ selector: 'dotcms-block-editor-renderer-unknown',
1046
+ standalone: true,
1047
+ template: `
1048
+ @if (isEditMode) {
1049
+ <div [style]="style" data-testid="unknown-block-type">
1050
+ <strong style="color: #c53030">Warning:</strong>
1051
+ The block type
1052
+ <strong>{{ node.type }}</strong>
1053
+ is not recognized. Please check your
1054
+ <a
1055
+ href="https://dev.dotcms.com/docs/block-editor"
1056
+ target="_blank"
1057
+ rel="noopener noreferrer">
1058
+ configuration
1059
+ </a>
1060
+ or contact support for assistance.
1061
+ </div>
1062
+ }
1063
+ `
1064
+ }]
1065
+ }], propDecorators: { node: [{
1066
+ type: Input
1067
+ }] } });
1068
+
1069
+ class DotVideoBlock {
1070
+ constructor() {
1071
+ this.$srcURL = computed(() => this.attrs?.['src']);
1072
+ this.$posterURL = computed(() => this.attrs?.['data']?.['thumbnail']);
1073
+ }
1074
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotVideoBlock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1075
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: DotVideoBlock, isStandalone: true, selector: "dotcms-block-editor-renderer-video", inputs: { attrs: "attrs" }, ngImport: i0, template: `
1076
+ <video
1077
+ [controls]="true"
1078
+ preload="metadata"
1079
+ [poster]="this.$posterURL()"
1080
+ [width]="attrs?.['width']"
1081
+ [height]="attrs?.['height']">
1082
+ <track default kind="captions" srclang="en" />
1083
+ <source [src]="this.$srcURL()" [type]="attrs?.['mimeType']" />
1084
+ Your browser does not support the
1085
+ <code>video</code>
1086
+ element.
1087
+ </video>
1088
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1089
+ }
1090
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotVideoBlock, decorators: [{
1091
+ type: Component,
1092
+ args: [{
1093
+ selector: 'dotcms-block-editor-renderer-video',
1094
+ standalone: true,
1095
+ changeDetection: ChangeDetectionStrategy.OnPush,
1096
+ template: `
1097
+ <video
1098
+ [controls]="true"
1099
+ preload="metadata"
1100
+ [poster]="this.$posterURL()"
1101
+ [width]="attrs?.['width']"
1102
+ [height]="attrs?.['height']">
1103
+ <track default kind="captions" srclang="en" />
1104
+ <source [src]="this.$srcURL()" [type]="attrs?.['mimeType']" />
1105
+ Your browser does not support the
1106
+ <code>video</code>
1107
+ element.
1108
+ </video>
1109
+ `
1110
+ }]
1111
+ }], propDecorators: { attrs: [{
1112
+ type: Input
1113
+ }] } });
1114
+
1115
+ class DotCMSBlockEditorItemComponent {
1116
+ constructor() {
1117
+ this.BLOCKS = BlockEditorDefaultBlocks;
1118
+ }
1119
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSBlockEditorItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1120
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotCMSBlockEditorItemComponent, isStandalone: true, selector: "dotcms-block-editor-renderer-block", inputs: { content: "content", customRenderers: "customRenderers" }, ngImport: i0, template: "@for (node of content; track node) {\n @if (customRenderers?.[node.type]) {\n <ng-container\n *ngTemplateOutlet=\"\n customRender;\n context: { customRender: customRenderers?.[node.type], node: node }\n \"></ng-container>\n } @else {\n @switch (node.type) {\n @case (BLOCKS.PARAGRAPH) {\n <dotcms-block-editor-renderer-paragraph [style]=\"node.attrs\">\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-paragraph>\n }\n\n @case (BLOCKS.TEXT) {\n <dotcms-block-editor-renderer-text [marks]=\"node.marks\" [text]=\"node.text || ''\" />\n }\n\n @case (BLOCKS.HEADING) {\n <dotcms-block-editor-renderer-heading\n [style]=\"node.attrs || {}\"\n [level]=\"node.attrs?.['level'] || '1'\">\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-heading>\n }\n\n @case (BLOCKS.BULLET_LIST) {\n <dotcms-block-editor-renderer-bullet-list>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-bullet-list>\n }\n\n @case (BLOCKS.ORDERED_LIST) {\n <dotcms-block-editor-renderer-ordered-list>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-ordered-list>\n }\n\n @case (BLOCKS.LIST_ITEM) {\n <dotcms-block-editor-renderer-list-item>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-list-item>\n }\n\n @case (BLOCKS.BLOCK_QUOTE) {\n <dotcms-block-editor-renderer-block-quote>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-block-quote>\n }\n\n @case (BLOCKS.CODE_BLOCK) {\n <dotcms-block-editor-renderer-code-block>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-code-block>\n }\n\n @case (BLOCKS.HARDBREAK) {\n <br />\n }\n\n @case (BLOCKS.HORIZONTAL_RULE) {\n <hr />\n }\n\n @case (BLOCKS.DOT_IMAGE) {\n <dotcms-block-editor-renderer-image [attrs]=\"node.attrs || {}\" />\n }\n\n @case (BLOCKS.DOT_VIDEO) {\n <dotcms-block-editor-renderer-video [attrs]=\"node.attrs || {}\" />\n }\n\n @case (BLOCKS.TABLE) {\n <dotcms-block-editor-renderer-table [content]=\"node.content\" />\n }\n\n @case (BLOCKS.DOT_CONTENT) {\n <dotcms-block-editor-renderer-contentlet\n [attrs]=\"node.attrs || {}\"\n [customRenderers]=\"customRenderers\" />\n }\n\n @default {\n <dotcms-block-editor-renderer-unknown [node]=\"node\" />\n }\n }\n }\n}\n\n<ng-template #customRender let-customRender=\"customRender\" let-node=\"node\">\n <ng-container\n *ngComponentOutlet=\"customRender | async; inputs: { content: node }\"></ng-container>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: DotCMSBlockEditorItemComponent, selector: "dotcms-block-editor-renderer-block", inputs: ["content", "customRenderers"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: DotParagraphBlock, selector: "dotcms-block-editor-renderer-paragraph" }, { kind: "component", type: DotTextBlock, selector: "dotcms-block-editor-renderer-text", inputs: ["marks", "text"] }, { kind: "component", type: DotHeadingBlock, selector: "dotcms-block-editor-renderer-heading", inputs: ["level"] }, { kind: "component", type: DotBulletList, selector: "dotcms-block-editor-renderer-bullet-list" }, { kind: "component", type: DotOrdererList, selector: "dotcms-block-editor-renderer-ordered-list" }, { kind: "component", type: DotListItem, selector: "dotcms-block-editor-renderer-list-item" }, { kind: "component", type: DotCodeBlock, selector: "dotcms-block-editor-renderer-code-block" }, { kind: "component", type: DotBlockQuote, selector: "dotcms-block-editor-renderer-block-quote" }, { kind: "component", type: DotImageBlock, selector: "dotcms-block-editor-renderer-image", inputs: ["attrs"] }, { kind: "component", type: DotVideoBlock, selector: "dotcms-block-editor-renderer-video", inputs: ["attrs"] }, { kind: "component", type: DotTableBlock, selector: "dotcms-block-editor-renderer-table", inputs: ["content"] }, { kind: "component", type: DotContentletBlock, selector: "dotcms-block-editor-renderer-contentlet", inputs: ["customRenderers", "attrs"] }, { kind: "component", type: DotUnknownBlockComponent, selector: "dotcms-block-editor-renderer-unknown", inputs: ["node"] }] }); }
1121
+ }
1122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSBlockEditorItemComponent, decorators: [{
1123
+ type: Component,
1124
+ args: [{ selector: 'dotcms-block-editor-renderer-block', standalone: true, imports: [
1125
+ NgTemplateOutlet,
1126
+ NgComponentOutlet,
1127
+ AsyncPipe,
1128
+ DotParagraphBlock,
1129
+ DotTextBlock,
1130
+ DotHeadingBlock,
1131
+ DotBulletList,
1132
+ DotOrdererList,
1133
+ DotListItem,
1134
+ DotCodeBlock,
1135
+ DotBlockQuote,
1136
+ DotImageBlock,
1137
+ DotVideoBlock,
1138
+ DotTableBlock,
1139
+ DotContentletBlock,
1140
+ DotUnknownBlockComponent
1141
+ ], template: "@for (node of content; track node) {\n @if (customRenderers?.[node.type]) {\n <ng-container\n *ngTemplateOutlet=\"\n customRender;\n context: { customRender: customRenderers?.[node.type], node: node }\n \"></ng-container>\n } @else {\n @switch (node.type) {\n @case (BLOCKS.PARAGRAPH) {\n <dotcms-block-editor-renderer-paragraph [style]=\"node.attrs\">\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-paragraph>\n }\n\n @case (BLOCKS.TEXT) {\n <dotcms-block-editor-renderer-text [marks]=\"node.marks\" [text]=\"node.text || ''\" />\n }\n\n @case (BLOCKS.HEADING) {\n <dotcms-block-editor-renderer-heading\n [style]=\"node.attrs || {}\"\n [level]=\"node.attrs?.['level'] || '1'\">\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-heading>\n }\n\n @case (BLOCKS.BULLET_LIST) {\n <dotcms-block-editor-renderer-bullet-list>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-bullet-list>\n }\n\n @case (BLOCKS.ORDERED_LIST) {\n <dotcms-block-editor-renderer-ordered-list>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-ordered-list>\n }\n\n @case (BLOCKS.LIST_ITEM) {\n <dotcms-block-editor-renderer-list-item>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-list-item>\n }\n\n @case (BLOCKS.BLOCK_QUOTE) {\n <dotcms-block-editor-renderer-block-quote>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-block-quote>\n }\n\n @case (BLOCKS.CODE_BLOCK) {\n <dotcms-block-editor-renderer-code-block>\n <dotcms-block-editor-renderer-block\n [content]=\"node.content\"\n [customRenderers]=\"customRenderers\" />\n </dotcms-block-editor-renderer-code-block>\n }\n\n @case (BLOCKS.HARDBREAK) {\n <br />\n }\n\n @case (BLOCKS.HORIZONTAL_RULE) {\n <hr />\n }\n\n @case (BLOCKS.DOT_IMAGE) {\n <dotcms-block-editor-renderer-image [attrs]=\"node.attrs || {}\" />\n }\n\n @case (BLOCKS.DOT_VIDEO) {\n <dotcms-block-editor-renderer-video [attrs]=\"node.attrs || {}\" />\n }\n\n @case (BLOCKS.TABLE) {\n <dotcms-block-editor-renderer-table [content]=\"node.content\" />\n }\n\n @case (BLOCKS.DOT_CONTENT) {\n <dotcms-block-editor-renderer-contentlet\n [attrs]=\"node.attrs || {}\"\n [customRenderers]=\"customRenderers\" />\n }\n\n @default {\n <dotcms-block-editor-renderer-unknown [node]=\"node\" />\n }\n }\n }\n}\n\n<ng-template #customRender let-customRender=\"customRender\" let-node=\"node\">\n <ng-container\n *ngComponentOutlet=\"customRender | async; inputs: { content: node }\"></ng-container>\n</ng-template>\n" }]
1142
+ }], propDecorators: { content: [{
1143
+ type: Input
1144
+ }], customRenderers: [{
1145
+ type: Input
1146
+ }] } });
1147
+
1148
+ /**
1149
+ * A component that renders content from DotCMS's Block Editor field.
1150
+ *
1151
+ * This component provides an easy way to render Block Editor content in your Angular applications.
1152
+ * It handles the rendering of standard blocks and allows customization through custom renderers.
1153
+ *
1154
+ * For more information about Block Editor, see {@link https://dev.dotcms.com/docs/block-editor}
1155
+ *
1156
+ * @example
1157
+ * ```html
1158
+ * <dotcms-block-editor-renderer
1159
+ * [blocks]="myBlockEditorContent"
1160
+ * [customRenderers]="myCustomRenderers">
1161
+ * </dotcms-block-editor-renderer>
1162
+ * ```
1163
+ */
1164
+ class DotCMSBlockEditorRendererComponent {
1165
+ constructor() {
1166
+ this.$blockEditorState = signal({ error: null });
1167
+ this.$isInEditMode = signal(getUVEState()?.mode === UVE_MODE.EDIT);
1168
+ }
1169
+ ngOnInit() {
1170
+ const state = isValidBlocks(this.blocks);
1171
+ if (state.error) {
1172
+ console.error('Error in dotcms-block-editor-renderer: ', state.error);
1173
+ }
1174
+ this.$blockEditorState.set(isValidBlocks(this.blocks));
1175
+ }
1176
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSBlockEditorRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1177
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotCMSBlockEditorRendererComponent, isStandalone: true, selector: "dotcms-block-editor-renderer", inputs: { blocks: "blocks", customRenderers: "customRenderers", class: "class", style: "style" }, ngImport: i0, template: "@if ($blockEditorState().error && $isInEditMode()) {\n <div data-testid=\"invalid-blocks-message\">\n {{ $blockEditorState().error }}\n </div>\n} @else if (!$blockEditorState().error) {\n <div [class]=\"class\" [style]=\"style\">\n <dotcms-block-editor-renderer-block\n [content]=\"blocks.content\"\n [customRenderers]=\"customRenderers\" />\n </div>\n}\n", styles: [""], dependencies: [{ kind: "component", type: DotCMSBlockEditorItemComponent, selector: "dotcms-block-editor-renderer-block", inputs: ["content", "customRenderers"] }] }); }
1178
+ }
1179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSBlockEditorRendererComponent, decorators: [{
1180
+ type: Component,
1181
+ args: [{ selector: 'dotcms-block-editor-renderer', standalone: true, imports: [DotCMSBlockEditorItemComponent], template: "@if ($blockEditorState().error && $isInEditMode()) {\n <div data-testid=\"invalid-blocks-message\">\n {{ $blockEditorState().error }}\n </div>\n} @else if (!$blockEditorState().error) {\n <div [class]=\"class\" [style]=\"style\">\n <dotcms-block-editor-renderer-block\n [content]=\"blocks.content\"\n [customRenderers]=\"customRenderers\" />\n </div>\n}\n" }]
1182
+ }], propDecorators: { blocks: [{
1183
+ type: Input
1184
+ }], customRenderers: [{
1185
+ type: Input
1186
+ }], class: [{
1187
+ type: Input
1188
+ }], style: [{
1189
+ type: Input
1190
+ }] } });
1191
+
1192
+ /**
1193
+ * @description This component is used to display a message when a page is missing the required `layout.body` property.
1194
+ * @internal
1195
+ * @class PageErrorMessageComponent
1196
+ */
1197
+ class PageErrorMessageComponent {
1198
+ ngOnInit() {
1199
+ console.warn('Missing required layout.body property in page');
1200
+ }
1201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: PageErrorMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.3", type: PageErrorMessageComponent, isStandalone: true, selector: "dotcms-page-error-message", ngImport: i0, template: `
1203
+ <div
1204
+ data-testid="error-message"
1205
+ style="padding: 1rem; border: 1px solid #e0e0e0; border-radius: 4px;">
1206
+ <p style="margin: 0 0 0.5rem; color: #666;">
1207
+ The
1208
+ <code>page</code>
1209
+ is missing the required
1210
+ <code>layout.body</code>
1211
+ property.
1212
+ </p>
1213
+ <p style="margin: 0; color: #666;">
1214
+ Make sure the page asset is properly loaded and includes a layout configuration.
1215
+ </p>
1216
+ </div>
1217
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1218
+ }
1219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: PageErrorMessageComponent, decorators: [{
1220
+ type: Component,
1221
+ args: [{
1222
+ selector: 'dotcms-page-error-message',
1223
+ standalone: true,
1224
+ imports: [],
1225
+ template: `
1226
+ <div
1227
+ data-testid="error-message"
1228
+ style="padding: 1rem; border: 1px solid #e0e0e0; border-radius: 4px;">
1229
+ <p style="margin: 0 0 0.5rem; color: #666;">
1230
+ The
1231
+ <code>page</code>
1232
+ is missing the required
1233
+ <code>layout.body</code>
1234
+ property.
1235
+ </p>
1236
+ <p style="margin: 0; color: #666;">
1237
+ Make sure the page asset is properly loaded and includes a layout configuration.
1238
+ </p>
1239
+ </div>
1240
+ `,
1241
+ changeDetection: ChangeDetectionStrategy.OnPush
1242
+ }]
1243
+ }] });
1244
+
1245
+ const EMPTY_DOTCMS_PAGE_STORE = {
1246
+ page: {},
1247
+ components: {},
1248
+ mode: PRODUCTION_MODE
1249
+ };
1250
+ /**
1251
+ * @description This service is responsible for managing the page context.
1252
+ * @internal
1253
+ * @author dotCMS
1254
+ * @export
1255
+ * @class DotCMSStore
1256
+ */
1257
+ class DotCMSStore {
1258
+ constructor() {
1259
+ this.$store = signal(EMPTY_DOTCMS_PAGE_STORE);
592
1260
  /**
593
- * The accept types for the container component.
594
- *
595
- * @type {(string | null)}
596
- * @memberof ContainerComponent
1261
+ * @description Get if the current context is in development mode
1262
+ * @readonly
1263
+ * @type {boolean}
1264
+ * @memberof DotCMSStore
597
1265
  */
1266
+ this.$isDevMode = computed(() => {
1267
+ const uveState = getUVEState();
1268
+ if (uveState?.mode) {
1269
+ return uveState?.mode === UVE_MODE.EDIT;
1270
+ }
1271
+ return this.$store()?.mode === DEVELOPMENT_MODE;
1272
+ });
1273
+ }
1274
+ /**
1275
+ * @description Get the store
1276
+ * @readonly
1277
+ * @type {DotCMSPageStore}
1278
+ * @memberof DotCMSStore
1279
+ */
1280
+ get store() {
1281
+ return this.$store();
1282
+ }
1283
+ /**
1284
+ * @description Set the store
1285
+ * @param {DotCMSPageStore} value
1286
+ * @memberof DotCMSStore
1287
+ */
1288
+ setStore(store) {
1289
+ this.$store.set(store);
1290
+ }
1291
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1292
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSStore, providedIn: 'root' }); }
1293
+ }
1294
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSStore, decorators: [{
1295
+ type: Injectable,
1296
+ args: [{
1297
+ providedIn: 'root'
1298
+ }]
1299
+ }] });
1300
+
1301
+ /**
1302
+ * @description This component is used to display a message when a container is not found.
1303
+ * @export
1304
+ * @internal
1305
+ * @class ContainerNotFoundComponent
1306
+ * @implements {OnInit}
1307
+ */
1308
+ class ContainerNotFoundComponent {
1309
+ constructor() {
1310
+ this.identifier = 'unknown';
1311
+ this.#dotcmsContextService = inject(DotCMSStore);
1312
+ this.$isDevMode = this.#dotcmsContextService.$isDevMode;
1313
+ this.emptyContainerStyle = EMPTY_CONTAINER_STYLE_ANGULAR;
1314
+ }
1315
+ #dotcmsContextService;
1316
+ ngOnInit() {
1317
+ if (this.$isDevMode()) {
1318
+ console.error(`Container with identifier ${this.identifier} not found`);
1319
+ }
1320
+ }
1321
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContainerNotFoundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1322
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ContainerNotFoundComponent, isStandalone: true, selector: "dotcms-container-not-found", inputs: { identifier: "identifier" }, ngImport: i0, template: `
1323
+ @if ($isDevMode()) {
1324
+ <div [attr.data-testid]="'container-not-found'" [ngStyle]="emptyContainerStyle">
1325
+ This container with identifier {{ identifier }} was not found.
1326
+ </div>
1327
+ }
1328
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
1329
+ }
1330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContainerNotFoundComponent, decorators: [{
1331
+ type: Component,
1332
+ args: [{
1333
+ selector: 'dotcms-container-not-found',
1334
+ standalone: true,
1335
+ imports: [NgStyle],
1336
+ template: `
1337
+ @if ($isDevMode()) {
1338
+ <div [attr.data-testid]="'container-not-found'" [ngStyle]="emptyContainerStyle">
1339
+ This container with identifier {{ identifier }} was not found.
1340
+ </div>
1341
+ }
1342
+ `
1343
+ }]
1344
+ }], propDecorators: { identifier: [{
1345
+ type: Input
1346
+ }] } });
1347
+
1348
+ /**
1349
+ * @description This component is used to display a message when a container is empty.
1350
+ * @export
1351
+ * @internal
1352
+ * @class EmptyContainerComponent
1353
+ */
1354
+ class EmptyContainerComponent {
1355
+ constructor() {
1356
+ this.emptyContainerStyle = EMPTY_CONTAINER_STYLE_ANGULAR;
1357
+ this.#dotCMSStore = inject(DotCMSStore);
1358
+ this.$isDevMode = this.#dotCMSStore.$isDevMode;
1359
+ }
1360
+ #dotCMSStore;
1361
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: EmptyContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1362
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: EmptyContainerComponent, isStandalone: true, selector: "dotcms-empty-container", ngImport: i0, template: `
1363
+ @if ($isDevMode()) {
1364
+ <div [ngStyle]="emptyContainerStyle" data-testid="empty-container">
1365
+ <span data-testid="empty-container-message" data-dot-object="empty-content">
1366
+ This container is empty.
1367
+ </span>
1368
+ </div>
1369
+ }
1370
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
1371
+ }
1372
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: EmptyContainerComponent, decorators: [{
1373
+ type: Component,
1374
+ args: [{
1375
+ selector: 'dotcms-empty-container',
1376
+ standalone: true,
1377
+ imports: [NgStyle],
1378
+ template: `
1379
+ @if ($isDevMode()) {
1380
+ <div [ngStyle]="emptyContainerStyle" data-testid="empty-container">
1381
+ <span data-testid="empty-container-message" data-dot-object="empty-content">
1382
+ This container is empty.
1383
+ </span>
1384
+ </div>
1385
+ }
1386
+ `
1387
+ }]
1388
+ }] });
1389
+
1390
+ /**
1391
+ * @description Fallback component that renders when no custom component is found for a contentlet
1392
+ * @category Components
1393
+ * @internal
1394
+ * @class FallbackComponent
1395
+ */
1396
+ class FallbackComponent {
1397
+ constructor() {
1398
+ this.UserNoComponent = null;
1399
+ }
1400
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: FallbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1401
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: FallbackComponent, isStandalone: true, selector: "dotcms-fallback-component", inputs: { UserNoComponent: "UserNoComponent", contentlet: "contentlet" }, ngImport: i0, template: `
1402
+ @if (UserNoComponent) {
1403
+ <ng-container *ngComponentOutlet="UserNoComponent | async; inputs: { contentlet }" />
1404
+ } @else {
1405
+ <div data-testid="dotcms-fallback-component">
1406
+ <p>No component found for content type: {{ contentlet.contentType }}</p>
1407
+ </div>
1408
+ }
1409
+ `, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1410
+ }
1411
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: FallbackComponent, decorators: [{
1412
+ type: Component,
1413
+ args: [{
1414
+ selector: 'dotcms-fallback-component',
1415
+ standalone: true,
1416
+ imports: [AsyncPipe, NgComponentOutlet],
1417
+ template: `
1418
+ @if (UserNoComponent) {
1419
+ <ng-container *ngComponentOutlet="UserNoComponent | async; inputs: { contentlet }" />
1420
+ } @else {
1421
+ <div data-testid="dotcms-fallback-component">
1422
+ <p>No component found for content type: {{ contentlet.contentType }}</p>
1423
+ </div>
1424
+ }
1425
+ `,
1426
+ changeDetection: ChangeDetectionStrategy.OnPush
1427
+ }]
1428
+ }], propDecorators: { UserNoComponent: [{
1429
+ type: Input
1430
+ }], contentlet: [{
1431
+ type: Input
1432
+ }] } });
1433
+
1434
+ /**
1435
+ * @description Contentlet component that renders DotCMS content with development mode support
1436
+ *
1437
+ * @component
1438
+ * @param {DotCMSContentlet} contentlet - The contentlet to be rendered
1439
+ * @param {string} container - The container identifier
1440
+ * @class ContentletComponent
1441
+ */
1442
+ class ContentletComponent {
1443
+ constructor() {
1444
+ this.dotObject = 'contentlet';
1445
+ this.#dotCMSStore = inject(DotCMSStore);
1446
+ this.$contentlet = signal(null);
1447
+ this.$UserComponent = signal(null);
1448
+ this.$UserNoComponent = signal(null);
1449
+ this.$isDevMode = this.#dotCMSStore.$isDevMode;
1450
+ this.$haveContent = signal(false);
1451
+ this.$style = computed(() => this.$isDevMode() && !this.$haveContent() ? { minHeight: '4rem' } : {});
1452
+ this.$dotAttributes = computed(() => {
1453
+ const contentlet = this.$contentlet();
1454
+ if (!contentlet || !this.$isDevMode())
1455
+ return {};
1456
+ return getDotContentletAttributes(contentlet, this.containerData.identifier);
1457
+ });
1458
+ this.identifier = null;
1459
+ this.basetype = null;
1460
+ this.title = null;
1461
+ this.inode = null;
1462
+ this.type = null;
1463
+ this.containerAttribute = null;
1464
+ this.onNumberOfPages = null;
1465
+ this.styleAttribute = null;
1466
+ }
1467
+ #dotCMSStore;
1468
+ ngOnChanges() {
1469
+ this.$contentlet.set(this.contentlet);
1470
+ this.setupComponents();
1471
+ this.identifier = this.$dotAttributes()['data-dot-identifier'];
1472
+ this.basetype = this.$dotAttributes()['data-dot-basetype'];
1473
+ this.title = this.$dotAttributes()['data-dot-title'];
1474
+ this.inode = this.$dotAttributes()['data-dot-inode'];
1475
+ this.type = this.$dotAttributes()['data-dot-type'];
1476
+ this.containerAttribute = JSON.stringify(this.containerData);
1477
+ this.onNumberOfPages = this.$dotAttributes()['data-dot-on-number-of-pages'];
1478
+ this.styleAttribute = this.$style();
1479
+ }
1480
+ ngAfterViewInit() {
1481
+ this.checkContent();
1482
+ }
1483
+ setupComponents() {
1484
+ const store = this.#dotCMSStore.store;
1485
+ if (!store)
1486
+ return;
1487
+ if (!store?.components)
1488
+ return;
1489
+ this.$UserComponent.set(store.components[this.contentlet?.contentType]);
1490
+ this.$UserNoComponent.set(store.components[CUSTOM_NO_COMPONENT]);
1491
+ }
1492
+ checkContent() {
1493
+ const element = this.contentletRef?.nativeElement;
1494
+ if (element) {
1495
+ const hasContent = element.getBoundingClientRect().height > 0;
1496
+ this.$haveContent.set(hasContent);
1497
+ }
1498
+ }
1499
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContentletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1500
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ContentletComponent, isStandalone: true, selector: "dotcms-contentlet", inputs: { contentlet: "contentlet", containerData: "containerData" }, host: { properties: { "attr.data-dot-object": "this.dotObject", "attr.data-dot-identifier": "this.identifier", "attr.data-dot-basetype": "this.basetype", "attr.data-dot-title": "this.title", "attr.data-dot-inode": "this.inode", "attr.data-dot-type": "this.type", "attr.data-dot-container": "this.containerAttribute", "attr.data-dot-on-number-of-pages": "this.onNumberOfPages", "style": "this.styleAttribute" } }, viewQueries: [{ propertyName: "contentletRef", first: true, predicate: ["contentletRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
1501
+ @if ($UserComponent()) {
1502
+ <ng-container
1503
+ *ngComponentOutlet="
1504
+ $UserComponent() | async;
1505
+ inputs: { contentlet: $contentlet() ?? contentlet }
1506
+ " />
1507
+ } @else if ($isDevMode()) {
1508
+ <dotcms-fallback-component
1509
+ [UserNoComponent]="$UserNoComponent()"
1510
+ [contentlet]="$contentlet() ?? contentlet" />
1511
+ }
1512
+ `, isInline: true, dependencies: [{ kind: "component", type: FallbackComponent, selector: "dotcms-fallback-component", inputs: ["UserNoComponent", "contentlet"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1513
+ }
1514
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContentletComponent, decorators: [{
1515
+ type: Component,
1516
+ args: [{
1517
+ selector: 'dotcms-contentlet',
1518
+ standalone: true,
1519
+ imports: [FallbackComponent, AsyncPipe, NgComponentOutlet],
1520
+ template: `
1521
+ @if ($UserComponent()) {
1522
+ <ng-container
1523
+ *ngComponentOutlet="
1524
+ $UserComponent() | async;
1525
+ inputs: { contentlet: $contentlet() ?? contentlet }
1526
+ " />
1527
+ } @else if ($isDevMode()) {
1528
+ <dotcms-fallback-component
1529
+ [UserNoComponent]="$UserNoComponent()"
1530
+ [contentlet]="$contentlet() ?? contentlet" />
1531
+ }
1532
+ `,
1533
+ changeDetection: ChangeDetectionStrategy.OnPush
1534
+ }]
1535
+ }], propDecorators: { contentlet: [{
1536
+ type: Input,
1537
+ args: [{ required: true }]
1538
+ }], containerData: [{
1539
+ type: Input,
1540
+ args: [{ required: true }]
1541
+ }], contentletRef: [{
1542
+ type: ViewChild,
1543
+ args: ['contentletRef']
1544
+ }], dotObject: [{
1545
+ type: HostBinding,
1546
+ args: ['attr.data-dot-object']
1547
+ }], identifier: [{
1548
+ type: HostBinding,
1549
+ args: ['attr.data-dot-identifier']
1550
+ }], basetype: [{
1551
+ type: HostBinding,
1552
+ args: ['attr.data-dot-basetype']
1553
+ }], title: [{
1554
+ type: HostBinding,
1555
+ args: ['attr.data-dot-title']
1556
+ }], inode: [{
1557
+ type: HostBinding,
1558
+ args: ['attr.data-dot-inode']
1559
+ }], type: [{
1560
+ type: HostBinding,
1561
+ args: ['attr.data-dot-type']
1562
+ }], containerAttribute: [{
1563
+ type: HostBinding,
1564
+ args: ['attr.data-dot-container']
1565
+ }], onNumberOfPages: [{
1566
+ type: HostBinding,
1567
+ args: ['attr.data-dot-on-number-of-pages']
1568
+ }], styleAttribute: [{
1569
+ type: HostBinding,
1570
+ args: ['style']
1571
+ }] } });
1572
+
1573
+ /**
1574
+ * @description This component renders a container with all its content using the layout provided by dotCMS Page API.
1575
+ *
1576
+ * @see {@link https://www.dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas}
1577
+ * @category Components
1578
+ * @internal
1579
+ * @class ContainerComponent
1580
+ */
1581
+ class ContainerComponent {
1582
+ constructor() {
1583
+ this.#dotCMSStore = inject(DotCMSStore);
1584
+ this.$containerData = signal(null);
1585
+ this.$contentlets = signal([]);
1586
+ this.$isEmpty = computed(() => this.$contentlets().length === 0);
1587
+ this.$dotAttributes = computed(() => {
1588
+ const containerData = this.$containerData();
1589
+ if (!containerData || !this.#dotCMSStore.$isDevMode()) {
1590
+ return {};
1591
+ }
1592
+ return getDotContainerAttributes(containerData);
1593
+ });
1594
+ this.dotObject = 'container';
598
1595
  this.acceptTypes = null;
599
- /**
600
- * The identifier for the container component.
601
- *
602
- * @type {(string | null)}
603
- * @memberof ContainerComponent
604
- */
605
1596
  this.identifier = null;
606
- /**
607
- * The max contentlets for the container component.
608
- *
609
- * @type {(number | null)}
610
- * @memberof ContainerComponent
611
- */
612
1597
  this.maxContentlets = null;
613
- /**
614
- * The uuid for the container component.
615
- *
616
- * @type {(string | null)}
617
- * @memberof ContainerComponent
618
- */
619
1598
  this.uuid = null;
620
- /**
621
- * The class for the container component.
622
- *
623
- * @type {(string | null)}
624
- * @memberof ContainerComponent
625
- */
626
- this.class = null;
627
- /**
628
- * The dot object for the container component.
629
- *
630
- * @type {(string | null)}
631
- * @memberof ContainerComponent
632
- */
633
- this.dotObject = null;
634
- /**
635
- * The data-testid attribute used for identifying the component during testing.
636
- *
637
- * @memberof ContainerComponent
638
- */
639
- this.testId = 'dot-container';
640
1599
  }
1600
+ #dotCMSStore;
641
1601
  ngOnChanges() {
642
- const { pageAsset, components, isInsideEditor } = this.pageContextService.context;
643
- const { acceptTypes, maxContentlets, variantId, path, contentlets } = getContainersData(pageAsset.containers, this.container);
644
- const { identifier, uuid } = this.container;
645
- this.componentsMap = components;
646
- this.$isInsideEditor.set(isInsideEditor);
647
- this.$contentlets.set(contentlets);
648
- this.$dotContainer.set({
649
- identifier: path ?? identifier,
650
- acceptTypes,
651
- maxContentlets,
652
- variantId,
653
- uuid
654
- });
655
- if (this.$isInsideEditor()) {
656
- this.acceptTypes = acceptTypes;
657
- this.identifier = identifier;
658
- this.maxContentlets = maxContentlets;
659
- this.uuid = uuid;
660
- this.class = this.$contentlets().length ? null : 'empty-container';
661
- this.dotObject = 'container';
1602
+ const { page } = this.#dotCMSStore.store ?? {};
1603
+ if (!page) {
1604
+ return;
662
1605
  }
1606
+ this.$containerData.set(getContainersData(page, this.container));
1607
+ this.$contentlets.set(getContentletsInContainer(page, this.container));
1608
+ this.acceptTypes = this.$dotAttributes()['data-dot-accept-types'];
1609
+ this.identifier = this.$dotAttributes()['data-dot-identifier'];
1610
+ this.maxContentlets = this.$dotAttributes()['data-max-contentlets'];
1611
+ this.uuid = this.$dotAttributes()['data-dot-uuid'];
663
1612
  }
664
1613
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
665
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ContainerComponent, isStandalone: true, selector: "dotcms-container", inputs: { container: "container" }, host: { properties: { "attr.data-dot-accept-types": "this.acceptTypes", "attr.data-dot-identifier": "this.identifier", "attr.data-max-contentlets": "this.maxContentlets", "attr.data-dot-uuid": "this.uuid", "class": "this.class", "attr.data-dot-object": "this.dotObject", "attr.data-testid": "this.testId" } }, usesOnChanges: true, ngImport: i0, template: "@if ($isInsideEditor()) {\n @if ($contentlets().length) {\n @for (contentlet of $contentlets(); track $index) {\n <dotcms-contentlet-wrapper\n [contentlet]=\"contentlet\"\n [container]=\"$dotContainerAsString()\">\n <ng-container\n *ngComponentOutlet=\"\n (componentsMap[contentlet.contentType] || componentsMap['CustomNoComponent']\n | async) || NoComponent;\n inputs: { contentlet }\n \" />\n </dotcms-contentlet-wrapper>\n }\n } @else {\n This container is empty.\n }\n} @else {\n @for (contentlet of $contentlets(); track $index) {\n <ng-container\n *ngComponentOutlet=\"\n componentsMap[contentlet.contentType] | async;\n inputs: { contentlet }\n \" />\n }\n}\n", styles: [":host.empty-container{width:100%;background-color:#ecf0fd;display:flex;justify-content:center;align-items:center;color:#030e32;height:10rem}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ContentletComponent, selector: "dotcms-contentlet-wrapper", inputs: ["contentlet", "container"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1614
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ContainerComponent, isStandalone: true, selector: "dotcms-container", inputs: { container: "container" }, host: { properties: { "attr.data-dot-object": "this.dotObject", "attr.data-dot-accept-types": "this.acceptTypes", "attr.data-dot-identifier": "this.identifier", "attr.data-max-contentlets": "this.maxContentlets", "attr.data-dot-uuid": "this.uuid" } }, usesOnChanges: true, ngImport: i0, template: `
1615
+ @if (!$containerData()) {
1616
+ <dotcms-container-not-found [identifier]="container.identifier" />
1617
+ } @else if ($isEmpty()) {
1618
+ <dotcms-empty-container />
1619
+ } @else {
1620
+ @for (contentlet of $contentlets(); track contentlet.identifier) {
1621
+ <dotcms-contentlet [contentlet]="contentlet" [containerData]="$containerData()!" />
1622
+ }
1623
+ }
1624
+ `, isInline: true, dependencies: [{ kind: "component", type: ContainerNotFoundComponent, selector: "dotcms-container-not-found", inputs: ["identifier"] }, { kind: "component", type: EmptyContainerComponent, selector: "dotcms-empty-container" }, { kind: "component", type: ContentletComponent, selector: "dotcms-contentlet", inputs: ["contentlet", "containerData"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
666
1625
  }
667
1626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ContainerComponent, decorators: [{
668
1627
  type: Component,
669
- args: [{ selector: 'dotcms-container', standalone: true, imports: [AsyncPipe, NgComponentOutlet, NoComponent, ContentletComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if ($isInsideEditor()) {\n @if ($contentlets().length) {\n @for (contentlet of $contentlets(); track $index) {\n <dotcms-contentlet-wrapper\n [contentlet]=\"contentlet\"\n [container]=\"$dotContainerAsString()\">\n <ng-container\n *ngComponentOutlet=\"\n (componentsMap[contentlet.contentType] || componentsMap['CustomNoComponent']\n | async) || NoComponent;\n inputs: { contentlet }\n \" />\n </dotcms-contentlet-wrapper>\n }\n } @else {\n This container is empty.\n }\n} @else {\n @for (contentlet of $contentlets(); track $index) {\n <ng-container\n *ngComponentOutlet=\"\n componentsMap[contentlet.contentType] | async;\n inputs: { contentlet }\n \" />\n }\n}\n", styles: [":host.empty-container{width:100%;background-color:#ecf0fd;display:flex;justify-content:center;align-items:center;color:#030e32;height:10rem}\n"] }]
1628
+ args: [{
1629
+ selector: 'dotcms-container',
1630
+ standalone: true,
1631
+ imports: [ContainerNotFoundComponent, EmptyContainerComponent, ContentletComponent],
1632
+ template: `
1633
+ @if (!$containerData()) {
1634
+ <dotcms-container-not-found [identifier]="container.identifier" />
1635
+ } @else if ($isEmpty()) {
1636
+ <dotcms-empty-container />
1637
+ } @else {
1638
+ @for (contentlet of $contentlets(); track contentlet.identifier) {
1639
+ <dotcms-contentlet [contentlet]="contentlet" [containerData]="$containerData()!" />
1640
+ }
1641
+ }
1642
+ `,
1643
+ changeDetection: ChangeDetectionStrategy.OnPush
1644
+ }]
670
1645
  }], propDecorators: { container: [{
671
1646
  type: Input,
672
1647
  args: [{ required: true }]
1648
+ }], dotObject: [{
1649
+ type: HostBinding,
1650
+ args: ['attr.data-dot-object']
673
1651
  }], acceptTypes: [{
674
1652
  type: HostBinding,
675
1653
  args: ['attr.data-dot-accept-types']
@@ -682,257 +1660,245 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImpor
682
1660
  }], uuid: [{
683
1661
  type: HostBinding,
684
1662
  args: ['attr.data-dot-uuid']
685
- }], class: [{
686
- type: HostBinding,
687
- args: ['class']
688
- }], dotObject: [{
689
- type: HostBinding,
690
- args: ['attr.data-dot-object']
691
- }], testId: [{
692
- type: HostBinding,
693
- args: ['attr.data-testid']
694
1663
  }] } });
695
1664
 
696
1665
  /**
697
- * This component is responsible to display a column with containers.
1666
+ * This component renders a column with all its content using the layout provided by dotCMS Page API.
698
1667
  *
699
- * @export
700
- * @class ColumnComponent
701
- * @implements {OnInit}
1668
+ * @see {@link https://www.dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas}
1669
+ * @category Components
1670
+ * @internal
702
1671
  */
703
1672
  class ColumnComponent {
704
1673
  constructor() {
705
- /**
706
- * The data-testid attribute used for identifying the component during testing.
707
- *
708
- * @memberof ColumnComponent
709
- */
710
- this.containerClasses = '';
1674
+ this.customClasses = '';
711
1675
  }
712
- ngOnInit() {
713
- const { startClass, endClass } = getPositionStyleClasses(this.column.leftOffset, this.column.width + this.column.leftOffset);
714
- this.containerClasses = `${startClass} ${endClass}`;
1676
+ ngOnChanges() {
1677
+ const positionClasses = getColumnPositionClasses(this.column);
1678
+ this.customClasses = combineClasses([positionClasses.startClass, positionClasses.endClass]);
715
1679
  }
716
1680
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
717
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ColumnComponent, isStandalone: true, selector: "dotcms-column", inputs: { column: "column" }, host: { properties: { "class": "this.containerClasses" } }, ngImport: i0, template: `
718
- @for (container of column.containers; track $index) {
719
- <dotcms-container [container]="container" />
720
- }
1681
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: ColumnComponent, isStandalone: true, selector: "dotcms-column", inputs: { column: "column" }, host: { properties: { "class": "this.customClasses" } }, usesOnChanges: true, ngImport: i0, template: `
1682
+ <div [class]="column.styleClass" data-testid="dotcms-column">
1683
+ @for (container of column.containers; track $index) {
1684
+ <dotcms-container [container]="container" />
1685
+ }
1686
+ </div>
721
1687
  `, isInline: true, styles: [":host.col-start-1{grid-column-start:1}:host.col-start-2{grid-column-start:2}:host.col-start-3{grid-column-start:3}:host.col-start-4{grid-column-start:4}:host.col-start-5{grid-column-start:5}:host.col-start-6{grid-column-start:6}:host.col-start-7{grid-column-start:7}:host.col-start-8{grid-column-start:8}:host.col-start-9{grid-column-start:9}:host.col-start-10{grid-column-start:10}:host.col-start-11{grid-column-start:11}:host.col-start-12{grid-column-start:12}:host.col-end-1{grid-column-end:1}:host.col-end-2{grid-column-end:2}:host.col-end-3{grid-column-end:3}:host.col-end-4{grid-column-end:4}:host.col-end-5{grid-column-end:5}:host.col-end-6{grid-column-end:6}:host.col-end-7{grid-column-end:7}:host.col-end-8{grid-column-end:8}:host.col-end-9{grid-column-end:9}:host.col-end-10{grid-column-end:10}:host.col-end-11{grid-column-end:11}:host.col-end-12{grid-column-end:12}:host.col-end-13{grid-column-end:13}\n"], dependencies: [{ kind: "component", type: ContainerComponent, selector: "dotcms-container", inputs: ["container"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
722
1688
  }
723
1689
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: ColumnComponent, decorators: [{
724
1690
  type: Component,
725
1691
  args: [{ selector: 'dotcms-column', standalone: true, imports: [ContainerComponent], template: `
726
- @for (container of column.containers; track $index) {
727
- <dotcms-container [container]="container" />
728
- }
1692
+ <div [class]="column.styleClass" data-testid="dotcms-column">
1693
+ @for (container of column.containers; track $index) {
1694
+ <dotcms-container [container]="container" />
1695
+ }
1696
+ </div>
729
1697
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host.col-start-1{grid-column-start:1}:host.col-start-2{grid-column-start:2}:host.col-start-3{grid-column-start:3}:host.col-start-4{grid-column-start:4}:host.col-start-5{grid-column-start:5}:host.col-start-6{grid-column-start:6}:host.col-start-7{grid-column-start:7}:host.col-start-8{grid-column-start:8}:host.col-start-9{grid-column-start:9}:host.col-start-10{grid-column-start:10}:host.col-start-11{grid-column-start:11}:host.col-start-12{grid-column-start:12}:host.col-end-1{grid-column-end:1}:host.col-end-2{grid-column-end:2}:host.col-end-3{grid-column-end:3}:host.col-end-4{grid-column-end:4}:host.col-end-5{grid-column-end:5}:host.col-end-6{grid-column-end:6}:host.col-end-7{grid-column-end:7}:host.col-end-8{grid-column-end:8}:host.col-end-9{grid-column-end:9}:host.col-end-10{grid-column-end:10}:host.col-end-11{grid-column-end:11}:host.col-end-12{grid-column-end:12}:host.col-end-13{grid-column-end:13}\n"] }]
730
1698
  }], propDecorators: { column: [{
731
- type: Input
732
- }], containerClasses: [{
1699
+ type: Input,
1700
+ args: [{ required: true }]
1701
+ }], customClasses: [{
733
1702
  type: HostBinding,
734
1703
  args: ['class']
735
1704
  }] } });
736
1705
 
737
1706
  /**
738
- * This component is responsible to display a row with columns.
1707
+ * @description This component renders a row with all its content using the layout provided by dotCMS Page API.
739
1708
  *
740
- * @export
1709
+ * @see {@link https://www.dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas}
1710
+ * @category Components
1711
+ * @internal
741
1712
  * @class RowComponent
742
1713
  */
743
1714
  class RowComponent {
1715
+ constructor() {
1716
+ this.customClasses = signal('');
1717
+ }
1718
+ ngOnChanges() {
1719
+ this.customClasses.set(combineClasses([this.row.styleClass || 'dot-row']));
1720
+ }
744
1721
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: RowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
745
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: RowComponent, isStandalone: true, selector: "dotcms-row", inputs: { row: "row" }, ngImport: i0, template: `
746
- @for (column of row.columns; track $index) {
747
- <dotcms-column [column]="column" />
748
- }
749
- `, isInline: true, styles: [":host{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem;row-gap:1rem}\n"], dependencies: [{ kind: "component", type: ColumnComponent, selector: "dotcms-column", inputs: ["column"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1722
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: RowComponent, isStandalone: true, selector: "dotcms-row", inputs: { row: "row" }, usesOnChanges: true, ngImport: i0, template: `
1723
+ <div class="dot-row-container">
1724
+ <div [class]="customClasses()" data-dot-object="row" data-testid="dotcms-row">
1725
+ @for (column of row.columns; track $index) {
1726
+ <dotcms-column [column]="column" />
1727
+ }
1728
+ </div>
1729
+ </div>
1730
+ `, isInline: true, styles: [".dot-row{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem}\n"], dependencies: [{ kind: "component", type: ColumnComponent, selector: "dotcms-column", inputs: ["column"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
750
1731
  }
751
1732
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: RowComponent, decorators: [{
752
1733
  type: Component,
753
1734
  args: [{ selector: 'dotcms-row', standalone: true, imports: [ColumnComponent], template: `
754
- @for (column of row.columns; track $index) {
755
- <dotcms-column [column]="column" />
756
- }
757
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem;row-gap:1rem}\n"] }]
1735
+ <div class="dot-row-container">
1736
+ <div [class]="customClasses()" data-dot-object="row" data-testid="dotcms-row">
1737
+ @for (column of row.columns; track $index) {
1738
+ <dotcms-column [column]="column" />
1739
+ }
1740
+ </div>
1741
+ </div>
1742
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".dot-row{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem}\n"] }]
758
1743
  }], propDecorators: { row: [{
759
1744
  type: Input,
760
1745
  args: [{ required: true }]
761
1746
  }] } });
762
1747
 
763
1748
  /**
764
- * `DotcmsLayoutComponent` is a class that represents the layout for a DotCMS page.
765
- * It includes a `pageAsset` property that represents the DotCMS page asset and a `components` property that represents the dynamic components for the page.
1749
+ * @description This component is used to render the layout for a DotCMS page.
1750
+ * @param {DotCMSPageAsset} page - The page to render the layout for
1751
+ * @param {DotCMSPageComponent} components - The components to render the layout for
1752
+ * @param {DotCMSPageRendererMode} mode - The mode to render the layout for
1753
+ *
1754
+ * @example
1755
+ * <dotcms-layout-body [page]="page" [components]="components" [mode]="'development'" />
766
1756
  *
767
1757
  * @export
768
- * @class DotcmsLayoutComponent
1758
+ * @implements {OnChanges}
1759
+ * @class DotCMSLayoutBodyComponent
769
1760
  */
770
- class DotcmsLayoutComponent {
1761
+ class DotCMSLayoutBodyComponent {
771
1762
  constructor() {
772
- this.route = inject(ActivatedRoute);
773
- this.pageContextService = inject(PageContextService);
774
- this.destroyRef$ = inject(DestroyRef);
775
- this.pageAsset$ = this.pageContextService.currentPage$;
776
- }
777
- /**
778
- * Represents the DotCMS page asset.
779
- *
780
- * @type {DotCMSPageAsset}
781
- * @memberof DotcmsLayoutComponent
782
- */
783
- set pageAsset(value) {
784
- this._pageAsset = value;
785
- if (!value.layout) {
786
- console.warn('Warning: pageAsset does not have a `layout` property. Might be using an advaced template or your dotCMS instance not have a enterprise license.');
787
- }
788
- }
789
- /**
790
- * Returns the DotCMS page asset.
791
- *
792
- * @readonly
793
- * @type {DotCMSPageAsset}
794
- * @memberof DotcmsLayoutComponent
795
- */
796
- get pageAsset() {
797
- return this._pageAsset;
1763
+ this.components = {};
1764
+ this.mode = 'production';
1765
+ this.#dotCMSStore = inject(DotCMSStore);
1766
+ this.$isDevMode = this.#dotCMSStore.$isDevMode;
1767
+ this.$rows = signal([]);
1768
+ this.$isEmpty = signal(false);
798
1769
  }
799
- ngOnInit() {
800
- this.pageContextService.setContext(this.pageAsset, this.components);
801
- if (!isInsideEditor()) {
802
- return;
803
- }
804
- this.client = DotCmsClient.instance;
805
- this.route.url.pipe(takeUntilDestroyed(this.destroyRef$)).subscribe((urlSegments) => {
806
- const pathname = '/' + urlSegments.join('/');
807
- initEditor({ pathname });
808
- updateNavigation(pathname || '/');
809
- });
810
- this.uveSubscription = createUVESubscription('changes', (data) => {
811
- if (this.onReload) {
812
- this.onReload();
813
- return;
814
- }
815
- this.pageContextService.setPageAsset(data);
1770
+ #dotCMSStore;
1771
+ ngOnChanges() {
1772
+ this.#dotCMSStore.setStore({
1773
+ page: this.page,
1774
+ components: this.components,
1775
+ mode: this.mode
816
1776
  });
817
- postMessageToEditor({ action: CLIENT_ACTIONS.CLIENT_READY, payload: this.editor });
818
- }
819
- ngOnDestroy() {
820
- if (!isInsideEditor()) {
821
- return;
822
- }
823
- this.uveSubscription?.unsubscribe();
1777
+ this.$isEmpty.set(!this.page?.layout?.body);
1778
+ this.$rows.set(this.page?.layout?.body?.rows ?? []);
824
1779
  }
825
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotcmsLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
826
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotcmsLayoutComponent, isStandalone: true, selector: "dotcms-layout", inputs: { pageAsset: "pageAsset", components: "components", onReload: "onReload", editor: "editor" }, ngImport: i0, template: `
827
- @if (pageAsset$ | async; as page) {
828
- @for (row of page?.layout?.body?.rows; track $index) {
1780
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSLayoutBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1781
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: DotCMSLayoutBodyComponent, isStandalone: true, selector: "dotcms-layout-body", inputs: { page: "page", components: "components", mode: "mode" }, providers: [DotCMSStore], usesOnChanges: true, ngImport: i0, template: `
1782
+ @if ($isEmpty() && $isDevMode()) {
1783
+ <dotcms-page-error-message />
1784
+ } @else {
1785
+ @for (row of $rows(); track row.identifier) {
829
1786
  <dotcms-row [row]="row" />
830
1787
  }
831
1788
  }
832
- `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: RowComponent, selector: "dotcms-row", inputs: ["row"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1789
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: PageErrorMessageComponent, selector: "dotcms-page-error-message" }, { kind: "component", type: RowComponent, selector: "dotcms-row", inputs: ["row"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
833
1790
  }
834
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotcmsLayoutComponent, decorators: [{
1791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSLayoutBodyComponent, decorators: [{
835
1792
  type: Component,
836
- args: [{ selector: 'dotcms-layout', standalone: true, imports: [RowComponent, AsyncPipe], template: `
837
- @if (pageAsset$ | async; as page) {
838
- @for (row of page?.layout?.body?.rows; track $index) {
1793
+ args: [{ selector: 'dotcms-layout-body', standalone: true, imports: [PageErrorMessageComponent, RowComponent], providers: [DotCMSStore], template: `
1794
+ @if ($isEmpty() && $isDevMode()) {
1795
+ <dotcms-page-error-message />
1796
+ } @else {
1797
+ @for (row of $rows(); track row.identifier) {
839
1798
  <dotcms-row [row]="row" />
840
1799
  }
841
1800
  }
842
1801
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
843
- }], propDecorators: { pageAsset: [{
1802
+ }], propDecorators: { page: [{
844
1803
  type: Input,
845
1804
  args: [{ required: true }]
846
1805
  }], components: [{
847
1806
  type: Input,
848
1807
  args: [{ required: true }]
849
- }], onReload: [{
850
- type: Input
851
- }], editor: [{
1808
+ }], mode: [{
852
1809
  type: Input
853
1810
  }] } });
854
1811
 
855
- /* eslint-disable @typescript-eslint/no-explicit-any */
856
-
857
- /* eslint-disable @typescript-eslint/no-explicit-any */
858
-
859
- /**
860
- * Validates if a given path is a valid URL string
861
- *
862
- * @param path - The path to validate
863
- * @returns boolean indicating if the path is valid
864
- */
865
- function isValidPath(path) {
866
- if (typeof path !== 'string' || path.trim() === '') {
867
- return false;
868
- }
869
- try {
870
- new URL(path);
871
- return true;
872
- }
873
- catch {
874
- return false;
875
- }
876
- }
877
- /**
878
- * Provides a DotCMS image loader configuration for the Angular Image directive
879
- *
880
- * @param path - The base URL path to the DotCMS instance, or empty to use current site
881
- * @returns An array of providers for the IMAGE_LOADER token
882
- * @throws Error if the provided path is invalid
883
- * @example
884
- * ```typescript
885
- * // In your app.config.ts
886
- * export const appConfig: ApplicationConfig = {
887
- * providers: [
888
- * provideDotCMSImageLoader('https://demo.dotcms.com')
889
- * // Or use current site:
890
- * // provideDotCMSImageLoader()
891
- * ]
892
- * };
893
- * ```
894
- */
895
- function provideDotCMSImageLoader(path) {
896
- // If path is provided, validate it
897
- if (path && !isValidPath(path)) {
898
- throw new Error(`Image loader has detected an invalid path (\`${path}\`). ` +
899
- `To fix this, supply either the full URL to the dotCMS site, or leave it empty to use the current site.`);
900
- }
901
- return [
902
- {
903
- provide: IMAGE_LOADER,
904
- useValue: (config) => createDotCMSUrl(config, path)
1812
+ class DotCMSEditablePageService {
1813
+ /**
1814
+ * Subject that emits the current editable page asset or null.
1815
+ * Used internally to track changes to the page data.
1816
+ *
1817
+ * @private
1818
+ * @type {Subject<DotCMSPageResponse | undefined>}
1819
+ */
1820
+ #responseSubject = new Subject();
1821
+ /**
1822
+ * Observable stream of the page asset changes.
1823
+ * Exposes the pageAssetSubject as an Observable for subscribers.
1824
+ *
1825
+ * @private
1826
+ * @type {Observable<DotCMSPageResponse | undefined>}
1827
+ */
1828
+ #response$ = this.#responseSubject.asObservable();
1829
+ /**
1830
+ * Listens for changes to an editable page and returns an Observable that emits the updated page data.
1831
+ * This method initializes the UVE (Universal Visual Editor) and sets up subscriptions to track content changes.
1832
+ *
1833
+ * @example
1834
+ * ```ts
1835
+ * // Import the service
1836
+ * import { DotCMSEditablePageService } from '@dotcms/angular';
1837
+ *
1838
+ * // Inject the service
1839
+ * constructor(private editablePageService: DotCMSEditablePageService) {}
1840
+ *
1841
+ * // Get the page data from your API call
1842
+ * const page = await client.page.get('/');
1843
+ *
1844
+ * // Listen for changes
1845
+ * const subscription = this.editablePageService.listen(page).subscribe(updatedPage => {
1846
+ * if (updatedPage) {
1847
+ * // Handle updated page data
1848
+ * console.log('Page updated:', updatedPage);
1849
+ * }
1850
+ * });
1851
+ *
1852
+ * // When done listening, unsubscribe
1853
+ * subscription.unsubscribe();
1854
+ * ```
1855
+ *
1856
+ * @param response Optional initial page data
1857
+ * @returns Observable that emits the updated page data or undefined
1858
+ */
1859
+ listen(response) {
1860
+ if (!getUVEState()) {
1861
+ return of(response);
905
1862
  }
906
- ];
907
- }
908
- /**
909
- * Creates a DotCMS-compatible URL for image loading
910
- *
911
- * @param config - The image loader configuration
912
- * @param path - The base URL path to the DotCMS instance
913
- * @returns A fully qualified URL for the image
914
- * @internal
915
- */
916
- function createDotCMSUrl(config, path) {
917
- // console.log('createDotCMSUrl', config, path);
918
- const { loaderParams, src, width } = config;
919
- const params = loaderParams;
920
- if (params?.isOutsideSRC) {
921
- return src;
1863
+ const pageURI = response?.pageAsset?.page?.pageURI;
1864
+ initUVE(response);
1865
+ // Update the navigation to the pageURI, when we have a pageURI
1866
+ // Sometimes the page is null due to permissions, so we don't want to update the navigation
1867
+ // And wait for the UVE to resolve the page
1868
+ if (pageURI) {
1869
+ updateNavigation(pageURI);
1870
+ }
1871
+ const unsubscribeUVEChanges = this.#listenUVEChanges();
1872
+ return this.#response$.pipe(finalize(() => {
1873
+ unsubscribeUVEChanges();
1874
+ }));
922
1875
  }
923
- // Use empty string as fallback to support using current site
924
- const dotcmsHost = path ? new URL(path).origin : '';
925
- const imageSRC = src.includes('/dA/') ? src : `/dA/${src}`;
926
- const languageId = params?.languageId ?? '1';
927
- if (width) {
928
- return `${dotcmsHost}${imageSRC}/${width}w?language_id=${languageId}`;
1876
+ /**
1877
+ * Sets up a subscription to listen for UVE content changes and updates the page asset subject.
1878
+ * This is an internal method used by listenEditablePage() to handle UVE events.
1879
+ *
1880
+ * @returns {UVEUnsubscribeFunction} Function to unsubscribe from the UVE content changes
1881
+ * @private
1882
+ */
1883
+ #listenUVEChanges() {
1884
+ const { unsubscribe } = createUVESubscription(UVEEventType.CONTENT_CHANGES, (payload) => {
1885
+ this.#responseSubject.next(payload);
1886
+ });
1887
+ return unsubscribe;
929
1888
  }
930
- return `${dotcmsHost}${imageSRC}?language_id=${languageId}`;
1889
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSEditablePageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1890
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSEditablePageService, providedIn: 'root' }); }
931
1891
  }
1892
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: DotCMSEditablePageService, decorators: [{
1893
+ type: Injectable,
1894
+ args: [{
1895
+ providedIn: 'root'
1896
+ }]
1897
+ }] });
932
1898
 
933
1899
  /**
934
1900
  * Generated bundle index. Do not edit.
935
1901
  */
936
1902
 
937
- export { DotEditableTextComponent, DotcmsLayoutComponent, PageContextService, provideDotCMSImageLoader };
1903
+ export { DotCMSBlockEditorRendererComponent, DotCMSClient, DotCMSEditablePageService, DotCMSEditableTextComponent, DotCMSLayoutBodyComponent, DotCMSShowWhenDirective, provideDotCMSClient, provideDotCMSImageLoader };
938
1904
  //# sourceMappingURL=dotcms-angular.mjs.map