@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.
- package/.eslintrc.json +18 -0
- package/jest.config.ts +22 -0
- package/ng-package.json +7 -0
- package/package.json +4 -21
- package/project.json +33 -0
- package/{index.d.ts → src/index.ts} +0 -1
- package/src/lib/components/dot-editable-text/dot-editable-text.component.css +4 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.html +8 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.spec.ts +424 -0
- package/src/lib/components/dot-editable-text/dot-editable-text.component.ts +269 -0
- package/src/lib/components/dot-editable-text/utils.ts +51 -0
- package/src/lib/components/no-component/no-component.component.css +3 -0
- package/src/lib/components/no-component/no-component.component.spec.ts +24 -0
- package/src/lib/components/no-component/no-component.component.ts +31 -0
- package/src/lib/layout/column/column.component.css +99 -0
- package/src/lib/layout/column/column.component.spec.ts +33 -0
- package/src/lib/layout/column/column.component.ts +49 -0
- package/src/lib/layout/container/container.component.css +9 -0
- package/src/lib/layout/container/container.component.html +26 -0
- package/src/lib/layout/container/container.component.spec.ts +205 -0
- package/src/lib/layout/container/container.component.ts +140 -0
- package/src/lib/layout/contentlet/contentlet.component.spec.ts +22 -0
- package/{lib/layout/contentlet/contentlet.component.d.ts → src/lib/layout/contentlet/contentlet.component.ts} +32 -17
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.css +3 -0
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.spec.ts +195 -0
- package/src/lib/layout/dotcms-layout/dotcms-layout.component.ts +150 -0
- package/src/lib/layout/row/row.component.css +6 -0
- package/src/lib/layout/row/row.component.spec.ts +28 -0
- package/src/lib/layout/row/row.component.ts +32 -0
- package/{lib/models/dotcms.model.d.ts → src/lib/models/dotcms.model.ts} +21 -3
- package/{lib/models/index.d.ts → src/lib/models/index.ts} +8 -1
- package/{lib/services/dotcms-context/page-context.service.d.ts → src/lib/services/dotcms-context/page-context.service.ts} +41 -12
- package/src/lib/services/dotcms-context/page-context.spec.ts +80 -0
- package/src/lib/utils/index.ts +92 -0
- package/src/lib/utils/testing.utils.ts +1019 -0
- package/src/test-setup.ts +8 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +11 -0
- package/dotcms-angular.d.ts.map +0 -1
- package/esm2022/dotcms-angular.mjs +0 -5
- package/esm2022/index.mjs +0 -5
- package/esm2022/lib/components/dot-editable-text/dot-editable-text.component.mjs +0 -225
- package/esm2022/lib/components/dot-editable-text/utils.mjs +0 -43
- package/esm2022/lib/components/no-component/no-component.component.mjs +0 -32
- package/esm2022/lib/layout/column/column.component.mjs +0 -45
- package/esm2022/lib/layout/container/container.component.mjs +0 -126
- package/esm2022/lib/layout/contentlet/contentlet.component.mjs +0 -120
- package/esm2022/lib/layout/dotcms-layout/dotcms-layout.component.mjs +0 -100
- package/esm2022/lib/layout/row/row.component.mjs +0 -29
- package/esm2022/lib/models/dotcms.model.mjs +0 -3
- package/esm2022/lib/models/index.mjs +0 -3
- package/esm2022/lib/services/dotcms-context/page-context.service.mjs +0 -75
- package/esm2022/lib/utils/index.mjs +0 -79
- package/fesm2022/dotcms-angular.mjs +0 -858
- package/fesm2022/dotcms-angular.mjs.map +0 -1
- package/index.d.ts.map +0 -1
- package/lib/components/dot-editable-text/dot-editable-text.component.d.ts +0 -129
- package/lib/components/dot-editable-text/dot-editable-text.component.d.ts.map +0 -1
- package/lib/components/dot-editable-text/utils.d.ts +0 -7
- package/lib/components/dot-editable-text/utils.d.ts.map +0 -1
- package/lib/components/no-component/no-component.component.d.ts +0 -22
- package/lib/components/no-component/no-component.component.d.ts.map +0 -1
- package/lib/layout/column/column.component.d.ts +0 -29
- package/lib/layout/column/column.component.d.ts.map +0 -1
- package/lib/layout/container/container.component.d.ts +0 -88
- package/lib/layout/container/container.component.d.ts.map +0 -1
- package/lib/layout/contentlet/contentlet.component.d.ts.map +0 -1
- package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts +0 -67
- package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts.map +0 -1
- package/lib/layout/row/row.component.d.ts +0 -20
- package/lib/layout/row/row.component.d.ts.map +0 -1
- package/lib/models/dotcms.model.d.ts.map +0 -1
- package/lib/models/index.d.ts.map +0 -1
- package/lib/services/dotcms-context/page-context.service.d.ts.map +0 -1
- package/lib/utils/index.d.ts +0 -63
- 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,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,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
|
+
}
|