nexheal-lib 0.0.2 → 0.0.3

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 (62) hide show
  1. package/.editorconfig +17 -0
  2. package/.vscode/extensions.json +4 -0
  3. package/.vscode/launch.json +20 -0
  4. package/.vscode/tasks.json +42 -0
  5. package/README.md +15 -19
  6. package/angular.json +36 -0
  7. package/package.json +47 -23
  8. package/projects/nexheal-lib/README.md +63 -0
  9. package/projects/nexheal-lib/ng-package.json +9 -0
  10. package/projects/nexheal-lib/package.json +12 -0
  11. package/projects/nexheal-lib/src/directives/clickoutside.directive.ts +34 -0
  12. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.html +52 -0
  13. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.scss +22 -0
  14. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.spec.ts +22 -0
  15. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.ts +367 -0
  16. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.html +152 -0
  17. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.scss +194 -0
  18. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.spec.ts +22 -0
  19. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.ts +759 -0
  20. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.html +4 -0
  21. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.spec.ts +22 -0
  22. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.ts +94 -0
  23. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.html +61 -0
  24. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.scss +132 -0
  25. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.spec.ts +22 -0
  26. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.ts +202 -0
  27. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.html +72 -0
  28. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.scss +90 -0
  29. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.spec.ts +22 -0
  30. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.ts +482 -0
  31. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.html +53 -0
  32. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.scss +19 -0
  33. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.spec.ts +22 -0
  34. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.ts +375 -0
  35. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.html +4 -0
  36. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.scss +53 -0
  37. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.spec.ts +22 -0
  38. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.ts +93 -0
  39. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.html +88 -0
  40. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.scss +122 -0
  41. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.spec.ts +22 -0
  42. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.ts +314 -0
  43. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.html +19 -0
  44. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.scss +15 -0
  45. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.spec.ts +22 -0
  46. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.ts +83 -0
  47. package/projects/nexheal-lib/src/public-api.ts +13 -0
  48. package/projects/nexheal-lib/src/styles/nexheal.scss +1 -0
  49. package/projects/nexheal-lib/tsconfig.lib.json +18 -0
  50. package/projects/nexheal-lib/tsconfig.lib.prod.json +11 -0
  51. package/projects/nexheal-lib/tsconfig.spec.json +14 -0
  52. package/tsconfig.json +39 -0
  53. package/fesm2022/nexheal-lib.mjs +0 -2837
  54. package/fesm2022/nexheal-lib.mjs.map +0 -1
  55. package/index.d.ts +0 -498
  56. package/src/styles/fonts/icomoon.eot +0 -0
  57. package/src/styles/fonts/icomoon.svg +0 -46
  58. package/src/styles/fonts/icomoon.ttf +0 -0
  59. package/src/styles/fonts/icomoon.woff +0 -0
  60. package/src/styles/icon.css +0 -133
  61. package/src/styles/nexheal.scss +0 -2
  62. /package/{src → projects/nexheal-lib/src}/styles/_formcontrols.scss +0 -0
