@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.
- package/lib/designSystemExtension/alert/alert.component.html +4 -1
- package/lib/designSystemExtension/alert/alert.component.scss +9 -10
- package/lib/designSystemExtension/alert/alert.component.ts +6 -2
- package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
- package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +51 -36
- package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +12 -0
- package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +11 -0
- package/lib/designSystemExtension/operator/operator.component.html +5 -3
- package/lib/designSystemExtension/operator/operator.component.scss +7 -0
- package/lib/designSystemExtension/operator/operator.component.ts +2 -1
- package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +100 -10
- package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
- package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +1 -1
- package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +240 -72
- package/lib/field/auto-complete/auto-complete.component.html +46 -5
- package/lib/field/auto-complete/auto-complete.component.scss +92 -0
- package/lib/field/auto-complete/auto-complete.component.ts +350 -23
- package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
- package/lib/field/check-box/check-box.component.html +8 -6
- package/lib/field/check-box/check-box.component.ts +9 -2
- package/lib/field/currency/currency.component.html +2 -1
- package/lib/field/currency/currency.component.ts +10 -1
- package/lib/field/date/date.component.html +2 -1
- package/lib/field/date/date.component.ts +2 -0
- package/lib/field/date-time/date-time.component.html +2 -1
- package/lib/field/date-time/date-time.component.scss +46 -40
- package/lib/field/date-time/date-time.component.ts +29 -0
- package/lib/field/decimal/decimal.component.html +2 -1
- package/lib/field/decimal/decimal.component.ts +2 -0
- package/lib/field/dropdown/dropdown.component.html +4 -1
- package/lib/field/dropdown/dropdown.component.ts +18 -19
- package/lib/field/email/email.component.html +2 -1
- package/lib/field/email/email.component.ts +2 -2
- package/lib/field/embedded-data-multi/embedded-data-multi.component.html +3 -0
- package/lib/field/embedded-data-multi/embedded-data-multi.component.scss +0 -0
- package/lib/field/embedded-data-multi/embedded-data-multi.component.ts +173 -0
- package/lib/field/field.base.ts +27 -1
- package/lib/field/group/group.component.html +1 -1
- package/lib/field/group/group.component.ts +16 -7
- package/lib/field/integer/integer.component.html +2 -1
- package/lib/field/integer/integer.component.ts +4 -2
- package/lib/field/location/location.component.html +2 -1
- package/lib/field/location/location.component.ts +3 -1
- package/lib/field/multiselect/multiselect.component.html +1 -1
- package/lib/field/multiselect/multiselect.component.ts +23 -4
- package/lib/field/multiselect/utils.ts +5 -3
- package/lib/field/object-reference/object-reference.component.html +30 -15
- package/lib/field/object-reference/object-reference.component.ts +818 -94
- package/lib/field/percentage/percentage.component.html +2 -1
- package/lib/field/percentage/percentage.component.ts +10 -1
- package/lib/field/phone/phone.component.html +2 -1
- package/lib/field/phone/phone.component.ts +2 -1
- package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
- package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
- package/lib/field/rich-text/rich-text.component.scss +1 -166
- package/lib/field/rich-text/rich-text.component.spec.ts +1 -1
- package/lib/field/rich-text/rich-text.component.ts +1 -1
- package/lib/field/selectable-card/selectable-card.component.html +1 -0
- package/lib/field/selectable-card/selectable-card.component.ts +31 -16
- package/lib/field/semantic-link/semantic-link.component.html +1 -1
- package/lib/field/semantic-link/semantic-link.component.scss +4 -0
- package/lib/field/semantic-link/semantic-link.component.ts +13 -7
- package/lib/field/text-area/text-area.component.html +2 -1
- package/lib/field/text-area/text-area.component.ts +2 -1
- package/lib/field/text-input/text-input.component.html +2 -1
- package/lib/field/text-input/text-input.component.ts +2 -1
- package/lib/field/time/time.component.html +2 -1
- package/lib/field/time/time.component.ts +2 -1
- package/lib/field/url/url.component.html +2 -1
- package/lib/field/url/url.component.ts +2 -1
- package/lib/field/user-reference/user-reference.component.html +4 -2
- package/lib/field/user-reference/user-reference.component.ts +11 -6
- package/lib/infra/Containers/flow-container/flow-container.component.html +2 -2
- package/lib/infra/Containers/flow-container/flow-container.component.ts +34 -42
- package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
- package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
- package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
- package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
- package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -196
- package/lib/infra/Containers/view-container/view-container.component.html +1 -1
- package/lib/infra/Containers/view-container/view-container.component.ts +36 -43
- package/lib/infra/assignment/assignment.component.html +2 -1
- package/lib/infra/assignment/assignment.component.ts +44 -22
- package/lib/infra/assignment-card/assignment-card.component.html +1 -0
- package/lib/infra/assignment-card/assignment-card.component.ts +2 -8
- package/lib/infra/defer-load/defer-load.component.ts +7 -5
- package/lib/infra/multi-step/multi-step.component.html +25 -33
- package/lib/infra/multi-step/multi-step.component.scss +67 -125
- package/lib/infra/multi-step/multi-step.component.ts +11 -0
- package/lib/infra/navbar/navbar.component.ts +33 -36
- package/lib/infra/reference/reference.component.html +7 -1
- package/lib/infra/reference/reference.component.ts +63 -109
- package/lib/infra/region/region.component.ts +1 -3
- package/lib/infra/root-container/root-container.component.html +1 -0
- package/lib/infra/root-container/root-container.component.ts +34 -42
- package/lib/infra/view/view.component.ts +22 -46
- package/lib/template/advanced-search/advanced-search.component.ts +0 -1
- package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
- package/lib/template/app-shell/app-shell.component.ts +13 -13
- package/lib/template/banner-page/banner-page.component.ts +1 -2
- package/lib/template/base/details-template-base.ts +41 -14
- package/lib/template/base/form-template-base.ts +0 -2
- package/lib/template/case-summary/case-summary.component.ts +15 -7
- package/lib/template/case-view/case-view.component.html +14 -2
- package/lib/template/case-view/case-view.component.spec.ts +119 -10
- package/lib/template/case-view/case-view.component.ts +52 -3
- package/lib/template/data-reference/data-reference.component.ts +29 -22
- package/lib/template/data-reference/search-form/search-form.component.html +1 -1
- package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
- package/lib/template/data-reference/search-form/tabsData.ts +0 -1
- package/lib/template/default-form/default-form.component.html +20 -4
- package/lib/template/default-form/default-form.component.ts +14 -5
- package/lib/template/default-page/default-page.component.ts +1 -2
- package/lib/template/details/details.component.html +23 -3
- package/lib/template/details/details.component.scss +9 -2
- package/lib/template/details/details.component.ts +3 -70
- package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
- package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
- package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
- package/lib/template/details-one-column/details-one-column.component.html +23 -1
- package/lib/template/details-one-column/details-one-column.component.scss +14 -0
- package/lib/template/details-one-column/details-one-column.component.ts +3 -40
- package/lib/template/details-three-column/details-three-column.component.html +21 -2
- package/lib/template/details-three-column/details-three-column.component.scss +8 -1
- package/lib/template/details-three-column/details-three-column.component.ts +3 -48
- package/lib/template/details-two-column/details-two-column.component.html +21 -2
- package/lib/template/details-two-column/details-two-column.component.scss +8 -1
- package/lib/template/details-two-column/details-two-column.component.ts +3 -46
- package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
- package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
- package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
- package/lib/template/dynamic-tabs/dynamic-tabs.component.html +1 -0
- package/lib/template/field-group-template/field-group-template.component.ts +19 -6
- package/lib/template/field-value-list/field-value-list.component.scss +17 -0
- package/lib/template/inline-dashboard-page/inline-dashboard-page.component.ts +2 -10
- package/lib/template/list-view/list-view.component.ts +47 -11
- package/lib/template/one-column/one-column.component.html +3 -0
- package/lib/template/one-column-tab/one-column-tab.component.html +1 -0
- package/lib/template/page/page.component.html +1 -0
- package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
- package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
- package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
- package/lib/template/simple-table/simple-table.component.ts +11 -1
- package/lib/template/simple-table-manual/helpers.ts +84 -16
- package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
- package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
- package/lib/template/simple-table-manual/simple-table-manual.component.ts +142 -46
- package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
- package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
- package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
- package/lib/template/wide-narrow-page/wide-narrow-page.component.ts +0 -3
- package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
- package/lib/widget/attachment/AttachmentUtils.ts +19 -11
- package/lib/widget/attachment/attachment.component.html +16 -6
- package/lib/widget/attachment/attachment.component.ts +14 -4
- package/lib/widget/case-history/case-history.component.ts +6 -2
- package/lib/widget/feed-container/feed-container.component.ts +1 -199
- package/lib/widget/file-utility/file-utility.component.ts +26 -30
- package/lib/widget/todo/todo.component.ts +7 -2
- package/package.json +2 -2
- package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
- package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
- package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
- package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
- package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
- package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
- package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
- package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -37
|
@@ -1,18 +1,37 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
30
|
-
editorConfig: any = {};
|
|
48
|
+
private editorEl?: ElementRef<HTMLDivElement>;
|
|
31
49
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
268
|
+
const file = e.target.files?.[0];
|
|
269
|
+
if (!file) return;
|
|
270
|
+
const reader = new FileReader();
|
|
86
271
|
reader.addEventListener('load', () => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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"
|
|
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
|
-
<
|
|
20
|
-
<
|
|
21
|
-
|
|
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
|
+
}
|