@pega/angular-sdk-overrides 25.1.13 → 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 (153) 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 +15 -5
  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 +1 -1
  74. package/lib/infra/Containers/flow-container/flow-container.component.ts +35 -46
  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 -194
  80. package/lib/infra/Containers/view-container/view-container.component.ts +32 -35
  81. package/lib/infra/assignment/assignment.component.html +2 -1
  82. package/lib/infra/assignment/assignment.component.ts +42 -15
  83. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  84. package/lib/infra/multi-step/multi-step.component.html +25 -33
  85. package/lib/infra/multi-step/multi-step.component.scss +67 -125
  86. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  87. package/lib/infra/navbar/navbar.component.ts +33 -36
  88. package/lib/infra/reference/reference.component.ts +1 -2
  89. package/lib/infra/root-container/root-container.component.ts +33 -38
  90. package/lib/infra/view/view.component.ts +21 -40
  91. package/lib/template/advanced-search/advanced-search.component.ts +0 -1
  92. package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
  93. package/lib/template/app-shell/app-shell.component.ts +13 -13
  94. package/lib/template/base/details-template-base.ts +41 -14
  95. package/lib/template/case-summary/case-summary.component.ts +15 -1
  96. package/lib/template/case-view/case-view.component.html +13 -1
  97. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  98. package/lib/template/case-view/case-view.component.ts +52 -3
  99. package/lib/template/data-reference/data-reference.component.ts +2 -4
  100. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  101. package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
  102. package/lib/template/data-reference/search-form/tabsData.ts +0 -1
  103. package/lib/template/default-form/default-form.component.html +19 -3
  104. package/lib/template/default-form/default-form.component.ts +13 -3
  105. package/lib/template/details/details.component.html +23 -3
  106. package/lib/template/details/details.component.scss +9 -2
  107. package/lib/template/details/details.component.ts +3 -70
  108. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  109. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  110. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
  111. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  112. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  113. package/lib/template/details-one-column/details-one-column.component.ts +3 -40
  114. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  115. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  116. package/lib/template/details-three-column/details-three-column.component.ts +3 -48
  117. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  118. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  119. package/lib/template/details-two-column/details-two-column.component.ts +3 -46
  120. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  121. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  122. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
  123. package/lib/template/field-group-template/field-group-template.component.ts +19 -6
  124. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  125. package/lib/template/list-view/list-view.component.ts +47 -11
  126. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  127. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  128. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  129. package/lib/template/simple-table/simple-table.component.ts +6 -0
  130. package/lib/template/simple-table-manual/helpers.ts +84 -16
  131. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  132. package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
  133. package/lib/template/simple-table-manual/simple-table-manual.component.ts +131 -43
  134. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
  135. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
  136. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
  137. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
  138. package/lib/widget/attachment/AttachmentUtils.ts +19 -11
  139. package/lib/widget/attachment/attachment.component.html +16 -6
  140. package/lib/widget/attachment/attachment.component.ts +14 -4
  141. package/lib/widget/case-history/case-history.component.ts +6 -2
  142. package/lib/widget/feed-container/feed-container.component.ts +1 -199
  143. package/lib/widget/file-utility/file-utility.component.ts +26 -30
  144. package/lib/widget/todo/todo.component.ts +7 -2
  145. package/package.json +2 -2
  146. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  147. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  148. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  149. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  150. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
  151. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  152. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  153. 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
+ }