@libs-ui/components-preview-text-data 0.2.356-9 → 0.2.357-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.
package/README.md CHANGED
@@ -1,37 +1,36 @@
1
1
  # @libs-ui/components-preview-text-data
2
2
 
3
- > Component hiển thị chỉnh sửa mã nguồn (code editor) dựa trên CodeMirror 6, hỗ trợ nhiều ngôn ngữ tính năng linter mạnh mẽ.
3
+ > Code editor/viewer mạnh mẽ dựa trên CodeMirror 6, hỗ trợ highlight pháp đa ngôn ngữ, linter thời gian thực, IntelliSense và bảo mật pattern.
4
4
 
5
5
  ## Giới thiệu
6
6
 
7
- `LibsUiComponentsPreviewTextDataComponent` cung cấp một trình soạn thảo mã nguồn nhẹ nhưng đầy đủ tính năng. hỗ trợ highlight cú pháp cho nhiều ngôn ngữ, kiểm tra lỗi (linter) cho JavaScript, JSON, SQL và cung cấp giao diện thân thiện với các tùy chọn copy, tự động xuống dòng (line wrap).
7
+ `LibsUiComponentsPreviewTextDataComponent` cung cấp một trình soạn thảo/xem mã nguồn tích hợp sẵn vào Angular với kiến trúc Signals và OnPush. Component hỗ trợ highlight cú pháp cho 14 ngôn ngữ lập trình, kiểm tra lỗi cú pháp thời gian thực (linter) cho JavaScript, JSON, SQL và Python, gợi ý code thông minh (IntelliSense với dot-notation), cùng chế bảo mật chặn pattern nguy hiểm cả khi gõ lẫn paste.
8
8
 
9
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - ✅ **Highlight cú pháp**: Hỗ trợ JavaScript, TypeScript, HTML, CSS, JSON, SQL, XML, YAML, Python, văn bản thuần túy.
12
- - ✅ **Linter tích hợp**: Kiểm tra lỗi cú pháp thời gian thực cho JS, JSON SQL.
13
- - **Chỉnh sửa (Editable)**: thể cấu hình để trở thành một trình soạn thảo mã nguồn đầy đủ.
14
- - **Line Wrapping**: Tùy chọn tự động xuống dòng để tránh thanh cuộn ngang.
15
- - **Dynamic Selection**: Cho phép thay đổi ngôn ngữ ngay trong giao diện nếu được cấu hình.
16
- - **Hiệu năng cao**: Sử dụng CodeMirror 6 với kiến trúc modular, load ngôn ngữ khi cần thiết.
17
- - **OnPush Change Detection**
18
- - **Angular Signals**
11
+ - Highlight cú pháp cho 14 ngôn ngữ: JavaScript/TypeScript, HTML, CSS/SCSS, JSON, SQL, XML, YAML, Python, Java, C/C++, PHP, Go, Markdown, Plain Text
12
+ - Linter tích hợp phát hiện lỗi cú pháp thời gian thực cho JavaScript, JSON, SQL, Python
13
+ - IntelliSense với dot-notation: định nghĩa object/biến sẵn, gợi ý properties/methods khi dấu chấm (giống IDE)
14
+ - Security policy: chặn pattern nguy hiểm (string hoặc RegExp) ngay khi gõ, chặn paste chứa vi phạm, emit danh sách vi phạm qua `(outViolations)`
15
+ - Lazy load ngôn ngữ: chỉ tải extension khi cần, tối ưu bundle size
16
+ - Non-destructive update: đổi ngôn ngữ, wrap, completions không làm mất nội dung hay vị trí con trỏ
17
+ - Chế độ editable/readonly linh hoạt
18
+ - Thanh công cụ tích hợp: copy nội dung, toggle line wrap, dropdown chọn ngôn ngữ
19
+ - Two-way binding cho `langSelected` qua `model()`
20
+ - Standalone component, ChangeDetectionStrategy.OnPush, Angular Signals
19
21
 
20
22
  ## Khi nào sử dụng
21
23
 
