commons-shared-web-ui 0.0.72 → 0.0.73

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.
@@ -55,6 +55,8 @@ import * as i1$1 from '@angular/platform-browser';
55
55
  import * as i1$4 from '@angular/router';
56
56
  import { RouterModule } from '@angular/router';
57
57
  import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
58
+ import * as i1$5 from '@angular/cdk/drag-drop';
59
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
58
60
 
59
61
  class MaterialModule {
60
62
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -10689,6 +10691,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
10689
10691
  * clone, so `applyTreeToSchema` simply walks the tree and assigns the node state
10690
10692
  * onto those references — there is no positional re-matching, which keeps it robust
10691
10693
  * against ROWs, nested groups, and mixed leaf/section children.
10694
+ *
10695
+ * Field order is a plain property of the schema — there is no separate `sequence`
10696
+ * field. Reordering rewrites the schema's own children array in place, so render
10697
+ * order (which simply follows array order, as it always has) reflects the
10698
+ * configurator's choices with no extra sorting logic anywhere else.
10692
10699
  */
10693
10700
  class ConfigSchemaTreeService {
10694
10701
  // ── Parsing ─────────────────────────────────────────────────────────────────
@@ -10704,8 +10711,9 @@ class ConfigSchemaTreeService {
10704
10711
  }
10705
10712
  /**
10706
10713
  * Write the current tree state back onto the schema the nodes reference.
10707
- * Sets isEnabled/visible/disabled for visibility and required for mandatory.
10708
- * Hidden fields always emit required: false ("mandatory only if visible").
10714
+ * Sets isEnabled/visible/disabled for visibility, required for mandatory, and
10715
+ * colSpan; and physically reorders each section's own children array to match
10716
+ * the tree's current field order.
10709
10717
  */
10710
10718
  applyTreeToSchema(sections) {
10711
10719
  for (const section of sections) {
@@ -10715,11 +10723,12 @@ class ConfigSchemaTreeService {
10715
10723
  // ── Private: parsing ──────────────────────────────────────────────────────────
10716
10724
  _buildSections(children) {
10717
10725
  const sections = [];
10726
+ const reorderable = !children.some(c => c.type === 'ROW');
10718
10727
  let leafBundle = [];
10719
10728
  for (const child of children) {
10720
10729
  if (this._isSection(child)) {
10721
10730
  if (leafBundle.length) {
10722
- sections.push(this._createBundleSection(leafBundle));
10731
+ sections.push(this._createBundleSection(leafBundle, children, reorderable));
10723
10732
  leafBundle = [];
10724
10733
  }
10725
10734
  sections.push(this._buildSection(child));
@@ -10733,11 +10742,12 @@ class ConfigSchemaTreeService {
10733
10742
  }
10734
10743
  }
10735
10744
  if (leafBundle.length)
10736
- sections.push(this._createBundleSection(leafBundle));
10745
+ sections.push(this._createBundleSection(leafBundle, children, reorderable));
10737
10746
  return sections;
10738
10747
  }
10739
10748
  _buildSection(field) {
10740
10749
  const children = field.sectionConfig?.children ?? field.children ?? [];
10750
+ const reorderable = !children.some(c => c.type === 'ROW');
10741
10751
  const fields = [];
10742
10752
  const subsections = [];
10743
10753
  for (const child of children) {
@@ -10771,9 +10781,11 @@ class ConfigSchemaTreeService {
10771
10781
  expanded: true,
10772
10782
  fields,
10773
10783
  subsections,
10784
+ fieldsSource: children,
10785
+ reorderable,
10774
10786
  };
10775
10787
  }
10776
- _createBundleSection(fields) {
10788
+ _createBundleSection(fields, fieldsSource, reorderable) {
10777
10789
  return {
10778
10790
  label: 'Fields',
10779
10791
  fieldConfig: null,
@@ -10783,6 +10795,8 @@ class ConfigSchemaTreeService {
10783
10795
  expanded: true,
10784
10796
  fields,
10785
10797
  subsections: [],
10798
+ fieldsSource,
10799
+ reorderable,
10786
10800
  };
10787
10801
  }
10788
10802
  _buildField(field) {
@@ -10793,6 +10807,7 @@ class ConfigSchemaTreeService {
10793
10807
  type: field.type,
10794
10808
  visible,
10795
10809
  mandatory: visible ? field.required === true : false,
10810
+ colSpan: field.colSpan ?? 12,
10796
10811
  lockVisibility: field.lockVisibility === true,
10797
10812
  lockMandatory: field.lockMandatory === true,
10798
10813
  parentName: field.optionConfig?.dependencies?.['parentDataCode'],
@@ -10809,15 +10824,34 @@ class ConfigSchemaTreeService {
10809
10824
  section.fieldConfig.visible = section.visible; // backward compatibility
10810
10825
  section.fieldConfig.disabled = !section.visible;
10811
10826
  }
10827
+ if (section.reorderable && section.fieldsSource) {
10828
+ this._reorderChildren(section.fieldsSource, section.fields);
10829
+ }
10812
10830
  for (const field of section.fields)
10813
10831
  this._applyField(field);
10814
10832
  for (const sub of section.subsections)
10815
10833
  this._applySection(sub);
10816
10834
  }
10835
+ /**
10836
+ * Rewrites `slots` in place so its leaf-field entries follow `fields`' current
10837
+ * order. Section entries (GROUP/SUBFIELDS) keep their original array position —
10838
+ * only the leaf fields move. Safe to call whenever `slots` contains no ROW
10839
+ * (guaranteed by the `reorderable` flag the caller checks).
10840
+ */
10841
+ _reorderChildren(slots, fields) {
10842
+ const orderedLeaves = fields.map(f => f.fieldConfig);
10843
+ let cursor = 0;
10844
+ for (let i = 0; i < slots.length; i++) {
10845
+ if (!this._isSection(slots[i])) {
10846
+ slots[i] = orderedLeaves[cursor++];
10847
+ }
10848
+ }
10849
+ }
10817
10850
  _applyField(field) {
10818
10851
  field.fieldConfig.isEnabled = field.visible;
10819
10852
  field.fieldConfig.visible = field.visible; // backward compatibility
10820
10853
  field.fieldConfig.required = field.visible ? field.mandatory : false;
10854
+ field.fieldConfig.colSpan = field.colSpan;
10821
10855
  }
10822
10856
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigSchemaTreeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10823
10857
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigSchemaTreeService, providedIn: 'root' });
@@ -10894,6 +10928,27 @@ class FieldConfigurationService {
10894
10928
  return { ...f, mandatory: !f.mandatory };
10895
10929
  });
10896
10930
  }
10931
+ /** Set a field's column span, clamped to the 1-12 grid range. */
10932
+ setFieldColSpan(path, fieldIndex, colSpan) {
10933
+ const clamped = Math.min(12, Math.max(1, Math.round(colSpan) || 1));
10934
+ this._updateFieldAtPath(path, fieldIndex, f => ({ ...f, colSpan: clamped }));
10935
+ }
10936
+ /**
10937
+ * Reorder a field within its own section (drag-and-drop). Reordering is scoped to
10938
+ * one section's direct fields — dragging never moves a field across sections.
10939
+ * Rejected (no-op) if the move would place a field above the field it depends on
10940
+ * via `optionConfig.dependencies` (e.g. a "state" field can never sort above the
10941
+ * "country" field it depends on).
10942
+ */
10943
+ reorderField(path, previousIndex, currentIndex) {
10944
+ if (previousIndex === currentIndex)
10945
+ return;
10946
+ this._updateSectionAtPath(path, section => {
10947
+ const fields = [...section.fields];
10948
+ moveItemInArray(fields, previousIndex, currentIndex);
10949
+ return this._isValidFieldOrder(fields) ? { ...section, fields } : section;
10950
+ });
10951
+ }
10897
10952
  // ── Output ──────────────────────────────────────────────────────────────────
10898
10953
  /**
10899
10954
  * Produce the updated schema. Writes the current tree state onto the working
@@ -11001,6 +11056,25 @@ class FieldConfigurationService {
11001
11056
  }));
11002
11057
  return rebuildSections(sections);
11003
11058
  }
11059
+ /**
11060
+ * True when every field with a dependency parent (`parentName`) sorts after that
11061
+ * parent within the given list. Fields whose parent isn't a sibling in this same
11062
+ * list (cross-section dependency, or no such field) are unconstrained here.
11063
+ */
11064
+ _isValidFieldOrder(fields) {
11065
+ const indexByName = new Map();
11066
+ fields.forEach((f, i) => {
11067
+ const name = f.fieldConfig.name;
11068
+ if (name)
11069
+ indexByName.set(name, i);
11070
+ });
11071
+ return fields.every((f, i) => {
11072
+ if (!f.parentName)
11073
+ return true;
11074
+ const parentIndex = indexByName.get(f.parentName);
11075
+ return parentIndex === undefined || parentIndex < i;
11076
+ });
11077
+ }
11004
11078
  /** Recursively set visibility on a section and all its descendants (respecting locks). */
11005
11079
  _cascadeVisibility(section, visible) {
11006
11080
  return {
@@ -11030,16 +11104,23 @@ class ConfigFieldNodeComponent {
11030
11104
  translate;
11031
11105
  toggleVisible = new EventEmitter();
11032
11106
  toggleMandatory = new EventEmitter();
11107
+ colSpanChange = new EventEmitter();
11033
11108
  get displayLabel() {
11034
11109
  const label = this.field.label;
11035
11110
  return this.translate ? this.translate(label) : label;
11036
11111
  }
11112
+ onColSpanInput(value) {
11113
+ const parsed = Number(value);
11114
+ if (!Number.isFinite(parsed))
11115
+ return;
11116
+ this.colSpanChange.emit(parsed);
11117
+ }
11037
11118
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigFieldNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11038
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigFieldNodeComponent, isStandalone: false, selector: "lib-config-field-node", inputs: { field: "field", translate: "translate" }, outputs: { toggleVisible: "toggleVisible", toggleMandatory: "toggleMandatory" }, ngImport: i0, template: "<div\r\n class=\"ffc-field\"\r\n [class.ffc-field--hidden]=\"!field.visible\"\r\n [class.ffc-field--locked]=\"field.lockVisibility\"\r\n>\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"ffc-field__eye\" [class.ffc-field__eye--off]=\"!field.visible\" aria-hidden=\"true\">\r\n @if (field.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"ffc-field__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Mandatory control (only meaningful when visible) -->\r\n <button\r\n type=\"button\"\r\n class=\"ffc-field__mandatory\"\r\n [class.ffc-field__mandatory--disabled]=\"!field.visible || field.lockMandatory\"\r\n [disabled]=\"!field.visible || field.lockMandatory\"\r\n [attr.aria-pressed]=\"field.mandatory\"\r\n (click)=\"toggleMandatory.emit()\"\r\n >\r\n <span class=\"ffc-check\" [class.ffc-check--on]=\"field.mandatory\">\r\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </span>\r\n <span class=\"ffc-field__mandatory-text\">Mandatory</span>\r\n </button>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (field.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This field is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"field.visible\"\r\n [class.ffc-toggle--disabled]=\"field.visibilityLockedByParent\"\r\n [disabled]=\"field.visibilityLockedByParent\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"field.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n [attr.title]=\"field.visibilityLockedByParent ? 'Show the field it depends on first' : null\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-field{display:flex;align-items:center;gap:var(--ffc-field-gap, 12px);padding:var(--ffc-field-padding, 10px 14px);border-radius:var(--ffc-field-radius, 8px);background:var(--ffc-field-bg, #ffffff);border:1px solid var(--ffc-field-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.ffc-field:hover:not(.ffc-field--locked){background:var(--ffc-field-hover-bg, #f8fafc)}.ffc-field--hidden .ffc-field__eye,.ffc-field--hidden .ffc-field__label,.ffc-field--hidden .ffc-field__mandatory{opacity:.55}.ffc-field--locked{background:var(--ffc-field-locked-bg, #f6f7f8)}.ffc-field__eye{display:flex;align-items:center;color:var(--ffc-eye-color, #6b7280);flex-shrink:0}.ffc-field__eye--off{color:var(--ffc-eye-off-color, #b0b6be)}.ffc-field__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-field-label-size, 14px);color:var(--ffc-field-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-field__mandatory{display:inline-flex;align-items:center;gap:7px;flex-shrink:0;padding:0;border:none;background:transparent;cursor:pointer;font:inherit}.ffc-field__mandatory--disabled{cursor:not-allowed}.ffc-field__mandatory-text{font-size:var(--ffc-mandatory-size, 13px);color:var(--ffc-mandatory-color, #52606d);white-space:nowrap}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11119
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigFieldNodeComponent, isStandalone: false, selector: "lib-config-field-node", inputs: { field: "field", translate: "translate" }, outputs: { toggleVisible: "toggleVisible", toggleMandatory: "toggleMandatory", colSpanChange: "colSpanChange" }, ngImport: i0, template: "<div\r\n class=\"ffc-field\"\r\n [class.ffc-field--hidden]=\"!field.visible\"\r\n [class.ffc-field--locked]=\"field.lockVisibility\"\r\n>\r\n <!-- Drag handle (reorder within this section) -->\r\n <span class=\"ffc-field__grip\" title=\"Drag to reorder within this section\" aria-hidden=\"true\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <circle cx=\"8\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"19\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"19\" r=\"1.6\"></circle>\r\n </svg>\r\n </span>\r\n\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"ffc-field__eye\" [class.ffc-field__eye--off]=\"!field.visible\" aria-hidden=\"true\">\r\n @if (field.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"ffc-field__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Column span (1-12 grid) -->\r\n <label class=\"ffc-field__colspan\" [attr.title]=\"'Column span (1-12)'\">\r\n <span class=\"ffc-field__colspan-text\">Col</span>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n class=\"ffc-field__colspan-input\"\r\n [value]=\"field.colSpan\"\r\n (change)=\"onColSpanInput($any($event.target).value)\"\r\n (click)=\"$event.stopPropagation()\"\r\n (mousedown)=\"$event.stopPropagation()\"\r\n />\r\n </label>\r\n\r\n <!-- Mandatory control (only meaningful when visible) -->\r\n <button\r\n type=\"button\"\r\n class=\"ffc-field__mandatory\"\r\n [class.ffc-field__mandatory--disabled]=\"!field.visible || field.lockMandatory\"\r\n [disabled]=\"!field.visible || field.lockMandatory\"\r\n [attr.aria-pressed]=\"field.mandatory\"\r\n (click)=\"toggleMandatory.emit()\"\r\n >\r\n <span class=\"ffc-check\" [class.ffc-check--on]=\"field.mandatory\">\r\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </span>\r\n <span class=\"ffc-field__mandatory-text\">Mandatory</span>\r\n </button>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (field.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This field is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"field.visible\"\r\n [class.ffc-toggle--disabled]=\"field.visibilityLockedByParent\"\r\n [disabled]=\"field.visibilityLockedByParent\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"field.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n [attr.title]=\"field.visibilityLockedByParent ? 'Show the field it depends on first' : null\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}:host{display:block;cursor:grab}:host.cdk-drag-dragging,:host.cdk-drag-placeholder{cursor:grabbing}.ffc-field{display:flex;align-items:center;gap:var(--ffc-field-gap, 12px);padding:var(--ffc-field-padding, 10px 14px);border-radius:var(--ffc-field-radius, 8px);background:var(--ffc-field-bg, #ffffff);border:1px solid var(--ffc-field-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.ffc-field:hover:not(.ffc-field--locked){background:var(--ffc-field-hover-bg, #f8fafc)}.ffc-field--hidden .ffc-field__eye,.ffc-field--hidden .ffc-field__label,.ffc-field--hidden .ffc-field__mandatory{opacity:.55}.ffc-field--locked{background:var(--ffc-field-locked-bg, #f6f7f8)}.ffc-field__grip{display:flex;align-items:center;color:var(--ffc-grip-color, #cbd2d9);flex-shrink:0}.ffc-field__eye{display:flex;align-items:center;color:var(--ffc-eye-color, #6b7280);flex-shrink:0}.ffc-field__eye--off{color:var(--ffc-eye-off-color, #b0b6be)}.ffc-field__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-field-label-size, 14px);color:var(--ffc-field-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-field__colspan{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;cursor:default}.ffc-field__colspan-text{font-size:var(--ffc-colspan-label-size, 12px);color:var(--ffc-colspan-label-color, #9aa2ab);padding-right:5px}.ffc-field__colspan-input{width:var(--ffc-colspan-input-width, 42px);padding:3px 4px;text-align:center;font-size:var(--ffc-colspan-input-size, 13px);color:var(--ffc-field-label-color, #1f2933);border:1px solid var(--ffc-field-border, #d7dce1);border-radius:5px;background:var(--ffc-colspan-input-bg, #ffffff);cursor:text}.ffc-field__colspan-input:focus{outline:none;border-color:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-field__colspan-input::-webkit-outer-spin-button,.ffc-field__colspan-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ffc-field__colspan-input[type=number]{-moz-appearance:textfield}.ffc-field__mandatory{display:inline-flex;align-items:center;gap:7px;flex-shrink:0;padding:0;border:none;background:transparent;cursor:pointer;font:inherit}.ffc-field__mandatory--disabled{cursor:not-allowed}.ffc-field__mandatory-text{font-size:var(--ffc-mandatory-size, 13px);color:var(--ffc-mandatory-color, #52606d);white-space:nowrap}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11039
11120
  }
11040
11121
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigFieldNodeComponent, decorators: [{
11041
11122
  type: Component,
11042
- args: [{ selector: 'lib-config-field-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"ffc-field\"\r\n [class.ffc-field--hidden]=\"!field.visible\"\r\n [class.ffc-field--locked]=\"field.lockVisibility\"\r\n>\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"ffc-field__eye\" [class.ffc-field__eye--off]=\"!field.visible\" aria-hidden=\"true\">\r\n @if (field.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"ffc-field__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Mandatory control (only meaningful when visible) -->\r\n <button\r\n type=\"button\"\r\n class=\"ffc-field__mandatory\"\r\n [class.ffc-field__mandatory--disabled]=\"!field.visible || field.lockMandatory\"\r\n [disabled]=\"!field.visible || field.lockMandatory\"\r\n [attr.aria-pressed]=\"field.mandatory\"\r\n (click)=\"toggleMandatory.emit()\"\r\n >\r\n <span class=\"ffc-check\" [class.ffc-check--on]=\"field.mandatory\">\r\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </span>\r\n <span class=\"ffc-field__mandatory-text\">Mandatory</span>\r\n </button>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (field.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This field is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"field.visible\"\r\n [class.ffc-toggle--disabled]=\"field.visibilityLockedByParent\"\r\n [disabled]=\"field.visibilityLockedByParent\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"field.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n [attr.title]=\"field.visibilityLockedByParent ? 'Show the field it depends on first' : null\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-field{display:flex;align-items:center;gap:var(--ffc-field-gap, 12px);padding:var(--ffc-field-padding, 10px 14px);border-radius:var(--ffc-field-radius, 8px);background:var(--ffc-field-bg, #ffffff);border:1px solid var(--ffc-field-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.ffc-field:hover:not(.ffc-field--locked){background:var(--ffc-field-hover-bg, #f8fafc)}.ffc-field--hidden .ffc-field__eye,.ffc-field--hidden .ffc-field__label,.ffc-field--hidden .ffc-field__mandatory{opacity:.55}.ffc-field--locked{background:var(--ffc-field-locked-bg, #f6f7f8)}.ffc-field__eye{display:flex;align-items:center;color:var(--ffc-eye-color, #6b7280);flex-shrink:0}.ffc-field__eye--off{color:var(--ffc-eye-off-color, #b0b6be)}.ffc-field__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-field-label-size, 14px);color:var(--ffc-field-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-field__mandatory{display:inline-flex;align-items:center;gap:7px;flex-shrink:0;padding:0;border:none;background:transparent;cursor:pointer;font:inherit}.ffc-field__mandatory--disabled{cursor:not-allowed}.ffc-field__mandatory-text{font-size:var(--ffc-mandatory-size, 13px);color:var(--ffc-mandatory-color, #52606d);white-space:nowrap}\n"] }]
11123
+ args: [{ selector: 'lib-config-field-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"ffc-field\"\r\n [class.ffc-field--hidden]=\"!field.visible\"\r\n [class.ffc-field--locked]=\"field.lockVisibility\"\r\n>\r\n <!-- Drag handle (reorder within this section) -->\r\n <span class=\"ffc-field__grip\" title=\"Drag to reorder within this section\" aria-hidden=\"true\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <circle cx=\"8\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"19\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"19\" r=\"1.6\"></circle>\r\n </svg>\r\n </span>\r\n\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"ffc-field__eye\" [class.ffc-field__eye--off]=\"!field.visible\" aria-hidden=\"true\">\r\n @if (field.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"ffc-field__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Column span (1-12 grid) -->\r\n <label class=\"ffc-field__colspan\" [attr.title]=\"'Column span (1-12)'\">\r\n <span class=\"ffc-field__colspan-text\">Col</span>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"12\"\r\n step=\"1\"\r\n class=\"ffc-field__colspan-input\"\r\n [value]=\"field.colSpan\"\r\n (change)=\"onColSpanInput($any($event.target).value)\"\r\n (click)=\"$event.stopPropagation()\"\r\n (mousedown)=\"$event.stopPropagation()\"\r\n />\r\n </label>\r\n\r\n <!-- Mandatory control (only meaningful when visible) -->\r\n <button\r\n type=\"button\"\r\n class=\"ffc-field__mandatory\"\r\n [class.ffc-field__mandatory--disabled]=\"!field.visible || field.lockMandatory\"\r\n [disabled]=\"!field.visible || field.lockMandatory\"\r\n [attr.aria-pressed]=\"field.mandatory\"\r\n (click)=\"toggleMandatory.emit()\"\r\n >\r\n <span class=\"ffc-check\" [class.ffc-check--on]=\"field.mandatory\">\r\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"20 6 9 17 4 12\"></polyline>\r\n </svg>\r\n </span>\r\n <span class=\"ffc-field__mandatory-text\">Mandatory</span>\r\n </button>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (field.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This field is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"field.visible\"\r\n [class.ffc-toggle--disabled]=\"field.visibilityLockedByParent\"\r\n [disabled]=\"field.visibilityLockedByParent\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"field.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n [attr.title]=\"field.visibilityLockedByParent ? 'Show the field it depends on first' : null\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}:host{display:block;cursor:grab}:host.cdk-drag-dragging,:host.cdk-drag-placeholder{cursor:grabbing}.ffc-field{display:flex;align-items:center;gap:var(--ffc-field-gap, 12px);padding:var(--ffc-field-padding, 10px 14px);border-radius:var(--ffc-field-radius, 8px);background:var(--ffc-field-bg, #ffffff);border:1px solid var(--ffc-field-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.ffc-field:hover:not(.ffc-field--locked){background:var(--ffc-field-hover-bg, #f8fafc)}.ffc-field--hidden .ffc-field__eye,.ffc-field--hidden .ffc-field__label,.ffc-field--hidden .ffc-field__mandatory{opacity:.55}.ffc-field--locked{background:var(--ffc-field-locked-bg, #f6f7f8)}.ffc-field__grip{display:flex;align-items:center;color:var(--ffc-grip-color, #cbd2d9);flex-shrink:0}.ffc-field__eye{display:flex;align-items:center;color:var(--ffc-eye-color, #6b7280);flex-shrink:0}.ffc-field__eye--off{color:var(--ffc-eye-off-color, #b0b6be)}.ffc-field__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-field-label-size, 14px);color:var(--ffc-field-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-field__colspan{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;cursor:default}.ffc-field__colspan-text{font-size:var(--ffc-colspan-label-size, 12px);color:var(--ffc-colspan-label-color, #9aa2ab);padding-right:5px}.ffc-field__colspan-input{width:var(--ffc-colspan-input-width, 42px);padding:3px 4px;text-align:center;font-size:var(--ffc-colspan-input-size, 13px);color:var(--ffc-field-label-color, #1f2933);border:1px solid var(--ffc-field-border, #d7dce1);border-radius:5px;background:var(--ffc-colspan-input-bg, #ffffff);cursor:text}.ffc-field__colspan-input:focus{outline:none;border-color:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-field__colspan-input::-webkit-outer-spin-button,.ffc-field__colspan-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ffc-field__colspan-input[type=number]{-moz-appearance:textfield}.ffc-field__mandatory{display:inline-flex;align-items:center;gap:7px;flex-shrink:0;padding:0;border:none;background:transparent;cursor:pointer;font:inherit}.ffc-field__mandatory--disabled{cursor:not-allowed}.ffc-field__mandatory-text{font-size:var(--ffc-mandatory-size, 13px);color:var(--ffc-mandatory-color, #52606d);white-space:nowrap}\n"] }]
11043
11124
  }], propDecorators: { field: [{
11044
11125
  type: Input,
11045
11126
  args: [{ required: true }]
@@ -11049,6 +11130,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
11049
11130
  type: Output
11050
11131
  }], toggleMandatory: [{
11051
11132
  type: Output
11133
+ }], colSpanChange: [{
11134
+ type: Output
11052
11135
  }] } });
11053
11136
 
11054
11137
  /**
@@ -11065,6 +11148,8 @@ class ConfigSectionNodeComponent {
11065
11148
  toggleExpanded = new EventEmitter();
11066
11149
  fieldToggleVisible = new EventEmitter();
11067
11150
  fieldToggleMandatory = new EventEmitter();
11151
+ fieldColSpanChange = new EventEmitter();
11152
+ fieldReorder = new EventEmitter();
11068
11153
  get displayLabel() {
11069
11154
  return this.translate ? this.translate(this.section.label) : this.section.label;
11070
11155
  }
@@ -11087,6 +11172,18 @@ class ConfigSectionNodeComponent {
11087
11172
  onFieldToggleMandatory(fieldIndex) {
11088
11173
  this.fieldToggleMandatory.emit({ sectionPath: [], fieldIndex });
11089
11174
  }
11175
+ onFieldColSpanChange(fieldIndex, colSpan) {
11176
+ this.fieldColSpanChange.emit({ sectionPath: [], fieldIndex, colSpan });
11177
+ }
11178
+ onFieldDrop(event) {
11179
+ if (event.previousIndex === event.currentIndex)
11180
+ return;
11181
+ this.fieldReorder.emit({
11182
+ sectionPath: [],
11183
+ previousIndex: event.previousIndex,
11184
+ currentIndex: event.currentIndex,
11185
+ });
11186
+ }
11090
11187
  // ── Subsection re-emits (prefix with the subsection index) ───
11091
11188
  onSubToggleVisible(subIndex, subPath) {
11092
11189
  this.toggleVisible.emit([subIndex, ...subPath]);
@@ -11106,15 +11203,29 @@ class ConfigSectionNodeComponent {
11106
11203
  fieldIndex: payload.fieldIndex,
11107
11204
  });
11108
11205
  }
11206
+ onSubFieldColSpanChange(subIndex, payload) {
11207
+ this.fieldColSpanChange.emit({
11208
+ sectionPath: [subIndex, ...payload.sectionPath],
11209
+ fieldIndex: payload.fieldIndex,
11210
+ colSpan: payload.colSpan,
11211
+ });
11212
+ }
11213
+ onSubFieldReorder(subIndex, payload) {
11214
+ this.fieldReorder.emit({
11215
+ sectionPath: [subIndex, ...payload.sectionPath],
11216
+ previousIndex: payload.previousIndex,
11217
+ currentIndex: payload.currentIndex,
11218
+ });
11219
+ }
11109
11220
  trackByIndex(index) {
11110
11221
  return index;
11111
11222
  }
11112
11223
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigSectionNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11113
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigSectionNodeComponent, isStandalone: false, selector: "lib-config-section-node", inputs: { section: "section", depth: "depth", translate: "translate" }, outputs: { toggleVisible: "toggleVisible", toggleExpanded: "toggleExpanded", fieldToggleVisible: "fieldToggleVisible", fieldToggleMandatory: "fieldToggleMandatory" }, ngImport: i0, template: "<div\r\n class=\"ffc-section\"\r\n [class.ffc-section--nested]=\"depth > 0\"\r\n [class.ffc-section--hidden]=\"hasToggle && !section.visible\"\r\n>\r\n <div class=\"ffc-section__header\">\r\n <button\r\n class=\"ffc-section__expand-btn\"\r\n type=\"button\"\r\n [attr.aria-expanded]=\"section.expanded\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <svg\r\n class=\"ffc-section__chevron\"\r\n [class.ffc-section__chevron--open]=\"section.expanded\"\r\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"\r\n >\r\n <polyline points=\"6 9 12 15 18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"ffc-section__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n @if (section.fields.length) {\r\n <span class=\"ffc-section__count\">{{ visibleFieldCount }}/{{ section.fields.length }} visible</span>\r\n }\r\n\r\n @if (hasToggle) {\r\n @if (section.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This section is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"section.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"section.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"onToggleVisible()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n\r\n @if (section.expanded) {\r\n <div class=\"ffc-section__body\">\r\n @for (field of section.fields; track trackByIndex($index); let fi = $index) {\r\n <lib-config-field-node\r\n [field]=\"field\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onFieldToggleVisible(fi)\"\r\n (toggleMandatory)=\"onFieldToggleMandatory(fi)\"\r\n />\r\n }\r\n\r\n @for (sub of section.subsections; track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"sub\"\r\n [depth]=\"depth + 1\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onSubToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onSubToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onSubFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onSubFieldToggleMandatory(si, $event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-section{border:1px solid var(--ffc-section-border, #e5e7eb);border-radius:var(--ffc-section-radius, 10px);background:var(--ffc-section-bg, #ffffff);overflow:hidden}.ffc-section--nested{border-color:var(--ffc-section-nested-border, #eef0f2);background:var(--ffc-section-nested-bg, #fbfcfd)}.ffc-section--hidden .ffc-section__label,.ffc-section--hidden .ffc-section__count{opacity:.55}.ffc-section__header{display:flex;align-items:center;gap:10px;padding:var(--ffc-section-header-padding, 12px 14px);background:var(--ffc-section-header-bg, #f6f7f8);-webkit-user-select:none;user-select:none}.ffc-section--nested .ffc-section__header{background:var(--ffc-section-nested-header-bg, #f1f3f5)}.ffc-section__expand-btn{background:none;border:none;cursor:pointer;padding:2px;display:flex;color:var(--ffc-chevron-color, #6b7280);flex-shrink:0}.ffc-section__chevron{transform:rotate(-90deg);transition:transform .2s}.ffc-section__chevron--open{transform:rotate(0)}.ffc-section__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-section-label-size, 14px);font-weight:600;color:var(--ffc-section-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-section__count{flex-shrink:0;font-size:var(--ffc-count-size, 12px);color:var(--ffc-count-color, #7b8794);white-space:nowrap}.ffc-section__body{padding:var(--ffc-section-body-padding, 8px 14px 14px 32px);display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}@media(max-width:640px){.ffc-section__body{padding:8px 10px 10px 18px;gap:6px}}\n"], dependencies: [{ kind: "component", type: ConfigSectionNodeComponent, selector: "lib-config-section-node", inputs: ["section", "depth", "translate"], outputs: ["toggleVisible", "toggleExpanded", "fieldToggleVisible", "fieldToggleMandatory"] }, { kind: "component", type: ConfigFieldNodeComponent, selector: "lib-config-field-node", inputs: ["field", "translate"], outputs: ["toggleVisible", "toggleMandatory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigSectionNodeComponent, isStandalone: false, selector: "lib-config-section-node", inputs: { section: "section", depth: "depth", translate: "translate" }, outputs: { toggleVisible: "toggleVisible", toggleExpanded: "toggleExpanded", fieldToggleVisible: "fieldToggleVisible", fieldToggleMandatory: "fieldToggleMandatory", fieldColSpanChange: "fieldColSpanChange", fieldReorder: "fieldReorder" }, ngImport: i0, template: "<div\r\n class=\"ffc-section\"\r\n [class.ffc-section--nested]=\"depth > 0\"\r\n [class.ffc-section--hidden]=\"hasToggle && !section.visible\"\r\n>\r\n <div class=\"ffc-section__header\">\r\n <button\r\n class=\"ffc-section__expand-btn\"\r\n type=\"button\"\r\n [attr.aria-expanded]=\"section.expanded\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <svg\r\n class=\"ffc-section__chevron\"\r\n [class.ffc-section__chevron--open]=\"section.expanded\"\r\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"\r\n >\r\n <polyline points=\"6 9 12 15 18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"ffc-section__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n @if (section.fields.length) {\r\n <span class=\"ffc-section__count\">{{ visibleFieldCount }}/{{ section.fields.length }} visible</span>\r\n }\r\n\r\n @if (hasToggle) {\r\n @if (section.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This section is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"section.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"section.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"onToggleVisible()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n\r\n @if (section.expanded) {\r\n <div class=\"ffc-section__body\">\r\n <div\r\n class=\"ffc-section__fields\"\r\n cdkDropList\r\n [cdkDropListDisabled]=\"!section.reorderable\"\r\n (cdkDropListDropped)=\"onFieldDrop($event)\"\r\n >\r\n @for (field of section.fields; track trackByIndex($index); let fi = $index) {\r\n <lib-config-field-node\r\n cdkDrag\r\n [cdkDragDisabled]=\"!section.reorderable\"\r\n [field]=\"field\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onFieldToggleVisible(fi)\"\r\n (toggleMandatory)=\"onFieldToggleMandatory(fi)\"\r\n (colSpanChange)=\"onFieldColSpanChange(fi, $event)\"\r\n />\r\n }\r\n </div>\r\n\r\n @for (sub of section.subsections; track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"sub\"\r\n [depth]=\"depth + 1\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onSubToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onSubToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onSubFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onSubFieldToggleMandatory(si, $event)\"\r\n (fieldColSpanChange)=\"onSubFieldColSpanChange(si, $event)\"\r\n (fieldReorder)=\"onSubFieldReorder(si, $event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-section{border:1px solid var(--ffc-section-border, #e5e7eb);border-radius:var(--ffc-section-radius, 10px);background:var(--ffc-section-bg, #ffffff);overflow:hidden}.ffc-section--nested{border-color:var(--ffc-section-nested-border, #eef0f2);background:var(--ffc-section-nested-bg, #fbfcfd)}.ffc-section--hidden .ffc-section__label,.ffc-section--hidden .ffc-section__count{opacity:.55}.ffc-section__header{display:flex;align-items:center;gap:10px;padding:var(--ffc-section-header-padding, 12px 14px);background:var(--ffc-section-header-bg, #f6f7f8);-webkit-user-select:none;user-select:none}.ffc-section--nested .ffc-section__header{background:var(--ffc-section-nested-header-bg, #f1f3f5)}.ffc-section__expand-btn{background:none;border:none;cursor:pointer;padding:2px;display:flex;color:var(--ffc-chevron-color, #6b7280);flex-shrink:0}.ffc-section__chevron{transform:rotate(-90deg);transition:transform .2s}.ffc-section__chevron--open{transform:rotate(0)}.ffc-section__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-section-label-size, 14px);font-weight:600;color:var(--ffc-section-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-section__count{flex-shrink:0;font-size:var(--ffc-count-size, 12px);color:var(--ffc-count-color, #7b8794);white-space:nowrap}.ffc-section__body{padding:var(--ffc-section-body-padding, 8px 14px 14px 14px);display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}@media(max-width:640px){.ffc-section__body{padding:8px 10px 10px 18px;gap:6px}}.ffc-section__fields{display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}\n"], dependencies: [{ kind: "directive", type: i1$5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ConfigSectionNodeComponent, selector: "lib-config-section-node", inputs: ["section", "depth", "translate"], outputs: ["toggleVisible", "toggleExpanded", "fieldToggleVisible", "fieldToggleMandatory", "fieldColSpanChange", "fieldReorder"] }, { kind: "component", type: ConfigFieldNodeComponent, selector: "lib-config-field-node", inputs: ["field", "translate"], outputs: ["toggleVisible", "toggleMandatory", "colSpanChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11114
11225
  }
11115
11226
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigSectionNodeComponent, decorators: [{
11116
11227
  type: Component,
11117
- args: [{ selector: 'lib-config-section-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"ffc-section\"\r\n [class.ffc-section--nested]=\"depth > 0\"\r\n [class.ffc-section--hidden]=\"hasToggle && !section.visible\"\r\n>\r\n <div class=\"ffc-section__header\">\r\n <button\r\n class=\"ffc-section__expand-btn\"\r\n type=\"button\"\r\n [attr.aria-expanded]=\"section.expanded\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <svg\r\n class=\"ffc-section__chevron\"\r\n [class.ffc-section__chevron--open]=\"section.expanded\"\r\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"\r\n >\r\n <polyline points=\"6 9 12 15 18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"ffc-section__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n @if (section.fields.length) {\r\n <span class=\"ffc-section__count\">{{ visibleFieldCount }}/{{ section.fields.length }} visible</span>\r\n }\r\n\r\n @if (hasToggle) {\r\n @if (section.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This section is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"section.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"section.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"onToggleVisible()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n\r\n @if (section.expanded) {\r\n <div class=\"ffc-section__body\">\r\n @for (field of section.fields; track trackByIndex($index); let fi = $index) {\r\n <lib-config-field-node\r\n [field]=\"field\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onFieldToggleVisible(fi)\"\r\n (toggleMandatory)=\"onFieldToggleMandatory(fi)\"\r\n />\r\n }\r\n\r\n @for (sub of section.subsections; track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"sub\"\r\n [depth]=\"depth + 1\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onSubToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onSubToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onSubFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onSubFieldToggleMandatory(si, $event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-section{border:1px solid var(--ffc-section-border, #e5e7eb);border-radius:var(--ffc-section-radius, 10px);background:var(--ffc-section-bg, #ffffff);overflow:hidden}.ffc-section--nested{border-color:var(--ffc-section-nested-border, #eef0f2);background:var(--ffc-section-nested-bg, #fbfcfd)}.ffc-section--hidden .ffc-section__label,.ffc-section--hidden .ffc-section__count{opacity:.55}.ffc-section__header{display:flex;align-items:center;gap:10px;padding:var(--ffc-section-header-padding, 12px 14px);background:var(--ffc-section-header-bg, #f6f7f8);-webkit-user-select:none;user-select:none}.ffc-section--nested .ffc-section__header{background:var(--ffc-section-nested-header-bg, #f1f3f5)}.ffc-section__expand-btn{background:none;border:none;cursor:pointer;padding:2px;display:flex;color:var(--ffc-chevron-color, #6b7280);flex-shrink:0}.ffc-section__chevron{transform:rotate(-90deg);transition:transform .2s}.ffc-section__chevron--open{transform:rotate(0)}.ffc-section__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-section-label-size, 14px);font-weight:600;color:var(--ffc-section-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-section__count{flex-shrink:0;font-size:var(--ffc-count-size, 12px);color:var(--ffc-count-color, #7b8794);white-space:nowrap}.ffc-section__body{padding:var(--ffc-section-body-padding, 8px 14px 14px 32px);display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}@media(max-width:640px){.ffc-section__body{padding:8px 10px 10px 18px;gap:6px}}\n"] }]
11228
+ args: [{ selector: 'lib-config-section-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"ffc-section\"\r\n [class.ffc-section--nested]=\"depth > 0\"\r\n [class.ffc-section--hidden]=\"hasToggle && !section.visible\"\r\n>\r\n <div class=\"ffc-section__header\">\r\n <button\r\n class=\"ffc-section__expand-btn\"\r\n type=\"button\"\r\n [attr.aria-expanded]=\"section.expanded\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <svg\r\n class=\"ffc-section__chevron\"\r\n [class.ffc-section__chevron--open]=\"section.expanded\"\r\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"\r\n >\r\n <polyline points=\"6 9 12 15 18 9\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"ffc-section__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n @if (section.fields.length) {\r\n <span class=\"ffc-section__count\">{{ visibleFieldCount }}/{{ section.fields.length }} visible</span>\r\n }\r\n\r\n @if (hasToggle) {\r\n @if (section.lockVisibility) {\r\n <span class=\"ffc-lock\" title=\"This section is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"ffc-toggle\"\r\n [class.ffc-toggle--on]=\"section.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"section.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"onToggleVisible()\"\r\n >\r\n <span class=\"ffc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n }\r\n </div>\r\n\r\n @if (section.expanded) {\r\n <div class=\"ffc-section__body\">\r\n <div\r\n class=\"ffc-section__fields\"\r\n cdkDropList\r\n [cdkDropListDisabled]=\"!section.reorderable\"\r\n (cdkDropListDropped)=\"onFieldDrop($event)\"\r\n >\r\n @for (field of section.fields; track trackByIndex($index); let fi = $index) {\r\n <lib-config-field-node\r\n cdkDrag\r\n [cdkDragDisabled]=\"!section.reorderable\"\r\n [field]=\"field\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onFieldToggleVisible(fi)\"\r\n (toggleMandatory)=\"onFieldToggleMandatory(fi)\"\r\n (colSpanChange)=\"onFieldColSpanChange(fi, $event)\"\r\n />\r\n }\r\n </div>\r\n\r\n @for (sub of section.subsections; track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"sub\"\r\n [depth]=\"depth + 1\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onSubToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onSubToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onSubFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onSubFieldToggleMandatory(si, $event)\"\r\n (fieldColSpanChange)=\"onSubFieldColSpanChange(si, $event)\"\r\n (fieldReorder)=\"onSubFieldReorder(si, $event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".ffc-toggle{position:relative;flex-shrink:0;width:var(--ffc-toggle-width, 38px);height:var(--ffc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--ffc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.ffc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--ffc-toggle-height, 22px) - 4px);height:calc(var(--ffc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--ffc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.ffc-toggle--on{background:var(--ffc-toggle-on-bg, #2f9e6f)}.ffc-toggle--on .ffc-toggle__thumb{left:calc(100% - (var(--ffc-toggle-height, 22px) - 2px))}.ffc-toggle:focus-visible{outline:2px solid var(--ffc-focus-ring, #2f9e6f);outline-offset:2px}.ffc-toggle--disabled{opacity:.45;cursor:not-allowed}.ffc-check{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-check-size, 18px);height:var(--ffc-check-size, 18px);border-radius:4px;border:2px solid var(--ffc-check-border, #c3c9d0);background:var(--ffc-check-bg, #ffffff);color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease}.ffc-check svg{opacity:0;transition:opacity .15s ease}.ffc-check--on{background:var(--ffc-check-on-bg, #d97757);border-color:var(--ffc-check-on-bg, #d97757);color:#fff}.ffc-check--on svg{opacity:1}.ffc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ffc-toggle-width, 38px);color:var(--ffc-lock-color, #9aa2ab)}.ffc-section{border:1px solid var(--ffc-section-border, #e5e7eb);border-radius:var(--ffc-section-radius, 10px);background:var(--ffc-section-bg, #ffffff);overflow:hidden}.ffc-section--nested{border-color:var(--ffc-section-nested-border, #eef0f2);background:var(--ffc-section-nested-bg, #fbfcfd)}.ffc-section--hidden .ffc-section__label,.ffc-section--hidden .ffc-section__count{opacity:.55}.ffc-section__header{display:flex;align-items:center;gap:10px;padding:var(--ffc-section-header-padding, 12px 14px);background:var(--ffc-section-header-bg, #f6f7f8);-webkit-user-select:none;user-select:none}.ffc-section--nested .ffc-section__header{background:var(--ffc-section-nested-header-bg, #f1f3f5)}.ffc-section__expand-btn{background:none;border:none;cursor:pointer;padding:2px;display:flex;color:var(--ffc-chevron-color, #6b7280);flex-shrink:0}.ffc-section__chevron{transform:rotate(-90deg);transition:transform .2s}.ffc-section__chevron--open{transform:rotate(0)}.ffc-section__label{flex:1 1 auto;min-width:0;font-size:var(--ffc-section-label-size, 14px);font-weight:600;color:var(--ffc-section-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ffc-section__count{flex-shrink:0;font-size:var(--ffc-count-size, 12px);color:var(--ffc-count-color, #7b8794);white-space:nowrap}.ffc-section__body{padding:var(--ffc-section-body-padding, 8px 14px 14px 14px);display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}@media(max-width:640px){.ffc-section__body{padding:8px 10px 10px 18px;gap:6px}}.ffc-section__fields{display:flex;flex-direction:column;gap:var(--ffc-field-row-gap, 8px)}\n"] }]
11118
11229
  }], propDecorators: { section: [{
11119
11230
  type: Input,
11120
11231
  args: [{ required: true }]
@@ -11130,22 +11241,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
11130
11241
  type: Output
11131
11242
  }], fieldToggleMandatory: [{
11132
11243
  type: Output
11244
+ }], fieldColSpanChange: [{
11245
+ type: Output
11246
+ }], fieldReorder: [{
11247
+ type: Output
11133
11248
  }] } });
11134
11249
 
11135
11250
  /**
11136
11251
  * Form Field Configuration
11137
11252
  * ------------------------
11138
11253
  * Renders a compact tree of a form schema's sections and fields, each with a
11139
- * **visibility** toggle and a **mandatory** control. Intended to be embedded
11140
- * inside a `cc-confirmation-modal` on a host page ("Configure fields").
11254
+ * **visibility** toggle, a **mandatory** control, an editable **column span**,
11255
+ * and a drag handle to **reorder** fields within their own section. Intended to
11256
+ * be embedded inside a `cc-confirmation-modal` on a host page ("Configure fields").
11141
11257
  *
11142
11258
  * Pass a `FormSchema` via `[schema]`. The input is deep-cloned, so it is never
11143
11259
  * mutated. When the user confirms, call `getUpdatedSchema()` to read back the
11144
- * modified schema (visible/isEnabled/disabled and required flags updated).
11260
+ * modified schema (visible/isEnabled/disabled, required, and colSpan flags
11261
+ * updated, and each section's own children reordered to match the tree).
11145
11262
  *
11146
11263
  * Fields/sections carrying `lockVisibility: true` cannot be hidden, and
11147
11264
  * `lockMandatory: true` freezes their required state.
11148
11265
  *
11266
+ * Reordering is scoped to one section's direct fields — a field can never be
11267
+ * dragged into another section, and a field can never be dragged above a field
11268
+ * it depends on via `optionConfig.dependencies` (e.g. "state" can't sort above
11269
+ * "country"). Sections whose children include a `ROW` wrapper can't be
11270
+ * reordered at all (dragging is disabled) to avoid flattening the row layout.
11271
+ *
11149
11272
  * @example
11150
11273
  * ```html
11151
11274
  * <cc-confirmation-modal [isOpen]="open" (confirm)="save()" (cancel)="open = false">
@@ -11190,15 +11313,21 @@ class FormFieldConfigurationComponent {
11190
11313
  onFieldToggleMandatory(sectionIndex, payload) {
11191
11314
  this.store.toggleFieldMandatory([sectionIndex, ...payload.sectionPath], payload.fieldIndex);
11192
11315
  }
11316
+ onFieldColSpanChange(sectionIndex, payload) {
11317
+ this.store.setFieldColSpan([sectionIndex, ...payload.sectionPath], payload.fieldIndex, payload.colSpan);
11318
+ }
11319
+ onFieldReorder(sectionIndex, payload) {
11320
+ this.store.reorderField([sectionIndex, ...payload.sectionPath], payload.previousIndex, payload.currentIndex);
11321
+ }
11193
11322
  trackByIndex(index) {
11194
11323
  return index;
11195
11324
  }
11196
11325
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11197
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: FormFieldConfigurationComponent, isStandalone: false, selector: "lib-form-field-configuration", inputs: { schema: "schema", translate: "translate", showLegend: "showLegend" }, providers: [FieldConfigurationService], usesOnChanges: true, ngImport: i0, template: "<div class=\"ffc-root\">\r\n @if (showLegend) {\r\n <div class=\"ffc-legend\">\r\n <span class=\"ffc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n <span class=\"ffc-legend__item\">\r\n <span class=\"ffc-legend__asterisk\" aria-hidden=\"true\">&#10033;</span>\r\n <span>Mandatory (only if visible)</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"ffc-tree\">\r\n @for (section of store.sections(); track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"section\"\r\n [depth]=\"0\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onFieldToggleMandatory(si, $event)\"\r\n />\r\n }\r\n\r\n @if (store.sections().length === 0) {\r\n <div class=\"ffc-empty\">\r\n <p>No sections or fields found in the schema.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".ffc-root{display:flex;flex-direction:column;gap:var(--ffc-root-gap, 12px);font-family:var(--ffc-font-family, inherit)}.ffc-legend{display:flex;align-items:center;gap:var(--ffc-legend-gap, 24px);padding:var(--ffc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--ffc-legend-border, #eef0f2);flex-wrap:wrap}.ffc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--ffc-legend-size, 13px);color:var(--ffc-legend-color, #52606d)}.ffc-legend__asterisk{color:var(--ffc-asterisk-color, #d97757);font-size:14px;line-height:1}.ffc-tree{display:flex;flex-direction:column;gap:var(--ffc-tree-gap, 12px)}.ffc-empty{padding:24px;text-align:center;color:var(--ffc-empty-color, #9aa2ab);font-size:13px}\n"], dependencies: [{ kind: "component", type: ConfigSectionNodeComponent, selector: "lib-config-section-node", inputs: ["section", "depth", "translate"], outputs: ["toggleVisible", "toggleExpanded", "fieldToggleVisible", "fieldToggleMandatory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11326
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: FormFieldConfigurationComponent, isStandalone: false, selector: "lib-form-field-configuration", inputs: { schema: "schema", translate: "translate", showLegend: "showLegend" }, providers: [FieldConfigurationService], usesOnChanges: true, ngImport: i0, template: "<div class=\"ffc-root\">\r\n @if (showLegend) {\r\n <div class=\"ffc-legend\">\r\n <span class=\"ffc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n <span class=\"ffc-legend__item\">\r\n <span class=\"ffc-legend__asterisk\" aria-hidden=\"true\">&#10033;</span>\r\n <span>Mandatory (only if visible)</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"ffc-tree\">\r\n @for (section of store.sections(); track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"section\"\r\n [depth]=\"0\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onFieldToggleMandatory(si, $event)\"\r\n (fieldColSpanChange)=\"onFieldColSpanChange(si, $event)\"\r\n (fieldReorder)=\"onFieldReorder(si, $event)\"\r\n />\r\n }\r\n\r\n @if (store.sections().length === 0) {\r\n <div class=\"ffc-empty\">\r\n <p>No sections or fields found in the schema.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".ffc-root{display:flex;flex-direction:column;gap:var(--ffc-root-gap, 12px);font-family:var(--ffc-font-family, inherit)}.ffc-legend{display:flex;align-items:center;gap:var(--ffc-legend-gap, 24px);padding:var(--ffc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--ffc-legend-border, #eef0f2);flex-wrap:wrap}.ffc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--ffc-legend-size, 13px);color:var(--ffc-legend-color, #52606d)}.ffc-legend__asterisk{color:var(--ffc-asterisk-color, #d97757);font-size:14px;line-height:1}.ffc-tree{display:flex;flex-direction:column;gap:var(--ffc-tree-gap, 12px)}.ffc-empty{padding:24px;text-align:center;color:var(--ffc-empty-color, #9aa2ab);font-size:13px}\n"], dependencies: [{ kind: "component", type: ConfigSectionNodeComponent, selector: "lib-config-section-node", inputs: ["section", "depth", "translate"], outputs: ["toggleVisible", "toggleExpanded", "fieldToggleVisible", "fieldToggleMandatory", "fieldColSpanChange", "fieldReorder"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11198
11327
  }
11199
11328
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationComponent, decorators: [{
11200
11329
  type: Component,
11201
- args: [{ selector: 'lib-form-field-configuration', standalone: false, providers: [FieldConfigurationService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ffc-root\">\r\n @if (showLegend) {\r\n <div class=\"ffc-legend\">\r\n <span class=\"ffc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n <span class=\"ffc-legend__item\">\r\n <span class=\"ffc-legend__asterisk\" aria-hidden=\"true\">&#10033;</span>\r\n <span>Mandatory (only if visible)</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"ffc-tree\">\r\n @for (section of store.sections(); track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"section\"\r\n [depth]=\"0\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onFieldToggleMandatory(si, $event)\"\r\n />\r\n }\r\n\r\n @if (store.sections().length === 0) {\r\n <div class=\"ffc-empty\">\r\n <p>No sections or fields found in the schema.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".ffc-root{display:flex;flex-direction:column;gap:var(--ffc-root-gap, 12px);font-family:var(--ffc-font-family, inherit)}.ffc-legend{display:flex;align-items:center;gap:var(--ffc-legend-gap, 24px);padding:var(--ffc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--ffc-legend-border, #eef0f2);flex-wrap:wrap}.ffc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--ffc-legend-size, 13px);color:var(--ffc-legend-color, #52606d)}.ffc-legend__asterisk{color:var(--ffc-asterisk-color, #d97757);font-size:14px;line-height:1}.ffc-tree{display:flex;flex-direction:column;gap:var(--ffc-tree-gap, 12px)}.ffc-empty{padding:24px;text-align:center;color:var(--ffc-empty-color, #9aa2ab);font-size:13px}\n"] }]
11330
+ args: [{ selector: 'lib-form-field-configuration', standalone: false, providers: [FieldConfigurationService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ffc-root\">\r\n @if (showLegend) {\r\n <div class=\"ffc-legend\">\r\n <span class=\"ffc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n <span class=\"ffc-legend__item\">\r\n <span class=\"ffc-legend__asterisk\" aria-hidden=\"true\">&#10033;</span>\r\n <span>Mandatory (only if visible)</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"ffc-tree\">\r\n @for (section of store.sections(); track trackByIndex($index); let si = $index) {\r\n <lib-config-section-node\r\n [section]=\"section\"\r\n [depth]=\"0\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(si, $event)\"\r\n (toggleExpanded)=\"onToggleExpanded(si, $event)\"\r\n (fieldToggleVisible)=\"onFieldToggleVisible(si, $event)\"\r\n (fieldToggleMandatory)=\"onFieldToggleMandatory(si, $event)\"\r\n (fieldColSpanChange)=\"onFieldColSpanChange(si, $event)\"\r\n (fieldReorder)=\"onFieldReorder(si, $event)\"\r\n />\r\n }\r\n\r\n @if (store.sections().length === 0) {\r\n <div class=\"ffc-empty\">\r\n <p>No sections or fields found in the schema.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".ffc-root{display:flex;flex-direction:column;gap:var(--ffc-root-gap, 12px);font-family:var(--ffc-font-family, inherit)}.ffc-legend{display:flex;align-items:center;gap:var(--ffc-legend-gap, 24px);padding:var(--ffc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--ffc-legend-border, #eef0f2);flex-wrap:wrap}.ffc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--ffc-legend-size, 13px);color:var(--ffc-legend-color, #52606d)}.ffc-legend__asterisk{color:var(--ffc-asterisk-color, #d97757);font-size:14px;line-height:1}.ffc-tree{display:flex;flex-direction:column;gap:var(--ffc-tree-gap, 12px)}.ffc-empty{padding:24px;text-align:center;color:var(--ffc-empty-color, #9aa2ab);font-size:13px}\n"] }]
11202
11331
  }], propDecorators: { schema: [{
11203
11332
  type: Input,
11204
11333
  args: [{ required: true }]
@@ -11217,8 +11346,10 @@ class FormFieldConfigurationModule {
11217
11346
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
11218
11347
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationModule, declarations: [FormFieldConfigurationComponent,
11219
11348
  ConfigSectionNodeComponent,
11220
- ConfigFieldNodeComponent], imports: [CommonModule], exports: [FormFieldConfigurationComponent] });
11221
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationModule, imports: [CommonModule] });
11349
+ ConfigFieldNodeComponent], imports: [CommonModule,
11350
+ DragDropModule], exports: [FormFieldConfigurationComponent] });
11351
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationModule, imports: [CommonModule,
11352
+ DragDropModule] });
11222
11353
  }
11223
11354
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldConfigurationModule, decorators: [{
11224
11355
  type: NgModule,
@@ -11226,6 +11357,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
11226
11357
  declarations: [...COMPONENTS$1],
11227
11358
  imports: [
11228
11359
  CommonModule,
11360
+ DragDropModule,
11229
11361
  ],
11230
11362
  exports: [
11231
11363
  FormFieldConfigurationComponent,
@@ -11251,7 +11383,8 @@ class TableColumnConfigurationService {
11251
11383
  */
11252
11384
  loadConfig(config) {
11253
11385
  const workingConfig = JSON.parse(JSON.stringify(config));
11254
- const columns = (workingConfig.columns ?? []).map(col => this._buildColumn(col));
11386
+ workingConfig.columns = this._sortBySequence(workingConfig.columns ?? []);
11387
+ const columns = workingConfig.columns.map(col => this._buildColumn(col));
11255
11388
  this._state.set({ columns, workingConfig });
11256
11389
  }
11257
11390
  // ── Mutations ─────────────────────────────────────────────────────────────
@@ -11262,6 +11395,14 @@ class TableColumnConfigurationService {
11262
11395
  columns: state.columns.map((c, i) => i === index && !c.lockVisibility ? { ...c, visible: !c.visible } : c),
11263
11396
  }));
11264
11397
  }
11398
+ /** Move a column from `previousIndex` to `currentIndex` (drag-and-drop reorder). */
11399
+ reorderColumn(previousIndex, currentIndex) {
11400
+ this._state.update(state => {
11401
+ const columns = [...state.columns];
11402
+ moveItemInArray(columns, previousIndex, currentIndex);
11403
+ return { ...state, columns };
11404
+ });
11405
+ }
11265
11406
  // ── Output ────────────────────────────────────────────────────────────────
11266
11407
  /**
11267
11408
  * Produce the updated config. Writes the current node state onto the working
@@ -11272,10 +11413,13 @@ class TableColumnConfigurationService {
11272
11413
  const { columns, workingConfig } = this._state();
11273
11414
  if (!workingConfig)
11274
11415
  return null;
11275
- for (const node of columns) {
11416
+ columns.forEach((node, index) => {
11276
11417
  node.column.isEnabled = node.visible;
11277
11418
  node.column.visible = node.visible; // backward compatibility
11278
- }
11419
+ node.column.sequence = index;
11420
+ });
11421
+ // Keep the working config's columns array in sync with the (possibly reordered) node list.
11422
+ workingConfig.columns = columns.map(node => node.column);
11279
11423
  return JSON.parse(JSON.stringify(workingConfig));
11280
11424
  }
11281
11425
  // ── Private helpers ─────────────────────────────────────────────────────────
@@ -11288,6 +11432,21 @@ class TableColumnConfigurationService {
11288
11432
  lockVisibility: column.lockVisibility === true,
11289
11433
  };
11290
11434
  }
11435
+ /**
11436
+ * Orders columns by `sequence` (ascending). Columns without a `sequence` keep their
11437
+ * original relative order, placed after any columns that do have one — so configs that
11438
+ * never set `sequence` come back untouched (stable sort).
11439
+ */
11440
+ _sortBySequence(columns) {
11441
+ const hasSequence = columns.some(c => c.sequence !== undefined && c.sequence !== null);
11442
+ if (!hasSequence)
11443
+ return columns;
11444
+ return [...columns].sort((a, b) => {
11445
+ const aSeq = a.sequence ?? Number.MAX_SAFE_INTEGER;
11446
+ const bSeq = b.sequence ?? Number.MAX_SAFE_INTEGER;
11447
+ return aSeq - bSeq;
11448
+ });
11449
+ }
11291
11450
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11292
11451
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationService });
11293
11452
  }
@@ -11310,11 +11469,11 @@ class ConfigColumnNodeComponent {
11310
11469
  return this.translate ? this.translate(label) : label;
11311
11470
  }
11312
11471
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigColumnNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11313
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigColumnNodeComponent, isStandalone: false, selector: "lib-config-column-node", inputs: { column: "column", translate: "translate" }, outputs: { toggleVisible: "toggleVisible" }, ngImport: i0, template: "<div\r\n class=\"tcc-col\"\r\n [class.tcc-col--hidden]=\"!column.visible\"\r\n [class.tcc-col--locked]=\"column.lockVisibility\"\r\n>\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"tcc-col__eye\" [class.tcc-col__eye--off]=\"!column.visible\" aria-hidden=\"true\">\r\n @if (column.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"tcc-col__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (column.lockVisibility) {\r\n <span class=\"tcc-lock\" title=\"This column is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"tcc-toggle\"\r\n [class.tcc-toggle--on]=\"column.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"column.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"tcc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".tcc-toggle{position:relative;flex-shrink:0;width:var(--tcc-toggle-width, 38px);height:var(--tcc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--tcc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.tcc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--tcc-toggle-height, 22px) - 4px);height:calc(var(--tcc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--tcc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.tcc-toggle--on{background:var(--tcc-toggle-on-bg, #2f9e6f)}.tcc-toggle--on .tcc-toggle__thumb{left:calc(100% - (var(--tcc-toggle-height, 22px) - 2px))}.tcc-toggle:focus-visible{outline:2px solid var(--tcc-focus-ring, #2f9e6f);outline-offset:2px}.tcc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--tcc-toggle-width, 38px);color:var(--tcc-lock-color, #9aa2ab)}.tcc-col{display:flex;align-items:center;gap:var(--tcc-col-gap, 12px);padding:var(--tcc-col-padding, 10px 14px);border-radius:var(--tcc-col-radius, 8px);background:var(--tcc-col-bg, #ffffff);border:1px solid var(--tcc-col-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.tcc-col:hover:not(.tcc-col--locked){background:var(--tcc-col-hover-bg, #f8fafc)}.tcc-col--hidden .tcc-col__eye,.tcc-col--hidden .tcc-col__label{opacity:.55}.tcc-col--locked{background:var(--tcc-col-locked-bg, #f6f7f8)}.tcc-col__eye{display:flex;align-items:center;color:var(--tcc-eye-color, #6b7280);flex-shrink:0}.tcc-col__eye--off{color:var(--tcc-eye-off-color, #b0b6be)}.tcc-col__label{flex:1 1 auto;min-width:0;font-size:var(--tcc-col-label-size, 14px);color:var(--tcc-col-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11472
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigColumnNodeComponent, isStandalone: false, selector: "lib-config-column-node", inputs: { column: "column", translate: "translate" }, outputs: { toggleVisible: "toggleVisible" }, ngImport: i0, template: "<div\r\n class=\"tcc-col\"\r\n [class.tcc-col--hidden]=\"!column.visible\"\r\n [class.tcc-col--locked]=\"column.lockVisibility\"\r\n>\r\n <!-- Drag handle (reorder via the sequence field) -->\r\n <span class=\"tcc-col__grip\" title=\"Drag to reorder\" aria-hidden=\"true\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <circle cx=\"8\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"19\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"19\" r=\"1.6\"></circle>\r\n </svg>\r\n </span>\r\n\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"tcc-col__eye\" [class.tcc-col__eye--off]=\"!column.visible\" aria-hidden=\"true\">\r\n @if (column.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"tcc-col__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (column.lockVisibility) {\r\n <span class=\"tcc-lock\" title=\"This column is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"tcc-toggle\"\r\n [class.tcc-toggle--on]=\"column.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"column.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"tcc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".tcc-toggle{position:relative;flex-shrink:0;width:var(--tcc-toggle-width, 38px);height:var(--tcc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--tcc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.tcc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--tcc-toggle-height, 22px) - 4px);height:calc(var(--tcc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--tcc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.tcc-toggle--on{background:var(--tcc-toggle-on-bg, #2f9e6f)}.tcc-toggle--on .tcc-toggle__thumb{left:calc(100% - (var(--tcc-toggle-height, 22px) - 2px))}.tcc-toggle:focus-visible{outline:2px solid var(--tcc-focus-ring, #2f9e6f);outline-offset:2px}.tcc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--tcc-toggle-width, 38px);color:var(--tcc-lock-color, #9aa2ab)}:host{display:block;cursor:grab}:host.cdk-drag-dragging,:host.cdk-drag-placeholder{cursor:grabbing}.tcc-col{display:flex;align-items:center;gap:var(--tcc-col-gap, 12px);padding:var(--tcc-col-padding, 10px 14px);border-radius:var(--tcc-col-radius, 8px);background:var(--tcc-col-bg, #ffffff);border:1px solid var(--tcc-col-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.tcc-col:hover:not(.tcc-col--locked){background:var(--tcc-col-hover-bg, #f8fafc)}.tcc-col__grip{display:flex;align-items:center;color:var(--tcc-grip-color, #cbd2d9);flex-shrink:0}.tcc-col--hidden .tcc-col__eye,.tcc-col--hidden .tcc-col__label{opacity:.55}.tcc-col--locked{background:var(--tcc-col-locked-bg, #f6f7f8)}.tcc-col__eye{display:flex;align-items:center;color:var(--tcc-eye-color, #6b7280);flex-shrink:0}.tcc-col__eye--off{color:var(--tcc-eye-off-color, #b0b6be)}.tcc-col__label{flex:1 1 auto;min-width:0;font-size:var(--tcc-col-label-size, 14px);color:var(--tcc-col-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11314
11473
  }
11315
11474
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigColumnNodeComponent, decorators: [{
11316
11475
  type: Component,
11317
- args: [{ selector: 'lib-config-column-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"tcc-col\"\r\n [class.tcc-col--hidden]=\"!column.visible\"\r\n [class.tcc-col--locked]=\"column.lockVisibility\"\r\n>\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"tcc-col__eye\" [class.tcc-col__eye--off]=\"!column.visible\" aria-hidden=\"true\">\r\n @if (column.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"tcc-col__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (column.lockVisibility) {\r\n <span class=\"tcc-lock\" title=\"This column is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"tcc-toggle\"\r\n [class.tcc-toggle--on]=\"column.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"column.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"tcc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".tcc-toggle{position:relative;flex-shrink:0;width:var(--tcc-toggle-width, 38px);height:var(--tcc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--tcc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.tcc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--tcc-toggle-height, 22px) - 4px);height:calc(var(--tcc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--tcc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.tcc-toggle--on{background:var(--tcc-toggle-on-bg, #2f9e6f)}.tcc-toggle--on .tcc-toggle__thumb{left:calc(100% - (var(--tcc-toggle-height, 22px) - 2px))}.tcc-toggle:focus-visible{outline:2px solid var(--tcc-focus-ring, #2f9e6f);outline-offset:2px}.tcc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--tcc-toggle-width, 38px);color:var(--tcc-lock-color, #9aa2ab)}.tcc-col{display:flex;align-items:center;gap:var(--tcc-col-gap, 12px);padding:var(--tcc-col-padding, 10px 14px);border-radius:var(--tcc-col-radius, 8px);background:var(--tcc-col-bg, #ffffff);border:1px solid var(--tcc-col-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.tcc-col:hover:not(.tcc-col--locked){background:var(--tcc-col-hover-bg, #f8fafc)}.tcc-col--hidden .tcc-col__eye,.tcc-col--hidden .tcc-col__label{opacity:.55}.tcc-col--locked{background:var(--tcc-col-locked-bg, #f6f7f8)}.tcc-col__eye{display:flex;align-items:center;color:var(--tcc-eye-color, #6b7280);flex-shrink:0}.tcc-col__eye--off{color:var(--tcc-eye-off-color, #b0b6be)}.tcc-col__label{flex:1 1 auto;min-width:0;font-size:var(--tcc-col-label-size, 14px);color:var(--tcc-col-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
11476
+ args: [{ selector: 'lib-config-column-node', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"tcc-col\"\r\n [class.tcc-col--hidden]=\"!column.visible\"\r\n [class.tcc-col--locked]=\"column.lockVisibility\"\r\n>\r\n <!-- Drag handle (reorder via the sequence field) -->\r\n <span class=\"tcc-col__grip\" title=\"Drag to reorder\" aria-hidden=\"true\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <circle cx=\"8\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"5\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"12\" r=\"1.6\"></circle>\r\n <circle cx=\"8\" cy=\"19\" r=\"1.6\"></circle>\r\n <circle cx=\"16\" cy=\"19\" r=\"1.6\"></circle>\r\n </svg>\r\n </span>\r\n\r\n <!-- Visibility indicator (eye / eye-off) -->\r\n <span class=\"tcc-col__eye\" [class.tcc-col__eye--off]=\"!column.visible\" aria-hidden=\"true\">\r\n @if (column.visible) {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n } @else {\r\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\r\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\r\n </svg>\r\n }\r\n </span>\r\n\r\n <span class=\"tcc-col__label\" [title]=\"displayLabel\">{{ displayLabel }}</span>\r\n\r\n <!-- Visibility toggle / lock -->\r\n @if (column.lockVisibility) {\r\n <span class=\"tcc-lock\" title=\"This column is always visible\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"></path>\r\n </svg>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"tcc-toggle\"\r\n [class.tcc-toggle--on]=\"column.visible\"\r\n role=\"switch\"\r\n [attr.aria-checked]=\"column.visible\"\r\n [attr.aria-label]=\"'Show ' + displayLabel\"\r\n (click)=\"toggleVisible.emit()\"\r\n >\r\n <span class=\"tcc-toggle__thumb\"></span>\r\n </button>\r\n }\r\n</div>\r\n", styles: [".tcc-toggle{position:relative;flex-shrink:0;width:var(--tcc-toggle-width, 38px);height:var(--tcc-toggle-height, 22px);padding:0;border:none;border-radius:999px;background:var(--tcc-toggle-off-bg, #cbd2d9);cursor:pointer;transition:background .18s ease}.tcc-toggle__thumb{position:absolute;top:2px;left:2px;width:calc(var(--tcc-toggle-height, 22px) - 4px);height:calc(var(--tcc-toggle-height, 22px) - 4px);border-radius:50%;background:var(--tcc-toggle-thumb, #ffffff);box-shadow:0 1px 2px #00000040;transition:left .18s ease}.tcc-toggle--on{background:var(--tcc-toggle-on-bg, #2f9e6f)}.tcc-toggle--on .tcc-toggle__thumb{left:calc(100% - (var(--tcc-toggle-height, 22px) - 2px))}.tcc-toggle:focus-visible{outline:2px solid var(--tcc-focus-ring, #2f9e6f);outline-offset:2px}.tcc-lock{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--tcc-toggle-width, 38px);color:var(--tcc-lock-color, #9aa2ab)}:host{display:block;cursor:grab}:host.cdk-drag-dragging,:host.cdk-drag-placeholder{cursor:grabbing}.tcc-col{display:flex;align-items:center;gap:var(--tcc-col-gap, 12px);padding:var(--tcc-col-padding, 10px 14px);border-radius:var(--tcc-col-radius, 8px);background:var(--tcc-col-bg, #ffffff);border:1px solid var(--tcc-col-border, #eef0f2);transition:background .15s,border-color .15s,opacity .15s}.tcc-col:hover:not(.tcc-col--locked){background:var(--tcc-col-hover-bg, #f8fafc)}.tcc-col__grip{display:flex;align-items:center;color:var(--tcc-grip-color, #cbd2d9);flex-shrink:0}.tcc-col--hidden .tcc-col__eye,.tcc-col--hidden .tcc-col__label{opacity:.55}.tcc-col--locked{background:var(--tcc-col-locked-bg, #f6f7f8)}.tcc-col__eye{display:flex;align-items:center;color:var(--tcc-eye-color, #6b7280);flex-shrink:0}.tcc-col__eye--off{color:var(--tcc-eye-off-color, #b0b6be)}.tcc-col__label{flex:1 1 auto;min-width:0;font-size:var(--tcc-col-label-size, 14px);color:var(--tcc-col-label-color, #1f2933);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
11318
11477
  }], propDecorators: { column: [{
11319
11478
  type: Input,
11320
11479
  args: [{ required: true }]
@@ -11328,15 +11487,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
11328
11487
  * Table Column Configuration
11329
11488
  * --------------------------
11330
11489
  * Renders a compact list of a table config's columns, each with a **visibility**
11331
- * toggle. Intended to be embedded inside a `cc-confirmation-modal` on a host page
11332
- * ("Configure columns").
11490
+ * toggle and a drag handle for **reordering**. Intended to be embedded inside a
11491
+ * `cc-confirmation-modal` on a host page ("Configure columns").
11333
11492
  *
11334
11493
  * Pass a `TableConfig` via `[config]`. The input is deep-cloned, so it is never
11335
- * mutated. When the user confirms, call `getUpdatedConfig()` to read back the
11336
- * modified config (isEnabled/visible flags updated per column).
11494
+ * mutated. Columns load pre-sorted by `sequence` (if set); columns without a
11495
+ * `sequence` keep their original relative order. When the user confirms, call
11496
+ * `getUpdatedConfig()` to read back the modified config (isEnabled/visible flags
11497
+ * and `sequence` updated per column, in the new order).
11337
11498
  *
11338
11499
  * Columns carrying `lockVisibility: true` cannot be hidden — their toggle is
11339
- * replaced by a lock icon.
11500
+ * replaced by a lock icon. They can still be dragged to reorder.
11340
11501
  *
11341
11502
  * @example
11342
11503
  * ```html
@@ -11372,15 +11533,21 @@ class TableColumnConfigurationComponent {
11372
11533
  onToggleVisible(index) {
11373
11534
  this.store.toggleColumnVisible(index);
11374
11535
  }
11536
+ /** Drag-and-drop reorder. Written back as `sequence` on each column by `getUpdatedConfig()`. */
11537
+ onDrop(event) {
11538
+ if (event.previousIndex === event.currentIndex)
11539
+ return;
11540
+ this.store.reorderColumn(event.previousIndex, event.currentIndex);
11541
+ }
11375
11542
  trackByIndex(index) {
11376
11543
  return index;
11377
11544
  }
11378
11545
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11379
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: TableColumnConfigurationComponent, isStandalone: false, selector: "lib-table-column-configuration", inputs: { config: "config", translate: "translate", showLegend: "showLegend" }, providers: [TableColumnConfigurationService], usesOnChanges: true, ngImport: i0, template: "<div class=\"tcc-root\">\r\n @if (showLegend) {\r\n <div class=\"tcc-legend\">\r\n <span class=\"tcc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"tcc-list\">\r\n @for (column of store.columns(); track trackByIndex($index); let ci = $index) {\r\n <lib-config-column-node\r\n [column]=\"column\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(ci)\"\r\n />\r\n }\r\n\r\n @if (store.columns().length === 0) {\r\n <div class=\"tcc-empty\">\r\n <p>No columns found in the config.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".tcc-root{display:flex;flex-direction:column;gap:var(--tcc-root-gap, 12px);font-family:var(--tcc-font-family, inherit)}.tcc-legend{display:flex;align-items:center;gap:var(--tcc-legend-gap, 24px);padding:var(--tcc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--tcc-legend-border, #eef0f2);flex-wrap:wrap}.tcc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--tcc-legend-size, 13px);color:var(--tcc-legend-color, #52606d)}.tcc-list{display:flex;flex-direction:column;gap:var(--tcc-list-gap, 10px)}.tcc-empty{padding:24px;text-align:center;color:var(--tcc-empty-color, #9aa2ab);font-size:13px}\n"], dependencies: [{ kind: "component", type: ConfigColumnNodeComponent, selector: "lib-config-column-node", inputs: ["column", "translate"], outputs: ["toggleVisible"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11546
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: TableColumnConfigurationComponent, isStandalone: false, selector: "lib-table-column-configuration", inputs: { config: "config", translate: "translate", showLegend: "showLegend" }, providers: [TableColumnConfigurationService], usesOnChanges: true, ngImport: i0, template: "<div class=\"tcc-root\">\r\n @if (showLegend) {\r\n <div class=\"tcc-legend\">\r\n <span class=\"tcc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"tcc-list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\r\n @for (column of store.columns(); track trackByIndex($index); let ci = $index) {\r\n <lib-config-column-node\r\n cdkDrag\r\n [column]=\"column\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(ci)\"\r\n />\r\n }\r\n\r\n @if (store.columns().length === 0) {\r\n <div class=\"tcc-empty\">\r\n <p>No columns found in the config.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".tcc-root{display:flex;flex-direction:column;gap:var(--tcc-root-gap, 12px);font-family:var(--tcc-font-family, inherit)}.tcc-legend{display:flex;align-items:center;gap:var(--tcc-legend-gap, 24px);padding:var(--tcc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--tcc-legend-border, #eef0f2);flex-wrap:wrap}.tcc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--tcc-legend-size, 13px);color:var(--tcc-legend-color, #52606d)}.tcc-list{display:flex;flex-direction:column;gap:var(--tcc-list-gap, 10px)}.tcc-empty{padding:24px;text-align:center;color:var(--tcc-empty-color, #9aa2ab);font-size:13px}\n"], dependencies: [{ kind: "directive", type: i1$5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ConfigColumnNodeComponent, selector: "lib-config-column-node", inputs: ["column", "translate"], outputs: ["toggleVisible"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11380
11547
  }
11381
11548
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationComponent, decorators: [{
11382
11549
  type: Component,
11383
- args: [{ selector: 'lib-table-column-configuration', standalone: false, providers: [TableColumnConfigurationService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"tcc-root\">\r\n @if (showLegend) {\r\n <div class=\"tcc-legend\">\r\n <span class=\"tcc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"tcc-list\">\r\n @for (column of store.columns(); track trackByIndex($index); let ci = $index) {\r\n <lib-config-column-node\r\n [column]=\"column\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(ci)\"\r\n />\r\n }\r\n\r\n @if (store.columns().length === 0) {\r\n <div class=\"tcc-empty\">\r\n <p>No columns found in the config.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".tcc-root{display:flex;flex-direction:column;gap:var(--tcc-root-gap, 12px);font-family:var(--tcc-font-family, inherit)}.tcc-legend{display:flex;align-items:center;gap:var(--tcc-legend-gap, 24px);padding:var(--tcc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--tcc-legend-border, #eef0f2);flex-wrap:wrap}.tcc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--tcc-legend-size, 13px);color:var(--tcc-legend-color, #52606d)}.tcc-list{display:flex;flex-direction:column;gap:var(--tcc-list-gap, 10px)}.tcc-empty{padding:24px;text-align:center;color:var(--tcc-empty-color, #9aa2ab);font-size:13px}\n"] }]
11550
+ args: [{ selector: 'lib-table-column-configuration', standalone: false, providers: [TableColumnConfigurationService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"tcc-root\">\r\n @if (showLegend) {\r\n <div class=\"tcc-legend\">\r\n <span class=\"tcc-legend__item\">\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z\"></path>\r\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\r\n </svg>\r\n <span>Visible</span>\r\n </span>\r\n </div>\r\n }\r\n\r\n <div class=\"tcc-list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\r\n @for (column of store.columns(); track trackByIndex($index); let ci = $index) {\r\n <lib-config-column-node\r\n cdkDrag\r\n [column]=\"column\"\r\n [translate]=\"translate\"\r\n (toggleVisible)=\"onToggleVisible(ci)\"\r\n />\r\n }\r\n\r\n @if (store.columns().length === 0) {\r\n <div class=\"tcc-empty\">\r\n <p>No columns found in the config.</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".tcc-root{display:flex;flex-direction:column;gap:var(--tcc-root-gap, 12px);font-family:var(--tcc-font-family, inherit)}.tcc-legend{display:flex;align-items:center;gap:var(--tcc-legend-gap, 24px);padding:var(--tcc-legend-padding, 4px 2px 8px);border-bottom:1px solid var(--tcc-legend-border, #eef0f2);flex-wrap:wrap}.tcc-legend__item{display:inline-flex;align-items:center;gap:6px;font-size:var(--tcc-legend-size, 13px);color:var(--tcc-legend-color, #52606d)}.tcc-list{display:flex;flex-direction:column;gap:var(--tcc-list-gap, 10px)}.tcc-empty{padding:24px;text-align:center;color:var(--tcc-empty-color, #9aa2ab);font-size:13px}\n"] }]
11384
11551
  }], propDecorators: { config: [{
11385
11552
  type: Input,
11386
11553
  args: [{ required: true }]
@@ -11397,8 +11564,10 @@ const COMPONENTS = [
11397
11564
  class TableColumnConfigurationModule {
11398
11565
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
11399
11566
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationModule, declarations: [TableColumnConfigurationComponent,
11400
- ConfigColumnNodeComponent], imports: [CommonModule], exports: [TableColumnConfigurationComponent] });
11401
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationModule, imports: [CommonModule] });
11567
+ ConfigColumnNodeComponent], imports: [CommonModule,
11568
+ DragDropModule], exports: [TableColumnConfigurationComponent] });
11569
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationModule, imports: [CommonModule,
11570
+ DragDropModule] });
11402
11571
  }
11403
11572
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TableColumnConfigurationModule, decorators: [{
11404
11573
  type: NgModule,
@@ -11406,6 +11575,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
11406
11575
  declarations: [...COMPONENTS],
11407
11576
  imports: [
11408
11577
  CommonModule,
11578
+ DragDropModule,
11409
11579
  ],
11410
11580
  exports: [
11411
11581
  TableColumnConfigurationComponent,
@@ -13498,6 +13668,7 @@ class SmartTableComponent {
13498
13668
  ngOnInit() {
13499
13669
  window.addEventListener('scroll', this.onScrollCapture, true);
13500
13670
  if (this.config) {
13671
+ this.sortColumnsBySequence();
13501
13672
  this.applyPaginationDefaults();
13502
13673
  if (this.config.sortBy) {
13503
13674
  this.activeSort.key = this.config.sortBy;
@@ -13528,8 +13699,9 @@ class SmartTableComponent {
13528
13699
  if (changes['totalItemsCount'] && this.totalItemsCount !== undefined) {
13529
13700
  this.totalItems = this.totalItemsCount;
13530
13701
  }
13531
- // Config changed (non-first) — reload filter opts; reload data only if NOT in external mode
13702
+ // Config changed — re-apply column ordering whenever a new config object arrives
13532
13703
  if (changes['config'] && !changes['config'].firstChange) {
13704
+ this.sortColumnsBySequence();
13533
13705
  this.applyPaginationDefaults();
13534
13706
  this.loadFilterOptions();
13535
13707
  if (this.tableData === undefined) {
@@ -14260,6 +14432,24 @@ class SmartTableComponent {
14260
14432
  get columnCount() {
14261
14433
  return this.config.columns.length;
14262
14434
  }
14435
+ /**
14436
+ * Orders `config.columns` by their `sequence` value (ascending). Columns without a
14437
+ * `sequence` are left in their original relative order, placed after any columns that
14438
+ * do have one — so configs that never set `sequence` are left untouched (stable sort).
14439
+ */
14440
+ sortColumnsBySequence() {
14441
+ const columns = this.config?.columns;
14442
+ if (!columns || columns.length === 0)
14443
+ return;
14444
+ const hasSequence = columns.some(c => c.sequence !== undefined && c.sequence !== null);
14445
+ if (!hasSequence)
14446
+ return;
14447
+ this.config.columns = [...columns].sort((a, b) => {
14448
+ const aSeq = a.sequence ?? Number.MAX_SAFE_INTEGER;
14449
+ const bSeq = b.sequence ?? Number.MAX_SAFE_INTEGER;
14450
+ return aSeq - bSeq;
14451
+ });
14452
+ }
14263
14453
  /**
14264
14454
  * Filters out actions/action-items that are either explicitly disabled via
14265
14455
  * `isEnabled: false` or hidden for the given row by their `visibleWhen` /