@dotcms/angular 0.0.1-alpha.38 → 0.0.1-alpha.40

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 (78) hide show
  1. package/.eslintrc.json +18 -0
  2. package/jest.config.ts +22 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +4 -21
  5. package/project.json +33 -0
  6. package/{index.d.ts → src/index.ts} +0 -1
  7. package/src/lib/components/dot-editable-text/dot-editable-text.component.css +4 -0
  8. package/src/lib/components/dot-editable-text/dot-editable-text.component.html +8 -0
  9. package/src/lib/components/dot-editable-text/dot-editable-text.component.spec.ts +424 -0
  10. package/src/lib/components/dot-editable-text/dot-editable-text.component.ts +269 -0
  11. package/src/lib/components/dot-editable-text/utils.ts +51 -0
  12. package/src/lib/components/no-component/no-component.component.css +3 -0
  13. package/src/lib/components/no-component/no-component.component.spec.ts +24 -0
  14. package/src/lib/components/no-component/no-component.component.ts +31 -0
  15. package/src/lib/layout/column/column.component.css +99 -0
  16. package/src/lib/layout/column/column.component.spec.ts +33 -0
  17. package/src/lib/layout/column/column.component.ts +49 -0
  18. package/src/lib/layout/container/container.component.css +9 -0
  19. package/src/lib/layout/container/container.component.html +26 -0
  20. package/src/lib/layout/container/container.component.spec.ts +205 -0
  21. package/src/lib/layout/container/container.component.ts +140 -0
  22. package/src/lib/layout/contentlet/contentlet.component.spec.ts +22 -0
  23. package/{lib/layout/contentlet/contentlet.component.d.ts → src/lib/layout/contentlet/contentlet.component.ts} +32 -17
  24. package/src/lib/layout/dotcms-layout/dotcms-layout.component.css +3 -0
  25. package/src/lib/layout/dotcms-layout/dotcms-layout.component.spec.ts +195 -0
  26. package/src/lib/layout/dotcms-layout/dotcms-layout.component.ts +150 -0
  27. package/src/lib/layout/row/row.component.css +6 -0
  28. package/src/lib/layout/row/row.component.spec.ts +28 -0
  29. package/src/lib/layout/row/row.component.ts +32 -0
  30. package/{lib/models/dotcms.model.d.ts → src/lib/models/dotcms.model.ts} +21 -3
  31. package/{lib/models/index.d.ts → src/lib/models/index.ts} +8 -1
  32. package/{lib/services/dotcms-context/page-context.service.d.ts → src/lib/services/dotcms-context/page-context.service.ts} +41 -12
  33. package/src/lib/services/dotcms-context/page-context.spec.ts +80 -0
  34. package/src/lib/utils/index.ts +92 -0
  35. package/src/lib/utils/testing.utils.ts +1019 -0
  36. package/src/test-setup.ts +8 -0
  37. package/tsconfig.json +29 -0
  38. package/tsconfig.lib.json +12 -0
  39. package/tsconfig.lib.prod.json +9 -0
  40. package/tsconfig.spec.json +11 -0
  41. package/dotcms-angular.d.ts.map +0 -1
  42. package/esm2022/dotcms-angular.mjs +0 -5
  43. package/esm2022/index.mjs +0 -5
  44. package/esm2022/lib/components/dot-editable-text/dot-editable-text.component.mjs +0 -225
  45. package/esm2022/lib/components/dot-editable-text/utils.mjs +0 -43
  46. package/esm2022/lib/components/no-component/no-component.component.mjs +0 -32
  47. package/esm2022/lib/layout/column/column.component.mjs +0 -45
  48. package/esm2022/lib/layout/container/container.component.mjs +0 -126
  49. package/esm2022/lib/layout/contentlet/contentlet.component.mjs +0 -120
  50. package/esm2022/lib/layout/dotcms-layout/dotcms-layout.component.mjs +0 -100
  51. package/esm2022/lib/layout/row/row.component.mjs +0 -29
  52. package/esm2022/lib/models/dotcms.model.mjs +0 -3
  53. package/esm2022/lib/models/index.mjs +0 -3
  54. package/esm2022/lib/services/dotcms-context/page-context.service.mjs +0 -75
  55. package/esm2022/lib/utils/index.mjs +0 -79
  56. package/fesm2022/dotcms-angular.mjs +0 -858
  57. package/fesm2022/dotcms-angular.mjs.map +0 -1
  58. package/index.d.ts.map +0 -1
  59. package/lib/components/dot-editable-text/dot-editable-text.component.d.ts +0 -129
  60. package/lib/components/dot-editable-text/dot-editable-text.component.d.ts.map +0 -1
  61. package/lib/components/dot-editable-text/utils.d.ts +0 -7
  62. package/lib/components/dot-editable-text/utils.d.ts.map +0 -1
  63. package/lib/components/no-component/no-component.component.d.ts +0 -22
  64. package/lib/components/no-component/no-component.component.d.ts.map +0 -1
  65. package/lib/layout/column/column.component.d.ts +0 -29
  66. package/lib/layout/column/column.component.d.ts.map +0 -1
  67. package/lib/layout/container/container.component.d.ts +0 -88
  68. package/lib/layout/container/container.component.d.ts.map +0 -1
  69. package/lib/layout/contentlet/contentlet.component.d.ts.map +0 -1
  70. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts +0 -67
  71. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts.map +0 -1
  72. package/lib/layout/row/row.component.d.ts +0 -20
  73. package/lib/layout/row/row.component.d.ts.map +0 -1
  74. package/lib/models/dotcms.model.d.ts.map +0 -1
  75. package/lib/models/index.d.ts.map +0 -1
  76. package/lib/services/dotcms-context/page-context.service.d.ts.map +0 -1
  77. package/lib/utils/index.d.ts +0 -63
  78. package/lib/utils/index.d.ts.map +0 -1