22
- - Hiển thị các đoạn mã mẫu trong tài liệu.
23
- - Chỉnh sửa cấu hình (JSON/YAML).
24
- - Viết câu lệnh truy vấn (SQL) trong các công cụ quản trị.
25
- - Kiểm tra dữ liệu thô (Raw data) từ API.
24
+ - Hiển thị cấu hình JSON, YAML hoặc đoạn mã mẫu trong tài liệu hoặc dashboard
25
+ - Cho phép người dùng biên tập câu lệnh SQL hoặc mã JavaScript trong các công cụ quản trị
26
+ - Xây dựng low-code/no-code editor cần kiểm soát bảo mật (chặn eval, fetch, localStorage...)
27
+ - Kiểm tra xem log hoặc raw data từ API với highlight cú pháp rõ ràng
28
+ - Tích hợp IntelliSense cho đối tượng domain cụ thể (biến, API object, schema) trong form nhập mã
26
29
 
27
30
  ## Cài đặt
28
31
 
29
32
  ```bash
30
- # npm
31
33
  npm install @libs-ui/components-preview-text-data
32
-
33
- # yarn
34
- yarn add @libs-ui/components-preview-text-data
35
34
  ```
36
35
 
37
36
  ## Import
@@ -44,89 +43,403 @@ import { LibsUiComponentsPreviewTextDataComponent } from '@libs-ui/components-pr
44
43
  imports: [LibsUiComponentsPreviewTextDataComponent],
45
44
  // ...
46
45
  })
47
- export class YourComponent {}
46
+ export class MyComponent {}
48
47
  ```
49
48
 
50
- ## dụ
49
+ Import thêm types, constants khi cần:
51
50
 
52
- ### Chế độ xem (Read-only)
51
+ ```typescript
52
+ import {
53
+ LibsUiComponentsPreviewTextDataComponent,
54
+ PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT,
55
+ IPreviewTextDataChange,
56
+ IPreviewTextDataCompletionItem,
57
+ IPreviewTextDataViolation,
58
+ ILanguageOptions,
59
+ DEFAULT_SECURITY_PATTERNS,
60
+ } from '@libs-ui/components-preview-text-data';
61
+ ```
53
62
 
54
- ```html
55
- <libs_ui-components-preview_text_data
56
- [content]="myJsonCode"
57
- langSelected="json"></libs_ui-components-preview_text_data>
63
+ ## Ví dụ sử dụng
64
+
65
+ ### 1. Chế độ xem (Read-only) — ẩn thanh công cụ
66
+
67
+ ```typescript
68
+ // my-viewer.component.ts
69
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
70
+ import { LibsUiComponentsPreviewTextDataComponent } from '@libs-ui/components-preview-text-data';
71
+
72
+ @Component({
73
+ selector: 'app-my-viewer',
74
+ standalone: true,
75
+ changeDetection: ChangeDetectionStrategy.OnPush,
76
+ imports: [LibsUiComponentsPreviewTextDataComponent],
77
+ template: `
78
+ <libs_ui-components-preview_text_data
79
+ [content]="configCode"
80
+ [langSelected]="'json'"
81
+ [hiddenAction]="true"
82
+ background="#fcfcfc" />
83
+ `,
84
+ })
85
+ export class MyViewerComponent {
86
+ readonly configCode = `{
87
+ "projectName": "Libs UI",
88
+ "version": "2.0.0",
89
+ "features": ["Signals", "CodeMirror", "OnPush"]
90
+ }`;
91
+ }
58
92
  ```
59
93
 
60
- ### Chế độ chỉnh sửa với Linter
94
+ ### 2. Chế độ chỉnh sửa với Linter và đổi ngôn ngữ
61
95
 
