@cqa-lib/cqa-ui 1.1.493 → 1.1.494

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.
@@ -19456,8 +19456,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
19456
19456
  type: Input
19457
19457
  }] } });
19458
19458
 
19459
- const VAR_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
19460
- const VAR_TOKEN_REGEX = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
19459
+ const VAR_PATH = '[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*|\\[\\d+\\])*';
19460
+ const VAR_NAME_REGEX = new RegExp('^' + VAR_PATH + '$');
19461
+ const VAR_TOKEN_REGEX = new RegExp('\\$\\{(' + VAR_PATH + ')\\}', 'g');
19462
+ const VAR_PATH_TAIL_REGEX = new RegExp('(' + VAR_PATH + ')$');
19463
+ const INVALID_VAR_MESSAGE = 'Invalid variable name.';
19464
+ const BLUR_HIDE_DELAY_MS = 150;
19461
19465
  class MixedVariableInputComponent {
19462
19466
  constructor(cdr, hostRef) {
19463
19467
  this.cdr = cdr;
@@ -19466,41 +19470,56 @@ class MixedVariableInputComponent {
19466
19470
  this.placeholder = 'Text Input';
19467
19471
  this.disabled = false;
19468
19472
  this.valueChange = new EventEmitter();
19473
+ this.validityChange = new EventEmitter();
19469
19474
  this.showSuggestion = false;
19470
19475
  this.pendingWord = '';
19476
+ this.canAddAsVariable = false;
19477
+ this.errorMessage = null;
19478
+ this.selectionRange = null;
19479
+ this.selectionMode = false;
19471
19480
  this.lastEmitted = '';
19481
+ this.lastValid = null;
19472
19482
  }
19473
- onDocumentMouseDown(event) {
19474
- if (!this.showSuggestion)
19483
+ // ---- Lifecycle ------------------------------------------------------------
19484
+ ngOnChanges(changes) {
19485
+ if (!changes['value'] || !this.editorRef)
19475
19486
  return;
19476
- const target = event.target;
19477
- if (this.hostRef.nativeElement.contains(target))
19487
+ const incoming = changes['value'].currentValue ?? '';
19488
+ if (incoming === this.lastEmitted)
19478
19489
  return;
19479
- this.hideSuggestion();
19480
- }
19481
- ngOnChanges(changes) {
19482
- if (changes['value'] && this.editorRef) {
19483
- const incoming = changes['value'].currentValue ?? '';
19484
- if (incoming !== this.lastEmitted) {
19485
- this.render(incoming);
19486
- }
19487
- }
19490
+ this.render(incoming);
19491
+ this.validate(incoming);
19488
19492
  }
19489
19493
  ngAfterViewInit() {
19490
- this.render(this.value || '');
19494
+ const initial = this.value || '';
19495
+ this.render(initial);
19496
+ this.validate(initial);
19491
19497
  }
19498
+ // ---- Editor event handlers -----------------------------------------------
19492
19499
  onInput() {
19493
- const str = this.serialize();
19494
- this.lastEmitted = str;
19495
- this.valueChange.emit(str);
19500
+ this.flattenStructure();
19501
+ this.sanitizeChips();
19496
19502
  this.autoChipify();
19503
+ this.emitValue();
19497
19504
  this.refreshSuggestion();
19498
19505
  }
19506
+ onEditorFocus() {
19507
+ this.refreshSuggestion();
19508
+ }
19509
+ onEditorBlur() {
19510
+ // Delay so clicks on suggestion buttons register before we hide them.
19511
+ setTimeout(() => {
19512
+ this.autoChipify(true);
19513
+ this.hideSuggestion();
19514
+ }, BLUR_HIDE_DELAY_MS);
19515
+ }
19499
19516
  onKeyDown(event) {
19500
19517
  if (event.key === 'Enter') {
19501
19518
  event.preventDefault();
19502
19519
  return;
19503
19520
  }
19521
+ if (event.key === 'Tab')
19522
+ return;
19504
19523
  if (event.key === 'Escape') {
19505
19524
  if (this.showSuggestion) {
19506
19525
  event.preventDefault();
@@ -19517,36 +19536,119 @@ class MixedVariableInputComponent {
19517
19536
  }
19518
19537
  }
19519
19538
  }
19520
- onEditorBlur() {
19521
- // Small timeout so clicks on suggestion buttons register before hiding.
19522
- setTimeout(() => this.hideSuggestion(), 150);
19539
+ onPaste(event) {
19540
+ event.preventDefault();
19541
+ const text = event.clipboardData?.getData('text/plain') ?? '';
19542
+ if (!text)
19543
+ return;
19544
+ this.insertPlainTextAtCaret(text);
19545
+ this.onInput();
19546
+ }
19547
+ onDrop(event) {
19548
+ event.preventDefault();
19549
+ const text = event.dataTransfer?.getData('text/plain') ?? '';
19550
+ if (text) {
19551
+ this.editorRef.nativeElement.focus();
19552
+ this.insertPlainTextAtCaret(text);
19553
+ }
19554
+ this.onInput();
19555
+ }
19556
+ onDragStart(event) {
19557
+ event.preventDefault();
19558
+ }
19559
+ onDocumentMouseDown(event) {
19560
+ if (!this.showSuggestion)
19561
+ return;
19562
+ if (this.hostRef.nativeElement.contains(event.target))
19563
+ return;
19564
+ this.hideSuggestion();
19565
+ }
19566
+ onDocumentSelectionChange() {
19567
+ if (document.activeElement !== this.editorRef?.nativeElement)
19568
+ return;
19569
+ this.refreshSuggestion();
19523
19570
  }
19571
+ // ---- Suggestion actions ---------------------------------------------------
19524
19572
  addAsText() {
19525
19573
  this.hideSuggestion();
19526
19574
  this.editorRef.nativeElement.focus();
19527
19575
  }
19528
19576
  addAsVariable() {
19529
- const name = (this.pendingWord || '').trim();
19577
+ const name = this.normalizeCandidate(this.pendingWord || '');
19530
19578
  if (!name || !VAR_NAME_REGEX.test(name)) {
19531
19579
  this.hideSuggestion();
19532
19580
  return;
19533
19581
  }
19534
- this.insertChipReplacingCurrentWord(name);
19582
+ if (this.selectionMode && this.selectionRange) {
19583
+ if (this.rangeCrossesChip(this.selectionRange)) {
19584
+ this.hideSuggestion();
19585
+ return;
19586
+ }
19587
+ this.insertChipReplacingRange(name, this.selectionRange);
19588
+ }
19589
+ else {
19590
+ this.insertChipReplacingCurrentWord(name);
19591
+ }
19535
19592
  this.hideSuggestion();
19593
+ this.emitValue();
19594
+ }
19595
+ // ---- Value emission / validation -----------------------------------------
19596
+ emitValue() {
19536
19597
  const str = this.serialize();
19537
- this.lastEmitted = str;
19538
- this.valueChange.emit(str);
19598
+ if (str !== this.lastEmitted) {
19599
+ this.lastEmitted = str;
19600
+ this.valueChange.emit(str);
19601
+ }
19602
+ this.validate(str);
19603
+ }
19604
+ validate(str) {
19605
+ const error = this.findValidationError(str);
19606
+ const valid = error === null;
19607
+ if (this.lastValid === valid && this.errorMessage === error)
19608
+ return;
19609
+ this.lastValid = valid;
19610
+ this.errorMessage = error;
19611
+ this.validityChange.emit({ valid, error });
19612
+ this.cdr.markForCheck();
19613
+ }
19614
+ findValidationError(str) {
19615
+ if (!str)
19616
+ return null;
19617
+ const braceBody = /\$\{([^}]*)\}/g;
19618
+ let m;
19619
+ braceBody.lastIndex = 0;
19620
+ while ((m = braceBody.exec(str)) !== null) {
19621
+ const body = m[1];
19622
+ if (body !== body.trim() || !VAR_NAME_REGEX.test(body)) {
19623
+ return INVALID_VAR_MESSAGE;
19624
+ }
19625
+ }
19626
+ const openCount = (str.match(/\$\{/g) || []).length;
19627
+ const closedCount = (str.match(braceBody) || []).length;
19628
+ if (openCount > closedCount)
19629
+ return INVALID_VAR_MESSAGE;
19630
+ return null;
19539
19631
  }
19632
+ // ---- Suggestion state -----------------------------------------------------
19540
19633
  refreshSuggestion() {
19541
- const word = this.getCurrentWord();
19542
- if (word && VAR_NAME_REGEX.test(word)) {
19543
- this.pendingWord = word;
19634
+ const selInfo = this.getSelectedInfo();
19635
+ if (selInfo) {
19636
+ const normalized = this.normalizeCandidate(selInfo.text);
19637
+ this.pendingWord = normalized;
19638
+ this.selectionRange = selInfo.range;
19639
+ this.selectionMode = true;
19544
19640
  this.showSuggestion = true;
19641
+ this.canAddAsVariable =
19642
+ !!normalized && VAR_NAME_REGEX.test(normalized) && !this.rangeCrossesChip(selInfo.range);
19643
+ this.cdr.markForCheck();
19644
+ return;
19545
19645
  }
19546
- else {
19547
- this.pendingWord = '';
19548
- this.showSuggestion = false;
19549
- }
19646
+ this.selectionMode = false;
19647
+ this.selectionRange = null;
19648
+ const word = this.getCurrentWord();
19649
+ this.pendingWord = word;
19650
+ this.showSuggestion = !!word;
19651
+ this.canAddAsVariable = !!word && VAR_NAME_REGEX.test(word);
19550
19652
  this.cdr.markForCheck();
19551
19653
  }
19552
19654
  hideSuggestion() {
@@ -19554,45 +19656,126 @@ class MixedVariableInputComponent {
19554
19656
  return;
19555
19657
  this.showSuggestion = false;
19556
19658
  this.pendingWord = '';
19659
+ this.canAddAsVariable = false;
19557
19660
  this.cdr.markForCheck();
19558
19661
  }
19559
- autoChipify() {
19662
+ normalizeCandidate(text) {
19663
+ let t = text.trim();
19664
+ if (t.startsWith('${') && t.endsWith('}')) {
19665
+ t = t.slice(2, -1).trim();
19666
+ }
19667
+ return t;
19668
+ }
19669
+ // ---- DOM sanitization -----------------------------------------------------
19670
+ flattenStructure() {
19560
19671
  const editor = this.editorRef.nativeElement;
19561
- const textNodes = [];
19562
- editor.childNodes.forEach((n) => {
19563
- if (n.nodeType === Node.TEXT_NODE)
19564
- textNodes.push(n);
19565
- });
19566
- for (const tn of textNodes) {
19672
+ const wrappers = Array.from(editor.querySelectorAll('div, p'));
19673
+ const strayInlines = Array.from(editor.querySelectorAll('span')).filter((s) => !s.classList.contains('cqa-var-chip') && !s.closest('.cqa-var-chip'));
19674
+ for (const el of [...wrappers, ...strayInlines]) {
19675
+ if (el.closest('.cqa-var-chip'))
19676
+ continue;
19677
+ while (el.firstChild)
19678
+ el.parentNode?.insertBefore(el.firstChild, el);
19679
+ el.remove();
19680
+ }
19681
+ const brs = Array.from(editor.querySelectorAll('br'));
19682
+ for (const br of brs) {
19683
+ if (br.closest('.cqa-var-chip'))
19684
+ continue;
19685
+ br.parentNode?.replaceChild(document.createTextNode(' '), br);
19686
+ }
19687
+ }
19688
+ sanitizeChips() {
19689
+ const editor = this.editorRef.nativeElement;
19690
+ const chips = Array.from(editor.querySelectorAll('.cqa-var-chip'));
19691
+ for (const chip of chips) {
19692
+ const name = (chip.getAttribute('data-var') || '').trim();
19693
+ if (!name || !VAR_NAME_REGEX.test(name)) {
19694
+ chip.parentNode?.replaceChild(document.createTextNode(chip.textContent || ''), chip);
19695
+ continue;
19696
+ }
19697
+ const structureBroken = chip.getAttribute('contenteditable') !== 'false' ||
19698
+ !chip.querySelector('.cqa-var-chip__remove');
19699
+ if (structureBroken) {
19700
+ chip.parentNode?.replaceChild(this.buildChip(name), chip);
19701
+ }
19702
+ }
19703
+ // Remove stray remove icons left from partial undo/redo states.
19704
+ const orphans = Array.from(editor.querySelectorAll('.cqa-var-chip__remove'));
19705
+ for (const o of orphans) {
19706
+ if (!o.closest('.cqa-var-chip'))
19707
+ o.remove();
19708
+ }
19709
+ }
19710
+ // ---- Chipification --------------------------------------------------------
19711
+ autoChipify(force = false) {
19712
+ const editor = this.editorRef.nativeElement;
19713
+ const caretRange = this.getSelectionRange();
19714
+ let changed = false;
19715
+ let lastChip = null;
19716
+ for (const tn of this.collectDescendantTextNodes(editor)) {
19717
+ if (!tn.parentNode)
19718
+ continue;
19567
19719
  const text = tn.nodeValue || '';
19568
19720
  VAR_TOKEN_REGEX.lastIndex = 0;
19569
19721
  if (!VAR_TOKEN_REGEX.test(text))
19570
19722
  continue;
19571
19723
  VAR_TOKEN_REGEX.lastIndex = 0;
19572
- const parent = tn.parentNode;
19573
- if (!parent)
19574
- continue;
19575
- const frag = document.createDocumentFragment();
19576
- let lastIndex = 0;
19724
+ const caretOffset = !force && caretRange && caretRange.collapsed && caretRange.startContainer === tn
19725
+ ? caretRange.startOffset
19726
+ : -1;
19727
+ const matches = [];
19577
19728
  let m;
19578
19729
  while ((m = VAR_TOKEN_REGEX.exec(text)) !== null) {
19579
- if (m.index > lastIndex) {
19580
- frag.appendChild(document.createTextNode(text.slice(lastIndex, m.index)));
19581
- }
19582
- frag.appendChild(this.buildChip(m[1]));
19583
- lastIndex = m.index + m[0].length;
19584
- }
19585
- if (lastIndex < text.length) {
19586
- frag.appendChild(document.createTextNode(text.slice(lastIndex)));
19730
+ const start = m.index;
19731
+ const end = start + m[0].length;
19732
+ // Keep the token as plain text while the caret is strictly inside.
19733
+ if (caretOffset > start && caretOffset < end)
19734
+ continue;
19735
+ matches.push({ start, end, name: m[1] });
19587
19736
  }
19588
- parent.replaceChild(frag, tn);
19589
- this.placeCaretAtEnd();
19590
- const str = this.serialize();
19591
- this.lastEmitted = str;
19592
- this.valueChange.emit(str);
19593
- return;
19594
- }
19737
+ if (!matches.length)
19738
+ continue;
19739
+ const frag = document.createDocumentFragment();
19740
+ let cursor = 0;
19741
+ for (const mt of matches) {
19742
+ if (mt.start > cursor) {
19743
+ frag.appendChild(document.createTextNode(text.slice(cursor, mt.start)));
19744
+ }
19745
+ const chip = this.buildChip(mt.name);
19746
+ frag.appendChild(chip);
19747
+ lastChip = chip;
19748
+ cursor = mt.end;
19749
+ }
19750
+ if (cursor < text.length) {
19751
+ frag.appendChild(document.createTextNode(text.slice(cursor)));
19752
+ }
19753
+ tn.parentNode.replaceChild(frag, tn);
19754
+ changed = true;
19755
+ }
19756
+ if (changed && lastChip)
19757
+ this.placeCaretAfter(lastChip);
19758
+ }
19759
+ collectDescendantTextNodes(root) {
19760
+ const out = [];
19761
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
19762
+ acceptNode: (node) => {
19763
+ let p = node.parentNode;
19764
+ while (p && p !== root) {
19765
+ if (p.classList?.contains?.('cqa-var-chip')) {
19766
+ return NodeFilter.FILTER_REJECT;
19767
+ }
19768
+ p = p.parentNode;
19769
+ }
19770
+ return NodeFilter.FILTER_ACCEPT;
19771
+ },
19772
+ });
19773
+ let n;
19774
+ while ((n = walker.nextNode()))
19775
+ out.push(n);
19776
+ return out;
19595
19777
  }
19778
+ // ---- Render / serialize ---------------------------------------------------
19596
19779
  render(value) {
19597
19780
  const editor = this.editorRef.nativeElement;
19598
19781
  editor.innerHTML = '';
@@ -19615,22 +19798,30 @@ class MixedVariableInputComponent {
19615
19798
  serialize() {
19616
19799
  const editor = this.editorRef.nativeElement;
19617
19800
  let out = '';
19618
- editor.childNodes.forEach((n) => {
19619
- if (n.nodeType === Node.TEXT_NODE) {
19620
- out += (n.nodeValue || '').replace(/\u00a0/g, ' ');
19801
+ const walk = (node) => {
19802
+ if (node.nodeType === Node.TEXT_NODE) {
19803
+ out += (node.nodeValue || '').replace(/\u00a0/g, ' ');
19804
+ return;
19621
19805
  }
19622
- else if (n.nodeType === Node.ELEMENT_NODE) {
19623
- const el = n;
19624
- if (el.classList.contains('cqa-var-chip')) {
19625
- const name = el.getAttribute('data-var') || '';
19626
- if (name)
19627
- out += '${' + name + '}';
19628
- }
19629
- else {
19630
- out += el.textContent || '';
19631
- }
19806
+ if (node.nodeType !== Node.ELEMENT_NODE)
19807
+ return;
19808
+ const el = node;
19809
+ if (el.classList.contains('cqa-var-chip')) {
19810
+ const name = el.getAttribute('data-var') || '';
19811
+ if (name)
19812
+ out += '${' + name + '}';
19813
+ return;
19632
19814
  }
19633
- });
19815
+ if (el.tagName === 'BR') {
19816
+ out += '\n';
19817
+ return;
19818
+ }
19819
+ const isBlock = el.tagName === 'DIV' || el.tagName === 'P';
19820
+ if (isBlock && out.length > 0 && !out.endsWith('\n'))
19821
+ out += '\n';
19822
+ el.childNodes.forEach(walk);
19823
+ };
19824
+ editor.childNodes.forEach(walk);
19634
19825
  return out;
19635
19826
  }
19636
19827
  buildChip(name) {
@@ -19655,6 +19846,7 @@ class MixedVariableInputComponent {
19655
19846
  chip.appendChild(remove);
19656
19847
  return chip;
19657
19848
  }
19849
+ // ---- Selection / caret helpers -------------------------------------------
19658
19850
  getSelectionRange() {
19659
19851
  const sel = window.getSelection();
19660
19852
  if (!sel || sel.rangeCount === 0)
@@ -19664,6 +19856,25 @@ class MixedVariableInputComponent {
19664
19856
  return null;
19665
19857
  return range;
19666
19858
  }
19859
+ getSelectedInfo() {
19860
+ const sel = window.getSelection();
19861
+ if (!sel || sel.rangeCount === 0)
19862
+ return null;
19863
+ const range = sel.getRangeAt(0);
19864
+ if (range.collapsed)
19865
+ return null;
19866
+ const editor = this.editorRef.nativeElement;
19867
+ if (!editor.contains(range.startContainer) || !editor.contains(range.endContainer))
19868
+ return null;
19869
+ const text = range.toString();
19870
+ if (!text)
19871
+ return null;
19872
+ return { text, range: range.cloneRange() };
19873
+ }
19874
+ rangeCrossesChip(range) {
19875
+ const contents = range.cloneContents();
19876
+ return !!contents.querySelector?.('.cqa-var-chip');
19877
+ }
19667
19878
  getNodeBeforeCaret() {
19668
19879
  const range = this.getSelectionRange();
19669
19880
  if (!range || !range.collapsed)
@@ -19678,16 +19889,39 @@ class MixedVariableInputComponent {
19678
19889
  }
19679
19890
  getCurrentWord() {
19680
19891
  const range = this.getSelectionRange();
19681
- if (!range)
19682
- return '';
19683
- const node = range.startContainer;
19684
- if (node.nodeType !== Node.TEXT_NODE)
19892
+ if (!range || !range.collapsed)
19685
19893
  return '';
19894
+ let node = range.startContainer;
19895
+ let offset = range.startOffset;
19896
+ if (node.nodeType !== Node.TEXT_NODE) {
19897
+ const prev = node.childNodes[offset - 1];
19898
+ if (!prev || prev.nodeType !== Node.TEXT_NODE)
19899
+ return '';
19900
+ node = prev;
19901
+ offset = (prev.nodeValue || '').length;
19902
+ }
19686
19903
  const text = node.nodeValue || '';
19687
- const before = text.slice(0, range.startOffset);
19688
- const match = before.match(/([A-Za-z_][A-Za-z0-9_]*)$/);
19904
+ const braceOpen = text.lastIndexOf('${', offset - 1);
19905
+ if (braceOpen !== -1) {
19906
+ const closeIdx = text.indexOf('}', braceOpen + 2);
19907
+ const tokenClosedBeforeCaret = closeIdx !== -1 && closeIdx < offset;
19908
+ if (!tokenClosedBeforeCaret) {
19909
+ const start = braceOpen + 2;
19910
+ const end = closeIdx === -1 ? text.length : closeIdx;
19911
+ return text.slice(start, end);
19912
+ }
19913
+ }
19914
+ const match = text.slice(0, offset).match(VAR_PATH_TAIL_REGEX);
19689
19915
  return match ? match[1] : '';
19690
19916
  }
19917
+ // ---- Chip insertion -------------------------------------------------------
19918
+ insertChipReplacingRange(name, range) {
19919
+ const chip = this.buildChip(name);
19920
+ range.deleteContents();
19921
+ range.insertNode(chip);
19922
+ this.stripBracesAround(chip);
19923
+ this.placeCaretAfter(chip);
19924
+ }
19691
19925
  insertChipReplacingCurrentWord(name) {
19692
19926
  const range = this.getSelectionRange();
19693
19927
  const chip = this.buildChip(name);
@@ -19697,26 +19931,59 @@ class MixedVariableInputComponent {
19697
19931
  return;
19698
19932
  }
19699
19933
  const node = range.startContainer;
19700
- if (node.nodeType === Node.TEXT_NODE) {
19701
- const text = node.nodeValue || '';
19702
- const before = text.slice(0, range.startOffset);
19703
- const after = text.slice(range.startOffset);
19704
- const wordMatch = before.match(/([A-Za-z_][A-Za-z0-9_]*)$/);
19705
- const beforeKept = wordMatch ? before.slice(0, before.length - wordMatch[1].length) : before;
19706
- const parent = node.parentNode;
19707
- const frag = document.createDocumentFragment();
19708
- if (beforeKept)
19709
- frag.appendChild(document.createTextNode(beforeKept));
19710
- frag.appendChild(chip);
19711
- if (after)
19712
- frag.appendChild(document.createTextNode(after));
19713
- parent.replaceChild(frag, node);
19714
- }
19715
- else {
19934
+ if (node.nodeType !== Node.TEXT_NODE) {
19716
19935
  range.insertNode(chip);
19936
+ this.placeCaretAfter(chip);
19937
+ return;
19717
19938
  }
19939
+ const text = node.nodeValue || '';
19940
+ const before = text.slice(0, range.startOffset);
19941
+ const after = text.slice(range.startOffset);
19942
+ const wordMatch = before.match(VAR_PATH_TAIL_REGEX);
19943
+ let beforeKept = wordMatch ? before.slice(0, before.length - wordMatch[1].length) : before;
19944
+ let afterKept = after;
19945
+ if (beforeKept.endsWith('${') && afterKept.startsWith('}')) {
19946
+ beforeKept = beforeKept.slice(0, -2);
19947
+ afterKept = afterKept.slice(1);
19948
+ }
19949
+ else if (beforeKept.endsWith('${')) {
19950
+ beforeKept = beforeKept.slice(0, -2);
19951
+ }
19952
+ const frag = document.createDocumentFragment();
19953
+ if (beforeKept)
19954
+ frag.appendChild(document.createTextNode(beforeKept));
19955
+ frag.appendChild(chip);
19956
+ if (afterKept)
19957
+ frag.appendChild(document.createTextNode(afterKept));
19958
+ node.parentNode.replaceChild(frag, node);
19718
19959
  this.placeCaretAfter(chip);
19719
19960
  }
19961
+ stripBracesAround(chip) {
19962
+ const prev = chip.previousSibling;
19963
+ const next = chip.nextSibling;
19964
+ if (prev && prev.nodeType === Node.TEXT_NODE) {
19965
+ const t = prev.nodeValue || '';
19966
+ if (t.endsWith('${'))
19967
+ prev.nodeValue = t.slice(0, -2);
19968
+ }
19969
+ if (next && next.nodeType === Node.TEXT_NODE) {
19970
+ const t = next.nodeValue || '';
19971
+ if (t.startsWith('}'))
19972
+ next.nodeValue = t.slice(1);
19973
+ }
19974
+ }
19975
+ insertPlainTextAtCaret(text) {
19976
+ const range = this.getSelectionRange();
19977
+ if (!range) {
19978
+ this.editorRef.nativeElement.appendChild(document.createTextNode(text));
19979
+ this.placeCaretAtEnd();
19980
+ return;
19981
+ }
19982
+ range.deleteContents();
19983
+ const node = document.createTextNode(text);
19984
+ range.insertNode(node);
19985
+ this.placeCaretAfter(node);
19986
+ }
19720
19987
  placeCaretAfter(node) {
19721
19988
  const sel = window.getSelection();
19722
19989
  if (!sel)
@@ -19728,22 +19995,21 @@ class MixedVariableInputComponent {
19728
19995
  sel.addRange(range);
19729
19996
  }
19730
19997
  placeCaretAtEnd() {
19731
- const editor = this.editorRef.nativeElement;
19732
19998
  const sel = window.getSelection();
19733
19999
  if (!sel)
19734
20000
  return;
19735
20001
  const range = document.createRange();
19736
- range.selectNodeContents(editor);
20002
+ range.selectNodeContents(this.editorRef.nativeElement);
19737
20003
  range.collapse(false);
19738
20004
  sel.removeAllRanges();
19739
20005
  sel.addRange(range);
19740
20006
  }
19741
20007
  }
19742
20008
  MixedVariableInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MixedVariableInputComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
19743
- MixedVariableInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: MixedVariableInputComponent, selector: "cqa-mixed-variable-input", inputs: { value: "value", placeholder: "placeholder", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event)" }, classAttribute: "cqa-ui-root" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-mixed-var-wrapper\">\n <div\n #editor\n class=\"cqa-mixed-var-editor\"\n role=\"textbox\"\n spellcheck=\"false\"\n [attr.contenteditable]=\"!disabled\"\n [attr.data-placeholder]=\"placeholder\"\n (input)=\"onInput()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onEditorBlur()\">\n </div>\n\n <div\n *ngIf=\"showSuggestion\"\n class=\"cqa-mixed-var-suggest\"\n (mousedown)=\"$event.preventDefault()\">\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item\" (click)=\"addAsText()\">\n Add as Text <span class=\"cqa-mixed-var-suggest__hint\">\"{{ pendingWord }}\"</span>\n </button>\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item cqa-mixed-var-suggest__item--primary\" (click)=\"addAsVariable()\">\n Add as local variable <span class=\"cqa-mixed-var-suggest__hint\">${{ '{' }}{{ pendingWord }}{{ '}' }}</span>\n </button>\n </div>\n</div>\n", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
20009
+ MixedVariableInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: MixedVariableInputComponent, selector: "cqa-mixed-variable-input", inputs: { value: "value", placeholder: "placeholder", disabled: "disabled" }, outputs: { valueChange: "valueChange", validityChange: "validityChange" }, host: { listeners: { "dragstart": "onDragStart($event)", "document:mousedown": "onDocumentMouseDown($event)", "document:selectionchange": "onDocumentSelectionChange()" }, classAttribute: "cqa-ui-root" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-mixed-var-wrapper\">\n <div\n #editor\n class=\"cqa-mixed-var-editor\"\n role=\"textbox\"\n spellcheck=\"false\"\n [attr.contenteditable]=\"!disabled\"\n [attr.data-placeholder]=\"placeholder\"\n [attr.aria-invalid]=\"errorMessage ? 'true' : null\"\n [class.cqa-mixed-var-editor--invalid]=\"errorMessage\"\n (input)=\"onInput()\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"onEditorFocus()\"\n (paste)=\"onPaste($event)\"\n (drop)=\"onDrop($event)\"\n (blur)=\"onEditorBlur()\">\n </div>\n <p *ngIf=\"errorMessage\" class=\"cqa-mixed-var-error\" role=\"alert\">{{ errorMessage }}</p>\n\n <div\n *ngIf=\"showSuggestion\"\n class=\"cqa-mixed-var-suggest\"\n (mousedown)=\"$event.preventDefault()\">\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item\" (click)=\"addAsText()\">\n Add as Text <span *ngIf=\"pendingWord\" class=\"cqa-mixed-var-suggest__hint\">\"{{ pendingWord }}\"</span>\n </button>\n <button *ngIf=\"canAddAsVariable\" type=\"button\"\n class=\"cqa-mixed-var-suggest__item cqa-mixed-var-suggest__item--primary\"\n (click)=\"addAsVariable()\">\n Add as local variable <span class=\"cqa-mixed-var-suggest__hint\">${{ '{' }}{{ pendingWord }}{{ '}' }}</span>\n </button>\n </div>\n</div>\n", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19744
20010
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: MixedVariableInputComponent, decorators: [{
19745
20011
  type: Component,
19746
- args: [{ selector: 'cqa-mixed-variable-input', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'cqa-ui-root' }, template: "<div class=\"cqa-mixed-var-wrapper\">\n <div\n #editor\n class=\"cqa-mixed-var-editor\"\n role=\"textbox\"\n spellcheck=\"false\"\n [attr.contenteditable]=\"!disabled\"\n [attr.data-placeholder]=\"placeholder\"\n (input)=\"onInput()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onEditorBlur()\">\n </div>\n\n <div\n *ngIf=\"showSuggestion\"\n class=\"cqa-mixed-var-suggest\"\n (mousedown)=\"$event.preventDefault()\">\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item\" (click)=\"addAsText()\">\n Add as Text <span class=\"cqa-mixed-var-suggest__hint\">\"{{ pendingWord }}\"</span>\n </button>\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item cqa-mixed-var-suggest__item--primary\" (click)=\"addAsVariable()\">\n Add as local variable <span class=\"cqa-mixed-var-suggest__hint\">${{ '{' }}{{ pendingWord }}{{ '}' }}</span>\n </button>\n </div>\n</div>\n", styles: [] }]
20012
+ args: [{ selector: 'cqa-mixed-variable-input', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'cqa-ui-root' }, template: "<div class=\"cqa-mixed-var-wrapper\">\n <div\n #editor\n class=\"cqa-mixed-var-editor\"\n role=\"textbox\"\n spellcheck=\"false\"\n [attr.contenteditable]=\"!disabled\"\n [attr.data-placeholder]=\"placeholder\"\n [attr.aria-invalid]=\"errorMessage ? 'true' : null\"\n [class.cqa-mixed-var-editor--invalid]=\"errorMessage\"\n (input)=\"onInput()\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"onEditorFocus()\"\n (paste)=\"onPaste($event)\"\n (drop)=\"onDrop($event)\"\n (blur)=\"onEditorBlur()\">\n </div>\n <p *ngIf=\"errorMessage\" class=\"cqa-mixed-var-error\" role=\"alert\">{{ errorMessage }}</p>\n\n <div\n *ngIf=\"showSuggestion\"\n class=\"cqa-mixed-var-suggest\"\n (mousedown)=\"$event.preventDefault()\">\n <button type=\"button\" class=\"cqa-mixed-var-suggest__item\" (click)=\"addAsText()\">\n Add as Text <span *ngIf=\"pendingWord\" class=\"cqa-mixed-var-suggest__hint\">\"{{ pendingWord }}\"</span>\n </button>\n <button *ngIf=\"canAddAsVariable\" type=\"button\"\n class=\"cqa-mixed-var-suggest__item cqa-mixed-var-suggest__item--primary\"\n (click)=\"addAsVariable()\">\n Add as local variable <span class=\"cqa-mixed-var-suggest__hint\">${{ '{' }}{{ pendingWord }}{{ '}' }}</span>\n </button>\n </div>\n</div>\n", styles: [] }]
19747
20013
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { value: [{
19748
20014
  type: Input
19749
20015
  }], placeholder: [{
@@ -19752,12 +20018,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
19752
20018
  type: Input
19753
20019
  }], valueChange: [{
19754
20020
  type: Output
20021
+ }], validityChange: [{
20022
+ type: Output
19755
20023
  }], editorRef: [{
19756
20024
  type: ViewChild,
19757
20025
  args: ['editor', { static: true }]
20026
+ }], onDragStart: [{
20027
+ type: HostListener,
20028
+ args: ['dragstart', ['$event']]
19758
20029
  }], onDocumentMouseDown: [{
19759
20030
  type: HostListener,
19760
20031
  args: ['document:mousedown', ['$event']]
20032
+ }], onDocumentSelectionChange: [{
20033
+ type: HostListener,
20034
+ args: ['document:selectionchange']
19761
20035
  }] } });
19762
20036
 
19763
20037
  class CustomTextareaComponent {
@@ -32672,6 +32946,10 @@ class TemplateVariablesFormComponent {
32672
32946
  { id: 'environment', value: 'environment', name: '*|Environment|', label: '*|Environment|' }
32673
32947
  ];
32674
32948
  this.createElementVisible = false;
32949
+ // Tracks active variable-syntax errors by variable name. The validator below
32950
+ // reads from this map so errors survive control re-validation on setValue().
32951
+ this.variableSyntaxErrors = new Map();
32952
+ this.controlsWithSyntaxValidator = new WeakSet();
32675
32953
  }
32676
32954
  onCreateElement(payload) {
32677
32955
  this.createElement.emit(payload);
@@ -34036,6 +34314,30 @@ class TemplateVariablesFormComponent {
34036
34314
  // Emit the search query to parent component
34037
34315
  this.searchElements.emit(event.query);
34038
34316
  }
34317
+ makeSyntaxValidator(variableName) {
34318
+ return () => {
34319
+ const err = this.variableSyntaxErrors.get(variableName);
34320
+ return err ? { invalidVariableSyntax: err } : null;
34321
+ };
34322
+ }
34323
+ onTestDataValidityChange(variableName, event) {
34324
+ const valueControl = this.getVariableFormGroup(variableName)?.get('value');
34325
+ if (!valueControl)
34326
+ return;
34327
+ if (event.valid) {
34328
+ this.variableSyntaxErrors.delete(variableName);
34329
+ }
34330
+ else {
34331
+ this.variableSyntaxErrors.set(variableName, event.error || 'Invalid variable name.');
34332
+ }
34333
+ if (!this.controlsWithSyntaxValidator.has(valueControl)) {
34334
+ valueControl.addValidators(this.makeSyntaxValidator(variableName));
34335
+ this.controlsWithSyntaxValidator.add(valueControl);
34336
+ }
34337
+ valueControl.updateValueAndValidity({ emitEvent: false });
34338
+ valueControl.markAsTouched();
34339
+ this.cdr.markForCheck();
34340
+ }
34039
34341
  onTestDataValueChange(variableName, rawValue) {
34040
34342
  // Find the variable
34041
34343
  const variable = this.templateVariables.find(v => v.name === variableName);
@@ -35178,10 +35480,10 @@ class TemplateVariablesFormComponent {
35178
35480
  }
35179
35481
  }
35180
35482
  TemplateVariablesFormComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TemplateVariablesFormComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
35181
- TemplateVariablesFormComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TemplateVariablesFormComponent, selector: "cqa-template-variables-form", inputs: { templateVariables: "templateVariables", variablesForm: "variablesForm", metadata: "metadata", description: "description", elementOptions: "elementOptions", hasMoreElements: "hasMoreElements", isLoadingElements: "isLoadingElements", screenNameOptions: "screenNameOptions", hasMoreScreenNames: "hasMoreScreenNames", isLoadingScreenNames: "isLoadingScreenNames", parameterOptions: "parameterOptions", hasMoreParameters: "hasMoreParameters", isLoadingParameters: "isLoadingParameters", uploadOptions: "uploadOptions", hasMoreUploads: "hasMoreUploads", isLoadingUploads: "isLoadingUploads", environmentOptions: "environmentOptions", hasMoreEnvironments: "hasMoreEnvironments", isLoadingEnvironments: "isLoadingEnvironments", defaultTestDataProfileId: "defaultTestDataProfileId", defaultTestDataStartIndex: "defaultTestDataStartIndex", defaultTestDataEndIndex: "defaultTestDataEndIndex", isInsideForLoopStep: "isInsideForLoopStep", hasTestDataProfile: "hasTestDataProfile", isEditInDepth: "isEditInDepth", isDebug: "isDebug", createElementVisible: "createElementVisible" }, outputs: { variableValueChange: "variableValueChange", variableBooleanChange: "variableBooleanChange", metadataChange: "metadataChange", descriptionChange: "descriptionChange", loadMoreElements: "loadMoreElements", searchElements: "searchElements", searchElementsByScreenName: "searchElementsByScreenName", createElement: "createElement", searchScreenName: "searchScreenName", loadMoreScreenNames: "loadMoreScreenNames", createScreenNameRequest: "createScreenNameRequest", searchParameters: "searchParameters", loadMoreParameters: "loadMoreParameters", searchUploads: "searchUploads", loadMoreUploads: "loadMoreUploads", searchEnvironments: "searchEnvironments", loadMoreEnvironments: "loadMoreEnvironments", cancelElementForm: "cancelElementForm", elementFormVisibilityChange: "elementFormVisibilityChange" }, host: { classAttribute: "cqa-ui-root" }, usesOnChanges: true, ngImport: i0, template: "<style>\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n</style>\n<div class=\"cqa-flex cqa-gap-x-6 cqa-gap-y-4 cqa-flex-wrap template-variables-form\" [ngClass]=\"{'cqa-flex-col': isEditInDepth}\" *ngIf=\"!createElementVisible\">\n \n <!-- Metadata - Only show if element is available in form -->\n <div *ngIf=\"selectorVariableAvailable\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Metadata\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"metadata\" [fullWidth]=\"true\"\n (valueChange)=\"metadataChange.emit($event)\">\n </cqa-custom-input>\n </div>\n\n <!-- Description -->\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Description\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"description\" [fullWidth]=\"true\"\n (valueChange)=\"descriptionChange.emit($event)\">\n </cqa-custom-input>\n </div>\n \n <ng-container *ngFor=\"let variable of templateVariables; let i = index; trackBy: trackByVariable\">\n <!-- Boolean variables with mat-slide-toggle -->\n <ng-container *ngIf=\"variable.type === 'boolean'\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <mat-slide-toggle [checked]=\"variablesForm.at(i)?.get('value')?.value || variable.value || false\"\n (change)=\"onVariableBooleanChange(variable.name, $event.checked)\" color=\"primary\">\n </mat-slide-toggle>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"variable.type === 'dropdown'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n\n <!-- Textarea variables -->\n <ng-container *ngIf=\"variable.type === 'textarea'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <textarea class=\"cqa-w-full cqa-border cqa-border-gray-300 cqa-rounded-lg cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-gray-900 cqa-resize-y focus:cqa-outline-none focus:cqa-ring-1 focus:cqa-ring-indigo-500 focus:cqa-border-indigo-500\"\n [value]=\"variable.value || ''\"\n rows=\"4\"\n style=\"min-height: 42px;\"\n (input)=\"onVariableValueChange(variable.name, $any($event.target).value)\"></textarea>\n </div>\n </ng-container>\n\n <!-- Number variables with number input -->\n <ng-container *ngIf=\"variable.type === 'number' && variable.dataKey !== 'element'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n\n <!-- Non-boolean, non-custom_code variables -->\n <ng-container *ngIf=\"variable.name !== 'custom_code' && variable.type !== 'boolean' && variable.type !== 'dropdown' && variable.type !== 'textarea' && !(variable.type === 'number' && variable.dataKey !== 'element')\">\n <!-- Element variables with cascading dropdowns (screen name + element) -->\n <ng-container *ngIf=\"isElementType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Screen Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getScreenNameSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedScreenName(variable); else elementManualInput\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getElementSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-template #elementManualInput>\n <ng-container *ngIf=\"variable.type === 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n <ng-container *ngIf=\"variable.type !== 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!isElementType(variable)\">\n <!-- Other dropdown variables (type, scrollTo, label) -->\n <ng-container *ngIf=\"shouldShowDropdown(variable); else defaultInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n </ng-container>\n <ng-template #defaultInput>\n <!-- Test-data, source-value, or target-value with data type dropdown -->\n <ng-container *ngIf=\"needsDataTypeDropdown(variable); else regularInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Type<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataTypeSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-container *ngIf=\"isEnvironmentType(variable)\">\n <div *ngIf=\"isEnvironmentType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedEnvironment(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Value<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isParameterType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Test Data Profile<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getTestDataProfileSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && !isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set Start<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'start')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set End<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'end')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedDataSet(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div> \n </ng-container>\n <div *ngIf=\"isPlainTextType(variable) || isRuntimeType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <ng-container *ngIf=\"isPlainTextType(variable); else runtimePlainInput\">\n <cqa-mixed-variable-input\n [placeholder]=\"'Text Input'\"\n [value]=\"getRawValue(variable)\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-mixed-variable-input>\n </ng-container>\n <ng-template #runtimePlainInput>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"getRawValue(variable)\" [fullWidth]=\"true\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </ng-template>\n </div>\n </ng-container>\n <ng-template #regularInput>\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-container>\n</div>\n\n<div *ngIf=\"createElementVisible\">\n <cqa-element-form\n [isCreateMode]=\"true\"\n [screenNameOptions]=\"screenNameOptions\"\n [hasMoreScreenNames]=\"hasMoreScreenNames\"\n [isLoadingScreenNames]=\"isLoadingScreenNames\"\n [isEditInDepthAvailable]=\"false\"\n (createElement)=\"onCreateElement($event)\"\n (cancel)=\"onCancelElementForm()\"\n (searchScreenName)=\"searchScreenName.emit($event)\"\n (loadMoreScreenNames)=\"loadMoreScreenNames.emit($event)\"\n (createScreenNameRequest)=\"createScreenNameRequest.emit($event)\">\n </cqa-element-form>\n</div>", styles: ["\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n"], components: [{ type: CustomInputComponent, selector: "cqa-custom-input", inputs: ["inputId", "label", "type", "placeholder", "value", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "inputInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused", "enterPressed"] }, { type: i3$4.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["disabled", "disableRipple", "color", "tabIndex", "name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "checked"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"], outputs: ["selectionChange", "selectClick", "searchChange", "loadMore", "addCustomValue"] }, { type: MixedVariableInputComponent, selector: "cqa-mixed-variable-input", inputs: ["value", "placeholder", "disabled"], outputs: ["valueChange"] }, { type: ElementFormComponent, selector: "cqa-element-form", inputs: ["elementId", "element", "screenNameOptions", "elements", "hasMoreScreenNames", "isLoadingScreenNames", "hasMoreElements", "isLoadingElements", "isElementLoading", "isEditMode", "isCreateMode", "isEditInDepthAvailable"], outputs: ["createElement", "updateElement", "createScreenNameRequest", "searchScreenName", "loadMoreScreenNames", "searchElements", "loadMoreElements", "searchElementsByScreenName", "cancel", "editInDepth"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "cqaCamelToTitle": CamelToTitlePipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
35483
+ TemplateVariablesFormComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TemplateVariablesFormComponent, selector: "cqa-template-variables-form", inputs: { templateVariables: "templateVariables", variablesForm: "variablesForm", metadata: "metadata", description: "description", elementOptions: "elementOptions", hasMoreElements: "hasMoreElements", isLoadingElements: "isLoadingElements", screenNameOptions: "screenNameOptions", hasMoreScreenNames: "hasMoreScreenNames", isLoadingScreenNames: "isLoadingScreenNames", parameterOptions: "parameterOptions", hasMoreParameters: "hasMoreParameters", isLoadingParameters: "isLoadingParameters", uploadOptions: "uploadOptions", hasMoreUploads: "hasMoreUploads", isLoadingUploads: "isLoadingUploads", environmentOptions: "environmentOptions", hasMoreEnvironments: "hasMoreEnvironments", isLoadingEnvironments: "isLoadingEnvironments", defaultTestDataProfileId: "defaultTestDataProfileId", defaultTestDataStartIndex: "defaultTestDataStartIndex", defaultTestDataEndIndex: "defaultTestDataEndIndex", isInsideForLoopStep: "isInsideForLoopStep", hasTestDataProfile: "hasTestDataProfile", isEditInDepth: "isEditInDepth", isDebug: "isDebug", createElementVisible: "createElementVisible" }, outputs: { variableValueChange: "variableValueChange", variableBooleanChange: "variableBooleanChange", metadataChange: "metadataChange", descriptionChange: "descriptionChange", loadMoreElements: "loadMoreElements", searchElements: "searchElements", searchElementsByScreenName: "searchElementsByScreenName", createElement: "createElement", searchScreenName: "searchScreenName", loadMoreScreenNames: "loadMoreScreenNames", createScreenNameRequest: "createScreenNameRequest", searchParameters: "searchParameters", loadMoreParameters: "loadMoreParameters", searchUploads: "searchUploads", loadMoreUploads: "loadMoreUploads", searchEnvironments: "searchEnvironments", loadMoreEnvironments: "loadMoreEnvironments", cancelElementForm: "cancelElementForm", elementFormVisibilityChange: "elementFormVisibilityChange" }, host: { classAttribute: "cqa-ui-root" }, usesOnChanges: true, ngImport: i0, template: "<style>\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n</style>\n<div class=\"cqa-flex cqa-gap-x-6 cqa-gap-y-4 cqa-flex-wrap template-variables-form\" [ngClass]=\"{'cqa-flex-col': isEditInDepth}\" *ngIf=\"!createElementVisible\">\n \n <!-- Metadata - Only show if element is available in form -->\n <div *ngIf=\"selectorVariableAvailable\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Metadata\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"metadata\" [fullWidth]=\"true\"\n (valueChange)=\"metadataChange.emit($event)\">\n </cqa-custom-input>\n </div>\n\n <!-- Description -->\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Description\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"description\" [fullWidth]=\"true\"\n (valueChange)=\"descriptionChange.emit($event)\">\n </cqa-custom-input>\n </div>\n \n <ng-container *ngFor=\"let variable of templateVariables; let i = index; trackBy: trackByVariable\">\n <!-- Boolean variables with mat-slide-toggle -->\n <ng-container *ngIf=\"variable.type === 'boolean'\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <mat-slide-toggle [checked]=\"variablesForm.at(i)?.get('value')?.value || variable.value || false\"\n (change)=\"onVariableBooleanChange(variable.name, $event.checked)\" color=\"primary\">\n </mat-slide-toggle>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"variable.type === 'dropdown'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n\n <!-- Textarea variables -->\n <ng-container *ngIf=\"variable.type === 'textarea'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <textarea class=\"cqa-w-full cqa-border cqa-border-gray-300 cqa-rounded-lg cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-gray-900 cqa-resize-y focus:cqa-outline-none focus:cqa-ring-1 focus:cqa-ring-indigo-500 focus:cqa-border-indigo-500\"\n [value]=\"variable.value || ''\"\n rows=\"4\"\n style=\"min-height: 42px;\"\n (input)=\"onVariableValueChange(variable.name, $any($event.target).value)\"></textarea>\n </div>\n </ng-container>\n\n <!-- Number variables with number input -->\n <ng-container *ngIf=\"variable.type === 'number' && variable.dataKey !== 'element'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n\n <!-- Non-boolean, non-custom_code variables -->\n <ng-container *ngIf=\"variable.name !== 'custom_code' && variable.type !== 'boolean' && variable.type !== 'dropdown' && variable.type !== 'textarea' && !(variable.type === 'number' && variable.dataKey !== 'element')\">\n <!-- Element variables with cascading dropdowns (screen name + element) -->\n <ng-container *ngIf=\"isElementType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Screen Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getScreenNameSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedScreenName(variable); else elementManualInput\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getElementSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-template #elementManualInput>\n <ng-container *ngIf=\"variable.type === 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n <ng-container *ngIf=\"variable.type !== 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!isElementType(variable)\">\n <!-- Other dropdown variables (type, scrollTo, label) -->\n <ng-container *ngIf=\"shouldShowDropdown(variable); else defaultInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n </ng-container>\n <ng-template #defaultInput>\n <!-- Test-data, source-value, or target-value with data type dropdown -->\n <ng-container *ngIf=\"needsDataTypeDropdown(variable); else regularInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Type<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataTypeSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-container *ngIf=\"isEnvironmentType(variable)\">\n <div *ngIf=\"isEnvironmentType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedEnvironment(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Value<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isParameterType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Test Data Profile<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getTestDataProfileSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && !isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set Start<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'start')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set End<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'end')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedDataSet(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div> \n </ng-container>\n <div *ngIf=\"isPlainTextType(variable) || isRuntimeType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <ng-container *ngIf=\"isPlainTextType(variable); else runtimePlainInput\">\n <cqa-mixed-variable-input\n [placeholder]=\"'Text Input'\"\n [value]=\"getRawValue(variable)\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\"\n (validityChange)=\"onTestDataValidityChange(variable.name, $event)\">\n </cqa-mixed-variable-input>\n </ng-container>\n <ng-template #runtimePlainInput>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"getRawValue(variable)\" [fullWidth]=\"true\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </ng-template>\n </div>\n </ng-container>\n <ng-template #regularInput>\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-container>\n</div>\n\n<div *ngIf=\"createElementVisible\">\n <cqa-element-form\n [isCreateMode]=\"true\"\n [screenNameOptions]=\"screenNameOptions\"\n [hasMoreScreenNames]=\"hasMoreScreenNames\"\n [isLoadingScreenNames]=\"isLoadingScreenNames\"\n [isEditInDepthAvailable]=\"false\"\n (createElement)=\"onCreateElement($event)\"\n (cancel)=\"onCancelElementForm()\"\n (searchScreenName)=\"searchScreenName.emit($event)\"\n (loadMoreScreenNames)=\"loadMoreScreenNames.emit($event)\"\n (createScreenNameRequest)=\"createScreenNameRequest.emit($event)\">\n </cqa-element-form>\n</div>", styles: ["\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n"], components: [{ type: CustomInputComponent, selector: "cqa-custom-input", inputs: ["inputId", "label", "type", "placeholder", "value", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "inputInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused", "enterPressed"] }, { type: i3$4.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["disabled", "disableRipple", "color", "tabIndex", "name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "checked"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"], outputs: ["selectionChange", "selectClick", "searchChange", "loadMore", "addCustomValue"] }, { type: MixedVariableInputComponent, selector: "cqa-mixed-variable-input", inputs: ["value", "placeholder", "disabled"], outputs: ["valueChange", "validityChange"] }, { type: ElementFormComponent, selector: "cqa-element-form", inputs: ["elementId", "element", "screenNameOptions", "elements", "hasMoreScreenNames", "isLoadingScreenNames", "hasMoreElements", "isLoadingElements", "isElementLoading", "isEditMode", "isCreateMode", "isEditInDepthAvailable"], outputs: ["createElement", "updateElement", "createScreenNameRequest", "searchScreenName", "loadMoreScreenNames", "searchElements", "loadMoreElements", "searchElementsByScreenName", "cancel", "editInDepth"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "cqaCamelToTitle": CamelToTitlePipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
35182
35484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TemplateVariablesFormComponent, decorators: [{
35183
35485
  type: Component,
35184
- args: [{ selector: 'cqa-template-variables-form', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<style>\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n</style>\n<div class=\"cqa-flex cqa-gap-x-6 cqa-gap-y-4 cqa-flex-wrap template-variables-form\" [ngClass]=\"{'cqa-flex-col': isEditInDepth}\" *ngIf=\"!createElementVisible\">\n \n <!-- Metadata - Only show if element is available in form -->\n <div *ngIf=\"selectorVariableAvailable\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Metadata\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"metadata\" [fullWidth]=\"true\"\n (valueChange)=\"metadataChange.emit($event)\">\n </cqa-custom-input>\n </div>\n\n <!-- Description -->\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Description\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"description\" [fullWidth]=\"true\"\n (valueChange)=\"descriptionChange.emit($event)\">\n </cqa-custom-input>\n </div>\n \n <ng-container *ngFor=\"let variable of templateVariables; let i = index; trackBy: trackByVariable\">\n <!-- Boolean variables with mat-slide-toggle -->\n <ng-container *ngIf=\"variable.type === 'boolean'\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <mat-slide-toggle [checked]=\"variablesForm.at(i)?.get('value')?.value || variable.value || false\"\n (change)=\"onVariableBooleanChange(variable.name, $event.checked)\" color=\"primary\">\n </mat-slide-toggle>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"variable.type === 'dropdown'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n\n <!-- Textarea variables -->\n <ng-container *ngIf=\"variable.type === 'textarea'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <textarea class=\"cqa-w-full cqa-border cqa-border-gray-300 cqa-rounded-lg cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-gray-900 cqa-resize-y focus:cqa-outline-none focus:cqa-ring-1 focus:cqa-ring-indigo-500 focus:cqa-border-indigo-500\"\n [value]=\"variable.value || ''\"\n rows=\"4\"\n style=\"min-height: 42px;\"\n (input)=\"onVariableValueChange(variable.name, $any($event.target).value)\"></textarea>\n </div>\n </ng-container>\n\n <!-- Number variables with number input -->\n <ng-container *ngIf=\"variable.type === 'number' && variable.dataKey !== 'element'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n\n <!-- Non-boolean, non-custom_code variables -->\n <ng-container *ngIf=\"variable.name !== 'custom_code' && variable.type !== 'boolean' && variable.type !== 'dropdown' && variable.type !== 'textarea' && !(variable.type === 'number' && variable.dataKey !== 'element')\">\n <!-- Element variables with cascading dropdowns (screen name + element) -->\n <ng-container *ngIf=\"isElementType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Screen Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getScreenNameSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedScreenName(variable); else elementManualInput\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getElementSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-template #elementManualInput>\n <ng-container *ngIf=\"variable.type === 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n <ng-container *ngIf=\"variable.type !== 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!isElementType(variable)\">\n <!-- Other dropdown variables (type, scrollTo, label) -->\n <ng-container *ngIf=\"shouldShowDropdown(variable); else defaultInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n </ng-container>\n <ng-template #defaultInput>\n <!-- Test-data, source-value, or target-value with data type dropdown -->\n <ng-container *ngIf=\"needsDataTypeDropdown(variable); else regularInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Type<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataTypeSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-container *ngIf=\"isEnvironmentType(variable)\">\n <div *ngIf=\"isEnvironmentType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedEnvironment(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Value<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isParameterType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Test Data Profile<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getTestDataProfileSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && !isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set Start<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'start')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set End<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'end')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedDataSet(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div> \n </ng-container>\n <div *ngIf=\"isPlainTextType(variable) || isRuntimeType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <ng-container *ngIf=\"isPlainTextType(variable); else runtimePlainInput\">\n <cqa-mixed-variable-input\n [placeholder]=\"'Text Input'\"\n [value]=\"getRawValue(variable)\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-mixed-variable-input>\n </ng-container>\n <ng-template #runtimePlainInput>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"getRawValue(variable)\" [fullWidth]=\"true\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </ng-template>\n </div>\n </ng-container>\n <ng-template #regularInput>\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-container>\n</div>\n\n<div *ngIf=\"createElementVisible\">\n <cqa-element-form\n [isCreateMode]=\"true\"\n [screenNameOptions]=\"screenNameOptions\"\n [hasMoreScreenNames]=\"hasMoreScreenNames\"\n [isLoadingScreenNames]=\"isLoadingScreenNames\"\n [isEditInDepthAvailable]=\"false\"\n (createElement)=\"onCreateElement($event)\"\n (cancel)=\"onCancelElementForm()\"\n (searchScreenName)=\"searchScreenName.emit($event)\"\n (loadMoreScreenNames)=\"loadMoreScreenNames.emit($event)\"\n (createScreenNameRequest)=\"createScreenNameRequest.emit($event)\">\n </cqa-element-form>\n</div>", styles: ["\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n"] }]
35486
+ args: [{ selector: 'cqa-template-variables-form', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<style>\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n</style>\n<div class=\"cqa-flex cqa-gap-x-6 cqa-gap-y-4 cqa-flex-wrap template-variables-form\" [ngClass]=\"{'cqa-flex-col': isEditInDepth}\" *ngIf=\"!createElementVisible\">\n \n <!-- Metadata - Only show if element is available in form -->\n <div *ngIf=\"selectorVariableAvailable\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Metadata\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"metadata\" [fullWidth]=\"true\"\n (valueChange)=\"metadataChange.emit($event)\">\n </cqa-custom-input>\n </div>\n\n <!-- Description -->\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Description\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"description\" [fullWidth]=\"true\"\n (valueChange)=\"descriptionChange.emit($event)\">\n </cqa-custom-input>\n </div>\n \n <ng-container *ngFor=\"let variable of templateVariables; let i = index; trackBy: trackByVariable\">\n <!-- Boolean variables with mat-slide-toggle -->\n <ng-container *ngIf=\"variable.type === 'boolean'\">\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <mat-slide-toggle [checked]=\"variablesForm.at(i)?.get('value')?.value || variable.value || false\"\n (change)=\"onVariableBooleanChange(variable.name, $event.checked)\" color=\"primary\">\n </mat-slide-toggle>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"variable.type === 'dropdown'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n\n <!-- Textarea variables -->\n <ng-container *ngIf=\"variable.type === 'textarea'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <textarea class=\"cqa-w-full cqa-border cqa-border-gray-300 cqa-rounded-lg cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-gray-900 cqa-resize-y focus:cqa-outline-none focus:cqa-ring-1 focus:cqa-ring-indigo-500 focus:cqa-border-indigo-500\"\n [value]=\"variable.value || ''\"\n rows=\"4\"\n style=\"min-height: 42px;\"\n (input)=\"onVariableValueChange(variable.name, $any($event.target).value)\"></textarea>\n </div>\n </ng-container>\n\n <!-- Number variables with number input -->\n <ng-container *ngIf=\"variable.type === 'number' && variable.dataKey !== 'element'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n\n <!-- Non-boolean, non-custom_code variables -->\n <ng-container *ngIf=\"variable.name !== 'custom_code' && variable.type !== 'boolean' && variable.type !== 'dropdown' && variable.type !== 'textarea' && !(variable.type === 'number' && variable.dataKey !== 'element')\">\n <!-- Element variables with cascading dropdowns (screen name + element) -->\n <ng-container *ngIf=\"isElementType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Screen Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getScreenNameSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedScreenName(variable); else elementManualInput\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getElementSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-template #elementManualInput>\n <ng-container *ngIf=\"variable.type === 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [type]=\"'number'\" [placeholder]=\"'Number Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n <ng-container *ngIf=\"variable.type !== 'number'\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-container>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!isElementType(variable)\">\n <!-- Other dropdown variables (type, scrollTo, label) -->\n <ng-container *ngIf=\"shouldShowDropdown(variable); else defaultInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n </ng-container>\n <ng-template #defaultInput>\n <!-- Test-data, source-value, or target-value with data type dropdown -->\n <ng-container *ngIf=\"needsDataTypeDropdown(variable); else regularInput\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }} Type<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataTypeSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <ng-container *ngIf=\"isEnvironmentType(variable)\">\n <div *ngIf=\"isEnvironmentType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Name<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedEnvironment(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Environment Value<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getEnvironmentParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isParameterType(variable)\">\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Test Data Profile<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getTestDataProfileSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && !isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set Start<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'start')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedTestDataProfile(variable) && isInsideForLoopStep\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1\">\n Data Set End<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getDataSetRangeSelectConfig(variable, i, 'end')\">\n </cqa-dynamic-select>\n </div>\n <div *ngIf=\"hasSelectedDataSet(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <cqa-dynamic-select [form]=\"getFormGroupAt(i)!\" [config]=\"getParameterSelectConfig(variable, i)\">\n </cqa-dynamic-select>\n </div> \n </ng-container>\n <div *ngIf=\"isPlainTextType(variable) || isRuntimeType(variable)\" class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}<span class=\"cqa-text-red-500\">*</span>\n </label>\n <ng-container *ngIf=\"isPlainTextType(variable); else runtimePlainInput\">\n <cqa-mixed-variable-input\n [placeholder]=\"'Text Input'\"\n [value]=\"getRawValue(variable)\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\"\n (validityChange)=\"onTestDataValidityChange(variable.name, $event)\">\n </cqa-mixed-variable-input>\n </ng-container>\n <ng-template #runtimePlainInput>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"getRawValue(variable)\" [fullWidth]=\"true\"\n (valueChange)=\"onTestDataValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </ng-template>\n </div>\n </ng-container>\n <ng-template #regularInput>\n <div class=\"cqa-flex cqa-flex-col\" [style.width]=\"isEditInDepth ? null : 'calc(50% - 12px)'\" [ngClass]=\"{'cqa-w-full': isEditInDepth}\">\n <label class=\"cqa-text-sm cqa-font-medium cqa-text-gray-700 cqa-mb-1 capitalize-first\">\n {{ variable.label | cqaCamelToTitle }}\n </label>\n <cqa-custom-input [placeholder]=\"'Text Input'\" [value]=\"variable.value\" [fullWidth]=\"true\"\n (valueChange)=\"onVariableValueChange(variable.name, $event)\">\n </cqa-custom-input>\n </div>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-container>\n</div>\n\n<div *ngIf=\"createElementVisible\">\n <cqa-element-form\n [isCreateMode]=\"true\"\n [screenNameOptions]=\"screenNameOptions\"\n [hasMoreScreenNames]=\"hasMoreScreenNames\"\n [isLoadingScreenNames]=\"isLoadingScreenNames\"\n [isEditInDepthAvailable]=\"false\"\n (createElement)=\"onCreateElement($event)\"\n (cancel)=\"onCancelElementForm()\"\n (searchScreenName)=\"searchScreenName.emit($event)\"\n (loadMoreScreenNames)=\"loadMoreScreenNames.emit($event)\"\n (createScreenNameRequest)=\"createScreenNameRequest.emit($event)\">\n </cqa-element-form>\n</div>", styles: ["\n .capitalize-first::first-letter {\n text-transform: uppercase;\n }\n"] }]
35185
35487
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { templateVariables: [{
35186
35488
  type: Input
35187
35489
  }], variablesForm: [{