@pega/angular-sdk-overrides 25.1.12 → 26.1.10

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 (168) hide show
  1. package/lib/designSystemExtension/alert/alert.component.html +4 -1
  2. package/lib/designSystemExtension/alert/alert.component.scss +9 -10
  3. package/lib/designSystemExtension/alert/alert.component.ts +6 -2
  4. package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
  5. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +51 -36
  6. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +12 -0
  7. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +11 -0
  8. package/lib/designSystemExtension/operator/operator.component.html +5 -3
  9. package/lib/designSystemExtension/operator/operator.component.scss +7 -0
  10. package/lib/designSystemExtension/operator/operator.component.ts +2 -1
  11. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +100 -10
  12. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
  13. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +1 -1
  14. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +240 -72
  15. package/lib/field/auto-complete/auto-complete.component.html +46 -5
  16. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  17. package/lib/field/auto-complete/auto-complete.component.ts +350 -23
  18. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  19. package/lib/field/check-box/check-box.component.html +8 -6
  20. package/lib/field/check-box/check-box.component.ts +9 -2
  21. package/lib/field/currency/currency.component.html +2 -1
  22. package/lib/field/currency/currency.component.ts +10 -1
  23. package/lib/field/date/date.component.html +2 -1
  24. package/lib/field/date/date.component.ts +2 -0
  25. package/lib/field/date-time/date-time.component.html +2 -1
  26. package/lib/field/date-time/date-time.component.scss +46 -40
  27. package/lib/field/date-time/date-time.component.ts +29 -0
  28. package/lib/field/decimal/decimal.component.html +2 -1
  29. package/lib/field/decimal/decimal.component.ts +2 -0
  30. package/lib/field/dropdown/dropdown.component.html +4 -1
  31. package/lib/field/dropdown/dropdown.component.ts +18 -19
  32. package/lib/field/email/email.component.html +2 -1
  33. package/lib/field/email/email.component.ts +2 -2
  34. package/lib/field/embedded-data-multi/embedded-data-multi.component.html +3 -0
  35. package/lib/field/embedded-data-multi/embedded-data-multi.component.scss +0 -0
  36. package/lib/field/embedded-data-multi/embedded-data-multi.component.ts +173 -0
  37. package/lib/field/field.base.ts +27 -1
  38. package/lib/field/group/group.component.html +1 -1
  39. package/lib/field/group/group.component.ts +16 -7
  40. package/lib/field/integer/integer.component.html +2 -1
  41. package/lib/field/integer/integer.component.ts +4 -2
  42. package/lib/field/location/location.component.html +2 -1
  43. package/lib/field/location/location.component.ts +3 -1
  44. package/lib/field/multiselect/multiselect.component.html +1 -1
  45. package/lib/field/multiselect/multiselect.component.ts +23 -4
  46. package/lib/field/multiselect/utils.ts +5 -3
  47. package/lib/field/object-reference/object-reference.component.html +30 -15
  48. package/lib/field/object-reference/object-reference.component.ts +818 -94
  49. package/lib/field/percentage/percentage.component.html +2 -1
  50. package/lib/field/percentage/percentage.component.ts +10 -1
  51. package/lib/field/phone/phone.component.html +2 -1
  52. package/lib/field/phone/phone.component.ts +2 -1
  53. package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
  54. package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
  55. package/lib/field/rich-text/rich-text.component.scss +1 -166
  56. package/lib/field/rich-text/rich-text.component.spec.ts +1 -1
  57. package/lib/field/rich-text/rich-text.component.ts +1 -1
  58. package/lib/field/selectable-card/selectable-card.component.html +1 -0
  59. package/lib/field/selectable-card/selectable-card.component.ts +31 -16
  60. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  61. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  62. package/lib/field/semantic-link/semantic-link.component.ts +13 -7
  63. package/lib/field/text-area/text-area.component.html +2 -1
  64. package/lib/field/text-area/text-area.component.ts +2 -1
  65. package/lib/field/text-input/text-input.component.html +2 -1
  66. package/lib/field/text-input/text-input.component.ts +2 -1
  67. package/lib/field/time/time.component.html +2 -1
  68. package/lib/field/time/time.component.ts +2 -1
  69. package/lib/field/url/url.component.html +2 -1
  70. package/lib/field/url/url.component.ts +2 -1
  71. package/lib/field/user-reference/user-reference.component.html +4 -2
  72. package/lib/field/user-reference/user-reference.component.ts +11 -6
  73. package/lib/infra/Containers/flow-container/flow-container.component.html +2 -2
  74. package/lib/infra/Containers/flow-container/flow-container.component.ts +34 -42
  75. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  76. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  77. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  78. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  79. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -196
  80. package/lib/infra/Containers/view-container/view-container.component.html +1 -1
  81. package/lib/infra/Containers/view-container/view-container.component.ts +36 -43
  82. package/lib/infra/assignment/assignment.component.html +2 -1
  83. package/lib/infra/assignment/assignment.component.ts +44 -22
  84. package/lib/infra/assignment-card/assignment-card.component.html +1 -0
  85. package/lib/infra/assignment-card/assignment-card.component.ts +2 -8
  86. package/lib/infra/defer-load/defer-load.component.ts +7 -5
  87. package/lib/infra/multi-step/multi-step.component.html +25 -33
  88. package/lib/infra/multi-step/multi-step.component.scss +67 -125
  89. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  90. package/lib/infra/navbar/navbar.component.ts +33 -36
  91. package/lib/infra/reference/reference.component.html +7 -1
  92. package/lib/infra/reference/reference.component.ts +63 -109
  93. package/lib/infra/region/region.component.ts +1 -3
  94. package/lib/infra/root-container/root-container.component.html +1 -0
  95. package/lib/infra/root-container/root-container.component.ts +34 -42
  96. package/lib/infra/view/view.component.ts +22 -46
  97. package/lib/template/advanced-search/advanced-search.component.ts +0 -1
  98. package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
  99. package/lib/template/app-shell/app-shell.component.ts +13 -13
  100. package/lib/template/banner-page/banner-page.component.ts +1 -2
  101. package/lib/template/base/details-template-base.ts +41 -14
  102. package/lib/template/base/form-template-base.ts +0 -2
  103. package/lib/template/case-summary/case-summary.component.ts +15 -7
  104. package/lib/template/case-view/case-view.component.html +14 -2
  105. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  106. package/lib/template/case-view/case-view.component.ts +52 -3
  107. package/lib/template/data-reference/data-reference.component.ts +29 -22
  108. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  109. package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
  110. package/lib/template/data-reference/search-form/tabsData.ts +0 -1
  111. package/lib/template/default-form/default-form.component.html +20 -4
  112. package/lib/template/default-form/default-form.component.ts +14 -5
  113. package/lib/template/default-page/default-page.component.ts +1 -2
  114. package/lib/template/details/details.component.html +23 -3
  115. package/lib/template/details/details.component.scss +9 -2
  116. package/lib/template/details/details.component.ts +3 -70
  117. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  118. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  119. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
  120. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  121. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  122. package/lib/template/details-one-column/details-one-column.component.ts +3 -40
  123. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  124. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  125. package/lib/template/details-three-column/details-three-column.component.ts +3 -48
  126. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  127. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  128. package/lib/template/details-two-column/details-two-column.component.ts +3 -46
  129. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  130. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  131. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
  132. package/lib/template/dynamic-tabs/dynamic-tabs.component.html +1 -0
  133. package/lib/template/field-group-template/field-group-template.component.ts +19 -6
  134. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  135. package/lib/template/inline-dashboard-page/inline-dashboard-page.component.ts +2 -10
  136. package/lib/template/list-view/list-view.component.ts +47 -11
  137. package/lib/template/one-column/one-column.component.html +3 -0
  138. package/lib/template/one-column-tab/one-column-tab.component.html +1 -0
  139. package/lib/template/page/page.component.html +1 -0
  140. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  141. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  142. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  143. package/lib/template/simple-table/simple-table.component.ts +11 -1
  144. package/lib/template/simple-table-manual/helpers.ts +84 -16
  145. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  146. package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
  147. package/lib/template/simple-table-manual/simple-table-manual.component.ts +142 -46
  148. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
  149. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
  150. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
  151. package/lib/template/wide-narrow-page/wide-narrow-page.component.ts +0 -3
  152. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
  153. package/lib/widget/attachment/AttachmentUtils.ts +19 -11
  154. package/lib/widget/attachment/attachment.component.html +16 -6
  155. package/lib/widget/attachment/attachment.component.ts +14 -4
  156. package/lib/widget/case-history/case-history.component.ts +6 -2
  157. package/lib/widget/feed-container/feed-container.component.ts +1 -199
  158. package/lib/widget/file-utility/file-utility.component.ts +26 -30
  159. package/lib/widget/todo/todo.component.ts +7 -2
  160. package/package.json +2 -2
  161. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  162. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  163. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  164. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  165. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
  166. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  167. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  168. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -37