62
- ```html
63
- <libs_ui-components-preview_text_data
64
- [(langSelected)]="currentLang"
65
- [content]="initialContent"
66
- [editable]="true"
67
- [langsAccept]="['javascript', 'json', 'sql']"
68
- (outChange)="onContentChange($event)"></libs_ui-components-preview_text_data>
96
+ ```typescript
97
+ // sql-editor.component.ts
98
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
99
+ import {
100
+ LibsUiComponentsPreviewTextDataComponent,
101
+ IPreviewTextDataChange,
102
+ PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT,
103
+ } from '@libs-ui/components-preview-text-data';
104
+ import { Diagnostic } from '@codemirror/lint';
105
+
106
+ @Component({
107
+ selector: 'app-sql-editor',
108
+ standalone: true,
109
+ changeDetection: ChangeDetectionStrategy.OnPush,
110
+ imports: [LibsUiComponentsPreviewTextDataComponent],
111
+ template: `
112
+ <libs_ui-components-preview_text_data
113
+ [content]="sqlQuery"
114
+ [(langSelected)]="currentLang"
115
+ [editable]="true"
116
+ [langsAccept]="['sql', 'javascript', 'json']"
117
+ (outChange)="handlerChange($event)"
118
+ (syntaxErrors)="handlerSyntaxErrors($event)" />
119
+
120
+ @if (lintErrors().length) {
121
+ <div class="p-3 bg-red-50 text-red-600 text-xs font-mono mt-2 rounded">
122
+ Lỗi cú pháp: {{ lintErrors()[0].message }}
123
+ </div>
124
+ }
125
+ `,
126
+ })
127
+ export class SqlEditorComponent {
128
+ readonly currentLang = signal<PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT>('sql');
129
+ readonly lintErrors = signal<Diagnostic[]>([]);
130
+ readonly sqlQuery = `SELECT id, name, email
131
+ FROM users
132
+ WHERE status = 'active'
133
+ ORDER BY created_at DESC
134
+ LIMIT 50;`;
135
+
136
+ handlerChange(event: IPreviewTextDataChange): void {
137
+ event.stopPropagation?.();
138
+ console.log('Nội dung thay đổi:', event.content, '— ngôn ngữ:', event.language);
139
+ }
140
+
141
+ handlerSyntaxErrors(errors: Diagnostic[]): void {
142
+ this.lintErrors.set(errors);
143
+ }
144
+ }
69
145
  ```
70
146
 
71
- ## API
147
+ ### 3. IntelliSense với dot-notation (giống IDE)
72
148
 
73
- ### libs_ui-components-preview_text_data
149
+ ```typescript
150
+ // js-intellisense.component.ts
151
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
152
+ import {
153
+ LibsUiComponentsPreviewTextDataComponent,
154
+ IPreviewTextDataCompletionItem,
155
+ PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT,
156
+ } from '@libs-ui/components-preview-text-data';
74
157
 
75
- #### Inputs
158
+ @Component({
159
+ selector: 'app-js-intellisense',
160
+ standalone: true,
161
+ changeDetection: ChangeDetectionStrategy.OnPush,
162
+ imports: [LibsUiComponentsPreviewTextDataComponent],
163
+ template: `
164
+ <libs_ui-components-preview_text_data
165
+ [content]="starterCode"
166
+ [(langSelected)]="lang"
167
+ [editable]="true"
168
+ [completions]="completions"
169
+ background="#fafbfc" />
170
+ `,
171
+ })
172
+ export class JsIntelliSenseComponent {
173
+ readonly lang = signal<PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT>('javascript');
174
+
175
+ readonly starterCode = `// Gõ "user." hoặc "config." để xem gợi ý
176
+ function process() {
177
+ const name = user.
178
+ const url = config.
179
+ }`;
180
+
181
+ readonly completions: IPreviewTextDataCompletionItem[] = [
182
+ {
183
+ label: 'user',
184
+ type: 'variable',
185
+ detail: 'object',
186
+ info: 'User object — thông tin người dùng hiện tại',
187
+ properties: [
188
+ { label: 'id', type: 'property', detail: 'number', info: 'Unique user ID' },
189
+ { label: 'name', type: 'property', detail: 'string', info: 'Họ và tên đầy đủ' },
190
+ { label: 'email', type: 'property', detail: 'string', info: 'Địa chỉ email' },
191
+ { label: 'role', type: 'property', detail: "'admin' | 'user'", info: 'Vai trò trong hệ thống' },
192
+ { label: 'logout', type: 'method', detail: '() => Promise<void>', info: 'Đăng xuất tài khoản' },
193
+ ],
194
+ },
195
+ {
196
+ label: 'config',
197
+ type: 'variable',
198
+ detail: 'object',
199
+ info: 'Application configuration',
200
+ properties: [
201
+ { label: 'apiUrl', type: 'property', detail: 'string', info: 'Base URL của API server' },
202
+ { label: 'timeout', type: 'property', detail: 'number', info: 'Request timeout (ms)' },
203
+ { label: 'debug', type: 'property', detail: 'boolean', info: 'Bật debug logging' },
204
+ ],
205
+ },
206
+ ];
207
+ }
208
+ ```
209
+
210
+ ### 4. Security policy — chặn pattern nguy hiểm
76
211
 
77
- | Property | Type | Default | Description |
78
- | ---------------------- | -------------------------------------- | ------------ | ------------------------------------------------ |
79
- | `[background]` | `string` | `'#f8f9fa'` | Màu nền của editor. |
80
- | `[content]` | `string` | `''` | Nội dung code hiển thị/chỉnh sửa. |
81
- | `[editable]` | `boolean` | `false` | Cho phép người dùng chỉnh sửa nội dung. |
82
- | `[hiddenAction]` | `boolean` | `false` | Ẩn thanh công cụ (Copy, Wrap, Lang selector). |
83
- | `[langsAccept]` | `PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT[]` | `undefined` | Danh sách ngôn ngữ cho phép chọn trong dropdown. |
84
- | `[langsChangeLabel]` | `ILanguageOptions` | `undefined` | Tùy chỉnh label hiển thị của các ngôn ngữ. |
85
- | `[langSelected]` | `PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT` | **Bắt buộc** | Ngôn ngữ hiện tại (hỗ trợ Two-way binding). |
86
- | `[lintIgnorePatterns]` | `string[]` | `[...]` | Các mẫu thông báo lỗi linter JS sẽ bỏ qua. |
212
+ ```typescript
213
+ // secure-editor.component.ts
214
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
215
+ import {
216
+ LibsUiComponentsPreviewTextDataComponent,
217
+ DEFAULT_SECURITY_PATTERNS,
218
+ IPreviewTextDataViolation,
219
+ PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT,
220
+ } from '@libs-ui/components-preview-text-data';
87
221
 
