{"version":3,"file":"sdcorejs-angular-components-code-editor.mjs","sources":["../../../projects/sdcorejs-angular/components/code-editor/src/code-editor.component.ts","../../../projects/sdcorejs-angular/components/code-editor/src/code-editor.component.html","../../../projects/sdcorejs-angular/components/code-editor/sdcorejs-angular-components-code-editor.ts"],"sourcesContent":["import { Clipboard } from '@angular/cdk/clipboard';\nimport { CommonModule } from '@angular/common';\nimport {\n  ChangeDetectionStrategy,\n  Component,\n  computed,\n  effect,\n  inject,\n  input,\n  model,\n  signal,\n  untracked,\n  ViewEncapsulation,\n  booleanAttribute,\n} from '@angular/core';\nimport { MatTooltipModule } from '@angular/material/tooltip';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\nimport * as Prism from 'prismjs';\nimport 'prismjs/components/prism-typescript';\nimport 'prismjs/components/prism-json';\nimport 'prismjs/components/prism-css';\nimport 'prismjs/components/prism-scss';\nimport { TranslatePipe } from '@sdcorejs/angular/i18n';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\nimport 'prismjs/components/prism-markup'; // HTML\n\nexport type CodeLanguage = 'html' | 'typescript' | 'json' | 'css' | 'scss';\n\n@Component({\n  selector: 'sd-code-editor',\n  standalone: true,\n  imports: [SdIcon, CommonModule, MatTooltipModule, TranslatePipe],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  templateUrl: './code-editor.component.html',\n  styleUrl: './code-editor.component.scss',\n  encapsulation: ViewEncapsulation.None, // Bắt buộc để nhận màu Prism\n})\nexport class SdCodeEditor {\n  #clipboard = inject(Clipboard);\n  #sanitizer = inject(DomSanitizer);\n\n  // ==========================================\n  // 1. SIGNAL INPUTS & MODEL\n  // ==========================================\n  // Nhận bất kỳ kiểu dữ liệu nào (string, array, object)\n  valueModel = model<any>(undefined, { alias: 'model' });\n\n  language = input<CodeLanguage>('typescript');\n  maxHeight = input<string>('500px');\n\n  // Trạng thái Viewed (true = Read Only, false = Editable)\n  viewed = input(false, { transform: booleanAttribute });\n\n  // ==========================================\n  // 2. INTERNAL STATE\n  // ==========================================\n  copied = signal<boolean>(false);\n\n  // Chuỗi text nội bộ dùng để map với thẻ <textarea>\n  textValue = signal<string>('');\n\n  prismLang = computed(() => (this.language() === 'html' ? 'markup' : this.language()));\n\n  // Cờ lưu vết để chống Loop (Vòng lặp vô tận khi bắn 2 chiều)\n  private _lastEmittedValue: any = undefined;\n\n  constructor() {\n    // ==========================================\n    // EFFECT 1: Dữ liệu từ CHA truyền vào (Model -> TextValue)\n    // ==========================================\n    effect(() => {\n      const extVal = this.valueModel();\n      const lang = this.language();\n\n      untracked(() => {\n        // Nếu giá trị này do chính component bắn ra, bỏ qua để tránh loop\n        if (extVal === this._lastEmittedValue) return;\n\n        if (typeof extVal === 'string') {\n          this.textValue.set(extVal);\n        } else if (extVal !== undefined && extVal !== null) {\n          // Tự động format Object -> String nếu là JSON\n          if (lang === 'json') {\n            try {\n              this.textValue.set(JSON.stringify(extVal, null, 2));\n            } catch {\n              this.textValue.set('// Lỗi: Object có tham chiếu vòng (Circular Reference)');\n            }\n          } else {\n            this.textValue.set(String(extVal));\n          }\n        } else {\n          this.textValue.set('');\n        }\n      });\n    });\n  }\n\n  // ==========================================\n  // EFFECT 2: PrismJS render (TextValue -> HTML MÀU)\n  // ==========================================\n  highlightedCode = computed<SafeHtml>(() => {\n    // Dùng khoảng trắng để giữ độ cao cho thẻ pre nếu rỗng\n    const rawCode = this.textValue() || ' ';\n    const langKey = this.prismLang();\n    const grammar = Prism.languages[langKey] || Prism.languages['markup'];\n\n    // Cộng thêm \\n ở cuối để chống lỗi con trỏ textarea ăn lẹm dòng cuối\n    const highlightedString = Prism.highlight(rawCode, grammar, langKey) + '\\n';\n    return this.#sanitizer.bypassSecurityTrustHtml(highlightedString);\n  });\n\n  // ==========================================\n  // EVENTS\n  // ==========================================\n\n  // Khi người dùng gõ vào Textarea (TextValue -> Model)\n  onTextChange(newText: string) {\n    this.textValue.set(newText);\n\n    let valToEmit: any = newText;\n\n    // Nếu ngôn ngữ là JSON, cố gắng trả về Object thật\n    if (this.language() === 'json') {\n      try {\n        valToEmit = JSON.parse(newText);\n      } catch {\n        // Nếu gõ dở ngoặc/sai cú pháp -> Trả về chuỗi String tạm\n        valToEmit = newText;\n      }\n    }\n\n    // Ghi sổ và bắn ra ngoài\n    this._lastEmittedValue = valToEmit;\n    this.valueModel.set(valToEmit);\n  }\n\n  copyToClipboard() {\n    const rawCode = this.textValue();\n    if (!rawCode) return;\n\n    if (this.#clipboard.copy(rawCode)) {\n      this.copied.set(true);\n      setTimeout(() => this.copied.set(false), 2000);\n    }\n  }\n}\n","<div class=\"sd-code-wrapper\" [class.is-editable]=\"!viewed()\">\n\n  <div class=\"sd-code-header\">\n    <div class=\"mac-dots\">\n      <span class=\"dot red\"></span>\n      <span class=\"dot yellow\"></span>\n      <span class=\"dot green\"></span>\n    </div>\n\n    <div class=\"lang-badge\">\n      {{ language().toUpperCase() }}\n      <span style=\"opacity: 0.6; margin-left: 4px; font-weight: normal;\">\n        {{ viewed() ? '(READ ONLY)' : '(EDITING)' }}\n      </span>\n    </div>\n\n    <button class=\"copy-btn\" [matTooltip]=\"copied() ? ('core.component.code-editor.copied' | translate) : 'Copy code'\" (click)=\"copyToClipboard()\">\n      <sd-icon [class.text-success]=\"copied()\" [name]=\"copied() ? 'check' : 'content_copy'\"></sd-icon>\n      <span>{{ copied() ? 'Copied' : 'Copy' }}</span>\n    </button>\n  </div>\n\n  <div class=\"sd-code-content\" [style.maxHeight]=\"maxHeight()\">\n    <div class=\"editor-overlay-container\">\n\n      @if (!viewed()) {\n        <textarea\n          class=\"code-textarea\"\n          [value]=\"textValue()\"\n          (input)=\"onTextChange($any($event.target).value)\"\n          spellcheck=\"false\"\n          autocomplete=\"off\"\n          autocorrect=\"off\"\n          autocapitalize=\"off\">\n        </textarea>\n      }\n\n      <pre aria-hidden=\"true\" class=\"code-display\"><code [class]=\"'language-' + prismLang()\" [innerHTML]=\"highlightedCode()\"></code></pre>\n\n    </div>\n  </div>\n</div>","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}