@@ -0,0 +1,269 @@
1
+ import { EditorComponent, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
2
+ import { EventObj } from '@tinymce/tinymce-angular/editor/Events';
3
+
4
+ import {
5
+ Component,
6
+ ElementRef,
7
+ HostListener,
8
+ inject,
9
+ Input,
10
+ OnChanges,
11
+ OnInit,
12
+ Renderer2,
13
+ SecurityContext,
14
+ ViewChild
15
+ } from '@angular/core';
16
+ import { DomSanitizer } from '@angular/platform-browser';
17
+
18
+ import {
19
+ CUSTOMER_ACTIONS,
20
+ DotCmsClient,
21
+ isInsideEditor,
22
+ postMessageToEditor
23
+ } from '@dotcms/client';
24
+
25
+ import { TINYMCE_CONFIG, DOT_EDITABLE_TEXT_FORMAT, DOT_EDITABLE_TEXT_MODE } from './utils';
26
+
27
+ import { DotCMSContentlet } from '../../models';
28
+
29
+ /**
30
+ * Dot editable text component.
31
+ * This component is responsible to render a text field that can be edited inline.
32
+ *
33
+ * @export
34
+ * @class DotEditableTextComponent
35
+ * @implements {OnInit}
36
+ * @implements {OnChanges}
37
+ */
38
+ @Component({
39
+ selector: 'dot-editable-text',
40
+ standalone: true,
41
+ templateUrl: './dot-editable-text.component.html',
42
+ styleUrl: './dot-editable-text.component.css',
43
+ imports: [EditorComponent],
44
+ providers: [
45
+ {
46
+ provide: TINYMCE_SCRIPT_SRC,
47
+ useFactory: () => {
48
+ return `${DotCmsClient.dotcmsUrl}/ext/tinymcev7/tinymce.min.js`;
49
+ }
50
+ }
51
+ ]
52
+ })
53
+ export class DotEditableTextComponent implements OnInit, OnChanges {
54
+ @ViewChild(EditorComponent) editorComponent!: EditorComponent;
55
+
56
+ /**
57
+ * Represents the mode of the editor which can be `plain`, `minimal`, or `full`
58
+ *
59
+ * @type {DOT_EDITABLE_TEXT_MODE}
60
+ * @memberof DotEditableTextComponent
61
+ */
62
+ @Input() mode: DOT_EDITABLE_TEXT_MODE = 'plain';
63
+ /**
64
+ * Represents the format of the editor which can be `text` or `html`
65
+ *
66
+ * @type {DOT_EDITABLE_TEXT_FORMAT}
67
+ * @memberof DotEditableTextComponent
68
+ */
69
+ @Input() format: DOT_EDITABLE_TEXT_FORMAT = 'text';
70
+ /**
71
+ * Represents the `contentlet` that can be inline edited
72
+ *
73
+ * @type {DotCMSContentlet}
74
+ * @memberof DotEditableTextComponent
75
+ */
76
+ @Input() contentlet!: DotCMSContentlet;
77
+ /**
78
+ * Represents the field name of the `contentlet` that can be edited
79
+ *
80
+ * @memberof DotEditableTextComponent
81
+ */
82
+ @Input() fieldName = '';
83
+
84
+ /**
85
+ * Represents the content of the `contentlet` that can be edited
86
+ *
87
+ * @protected
88
+ * @memberof DotEditableTextComponent
89
+ */
90
+ protected content = '';
91
+ /**
92
+ * Represents the configuration of the editor
93
+ *
94
+ * @protected
95
+ * @type {EditorComponent['init']}
96
+ * @memberof DotEditableTextComponent
97
+ */
98
+ protected init!: EditorComponent['init'];
99
+ /**
100
+ * Represents if the component is inside the editor
101
+ *
102
+ * @protected
103
+ * @type {boolean}
104
+ * @memberof DotEditableTextComponent
105
+ */
106
+ protected isInsideEditor!: boolean;
107
+
108
+ readonly #sanitizer = inject<DomSanitizer>(DomSanitizer);
109
+ readonly #renderer = inject<Renderer2>(Renderer2);
110
+ readonly #elementRef = inject<ElementRef>(ElementRef);
111
+
112
+ /**
113
+ * The TinyMCE editor
114
+ *
115
+ * @readonly
116
+ * @memberof DotEditableTextComponent
117
+ */
118
+ get editor() {
119
+ return this.editorComponent?.editor;
120
+ }
121
+
122
+ /**
123
+ * Returns the number of pages the contentlet is on
124
+ *
125
+ * @readonly
126
+ * @memberof DotEditableTextComponent
127
+ */
128
+ get onNumberOfPages() {
129
+ return this.contentlet['onNumberOfPages'] || 1;
130
+ }
131
+
132
+ /**
133
+ * Handle copy contentlet inline editing success event
134
+ *
135
+ * @param {MessageEvent} { data }
136
+ * @return {*}
137
+ * @memberof DotEditableTextComponent
138
+ */
139
+ @HostListener('window:message', ['$event'])
140
+ onMessage({ data }: MessageEvent) {
141
+ const { name, payload } = data;
142
+ if (name !== 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS') {
143
+ return;
144
+ }
145
+
146
+ const { oldInode, inode } = payload;
147
+ const currentInode = this.contentlet.inode;
148
+
149
+ if (currentInode === oldInode || currentInode === inode) {
150
+ this.editorComponent.editor.focus();
151
+
152
+ return;
153
+ }
154
+ }
155
+
156
+ ngOnInit() {
157
+ this.isInsideEditor = isInsideEditor();
158
+
159
+ if (!this.isInsideEditor) {
160
+ this.innerHTMLToElement();
161
+
162
+ return;
163
+ }
164
+
165
+ this.init = {
166
+ ...TINYMCE_CONFIG[this.mode],
167
+ base_url: `${DotCmsClient.dotcmsUrl}/ext/tinymcev7`
168
+ };
169
+ }
170
+
171
+ ngOnChanges() {
172
+ this.content = this.contentlet[this.fieldName] || '';
173
+ if (this.editor) {
174
+ this.editor.setContent(this.content, { format: this.format });
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Handle mouse down event
180
+ *
181
+ * @param {EventObj<MouseEvent>} { event }
182
+ * @return {*}
183
+ * @memberof DotEditableTextComponent
184
+ */
185
+ onMouseDown({ event }: EventObj<MouseEvent>) {
186
+ if (this.onNumberOfPages <= 1 || this.editorComponent.editor.hasFocus()) {
187
+ return;
188
+ }
189
+
190
+ const { inode, languageId: language } = this.contentlet;
191
+
192
+ event.stopPropagation();
193
+ event.preventDefault();
194
+
195
+ try {
196
+ postMessageToEditor({
197
+ action: CUSTOMER_ACTIONS.COPY_CONTENTLET_INLINE_EDITING,
198
+ payload: {
199
+ dataset: {
200
+ inode,
201
+ language,
202
+ fieldName: this.fieldName
203
+ }
204
+ }
205
+ });
206
+ } catch (error) {
207
+ console.error('Failed to post message to editor:', error);
208
+ }
209
+ }
210
+ /**
211
+ * Handle focus out event
212
+ *
213
+ * @return {*}
214
+ * @memberof DotEditableTextComponent
215
+ */
216
+ onFocusOut() {
217
+ const content = this.editor.getContent({ format: this.format });
218
+
219
+ if (!this.editor.isDirty() || !this.didContentChange(content)) {
220
+ return;
221
+ }
222
+
223
+ const { inode, languageId: langId } = this.contentlet;
224
+
225
+ try {
226
+ postMessageToEditor({
227
+ action: CUSTOMER_ACTIONS.UPDATE_CONTENTLET_INLINE_EDITING,
228
+ payload: {
229
+ content,
230
+ dataset: {
231
+ inode,
232
+ langId,
233
+ fieldName: this.fieldName
234
+ }
235
+ }
236
+ });
237
+ } catch (error) {
238
+ console.error('Failed to post message to editor:', error);
239
+ }
240
+ }
241
+
242
+ /**
243
+ * inner HTML to element
244
+ *
245
+ * @private
246
+ * @param {string} editedContent
247
+ * @return {*}
248
+ * @memberof DotEditableTextComponent
249
+ */
250
+ private innerHTMLToElement() {
251
+ const element = this.#elementRef.nativeElement;
252
+ const safeHtml = this.#sanitizer.bypassSecurityTrustHtml(this.content);
253
+ const content = this.#sanitizer.sanitize(SecurityContext.HTML, safeHtml) || '';
254
+
255
+ this.#renderer.setProperty(element, 'innerHTML', content);
256
+ }
257
+
258
+ /**
259
+ * Check if the content has changed
260
+ *
261
+ * @private
262
+ * @param {string} editedContent
263
+ * @return {*}
264
+ * @memberof DotEditableTextComponent
265
+ */
266
+ private didContentChange(editedContent: string) {
267
+ return this.content !== editedContent;
268
+ }
269
+ }
@@ -0,0 +1,51 @@
1
+ import { EditorComponent } from '@tinymce/tinymce-angular';
2
+
3
+ export type DOT_EDITABLE_TEXT_MODE = 'minimal' | 'full' | 'plain';
4
+
5
+ export type DOT_EDITABLE_TEXT_FORMAT = 'html' | 'text';
6
+
7
+ const DEFAULT_TINYMCE_CONFIG: EditorComponent['init'] = {
8
+ menubar: false,
9
+ inline: true,
10
+ valid_styles: {
11
+ '*': 'font-size,font-family,color,text-decoration,text-align'
12
+ },
13
+ powerpaste_word_import: 'clean',
14
+ powerpaste_html_import: 'clean',
15
+ suffix: '.min', // Suffix to use when loading resources
16
+ license_key: 'gpl'
17
+ };
18
+
19
+ export const TINYMCE_CONFIG: {
20
+ [key in DOT_EDITABLE_TEXT_MODE]: EditorComponent['init'];
21
+ } = {
22
+ minimal: {
23
+ ...DEFAULT_TINYMCE_CONFIG,
24
+ plugins: 'link autolink',
25
+ toolbar: 'bold italic underline | link',
26
+ valid_elements: 'strong,em,span[style],a[href]'
27
+ },
28
+ full: {
29
+ ...DEFAULT_TINYMCE_CONFIG,
30
+ plugins: 'link lists autolink charmap',
31
+ style_formats: [
32
+ { title: 'Paragraph', format: 'p' },
33
+ { title: 'Header 1', format: 'h1' },
34
+ { title: 'Header 2', format: 'h2' },
35
+ { title: 'Header 3', format: 'h3' },
36
+ { title: 'Header 4', format: 'h4' },
37
+ { title: 'Header 5', format: 'h5' },
38
+ { title: 'Header 6', format: 'h6' },
39
+ { title: 'Pre', format: 'pre' },
40
+ { title: 'Code', format: 'code' }
41
+ ],
42
+ toolbar: [
43
+ 'styleselect undo redo | bold italic underline | forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent | hr charmap removeformat | link'
44
+ ]
45
+ },
46
+ plain: {
47
+ ...DEFAULT_TINYMCE_CONFIG,
48
+ plugins: '',
49
+ toolbar: ''
50
+ }
51
+ };
@@ -0,0 +1,3 @@
1
+ :host {
2
+ display: block;
3
+ }
@@ -0,0 +1,24 @@
1
+ import { Spectator, createComponentFactory } from '@ngneat/spectator';
2
+
3
+ import { NoComponent } from './no-component.component';
4
+
5
+ import { DotCMSContentlet } from '../../models';
6
+
7
+ describe('NoComponentComponent', () => {
8
+ let spectator: Spectator<NoComponent>;
9
+
10
+ const createComponent = createComponentFactory(NoComponent);
11
+
12
+ beforeEach(() => {
13
+ spectator = createComponent({
14
+ props: {
15
+ contentlet: { contentType: 'exampleContentType' } as DotCMSContentlet
16
+ }
17
+ });
18
+ });
19
+
20
+ it('should display the content type', () => {
21
+ const noComponent = spectator.debugElement.nativeElement;
22
+ expect(noComponent?.innerHTML).toContain('No Component for exampleContentType');
23
+ });
24
+ });
@@ -0,0 +1,31 @@
1
+ import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core';
2
+
3
+ import { DotCMSContentlet } from '../../models';
4
+
5
+ /**
6
+ * This component is responsible to display a message when there is no component for a contentlet.
7
+ *
8
+ * @export
9
+ * @class NoComponent
10
+ */
11
+ @Component({
12
+ selector: 'dotcms-no-component',
13
+ standalone: true,
14
+ template: `
15
+ No Component for {{ contentlet.contentType }}
16
+ `,
17
+ styleUrl: './no-component.component.css',
18
+ changeDetection: ChangeDetectionStrategy.OnPush
19
+ })
20
+ export class NoComponent {
21
+ /**
22
+ * The contentlet object containing content data.
23
+ * The component displays a message based on the content type of this contentlet.
24
+ */
25
+ @Input() contentlet!: DotCMSContentlet;
26
+
27
+ /**
28
+ * The data-testid attribute used for identifying the component during testing.
29
+ */
30
+ @HostBinding('attr.data-testid') testId = 'no-component';
31
+ }
@@ -0,0 +1,99 @@
1
+ :host.col-start-1 {
2
+ grid-column-start: 1;
3
+ }
4
+
5
+ :host.col-start-2 {
6
+ grid-column-start: 2;
7
+ }
8
+
9
+ :host.col-start-3 {
10
+ grid-column-start: 3;
11
+ }
12
+
13
+ :host.col-start-4 {
14
+ grid-column-start: 4;
15
+ }
16
+
17
+ :host.col-start-5 {
18
+ grid-column-start: 5;
19
+ }
20
+
21
+ :host.col-start-6 {
22
+ grid-column-start: 6;
23
+ }
24
+
25
+ :host.col-start-7 {
26
+ grid-column-start: 7;
27
+ }
28
+
29
+ :host.col-start-8 {
30
+ grid-column-start: 8;
31
+ }
32
+
33
+ :host.col-start-9 {
34
+ grid-column-start: 9;
35
+ }
36
+
37
+ :host.col-start-10 {
38
+ grid-column-start: 10;
39
+ }
40
+
41
+ :host.col-start-11 {
42
+ grid-column-start: 11;
43
+ }
44
+
45
+ :host.col-start-12 {
46
+ grid-column-start: 12;
47
+ }
48
+
49
+ :host.col-end-1 {
50
+ grid-column-end: 1;
51
+ }
52
+
53
+ :host.col-end-2 {
54
+ grid-column-end: 2;
55
+ }
56
+
57
+ :host.col-end-3 {
58
+ grid-column-end: 3;
59
+ }
60
+
61
+ :host.col-end-4 {
62
+ grid-column-end: 4;
63
+ }
64
+
65
+ :host.col-end-5 {
66
+ grid-column-end: 5;
67
+ }
68
+
69
+ :host.col-end-6 {
70
+ grid-column-end: 6;
71
+ }
72
+
73
+ :host.col-end-7 {
74
+ grid-column-end: 7;
75
+ }
76
+
77
+ :host.col-end-8 {
78
+ grid-column-end: 8;
79
+ }
80
+
81
+ :host.col-end-9 {
82
+ grid-column-end: 9;
83
+ }
84
+
85
+ :host.col-end-10 {
86
+ grid-column-end: 10;
87
+ }
88
+
89
+ :host.col-end-11 {
90
+ grid-column-end: 11;
91
+ }
92
+
93
+ :host.col-end-12 {
94
+ grid-column-end: 12;
95
+ }
96
+
97
+ :host.col-end-13 {
98
+ grid-column-end: 13;
99
+ }
@@ -0,0 +1,33 @@
1
+ import { Spectator, createComponentFactory } from '@ngneat/spectator';
2
+ import { MockComponent } from 'ng-mocks';
3
+
4
+ import { ColumnComponent } from './column.component';
5
+
6
+ import { DotPageAssetLayoutColumn } from '../../models';
7
+ import { PageResponseMock } from '../../utils/testing.utils';
8
+ import { ContainerComponent } from '../container/container.component';
9
+
10
+ describe('ColumnComponent', () => {
11
+ let spectator: Spectator<ColumnComponent>;
12
+
13
+ const createComponent = createComponentFactory({
14
+ component: ColumnComponent,
15
+ imports: [MockComponent(ContainerComponent)]
16
+ });
17
+
18
+ beforeEach(() => {
19
+ spectator = createComponent({
20
+ props: {
21
+ column: PageResponseMock.layout.body.rows[0].columns[0] as DotPageAssetLayoutColumn
22
+ }
23
+ });
24
+ });
25
+
26
+ it('should render one container', () => {
27
+ expect(spectator.queryAll(ContainerComponent)?.length).toBe(1);
28
+ });
29
+
30
+ it('should set correct containerClasses', () => {
31
+ expect(spectator.component.containerClasses).toBe('col-start-1 col-end-13');
32
+ });
33
+ });
@@ -0,0 +1,49 @@
1
+ import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit } from '@angular/core';
2
+
3
+ import { DotPageAssetLayoutColumn } from '../../models';
4
+ import { getPositionStyleClasses } from '../../utils';
5
+ import { ContainerComponent } from '../container/container.component';
6
+
7
+ /**
8
+ * This component is responsible to display a column with containers.
9
+ *
10
+ * @export
11
+ * @class ColumnComponent
12
+ * @implements {OnInit}
13
+ */
14
+ @Component({
15
+ selector: 'dotcms-column',
16
+ standalone: true,
17
+ imports: [ContainerComponent],
18
+ template: `
19
+ @for (container of column.containers; track $index) {
20
+ <dotcms-container [container]="container" />
21
+ }
22
+ `,
23
+ styleUrl: './column.component.css',
24
+ changeDetection: ChangeDetectionStrategy.OnPush
25
+ })
26
+ export class ColumnComponent implements OnInit {
27
+ /**
28
+ * The column object containing the containers.
29
+ *
30
+ * @type {DotPageAssetLayoutColumn}
31
+ * @memberof ColumnComponent
32
+ */
33
+ @Input() column!: DotPageAssetLayoutColumn;
34
+
35
+ /**
36
+ * The data-testid attribute used for identifying the component during testing.
37
+ *
38
+ * @memberof ColumnComponent
39
+ */
40
+ @HostBinding('class') containerClasses = '';
41
+
42
+ ngOnInit() {
43
+ const { startClass, endClass } = getPositionStyleClasses(
44
+ this.column.leftOffset,
45
+ this.column.width + this.column.leftOffset
46
+ );
47
+ this.containerClasses = `${startClass} ${endClass}`;
48
+ }
49
+ }
@@ -0,0 +1,9 @@
1
+ :host.empty-container {
2
+ width: 100%;
3
+ background-color: #ecf0fd;
4
+ display: flex;
5
+ justify-content: center;
6
+ align-items: center;
7
+ color: #030e32;
8
+ height: 10rem;
9
+ }
@@ -0,0 +1,26 @@
1
+ @if ($isInsideEditor()) {
2
+ @if ($contentlets().length) {
3
+ @for (contentlet of $contentlets(); track $index) {
4
+ <dotcms-contentlet-wrapper
5
+ [contentlet]="contentlet"
6
+ [container]="$dotContainerAsString()">
7
+ <ng-container
8
+ *ngComponentOutlet="
9
+ (componentsMap[contentlet.contentType] || componentsMap['CustomNoComponent']
10
+ | async) || NoComponent;
11
+ inputs: { contentlet }
12
+ " />
13
+ </dotcms-contentlet-wrapper>
14
+ }
15
+ } @else {
16
+ This container is empty.
17
+ }
18
+ } @else {
19
+ @for (contentlet of $contentlets(); track $index) {
20
+ <ng-container
21
+ *ngComponentOutlet="
22
+ componentsMap[contentlet.contentType] | async;
23
+ inputs: { contentlet }
24
+ " />
25
+ }
26
+ }