88
- #### Outputs
222
+ @Component({
223
+ selector: 'app-secure-editor',
224
+ standalone: true,
225
+ changeDetection: ChangeDetectionStrategy.OnPush,
226
+ imports: [LibsUiComponentsPreviewTextDataComponent],
227
+ template: `
228
+ <libs_ui-components-preview_text_data
229
+ [content]="starterCode"
230
+ [(langSelected)]="lang"
231
+ [editable]="true"
232
+ [forbiddenPatterns]="securityPatterns"
233
+ (outViolations)="handlerViolations($event)"
234
+ background="#fafbfc" />
235
+
236
+ @if (violations().length) {
237
+ <div class="p-3 bg-red-50 border border-red-200 rounded mt-2">
238
+ <p class="text-sm font-semibold text-red-700 mb-1">
239
+ Phát hiện {{ violations().length }} vi phạm bảo mật:
240
+ </p>
241
+ @for (v of violations(); track v.from) {
242
+ <p class="text-xs text-red-600 font-mono">
243
+ Dòng {{ v.line }}: "{{ v.matched }}" — vi phạm pattern {{ v.pattern }}
244
+ </p>
245
+ }
246
+ </div>
247
+ }
248
+ `,
249
+ })
250
+ export class SecureEditorComponent {
251
+ readonly lang = signal<PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT>('javascript');
252
+ readonly violations = signal<IPreviewTextDataViolation[]>([]);
253
+ readonly securityPatterns = DEFAULT_SECURITY_PATTERNS;
254
+
255
+ readonly starterCode = `// Code thuần — không gọi API hay truy cập storage
256
+ function calculateTotal(items) {
257
+ return items.reduce((sum, item) => sum + item.price * item.qty, 0);
258
+ }
259
+ // Thử gõ: eval("test") hoặc localStorage.getItem('key')
260
+ // → sẽ bị highlight đỏ và chặn paste`;
261
+
262
+ handlerViolations(violations: IPreviewTextDataViolation[]): void {
263
+ this.violations.set(violations);
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### 5. Chỉ cho phép một ngôn ngữ cố định (không hiển thị dropdown)
269
+
270
+ ```html
271
+ <!-- Khi langsAccept có đúng 1 phần tử trùng với langSelected → dropdown bị ẩn -->
272
+ <libs_ui-components-preview_text_data
273
+ [content]="jsonData"
274
+ [(langSelected)]="lang"
275
+ [editable]="true"
276
+ [langsAccept]="['json']" />
277
+ ```
89
278
 
90
- | Property | Type | Description |
91
- | ---------------- | ------------------------ | ------------------------------------------------------------- |
92
- | `(outChange)` | `IPreviewTextDataChange` | Phát ra khi nội dung, trạng thái wrap hoặc ngôn ngữ thay đổi. |
93
- | `(syntaxErrors)` | `Diagnostic[]` | Danh sách lỗi cú pháp được phát hiện bởi linter. |
279
+ ## @Input()
280
+
281
+ | Input | Type | Default | tả | dụ |
282
+ |---|---|---|---|---|
283
+ | `[content]` | `string` | `''` | Nội dung code cần hiển thị hoặc chỉnh sửa | `[content]="myCode"` |
284
+ | `[(langSelected)]` | `PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT` | **Bắt buộc** | Ngôn ngữ highlight hiện tại. Hỗ trợ two-way binding qua `model()` | `[(langSelected)]="currentLang"` |
285
+ | `[editable]` | `boolean` | `false` | Cho phép người dùng chỉnh sửa nội dung trong editor | `[editable]="true"` |
286
+ | `[hiddenAction]` | `boolean` | `false` | Ẩn toàn bộ thanh công cụ (Copy, Wrap, dropdown ngôn ngữ) | `[hiddenAction]="true"` |
287
+ | `[langsAccept]` | `PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT[]` | `undefined` | Danh sách ngôn ngữ được phép chọn trong dropdown. Nếu chỉ có 1 phần tử trùng `langSelected` → dropdown bị ẩn | `[langsAccept]="['json', 'sql']"` |
288
+ | `[langsChangeLabel]` | `ILanguageOptions` | `undefined` | Tùy chỉnh nhãn hiển thị của một ngôn ngữ trong dropdown | `[langsChangeLabel]="{ id: 'json', label: 'Cấu hình JSON' }"` |
289
+ | `[completions]` | `IPreviewTextDataCompletionItem[]` | `[]` | Danh sách object/biến gợi ý khi gõ — hỗ trợ dot-notation cho properties lồng nhau (IntelliSense) | `[completions]="myCompletions"` |
290
+ | `[forbiddenPatterns]` | `T_ForbiddenPattern[]` | `[]` | Danh sách pattern bị cấm (string hoặc RegExp). Highlight đỏ khi gõ, chặn paste chứa vi phạm, phát ra `(outViolations)` | `[forbiddenPatterns]="DEFAULT_SECURITY_PATTERNS"` |
291
+ | `[lintIgnorePatterns]` | `string[]` | `['Cannot use import statement...', 'Unexpected token export', 'import ', '@angular/core']` | Danh sách chuỗi lỗi linter JS sẽ bị bỏ qua (ignore) trong quá trình kiểm tra | `[lintIgnorePatterns]="['import ']"` |
292
+ | `[background]` | `string` | `'#f8f9fa'` | Màu nền của vùng soạn thảo (CSS color value) | `background="#ffffff"` |
293
+
294
+ ## @Output()
295
+
296
+ | Output | Type | Mô tả | Handler TS | Binding HTML |
297
+ |---|---|---|---|---|
298
+ | `(outChange)` | `IPreviewTextDataChange` | Phát ra khi nội dung thay đổi, người dùng đổi ngôn ngữ hoặc toggle line wrap | `handlerChange(event: IPreviewTextDataChange): void { event.stopPropagation?.(); this.code.set(event.content); }` | `(outChange)="handlerChange($event)"` |
299
+ | `(syntaxErrors)` | `Diagnostic[]` | Phát ra mảng lỗi cú pháp mỗi khi linter chạy. Mảng rỗng khi không có lỗi | `handlerSyntaxErrors(errors: Diagnostic[]): void { this.errors.set(errors); }` | `(syntaxErrors)="handlerSyntaxErrors($event)"` |
300
+ | `(outViolations)` | `IPreviewTextDataViolation[]` | Phát ra danh sách vi phạm (vị trí, dòng, pattern, chuỗi bị match) mỗi khi nội dung thay đổi khi có `forbiddenPatterns`. Mảng rỗng khi không có vi phạm | `handlerViolations(violations: IPreviewTextDataViolation[]): void { this.violations.set(violations); }` | `(outViolations)="handlerViolations($event)"` |
94
301
 
95
302
  ## Types & Interfaces
96
303
 
97
304
  ```typescript
98
- export type PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT = 'javascript' | 'json' | 'sql' | 'html' | 'css' | 'xml' | 'yaml' | 'python' | 'text';
305
+ import {
306
+ PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT,
307
+ IPreviewTextDataChange,
308
+ IPreviewTextDataCompletionItem,
309
+ IPreviewTextDataViolation,
310
+ ILanguageOptions,
311
+ T_ForbiddenPattern,
312
+ DEFAULT_SECURITY_PATTERNS,
313
+ } from '@libs-ui/components-preview-text-data';
314
+ ```
99
315
 
316
+ ```typescript
317
+ /** Tất cả ngôn ngữ được hỗ trợ */
318
+ export type PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT =
319
+ | 'javascript'
320
+ | 'html'
321
+ | 'css'
322
+ | 'markdown'
323
+ | 'json'
324
+ | 'sql'
325
+ | 'xml'
326
+ | 'yaml'
327
+ | 'python'
328
+ | 'java'
329
+ | 'cpp'
330
+ | 'php'
331
+ | 'go'
332
+ | 'text';
333
+
334
+ /** Payload phát ra qua (outChange) */
100
335
  export interface IPreviewTextDataChange {
101
336
  content: string;
102
337
  isWrap: boolean;
103
338
  language: PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT;
104
- contextChange: 'content' | 'isWrap' | 'language';
339
+ /** Loại thay đổi: nội dung / ngôn ngữ / line wrap */
340
+ contextChange: 'content' | 'language' | 'isWrap';
341
+ }
342
+
343
+ /** Một item trong danh sách completions — hỗ trợ dot-notation qua properties[] */
344
+ export interface IPreviewTextDataCompletionItem {
345
+ label: string;
346
+ /** @default 'variable' */
347
+ type?: 'variable' | 'property' | 'function' | 'class' | 'method' | 'keyword' | 'text' | 'constant' | 'interface' | 'type' | 'enum' | 'field' | 'namespace' | 'module';
348
+ /** Gợi ý type ngắn hiển thị cạnh label, VD: 'string', '() => void' */
349
+ detail?: string;
350
+ /** Mô tả dài hiển thị trong tooltip */
351
+ info?: string;
352
+ /** Properties lồng nhau — được gợi ý sau dấu chấm (dot-notation) */
353
+ properties?: IPreviewTextDataCompletionItem[];
354
+ }
355
+
356
+ /** Vi phạm được phát hiện trong editor, phát ra qua (outViolations) */
357
+ export interface IPreviewTextDataViolation {
358
+ /** Vị trí bắt đầu (character offset) */
359
+ from: number;
360
+ /** Vị trí kết thúc (character offset) */
361
+ to: number;
362
+ /** Số dòng (1-indexed) */
363
+ line: number;
364
+ /** Tên/mô tả của pattern đã bị vi phạm */
365
+ pattern: string;
366
+ /** Đoạn text thực tế bị match */
367
+ matched: string;
105
368
  }
106
369
 
370
+ /** Tùy chỉnh label hiển thị của một ngôn ngữ trong dropdown */
107
371
  export interface ILanguageOptions {
108
- [key: string]: string;
372
+ id: PREVIEW_TEXT_DATA_LANGUAGE_SUPPORT;
373
+ label: string;
109
374
  }
375
+
376
+ /** Pattern cấm: string literal (case-insensitive) hoặc RegExp tùy chỉnh */
377
+ export type T_ForbiddenPattern = string | RegExp;
110
378
  ```
111
379
 
112
- ## Styling
380
+ ## Constants
381
+
382
+ ### DEFAULT_SECURITY_PATTERNS
113
383
 
114
- ### CSS Classes
384
+ Bộ pattern bảo mật mặc định cho low-code platform — chặn các API nguy hiểm:
115
385
 
116
- | Class | Description |
117
- | -------------- | -------------------------------- |
118
- | `.cm-editor` | Class gốc của editor CodeMirror. |
119
- | `.cm-scroller` | Vùng cuộn của editor. |
120
- | `.cm-content` | Vùng chứa nội dung chữ. |
386
+ ```typescript
387
+ import { DEFAULT_SECURITY_PATTERNS } from '@libs-ui/components-preview-text-data';
121
388
 
122
- ## Công nghệ
389
+ // Sử dụng trực tiếp
390
+ [forbiddenPatterns]="DEFAULT_SECURITY_PATTERNS"
391
+ ```
123
392
 
124
- | Technology | Version | Purpose |
125
- | --------------- | ------- | ------------------ |
126
- | Angular | 18+ | Framework |
127
- | CodeMirror | 6.x | Core Editor Engine |
128
- | Angular Signals | - | State management |
129
- | TailwindCSS | 3.x | Styling |
393
+ Các pattern được chặn:
394
+
395
+ | Pattern | Lý do |
396
+ |---|---|
397
+ | `/eval\s*\(/i` | Thực thi code động |
398
+ | `/new\s+Function\s*\(/i` | Tạo hàm từ string |
399
+ | `/document\.cookie/i` | Đọc cookie nhạy cảm |
400
+ | `/localStorage/i` | Truy cập local storage |
401
+ | `/sessionStorage/i` | Truy cập session storage |
402
+ | `/fetch\s*\(/i` | Gọi HTTP request |
403
+ | `/new\s+XMLHttpRequest/i` | Gọi HTTP request cũ |
404
+ | `/require\s*\(/i` | Load module Node.js |
405
+ | `/import\s*\(/i` | Dynamic import |
406
+ | `/process\.env/i` | Đọc biến môi trường |
407
+ | `/<script/i` | Chèn thẻ script |
408
+ | `/document\.write\s*\(/i` | Ghi trực tiếp vào DOM |
409
+ | `/window\.location\s*=/i` | Chuyển hướng trang |
410
+
411
+ ## Ngôn ngữ được hỗ trợ
412
+
413
+ | ID | Nhãn hiển thị | Linter |
414
+ |---|---|---|
415
+ | `text` | Plain Text | Không |
416
+ | `javascript` | JavaScript/TypeScript | Có (JS syntax + trailing dot detection) |
417
+ | `html` | HTML | Không |
418
+ | `css` | CSS/SCSS/SASS | Không |
419
+ | `markdown` | Markdown | Không |
420
+ | `json` | JSON | Có (JSON.parse validation) |
421
+ | `sql` | SQL | Có (syntaxTree parse errors) |
422
+ | `xml` | XML | Không |
423
+ | `yaml` | YAML | Không |
424
+ | `python` | Python | Có (syntaxTree parse errors) |
425
+ | `java` | Java | Không |
426
+ | `cpp` | C/C++ | Không |
427
+ | `php` | PHP | Không |
428
+ | `go` | Go | Không |
429
+
430
+ ## Lưu ý quan trọng
431
+
432
+ ⚠️ **langSelected là bắt buộc**: Input `[(langSelected)]` là `model.required()` — PHẢI truyền giá trị, không có default. Thiếu giá trị sẽ gây lỗi Angular runtime.
433
+
434
+ ⚠️ **Lazy load ngôn ngữ**: Component lazy-load extension của từng ngôn ngữ khi cần. Lần đầu tiên chọn một ngôn ngữ mới có thể có độ trễ nhỏ (vài trăm ms) để tải thư viện từ bundle.
435
+
436
+ ⚠️ **Linter chỉ hoạt động khi editable**: Linter phát hiện lỗi cú pháp ngay cả khi `[editable]="false"`, nhưng người dùng chỉ nhập được khi `[editable]="true"`. Output `(syntaxErrors)` và `(outViolations)` luôn phát ra dù ở chế độ nào.
437
+
438
+ ⚠️ **forbiddenPatterns chặn paste hoàn toàn**: Khi `[forbiddenPatterns]` được cấu hình, mọi thao tác paste chứa pattern bị cấm sẽ bị hủy toàn bộ (không paste được phần nào). Người dùng cần paste từng phần hoặc nhập thủ công.
439
+
440
+ ⚠️ **Dropdown ngôn ngữ bị ẩn tự động**: Khi `[langsAccept]` chứa đúng 1 ngôn ngữ trùng với `langSelected` hiện tại, dropdown chọn ngôn ngữ sẽ bị ẩn tự động (computed `acceptChangeLang = false`).
441
+
442
+ ⚠️ **Cleanup tự động**: Component tự hủy EditorView instance và xóa cache ngôn ngữ khi bị destroy (qua `DestroyRef`). Không cần thao tác cleanup thủ công từ phía consumer.
130
443
 
131
444
  ## Demo
132
445
 
@@ -134,18 +447,17 @@ export interface ILanguageOptions {
134
447
  npx nx serve core-ui
135
448
  ```
136
449
 
137
- Truy cập: `http://localhost:4200/preview-text-data`
450
+ Truy cập: http://localhost:4500/preview-text-data
138
451
 
139
452
  ## Unit Tests
140
453
 
141
454
  ```bash
142
- # Chạy tests
455
+ # Chạy test cho lib
143
456
  npx nx test components-preview-text-data
144
457
 
458
+ # Chạy test một file cụ thể
459
+ npx nx test components-preview-text-data --testFile=libs-ui/components/preview-text-data/src/preview-text-data.component.spec.ts
460
+
145
461
  # Coverage
146
462
  npx nx test components-preview-text-data --coverage
147
463
  ```
148
-
149
- ## License
150
-
151
- MIT