@@ -1,18 +1,37 @@
1
- import { Component, Input, Output, EventEmitter, OnChanges } from '@angular/core';
1
+ import {
2
+ AfterViewInit,
3
+ ChangeDetectorRef,
4
+ Component,
5
+ ElementRef,
6
+ EventEmitter,
7
+ Input,
8
+ OnChanges,
9
+ OnDestroy,
10
+ Output,
11
+ SimpleChanges,
12
+ ViewChild
13
+ } from '@angular/core';
2
14
  import { CommonModule } from '@angular/common';
3
15
  import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
4
- import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
16
+ import { MatButtonModule } from '@angular/material/button';
17
+ import { MatFormFieldModule } from '@angular/material/form-field';
18
+ import { MatIconModule } from '@angular/material/icon';
19
+ import { MatSelectModule } from '@angular/material/select';
20
+ import { Editor } from '@tiptap/core';
21
+ import StarterKit from '@tiptap/starter-kit';
22
+ import Placeholder from '@tiptap/extension-placeholder';
23
+ import Image from '@tiptap/extension-image';
24
+ import Link from '@tiptap/extension-link';
5
25
 
6
- declare let tinymce: any;
26
+ type BlockType = 'paragraph' | 'h1' | 'h2' | 'h3';
7
27
 