@@ -0,0 +1,122 @@
1
+ .text-editor {
2
+ border: 1px solid #ccc;
3
+ .toolbar {
4
+ gap: 0.75rem;
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ background: #f8f8f8;
8
+ padding: 0.75rem 0.75rem;
9
+ border-bottom: 1px solid #cccccc;
10
+ .toolbar-items {
11
+ height: 18px;
12
+ display: flex;
13
+ flex-wrap: wrap;
14
+ align-items: center;
15
+ padding-right: 0.75rem;
16
+ border-right: 1px solid #969090;
17
+ button,
18
+ .input-wrap {
19
+ cursor: pointer;
20
+ min-width: 25px;
21
+ min-height: 20px;
22
+ &:hover {
23
+ i {
24
+ color: #000000;
25
+ }
26
+ }
27
+ }
28
+ button {
29
+ border: 0;
30
+ display: grid;
31
+ place-items: center;
32
+ background: transparent;
33
+ &:hover {
34
+ background: #eeeeee;
35
+ }
36
+ }
37
+ .input-wrap {
38
+ gap: 2px;
39
+ display: flex;
40
+ cursor: pointer;
41
+ align-items: center;
42
+ flex-direction: column;
43
+ justify-content: center;
44
+ input[type="color"] {
45
+ width: 80%;
46
+ padding: 0;
47
+ height: 2px;
48
+ border: none;
49
+ cursor: inherit;
50
+ }
51
+ }
52
+ i {
53
+ font-size: 13px;
54
+ color: #3c4148;
55
+ &.he-bold {
56
+ font-weight: 600;
57
+ }
58
+ &.he-underline {
59
+ font-size: 14px;
60
+ }
61
+ &.he-text-drop {
62
+ font-size: 12px;
63
+ }
64
+ &.he-background-drop {
65
+ top: -1px;
66
+ font-size: 12px;
67
+ }
68
+ &.he-heading-1,
69
+ &.he-link,
70
+ &.he-left-align,
71
+ &.he-center-align,
72
+ &.he-right-align,
73
+ &.he-justify {
74
+ font-size: 16px;
75
+ }
76
+ &.he-text-indent-left,
77
+ &.he-text-indent-right {
78
+ font-size: 17px;
79
+ }
80
+ &.he-heading-2 {
81
+ top: 1px;
82
+ font-size: 16px;
83
+ }
84
+ &.he-unordered-list,
85
+ &.he-ordered-list {
86
+ top: -1px;
87
+ font-size: 20px;
88
+ }
89
+ &.he-image {
90
+ font-size: 15px;
91
+ font-weight: 600;
92
+ }
93
+ &.he-redo,
94
+ &.he-undo {
95
+ top: -1px;
96
+ font-size: 14px;
97
+ }
98
+ }
99
+ &.more-gap {
100
+ gap: 5px;
101
+ }
102
+ &:last-child {
103
+ border-right: 0;
104
+ }
105
+ }
106
+ }
107
+ .editor-content {
108
+ outline: none;
109
+ min-height: 200px;
110
+ position: relative;
111
+ background: #ffffff;
112
+ padding: 0.75rem 0.75rem;
113
+ &[data-placeholder]:empty:before {
114
+ content: attr(data-placeholder);
115
+ color: #aaa;
116
+ pointer-events: none;
117
+ position: absolute;
118
+ left: 0.75rem;
119
+ top: 0.75rem;
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,22 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { TextEditor } from './text-editor.component';
3
+
4
+ describe('TextEditor', () => {
5
+ let component: TextEditor;
6
+ let fixture: ComponentFixture<TextEditor>;
7
+
8
+ beforeEach(async () => {
9
+ await TestBed.configureTestingModule({
10
+ imports: [TextEditor]
11
+ })
12
+ .compileComponents();
13
+
14
+ fixture = TestBed.createComponent(TextEditor);
15
+ component = fixture.componentInstance;
16
+ fixture.detectChanges();
17
+ });
18
+
19
+ it('should create', () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+ });
@@ -0,0 +1,314 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, OnInit, Input, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
3
+ import { ControlValueAccessor } from '@angular/forms';
4
+
5
+ interface HistoryState {
6
+ html: string;
7
+ selection?: SelectionRange;
8
+ }
9
+
10
+ // To restore selection, we need to store something about it. This is complex.
11
+ // For simplicity, we only store HTML. True undo/redo of selection positions is non-trivial.
12
+ interface SelectionRange {
13
+ startContainerPath: number[];
14
+ startOffset: number;
15
+ endContainerPath: number[];
16
+ endOffset: number;
17
+ }
18
+
19
+ @Component({
20
+ selector: 'app-text-editor',
21
+ imports:
22
+ [CommonModule,
23
+
24
+ ],
25
+ templateUrl: './text-editor.component.html',
26
+ styleUrl: './text-editor.component.scss'
27
+ })
28
+ export class TextEditor implements ControlValueAccessor, OnInit, AfterViewInit {
29
+
30
+ @Input() header: boolean = true;
31
+ @Input() media: boolean = true;
32
+ @Input() link: boolean = true;
33
+ @Input() placeholder: string = 'Type here...';
34
+
35
+ @ViewChild('editor') editorRef!: ElementRef<HTMLDivElement>;
36
+
37
+ private onChange: (value: string) => void = () => { };
38
+ private onTouch: () => void = () => { };
39
+ private undoStack: HistoryState[] = [];
40
+ private redoStack: HistoryState[] = [];
41
+
42
+ ngOnInit() { }
43
+
44
+ ngAfterViewInit() {
45
+ this.saveState();
46
+ }
47
+
48
+ writeValue(value: string): void {
49
+ this.editorRef.nativeElement.innerHTML = value || '';
50
+ this.saveState();
51
+ }
52
+
53
+ registerOnChange(fn: any): void {
54
+ this.onChange = fn;
55
+ }
56
+ registerOnTouched(fn: any): void {
57
+ this.onTouch = fn;
58
+ }
59
+
60
+ onInput() {
61
+ const html = this.editorRef.nativeElement.innerHTML;
62
+ this.onChange(html);
63
+ this.saveState();
64
+ }
65
+
66
+ // events
67
+ onTouched() {
68
+ this.onTouch();
69
+ }
70
+ setDisabledState(isDisabled: boolean): void {
71
+ this.editorRef.nativeElement.contentEditable = (!isDisabled).toString();
72
+ }
73
+
74
+ // ---- Selection & Range Utilities ----
75
+ private getSelectionRange(): Range | null {
76
+ const selection = window.getSelection();
77
+ if (selection && selection.rangeCount > 0) {
78
+ return selection.getRangeAt(0);
79
+ }
80
+ return null;
81
+ }
82
+
83
+ private wrapSelection(htmlTag: string, style?: string) {
84
+ const range = this.getSelectionRange();
85
+ if (!range || range.collapsed) return; // no selection
86
+ const selectedText = range.extractContents();
87
+
88
+ const el = document.createElement(htmlTag);
89
+ if (style) {
90
+ el.setAttribute('style', style);
91
+ }
92
+ el.appendChild(selectedText);
93
+ range.insertNode(el);
94
+ this.mergeAdjacentSimilarElements(el);
95
+ this.onInput();
96
+ }
97
+
98
+ // Merges adjacent spans with the same style to avoid clutter
99
+ private mergeAdjacentSimilarElements(el: HTMLElement) {
100
+ const parent = el.parentElement;
101
+ if (!parent) return;
102
+
103
+ const siblings = Array.from(parent.childNodes);
104
+ for (let i = 0; i < siblings.length - 1; i++) {
105
+ const current = siblings[i] as HTMLElement;
106
+ const next = siblings[i + 1] as HTMLElement;
107
+ if (current.nodeType === 1 && next?.nodeType === 1) {
108
+ if (current.tagName === next.tagName && current.getAttribute('style') === next.getAttribute('style')) {
109
+ // merge them
110
+ while (next.firstChild) {
111
+ current.appendChild(next.firstChild);
112
+ }
113
+ next.remove();
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // ---- Inline Formatting ----
120
+ // applyInlineStyle() toggles inline styles by wrapping selection in a styled span
121
+ applyInlineStyle(styleType: 'bold' | 'italic' | 'underline') {
122
+ let style = '';
123
+ if (styleType === 'bold') style = 'font-weight:bold;';
124
+ else if (styleType === 'italic') style = 'font-style:italic;';
125
+ else if (styleType === 'underline') style = 'text-decoration:underline;';
126
+
127
+ this.wrapSelection('span', style);
128
+ }
129
+
130
+ applyColor(event: Event) {
131
+ const input = event.target as HTMLInputElement;
132
+ const color = input.value;
133
+ // Now `color` is a string and `input` is never null
134
+ this.wrapSelection('span', `color:${color};`);
135
+ }
136
+
137
+ applyHighlight(event: Event) {
138
+ const input = event.target as HTMLInputElement;
139
+ const color = input.value;
140
+ this.wrapSelection('span', `background-color:${color};`);
141
+ }
142
+
143
+ // ---- Block Formatting (Headings, Paragraph) ----
144
+ // Replaces the parent block element containing selection with a new block type
145
+ applyBlockFormat(blockTag: 'h1' | 'h2' | 'p') {
146
+ const range = this.getSelectionRange();
147
+ if (!range) return;
148
+
149
+ // Find the nearest block element
150
+ let block = this.findBlockAncestor(range.commonAncestorContainer);
151
+ if (!block) {
152
+ // If no block found, wrap the selection in a block
153
+ const wrapper = document.createElement(blockTag);
154
+ wrapper.appendChild(range.extractContents());
155
+ range.insertNode(wrapper);
156
+ } else {
157
+ // Replace the block with a new block type
158
+ const newBlock = document.createElement(blockTag);
159
+ // Move children
160
+ while (block.firstChild) {
161
+ newBlock.appendChild(block.firstChild);
162
+ }
163
+ block.replaceWith(newBlock);
164
+ }
165
+ this.onInput();
166
+ }
167
+
168
+ private findBlockAncestor(node: Node): HTMLElement | null {
169
+ while (node && node !== this.editorRef.nativeElement) {
170
+ if (this.isBlockElement(node)) {
171
+ return node as HTMLElement;
172
+ }
173
+ node = node.parentNode!;
174
+ }
175
+ return null;
176
+ }
177
+
178
+ private isBlockElement(node: Node): boolean {
179
+ if (node.nodeType !== 1) return false;
180
+ const display = window.getComputedStyle(node as HTMLElement).display;
181
+ return display === 'block' || display === 'list-item';
182
+ }
183
+
184
+ // ---- Lists ----
185
+ // Convert selected lines into a list
186
+ applyList(listType: 'ul' | 'ol') {
187
+ const range = this.getSelectionRange();
188
+ if (!range) return;
189
+ const commonBlock = this.findBlockAncestor(range.commonAncestorContainer);
190
+
191
+ // Get the text in the selection
192
+ const content = range.extractContents();
193
+ const lines = this.splitContentByLine(content);
194
+
195
+ const listEl = document.createElement(listType);
196
+ for (const line of lines) {
197
+ const li = document.createElement('li');
198
+ li.appendChild(line);
199
+ listEl.appendChild(li);
200
+ }
201
+
202
+ if (commonBlock) {
203
+ // Insert the list right where the block was or at the selection
204
+ range.insertNode(listEl);
205
+ } else {
206
+ // If no common block, just insert at current position
207
+ range.insertNode(listEl);
208
+ }
209
+ this.onInput();
210
+ }
211
+
212
+ private splitContentByLine(fragment: DocumentFragment): DocumentFragment[] {
213
+ // A very naive approach: split by <br> or block elements.
214
+ // For advanced logic, detect line breaks more thoroughly.
215
+ const lines: DocumentFragment[] = [];
216
+ let currentFrag = document.createDocumentFragment();
217
+
218
+ Array.from(fragment.childNodes).forEach((node) => {
219
+ if (node.nodeName === 'BR' || this.isBlockElement(node)) {
220
+ // This node signifies a new line
221
+ lines.push(currentFrag);
222
+ currentFrag = document.createDocumentFragment();
223
+ if (this.isBlockElement(node)) {
224
+ while (node.firstChild) {
225
+ currentFrag.appendChild(node.firstChild);
226
+ }
227
+ lines.push(currentFrag);
228
+ currentFrag = document.createDocumentFragment();
229
+ }
230
+ } else {
231
+ currentFrag.appendChild(node);
232
+ }
233
+ });
234
+
235
+ if (currentFrag.childNodes.length > 0) {
236
+ lines.push(currentFrag);
237
+ }
238
+ return lines;
239
+ }
240
+
241
+ // ---- Links ----
242
+ createLink() {
243
+ const url = prompt('Enter URL', 'https://');
244
+ if (!url) return;
245
+ const range = this.getSelectionRange();
246
+ if (!range || range.collapsed) return;
247
+ const selectedContent = range.extractContents();
248
+ const a = document.createElement('a');
249
+ a.href = url;
250
+ a.appendChild(selectedContent);
251
+ range.insertNode(a);
252
+ this.onInput();
253
+ }
254
+
255
+ // ---- Images ----
256
+ insertImage() {
257
+ const url = prompt('Enter image URL:');
258
+ if (!url) return;
259
+ const range = this.getSelectionRange();
260
+ if (!range) return;
261
+ const img = document.createElement('img');
262
+ img.src = url;
263
+ range.insertNode(img);
264
+ this.onInput();
265
+ }
266
+
267
+ // ---- Alignment ----
268
+ setAlignment(alignment: 'left' | 'center' | 'right' | 'justify') {
269
+ // Find the block and set text-align
270
+ const range = this.getSelectionRange();
271
+ if (!range) return;
272
+ const block = this.findBlockAncestor(range.commonAncestorContainer);
273
+ if (block) {
274
+ block.style.textAlign = alignment;
275
+ this.onInput();
276
+ }
277
+ }
278
+
279
+ // ---- Clear Formatting ----
280
+ clearFormatting() {
281
+ const html = this.editorRef.nativeElement.innerText;
282
+ // Convert innerText to a plain <p> block for simplicity
283
+ this.editorRef.nativeElement.innerHTML = `<p>${html}</p>`;
284
+ this.onInput();
285
+ }
286
+
287
+ // ---- Undo/Redo ----
288
+ saveState() {
289
+ const currentHtml = this.editorRef.nativeElement.innerHTML;
290
+ if (this.undoStack.length === 0 || this.undoStack[this.undoStack.length - 1].html !== currentHtml) {
291
+ this.undoStack.push({ html: currentHtml });
292
+ this.redoStack = []; // clear redo on new input
293
+ }
294
+ }
295
+
296
+ // clear options
297
+ undo() {
298
+ if (this.undoStack.length > 1) {
299
+ const current = this.undoStack.pop()!;
300
+ this.redoStack.push(current);
301
+ const previous = this.undoStack[this.undoStack.length - 1];
302
+ this.editorRef.nativeElement.innerHTML = previous.html;
303
+ this.onChange(previous.html);
304
+ }
305
+ }
306
+ redo() {
307
+ if (this.redoStack.length > 0) {
308
+ const state = this.redoStack.pop()!;
309
+ this.undoStack.push(state);
310
+ this.editorRef.nativeElement.innerHTML = state.html;
311
+ this.onChange(state.html);
312
+ }
313
+ }
314
+ }
@@ -0,0 +1,19 @@
1
+ <div class="form-group textarea" [ngClass]="customClass">
2
+ @if (title) {
3
+ <label class="inp-label" [ngClass]="{'required' : required}">{{ title }}</label>
4
+ }
5
+ <textarea class="form-control" [placeholder]="placeholder" [formControl]="textareaControl"
6
+ [ngClass]="{ 'is-invalid': error }" (blur)="onBlur()"></textarea>
7
+
8
+ <span class="focus-border"></span>
9
+
10
+ @if (textareaControl.value && clearVal) {
11
+ <label class="clear" (click)="resetTextarea()">
12
+ <i class="he he-close"></i>
13
+ </label>
14
+ }
15
+
16
+ @if (error) {
17
+ <div class="val-msg">{{ errorMessage }}</div>
18
+ }
19
+ </div>
@@ -0,0 +1,15 @@
1
+ .form-group {
2
+ textarea {
3
+ resize: none;
4
+ min-height: 100px;
5
+ &.resize {
6
+ resize: auto;
7
+ }
8
+ &::-webkit-scrollbar-thumb {
9
+ border-radius: 10px;
10
+ }
11
+ &::-webkit-scrollbar-track {
12
+ background: #e8eaf1;
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,22 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { TextareaControl } from './textarea-control.component';
3
+
4
+ describe('TextareaControl', () => {
5
+ let component: TextareaControl;
6
+ let fixture: ComponentFixture<TextareaControl>;
7
+
8
+ beforeEach(async () => {
9
+ await TestBed.configureTestingModule({
10
+ imports: [TextareaControl]
11
+ })
12
+ .compileComponents();
13
+
14
+ fixture = TestBed.createComponent(TextareaControl);
15
+ component = fixture.componentInstance;
16
+ fixture.detectChanges();
17
+ });
18
+
19
+ it('should create', () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+ });
@@ -0,0 +1,83 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR, ControlValueAccessor, ReactiveFormsModule, FormControl } from '@angular/forms';
4
+
5
+ @Component({
6
+ selector: 'textarea-control',
7
+ standalone: true,
8
+ imports: [
9
+ CommonModule,
10
+ ReactiveFormsModule
11
+ ],
12
+ providers: [
13
+ {
14
+ provide: NG_VALUE_ACCESSOR,
15
+ useExisting: forwardRef(() => TextareaControl),
16
+ multi: true,
17
+ },
18
+ ],
19
+ templateUrl: './textarea-control.component.html',
20
+ styleUrl: './textarea-control.component.scss'
21
+ })
22
+ export class TextareaControl {
23
+ @Input() title!: string;
24
+ @Input() placeholder: string = '';
25
+ @Input() customClass!: string;
26
+ @Input() clearVal: boolean = true;
27
+ @Input() disabled: boolean = false;
28
+ @Input() required: boolean = false;
29
+ @Input() error: boolean = false; // New input for error state
30
+ @Input() errorMessage: string = ''; // New input for error message
31
+ @Output() textareaChange = new EventEmitter<string>();
32
+ @Output() selectionCleared = new EventEmitter<void>();
33
+ @Output() blurEvent = new EventEmitter<void>();
34
+
35
+ private onChange: any = () => { };
36
+ private onTouched: any = () => { };
37
+
38
+ textareaControl = new FormControl('');
39
+
40
+ constructor() {
41
+ this.textareaControl.valueChanges.subscribe(value => {
42
+ this.onChange(value);
43
+ this.onTouched();
44
+ this.textareaChange.emit(value ?? '');
45
+ });
46
+ }
47
+
48
+ writeValue(value: any): void {
49
+ if (value !== undefined) {
50
+ this.textareaControl.setValue(value, { emitEvent: false });
51
+ }
52
+ if (this.textareaControl.disabled !== this.disabled) {
53
+ this.setDisabledState(this.disabled);
54
+ }
55
+ }
56
+
57
+ registerOnChange(fn: any): void {
58
+ this.onChange = fn;
59
+ }
60
+ registerOnTouched(fn: any): void {
61
+ this.onTouched = fn;
62
+ }
63
+
64
+ // events
65
+ onBlur() {
66
+ this.blurEvent.emit();
67
+ this.onTouched();
68
+ }
69
+ setDisabledState(isDisabled: boolean): void {
70
+ if (this.disabled) {
71
+ this.textareaControl.disable({ emitEvent: false });
72
+ } else {
73
+ this.textareaControl.enable({ emitEvent: false });
74
+ }
75
+ }
76
+
77
+ // clear
78
+ resetTextarea() {
79
+ this.textareaControl.setValue('');
80
+ this.selectionCleared.emit();
81
+ }
82
+
83
+ }
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Public API Surface of heal-lib
3
+ */
4
+
5
+ export { AutocompleteControl } from './lib/controls/autocomplete-control/autocomplete-control.component';
6
+ export { CalendarControl } from './lib/controls/calendar-control/calendar-control.component';
7
+ export { CheckboxControl } from './lib/controls/checkbox-control/checkbox-control.component';
8
+ export { InputControl } from './lib/controls/input-control/input-control.component';
9
+ export { MultiselectControl } from './lib/controls/multiselect-control/multiselect-control.component';
10
+ export { SelectControl } from './lib/controls/select-control/select-control.component';
11
+ export { SwitchControl } from './lib/controls/switch-control/switch-control.component';
12
+ export { TextEditor } from './lib/controls/text-editor/text-editor.component';
13
+ export { TextareaControl } from './lib/controls/textarea-control/textarea-control.component';
@@ -0,0 +1 @@
1
+ @use "formcontrols";
@@ -0,0 +1,18 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "extends": "../../tsconfig.json",
5
+ "compilerOptions": {
6
+ "outDir": "../../out-tsc/lib",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "inlineSources": true,
10
+ "types": []
11
+ },
12
+ "include": [
13
+ "src/**/*.ts"
14
+ ],
15
+ "exclude": [
16
+ "**/*.spec.ts"
17
+ ]
18
+ }
@@ -0,0 +1,11 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "extends": "./tsconfig.lib.json",
5
+ "compilerOptions": {
6
+ "declarationMap": false
7
+ },
8
+ "angularCompilerOptions": {
9
+ "compilationMode": "partial"
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2
+ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3
+ {
4
+ "extends": "../../tsconfig.json",
5
+ "compilerOptions": {
6
+ "outDir": "../../out-tsc/spec",
7
+ "types": [
8
+ "jasmine"
9
+ ]
10
+ },
11
+ "include": [
12
+ "src/**/*.ts"
13
+ ]
14
+ }