@fuentis/phoenix-ui 0.0.9-alpha.632 → 0.0.9-alpha.634

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.
@@ -67,11 +67,13 @@ import * as i5$2 from 'primeng/multiselect';
67
67
  import { MultiSelectModule } from 'primeng/multiselect';
68
68
  import * as i10 from 'primeng/listbox';
69
69
  import { ListboxModule } from 'primeng/listbox';
70
- import * as i12 from 'primeng/overlaybadge';
70
+ import * as i12 from 'primeng/slider';
71
+ import { SliderModule } from 'primeng/slider';
72
+ import * as i13 from 'primeng/overlaybadge';
71
73
  import { OverlayBadgeModule } from 'primeng/overlaybadge';
72
- import * as i13 from 'primeng/ripple';
74
+ import * as i14 from 'primeng/ripple';
73
75
  import { RippleModule } from 'primeng/ripple';
74
- import * as i14 from 'primeng/floatlabel';
76
+ import * as i15 from 'primeng/floatlabel';
75
77
  import { FloatLabelModule } from 'primeng/floatlabel';
76
78
  import * as i2$3 from 'primeng/selectbutton';
77
79
  import { SelectButtonModule } from 'primeng/selectbutton';
@@ -1781,6 +1783,7 @@ var tableFilterType;
1781
1783
  tableFilterType["BOOLEAN"] = "boolean";
1782
1784
  tableFilterType["MULTISELECT"] = "multiselect";
1783
1785
  tableFilterType["COMPLETNESS"] = "completness";
1786
+ tableFilterType["RANGE"] = "range";
1784
1787
  // ICON = 'icon',
1785
1788
  })(tableFilterType || (tableFilterType = {}));
1786
1789
 
@@ -2070,7 +2073,9 @@ class TableCaptionComponent {
2070
2073
  ? this.normalizeMultiInitial(f.selected)
2071
2074
  : t === 'checkbox'
2072
2075
  ? !!f.selected
2073
- : f.selected ?? '';
2076
+ : t === 'range'
2077
+ ? this.normalizeRangeInitial(f.selected, f)
2078
+ : f.selected ?? '';
2074
2079
  fg.addControl(f.key, this.fb.control(initialValue));
2075
2080
  }
2076
2081
  this.filtersForm = fg;
@@ -2147,6 +2152,35 @@ class TableCaptionComponent {
2147
2152
  return Array.isArray(selected) ? selected : [selected];
2148
2153
  }
2149
2154
  // ---------------------------------------------------------------------------
2155
+ // range helpers (UI value = [from, to]; output = { type:'range', min, max })
2156
+ // ---------------------------------------------------------------------------
2157
+ /** Full bounds of a range filter as [min, max]. */
2158
+ rangeBounds(cfg) {
2159
+ const min = Number.isFinite(cfg.min) ? cfg.min : 0;
2160
+ const max = Number.isFinite(cfg.max) ? cfg.max : 100;
2161
+ return min <= max ? [min, max] : [max, min];
2162
+ }
2163
+ /** Clamp a selected [from, to] to the config bounds; defaults to full bounds. */
2164
+ normalizeRangeInitial(selected, cfg) {
2165
+ const [min, max] = this.rangeBounds(cfg);
2166
+ const arr = Array.isArray(selected) ? selected : [];
2167
+ let from = Number(arr[0]);
2168
+ let to = Number(arr[1]);
2169
+ if (!Number.isFinite(from))
2170
+ from = min;
2171
+ if (!Number.isFinite(to))
2172
+ to = max;
2173
+ from = Math.min(Math.max(from, min), max);
2174
+ to = Math.min(Math.max(to, min), max);
2175
+ return from <= to ? [from, to] : [to, from];
2176
+ }
2177
+ /** A range is "active" only when it is narrower than the full bounds. */
2178
+ isRangeActive(value, cfg) {
2179
+ const [min, max] = this.rangeBounds(cfg);
2180
+ const [from, to] = this.normalizeRangeInitial(value, cfg);
2181
+ return from > min || to < max;
2182
+ }
2183
+ // ---------------------------------------------------------------------------
2150
2184
  // boolean-multiselect helpers (UI value = option objects, output = boolean[])
