@fuentis/phoenix-ui 0.0.9-alpha.585 → 0.0.9-alpha.587

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.
@@ -2040,10 +2040,14 @@ class TableCaptionComponent {
2040
2040
  const fg = this.fb.group({});
2041
2041
  for (const f of filtersCfg) {
2042
2042
  const t = f.type ?? 'text';
2043
- // We DO NOT store null for multiselect (we store []),
2044
- // because null is the common cause of "everything selected" / broken filtering.
2043
+ /**
2044
+ * Important:
2045
+ * - For boolean-multiselect we keep SELECTED VALUES AS OPTION OBJECTS
2046
+ * (PrimeNG is much more stable with object model here than with boolean primitives),
2047
+ * but outgoing payload will still be boolean[].
2048
+ */
2045
2049
  const initialValue = t === 'boolean-multiselect'
2046
- ? this.normalizeBooleanMultiInitial(f.selected)
2050
+ ? this.normalizeBooleanMultiSelection(f.selected, f)
2047
2051
  : t === 'multiselect'
2048
2052
  ? this.normalizeMultiInitial(f.selected)
2049
2053
  : t === 'checkbox'
@@ -2100,28 +2104,66 @@ class TableCaptionComponent {
2100
2104
  return [];
2101
2105
  return Array.isArray(selected) ? selected : [selected];
2102
2106
  }
2103
- normalizeBooleanMultiInitial(selected) {
2104
- if (selected == null)
2105
- return [];
2106
- const arr = Array.isArray(selected) ? selected : [selected];
2107
- return arr
2107
+ // ---------------------------------------------------------------------------
2108
+ // boolean-multiselect helpers (UI value = option objects, output = boolean[])
2109
+ // ---------------------------------------------------------------------------
2110
+ coerceBool(x) {
2111
+ if (typeof x === 'boolean')
2112
+ return x;
2113
+ if (typeof x === 'string') {
2114
+ if (x === 'true')
2115
+ return true;
2116
+ if (x === 'false')
2117
+ return false;
2118
+ }
2119
+ return null;
2120
+ }
2121
+ /**
2122
+ * Returns array of OPTION OBJECTS that match selected booleans.
2123
+ * Accepts selected coming as:
2124
+ * - boolean | boolean[]
2125
+ * - 'true'/'false' | string[]
2126
+ * - option object(s) (already from UI)
2127
+ */
2128
+ normalizeBooleanMultiSelection(value, cfg) {
2129
+ const options = Array.isArray(cfg.options) ? cfg.options : [];
2130
+ // If UI already provides objects, keep them (PrimeNG uses them to render labels/checks)
2131
+ if (Array.isArray(value) && value.length && typeof value[0] === 'object') {
2132
+ return value;
2133
+ }
2134
+ const arr = Array.isArray(value) ? value : value == null ? [] : [value];
2135
+ const bools = arr
2136
+ .map((v) => this.coerceBool(v))
2137
+ .filter((v) => v !== null);
2138
+ const ov = cfg.optionValue ?? 'key';
2139
+ return bools
2140
+ .map((b) => options.find((o) => this.coerceBool(o?.[ov]) === b))
2141
+ .filter(Boolean);
2142
+ }
2143
+ extractBooleanArrayFromUiValue(value, cfg) {
2144
+ const ov = cfg.optionValue ?? 'key';
2145
+ const arr = Array.isArray(value) ? value : value == null ? [] : [value];
2146
+ const bools = arr
2108
2147
  .map((x) => {
2109
- if (typeof x === 'boolean')
2110
- return x;
2111
- if (typeof x === 'string')
2112
- return x === 'true';
2148
+ // UI sends option objects (preferred)
2113
2149
  if (x && typeof x === 'object')
2114
- return x.key === true || x.key === 'true';
2115
- return null;
2150
+ return this.coerceBool(x?.[ov]);
2151
+ // fallback if primitives ever come through
2152
+ return this.coerceBool(x);
2116
2153
  })
2117
- .filter((x) => typeof x === 'boolean');
2154
+ .filter((x) => x !== null);
2155
+ return bools;
2118
2156
  }
2157
+ // ---------------------------------------------------------------------------
2158
+ // Payload normalization
2159
+ // ---------------------------------------------------------------------------
2119
2160
  /**
2120
2161
  * Emit only meaningful values:
2121
2162
  * - text: '' removed
2122
2163
  * - dropdown: '' / null removed
2123
2164
  * - multiselect: [] removed
2124
- * - checkbox: always included only if true (optional: keep false too if you prefer)
2165
+ * - boolean-multiselect: boolean[] removed if empty (UI stores option objects)
2166
+ * - checkbox: included only if true
2125
2167
  */
2126
2168
  normalizeOutgoingPayload(raw, cfg) {
2127
2169
  const out = {};
@@ -2133,20 +2175,20 @@ class TableCaptionComponent {
2133
2175
  const t = f.type ?? 'text';
2134
2176
  const v = raw[key];
2135
2177
  if (t === 'boolean-multiselect') {
2136
- const arr = this.normalizeBooleanMultiInitial(v);
2137
- if (arr.length)
2138
- out[key] = arr; // boolean[]
2178
+ const bools = this.extractBooleanArrayFromUiValue(v, f);
2179
+ if (bools.length)
2180
+ out[key] = bools;
2139
2181
  continue;
2140
2182
  }
2141
2183
  if (t === 'multiselect') {
2142
2184
  const arr = Array.isArray(v) ? v.filter((x) => x != null && x !== '') : [];
2143
2185
  if (arr.length)
2144
- out[key] = arr; // keep array of optionValue(s)
2186
+ out[key] = arr;
2145
2187
  continue;
2146
2188
  }
2147
2189
  if (t === 'checkbox') {
2148
2190
  if (!!v)
2149
- out[key] = true; // keep only true (fast + clean)
2191
+ out[key] = true;
2150
2192
  continue;
2151
2193
  }
2152
2194
  // dropdown or text:
@@ -2158,6 +2200,10 @@ class TableCaptionComponent {
2158
2200
  /**
2159
2201
  * Restore persisted filters from localStorage.
2160
2202
  * We patchValue with emitEvent:false to avoid double emission.
2203
+ *
2204
+ * NOTE:
2205
+ * - boolean-multiselect is restored into OPTION OBJECTS (for UI),
2206
+ * even though persisted payload is boolean[].
2161
2207
  */
2162
2208
  restoreFilterState(cfg) {
2163
2209
  const stored = localStorage.getItem(this.FILTER_KEY);
@@ -2167,18 +2213,17 @@ class TableCaptionComponent {
2167
2213
  const parsed = JSON.parse(stored);
2168
2214
  if (!parsed || typeof parsed !== 'object')
2169
2215
  return;
2170
- // allow only known keys
2171
2216
  const known = new Set(cfg.map((f) => f.key));
2172
2217
  const patch = {};
2173
2218
  for (const k of Object.keys(parsed)) {
2174
2219
  if (known.has(k))
2175
2220
  patch[k] = parsed[k];
2176
2221
  }
2177
- // ensure multiselect never becomes null
2178
2222
  for (const f of cfg) {
2179
2223
  const t = f.type ?? 'text';
2180
2224
  if (t === 'boolean-multiselect') {
2181
- patch[f.key] = this.normalizeBooleanMultiInitial(patch[f.key]);
2225
+ // parsed payload is boolean[]; map -> option objects
2226
+ patch[f.key] = this.normalizeBooleanMultiSelection(patch[f.key], f);
2182
2227
  continue;
2183
2228
  }
2184
2229
  if (t === 'multiselect') {
@@ -2203,7 +2248,6 @@ class TableCaptionComponent {
2203
2248
  // ---------------------------------------------------------------------------
2204
2249
  // Columns
2205
2250
  // ---------------------------------------------------------------------------
2206
- /** Called from listbox selection */
2207
2251
  onColumnSelectionChange(newSelection) {
2208
2252
  this.selectedColumns = newSelection;
2209
2253
  this.applyColumns();
@@ -2263,10 +2307,6 @@ class TableCaptionComponent {
2263
2307
  // ---------------------------------------------------------------------------
2264
2308
  // Utilities
2265
2309
  // ---------------------------------------------------------------------------
2266
- /**
2267
- * Used by badge indicator to show "filters active".
2268
- * It checks raw form value, not normalized payload.
2269
- */
2270
2310
  hasFormValues() {
2271
2311
  const v = this.filtersForm?.value ?? {};
2272
2312
  return Object.values(v).some((x) => {
@@ -2280,11 +2320,9 @@ class TableCaptionComponent {
2280
2320
  });
2281
2321
  }
2282
2322
  clearSelectFilter(key) {
2283
- // Important: multiselect must be [] not null (prevents "selects everything" bugs)
2284
2323
  this.filtersForm.get(key)?.setValue([], { emitEvent: true });
2285
2324
  }
2286
2325
  resetFilters() {
2287
- // Reset form -> then also clear persisted state
2288
2326
  this.filtersForm.reset({}, { emitEvent: true });
2289
2327
  localStorage.removeItem(this.FILTER_KEY);
2290
2328
  this.filtersPopover?.hide();
@@ -2298,8 +2336,19 @@ class TableCaptionComponent {
2298
2336
  else
2299
2337
  localStorage.setItem(key, JSON.stringify(state));
2300
2338
  }
2339
+ getOptionLabelField(control) {
2340
+ return (control?.optionLabel ?? 'label');
2341
+ }
2342
+ getSelectedLabels(value, control) {
2343
+ const arr = Array.isArray(value) ? value : [];
2344
+ const labelField = this.getOptionLabelField(control);
2345
+ return arr
2346
+ .map((x) => (x && typeof x === 'object' ? x[labelField] : String(x ?? '')))
2347
+ .filter((s) => !!s)
2348
+ .join(', ');
2349
+ }
2301
2350
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TableCaptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2302
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: TableCaptionComponent, isStandalone: true, selector: "table-caption", inputs: { tableConfiguration: "tableConfiguration", columns: "columns", selectedItems: "selectedItems", filters: "filters" }, outputs: { applyFiltersEvent: "applyFiltersEvent", applyColumnsEvent: "applyColumnsEvent", searchChange: "searchChange", actionClick: "actionClick" }, viewQueries: [{ propertyName: "filtersPopover", first: true, predicate: ["filtersPopover"], descendants: true }, { propertyName: "columnsPopover", first: true, predicate: ["columnsPopover"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex justify-content-between border-bottom-1 border-300 p-2\">\n <!-- Left: Tabs + Global actions -->\n <div class=\"flex align-items-center gap-2\">\n <phoenix-data-table-tabs\n [attr.data-cy]=\"'table-select-button-' + (tableConfiguration?.tabs?.key ?? 'tabs')\"\n [actions]=\"tableConfiguration?.tabs\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n\n @for (action of (tableConfiguration?.globalActions ?? []); track action?.key ?? $index) {\n <phoenix-data-table-action\n [actionConfig]=\"action\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n\n <!-- Right: caption filters + icons + search + more -->\n <div class=\"flex align-items-center gap-2\">\n <!-- Filters in caption row -->\n @for (control of (filters ?? []); track control.key) {\n @if (control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('dropdown') {\n <p-select\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n }\n @case ('multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n }\n @case ('checkbox') {\n <p-checkbox\n binary=\"true\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n />\n }\n @default {\n <!-- text -->\n <input\n class=\"w-12rem\"\n pInputText\n [formControl]=\"$any(filtersForm.get(control.key))\"\n placeholder=\"{{ control.label | translate }}\"\n />\n }\n }\n }\n }\n\n <!-- Filter icon -->\n @if (tableConfiguration?.hasFilter) {\n @if (hasFormValues()) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n }\n }\n\n <!-- Columns icon -->\n @if (tableConfiguration?.hasColumns) {\n @if (columnsChanged) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n }\n }\n\n <!-- Search + More -->\n <div class=\"flex\">\n @if (tableConfiguration?.hasSearch) {\n <p-iconfield>\n <p-inputicon styleClass=\"pi pi-search\" />\n <input\n type=\"text\"\n pInputText\n (input)=\"emitSearch($any($event.target).value)\"\n placeholder=\"{{ 'SEARCH_KEYWORD' | translate }}\"\n />\n </p-iconfield>\n }\n\n @if (tableConfiguration?.hasMore && tableConfiguration?.moreActions) {\n <phoenix-data-table-action\n [actionConfig]=\"tableConfiguration.moreActions\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n </div>\n</div>\n\n<!-- Filters Popover -->\n<p-popover #filtersPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.FILTERS' | translate }}</div>\n <p-button\n icon=\"pi pi-refresh\"\n [text]=\"true\"\n label=\"{{ 'ACTION.CLEAR' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetFilters()\"\n />\n </div>\n\n <form [formGroup]=\"filtersForm\">\n @for (control of (filters ?? []); track control.key) {\n @if (!control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('checkbox') {\n <div class=\"mb-2\">\n <p-checkbox binary=\"true\" [formControlName]=\"control.key\" />\n <label class=\"ml-2\">{{ control.label | translate }}</label>\n </div>\n }\n @case ('dropdown') {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <p-select\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n </div>\n }\n @case ('multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n @default {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <input\n class=\"w-full\"\n pInputText\n [formControlName]=\"control.key\"\n placeholder=\"{{ control.label | translate }}\"\n />\n </div>\n }\n }\n }\n }\n </form>\n </div>\n</p-popover>\n\n<!-- Columns Popover -->\n<p-popover #columnsPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.COLUMNS' | translate }}</div>\n <p-button\n icon=\"pi pi-eye\"\n [text]=\"true\"\n label=\"{{ 'ACTION.SHOW_ALL' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetColumns()\"\n />\n </div>\n\n <p-listbox\n [options]=\"columns\"\n [ngModel]=\"_selectedColumns\"\n (ngModelChange)=\"onColumnSelectionChange($event)\"\n [multiple]=\"true\"\n [metaKeySelection]=\"false\"\n [listStyle]=\"{ 'max-height': '420px' }\"\n [attr.data-cy]=\"'table-list-box'\"\n >\n <ng-template pTemplate=\"item\" let-item>\n @if (item | isSelected : selectedColumns) {\n <i class=\"pi pi-eye\"></i>\n } @else {\n <i class=\"pi pi-eye-slash\"></i>\n }\n <span class=\"ml-2\">{{ item.header | translate }}</span>\n </ng-template>\n </p-listbox>\n </div>\n</p-popover>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3$3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i5$2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i5.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i4$4.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: ListboxModule }, { kind: "component", type: i10.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["hostName", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "dropListData", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i12.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i13.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: FloatLabelModule }, { kind: "component", type: i14.FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: PhoenixDataTableActionComponent, selector: "phoenix-data-table-action", inputs: ["actionConfig", "rowData"], outputs: ["actionClick"] }, { kind: "component", type: PhoenixDataTableTabsComponent, selector: "phoenix-data-table-tabs", inputs: ["actions"], outputs: ["actionClick"] }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }] });
2351
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: TableCaptionComponent, isStandalone: true, selector: "table-caption", inputs: { tableConfiguration: "tableConfiguration", columns: "columns", selectedItems: "selectedItems", filters: "filters" }, outputs: { applyFiltersEvent: "applyFiltersEvent", applyColumnsEvent: "applyColumnsEvent", searchChange: "searchChange", actionClick: "actionClick" }, viewQueries: [{ propertyName: "filtersPopover", first: true, predicate: ["filtersPopover"], descendants: true }, { propertyName: "columnsPopover", first: true, predicate: ["columnsPopover"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex justify-content-between border-bottom-1 border-300 p-2\">\n <!-- Left: Tabs + Global actions -->\n <div class=\"flex align-items-center gap-2\">\n <phoenix-data-table-tabs\n [attr.data-cy]=\"'table-select-button-' + (tableConfiguration?.tabs?.key ?? 'tabs')\"\n [actions]=\"tableConfiguration?.tabs\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n\n @for (action of (tableConfiguration?.globalActions ?? []); track action?.key ?? $index) {\n <phoenix-data-table-action\n [actionConfig]=\"action\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n\n <!-- Right: caption filters + icons + search + more -->\n <div class=\"flex align-items-center gap-2\">\n <!-- Filters in caption row -->\n @for (control of (filters ?? []); track control.key) {\n @if (control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('dropdown') {\n <p-select\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n }\n\n @case ('multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n }\n\n @case ('boolean-multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n >\n <ng-template pTemplate=\"selectedItems\" let-value>\n @if ((value?.length ?? 0) === 0) {\n <span class=\"p-2 text-500\">{{ control.label | translate }}</span>\n } @else {\n <span class=\"p-2\">{{ getSelectedLabels(value, control) }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-item>\n <span>{{ item?.[control.optionLabel ?? 'label'] }}</span>\n </ng-template>\n </p-multiselect>\n }\n\n @case ('checkbox') {\n <p-checkbox binary=\"true\" [formControl]=\"$any(filtersForm.get(control.key))\" />\n }\n\n @default {\n <input\n class=\"w-12rem\"\n pInputText\n [formControl]=\"$any(filtersForm.get(control.key))\"\n placeholder=\"{{ control.label | translate }}\"\n />\n }\n }\n }\n }\n\n <!-- Filter icon -->\n @if (tableConfiguration?.hasFilter) {\n @if (hasFormValues()) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n }\n }\n\n <!-- Columns icon -->\n @if (tableConfiguration?.hasColumns) {\n @if (columnsChanged) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n }\n }\n\n <!-- Search + More -->\n <div class=\"flex\">\n @if (tableConfiguration?.hasSearch) {\n <p-iconfield>\n <p-inputicon styleClass=\"pi pi-search\" />\n <input\n type=\"text\"\n pInputText\n (input)=\"emitSearch($any($event.target).value)\"\n placeholder=\"{{ 'SEARCH_KEYWORD' | translate }}\"\n />\n </p-iconfield>\n }\n\n @if (tableConfiguration?.hasMore && tableConfiguration?.moreActions) {\n <phoenix-data-table-action\n [actionConfig]=\"tableConfiguration.moreActions\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n </div>\n</div>\n\n<!-- Filters Popover -->\n<p-popover #filtersPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.FILTERS' | translate }}</div>\n <p-button\n icon=\"pi pi-refresh\"\n [text]=\"true\"\n label=\"{{ 'ACTION.CLEAR' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetFilters()\"\n />\n </div>\n\n <form [formGroup]=\"filtersForm\">\n @for (control of (filters ?? []); track control.key) {\n @if (!control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('checkbox') {\n <div class=\"mb-2\">\n <p-checkbox binary=\"true\" [formControlName]=\"control.key\" />\n <label class=\"ml-2\">{{ control.label | translate }}</label>\n </div>\n }\n\n @case ('dropdown') {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <p-select\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n </div>\n }\n\n @case ('multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n\n @case ('boolean-multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n >\n <ng-template pTemplate=\"selectedItems\" let-value>\n @if ((value?.length ?? 0) === 0) {\n <span class=\"p-2 text-500\">{{ control.label | translate }}</span>\n } @else {\n <span class=\"p-2\">{{ getSelectedLabels(value, control) }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-item>\n <span>{{ item?.[control.optionLabel ?? 'label'] }}</span>\n </ng-template>\n </p-multiselect>\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n\n @default {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <input\n class=\"w-full\"\n pInputText\n [formControlName]=\"control.key\"\n placeholder=\"{{ control.label | translate }}\"\n />\n </div>\n }\n }\n }\n }\n </form>\n </div>\n</p-popover>\n\n<!-- Columns Popover -->\n<p-popover #columnsPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.COLUMNS' | translate }}</div>\n <p-button\n icon=\"pi pi-eye\"\n [text]=\"true\"\n label=\"{{ 'ACTION.SHOW_ALL' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetColumns()\"\n />\n </div>\n\n <p-listbox\n [options]=\"columns\"\n [ngModel]=\"_selectedColumns\"\n (ngModelChange)=\"onColumnSelectionChange($event)\"\n [multiple]=\"true\"\n [metaKeySelection]=\"false\"\n [listStyle]=\"{ 'max-height': '420px' }\"\n [attr.data-cy]=\"'table-list-box'\"\n >\n <ng-template pTemplate=\"item\" let-item>\n @if (item | isSelected : selectedColumns) {\n <i class=\"pi pi-eye\"></i>\n } @else {\n <i class=\"pi pi-eye-slash\"></i>\n }\n <span class=\"ml-2\">{{ item.header | translate }}</span>\n </ng-template>\n </p-listbox>\n </div>\n</p-popover>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3$3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i5$2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i5.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i4$4.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: ListboxModule }, { kind: "component", type: i10.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["hostName", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "dropListData", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i12.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i13.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: FloatLabelModule }, { kind: "component", type: i14.FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: PhoenixDataTableActionComponent, selector: "phoenix-data-table-action", inputs: ["actionConfig", "rowData"], outputs: ["actionClick"] }, { kind: "component", type: PhoenixDataTableTabsComponent, selector: "phoenix-data-table-tabs", inputs: ["actions"], outputs: ["actionClick"] }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }] });
2303
2352
  }
2304
2353
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TableCaptionComponent, decorators: [{
2305
2354
  type: Component,
@@ -2323,7 +2372,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2323
2372
  PhoenixDataTableActionComponent,
2324
2373
  PhoenixDataTableTabsComponent,
2325
2374
  IsSelectedPipe,
2326
- ], template: "<div class=\"flex justify-content-between border-bottom-1 border-300 p-2\">\n <!-- Left: Tabs + Global actions -->\n <div class=\"flex align-items-center gap-2\">\n <phoenix-data-table-tabs\n [attr.data-cy]=\"'table-select-button-' + (tableConfiguration?.tabs?.key ?? 'tabs')\"\n [actions]=\"tableConfiguration?.tabs\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n\n @for (action of (tableConfiguration?.globalActions ?? []); track action?.key ?? $index) {\n <phoenix-data-table-action\n [actionConfig]=\"action\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n\n <!-- Right: caption filters + icons + search + more -->\n <div class=\"flex align-items-center gap-2\">\n <!-- Filters in caption row -->\n @for (control of (filters ?? []); track control.key) {\n @if (control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('dropdown') {\n <p-select\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n }\n @case ('multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n }\n @case ('checkbox') {\n <p-checkbox\n binary=\"true\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n />\n }\n @default {\n <!-- text -->\n <input\n class=\"w-12rem\"\n pInputText\n [formControl]=\"$any(filtersForm.get(control.key))\"\n placeholder=\"{{ control.label | translate }}\"\n />\n }\n }\n }\n }\n\n <!-- Filter icon -->\n @if (tableConfiguration?.hasFilter) {\n @if (hasFormValues()) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n }\n }\n\n <!-- Columns icon -->\n @if (tableConfiguration?.hasColumns) {\n @if (columnsChanged) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n }\n }\n\n <!-- Search + More -->\n <div class=\"flex\">\n @if (tableConfiguration?.hasSearch) {\n <p-iconfield>\n <p-inputicon styleClass=\"pi pi-search\" />\n <input\n type=\"text\"\n pInputText\n (input)=\"emitSearch($any($event.target).value)\"\n placeholder=\"{{ 'SEARCH_KEYWORD' | translate }}\"\n />\n </p-iconfield>\n }\n\n @if (tableConfiguration?.hasMore && tableConfiguration?.moreActions) {\n <phoenix-data-table-action\n [actionConfig]=\"tableConfiguration.moreActions\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n </div>\n</div>\n\n<!-- Filters Popover -->\n<p-popover #filtersPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.FILTERS' | translate }}</div>\n <p-button\n icon=\"pi pi-refresh\"\n [text]=\"true\"\n label=\"{{ 'ACTION.CLEAR' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetFilters()\"\n />\n </div>\n\n <form [formGroup]=\"filtersForm\">\n @for (control of (filters ?? []); track control.key) {\n @if (!control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('checkbox') {\n <div class=\"mb-2\">\n <p-checkbox binary=\"true\" [formControlName]=\"control.key\" />\n <label class=\"ml-2\">{{ control.label | translate }}</label>\n </div>\n }\n @case ('dropdown') {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <p-select\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n </div>\n }\n @case ('multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n @default {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <input\n class=\"w-full\"\n pInputText\n [formControlName]=\"control.key\"\n placeholder=\"{{ control.label | translate }}\"\n />\n </div>\n }\n }\n }\n }\n </form>\n </div>\n</p-popover>\n\n<!-- Columns Popover -->\n<p-popover #columnsPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.COLUMNS' | translate }}</div>\n <p-button\n icon=\"pi pi-eye\"\n [text]=\"true\"\n label=\"{{ 'ACTION.SHOW_ALL' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetColumns()\"\n />\n </div>\n\n <p-listbox\n [options]=\"columns\"\n [ngModel]=\"_selectedColumns\"\n (ngModelChange)=\"onColumnSelectionChange($event)\"\n [multiple]=\"true\"\n [metaKeySelection]=\"false\"\n [listStyle]=\"{ 'max-height': '420px' }\"\n [attr.data-cy]=\"'table-list-box'\"\n >\n <ng-template pTemplate=\"item\" let-item>\n @if (item | isSelected : selectedColumns) {\n <i class=\"pi pi-eye\"></i>\n } @else {\n <i class=\"pi pi-eye-slash\"></i>\n }\n <span class=\"ml-2\">{{ item.header | translate }}</span>\n </ng-template>\n </p-listbox>\n </div>\n</p-popover>" }]
2375
+ ], template: "<div class=\"flex justify-content-between border-bottom-1 border-300 p-2\">\n <!-- Left: Tabs + Global actions -->\n <div class=\"flex align-items-center gap-2\">\n <phoenix-data-table-tabs\n [attr.data-cy]=\"'table-select-button-' + (tableConfiguration?.tabs?.key ?? 'tabs')\"\n [actions]=\"tableConfiguration?.tabs\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n\n @for (action of (tableConfiguration?.globalActions ?? []); track action?.key ?? $index) {\n <phoenix-data-table-action\n [actionConfig]=\"action\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n\n <!-- Right: caption filters + icons + search + more -->\n <div class=\"flex align-items-center gap-2\">\n <!-- Filters in caption row -->\n @for (control of (filters ?? []); track control.key) {\n @if (control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('dropdown') {\n <p-select\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n }\n\n @case ('multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n }\n\n @case ('boolean-multiselect') {\n <p-multiselect\n class=\"w-12rem\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [formControl]=\"$any(filtersForm.get(control.key))\"\n [showClear]=\"true\"\n placeholder=\"{{ control.label | translate }}\"\n (onClear)=\"clearSelectFilter(control.key)\"\n >\n <ng-template pTemplate=\"selectedItems\" let-value>\n @if ((value?.length ?? 0) === 0) {\n <span class=\"p-2 text-500\">{{ control.label | translate }}</span>\n } @else {\n <span class=\"p-2\">{{ getSelectedLabels(value, control) }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-item>\n <span>{{ item?.[control.optionLabel ?? 'label'] }}</span>\n </ng-template>\n </p-multiselect>\n }\n\n @case ('checkbox') {\n <p-checkbox binary=\"true\" [formControl]=\"$any(filtersForm.get(control.key))\" />\n }\n\n @default {\n <input\n class=\"w-12rem\"\n pInputText\n [formControl]=\"$any(filtersForm.get(control.key))\"\n placeholder=\"{{ control.label | translate }}\"\n />\n }\n }\n }\n }\n\n <!-- Filter icon -->\n @if (tableConfiguration?.hasFilter) {\n @if (hasFormValues()) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"filtersPopover?.toggle($event)\">\n <i class=\"pi pi-filter\"></i>\n </span>\n }\n }\n\n <!-- Columns icon -->\n @if (tableConfiguration?.hasColumns) {\n @if (columnsChanged) {\n <p-overlaybadge class=\"p-2\" severity=\"danger\">\n <span pRipple class=\"cursor-pointer\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n </p-overlaybadge>\n } @else {\n <span pRipple class=\"cursor-pointer p-2\" (click)=\"columnsPopover?.toggle($event)\">\n <i class=\"pi pi-sliders-v\"></i>\n </span>\n }\n }\n\n <!-- Search + More -->\n <div class=\"flex\">\n @if (tableConfiguration?.hasSearch) {\n <p-iconfield>\n <p-inputicon styleClass=\"pi pi-search\" />\n <input\n type=\"text\"\n pInputText\n (input)=\"emitSearch($any($event.target).value)\"\n placeholder=\"{{ 'SEARCH_KEYWORD' | translate }}\"\n />\n </p-iconfield>\n }\n\n @if (tableConfiguration?.hasMore && tableConfiguration?.moreActions) {\n <phoenix-data-table-action\n [actionConfig]=\"tableConfiguration.moreActions\"\n (actionClick)=\"actionClick.emit($event)\"\n />\n }\n </div>\n </div>\n</div>\n\n<!-- Filters Popover -->\n<p-popover #filtersPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.FILTERS' | translate }}</div>\n <p-button\n icon=\"pi pi-refresh\"\n [text]=\"true\"\n label=\"{{ 'ACTION.CLEAR' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetFilters()\"\n />\n </div>\n\n <form [formGroup]=\"filtersForm\">\n @for (control of (filters ?? []); track control.key) {\n @if (!control.isInCaption) {\n @switch (control.type ?? (control.options?.length ? 'dropdown' : 'text')) {\n @case ('checkbox') {\n <div class=\"mb-2\">\n <p-checkbox binary=\"true\" [formControlName]=\"control.key\" />\n <label class=\"ml-2\">{{ control.label | translate }}</label>\n </div>\n }\n\n @case ('dropdown') {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <p-select\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"filtersForm.get(control.key)?.setValue('')\"\n />\n </div>\n }\n\n @case ('multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [optionValue]=\"control.optionValue ?? 'key'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n />\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n\n @case ('boolean-multiselect') {\n <div class=\"mb-2\">\n <p-floatlabel variant=\"on\">\n <p-multiselect\n class=\"w-full\"\n [options]=\"control.options ?? []\"\n [optionLabel]=\"control.optionLabel ?? 'label'\"\n [showClear]=\"true\"\n [formControlName]=\"control.key\"\n (onClear)=\"clearSelectFilter(control.key)\"\n >\n <ng-template pTemplate=\"selectedItems\" let-value>\n @if ((value?.length ?? 0) === 0) {\n <span class=\"p-2 text-500\">{{ control.label | translate }}</span>\n } @else {\n <span class=\"p-2\">{{ getSelectedLabels(value, control) }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-item>\n <span>{{ item?.[control.optionLabel ?? 'label'] }}</span>\n </ng-template>\n </p-multiselect>\n <label>{{ control.label | translate }}</label>\n </p-floatlabel>\n </div>\n }\n\n @default {\n <div class=\"mb-2\">\n <label class=\"mb-2 block\">{{ control.label | translate }}</label>\n <input\n class=\"w-full\"\n pInputText\n [formControlName]=\"control.key\"\n placeholder=\"{{ control.label | translate }}\"\n />\n </div>\n }\n }\n }\n }\n </form>\n </div>\n</p-popover>\n\n<!-- Columns Popover -->\n<p-popover #columnsPopover>\n <div class=\"flex flex-column gap-3 w-25rem\">\n <div class=\"flex justify-content-between align-items-center\">\n <div class=\"text-lg font-bold\">{{ 'LABELS.COLUMNS' | translate }}</div>\n <p-button\n icon=\"pi pi-eye\"\n [text]=\"true\"\n label=\"{{ 'ACTION.SHOW_ALL' | translate }}\"\n class=\"mr-2\"\n (click)=\"resetColumns()\"\n />\n </div>\n\n <p-listbox\n [options]=\"columns\"\n [ngModel]=\"_selectedColumns\"\n (ngModelChange)=\"onColumnSelectionChange($event)\"\n [multiple]=\"true\"\n [metaKeySelection]=\"false\"\n [listStyle]=\"{ 'max-height': '420px' }\"\n [attr.data-cy]=\"'table-list-box'\"\n >\n <ng-template pTemplate=\"item\" let-item>\n @if (item | isSelected : selectedColumns) {\n <i class=\"pi pi-eye\"></i>\n } @else {\n <i class=\"pi pi-eye-slash\"></i>\n }\n <span class=\"ml-2\">{{ item.header | translate }}</span>\n </ng-template>\n </p-listbox>\n </div>\n</p-popover>" }]
2327
2376
  }], propDecorators: { filtersPopover: [{
2328
2377
  type: ViewChild,
2329
2378
  args: ['filtersPopover']