@mindfiredigital/textigniter-angular 1.1.1 → 1.2.0

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.
@@ -0,0 +1,26 @@
1
+
2
+ > @mindfiredigital/textigniter-angular@1.2.0 build /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular
3
+ > ng build text-igniter
4
+
5
+ Building Angular Package
6
+
7
+ ------------------------------------------------------------------------------
8
+ Building entry point 'text-igniter'
9
+ ------------------------------------------------------------------------------
10
+ - Compiling with Angular sources in Ivy partial compilation mode.
11
+ ✔ Compiling with Angular sources in Ivy partial compilation mode.
12
+ ✔ Generating FESM bundles
13
+ - Copying assets
14
+ ✔ Copying assets
15
+ - Writing package manifest
16
+ ✔ Writing package manifest
17
+ ✔ Built text-igniter
18
+
19
+ ------------------------------------------------------------------------------
20
+ Built Angular Package
21
+ - from: /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular/projects/text-igniter
22
+ - to: /home/runner/work/TextIgniterJS/TextIgniterJS/packages/angular/dist/text-igniter
23
+ ------------------------------------------------------------------------------
24
+
25
+ Build at: 2026-02-25T11:00:57.492Z - Time: 2425ms
26
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @mindfiredigital/textigniter-angular
2
2
 
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Merge pull request #186 from mindfiredigital/dev
8
+
3
9
  ## 1.1.1
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -1,42 +1,407 @@
1
- # Angular
1
+ # @mindfiredigital/textigniter-angular
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.7.
3
+ Angular wrapper component for TextIgniter - A powerful rich text editor for Angular applications.
4
4
 