8
28
  @Component({
9
29
  selector: 'app-rich-text-editor',
10
30
  templateUrl: './rich-text-editor.component.html',
11
31
  styleUrls: ['./rich-text-editor.component.scss'],
12
- imports: [CommonModule, EditorModule, ReactiveFormsModule],
13
- providers: [{ provide: TINYMCE_SCRIPT_SRC, useValue: 'tinymce/tinymce.min.js' }]
32
+ imports: [CommonModule, ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatSelectModule]
14
33
  })
15
- export class RichTextEditorComponent implements OnChanges {
34
+ export class RichTextEditorComponent implements OnChanges, AfterViewInit, OnDestroy {
16
35
  @Input() placeholder;
17
36
  @Input() disabled;
18
37
  @Input() readonly;
@@ -26,13 +45,27 @@ export class RichTextEditorComponent implements OnChanges {
26
45
  @Output() onBlur: EventEmitter<any> = new EventEmitter();
27
46
  @Output() onChange: EventEmitter<any> = new EventEmitter();
28
47
 
29
- richText = new FormControl();
30
- editorConfig: any = {};
48
+ private editorEl?: ElementRef<HTMLDivElement>;
31
49
 
32
- ngOnChanges() {
33
- if (this.required) {
34
- this.richText.addValidators(Validators.required);
50
+ richText = new FormControl('');
51
+ editor?: Editor;
52
+ currentBlock: BlockType = 'paragraph';
53
+
54
+ private viewInitialized = false;
55
+
56
+ constructor(private cdr: ChangeDetectorRef) {}
57
+
58
+ @ViewChild('editorEl')
59
+ set editorElement(element: ElementRef<HTMLDivElement> | undefined) {
60
+ this.editorEl = element;
61
+ if (element && this.viewInitialized && !this.readonly && !this.editor) {
62
+ this.initializeEditor();
35
63
  }
64
+ }
65
+
66
+ ngOnChanges(changes: SimpleChanges) {
67
+ this.richText.setValidators(this.required ? Validators.required : null);
68
+ this.richText.updateValueAndValidity({ emitEvent: false });
36
69
 
37
70
  if (this.disabled) {
38
71
  this.richText.disable();
@@ -40,79 +73,214 @@ export class RichTextEditorComponent implements OnChanges {
40
73
  this.richText.enable();
41
74
  }
42
75
 
43
- if (this.value) {
44
- this.richText.setValue(this.value);
76
+ this.richText.setValue(this.value ?? '', { emitEvent: false });
77
+
78
+ if (!this.viewInitialized) return;
79
+
80
+ if (this.readonly) {
81
+ this.destroyEditor();
82
+ return;
83
+ }
84
+
85
+ if (!this.editor && this.editorEl) {
86
+ this.initializeEditor();
87
+ }
88
+
89
+ if (this.editor && changes['value'] && (this.value ?? '') !== this.getEditorValue(this.editor)) {
90
+ this.editor.commands.setContent(this.value ?? '', { emitUpdate: false });
91
+ }
92
+ if (this.editor && changes['disabled']) {
93
+ this.editor.setEditable(!this.disabled);
94
+ }
95
+ }
96
+
97
+ ngAfterViewInit(): void {
98
+ this.viewInitialized = true;
99
+ if (!this.readonly) this.initializeEditor();
100
+ }
101
+
102
+ private initializeEditor(): void {
103
+ if (!this.editorEl || this.editor) return;
104
+
105
+ this.editor = new Editor({
106
+ element: this.editorEl.nativeElement,
107
+ extensions: [
108
+ StarterKit,
109
+ Placeholder.configure({ placeholder: this.placeholder || '' }),
110
+ Image.configure({ inline: false, allowBase64: true }),
111
+ Link.configure({ openOnClick: false, autolink: true })
112
+ ],
113
+ content: this.value || '',
114
+ editable: !this.disabled,
115
+ onUpdate: ({ editor }) => {
116
+ const html = this.getEditorValue(editor);
117
+ this.richText.setValue(html, { emitEvent: false });
118
+ this.richText.markAsDirty();
119
+ this.onChange.emit(html);
120
+ this.cdr.markForCheck();
121
+ },
122
+ onBlur: ({ editor }) => {
123
+ this.richText.markAsTouched();
124
+ this.onBlur.emit(this.getEditorValue(editor));
125
+ this.cdr.markForCheck();
126
+ },
127
+ onSelectionUpdate: ({ editor }) => {
128
+ this.updateCurrentBlock(editor);
129
+ this.cdr.markForCheck();
130
+ }
131
+ });
132
+ this.updateCurrentBlock(this.editor);
133
+ }
134
+
135
+ // Tiptap reports an empty document as "<p></p>", which would satisfy Validators.required and the engine's required check.
136
+ private getEditorValue(editor: Editor): string {
137
+ return editor.isEmpty ? '' : editor.getHTML();
138
+ }
139
+
140
+ get showError(): boolean {
141
+ return !!this.error || (this.richText.invalid && (this.richText.touched || this.richText.dirty));
142
+ }
143
+
144
+ get errorMessage(): string {
145
+ if (!this.error && this.richText.hasError('required')) {
146
+ return 'You must enter a value';
147
+ }
148
+ return this.info;
149
+ }
150
+
151
+ private updateCurrentBlock(editor: Editor): void {
152
+ if (editor.isActive('heading', { level: 1 })) this.currentBlock = 'h1';
153
+ else if (editor.isActive('heading', { level: 2 })) this.currentBlock = 'h2';
154
+ else if (editor.isActive('heading', { level: 3 })) this.currentBlock = 'h3';
155
+ else this.currentBlock = 'paragraph';
156
+ }
157
+
158
+ private destroyEditor(): void {
159
+ this.editor?.destroy();
160
+ this.editor = undefined;
161
+ this.currentBlock = 'paragraph';
162
+ }
163
+
164
+ ngOnDestroy(): void {
165
+ this.destroyEditor();
166
+ }
167
+
168
+ isActive(name: string, attrs?: Record<string, any>): boolean {
169
+ return this.editor ? this.editor.isActive(name, attrs) : false;
170
+ }
171
+
172
+ setBlock(value: string) {
173
+ if (!this.editor) return;
174
+ const chain = this.editor.chain().focus();
175
+ switch (value) {
176
+ case 'h1':
177
+ chain.toggleHeading({ level: 1 }).run();
178
+ break;
179
+ case 'h2':
180
+ chain.toggleHeading({ level: 2 }).run();
181
+ break;
182
+ case 'h3':
183
+ chain.toggleHeading({ level: 3 }).run();
184
+ break;
185
+ default:
186
+ chain.setParagraph().run();
187
+ }
188
+ this.currentBlock = value as BlockType;
189
+ }
190
+
191
+ toggleBold() {
192
+ this.editor?.chain().focus().toggleBold().run();
193
+ }
194
+
195
+ toggleItalic() {
196
+ this.editor?.chain().focus().toggleItalic().run();
197
+ }
198
+
199
+ toggleStrike() {
200
+ this.editor?.chain().focus().toggleStrike().run();
201
+ }
202
+
203
+ toggleBulletList() {
204
+ this.editor?.chain().focus().toggleBulletList().run();
205
+ }
206
+
207
+ toggleOrderedList() {
208
+ this.editor?.chain().focus().toggleOrderedList().run();
209
+ }
210
+
211
+ outdent() {
212
+ if (!this.editor) return;
213
+ if (this.editor.can().liftListItem('listItem')) {
214
+ this.editor.chain().focus().liftListItem('listItem').run();
215
+ } else {
216
+ this.editor.chain().focus().liftEmptyBlock().run();
217
+ }
218
+ }
219
+
220
+ indent() {
221
+ this.editor?.chain().focus().sinkListItem('listItem').run();
222
+ }
223
+
224
+ addLink() {
225
+ if (!this.editor) return;
226
+ const previous = this.editor.getAttributes('link')?.['href'] ?? '';
227
+ const { from, to, empty } = this.editor.state.selection;
228
+ const url = window.prompt('Enter URL', previous);
229
+ if (url === null) return;
230
+ if (url === '') {
231
+ this.editor.chain().focus().setTextSelection({ from, to }).extendMarkRange('link').unsetLink().run();
232
+ return;
233
+ }
234
+ const normalizedUrl = this.normalizeUrl(url);
235
+ if (!normalizedUrl) return;
236
+ if (empty) {
237
+ this.editor
238
+ .chain()
239
+ .focus()
240
+ .insertContentAt(from, {
241
+ type: 'text',
242
+ text: url,
243
+ marks: [{ type: 'link', attrs: { href: normalizedUrl } }]
244
+ })
245
+ .run();
246
+ return;
45
247
  }
248
+ this.editor.chain().focus().setTextSelection({ from, to }).extendMarkRange('link').setLink({ href: normalizedUrl }).run();
249
+ }
250
+
251
+ private normalizeUrl(url: string): string | null {
252
+ const trimmedUrl = url.trim();
253
+ if (!trimmedUrl) return null;
254
+ if (/^\/\//.test(trimmedUrl)) return `https:${trimmedUrl}`;
255
+ if (/^(?:https?|mailto|tel|ftp):/i.test(trimmedUrl)) return trimmedUrl;
256
+ if (/^[a-z][a-z\d+.-]*:/i.test(trimmedUrl)) return null;
257
+ return `https://${trimmedUrl}`;
258
+ }
46
259
 
47
- const themeElement = document.querySelector('.dark') || document.body;
48
- let textColor = getComputedStyle(themeElement).getPropertyValue('--mat-sys-on-surface').trim();
49
- if (!textColor) textColor = '#000000';
50
-
51
- this.editorConfig = {
52
- base_url: '/tinymce',
53
- suffix: '.min',
54
- menubar: false,
55
- placeholder: this.placeholder,
56
- statusbar: false,
57
- min_height: 130,
58
- plugins: ['lists', 'advlist', 'autolink', 'image', 'link', 'autoresize'],
59
- autoresize_bottom_margin: 0,
60
- toolbar: this.disabled ? false : 'blocks | bold italic strikethrough | bullist numlist outdent indent | link image',
61
- toolbar_location: 'bottom',
62
- content_style: `
63
- body {
64
- font-family: Helvetica, Arial, sans-serif;
65
- font-size: 14px;
66
- color: ${textColor} !important;
67
- background: transparent !important;
68
- }
69
- `,
70
- branding: false,
71
- paste_data_images: true,
72
- file_picker_types: 'image',
73
- file_picker_callback: this.filePickerCallback
74
- };
75
- }
76
-
77
- filePickerCallback = cb => {
260
+ addImage() {
261
+ if (!this.editor) return;
262
+ const insertPosition = this.editor.state.selection.to;
78
263
  const input = document.createElement('input');
79
264
  input.setAttribute('type', 'file');
80
265
  input.setAttribute('accept', 'image/*');
81
266
 
82
267
  input.addEventListener('change', (e: any) => {
83
- const file = e.target.files[0];
84
-
85
- const reader: any = new FileReader();
268
+ const file = e.target.files?.[0];
269
+ if (!file) return;
270
+ const reader = new FileReader();
86
271
  reader.addEventListener('load', () => {
87
- /*
88
- Note: Now we need to register the blob in TinyMCEs image blob
89
- registry. In the next release this part hopefully won't be
90
- necessary, as we are looking to handle it internally.
91
- */
92
- const blobId = `blobid${new Date().getTime()}`;
93
- console.log('editorRef', tinymce.activeEditor);
94
- const blobCache = tinymce.activeEditor.editorUpload.blobCache;
95
- const base64 = reader.result.split(',')[1];
96
- const blobInfo = blobCache.create(blobId, file, base64);
97
- blobCache.add(blobInfo);
98
-
99
- /* call the callback and populate the Title field with the file name */
100
- cb(blobInfo.blobUri(), { title: file.name });
272
+ const src = reader.result as string;
273
+ this.editor
274
+ ?.chain()
275
+ .focus()
276
+ .setTextSelection(insertPosition)
277
+ .insertContent({ type: 'image', attrs: { src, alt: file.name, title: file.name } })
278
+ .run();
101
279
  });
280
+ reader.addEventListener('error', () => this.cdr.markForCheck());
102
281
  reader.readAsDataURL(file);
103
282
  });
104
283
 
105
284
  input.click();
106
- };
107
-
108
- blur() {
109
- if (tinymce.activeEditor) {
110
- const editorValue = tinymce.activeEditor.getContent({ format: 'html' });
111
- this.onBlur.emit(editorValue);
112
- }
113
- }
114
-
115
- change(event) {
116
- this.onChange.emit(event);
117
285
  }
118
286
  }
@@ -4,7 +4,7 @@
4
4
  <ng-template #noDisplayMode>
5
5
  <div *ngIf="!bReadonly$ && bHasForm$; else noEdit">
6
6
  <div [formGroup]="formGroup$" *ngIf="bVisible$">
7
- <mat-form-field class="psdk-full-width" subscriptSizing="dynamic" [hintLabel]="helperText">
7
+ <mat-form-field class="psdk-full-width" subscriptSizing="dynamic">
8
8
  <mat-label>{{ label$ }}</mat-label>
9
9
  <input
10
10
  matInput
@@ -15,11 +15,52 @@
15
15
  [matAutocomplete]="auto"
16
16
  (input)="fieldOnChange($event)"
17
17
  />
18
- <mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption (optionSelected)="optionChanged($event)">
19
- <mat-option *ngFor="let opt of filteredOptions | async" [value]="opt.value">
20
- <span>{{ opt.value }}</span>
21
- </mat-option>
18
+ <mat-autocomplete #auto="matAutocomplete" [class]="'psdk-autocomplete-panel'" autoActiveFirstOption (optionSelected)="optionChanged($event)">
19
+ <ng-container *ngIf="hasGroupBy; else flatOptionsList">
20
+ <mat-optgroup *ngFor="let group of groupedFilteredOptions$ | async" [label]="group.label">
21
+ <mat-option *ngFor="let opt of group.options" [value]="opt.value">
22
+ <span class="psdk-autocomplete-option-primary">{{ opt.value }}</span>
23
+ <div class="psdk-autocomplete-option-secondary" *ngIf="opt.secondaryComponents?.length">
24
+ <ng-container *ngFor="let secondary of opt.secondaryComponents; let last = last">
25
+ <component-mapper
26
+ [name]="secondary.getPConnect().getComponentName()"
27
+ [props]="{ pConn$: secondary.getPConnect(), formGroup$ }"
28
+ ></component-mapper>
29
+ <span *ngIf="!last" class="psdk-autocomplete-option-separator" aria-hidden="true"> · </span>
30
+ </ng-container>
31
+ </div>
32
+ </mat-option>
33
+ </mat-optgroup>
34
+ </ng-container>
35
+ <ng-template #flatOptionsList>
36
+ <mat-option *ngFor="let opt of filteredOptions | async" [value]="opt.value">
37
+ <span class="psdk-autocomplete-option-primary">{{ opt.value }}</span>
38
+ <div class="psdk-autocomplete-option-secondary" *ngIf="opt.secondaryComponents?.length">
39
+ <ng-container *ngFor="let secondary of opt.secondaryComponents; let last = last">
40
+ <component-mapper
41
+ [name]="secondary.getPConnect().getComponentName()"
42
+ [props]="{ pConn$: secondary.getPConnect(), formGroup$ }"
43
+ ></component-mapper>
44
+ <span *ngIf="!last" class="psdk-autocomplete-option-separator" aria-hidden="true"> · </span>
45
+ </ng-container>
46
+ </div>
47
+ </mat-option>
48
+ </ng-template>
49
+ <div *ngIf="showCreateButton" class="psdk-autocomplete-create-new-wrapper">
50
+ <mat-divider></mat-divider>
51
+ <button
52
+ mat-button
53
+ type="button"
54
+ class="psdk-autocomplete-create-new"
55
+ (mousedown)="$event.preventDefault()"
56
+ (click)="createNewButtonHandler()"
57
+ >
58
+ <mat-icon>add</mat-icon>
59
+ {{ createNewLabel }}
60
+ </button>
61
+ </div>
22
62
  </mat-autocomplete>
63
+ <mat-hint *ngIf="helperText" [appFieldWarning]="bFieldMessageVisible$">{{ helperText }}</mat-hint>
23
64
  <mat-error *ngIf="fieldControl.invalid">{{ getErrorMessage() }}</mat-error>
24
65
  </mat-form-field>
25
66
  </div>
@@ -18,3 +18,95 @@
18
18
  ::ng-deep .mat-mdc-form-field-infix {
19
19
  width: auto;
20
20
  }
21
+
22
+ // Scoped to this component's own panel via mat-autocomplete's [class] input — does not affect
23
+ // other mat-option/mat-autocomplete instances in the app (research.md §3).
24
+ ::ng-deep .psdk-autocomplete-panel {
25
+ overflow-x: hidden;
26
+
27
+ .mat-mdc-option {
28
+ .mdc-list-item__primary-text {
29
+ width: 100%;
30
+ white-space: normal;
31
+ overflow: visible;
32
+ text-overflow: unset;
33
+ }
34
+
35
+ // Only grow/top-align options that actually render secondary content — leaves options
36
+ // without secondary text at Material's default single-line height/vertical centering.
37
+ &:has(.psdk-autocomplete-option-secondary) {
38
+ height: auto;
39
+ min-height: 48px;
40
+ align-items: flex-start;
41
+ padding-top: 8px;
42
+ padding-bottom: 8px;
43
+ }
44
+ }
45
+ }
46
+
47
+ .psdk-autocomplete-option-secondary {
48
+ display: flex;
49
+ flex-wrap: wrap;
50
+ align-items: baseline;
51
+ column-gap: 0.25rem;
52
+ width: 100%;
53
+ overflow-wrap: break-word;
54
+ word-break: break-word;
55
+ color: var(--mat-sys-on-surface-variant);
56
+
57
+ // Each secondary field renders through FieldValueList's own DISPLAY_ONLY template, which
58
+ // defaults to a two-column grid block (.psdk-container-labels-left) or an unset-display
59
+ // block (.psdk-container-nolabels). Override both to render inline here so multiple fields
60
+ // join on one line via the `·` separators, instead of stacking as separate block rows
61
+ // (FR-002/FR-004). FieldValueListComponent itself is not modified (research.md §6).
62
+ ::ng-deep .psdk-container-labels-left {
63
+ display: inline-flex;
64
+ column-gap: 0.25rem;
65
+ row-gap: 0;
66
+ padding-block: 0;
67
+ }
68
+
69
+ ::ng-deep .psdk-container-nolabels {
70
+ display: inline;
71
+ padding-block: 0;
72
+ }
73
+
74
+ ::ng-deep .psdk-grid-label::after {
75
+ content: ':';
76
+ }
77
+
78
+ ::ng-deep .psdk-val-labels-left {
79
+ white-space: normal;
80
+ }
81
+ }
82
+
83
+ ::ng-deep .psdk-autocomplete-create-new-wrapper {
84
+ position: sticky;
85
+ bottom: 0;
86
+ z-index: 1;
87
+ background-color: var(--mat-sys-surface-container, var(--mat-sys-surface, #fff));
88
+
89
+ .mat-divider {
90
+ margin: 0;
91
+ }
92
+
93
+ .psdk-autocomplete-create-new {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: flex-start;
97
+ width: 100%;
98
+ height: 48px;
99
+ padding: 0 16px;
100
+ text-align: left;
101
+ text-transform: none;
102
+ color: var(--mat-sys-primary);
103
+
104
+ .mat-mdc-button-touch-target {
105
+ width: 100%;
106
+ }
107
+
108
+ .mat-icon {
109
+ margin-right: 8px;
110
+ }
111
+ }
112
+ }