2151
2185
  // ---------------------------------------------------------------------------
2152
2186
  coerceBool(x) {
@@ -2233,6 +2267,13 @@ class TableCaptionComponent {
2233
2267
  out[key] = true;
2234
2268
  continue;
2235
2269
  }
2270
+ if (t === 'range') {
2271
+ if (this.isRangeActive(v, f)) {
2272
+ const [min, max] = this.normalizeRangeInitial(v, f);
2273
+ out[key] = { type: 'range', min, max };
2274
+ }
2275
+ continue;
2276
+ }
2236
2277
  // dropdown or text:
2237
2278
  if (v != null && v !== '')
2238
2279
  out[key] = v;
@@ -2280,6 +2321,17 @@ class TableCaptionComponent {
2280
2321
  patch[f.key] = !!patch[f.key];
2281
2322
  continue;
2282
2323
  }
2324
+ if (t === 'range') {
2325
+ // persisted payload is { type:'range', min, max }; map -> [from, to]
2326
+ const stored = patch[f.key];
2327
+ if (stored == null) {
2328
+ delete patch[f.key];
2329
+ }
2330
+ else {
2331
+ patch[f.key] = this.normalizeRangeInitial([stored?.min, stored?.max], f);
2332
+ }
2333
+ continue;
2334
+ }
2283
2335
  }
2284
2336
  this.filtersForm.patchValue(patch, { emitEvent: false });
2285
2337
  }
