@cqa-lib/cqa-ui 1.1.482 → 1.1.484
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/dynamic-select/dynamic-select-field.component.mjs +28 -3
- package/esm2020/lib/execution-screen/api-step/api-step.component.mjs +78 -54
- package/fesm2015/cqa-lib-cqa-ui.mjs +106 -57
- package/fesm2015/cqa-lib-cqa-ui.mjs.map +1 -1
- package/fesm2020/cqa-lib-cqa-ui.mjs +105 -56
- package/fesm2020/cqa-lib-cqa-ui.mjs.map +1 -1
- package/lib/dynamic-select/dynamic-select-field.component.d.ts +10 -0
- package/lib/execution-screen/api-step/api-step.component.d.ts +9 -1
- package/package.json +1 -1
|
@@ -2016,6 +2016,31 @@ class DynamicSelectFieldComponent {
|
|
|
2016
2016
|
}
|
|
2017
2017
|
return String(controlValue);
|
|
2018
2018
|
}
|
|
2019
|
+
/** Resolved option for single-select trigger (icon + label). */
|
|
2020
|
+
get singleSelectedOption() {
|
|
2021
|
+
const key = this.config?.key;
|
|
2022
|
+
if (!key || !this.form || this.isMultiple)
|
|
2023
|
+
return null;
|
|
2024
|
+
const controlValue = this.form.get(key)?.value;
|
|
2025
|
+
if (controlValue === null || controlValue === undefined || controlValue === '')
|
|
2026
|
+
return null;
|
|
2027
|
+
const matched = (this.config?.options || []).find((opt) => this.getOptionValue(opt) === controlValue || String(this.getOptionValue(opt)) === String(controlValue));
|
|
2028
|
+
return matched ?? null;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Maps API testType strings to icon variants. Unknown non-empty values use 'other'.
|
|
2032
|
+
*/
|
|
2033
|
+
testTypeIconKind(opt) {
|
|
2034
|
+
const raw = opt?.testType;
|
|
2035
|
+
if (raw == null || String(raw).trim() === '')
|
|
2036
|
+
return null;
|
|
2037
|
+
const u = String(raw).trim().toUpperCase();
|
|
2038
|
+
if (u === 'MOBILE')
|
|
2039
|
+
return 'mobile';
|
|
2040
|
+
if (u === 'BROWSER' || u === 'WEB')
|
|
2041
|
+
return 'web';
|
|
2042
|
+
return 'other';
|
|
2043
|
+
}
|
|
2019
2044
|
get isDisabled() {
|
|
2020
2045
|
return this.toBoolean(this.config?.disabled);
|
|
2021
2046
|
}
|
|
@@ -2784,10 +2809,10 @@ class DynamicSelectFieldComponent {
|
|
|
2784
2809
|
}
|
|
2785
2810
|
}
|
|
2786
2811
|
DynamicSelectFieldComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2787
|
-
DynamicSelectFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: { form: "form", config: "config" }, outputs: { selectionChange: "selectionChange", selectClick: "selectClick", searchChange: "searchChange", loadMore: "loadMore", addCustomValue: "addCustomValue" }, host: { listeners: { "document:click": "handleDocumentClick($event)" } }, viewQueries: [{ propertyName: "selectRef", first: true, predicate: ["selectRef"], descendants: true }, { propertyName: "hostEl", first: true, predicate: ["host"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"displayPlaceholder\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onSelectionChange($event, selectRef)\"\n (keydown)=\"onSelectKeydown($event)\">\n <mat-select-trigger *ngIf=\"!isMultiple && singleSelectedDisplayLabel\">\n <span [innerHTML]=\"singleSelectedDisplayLabel\"></span>\n </mat-select-trigger>\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input cqa-text-black-100\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"onSearchInputKeydown($event)\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n \n <mat-option [ngClass]=\"{'checkmark': config.optionStyle === 'checkmark','checkbox': config.optionStyle !== 'checkmark','mat-selected': allSelected}\" [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngIf=\"isMultiple && config.showSelectAll\" [value]=\"SELECT_ALL_VALUE\">\n <ng-container *ngIf=\"useCheckboxStyle; else selectAllDefaultTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"allSelected ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"allSelected\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\">{{ config.selectAllLabel || 'All' }}</span>\n </span>\n </ng-container>\n <ng-template #selectAllDefaultTpl>\n {{ config.selectAllLabel || 'All' }}\n </ng-template>\n </mat-option>\n\n <mat-option *ngIf=\"showAddCustomOption\" [value]=\"addCustomSentinelValue\" class=\"cqa-text-primary\">\n New\n </mat-option>\n\n <mat-option [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngFor=\"let opt of filteredOptions(config)\" [value]=\"getOptionValue(opt)\">\n <ng-container *ngIf=\"config.isCompareRuns\"> \n <ng-container *ngIf=\"useCheckboxStyle; else compareRunsDefaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else checkboxDefaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #checkboxDefaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </span>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-container>\n <ng-template #compareRunsDefaultOptionTpl>\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else defaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #defaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!config.isCompareRuns\">\n <ng-container *ngIf=\"useCheckboxStyle; else defaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <!-- When displayLabelAsInnerHtml is true, render label as raw HTML -->\n <span class=\"cqa-min-w-0\"\n *ngIf=\"config?.displayLabelAsInnerHtml\"\n [innerHTML]=\"opt.label ?? opt.name ?? opt.value\">\n </span>\n <!-- Otherwise use normal/highlighted text rendering -->\n <span class=\"cqa-min-w-0\"\n *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\">\n </span>\n <span class=\"cqa-min-w-0\"\n *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #defaultOptionTpl>\n <!-- When displayLabelAsInnerHtml is true, render label as raw HTML -->\n <span *ngIf=\"config?.displayLabelAsInnerHtml\"\n [innerHTML]=\"opt.label ?? opt.name ?? opt.value\">\n </span>\n <!-- Otherwise use normal/highlighted text rendering -->\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\">\n </span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </mat-option>\n \n <!-- No results state (only when not loading and no options) -->\n <mat-option disabled *ngIf=\"!(config?.options?.length || 0) && !(config?.isLoading || loadingMore)\">\n No results\n </mat-option>\n <!-- Infinite scroll sentinel (serverSearch or explicit hasMore) -->\n <mat-option disabled class=\"load-more-sentinel\" *ngIf=\"config?.hasMore\">\n <span *ngIf=\"loadingMore || config?.isLoading\">Loading...</span>\n <span *ngIf=\"!loadingMore && !config?.isLoading\">Scroll to load more\u2026</span>\n </mat-option>\n </mat-select>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <!-- Inline spinner shown when loading more (infinite scroll) or when config.isLoading is true -->\n <svg *ngIf=\"loadingMore || config?.isLoading\" width=\"16\" height=\"16\" viewBox=\"0 0 50 50\" aria-label=\"loading\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"/>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\"\n dur=\"0.8s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n</div>", components: [{ type: i10.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i2$1.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i3$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i2$1.MatSelectTrigger, selector: "mat-select-trigger" }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2812
|
+
DynamicSelectFieldComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: { form: "form", config: "config" }, outputs: { selectionChange: "selectionChange", selectClick: "selectClick", searchChange: "searchChange", loadMore: "loadMore", addCustomValue: "addCustomValue" }, host: { listeners: { "document:click": "handleDocumentClick($event)" } }, viewQueries: [{ propertyName: "selectRef", first: true, predicate: ["selectRef"], descendants: true }, { propertyName: "hostEl", first: true, predicate: ["host"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"displayPlaceholder\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onSelectionChange($event, selectRef)\"\n (keydown)=\"onSelectKeydown($event)\">\n <mat-select-trigger *ngIf=\"!isMultiple && singleSelectedDisplayLabel\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <ng-container *ngIf=\"config?.showTestTypeIcon\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(singleSelectedOption) }\"></ng-container>\n </ng-container>\n <span class=\"cqa-min-w-0\" [innerHTML]=\"singleSelectedDisplayLabel\"></span>\n </span>\n </mat-select-trigger>\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input cqa-text-black-100\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"onSearchInputKeydown($event)\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n \n <mat-option [ngClass]=\"{'checkmark': config.optionStyle === 'checkmark','checkbox': config.optionStyle !== 'checkmark','mat-selected': allSelected}\" [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngIf=\"isMultiple && config.showSelectAll\" [value]=\"SELECT_ALL_VALUE\">\n <ng-container *ngIf=\"useCheckboxStyle; else selectAllDefaultTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"allSelected ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"allSelected\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\">{{ config.selectAllLabel || 'All' }}</span>\n </span>\n </ng-container>\n <ng-template #selectAllDefaultTpl>\n {{ config.selectAllLabel || 'All' }}\n </ng-template>\n </mat-option>\n\n <mat-option *ngIf=\"showAddCustomOption\" [value]=\"addCustomSentinelValue\" class=\"cqa-text-primary\">\n New\n </mat-option>\n\n <mat-option [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngFor=\"let opt of filteredOptions(config)\" [value]=\"getOptionValue(opt)\">\n <ng-container *ngIf=\"config.isCompareRuns\"> \n <ng-container *ngIf=\"useCheckboxStyle; else compareRunsDefaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else checkboxDefaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #checkboxDefaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </span>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-container>\n <ng-template #compareRunsDefaultOptionTpl>\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else defaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #defaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!config.isCompareRuns\">\n <ng-container *ngIf=\"useCheckboxStyle; else defaultOptionTpl\">\n <ng-container *ngIf=\"config?.showTestTypeIcon; else checkboxNoIconTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-1 cqa-min-w-0\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(opt) }\"></ng-container>\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\" *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #checkboxNoIconTpl>\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-template #defaultOptionTpl>\n <ng-container *ngIf=\"config?.showTestTypeIcon; else defaultNoIconTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(opt) }\"></ng-container>\n <span class=\"cqa-min-w-0\" *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #defaultNoIconTpl>\n <span *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-template>\n </ng-container>\n </mat-option>\n \n <!-- No results state (only when not loading and no options) -->\n <mat-option disabled *ngIf=\"!(config?.options?.length || 0) && !(config?.isLoading || loadingMore)\">\n No results\n </mat-option>\n <!-- Infinite scroll sentinel (serverSearch or explicit hasMore) -->\n <mat-option disabled class=\"load-more-sentinel\" *ngIf=\"config?.hasMore\">\n <span *ngIf=\"loadingMore || config?.isLoading\">Loading...</span>\n <span *ngIf=\"!loadingMore && !config?.isLoading\">Scroll to load more\u2026</span>\n </mat-option>\n </mat-select>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <!-- Inline spinner shown when loading more (infinite scroll) or when config.isLoading is true -->\n <svg *ngIf=\"loadingMore || config?.isLoading\" width=\"16\" height=\"16\" viewBox=\"0 0 50 50\" aria-label=\"loading\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"/>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\"\n dur=\"0.8s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n\n <ng-template #optionTestTypeIconTpl let-kind=\"kind\">\n <span *ngIf=\"kind === 'mobile'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/>\n <path d=\"M6 5a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2v-14\"/>\n <path d=\"M11 4h2\"/>\n <path d=\"M12 17v.01\"/>\n </svg>\n </span>\n <span *ngIf=\"kind === 'web'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/>\n <path d=\"M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0\"/>\n <path d=\"M3.6 9h16.8\"/>\n <path d=\"M3.6 15h16.8\"/>\n <path d=\"M11.5 3a17 17 0 0 0 0 18\"/>\n <path d=\"M12.5 3a17 17 0 0 1 0 18\"/>\n </svg>\n </span>\n <span *ngIf=\"kind === 'other'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6.5h16v11H4z\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M8 6.5V5h8v1.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </ng-template>\n</div>", components: [{ type: i10.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { type: i2$1.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i3$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i2$1.MatSelectTrigger, selector: "mat-select-trigger" }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2788
2813
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicSelectFieldComponent, decorators: [{
|
|
2789
2814
|
type: Component,
|
|
2790
|
-
args: [{ selector: 'cqa-dynamic-select', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"displayPlaceholder\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onSelectionChange($event, selectRef)\"\n (keydown)=\"onSelectKeydown($event)\">\n <mat-select-trigger *ngIf=\"!isMultiple && singleSelectedDisplayLabel\">\n <span [innerHTML]=\"singleSelectedDisplayLabel\"></span>\n </mat-select-trigger>\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input cqa-text-black-100\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"onSearchInputKeydown($event)\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n \n <mat-option [ngClass]=\"{'checkmark': config.optionStyle === 'checkmark','checkbox': config.optionStyle !== 'checkmark','mat-selected': allSelected}\" [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngIf=\"isMultiple && config.showSelectAll\" [value]=\"SELECT_ALL_VALUE\">\n <ng-container *ngIf=\"useCheckboxStyle; else selectAllDefaultTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"allSelected ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"allSelected\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\">{{ config.selectAllLabel || 'All' }}</span>\n </span>\n </ng-container>\n <ng-template #selectAllDefaultTpl>\n {{ config.selectAllLabel || 'All' }}\n </ng-template>\n </mat-option>\n\n <mat-option *ngIf=\"showAddCustomOption\" [value]=\"addCustomSentinelValue\" class=\"cqa-text-primary\">\n New\n </mat-option>\n\n <mat-option [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngFor=\"let opt of filteredOptions(config)\" [value]=\"getOptionValue(opt)\">\n <ng-container *ngIf=\"config.isCompareRuns\"> \n <ng-container *ngIf=\"useCheckboxStyle; else compareRunsDefaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else checkboxDefaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #checkboxDefaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </span>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-container>\n <ng-template #compareRunsDefaultOptionTpl>\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else defaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #defaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!config.isCompareRuns\">\n <ng-container *ngIf=\"useCheckboxStyle; else defaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <!-- When displayLabelAsInnerHtml is true, render label as raw HTML -->\n <span class=\"cqa-min-w-0\"\n *ngIf=\"config?.displayLabelAsInnerHtml\"\n [innerHTML]=\"opt.label ?? opt.name ?? opt.value\">\n </span>\n <!-- Otherwise use normal/highlighted text rendering -->\n <span class=\"cqa-min-w-0\"\n *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\">\n </span>\n <span class=\"cqa-min-w-0\"\n *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #defaultOptionTpl>\n <!-- When displayLabelAsInnerHtml is true, render label as raw HTML -->\n <span *ngIf=\"config?.displayLabelAsInnerHtml\"\n [innerHTML]=\"opt.label ?? opt.name ?? opt.value\">\n </span>\n <!-- Otherwise use normal/highlighted text rendering -->\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\">\n </span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </mat-option>\n \n <!-- No results state (only when not loading and no options) -->\n <mat-option disabled *ngIf=\"!(config?.options?.length || 0) && !(config?.isLoading || loadingMore)\">\n No results\n </mat-option>\n <!-- Infinite scroll sentinel (serverSearch or explicit hasMore) -->\n <mat-option disabled class=\"load-more-sentinel\" *ngIf=\"config?.hasMore\">\n <span *ngIf=\"loadingMore || config?.isLoading\">Loading...</span>\n <span *ngIf=\"!loadingMore && !config?.isLoading\">Scroll to load more\u2026</span>\n </mat-option>\n </mat-select>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <!-- Inline spinner shown when loading more (infinite scroll) or when config.isLoading is true -->\n <svg *ngIf=\"loadingMore || config?.isLoading\" width=\"16\" height=\"16\" viewBox=\"0 0 50 50\" aria-label=\"loading\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"/>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\"\n dur=\"0.8s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n</div>" }]
|
|
2815
|
+
args: [{ selector: 'cqa-dynamic-select', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"cqa-ui-root\">\n <ng-container [formGroup]=\"form\">\n <label *ngIf=\"config.label\"\n class=\"form-label cqa-text-[#374151] cqa-text-[14px] cqa-font-medium cqa-block cqa-leading-[1.4] cqa-mb-2\">{{\n config.label }}</label>\n <mat-form-field #host class=\"mat-select-custom cqa-w-full\" appearance=\"fill\">\n <mat-select #selectRef=\"matSelect\" [placeholder]=\"displayPlaceholder\" [multiple]=\"isMultiple\"\n disableOptionCentering [panelClass]=\"panelClass\" [formControlName]=\"config.key\"\n (openedChange)=\"onSelectOpenedChange($event, selectRef)\" (selectionChange)=\"onSelectionChange($event, selectRef)\"\n (keydown)=\"onSelectKeydown($event)\">\n <mat-select-trigger *ngIf=\"!isMultiple && singleSelectedDisplayLabel\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <ng-container *ngIf=\"config?.showTestTypeIcon\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(singleSelectedOption) }\"></ng-container>\n </ng-container>\n <span class=\"cqa-min-w-0\" [innerHTML]=\"singleSelectedDisplayLabel\"></span>\n </span>\n </mat-select-trigger>\n\n <mat-option *ngIf=\"config.searchable\" class=\"ts-select-search\" disabled>\n <input class=\"ts-select-search-input cqa-text-black-100\" type=\"text\" [value]=\"searchTextByKey[config.key] || ''\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"onSearchInputKeydown($event)\" (input)=\"onSearch(config.key, $any($event.target).value)\"\n placeholder=\"Search...\" />\n </mat-option>\n \n <mat-option [ngClass]=\"{'checkmark': config.optionStyle === 'checkmark','checkbox': config.optionStyle !== 'checkmark','mat-selected': allSelected}\" [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngIf=\"isMultiple && config.showSelectAll\" [value]=\"SELECT_ALL_VALUE\">\n <ng-container *ngIf=\"useCheckboxStyle; else selectAllDefaultTpl\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"allSelected ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"allSelected\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\">{{ config.selectAllLabel || 'All' }}</span>\n </span>\n </ng-container>\n <ng-template #selectAllDefaultTpl>\n {{ config.selectAllLabel || 'All' }}\n </ng-template>\n </mat-option>\n\n <mat-option *ngIf=\"showAddCustomOption\" [value]=\"addCustomSentinelValue\" class=\"cqa-text-primary\">\n New\n </mat-option>\n\n <mat-option [class]=\"config.optionStyle == 'checkmark' ? 'checkmark' : 'checkbox'\" *ngFor=\"let opt of filteredOptions(config)\" [value]=\"getOptionValue(opt)\">\n <ng-container *ngIf=\"config.isCompareRuns\"> \n <ng-container *ngIf=\"useCheckboxStyle; else compareRunsDefaultOptionTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else checkboxDefaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #checkboxDefaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n </span>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-container>\n <ng-template #compareRunsDefaultOptionTpl>\n <span class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-compare-runs-item\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"hasHighlighting\"\n [style.color]=\"opt.statusColor || null\"\n [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <ng-container *ngIf=\"!hasHighlighting\">\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n *ngIf=\"opt.runNumberLabel && opt.runDateLabel; else defaultLabel\">\n <span [style.color]=\"opt.statusColor || null\">{{ opt.runNumberLabel }}</span>\n <span class=\"cqa-text-[#6B7280]\"> {{ opt.runDateLabel }} </span>\n </span>\n <ng-template #defaultLabel>\n <span\n class=\"cqa-min-w-0 cqa-compare-runs\"\n [style.color]=\"opt.statusColor || null\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"opt.durationFormatted\"\n class=\"cqa-flex cqa-items-center cqa-justify-start cqa-gap-1 cqa-text-[12px] cqa-text-[#6B7280] cqa-flex-shrink-0 cqa-whitespace-nowrap cqa-max-w-[80px]\" \n [ngClass]=\"{\n 'cqa-min-w-[82px]': opt?.hasHourRun,\n 'cqa-min-w-[66px]': !opt?.hasHourRun && opt?.hasMinuteRun,\n 'cqa-min-w-[40px]': !opt?.hasHourRun && !opt?.hasMinuteRun\n }\">\n <svg class=\"cqa-min-w-[12px] cqa-max-w-[12px]\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"6\" r=\"4.5\" stroke=\"#9CA3AF\" stroke-width=\"1\"/>\n <path d=\"M6 3.5V6L7.5 7\" stroke=\"#4B5563\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ opt.durationFormatted }}</span>\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!config.isCompareRuns\">\n <ng-container *ngIf=\"useCheckboxStyle; else defaultOptionTpl\">\n <ng-container *ngIf=\"config?.showTestTypeIcon; else checkboxNoIconTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-1 cqa-min-w-0\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(opt) }\"></ng-container>\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-min-w-0\" *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #checkboxNoIconTpl>\n <span class=\"cqa-flex cqa-items-center\">\n <span class=\"cqa-w-4 cqa-h-4 cqa-flex-shrink-0 cqa-rounded-[4px] cqa-border cqa-border-[#D1D5DB] cqa-mr-2 cqa-flex cqa-items-center cqa-justify-center cqa-border-solid\"\n [ngStyle]=\"isOptionSelected(opt) ? {'background-color':'#4F46E5','border-color':'#4F46E5'} : {}\">\n <svg *ngIf=\"isOptionSelected(opt)\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 3L4.5 8.5L2 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n <span *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-template>\n </ng-container>\n <ng-template #defaultOptionTpl>\n <ng-container *ngIf=\"config?.showTestTypeIcon; else defaultNoIconTpl\">\n <span class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-min-w-0\">\n <ng-container *ngTemplateOutlet=\"optionTestTypeIconTpl; context: { kind: testTypeIconKind(opt) }\"></ng-container>\n <span class=\"cqa-min-w-0\" *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span class=\"cqa-min-w-0\" *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </span>\n </ng-container>\n <ng-template #defaultNoIconTpl>\n <span *ngIf=\"config?.displayLabelAsInnerHtml\" [innerHTML]=\"opt.label ?? opt.name ?? opt.value\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && hasHighlighting\" [innerHTML]=\"highlightText(opt.name ?? opt.label ?? opt.value)\"></span>\n <span *ngIf=\"!config?.displayLabelAsInnerHtml && !hasHighlighting\">\n {{ opt.name ?? opt.label ?? opt.value }}\n </span>\n </ng-template>\n </ng-template>\n </ng-container>\n </mat-option>\n \n <!-- No results state (only when not loading and no options) -->\n <mat-option disabled *ngIf=\"!(config?.options?.length || 0) && !(config?.isLoading || loadingMore)\">\n No results\n </mat-option>\n <!-- Infinite scroll sentinel (serverSearch or explicit hasMore) -->\n <mat-option disabled class=\"load-more-sentinel\" *ngIf=\"config?.hasMore\">\n <span *ngIf=\"loadingMore || config?.isLoading\">Loading...</span>\n <span *ngIf=\"!loadingMore && !config?.isLoading\">Scroll to load more\u2026</span>\n </mat-option>\n </mat-select>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <!-- Inline spinner shown when loading more (infinite scroll) or when config.isLoading is true -->\n <svg *ngIf=\"loadingMore || config?.isLoading\" width=\"16\" height=\"16\" viewBox=\"0 0 50 50\" aria-label=\"loading\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"/>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\"\n dur=\"0.8s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6L8 10L12 6\" stroke=\"#0A0A0A\" stroke-width=\"1.33333\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </div>\n </mat-form-field>\n </ng-container>\n\n <ng-template #optionTestTypeIconTpl let-kind=\"kind\">\n <span *ngIf=\"kind === 'mobile'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/>\n <path d=\"M6 5a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2v-14\"/>\n <path d=\"M11 4h2\"/>\n <path d=\"M12 17v.01\"/>\n </svg>\n </span>\n <span *ngIf=\"kind === 'web'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/>\n <path d=\"M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0\"/>\n <path d=\"M3.6 9h16.8\"/>\n <path d=\"M3.6 15h16.8\"/>\n <path d=\"M11.5 3a17 17 0 0 0 0 18\"/>\n <path d=\"M12.5 3a17 17 0 0 1 0 18\"/>\n </svg>\n </span>\n <span *ngIf=\"kind === 'other'\" class=\"cqa-inline-flex cqa-flex-shrink-0 cqa-items-center\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 6.5h16v11H4z\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M8 6.5V5h8v1.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </ng-template>\n</div>" }]
|
|
2791
2816
|
}], propDecorators: { form: [{
|
|
2792
2817
|
type: Input
|
|
2793
2818
|
}], config: [{
|
|
@@ -14174,18 +14199,56 @@ class ApiStepComponent extends BaseStepComponent {
|
|
|
14174
14199
|
}
|
|
14175
14200
|
}
|
|
14176
14201
|
/**
|
|
14177
|
-
*
|
|
14202
|
+
* Walks jsonData and builds a map from each line number (in the output of
|
|
14203
|
+
* JSON.stringify(jsonData, null, 2)) to the segment path that line represents.
|
|
14204
|
+
* Segments are strings for object keys and numbers for array indices.
|
|
14205
|
+
*/
|
|
14206
|
+
buildLinePathMap(obj) {
|
|
14207
|
+
const map = new Map();
|
|
14208
|
+
const state = { line: 0 };
|
|
14209
|
+
const walk = (value, path) => {
|
|
14210
|
+
if (Array.isArray(value)) {
|
|
14211
|
+
if (value.length === 0)
|
|
14212
|
+
return;
|
|
14213
|
+
for (let i = 0; i < value.length; i++) {
|
|
14214
|
+
state.line += 1; // newline before element
|
|
14215
|
+
const elementPath = [...path, i];
|
|
14216
|
+
map.set(state.line, elementPath);
|
|
14217
|
+
walk(value[i], elementPath);
|
|
14218
|
+
}
|
|
14219
|
+
state.line += 1; // newline before closing ]
|
|
14220
|
+
return;
|
|
14221
|
+
}
|
|
14222
|
+
if (value && typeof value === 'object') {
|
|
14223
|
+
const keys = Object.keys(value);
|
|
14224
|
+
if (keys.length === 0)
|
|
14225
|
+
return;
|
|
14226
|
+
for (const key of keys) {
|
|
14227
|
+
state.line += 1; // newline before "key":
|
|
14228
|
+
const keyPath = [...path, key];
|
|
14229
|
+
map.set(state.line, keyPath);
|
|
14230
|
+
walk(value[key], keyPath);
|
|
14231
|
+
}
|
|
14232
|
+
state.line += 1; // newline before closing }
|
|
14233
|
+
return;
|
|
14234
|
+
}
|
|
14235
|
+
// primitive — no additional lines
|
|
14236
|
+
};
|
|
14237
|
+
// Root line (line 0) is the opening { or [ or primitive.
|
|
14238
|
+
walk(obj, []);
|
|
14239
|
+
return map;
|
|
14240
|
+
}
|
|
14241
|
+
/**
|
|
14242
|
+
* Extracts JSON path from the clicked position in a formatted JSON string.
|
|
14243
|
+
* Returns an array of segments (string keys or numeric array indices), or null.
|
|
14178
14244
|
*/
|
|
14179
14245
|
getJsonPathFromClick(event, jsonData, prefix = '') {
|
|
14180
14246
|
const target = event.target;
|
|
14181
|
-
if (!target ||
|
|
14247
|
+
if (!target || jsonData == null)
|
|
14182
14248
|
return null;
|
|
14183
14249
|
const preElement = target.closest('pre');
|
|
14184
14250
|
if (!preElement)
|
|
14185
14251
|
return null;
|
|
14186
|
-
// Get the formatted JSON text
|
|
14187
|
-
const formattedJson = this.formatJson(jsonData);
|
|
14188
|
-
const lines = formattedJson.split('\n');
|
|
14189
14252
|
// Create a range at the click position
|
|
14190
14253
|
let range = null;
|
|
14191
14254
|
if (document.caretRangeFromPoint) {
|
|
@@ -14208,58 +14271,44 @@ class ApiStepComponent extends BaseStepComponent {
|
|
|
14208
14271
|
const textBeforeClick = preRange.toString();
|
|
14209
14272
|
// Count newlines to find which line we're on
|
|
14210
14273
|
const lineIndex = (textBeforeClick.match(/\n/g) || []).length;
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
//
|
|
14214
|
-
|
|
14215
|
-
if (!
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
const lineKeyMatch = line.match(/^(\s+)"([^"]+)":/);
|
|
14226
|
-
if (lineKeyMatch) {
|
|
14227
|
-
const lineIndent = lineKeyMatch[1].length / 2; // Divide by 2 for 2-space indentation
|
|
14228
|
-
const lineKey = lineKeyMatch[2];
|
|
14229
|
-
// If this line has less indentation, it's a parent
|
|
14230
|
-
if (lineIndent < currentIndent) {
|
|
14231
|
-
path.unshift(lineKey);
|
|
14232
|
-
currentIndent = lineIndent;
|
|
14233
|
-
// Stop if we've reached the root level
|
|
14234
|
-
if (lineIndent === 0)
|
|
14235
|
-
break;
|
|
14236
|
-
}
|
|
14237
|
-
}
|
|
14238
|
-
}
|
|
14239
|
-
// Add the clicked key
|
|
14240
|
-
path.push(clickedKey);
|
|
14241
|
-
// Build the full path string
|
|
14242
|
-
if (path.length === 0)
|
|
14274
|
+
const linePathMap = this.buildLinePathMap(jsonData);
|
|
14275
|
+
// Exact match first; otherwise fall back to the nearest preceding mapped line
|
|
14276
|
+
// (handles clicks on closing brackets or blank columns).
|
|
14277
|
+
let segments = linePathMap.get(lineIndex);
|
|
14278
|
+
if (!segments) {
|
|
14279
|
+
for (let i = lineIndex - 1; i >= 0; i--) {
|
|
14280
|
+
const candidate = linePathMap.get(i);
|
|
14281
|
+
if (candidate) {
|
|
14282
|
+
segments = candidate;
|
|
14283
|
+
break;
|
|
14284
|
+
}
|
|
14285
|
+
}
|
|
14286
|
+
}
|
|
14287
|
+
if (!segments || segments.length === 0)
|
|
14243
14288
|
return null;
|
|
14244
|
-
|
|
14245
|
-
return fullPath;
|
|
14289
|
+
return prefix ? [prefix, ...segments] : [...segments];
|
|
14246
14290
|
}
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14291
|
+
formatSegmentsPath(segments) {
|
|
14292
|
+
return segments.reduce((acc, seg) => {
|
|
14293
|
+
if (typeof seg === 'number')
|
|
14294
|
+
return `${acc}[${seg}]`;
|
|
14295
|
+
return acc ? `${acc}.${seg}` : String(seg);
|
|
14296
|
+
}, '');
|
|
14297
|
+
}
|
|
14298
|
+
normalizeJsonPath(segments, prefix) {
|
|
14299
|
+
if (!segments || segments.length === 0)
|
|
14300
|
+
return '';
|
|
14301
|
+
if (!prefix || segments.length < 2) {
|
|
14302
|
+
return this.formatSegmentsPath(segments);
|
|
14254
14303
|
}
|
|
14255
|
-
const [, ...rest] =
|
|
14304
|
+
const [, ...rest] = segments;
|
|
14256
14305
|
if (prefix === 'requestHeaders' || prefix === 'responseHeaders') {
|
|
14257
|
-
return rest.length ? `headers
|
|
14306
|
+
return rest.length ? `headers${this.formatSegmentsPath(rest).startsWith('[') ? '' : '.'}${this.formatSegmentsPath(rest)}` : 'headers';
|
|
14258
14307
|
}
|
|
14259
14308
|
if (prefix === 'requestBody' || prefix === 'responseBody') {
|
|
14260
|
-
return rest.length ? `body
|
|
14309
|
+
return rest.length ? `body${this.formatSegmentsPath(rest).startsWith('[') ? '' : '.'}${this.formatSegmentsPath(rest)}` : 'body';
|
|
14261
14310
|
}
|
|
14262
|
-
return
|
|
14311
|
+
return this.formatSegmentsPath(segments);
|
|
14263
14312
|
}
|
|
14264
14313
|
/**
|
|
14265
14314
|
* Copy JSON path on double-click
|
|
@@ -14267,12 +14316,12 @@ class ApiStepComponent extends BaseStepComponent {
|
|
|
14267
14316
|
copyJsonPath(event, jsonData, prefix = '') {
|
|
14268
14317
|
if (!jsonData)
|
|
14269
14318
|
return;
|
|
14270
|
-
const
|
|
14271
|
-
if (
|
|
14319
|
+
const segments = this.getJsonPathFromClick(event, jsonData, prefix);
|
|
14320
|
+
if (segments && segments.length > 0) {
|
|
14272
14321
|
const source = prefix;
|
|
14273
|
-
const normalizedPath = this.normalizeJsonPath(
|
|
14322
|
+
const normalizedPath = this.normalizeJsonPath(segments, prefix);
|
|
14274
14323
|
navigator.clipboard.writeText(normalizedPath).then(() => {
|
|
14275
|
-
console.log('Copied to clipboard:',
|
|
14324
|
+
console.log('Copied to clipboard:', normalizedPath);
|
|
14276
14325
|
// Call the handler on every double-click (even on success, for now showing error as per requirement)
|
|
14277
14326
|
if (this.onJsonPathCopiedHandler) {
|
|
14278
14327
|
console.log('Calling onJsonPathCopiedHandler on success with:', { path: normalizedPath, source });
|