5
- ## Development server
5
+ [![npm version](https://badge.fury.io/js/@mindfiredigital%2Ftextigniter-angular.svg)](https://www.npmjs.com/package/@mindfiredigital/textigniter-angular)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
- To start a local development server, run:
8
+ ## 📦 Installation
8
9
 
9
10
  ```bash
10
- ng serve
11
+ npm install @mindfiredigital/textigniter-angular
11
12
  ```
12
13
 
13
- Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
14
+ Or using yarn:
14
15
 
15
- ## Code scaffolding
16
+ ```bash
17
+ yarn add @mindfiredigital/textigniter-angular
18
+ ```
16
19
 
17
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
20
+ Or using pnpm:
18
21
 
19
22
  ```bash
20
- ng generate component component-name
23
+ pnpm add @mindfiredigital/textigniter-angular
21
24
  ```
22
25
 
23
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
26
+ ## 🚀 Quick Start
24
27
 
25
- ```bash
26
- ng generate --help
28
+ ### 1. Import the Module
29
+
30
+ ```typescript
31
+ // app.component.ts
32
+ import { Component } from '@angular/core';
33
+ import { RouterOutlet } from '@angular/router';
34
+ import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
35
+
36
+ @Component({
37
+ selector: 'app-root',
38
+ imports: [RouterOutlet, TextIgniterModule],
39
+ templateUrl: './app.component.html',
40
+ styleUrl: './app.component.css',
41
+ })
42
+ export class AppComponent {
43
+ title = 'demo-app';
44
+
45
+ config = {
46
+ showToolbar: true,
47
+ features: [
48
+ 'bold',
49
+ 'italic',
50
+ 'underline',
51
+ 'hyperlink',
52
+ 'fontFamily',
53
+ 'fontSize',
54
+ 'alignLeft',
55
+ 'alignCenter',
56
+ 'alignRight',
57
+ 'unorderedList',
58
+ 'orderedList',
59
+ 'image',
60
+ 'fontColor',
61
+ 'bgColor',
62
+ 'getHtmlContent',
63
+ 'loadHtmlContent',
64
+ ],
65
+ };
66
+ }
67
+ ```
68
+
69
+ ### 2. Use in Template
70
+
71
+ ```html
72
+ <!-- app.component.html -->
73
+ <ngx-text-igniter [config]="config"></ngx-text-igniter>
74
+ ```
75
+
76
+ ## 🎯 Features
77
+
78
+ - ✅ Rich text editing with toolbar
79
+ - ✅ Bold, Italic, Underline, Strikethrough
80
+ - ✅ Font family and size customization
81
+ - ✅ Text and background color
82
+ - ✅ Text alignment (left, center, right)
83
+ - ✅ Lists (ordered and unordered)
84
+ - ✅ Hyperlinks
85
+ - ✅ Image insertion
86
+ - ✅ Undo/Redo functionality
87
+ - ✅ **Real-time content change events** 🆕
88
+ - ✅ TypeScript support
89
+ - ✅ Fully customizable
90
+
91
+ ## 📚 API Reference
92
+
93
+ ### Component Inputs
94
+
95
+ #### `config` (Required)
96
+
97
+ Configuration object for the editor.
98
+
99
+ ```typescript
100
+ interface EditorConfig {
101
+ showToolbar: boolean;
102
+ features: string[];
103
+ }
104
+ ```
105
+
106
+ **Available Features:**
107
+
108
+ - `'bold'` - Bold text
109
+ - `'italic'` - Italic text
110
+ - `'underline'` - Underline text
111
+ - `'strikethrough'` - Strikethrough text
112
+ - `'hyperlink'` - Add hyperlinks
113
+ - `'fontFamily'` - Change font family
114
+ - `'fontSize'` - Change font size
115
+ - `'fontColor'` - Change text color
116
+ - `'bgColor'` - Change background color
117
+ - `'alignLeft'` - Align text left
118
+ - `'alignCenter'` - Align text center
119
+ - `'alignRight'` - Align text right
120
+ - `'unorderedList'` - Create bullet lists
121
+ - `'orderedList'` - Create numbered lists
122
+ - `'image'` - Insert images
123
+ - `'getHtmlContent'` - Get HTML content button
124
+ - `'loadHtmlContent'` - Load HTML content button
125
+
126
+ #### `editorId` (Optional)
127
+
128
+ Custom ID for the editor element. Default: `'editor-ngId'`
129
+
130
+ ```typescript
131
+ <ngx-text-igniter
132
+ [config]="config"
133
+ [editorId]="'my-custom-editor'">
134
+ </ngx-text-igniter>
135
+ ```
136
+
137
+ ### Component Outputs
138
+
139
+ #### `contentChange` 🆕
140
+
141
+ Emits whenever the editor content changes, providing both HTML and plain text.
142
+
143
+ ```typescript
144
+ interface ContentChangeEvent {
145
+ html: string; // HTML content
146
+ text: string; // Plain text content (without HTML tags)
147
+ }
148
+ ```
149
+
150
+ ## 💡 Usage Examples
151
+
152
+ ### Basic Example
153
+
154
+ ```typescript
155
+ // app.component.ts
156
+ import { Component } from '@angular/core';
157
+ import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
158
+
159
+ @Component({
160
+ selector: 'app-root',
161
+ imports: [TextIgniterModule],
162
+ template: `<ngx-text-igniter [config]="config"></ngx-text-igniter>`,
163
+ })
164
+ export class AppComponent {
165
+ config = {
166
+ showToolbar: true,
167
+ features: ['bold', 'italic', 'underline'],
168
+ };
169
+ }
170
+ ```
171
+
172
+ ### Real-time Content Updates 🆕
173
+
174
+ Get notified whenever the content changes:
175
+
176
+ ```typescript
177
+ // app.component.ts
178
+ import { Component } from '@angular/core';
179
+ import { TextIgniterModule } from '@mindfiredigital/textigniter-angular';
180
+
181
+ @Component({
182
+ selector: 'app-root',
183
+ imports: [TextIgniterModule],
184
+ templateUrl: './app.component.html',
185
+ })
186
+ export class AppComponent {
187
+ htmlContent = '';
188
+ textContent = '';
189
+
190
+ config = {
191
+ showToolbar: true,
192
+ features: ['bold', 'italic', 'underline', 'fontColor', 'bgColor'],
193
+ };
194
+
195
+ onContentChange(data: { html: string; text: string }) {
196
+ console.log('Content changed:', data);
197
+ this.htmlContent = data.html;
198
+ this.textContent = data.text;
199
+ }
200
+
201
+ get characterCount(): number {
202
+ return this.textContent.length;
203
+ }
204
+
205
+ get wordCount(): number {
206
+ return this.textContent.trim()
207
+ ? this.textContent.trim().split(/\s+/).length
208
+ : 0;
209
+ }
210
+ }
27
211
  ```
28
212
 
29
- ## Building
213
+ ```html
214
+ <!-- app.component.html -->
215
+ <h1>TextIgniter Angular Example</h1>
216
+
217
+ <ngx-text-igniter [config]="config" (contentChange)="onContentChange($event)">
218
+ </ngx-text-igniter>
219
+
220
+ <!-- Real-time Content Preview -->
221
+ <div class="preview-container">
222
+ <h3>Real-time Content Preview:</h3>
223
+
224
+ <div class="content-section">
225
+ <strong>HTML Content:</strong>
226
+ <pre>{{ htmlContent || 'Start typing to see content...' }}</pre>
227
+ </div>
228
+
229
+ <div class="content-section">
230
+ <strong>Text Content:</strong>
231
+ <pre>{{ textContent || 'Start typing to see text...' }}</pre>
232
+ </div>
233
+
234
+ <div class="stats">
235
+ <strong>Stats:</strong>
236
+ <p>Characters: {{ characterCount }} | Words: {{ wordCount }}</p>
237
+ </div>
238
+ </div>
239
+ ```
30
240
 
31
- To build the project run:
241
+ ### Auto-Save Functionality
242
+
243
+ Implement auto-save with debouncing:
244
+
245
+ ```typescript
246
+ import { Component, OnDestroy } from '@angular/core';
247
+ import { Subject } from 'rxjs';
248
+ import { debounceTime } from 'rxjs/operators';
249
+
250
+ @Component({
251
+ selector: 'app-editor',
252
+ template: `
253
+ <ngx-text-igniter
254
+ [config]="config"
255
+ (contentChange)="onContentChange($event)"
256
+ >
257
+ </ngx-text-igniter>
258
+ <p *ngIf="lastSaved">Last saved: {{ lastSaved | date: 'medium' }}</p>
259
+ `,
260
+ })
261
+ export class EditorComponent implements OnDestroy {
262
+ config = { showToolbar: true, features: ['bold', 'italic'] };
263
+ lastSaved: Date | null = null;
264
+
265
+ private contentChange$ = new Subject<{ html: string; text: string }>();
266
+
267
+ constructor() {
268
+ // Auto-save after 2 seconds of inactivity
269
+ this.contentChange$
270
+ .pipe(debounceTime(2000))
271
+ .subscribe(data => this.saveContent(data));
272
+ }
273
+
274
+ onContentChange(data: { html: string; text: string }) {
275
+ this.contentChange$.next(data);
276
+ }
277
+
278
+ saveContent(data: { html: string; text: string }) {
279
+ // Save to backend
280
+ console.log('Saving content...', data);
281
+ this.lastSaved = new Date();
282
+
283
+ // Example: Call your API
284
+ // this.http.post('/api/save', data).subscribe();
285
+ }
286
+
287
+ ngOnDestroy() {
288
+ this.contentChange$.complete();
289
+ }
290
+ }
291
+ ```
292
+
293
+ ### Form Integration
294
+
295
+ Use with Angular Reactive Forms:
296
+
297
+ ```typescript
298
+ import { Component } from '@angular/core';
299
+ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
300
+
301
+ @Component({
302
+ selector: 'app-form',
303
+ template: `
304
+ <form [formGroup]="form" (ngSubmit)="onSubmit()">
305
+ <ngx-text-igniter
306
+ [config]="config"
307
+ (contentChange)="onContentChange($event)"
308
+ >
309
+ </ngx-text-igniter>
310
+
311
+ <button type="submit" [disabled]="!form.valid">Submit</button>
312
+ </form>
313
+ `,
314
+ })
315
+ export class FormComponent {
316
+ form: FormGroup;
317
+
318
+ config = {
319
+ showToolbar: true,
320
+ features: ['bold', 'italic', 'underline'],
321
+ };
322
+
323
+ constructor(private fb: FormBuilder) {
324
+ this.form = this.fb.group({
325
+ content: ['', Validators.required],
326
+ });
327
+ }
328
+
329
+ onContentChange(data: { html: string; text: string }) {
330
+ this.form.patchValue({ content: data.html });
331
+ }
332
+
333
+ onSubmit() {
334
+ if (this.form.valid) {
335
+ console.log('Form data:', this.form.value);
336
+ // Submit to backend
337
+ }
338
+ }
339
+ }
340
+ ```
341
+
342
+ ### Custom Editor ID
343
+
344
+ Use a custom ID for multiple editors:
345
+
346
+ ```typescript
347
+ @Component({
348
+ template: `
349
+ <ngx-text-igniter
350
+ [config]="config"
351
+ [editorId]="'editor-1'"
352
+ (contentChange)="onEditor1Change($event)"
353
+ >
354
+ </ngx-text-igniter>
355
+
356
+ <ngx-text-igniter
357
+ [config]="config"
358
+ [editorId]="'editor-2'"
359
+ (contentChange)="onEditor2Change($event)"
360
+ >
361
+ </ngx-text-igniter>
362
+ `,
363
+ })
364
+ export class MultiEditorComponent {
365
+ config = { showToolbar: true, features: ['bold', 'italic'] };
366
+
367
+ onEditor1Change(data: { html: string; text: string }) {
368
+ console.log('Editor 1:', data);
369
+ }
370
+
371
+ onEditor2Change(data: { html: string; text: string }) {
372
+ console.log('Editor 2:', data);
373
+ }
374
+ }
375
+ ```
376
+
377
+ ## 🔧 Development
378
+
379
+ ### Building the Library
380
+
381
+ To build the library, run:
32
382
 
33
383
  ```bash
34
- ng build
384
+ ng build text-igniter
35
385
  ```
36
386
 
37
- This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
387
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
38
388
 
39
- ## Running unit tests
389
+ ### Publishing the Library
390
+
391
+ Once the project is built, you can publish your library by following these steps:
392
+
393
+ 1. Navigate to the `dist` directory:
394
+
395
+ ```bash
396
+ cd dist/text-igniter
397
+ ```
398
+
399
+ 2. Run the `npm publish` command to publish your library to the npm registry:
400
+ ```bash
401
+ npm publish --access public
402
+ ```
403
+
404
+ ### Running Unit Tests
40
405
 
41
406
  To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
42
407
 
@@ -44,16 +409,51 @@ To execute unit tests with the [Karma](https://karma-runner.github.io) test runn
44
409
  ng test
45
410
  ```
46
411
 
47
- ## Running end-to-end tests
412
+ ### Running the Demo App
48
413
 
49
- For end-to-end (e2e) testing, run:
414
+ To run the demo application:
50
415
 
51
416
  ```bash
52
- ng e2e
417
+ ng serve demo-app
53
418
  ```
54
419
 
55
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
420
+ Navigate to `http://localhost:4200/` to see the editor in action.
421
+
422
+ ## 📋 Requirements
423
+
424
+ - Angular >= 19.0.0
425
+ - TypeScript >= 5.7.0
426
+ - Node.js >= 12.0.0
427
+
428
+ ## 📝 License
429
+
430
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
431
+
432
+ ## 🔗 Links
433
+
434
+ - [NPM Package](https://www.npmjs.com/package/@mindfiredigital/textigniter-angular)
435
+ - [GitHub Repository](https://github.com/mindfiredigital/textigniterjs)
436
+ - [Documentation](https://github.com/mindfiredigital/textigniterjs/tree/main/docs)
437
+ - [Core Package](https://www.npmjs.com/package/@mindfiredigital/textigniter)
438
+ - [React Package](https://www.npmjs.com/package/@mindfiredigital/textigniter-react)
439
+ - [Web Component](https://www.npmjs.com/package/@mindfiredigital/textigniter-web-component)
440
+
441
+ ## 📞 Support
442
+
443
+ For issues, questions, or suggestions:
444
+
445
+ - [GitHub Issues](https://github.com/mindfiredigital/textigniterjs/issues)
446
+ - [Discussions](https://github.com/mindfiredigital/textigniterjs/discussions)
447
+
448
+ ## 🎉 What's New
449
+
450
+ ### v1.2.0
451
+
452
+ - ✨ Added `contentChange` event emitter for real-time content updates
453
+ - 🐛 Fixed clipboard permission error
454
+ - 📚 Updated documentation and examples
455
+ - 🔧 Improved TypeScript support
56
456
 
57
- ## Additional Resources
457
+ ---
58
458
 
59
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
459
+ Made with ❤️ by [Mindfire Digital](https://github.com/mindfiredigital)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindfiredigital/textigniter-angular",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,2 +1,25 @@
1
+ <h1>TextIgniter Angular Example</h1>
1
2
 
2
- <ngx-text-igniter [config]="config"></ngx-text-igniter>
3
+ <ngx-text-igniter [config]="config" (contentChange)="onContentChange($event)"></ngx-text-igniter>
4
+
5
+ <!-- Real-time Content Preview -->
6
+ <div style="margin-top: 20px; padding: 20px; border: 1px solid #ccc; background: #f9f9f9;">
7
+ <h3>Real-time Content Preview:</h3>
8
+
9
+ <div style="margin-top: 10px;">
10
+ <strong>HTML Content:</strong>
11
+ <pre
12
+ style="background: white; padding: 10px; overflow: auto; max-height: 200px;">{{ htmlContent || 'Start typing to see content...' }}</pre>
13
+ </div>
14
+
15
+ <div style="margin-top: 10px;">
16
+ <strong>Text Content:</strong>
17
+ <pre
18
+ style="background: white; padding: 10px; overflow: auto; max-height: 100px;">{{ textContent || 'Start typing to see text...' }}</pre>
19
+ </div>
20
+
21
+ <div style="margin-top: 10px;">
22
+ <strong>Stats:</strong>
23
+ <p>Characters: {{ characterCount }} | Words: {{ wordCount }}</p>
24
+ </div>
25
+ </div>
@@ -6,10 +6,13 @@ import { TextIgniterModule } from 'text-igniter';
6
6
  selector: 'app-root',
7
7
  imports: [RouterOutlet, TextIgniterModule],
8
8
  templateUrl: './app.component.html',
9
- styleUrl: './app.component.css'
9
+ styleUrl: './app.component.css',
10
10
  })
11
11
  export class AppComponent {
12
12
  title = 'demo-app';
13
+ htmlContent = '';
14
+ textContent = '';
15
+
13
16
  config = {
14
17
  showToolbar: true,
15
18
  features: [
@@ -28,7 +31,23 @@ export class AppComponent {
28
31
  'fontColor',
29
32
  'bgColor',
30
33
  'getHtmlContent',
31
- 'loadHtmlContent'
32
- ]
34
+ 'loadHtmlContent',
35
+ ],
36
+ };
37
+
38
+ onContentChange(data: { html: string; text: string }) {
39
+ console.log('Content changed:', data);
40
+ this.htmlContent = data.html;
41
+ this.textContent = data.text;
42
+ }
43
+
44
+ get characterCount(): number {
45
+ return this.textContent.length;
46
+ }
47
+
48
+ get wordCount(): number {
49
+ return this.textContent.trim()
50
+ ? this.textContent.trim().split(/\s+/).length
51
+ : 0;
33
52
  }
34
53
  }
@@ -14,6 +14,8 @@ import {
14
14
  AfterViewInit,
15
15
  Component,
16
16
  Input,
17
+ Output,
18
+ EventEmitter,
17
19
  OnChanges,
18
20
  SimpleChanges,
19
21
  } from '@angular/core';
@@ -28,11 +30,15 @@ import { TextIgniter } from '@mindfiredigital/textigniter/dist/TextIgniter';
28
30
  export class TextIgniterComponent implements OnChanges, AfterViewInit {
29
31
  @Input() config:
30
32
  | {
31
- showToolbar: boolean;
32
- features: string[];
33
- }
33
+ showToolbar: boolean;
34
+ features: string[];
35
+ }
34
36
  | undefined;
35
37
  @Input() editorId: string | 'editor-ngId' = 'editor-ngId';
38
+ @Output() contentChange = new EventEmitter<{ html: string; text: string }>();
39
+
40
+ private textIgniterInstance?: TextIgniter;
41
+
36
42
  ngAfterViewInit(): void {
37
43
  this.renderTable();
38
44
  }
@@ -48,7 +54,12 @@ export class TextIgniterComponent implements OnChanges, AfterViewInit {
48
54
  return console.log("editor-ngId can't be using name as id='editor' ");
49
55
  }
50
56
  if (this.config) {
51
- new TextIgniter(this.editorId, this.config);
57
+ this.textIgniterInstance = new TextIgniter(this.editorId, this.config);
58
+
59
+ // Subscribe to content changes and emit through Angular EventEmitter
60
+ this.textIgniterInstance.onContentChange(data => {
61
+ this.contentChange.emit(data);
62
+ });
52
63
  }
53
64
  }
54
- }
65
+ }
package/real-test.html ADDED
@@ -0,0 +1,328 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TextIgniter Angular Real Component Test</title>
7
+ <script src="https://unpkg.com/zone.js@0.15.0/dist/zone.min.js"></script>
8
+ <script src="https://unpkg.com/@angular/core@19/bundles/core.umd.js"></script>
9
+ <script src="https://unpkg.com/@angular/common@19/bundles/common.umd.js"></script>
10
+ <script src="https://unpkg.com/@angular/platform-browser@19/bundles/platform-browser.umd.js"></script>
11
+ <script src="https://unpkg.com/@angular/platform-browser-dynamic@19/bundles/platform-browser-dynamic.umd.js"></script>
12
+
13
+ <!-- Load the actual built TextIgniter components -->
14
+ <script src="../core/dist/index.umd.js"></script>
15
+ <script src="../web-component/dist/index.umd.js"></script>
16
+
17
+ <style>
18
+ .app-container {
19
+ margin: 20px;
20
+ padding: 20px;
21
+ font-family: Arial, sans-serif;
22
+ }
23
+ .editor-section {
24
+ margin: 20px 0;
25
+ padding: 15px;
26
+ border: 1px solid #ddd;
27
+ border-radius: 4px;
28
+ }
29
+ .controls {
30
+ margin: 10px 0;
31
+ padding: 10px;
32
+ background: #f5f5f5;
33
+ border-radius: 4px;
34
+ }
35
+ .controls button {
36
+ margin: 5px;
37
+ padding: 8px 16px;
38
+ border: 1px solid #ccc;
39
+ border-radius: 3px;
40
+ background: white;
41
+ cursor: pointer;
42
+ }
43
+ .test-output {
44
+ margin: 20px 0;
45
+ padding: 15px;
46
+ border: 1px solid #ccc;
47
+ border-radius: 4px;
48
+ background: white;
49
+ }
50
+ .test-result {
51
+ padding: 5px;
52
+ margin: 2px 0;
53
+ }
54
+ .test-pass { color: green; }
55
+ .test-fail { color: red; }
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div id="angular-app" class="app-container">
60
+ <h1>TextIgniter Angular Real Component Test</h1>
61
+
62
+ <div class="controls">
63
+ <button id="toggle-config-btn">Toggle Config</button>
64
+ <button id="change-features-btn">Change Features</button>
65
+ <button id="test-editor-btn">Test Editor Functions</button>
66
+ <button id="run-integration-tests-btn">Run Integration Tests</button>
67
+ </div>
68
+
69
+ <div class="editor-section">
70
+ <h3>Angular TextIgniter Component:</h3>
71
+ <div id="angular-textigniter-container">
72
+ <!-- This will be populated by Angular -->
73
+ </div>
74
+ </div>
75
+
76
+ <div class="test-output">
77
+ <h3>Integration Test Results:</h3>
78
+ <div id="integration-test-results"></div>
79
+ </div>
80
+ </div>
81
+
82
+ <script>
83
+ // Angular component definition using the real TextIgniter
84
+ const { Component, Input, OnInit, OnChanges, SimpleChanges, NgModule, platformBrowserDynamic } = ng.core;
85
+ const { CommonModule } = ng.common;
86
+ const { BrowserModule } = ng.platformBrowser;
87
+
88
+ // Real Angular TextIgniter Component that uses the actual library
89
+ @Component({
90
+ selector: 'app-textigniter-wrapper',
91
+ template: `
92
+ <div [id]="editorId" class="textigniter-wrapper"
93
+ [attr.data-testid]="'real-angular-editor-' + editorId"></div>
94
+ `,
95
+ standalone: false
96
+ })
97
+ class TextIgniterWrapperComponent {
98
+ @Input() config = {
99
+ showToolbar: true,
100
+ features: ['bold', 'italic', 'underline', 'hyperlink']
101
+ };
102
+ @Input() editorId = 'real-angular-editor';
103
+
104
+ textIgniterInstance = null;
105
+
106
+ ngAfterViewInit() {
107
+ this.initializeTextIgniter();
108
+ }
109
+
110
+ ngOnChanges(changes) {
111
+ if (changes['config'] && !changes['config'].firstChange) {
112
+ this.reinitializeTextIgniter();
113
+ }
114
+ }
115
+
116
+ initializeTextIgniter() {
117
+ try {
118
+ // Wait for DOM to be ready
119
+ setTimeout(() => {
120
+ if (window.TextIgniter && this.editorId !== 'editor') {
121
+ this.textIgniterInstance = new window.TextIgniter(this.editorId, this.config);
122
+ console.log('TextIgniter initialized for Angular component:', this.editorId);
123
+
124
+ // Mark as initialized for testing
125
+ const container = document.getElementById(this.editorId);
126
+ if (container) {
127
+ container.setAttribute('data-angular-textigniter-ready', 'true');
128
+ }
129
+ }
130
+ }, 100);
131
+ } catch (error) {
132
+ console.error('Error initializing TextIgniter:', error);
133
+ }
134
+ }
135
+
136
+ reinitializeTextIgniter() {
137
+ // Clear existing instance
138
+ const container = document.getElementById(this.editorId);
139
+ if (container) {
140
+ container.innerHTML = '';
141
+ }
142
+ this.initializeTextIgniter();
143
+ }
144
+ }
145
+
146
+ // Test Application Component
147
+ @Component({
148
+ selector: 'app-root',
149
+ template: `
150
+ <app-textigniter-wrapper
151
+ [config]="currentConfig"
152
+ [editorId]="'angular-editor-test'">
153
+ </app-textigniter-wrapper>
154
+ `,
155
+ standalone: false
156
+ })
157
+ class AppComponent {
158
+ currentConfig = {
159
+ showToolbar: true,
160
+ features: ['bold', 'italic', 'underline', 'hyperlink']
161
+ };
162
+
163
+ toggleConfig() {
164
+ this.currentConfig = {
165
+ ...this.currentConfig,
166
+ showToolbar: !this.currentConfig.showToolbar
167
+ };
168
+ }
169
+
170
+ changeFeatures() {
171
+ this.currentConfig = {
172
+ ...this.currentConfig,
173
+ features: this.currentConfig.features.length > 4
174
+ ? ['bold', 'italic']
175
+ : ['bold', 'italic', 'underline', 'hyperlink', 'fontColor', 'alignCenter']
176
+ };
177
+ }
178
+ }
179
+
180
+ // Angular Module
181
+ @NgModule({
182
+ declarations: [AppComponent, TextIgniterWrapperComponent],
183
+ imports: [BrowserModule, CommonModule],
184
+ bootstrap: [AppComponent]
185
+ })
186
+ class AppModule {}
187
+
188
+ // Bootstrap the application
189
+ let appInstance = null;
190
+ platformBrowserDynamic()
191
+ .bootstrapModule(AppModule)
192
+ .then(moduleRef => {
193
+ appInstance = moduleRef.instance;
194
+ console.log('Angular application bootstrapped successfully');
195
+
196
+ // Set up event handlers
197
+ setupEventHandlers();
198
+
199
+ // Run initial tests
200
+ setTimeout(() => {
201
+ runIntegrationTests();
202
+ }, 2000);
203
+ })
204
+ .catch(err => {
205
+ console.error('Error starting Angular app:', err);
206
+ });
207
+
208
+ // Event handlers for testing
209
+ function setupEventHandlers() {
210
+ document.getElementById('toggle-config-btn').addEventListener('click', () => {
211
+ const appComponent = document.querySelector('app-root');
212
+ if (appComponent && appComponent.__ngContext__) {
213
+ const component = appComponent.__ngContext__[8]; // Get component instance
214
+ component.toggleConfig();
215
+ }
216
+ });
217
+
218
+ document.getElementById('change-features-btn').addEventListener('click', () => {
219
+ const appComponent = document.querySelector('app-root');
220
+ if (appComponent && appComponent.__ngContext__) {
221
+ const component = appComponent.__ngContext__[8];
222
+ component.changeFeatures();
223
+ }
224
+ });
225
+
226
+ document.getElementById('test-editor-btn').addEventListener('click', () => {
227
+ testEditorFunctionality();
228
+ });
229
+
230
+ document.getElementById('run-integration-tests-btn').addEventListener('click', () => {
231
+ runIntegrationTests();
232
+ });
233
+ }
234
+
235
+ // Integration tests
236
+ const integrationTestResults = [];
237
+
238
+ function addIntegrationTestResult(testName, passed, message) {
239
+ integrationTestResults.push({ testName, passed, message });
240
+ updateIntegrationTestDisplay();
241
+ }
242
+
243
+ function updateIntegrationTestDisplay() {
244
+ const output = document.getElementById('integration-test-results');
245
+ output.innerHTML = integrationTestResults.map(result =>
246
+ `<div class="test-result ${result.passed ? 'test-pass' : 'test-fail'}"
247
+ data-test="${result.testName.toLowerCase().replace(/ /g, '-')}"
248
+ data-passed="${result.passed}">
249
+ ${result.testName}: ${result.passed ? 'PASS' : 'FAIL'} - ${result.message}
250
+ </div>`
251
+ ).join('');
252
+ }
253
+
254
+ function runIntegrationTests() {
255
+ integrationTestResults.length = 0;
256
+
257
+ // Test 1: Angular component renders
258
+ setTimeout(() => {
259
+ const wrapper = document.querySelector('app-textigniter-wrapper');
260
+ addIntegrationTestResult(
261
+ 'Angular Component Renders',
262
+ !!wrapper,
263
+ wrapper ? 'Angular wrapper component found' : 'Angular wrapper component not found'
264
+ );
265
+ }, 100);
266
+
267
+ // Test 2: TextIgniter instance created
268
+ setTimeout(() => {
269
+ const container = document.getElementById('angular-editor-test');
270
+ const isReady = container && container.hasAttribute('data-angular-textigniter-ready');
271
+ addIntegrationTestResult(
272
+ 'TextIgniter Instance Created',
273
+ isReady,
274
+ isReady ? 'TextIgniter instance initialized' : 'TextIgniter instance not found'
275
+ );
276
+ }, 200);
277
+
278
+ // Test 3: Editor DOM structure exists
279
+ setTimeout(() => {
280
+ const editorContainer = document.querySelector('#angular-editor-test .editor-container');
281
+ const toolbarContainer = document.querySelector('#angular-editor-test .toolbar-container');
282
+ addIntegrationTestResult(
283
+ 'Editor DOM Structure',
284
+ !!(editorContainer || toolbarContainer || document.querySelector('#angular-editor-test .text-igniter-editor')),
285
+ editorContainer ? 'Editor DOM structure found' : 'Editor DOM structure missing (may be mock)'
286
+ );
287
+ }, 300);
288
+
289
+ // Test 4: Configuration applied
290
+ setTimeout(() => {
291
+ const container = document.getElementById('angular-editor-test');
292
+ const hasContent = container && container.innerHTML.trim().length > 0;
293
+ addIntegrationTestResult(
294
+ 'Configuration Applied',
295
+ hasContent,
296
+ hasContent ? 'Editor has content/structure' : 'Editor appears empty'
297
+ );
298
+ }, 400);
299
+
300
+ // Test 5: Event binding works
301
+ setTimeout(() => {
302
+ const appRoot = document.querySelector('app-root');
303
+ const hasAngularContext = appRoot && appRoot.__ngContext__;
304
+ addIntegrationTestResult(
305
+ 'Angular Binding Active',
306
+ hasAngularContext,
307
+ hasAngularContext ? 'Angular data binding active' : 'Angular binding not detected'
308
+ );
309
+ }, 500);
310
+ }
311
+
312
+ function testEditorFunctionality() {
313
+ const container = document.getElementById('angular-editor-test');
314
+ if (container) {
315
+ // Try to interact with the editor
316
+ const editableArea = container.querySelector('[contenteditable="true"]');
317
+ if (editableArea) {
318
+ editableArea.focus();
319
+ editableArea.textContent = 'Integration test text';
320
+ console.log('Editor functionality test completed');
321
+ } else {
322
+ console.log('No editable area found - using mock implementation');
323
+ }
324
+ }
325
+ }
326
+ </script>
327
+ </body>
328
+ </html>
package/test.html ADDED
@@ -0,0 +1,255 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TextIgniter Angular Package Test</title>
7
+ <script src="https://unpkg.com/zone.js@0.15.0/dist/zone.min.js"></script>
8
+ <script src="https://unpkg.com/@angular/core@19/bundles/core.umd.js"></script>
9
+ <script src="https://unpkg.com/@angular/common@19/bundles/common.umd.js"></script>
10
+ <script src="https://unpkg.com/@angular/platform-browser@19/bundles/platform-browser.umd.js"></script>
11
+ <script src="https://unpkg.com/@angular/platform-browser-dynamic@19/bundles/platform-browser-dynamic.umd.js"></script>
12
+ <style>
13
+ .editor-container {
14
+ margin: 20px;
15
+ padding: 20px;
16
+ border: 1px solid #ddd;
17
+ border-radius: 4px;
18
+ }
19
+ .test-controls {
20
+ margin: 20px;
21
+ padding: 10px;
22
+ background: #f5f5f5;
23
+ border-radius: 4px;
24
+ }
25
+ .test-controls button {
26
+ margin: 5px;
27
+ padding: 8px 16px;
28
+ border: 1px solid #ccc;
29
+ border-radius: 3px;
30
+ background: white;
31
+ cursor: pointer;
32
+ }
33
+ .test-controls button:hover {
34
+ background: #e9e9e9;
35
+ }
36
+ #cypress-test-results {
37
+ margin: 20px;
38
+ padding: 15px;
39
+ border: 1px solid #ccc;
40
+ border-radius: 4px;
41
+ background: white;
42
+ }
43
+ .test-result {
44
+ padding: 5px;
45
+ margin: 2px 0;
46
+ }
47
+ .test-pass {
48
+ color: green;
49
+ }
50
+ .test-fail {
51
+ color: red;
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div id="angular-root">
57
+ <div class="test-controls">
58
+ <h2>TextIgniter Angular Package Test</h2>
59
+ <button id="config-change-btn">Toggle Config</button>
60
+ <button id="run-tests-btn">Run Tests</button>
61
+ </div>
62
+ <div class="editor-container">
63
+ <div id="editor-angular"></div>
64
+ </div>
65
+ <div id="cypress-test-results">
66
+ <h3>Test Results:</h3>
67
+ <div id="test-output"></div>
68
+ </div>
69
+ </div>
70
+
71
+ <script>
72
+ // Mock Angular TextIgniter component for testing
73
+ class MockTextIgniterComponent {
74
+ constructor(containerId, config) {
75
+ this.containerId = containerId;
76
+ this.config = config;
77
+ this.container = document.getElementById(containerId);
78
+ this.rendered = false;
79
+ this.render();
80
+ }
81
+
82
+ render() {
83
+ if (!this.container) return;
84
+
85
+ // Clear container
86
+ this.container.innerHTML = '';
87
+
88
+ // Create mock editor structure
89
+ const editorDiv = document.createElement('div');
90
+ editorDiv.id = 'mock-angular-editor';
91
+ editorDiv.setAttribute('data-testid', 'angular-editor');
92
+ editorDiv.style.minHeight = '100px';
93
+ editorDiv.style.border = '1px solid #ccc';
94
+ editorDiv.style.padding = '10px';
95
+ editorDiv.style.background = '#f9f9f9';
96
+
97
+ // Add toolbar if enabled
98
+ if (this.config.showToolbar) {
99
+ const toolbar = document.createElement('div');
100
+ toolbar.id = 'mock-toolbar';
101
+ toolbar.setAttribute('data-testid', 'angular-toolbar');
102
+ toolbar.style.padding = '5px';
103
+ toolbar.style.border = '1px solid #ddd';
104
+ toolbar.style.marginBottom = '5px';
105
+ toolbar.style.background = '#fff';
106
+
107
+ // Add feature buttons
108
+ this.config.features.forEach(feature => {
109
+ const btn = document.createElement('button');
110
+ btn.textContent = feature;
111
+ btn.setAttribute('data-feature', feature);
112
+ btn.style.margin = '2px';
113
+ btn.style.padding = '4px 8px';
114
+ btn.style.fontSize = '12px';
115
+ toolbar.appendChild(btn);
116
+ });
117
+
118
+ this.container.appendChild(toolbar);
119
+ }
120
+
121
+ // Add editor content area
122
+ const contentArea = document.createElement('div');
123
+ contentArea.id = 'editor-content';
124
+ contentArea.setAttribute('contenteditable', 'true');
125
+ contentArea.setAttribute('data-testid', 'editor-content');
126
+ contentArea.style.minHeight = '80px';
127
+ contentArea.style.padding = '8px';
128
+ contentArea.style.border = '1px solid #ddd';
129
+ contentArea.style.background = 'white';
130
+ contentArea.textContent = 'Mock TextIgniter Angular Editor';
131
+
132
+ editorDiv.appendChild(contentArea);
133
+ this.container.appendChild(editorDiv);
134
+
135
+ this.rendered = true;
136
+
137
+ // Simulate component lifecycle
138
+ setTimeout(() => {
139
+ this.container.setAttribute('data-angular-initialized', 'true');
140
+ }, 100);
141
+ }
142
+
143
+ updateConfig(newConfig) {
144
+ this.config = { ...this.config, ...newConfig };
145
+ this.render();
146
+ }
147
+ }
148
+
149
+ // Initialize the mock component
150
+ let currentConfig = {
151
+ showToolbar: true,
152
+ features: ['bold', 'italic', 'underline', 'hyperlink', 'image']
153
+ };
154
+
155
+ let textIgniterInstance = new MockTextIgniterComponent('editor-angular', currentConfig);
156
+
157
+ // Test functionality
158
+ const testResults = [];
159
+
160
+ function addTestResult(testName, passed, message) {
161
+ testResults.push({ testName, passed, message });
162
+ updateTestDisplay();
163
+ }
164
+
165
+ function updateTestDisplay() {
166
+ const output = document.getElementById('test-output');
167
+ output.innerHTML = testResults.map(result =>
168
+ `<div class="test-result ${result.passed ? 'test-pass' : 'test-fail'}"
169
+ data-test="${result.testName.toLowerCase().replace(/ /g, '-')}"
170
+ data-passed="${result.passed}">
171
+ ${result.testName}: ${result.passed ? 'PASS' : 'FAIL'} - ${result.message}
172
+ </div>`
173
+ ).join('');
174
+ }
175
+
176
+ // Event handlers
177
+ document.getElementById('config-change-btn').addEventListener('click', () => {
178
+ currentConfig = {
179
+ showToolbar: !currentConfig.showToolbar,
180
+ features: currentConfig.showToolbar
181
+ ? ['bold', 'italic']
182
+ : ['bold', 'italic', 'underline', 'fontColor', 'alignCenter']
183
+ };
184
+ textIgniterInstance.updateConfig(currentConfig);
185
+ });
186
+
187
+ document.getElementById('run-tests-btn').addEventListener('click', () => {
188
+ runAllTests();
189
+ });
190
+
191
+ function runAllTests() {
192
+ testResults.length = 0; // Clear previous results
193
+
194
+ // Test 1: Component renders
195
+ setTimeout(() => {
196
+ const editor = document.querySelector('[data-testid="angular-editor"]');
197
+ addTestResult(
198
+ 'Component Renders',
199
+ !!editor,
200
+ editor ? 'Angular editor component found' : 'Angular editor component not found'
201
+ );
202
+ }, 100);
203
+
204
+ // Test 2: Config is applied
205
+ setTimeout(() => {
206
+ const toolbar = document.querySelector('[data-testid="angular-toolbar"]');
207
+ const hasToolbar = currentConfig.showToolbar ? !!toolbar : !toolbar;
208
+ addTestResult(
209
+ 'Config Applied',
210
+ hasToolbar,
211
+ hasToolbar ? 'Toolbar visibility matches config' : 'Toolbar visibility does not match config'
212
+ );
213
+ }, 200);
214
+
215
+ // Test 3: Features are rendered
216
+ setTimeout(() => {
217
+ const featureButtons = document.querySelectorAll('[data-feature]');
218
+ const expectedCount = currentConfig.showToolbar ? currentConfig.features.length : 0;
219
+ const actualCount = featureButtons.length;
220
+ addTestResult(
221
+ 'Features Rendered',
222
+ actualCount === expectedCount,
223
+ `Expected ${expectedCount} features, found ${actualCount}`
224
+ );
225
+ }, 300);
226
+
227
+ // Test 4: Editor content area exists
228
+ setTimeout(() => {
229
+ const contentArea = document.querySelector('[data-testid="editor-content"]');
230
+ addTestResult(
231
+ 'Editor Content Area',
232
+ !!contentArea && contentArea.isContentEditable,
233
+ contentArea ? 'Editor content area is editable' : 'Editor content area not found'
234
+ );
235
+ }, 400);
236
+
237
+ // Test 5: Component initialization
238
+ setTimeout(() => {
239
+ const container = document.getElementById('editor-angular');
240
+ const isInitialized = container.hasAttribute('data-angular-initialized');
241
+ addTestResult(
242
+ 'Component Initialized',
243
+ isInitialized,
244
+ isInitialized ? 'Component initialization complete' : 'Component not properly initialized'
245
+ );
246
+ }, 500);
247
+ }
248
+
249
+ // Auto-run tests on load
250
+ setTimeout(() => {
251
+ runAllTests();
252
+ }, 1000);
253
+ </script>
254
+ </body>
255
+ </html>