@@ -2351,7 +2403,11 @@ class TableCaptionComponent {
2351
2403
  // ---------------------------------------------------------------------------
2352
2404
  hasFormValues() {
2353
2405
  const v = this.filtersForm?.value ?? {};
2354
- return Object.values(v).some((x) => {
2406
+ const cfgByKey = new Map(this.normalizeFilters(this.filters).map((f) => [f.key, f]));
2407
+ return Object.entries(v).some(([key, x]) => {
2408
+ const cfg = cfgByKey.get(key);
2409
+ if (cfg?.type === 'range')
2410
+ return this.isRangeActive(x, cfg);
2355
2411
  if (x == null || x === '')
2356
2412
  return false;
2357
2413
  if (Array.isArray(x))
@@ -2364,6 +2420,15 @@ class TableCaptionComponent {
2364
2420
  clearSelectFilter(key) {
2365
2421
  this.filtersForm.get(key)?.setValue([], { emitEvent: true });
2366
2422
  }
2423
+ /** True when a range control has been narrowed below its full bounds. */
2424
+ isRangeControlActive(control) {
2425
+ return this.isRangeActive(this.filtersForm.get(control.key)?.value, control);
2426
+ }
2427
+ /** Reset a range filter back to its full [min, max] bounds. */
2428
+ resetRangeFilter(control) {
2429
+ const [min, max] = this.rangeBounds(control);
2430
+ this.filtersForm.get(control.key)?.setValue([min, max], { emitEvent: true });
2431
+ }
2367
2432
  resetFilters() {
2368
2433
  this.filtersForm.reset({}, { emitEvent: true });
2369
2434
  localStorage.removeItem(this.FILTER_KEY);
@@ -2390,7 +2455,7 @@ class TableCaptionComponent {
2390
2455
  .join(', ');
2391
2456
  }
2392
2457
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TableCaptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2393
- 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", filterValidValues: "filterValidValues" }, 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$2.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$4.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$3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.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$5.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$4.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: i4$2.TranslatePipe, name: "translate" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }] });
2458
+ 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", filterValidValues: "filterValidValues" }, 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 ('range') {\n <div class=\"mb-2 px-3 py-2\">\n <label class=\"block mb-2\">{{ control.label | translate }}</label>\n <div class=\"flex justify-content-between text-sm text-600 mb-2\">\n <span>{{ filtersForm.get(control.key)?.value?.[0] ?? control.min }}</span>\n <span>{{ filtersForm.get(control.key)?.value?.[1] ?? control.max }}</span>\n </div>\n <div class=\"flex align-items-center gap-2\">\n <div class=\"flex-1 px-2\">\n <p-slider\n class=\"w-full\"\n [range]=\"true\"\n [min]=\"control.min ?? 0\"\n [max]=\"control.max ?? 100\"\n [step]=\"control.step ?? 1\"\n [formControlName]=\"control.key\"\n />\n </div>\n <p-button\n icon=\"pi pi-times\"\n [text]=\"true\"\n [rounded]=\"true\"\n size=\"small\"\n [disabled]=\"!isRangeControlActive(control)\"\n (click)=\"resetRangeFilter(control)\"\n />\n </div>\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$2.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$4.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$3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.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$5.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$4.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: SliderModule }, { kind: "component", type: i12.Slider, selector: "p-slider", inputs: ["animate", "min", "max", "orientation", "step", "range", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i13.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i14.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: FloatLabelModule }, { kind: "component", type: i15.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: i4$2.TranslatePipe, name: "translate" }, { kind: "pipe", type: IsSelectedPipe, name: "isSelected" }] });
2394
2459
  }
2395
2460
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TableCaptionComponent, decorators: [{
2396
2461
  type: Component,
@@ -2407,6 +2472,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2407
2472
  IconFieldModule,
2408
2473
  ListboxModule,
2409
2474
  CheckboxModule,
2475
+ SliderModule,
2410
2476
  OverlayBadgeModule,
2411
2477
  RippleModule,
2412
2478
  FloatLabelModule,
@@ -2414,7 +2480,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2414
2480
  PhoenixDataTableActionComponent,
2415
2481
  PhoenixDataTableTabsComponent,
2416
2482
  IsSelectedPipe,
2417
- ], 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>" }]
2483
+ ], 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 ('range') {\n <div class=\"mb-2 px-3 py-2\">\n <label class=\"block mb-2\">{{ control.label | translate }}</label>\n <div class=\"flex justify-content-between text-sm text-600 mb-2\">\n <span>{{ filtersForm.get(control.key)?.value?.[0] ?? control.min }}</span>\n <span>{{ filtersForm.get(control.key)?.value?.[1] ?? control.max }}</span>\n </div>\n <div class=\"flex align-items-center gap-2\">\n <div class=\"flex-1 px-2\">\n <p-slider\n class=\"w-full\"\n [range]=\"true\"\n [min]=\"control.min ?? 0\"\n [max]=\"control.max ?? 100\"\n [step]=\"control.step ?? 1\"\n [formControlName]=\"control.key\"\n />\n </div>\n <p-button\n icon=\"pi pi-times\"\n [text]=\"true\"\n [rounded]=\"true\"\n size=\"small\"\n [disabled]=\"!isRangeControlActive(control)\"\n (click)=\"resetRangeFilter(control)\"\n />\n </div>\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>" }]
2418
2484
  }], propDecorators: { filtersPopover: [{
2419
2485
  type: ViewChild,
2420
2486
  args: ['filtersPopover']
@@ -3446,6 +3512,13 @@ class TableComponent {
3446
3512
  let hasStale = false;
3447
3513
  const result = {};
3448
3514
  for (const [key, filterValue] of Object.entries(this.currentFilters)) {
3515
+ // Range filters carry numeric bounds, not enumerable option values — never stale.
3516
+ if (filterValue &&
3517
+ typeof filterValue === 'object' &&
3518
+ !Array.isArray(filterValue) &&
3519
+ filterValue.type === 'range') {
3520
+ continue;
3521
+ }
3449
3522
  const values = Array.isArray(filterValue) ? filterValue : filterValue != null ? [filterValue] : [];
3450
3523
  if (!values.length)
3451
3524
  continue;
@@ -3501,6 +3574,16 @@ class TableComponent {
3501
3574
  (Array.isArray(filterValue) && filterValue.length === 0))
3502
3575
  return true;
3503
3576
  const itemValue = this.getNestedValue(item, key);
3577
+ // Range filter: { type: 'range', min, max } -> numeric between check
3578
+ if (filterValue &&
3579
+ typeof filterValue === 'object' &&
3580
+ !Array.isArray(filterValue) &&
3581
+ filterValue.type === 'range') {
3582
+ const num = Number(itemValue);
3583
+ if (!Number.isFinite(num))
3584
+ return false;
3585
+ return num >= filterValue.min && num <= filterValue.max;
3586
+ }
3504
3587
  // Ako je itemValue niz (kao u scopes)
3505
3588
  if (Array.isArray(itemValue)) {
3506
3589
  return filterValue.some((option) => {
@@ -10263,16 +10346,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10263
10346
  type: Input
10264
10347
  }] } });
10265
10348
 
10349
+ /**
10350
+ * Splits a flat list of fields into row chunks for grid rendering.
10351
+ * A field with `style.newRow === true` always starts a fresh row,
10352
+ * regardless of how much space is left in the current row.
10353
+ * Fields without `newRow` keep flowing into the current row (existing
10354
+ * PrimeFlex wrap-by-colWidth behavior is preserved within each chunk).
10355
+ */
10356
+ function splitControlsIntoRows(controls) {
10357
+ if (!controls || !controls.length)
10358
+ return [];
10359
+ const rows = [];
10360
+ let current = [];
10361
+ for (const field of controls) {
10362
+ if (field?.style?.newRow && current.length) {
10363
+ rows.push(current);
10364
+ current = [];
10365
+ }
10366
+ current.push(field);
10367
+ }
10368
+ if (current.length)
10369
+ rows.push(current);
10370
+ return rows;
10371
+ }
10372
+
10266
10373
  class MetaFormGroupV2Component {
10267
10374
  group;
10268
10375
  form;
10269
10376
  readOnly = false;
10377
+ /** Group's fields split into row chunks, honoring `style.newRow` hard breaks. */
10378
+ get rows() {
10379
+ return splitControlsIntoRows(this.group?.ctrl);
10380
+ }
10270
10381
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MetaFormGroupV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
10271
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MetaFormGroupV2Component, isStandalone: true, selector: "phoenix-meta-form-group-v2", inputs: { group: "group", form: "form", readOnly: "readOnly" }, ngImport: i0, template: "<div class=\"pho-group\" [formGroup]=\"form\">\n <div class=\"grid\">\n @for (f of group.ctrl; track f?.configuration?.key) {\n <div [ngClass]=\"f.style.colWidth || 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n\n</div>", styles: [".pho-group{margin-bottom:16px}.pho-group__title{font-weight:700;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: MetaFormFieldV2Component, selector: "phoenix-meta-form-field-v2", inputs: ["field", "form", "readOnly", "disableForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10382
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MetaFormGroupV2Component, isStandalone: true, selector: "phoenix-meta-form-group-v2", inputs: { group: "group", form: "form", readOnly: "readOnly" }, ngImport: i0, template: "<div class=\"pho-group\" [formGroup]=\"form\">\n @for (row of rows; track $index) {\n <div class=\"grid\">\n @for (f of row; track f?.configuration?.key) {\n <div [ngClass]=\"f.style.colWidth || 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n\n</div>", styles: [".pho-group{margin-bottom:16px}.pho-group__title{font-weight:700;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: MetaFormFieldV2Component, selector: "phoenix-meta-form-field-v2", inputs: ["field", "form", "readOnly", "disableForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10272
10383
  }
10273
10384
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MetaFormGroupV2Component, decorators: [{
10274
10385
  type: Component,
10275
- args: [{ selector: 'phoenix-meta-form-group-v2', standalone: true, imports: [CommonModule, ReactiveFormsModule, MetaFormFieldV2Component], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pho-group\" [formGroup]=\"form\">\n <div class=\"grid\">\n @for (f of group.ctrl; track f?.configuration?.key) {\n <div [ngClass]=\"f.style.colWidth || 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n\n</div>", styles: [".pho-group{margin-bottom:16px}.pho-group__title{font-weight:700;margin-bottom:8px}\n"] }]
10386
+ args: [{ selector: 'phoenix-meta-form-group-v2', standalone: true, imports: [CommonModule, ReactiveFormsModule, MetaFormFieldV2Component], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pho-group\" [formGroup]=\"form\">\n @for (row of rows; track $index) {\n <div class=\"grid\">\n @for (f of row; track f?.configuration?.key) {\n <div [ngClass]=\"f.style.colWidth || 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n\n</div>", styles: [".pho-group{margin-bottom:16px}.pho-group__title{font-weight:700;margin-bottom:8px}\n"] }]
10276
10387
  }], propDecorators: { group: [{
10277
10388
  type: Input,
10278
10389
  args: [{ required: true }]
@@ -10885,6 +10996,13 @@ class MetaFormV2Component {
10885
10996
  get flatControls() {
10886
10997
  return this.config?.controls ?? [];
10887
10998
  }
10999
+ /**
11000
+ * Flat schema split into row chunks, honoring `style.newRow` hard breaks.
11001
+ * Used by the non-grouped grid template.
11002
+ */
11003
+ get controlRows() {
11004
+ return splitControlsIntoRows(this.flatControls);
11005
+ }
10888
11006
  /** TrackBy for group rendering */
10889
11007
  groupTrack(g, idx) {
10890
11008
  return g?.id ?? idx;
@@ -10992,7 +11110,7 @@ class MetaFormV2Component {
10992
11110
  this.depCleanup?.();
10993
11111
  }
10994
11112
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MetaFormV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
10995
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MetaFormV2Component, isStandalone: true, selector: "phoenix-meta-form-v2", inputs: { form: "form", config: "config", readOnly: "readOnly", contentStyle: "contentStyle", contentClass: "contentClass" }, usesOnChanges: true, ngImport: i0, template: "<div [formGroup]=\"form\">\n @if (hasControls) {\n\n @if (isGrouped) {\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedGroupIds\"\n (valueChange)=\"onAccordionValueChange($event)\"\n >\n @for (g of groupedControls; track groupTrack(g, $index)) {\n <p-accordion-panel [value]=\"panelValue(g)\">\n <p-accordion-header>\n\n <!-- plus/minus toggle icon -->\n <ng-template #toggleicon let-active=\"active\">\n @if (active) {\n <i class=\"pi pi-minus-circle\"></i>\n } @else {\n <i class=\"pi pi-plus-circle\"></i>\n }\n </ng-template>\n\n {{ g?.groupName | translate }}\n </p-accordion-header>\n \n <p-accordion-content>\n <div [ngStyle]=\"contentStyle\" [ngClass]=\"contentClass\">\n <phoenix-meta-form-group-v2 [group]=\"g\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-group-v2>\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n </p-accordion>\n\n } @else {\n <div class=\"grid\">\n @for (f of flatControls; track f.configuration.key) {\n <div [ngClass]=\"f.style.colWidth ?? 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n\n }\n</div>", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AccordionModule }, { kind: "component", type: i3$1.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i3$1.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i3$1.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i3$1.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: MetaFormFieldV2Component, selector: "phoenix-meta-form-field-v2", inputs: ["field", "form", "readOnly", "disableForm"] }, { kind: "component", type: MetaFormGroupV2Component, selector: "phoenix-meta-form-group-v2", inputs: ["group", "form", "readOnly"] }, { kind: "pipe", type: i4$2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11113
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MetaFormV2Component, isStandalone: true, selector: "phoenix-meta-form-v2", inputs: { form: "form", config: "config", readOnly: "readOnly", contentStyle: "contentStyle", contentClass: "contentClass" }, usesOnChanges: true, ngImport: i0, template: "<div [formGroup]=\"form\">\n @if (hasControls) {\n\n @if (isGrouped) {\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedGroupIds\"\n (valueChange)=\"onAccordionValueChange($event)\"\n >\n @for (g of groupedControls; track groupTrack(g, $index)) {\n <p-accordion-panel [value]=\"panelValue(g)\">\n <p-accordion-header>\n\n <!-- plus/minus toggle icon -->\n <ng-template #toggleicon let-active=\"active\">\n @if (active) {\n <i class=\"pi pi-minus-circle\"></i>\n } @else {\n <i class=\"pi pi-plus-circle\"></i>\n }\n </ng-template>\n\n {{ g?.groupName | translate }}\n </p-accordion-header>\n \n <p-accordion-content>\n <div [ngStyle]=\"contentStyle\" [ngClass]=\"contentClass\">\n <phoenix-meta-form-group-v2 [group]=\"g\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-group-v2>\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n </p-accordion>\n\n } @else {\n @for (row of controlRows; track $index) {\n <div class=\"grid\">\n @for (f of row; track f.configuration.key) {\n <div [ngClass]=\"f.style.colWidth ?? 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n }\n\n }\n</div>", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AccordionModule }, { kind: "component", type: i3$1.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i3$1.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i3$1.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i3$1.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: MetaFormFieldV2Component, selector: "phoenix-meta-form-field-v2", inputs: ["field", "form", "readOnly", "disableForm"] }, { kind: "component", type: MetaFormGroupV2Component, selector: "phoenix-meta-form-group-v2", inputs: ["group", "form", "readOnly"] }, { kind: "pipe", type: i4$2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10996
11114
  }
10997
11115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MetaFormV2Component, decorators: [{
10998
11116
  type: Component,
@@ -11003,7 +11121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11003
11121
  TranslateModule,
11004
11122
  MetaFormFieldV2Component,
11005
11123
  MetaFormGroupV2Component,
11006
- ], template: "<div [formGroup]=\"form\">\n @if (hasControls) {\n\n @if (isGrouped) {\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedGroupIds\"\n (valueChange)=\"onAccordionValueChange($event)\"\n >\n @for (g of groupedControls; track groupTrack(g, $index)) {\n <p-accordion-panel [value]=\"panelValue(g)\">\n <p-accordion-header>\n\n <!-- plus/minus toggle icon -->\n <ng-template #toggleicon let-active=\"active\">\n @if (active) {\n <i class=\"pi pi-minus-circle\"></i>\n } @else {\n <i class=\"pi pi-plus-circle\"></i>\n }\n </ng-template>\n\n {{ g?.groupName | translate }}\n </p-accordion-header>\n \n <p-accordion-content>\n <div [ngStyle]=\"contentStyle\" [ngClass]=\"contentClass\">\n <phoenix-meta-form-group-v2 [group]=\"g\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-group-v2>\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n </p-accordion>\n\n } @else {\n <div class=\"grid\">\n @for (f of flatControls; track f.configuration.key) {\n <div [ngClass]=\"f.style.colWidth ?? 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n\n }\n</div>", styles: [":host{display:block}\n"] }]
11124
+ ], template: "<div [formGroup]=\"form\">\n @if (hasControls) {\n\n @if (isGrouped) {\n <p-accordion\n [multiple]=\"true\"\n [value]=\"expandedGroupIds\"\n (valueChange)=\"onAccordionValueChange($event)\"\n >\n @for (g of groupedControls; track groupTrack(g, $index)) {\n <p-accordion-panel [value]=\"panelValue(g)\">\n <p-accordion-header>\n\n <!-- plus/minus toggle icon -->\n <ng-template #toggleicon let-active=\"active\">\n @if (active) {\n <i class=\"pi pi-minus-circle\"></i>\n } @else {\n <i class=\"pi pi-plus-circle\"></i>\n }\n </ng-template>\n\n {{ g?.groupName | translate }}\n </p-accordion-header>\n \n <p-accordion-content>\n <div [ngStyle]=\"contentStyle\" [ngClass]=\"contentClass\">\n <phoenix-meta-form-group-v2 [group]=\"g\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-group-v2>\n </div>\n </p-accordion-content>\n </p-accordion-panel>\n }\n </p-accordion>\n\n } @else {\n @for (row of controlRows; track $index) {\n <div class=\"grid\">\n @for (f of row; track f.configuration.key) {\n <div [ngClass]=\"f.style.colWidth ?? 'col-12 md:col-6'\">\n <phoenix-meta-form-field-v2 [field]=\"f\" [form]=\"form\" [readOnly]=\"readOnly\"></phoenix-meta-form-field-v2>\n </div>\n }\n </div>\n }\n }\n\n }\n</div>", styles: [":host{display:block}\n"] }]
11007
11125
  }], propDecorators: { form: [{
11008
11126
  type: Input,
11009
11127
  args: [{ required: true }]