@shival99/z-ui 2.0.87 → 2.0.88

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.
@@ -4555,130 +4555,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
4555
4555
  /**
4556
4556
  * EN: Calculates sticky pinning style offsets for header, body, and footer cells, including colSpan cases.
4557
4557
  * VI: Tính toán offset sticky pinning cho header, body và footer, bao gồm các trường hợp có colSpan.
4558
+ *
4559
+ * Uses DOM-measured column widths (domWidthMap) as the source of truth for all offset calculations,
4560
+ * so that sub-pixel rendering, flex/fit stretching, and user-resized columns are all accounted for.
4558
4561
  */
4559
4562
  class ZTablePinningStylesPipe {
4560
4563
  /**
4561
- * Main transform method - routes to appropriate calculation based on type
4562
- *
4563
4564
  * @param column - The column being styled
4564
4565
  * @param item - The cell or header item
4565
4566
  * @param type - 'header' | 'body' | 'footer'
4566
- * @param tableOrCells - Table instance or array of cells
4567
+ * @param tableOrCells - Table instance (header/footer) or array of cells (body)
4567
4568
  * @param columns - Column configuration array
4568
- * @param _sizingTrigger - Optional sizing trigger to force recalculation on resize
4569
- * @param renderedSizeMap - Map of columnId rendered pixel width (includes distributed extra width)
4570
- * @returns CSS style object for sticky positioning
4569
+ * @param _sizingTrigger - Sizing trigger to force recalculation on resize
4570
+ * @param domWidthMap - DOM-measured pixel widths keyed by column id (from colDomWidthMap signal)
4571
4571
  */
4572
- transform(column, item, type, tableOrCells, columns, _sizingTrigger, renderedSizeMap) {
4572
+ transform(column, item, type, tableOrCells, columns, _sizingTrigger, domWidthMap) {
4573
4573
  if (type === 'body') {
4574
4574
  const allCells = Array.isArray(tableOrCells) ? tableOrCells : undefined;
4575
- return this._getBodyPinningStyles(column, item, allCells, columns || [], renderedSizeMap);
4575
+ return this._getBodyPinningStyles(column, item, allCells, columns || [], domWidthMap);
4576
4576
  }
4577
4577
  if (type === 'footer' && item && tableOrCells && !Array.isArray(tableOrCells)) {
4578
- return this._getFooterPinningStyles(column, item, tableOrCells, columns || []);
4578
+ return this._getFooterPinningStyles(column, item, tableOrCells, columns || [], domWidthMap);
4579
4579
  }
4580
4580
  if (type === 'header' && item && tableOrCells && !Array.isArray(tableOrCells)) {
4581
- return this._getHeaderPinningStyles(column, item, tableOrCells, columns || []);
4581
+ return this._getHeaderPinningStyles(column, item, tableOrCells, columns || [], domWidthMap);
4582
4582
  }
4583
4583
  return {};
4584
4584
  }
4585
- _getBodyPinningStyles(column, cell, allCells, columns, renderedSizeMap) {
4585
+ _getColWidth(columnId, column, domWidthMap) {
4586
+ return domWidthMap?.[columnId] ?? column.getSize();
4587
+ }
4588
+ _getBodyPinningStyles(column, cell, allCells, columns, domWidthMap) {
4586
4589
  const isPinned = column.getIsPinned();
4587
- // TanStack's getStart/getAfter correctly compute offsets from columnSizingState
4588
- // and are the source of truth for sticky positioning. The custom body calculation
4589
- // below is only needed when body cells use colSpan > 1 (which visually shifts
4590
- // adjacent pinned cells). Without colSpan, always trust TanStack's values.
4590
+ if (!isPinned)
4591
+ return this._buildPinningResult(undefined, 0, 0);
4591
4592
  const hasAnyColspan = allCells?.some(c => {
4592
4593
  const config = findColumnConfig(c.column.id, columns);
4593
4594
  const colSpan = getBodyColSpan(config, c.getContext());
4594
4595
  return colSpan !== undefined && colSpan > 1;
4595
4596
  }) ?? false;
4596
- let leftOffset = column.getStart('left');
4597
- let rightOffset = column.getAfter('right');
4598
- if (hasAnyColspan && allCells && cell && isPinned === 'left') {
4599
- leftOffset = this._calculateBodyLeftOffset(cell, allCells, columns, renderedSizeMap);
4597
+ let leftOffset;
4598
+ let rightOffset;
4599
+ if (hasAnyColspan && allCells && cell) {
4600
+ leftOffset = isPinned === 'left'
4601
+ ? this._calculateBodyLeftOffset(cell, allCells, columns, domWidthMap)
4602
+ : 0;
4603
+ rightOffset = isPinned === 'right'
4604
+ ? this._calculateBodyRightOffset(cell, allCells, columns, domWidthMap)
4605
+ : 0;
4600
4606
  }
4601
- if (hasAnyColspan && allCells && cell && isPinned === 'right') {
4602
- rightOffset = this._calculateBodyRightOffset(cell, allCells, columns, renderedSizeMap);
4607
+ else {
4608
+ // No colspan: sum DOM widths of pinned columns before/after this column using allCells
4609
+ if (isPinned === 'left') {
4610
+ const idx = allCells ? allCells.findIndex(c => c.column.id === column.id) : -1;
4611
+ leftOffset = allCells && idx > 0
4612
+ ? allCells.slice(0, idx)
4613
+ .filter(c => c.column.getIsPinned() === 'left')
4614
+ .reduce((sum, c) => sum + this._getColWidth(c.column.id, c.column, domWidthMap), 0)
4615
+ : 0;
4616
+ rightOffset = 0;
4617
+ }
4618
+ else {
4619
+ leftOffset = 0;
4620
+ const idx = allCells ? allCells.findIndex(c => c.column.id === column.id) : -1;
4621
+ rightOffset = allCells && idx >= 0
4622
+ ? allCells.slice(idx + 1)
4623
+ .filter(c => c.column.getIsPinned() === 'right')
4624
+ .reduce((sum, c) => sum + this._getColWidth(c.column.id, c.column, domWidthMap), 0)
4625
+ : 0;
4626
+ }
4603
4627
  }
4604
4628
  return this._buildPinningResult(isPinned, leftOffset, rightOffset);
4605
4629
  }
4606
- /**
4607
- * Calculates left offset for a body cell considering colspan.
4608
- * Sums widths of all cells before the current cell.
4609
- */
4610
- _calculateBodyLeftOffset(cell, allCells, columns, renderedSizeMap) {
4630
+ _calculateBodyLeftOffset(cell, allCells, columns, domWidthMap) {
4611
4631
  const currentIdx = allCells.findIndex(c => c.id === cell.id);
4612
- if (currentIdx <= 0) {
4632
+ if (currentIdx <= 0)
4613
4633
  return 0;
4614
- }
4615
- let calculatedOffset = 0;
4634
+ let offset = 0;
4616
4635
  for (let i = 0; i < currentIdx; i++) {
4617
4636
  const prevCell = allCells[i];
4618
- // Skip cells that are covered by colspan from previous cells
4619
- if (!shouldBodyCellRenderColSpan(prevCell, allCells, columns)) {
4637
+ if (!shouldBodyCellRenderColSpan(prevCell, allCells, columns))
4620
4638
  continue;
4621
- }
4622
- calculatedOffset += this._getCellWidthWithColspan(prevCell, allCells, i, columns, renderedSizeMap);
4639
+ offset += this._getCellWidthWithColspan(prevCell, allCells, i, columns, domWidthMap);
4623
4640
  }
4624
- return calculatedOffset;
4641
+ return offset;
4625
4642
  }
4626
- /**
4627
- * Calculates right offset for a body cell considering colspan.
4628
- * Sums widths of all cells after the current cell.
4629
- */
4630
- _calculateBodyRightOffset(cell, allCells, columns, renderedSizeMap) {
4643
+ _calculateBodyRightOffset(cell, allCells, columns, domWidthMap) {
4631
4644
  const currentIdx = allCells.findIndex(c => c.id === cell.id);
4632
- // Last cell has 0 right offset
4633
- if (currentIdx === allCells.length - 1) {
4645
+ if (currentIdx < 0 || currentIdx === allCells.length - 1)
4634
4646
  return 0;
4635
- }
4636
- if (currentIdx < 0) {
4637
- return 0;
4638
- }
4639
- let calculatedOffset = 0;
4647
+ let offset = 0;
4640
4648
  for (let i = currentIdx + 1; i < allCells.length; i++) {
4641
4649
  const nextCell = allCells[i];
4642
- // Skip cells that are covered by colspan
4643
- if (!shouldBodyCellRenderColSpan(nextCell, allCells, columns)) {
4650
+ if (!shouldBodyCellRenderColSpan(nextCell, allCells, columns))
4644
4651
  continue;
4645
- }
4646
- calculatedOffset += this._getCellWidthWithColspan(nextCell, allCells, i, columns, renderedSizeMap);
4652
+ offset += this._getCellWidthWithColspan(nextCell, allCells, i, columns, domWidthMap);
4647
4653
  }
4648
- return calculatedOffset;
4654
+ return offset;
4649
4655
  }
4650
- /**
4651
- * Gets cell width including any colspan expansion.
4652
- * Uses renderedSizeMap when available so that distributed extra-width is accounted for,
4653
- * preventing left/right sticky offsets from drifting when the table fills its container.
4654
- */
4655
- _getCellWidthWithColspan(cell, allCells, cellIndex, columns, renderedSizeMap) {
4656
- const getCellWidth = (c) => renderedSizeMap?.[c.column.id] ?? c.column.getSize();
4656
+ _getCellWidthWithColspan(cell, allCells, cellIndex, columns, domWidthMap) {
4657
+ const getW = (c) => this._getColWidth(c.column.id, c.column, domWidthMap);
4657
4658
  const config = findColumnConfig(cell.column.id, columns);
4658
4659
  const colSpan = getBodyColSpan(config, cell.getContext());
4659
4660
  if (colSpan && colSpan > 1) {
4660
- let spanWidth = getCellWidth(cell);
4661
+ let w = getW(cell);
4661
4662
  for (let j = 1; j < colSpan && cellIndex + j < allCells.length; j++) {
4662
- spanWidth += getCellWidth(allCells[cellIndex + j]);
4663
+ w += getW(allCells[cellIndex + j]);
4663
4664
  }
4664
- return spanWidth;
4665
+ return w;
4665
4666
  }
4666
- return getCellWidth(cell);
4667
+ return getW(cell);
4667
4668
  }
4668
- _getHeaderPinningStyles(column, headerCurrent, table, columns) {
4669
+ _getHeaderPinningStyles(column, headerCurrent, table, columns, domWidthMap) {
4669
4670
  const isPinnedColumn = column.getIsPinned();
4670
- if (!isPinnedColumn) {
4671
+ if (!isPinnedColumn)
4671
4672
  return this._buildPinningResult(undefined, 0, 0);
4672
- }
4673
4673
  const isChildPinned = this._determineChildPinDirection(column, headerCurrent);
4674
4674
  const headerGroups = headerCurrent.headerGroup.headers;
4675
4675
  let leftOffset;
4676
4676
  let rightOffset;
4677
4677
  if (isChildPinned === 'left') {
4678
- leftOffset = this._calculateHeaderLeftOffset(headerCurrent, headerGroups, columns);
4678
+ leftOffset = this._calculateHeaderLeftOffset(headerCurrent, headerGroups, columns, domWidthMap);
4679
4679
  }
4680
4680
  if (isChildPinned === 'right') {
4681
- rightOffset = this._calculateHeaderRightOffset(headerCurrent, headerGroups, columns);
4681
+ rightOffset = this._calculateHeaderRightOffset(headerCurrent, headerGroups, columns, domWidthMap);
4682
4682
  }
4683
4683
  return {
4684
4684
  position: isChildPinned ? 'sticky' : 'relative',
@@ -4687,94 +4687,67 @@ class ZTablePinningStylesPipe {
4687
4687
  right: rightOffset !== undefined ? `${rightOffset}px` : undefined,
4688
4688
  };
4689
4689
  }
4690
- /**
4691
- * Determines if any child header is pinned and returns the direction.
4692
- * Checks leaf headers to find pinning status.
4693
- */
4694
4690
  _determineChildPinDirection(column, header) {
4695
4691
  const isPinnedColumn = column.getIsPinned();
4696
4692
  const leafHeaders = header.getLeafHeaders();
4697
4693
  const filterChild = leafHeaders.filter(h => h.column.id !== column.id);
4698
- // No children - use column's own pin status
4699
- if (filterChild.length === 0) {
4694
+ if (filterChild.length === 0)
4700
4695
  return isPinnedColumn || undefined;
4701
- }
4702
- // Check if any child is pinned
4703
4696
  for (const child of filterChild) {
4704
4697
  const isPinned = child.column.getIsPinned();
4705
- if (isPinned) {
4698
+ if (isPinned)
4706
4699
  return isPinned;
4707
- }
4708
4700
  }
4709
4701
  return undefined;
4710
4702
  }
4711
- /**
4712
- * Calculates left offset for a header cell.
4713
- * Sums widths of all left-pinned headers before current header.
4714
- */
4715
- _calculateHeaderLeftOffset(headerCurrent, headerGroups, columns) {
4703
+ _calculateHeaderLeftOffset(headerCurrent, headerGroups, columns, domWidthMap) {
4716
4704
  const headersBeforeCurrent = this._getLeftPinnedHeadersBefore(headerCurrent, headerGroups);
4717
4705
  return headersBeforeCurrent.reduce((sum, h) => {
4718
- if (!shouldHeaderRender(h, headerGroups, columns)) {
4706
+ if (!shouldHeaderRender(h, headerGroups, columns))
4719
4707
  return sum;
4720
- }
4721
- return sum + this._getHeaderWidthWithColspan(h, headerGroups, columns, 'header');
4708
+ return sum + this._getHeaderWidthWithColspan(h, headerGroups, columns, 'header', domWidthMap);
4722
4709
  }, 0);
4723
4710
  }
4724
- /**
4725
- * Calculates right offset for a header cell.
4726
- * Sums widths of all right-pinned headers after current header.
4727
- */
4728
- _calculateHeaderRightOffset(headerCurrent, headerGroups, columns) {
4711
+ _calculateHeaderRightOffset(headerCurrent, headerGroups, columns, domWidthMap) {
4729
4712
  const headersAfterCurrent = this._getRightPinnedHeadersAfter(headerCurrent, headerGroups);
4730
4713
  return headersAfterCurrent.reduce((sum, h) => {
4731
- if (!shouldHeaderRender(h, headerGroups, columns)) {
4714
+ if (!shouldHeaderRender(h, headerGroups, columns))
4732
4715
  return sum;
4733
- }
4734
- return sum + this._getHeaderWidthWithColspan(h, headerGroups, columns, 'header');
4716
+ return sum + this._getHeaderWidthWithColspan(h, headerGroups, columns, 'header', domWidthMap);
4735
4717
  }, 0);
4736
4718
  }
4737
- /**
4738
- * Gets all left-pinned headers that appear before the current header.
4739
- */
4740
4719
  _getLeftPinnedHeadersBefore(headerCurrent, headerGroups) {
4741
4720
  const result = [];
4742
4721
  const indexOfHeader = headerGroups.findIndex(h => h.id === headerCurrent.id);
4743
4722
  for (let i = 0; i < indexOfHeader; i++) {
4744
4723
  const leafs = headerGroups[i].getLeafHeaders();
4745
4724
  const hasLeftPinned = leafs.some(lh => lh.column.getIsPinned() === 'left');
4746
- if (hasLeftPinned) {
4725
+ if (hasLeftPinned)
4747
4726
  result.push(headerGroups[i]);
4748
- }
4749
4727
  }
4750
4728
  return result;
4751
4729
  }
4752
- /**
4753
- * Gets all headers that appear after the current header (for right pinning).
4754
- */
4755
4730
  _getRightPinnedHeadersAfter(headerCurrent, headerGroups) {
4756
4731
  const result = [];
4757
4732
  const reversed = [...headerGroups].reverse();
4758
4733
  const indexOfHeader = reversed.findIndex(h => h.id === headerCurrent.id);
4759
- for (let i = 0; i < indexOfHeader; i++) {
4734
+ for (let i = 0; i < indexOfHeader; i++)
4760
4735
  result.push(reversed[i]);
4761
- }
4762
4736
  return result;
4763
4737
  }
4764
- _getFooterPinningStyles(column, footerCurrent, table, columns) {
4738
+ _getFooterPinningStyles(column, footerCurrent, table, columns, domWidthMap) {
4765
4739
  const isPinnedColumn = column.getIsPinned();
4766
- if (!isPinnedColumn) {
4740
+ if (!isPinnedColumn)
4767
4741
  return this._buildPinningResult(undefined, 0, 0);
4768
- }
4769
4742
  const isChildPinned = this._determineChildPinDirection(column, footerCurrent);
4770
4743
  const footerHeaders = footerCurrent.headerGroup.headers;
4771
4744
  let leftOffset;
4772
4745
  let rightOffset;
4773
4746
  if (isChildPinned === 'left') {
4774
- leftOffset = this._calculateFooterLeftOffset(footerCurrent, footerHeaders, columns);
4747
+ leftOffset = this._calculateFooterLeftOffset(footerCurrent, footerHeaders, columns, domWidthMap);
4775
4748
  }
4776
4749
  if (isChildPinned === 'right') {
4777
- rightOffset = this._calculateFooterRightOffset(footerCurrent, footerHeaders, columns);
4750
+ rightOffset = this._calculateFooterRightOffset(footerCurrent, footerHeaders, columns, domWidthMap);
4778
4751
  }
4779
4752
  return {
4780
4753
  position: isChildPinned ? 'sticky' : 'relative',
@@ -4783,61 +4756,42 @@ class ZTablePinningStylesPipe {
4783
4756
  right: rightOffset !== undefined ? `${rightOffset}px` : undefined,
4784
4757
  };
4785
4758
  }
4786
- /**
4787
- * Calculates left offset for a footer cell.
4788
- * Sums widths of all left-pinned footers before current footer.
4789
- */
4790
- _calculateFooterLeftOffset(footerCurrent, footerHeaders, columns) {
4759
+ _calculateFooterLeftOffset(footerCurrent, footerHeaders, columns, domWidthMap) {
4791
4760
  const footersBeforeCurrent = this._getLeftPinnedHeadersBefore(footerCurrent, footerHeaders);
4792
4761
  return footersBeforeCurrent.reduce((sum, h) => {
4793
- if (!shouldFooterRender(h, footerHeaders, columns)) {
4762
+ if (!shouldFooterRender(h, footerHeaders, columns))
4794
4763
  return sum;
4795
- }
4796
- return sum + this._getHeaderWidthWithColspan(h, footerHeaders, columns, 'footer');
4764
+ return sum + this._getHeaderWidthWithColspan(h, footerHeaders, columns, 'footer', domWidthMap);
4797
4765
  }, 0);
4798
4766
  }
4799
- /**
4800
- * Calculates right offset for a footer cell.
4801
- * Handles colspan coverage to exclude covered cells from calculation.
4802
- */
4803
- _calculateFooterRightOffset(footerCurrent, footerHeaders, columns) {
4767
+ _calculateFooterRightOffset(footerCurrent, footerHeaders, columns, domWidthMap) {
4804
4768
  const footersAfterCurrent = this._getRightPinnedHeadersAfter(footerCurrent, footerHeaders);
4805
- // Check if current footer has colspan
4806
4769
  const currentConfig = findColumnConfig(footerCurrent.column.id, columns);
4807
4770
  const currentColSpan = getFooterColSpan(currentConfig);
4808
4771
  if (currentColSpan && currentColSpan > 1) {
4809
- return this._calculateRightOffsetWithColspan(footerCurrent, footerHeaders, footersAfterCurrent, currentColSpan, columns);
4772
+ return this._calculateRightOffsetWithColspan(footerCurrent, footerHeaders, footersAfterCurrent, currentColSpan, columns, domWidthMap);
4810
4773
  }
4811
- // No colspan - sum all footers after
4812
4774
  return footersAfterCurrent.reduce((sum, h) => {
4813
- if (!shouldFooterRender(h, footerHeaders, columns)) {
4775
+ if (!shouldFooterRender(h, footerHeaders, columns))
4814
4776
  return sum;
4815
- }
4816
- return sum + this._getHeaderWidthWithColspan(h, footerHeaders, columns, 'footer');
4777
+ return sum + this._getHeaderWidthWithColspan(h, footerHeaders, columns, 'footer', domWidthMap);
4817
4778
  }, 0);
4818
4779
  }
4819
- /**
4820
- * Calculates right offset when current footer has colspan.
4821
- * Excludes cells that are covered by the colspan.
4822
- */
4823
- _calculateRightOffsetWithColspan(footerCurrent, footerHeaders, footersAfterCurrent, colSpan, columns) {
4780
+ _calculateRightOffsetWithColspan(footerCurrent, footerHeaders, footersAfterCurrent, colSpan, columns, domWidthMap) {
4824
4781
  const currentIdx = footerHeaders.findIndex(h => h.id === footerCurrent.id);
4825
4782
  const coveredCount = Math.min(colSpan - 1, footerHeaders.length - currentIdx - 1);
4826
- // Build set of covered column IDs
4827
4783
  const coveredColumnIds = new Set();
4828
4784
  for (let j = 1; j <= coveredCount && currentIdx + j < footerHeaders.length; j++) {
4829
4785
  coveredColumnIds.add(footerHeaders[currentIdx + j].id);
4830
4786
  }
4831
- // Only sum uncovered footers
4832
4787
  const uncoveredFooters = footersAfterCurrent.filter(h => !coveredColumnIds.has(h.id));
4833
4788
  return uncoveredFooters.reduce((sum, h) => {
4834
- if (!shouldFooterRender(h, footerHeaders, columns)) {
4789
+ if (!shouldFooterRender(h, footerHeaders, columns))
4835
4790
  return sum;
4836
- }
4837
- return sum + h.column.getSize();
4791
+ return sum + this._getColWidth(h.column.id, h.column, domWidthMap);
4838
4792
  }, 0);
4839
4793
  }
4840
- _getHeaderWidthWithColspan(header, allHeaders, columns, type) {
4794
+ _getHeaderWidthWithColspan(header, allHeaders, columns, type, domWidthMap) {
4841
4795
  const getColSpan = type === 'header' ? getHeaderColSpan : getFooterColSpan;
4842
4796
  const shouldRender = type === 'header' ? shouldHeaderRender : shouldFooterRender;
4843
4797
  if (header.subHeaders.length > 0) {
@@ -4845,29 +4799,24 @@ class ZTablePinningStylesPipe {
4845
4799
  return header.subHeaders
4846
4800
  .filter(subH => subH.column.getIsPinned() === pinDirection || subH.column.getIsPinned())
4847
4801
  .filter(subH => shouldRender(subH, header.subHeaders, columns))
4848
- .reduce((subAcc, subH) => subAcc + this._getSingleHeaderWidth(subH, header.subHeaders, columns, getColSpan), 0);
4802
+ .reduce((subAcc, subH) => subAcc + this._getSingleHeaderWidth(subH, header.subHeaders, columns, getColSpan, domWidthMap), 0);
4849
4803
  }
4850
- return this._getSingleHeaderWidth(header, allHeaders, columns, getColSpan);
4804
+ return this._getSingleHeaderWidth(header, allHeaders, columns, getColSpan, domWidthMap);
4851
4805
  }
4852
- /**
4853
- * Gets width of a single header considering its colspan.
4854
- */
4855
- _getSingleHeaderWidth(header, allHeaders, columns, getColSpanFn) {
4806
+ _getSingleHeaderWidth(header, allHeaders, columns, getColSpanFn, domWidthMap) {
4856
4807
  const config = findColumnConfig(header.column.id, columns);
4857
4808
  const colSpan = getColSpanFn(config);
4858
4809
  if (colSpan && colSpan > 1) {
4859
4810
  const currentIdx = allHeaders.findIndex(h => h.id === header.id);
4860
- let totalWidth = header.column.getSize();
4811
+ let totalWidth = this._getColWidth(header.column.id, header.column, domWidthMap);
4861
4812
  for (let j = 1; j < colSpan && currentIdx + j < allHeaders.length; j++) {
4862
- totalWidth += allHeaders[currentIdx + j].column.getSize();
4813
+ const next = allHeaders[currentIdx + j];
4814
+ totalWidth += this._getColWidth(next.column.id, next.column, domWidthMap);
4863
4815
  }
4864
4816
  return totalWidth;
4865
4817
  }
4866
- return header.column.getSize();
4818
+ return this._getColWidth(header.column.id, header.column, domWidthMap);
4867
4819
  }
4868
- /**
4869
- * Builds the final pinning styles result object.
4870
- */
4871
4820
  _buildPinningResult(isPinned, leftOffset, rightOffset) {
4872
4821
  return {
4873
4822
  left: isPinned === 'left' ? `${leftOffset}px` : undefined,
@@ -6376,6 +6325,31 @@ class ZTableComponent {
6376
6325
  shouldFooterShowShadow = computed(() => this.showHeaderFooterShadow() && this.table.getBottomRows().length === 0, ...(ngDevMode ? [{ debugName: "shouldFooterShowShadow" }] : []));
6377
6326
  tbodyContainerWidth = signal(0, ...(ngDevMode ? [{ debugName: "tbodyContainerWidth" }] : []));
6378
6327
  tbodyContainerHeight = signal(0, ...(ngDevMode ? [{ debugName: "tbodyContainerHeight" }] : []));
6328
+ /** DOM-measured widths of all visible columns, keyed by column id. Updated after each render cycle. */
6329
+ colDomWidthMap = signal({}, ...(ngDevMode ? [{ debugName: "colDomWidthMap" }] : []));
6330
+ _colDomWidthUpdater = effect(() => {
6331
+ // Track signals that affect column widths so the effect re-runs when they change
6332
+ void this.columnPinning();
6333
+ void this.columnSizing();
6334
+ void this.columnVisibility();
6335
+ void this.columnOrder();
6336
+ void this.tbodyContainerWidth();
6337
+ // Read DOM after Angular finishes rendering
6338
+ queueMicrotask(() => {
6339
+ const thead = this.theadWrapper()?.nativeElement;
6340
+ if (!thead)
6341
+ return;
6342
+ const ths = thead.querySelectorAll('th[id]');
6343
+ if (!ths.length)
6344
+ return;
6345
+ const map = {};
6346
+ ths.forEach(th => {
6347
+ if (th.id)
6348
+ map[th.id] = th.getBoundingClientRect().width;
6349
+ });
6350
+ this.colDomWidthMap.set(map);
6351
+ });
6352
+ }, ...(ngDevMode ? [{ debugName: "_colDomWidthUpdater" }] : []));
6379
6353
  skeletonRowHeight = signal(Z_SKELETON_ROW_HEIGHT, ...(ngDevMode ? [{ debugName: "skeletonRowHeight" }] : []));
6380
6354
  skeletonRowCount = computed(() => {
6381
6355
  const containerHeight = this.tbodyContainerHeight();
@@ -9571,7 +9545,7 @@ class ZTableComponent {
9571
9545
  return result;
9572
9546
  }
9573
9547
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9574
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { zClass: { classPropertyName: "zClass", publicName: "zClass", isSignal: true, isRequired: false, transformFunction: null }, zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null }, zVariant: { classPropertyName: "zVariant", publicName: "zVariant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative py-1" }, providers: [TranslatePipe, ZTableDragService], viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tableContainer", first: true, predicate: ["tableContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }, { propertyName: "virtualRowElements", predicate: ["virtualRow"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n #tableContainer\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.width]=\"zConfig().width\"\n [style.height]=\"zConfig().height\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\"\n [attr.tabindex]=\"canUseContentRowInsert() || canUseCellSelection() ? 0 : null\"\n (keydown)=\"onContentTableKeydown($event)\">\n <ng-template #contentRowMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n @if (contentRowMenu()?.columnId) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"copyContentCell()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n }\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"copyContentRows()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_row' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"contentRowClipboard().length === 0\"\n (click)=\"pasteContentRows('below')\">\n <z-icon zType=\"lucideClipboardPaste\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_paste_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowUp()\"\n (click)=\"moveContentRow('up')\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_up' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowDown()\"\n (click)=\"moveContentRow('down')\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_down' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('above')\">\n <z-icon zType=\"lucideArrowUpToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_above' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('below')\">\n <z-icon zType=\"lucideArrowDownToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"deleteContentRows()\">\n <z-icon zType=\"lucideTrash2\" zSize=\"12\" />\n <span>\n {{\n contentRowDeleteCount() > 1\n ? ('i18n_z_ui_table_delete_selected_rows' | translate)\n : ('i18n_z_ui_table_delete_row' | translate)\n }}\n </span>\n </button>\n </div>\n </ng-template>\n\n <ng-template #cellMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(false)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n @if (cellSelectionOptions().copyWithHeaders) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(true)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell_with_headers' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"contentRowMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-content-row-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"contentRowMenu()?.left ?? -9999\"\n [style.top.px]=\"contentRowMenu()?.top ?? -9999\"\n (zControl)=\"setContentRowMenuControl($event)\"\n (zHide)=\"clearContentRowMenu()\"></div>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"cellMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-cell-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"cellMenu()?.left ?? -9999\"\n [style.top.px]=\"cellMenu()?.top ?? -9999\"\n (zControl)=\"setCellMenuControl($event)\"\n (zHide)=\"clearCellMenu()\"></div>\n\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @if (canUseVirtualColumns()) {\n @for (column of leftLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualLeftSpacerWidth()\" />\n }\n @for (column of virtualCenterColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualRightSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualRightSpacerWidth()\" />\n }\n @for (column of rightLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n } @else {\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-card\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @if (canUseVirtualColumns()) {\n <tr>\n @for (header of leftHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: leftHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @for (header of virtualCenterHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: virtualCenterHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualRightSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n @for (header of rightHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: rightHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n <!-- Column Resizer -->\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n } @else {\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"\n handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\n \"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon\n zType=\"lucideChevronsLeftRight\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n @let virtualFlatItem = virtualGroupedFlatItems()[virtualItem.index];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drop-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drag-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n [z-table-drop-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @let groupHeaderItem = _getVirtualGroupHeader(virtualFlatItem);\n @if (groupHeaderItem) {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"groupHeaderItem.expanded\"\n [attr.data-group-id]=\"groupHeaderItem.id\"\n [attr.data-group-depth]=\"groupHeaderItem.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + groupHeaderItem.depth * 0.75\"\n [attr.aria-expanded]=\"groupHeaderItem.expanded\"\n (click)=\"toggleGroupRow(groupHeaderItem.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"groupHeaderItem.expanded\" />\n <span class=\"z-group-row-label\">\n @if (groupHeaderItem.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ groupHeaderItem.columnLabel }}:</span>\n }\n {{ groupHeaderItem.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ groupHeaderItem.count }}</span>\n }\n @if (groupHeaderItem.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n [zWave]=\"false\"\n z-tooltip\n zSizeIcon=\"14\"\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(groupHeaderItem.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @for (row of groupRows; track row.id) {\n @let virtualGroupDepth = _getVirtualGroupDepth(virtualFlatItem);\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n <tr\n z-table-row-hover\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [style.--z-row-height]=\"dynamicSize() ? 'auto' : virtualRowHeight() + 'px'\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"_isVirtualGroupChild(virtualFlatItem)\"\n [style.--z-group-content-indent]=\"\n virtualGroupDepth > 0 ? 1.75 + (virtualGroupDepth - 1) * 0.75 + 'rem' : null\n \"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : visibleCells\n : zConfig().columns\n : columnSizingInfo()\n : nativeRenderedColumnSizeMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"\n cellPinned === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cellPinned === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [style.--z-cell-range-top]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\n \"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\n \"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground inline-flex size-7 items-center justify-center rounded-md\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfoVirtual.contentEnabled) {\n @let contentConfigVirtual = editInfoVirtual.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfigVirtual\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfigVirtual.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n class=\"z-table-drag-body\"\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row let-isGroupChild=\"isGroupChild\" let-groupDepth=\"groupDepth\">\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n @let rowPinned = row.getIsPinned();\n <tr\n z-table-row-hover\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"row.id\"\n [z-table-drop-item-id]=\"row.id\"\n [z-table-drag-disabled]=\"!isRowDragEnabled()\"\n [z-table-drop-disabled]=\"!isRowDragEnabled()\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-row-id]=\"row.id\"\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : pinnedRowHeights() : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"isGroupChild\"\n [style.--z-group-content-indent]=\"groupDepth > 0 ? 1.75 + (groupDepth - 1) * 0.75 + 'rem' : null\"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n [class.z-pinned-top]=\"rowPinned === 'top'\"\n [class.z-pinned-bottom]=\"rowPinned === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() && rowPinned === 'top' && (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n rowPinned === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @let cellRowPinned = cell.row.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : visibleCells : zConfig().columns : columnSizingInfo() : nativeRenderedColumnSizeMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"cellPinned === 'right' && cell.column.getIsFirstColumn('right')\"\n [class.z-sticky-right-last]=\"cellPinned === 'right' && cell.column.getIsLastColumn('right')\"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style.--z-cell-range-top]=\"(selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"(selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.cursor-not-allowed]=\"!isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (\n (cell.column.id === 'actionRowPin' || cell.column.id === 'actions') &&\n cell.column.id !== actionColumnInfo()?.columnId\n ) {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfo.contentEnabled) {\n @let contentConfig = editInfo.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfig\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfig.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"(cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length) {\n @let resolvedSubTableConfig = row | zTableSubTableConfig: zConfig();\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"renderedColumnCount()\" class=\"p-0\">\n @if (zConfig().expandedRowTemplate) {\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n } @else if (resolvedSubTableConfig) {\n <div class=\"z-sub-table-container\">\n <z-table [zConfig]=\"$any(resolvedSubTableConfig)\" zVariant=\"borderless\" />\n </div>\n }\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (item of groupedCenterRowItems(); track item.type === 'group' ? item.id : item.row.id) {\n @if (item.type === 'group') {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"item.expanded\"\n [attr.data-group-id]=\"item.id\"\n [attr.data-group-depth]=\"item.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + item.depth * 0.75\"\n [attr.aria-expanded]=\"item.expanded\"\n (click)=\"toggleGroupRow(item.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"item.expanded\" />\n <span class=\"z-group-row-label\">\n @if (item.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ item.columnLabel }}:</span>\n }\n {{ item.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ item.count }}</span>\n }\n @if (item.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n zSizeIcon=\"14\"\n [zWave]=\"false\"\n z-tooltip\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(item.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @if (item.type === 'row') {\n <ng-container\n *ngTemplateOutlet=\"\n rowTemplate;\n context: {\n $implicit: item.row,\n isGroupChild: item.isGroupChild,\n groupDepth: item.groupDepth,\n }\n \" />\n }\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n footer.column.id === firstVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n footer.column.getIsPinned() !== false ||\n virtualCenterColumnVisibilityMap()[footer.column.id];\n @if (rowSpan && shouldRender && isVirtualColumnVisible) {\n <th\n [attr.data-column-id]=\"footer.column.id\"\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n class=\"z-template-content\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [class]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n footer.column.id === lastVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (paginationTotal() | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"paginationTotal()\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Floating Bulk Action Bar -->\n<div class=\"z-bulk-action-bar-origin\" cdkOverlayOrigin #bulkBarOrigin=\"cdkOverlayOrigin\"></div>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"bulkBarOrigin\"\n [cdkConnectedOverlayOpen]=\"bulkBarMounted()\"\n [cdkConnectedOverlayPositions]=\"bulkBarPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n cdkConnectedOverlayPanelClass=\"z-bulk-action-bar-overlay\">\n @if (bulkBarConfig(); as config) {\n <div class=\"z-bulk-action-bar-inner\" [class.z-leaving]=\"bulkBarClosing()\">\n <div class=\"z-bulk-action-bar-count\">\n {{ bulkBarContext()?.selectedRowIds?.length ?? 0 | zFormatNum }}\n {{ config.selectedLabel ?? ('i18n_z_ui_table_selected' | translate) }}\n </div>\n\n <div class=\"z-bulk-action-bar-divider\"></div>\n\n @for (item of bulkBarItems(); track item.action.key) {\n <button\n type=\"button\"\n z-button\n zSize=\"sm\"\n [zType]=\"item.buttonType\"\n [zDisabled]=\"item.disabled\"\n class=\"z-bulk-action-bar-button\"\n [disabled]=\"item.disabled\"\n (click)=\"onBulkActionClick(item.action)\">\n @if (item.action.icon) {\n <z-icon [zType]=\"item.action.icon\" [zSize]=\"item.action.iconSize ?? '14'\" />\n }\n @if (item.action.label) {\n <span>{{ item.action.label | translate }}</span>\n }\n </button>\n }\n </div>\n }\n</ng-template>\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n <!-- T\u1EA1m t\u1EAFt \u0111i\u1EC1u ki\u1EC7n: @if (zConfig().enableSettings) -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n [cdkDropListAutoScrollDisabled]=\"true\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select' && columnId !== 'rowDrag') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragDisabled]=\"isGroupedColumn\"\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragPreviewContainer=\"global\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-default items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\"\n [class.cursor-pointer]=\"!isGroupedColumn\"\n [class.cursor-default]=\"isGroupedColumn\"\n [class.opacity-40]=\"isGroupedColumn\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[0.625rem] font-medium\">\n {{\n isPinned === 'left' ? ('i18n_z_ui_table_left' | translate) : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n [disabled]=\"isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow-x:visible;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border);--z-cell-selection-background: color-mix(in srgb, var(--primary) 12%, transparent)}.z-table-container.z-column-resizing table,.z-table-toolbar{min-width:0}.z-bulk-action-bar-origin{position:fixed;left:50%;bottom:1rem;width:1px;height:1px;pointer-events:none}:host ::ng-deep .z-bulk-action-bar-overlay{max-width:calc(100vw - 2rem);margin-bottom:1rem;transform-origin:center bottom}.z-bulk-action-bar-inner{border:thin solid color-mix(in srgb,var(--border) 88%,transparent);border-radius:.4rem;background:color-mix(in srgb,var(--card) 96%,var(--background));color:var(--foreground);box-shadow:0 1rem 2.5rem color-mix(in srgb,var(--foreground) 20%,transparent),0 .25rem .75rem color-mix(in srgb,var(--foreground) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--background) 65%,transparent) inset;display:flex;align-items:center;gap:.5rem;width:max-content;min-height:2.25rem;max-width:calc(100vw - 2rem);padding:.5rem;transform-origin:center bottom;animation:z-bulk-action-bar-enter .18s cubic-bezier(.16,1,.3,1);will-change:opacity,transform}.z-bulk-action-bar-inner.z-leaving{pointer-events:none;animation:z-bulk-action-bar-exit .14s ease-in forwards}.z-bulk-action-bar-count{padding:0 .25rem;white-space:nowrap;font-size:.8125rem;font-weight:500;color:var(--foreground)}.z-bulk-action-bar-divider{width:1px;height:1.5rem;flex:none;background:color-mix(in srgb,var(--border) 88%,transparent)}.z-bulk-action-bar-button{flex:none}@keyframes z-bulk-action-bar-enter{0%{opacity:0;transform:translateY(.875rem)}to{opacity:1;transform:translateY(0)}}@keyframes z-bulk-action-bar-exit{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(.875rem)}}@media(prefers-reduced-motion:reduce){.z-bulk-action-bar-inner{animation:none}}:host ::ng-deep .z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;-webkit-user-select:text;user-select:text}:host ::ng-deep .z-table-cell-text>*,:host ::ng-deep .z-table-cell-text *{display:inline-block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.z-template-content{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.z-template-content>*{min-width:0;max-width:100%}.z-template-content>[class*=flex]{min-width:0;max-width:100%}.z-template-content>[class*=flex]>*{min-width:0;flex-shrink:1}.z-template-content>[class*=grid]{min-width:0;max-width:100%}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%}.z-sub-table-container{padding:.75rem 1rem .75rem 3rem;background-color:var(--background)}.z-table-content-trigger{display:block;width:100%;min-width:0;min-height:1.5rem;padding:0;border:0;border-radius:0;background:transparent;color:inherit;font:inherit;line-height:1.5rem;text-align:inherit;outline:0;cursor:text}.z-table-content-trigger:hover{color:var(--primary)}.z-content-edit-active,td:has(>.z-table-content-trigger:focus-visible){position:relative;z-index:2}.z-content-edit-active:after,td:has(>.z-table-content-trigger:focus-visible):after{position:absolute;z-index:3;inset:0;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-cell-selectable{-webkit-user-select:none;user-select:none}td.z-cell-range-selected{position:relative;isolation:isolate}td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);content:\"\";pointer-events:none}td.z-cell-range-selected>*{position:relative;z-index:1}td.z-cell-range-top:before{border-top:1px solid var(--primary)}td.z-cell-range-right:before{border-right:1px solid var(--primary)}td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}td.z-cell-range-left:before{border-left:1px solid var(--primary)}td.z-cell-active{position:relative;z-index:3;background-color:var(--z-cell-selection-background)!important}td.z-cell-active:after{position:absolute;z-index:4;inset:-1px;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-content-edit-active{-webkit-user-select:text;user-select:text}td.z-cell-active.z-row-hover,td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}.z-tfoot-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-column-menu-item{line-height:1.25rem}.z-column-menu-item:focus-visible,.z-column-menu-item.z-popover-open{background-color:var(--muted);outline:none}.z-table-drag-preview-row{border:0;border-radius:.125rem;background-color:var(--background);box-shadow:0 8px 20px #0000001f,inset 0 0 0 1px color-mix(in srgb,var(--primary) 45%,var(--border))}.z-table-drag-preview-row table,.z-table-drag-preview-row tbody,.z-table-drag-preview-row tr{height:100%}.z-table-drag-preview-row td{vertical-align:middle!important;background-color:var(--background)}[data-z-table-drag-handle]{touch-action:none;-webkit-user-select:none;user-select:none}.z-table-drag-placeholder{width:100%;min-height:2.625rem;box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background));pointer-events:none}.z-table-drag-placeholder-row>td{padding:0!important;border:0!important;background:transparent!important}.z-table-drag-placeholder-row .z-table-drag-placeholder{border-radius:0;background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.z-table-drag-placeholder-virtual{position:absolute;top:0;left:0;z-index:20;border-radius:0}.z-tbody-wrapper tr.z-group-row td{background:var(--card);border-top:none;border-bottom:1px solid var(--border)}.z-virtual-row tr.z-group-row td{background:var(--card);border-top:none;border-bottom:none}.z-group-row-sticky-wrapper{display:inline-flex;align-items:center;translate:var(--z-scroll-x, 0px) 0}.z-table-container:not(.z-hide-horizontal-border) .z-virtual-row:after{content:\"\";position:absolute;right:0;bottom:0;left:0;z-index:3;height:1px;background:var(--border);pointer-events:none}.z-tbody-wrapper tr.z-group-child-row td.z-at-left-edge,.z-virtual-row tr.z-group-child-row td.z-at-left-edge{border-left:2px solid var(--primary)!important;padding-left:var(--z-group-content-indent, .75rem)}.z-group-row-trigger{display:inline-flex;min-height:2rem;max-width:100%;align-items:center;gap:.5rem;padding:.25rem .5rem .25rem .75rem;color:var(--primary);font-size:.8125rem;font-weight:600;vertical-align:middle;cursor:pointer}.z-group-row-remove{margin-left:.125rem;color:var(--muted-foreground);vertical-align:middle}.z-group-row-remove:hover,.z-group-row-remove:focus-visible{color:var(--destructive);background:color-mix(in srgb,var(--destructive) 10%,transparent);outline:none}.z-group-row-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.z-group-row-column-label{color:color-mix(in srgb,var(--primary) 65%,var(--foreground));font-weight:500;margin-right:.25rem}.z-group-row-count{display:inline-flex;min-width:1.25rem;height:1.25rem;align-items:center;justify-content:center;padding:0 .375rem;border-radius:999px;background:var(--primary);color:var(--primary-foreground);font-size:.6875rem;font-weight:600}.z-group-row-loading{display:inline-flex;align-items:center;margin-left:.25rem}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{position:absolute!important;z-index:0!important;box-sizing:border-box!important;top:var(--z-cell-range-top, 0)!important;right:var(--z-cell-range-right, 0)!important;bottom:var(--z-cell-range-bottom, 0)!important;left:var(--z-cell-range-left, 0)!important;width:auto!important;background-color:var(--z-cell-selection-background)!important;box-shadow:none!important;content:\"\"!important;pointer-events:none!important;opacity:1!important}:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:after{display:none!important}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-top:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-top:before{border-top:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-right:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-right:before{border-right:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-bottom:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-left:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{background-color:transparent!important}:host td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);box-shadow:none;content:\"\";pointer-events:none;opacity:1}:host td.z-cell-range-top:before{border-top:1px solid var(--primary)}:host td.z-cell-range-right:before{border-right:1px solid var(--primary)}:host td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host td.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) td.z-row-hover{background-color:var(--background)!important}:host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow:hidden;border-radius:.3125rem;border:thin solid var(--border);background-color:var(--card)}.z-table-container:focus{outline:none}.z-table-container.z-table-borderless{border:none;border-radius:0;box-shadow:none!important;background-color:transparent}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:.875rem}.z-table-toolbar{min-width:0}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-thead-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:.5rem;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;width:100%;max-width:100%;min-width:0;min-height:6.25rem;display:flex;flex-direction:column;overflow:hidden}.z-tbody-wrapper{flex:1;display:flex;flex-direction:column;min-height:0;width:100%}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%;transition:opacity .2s ease-in-out}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:6.25rem;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:.5rem 12px;height:var(--z-row-height, 2.375rem);vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom-color:transparent}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:.75rem}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:.5rem 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{border-left:.125rem solid var(--primary)!important;padding-left:.75rem!important}tbody tr.z-context-menu-active,tbody tr.z-context-menu-active td{background-color:var(--muted)!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td{background-color:var(--muted)!important}tr.z-expanded-row>td{border-left:.125rem solid var(--primary);background-color:var(--background)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:.5rem;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:.1875rem;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1.875rem;width:1.875rem;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:.5rem 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:.75rem;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:.125rem 4px;border-radius:.25rem;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:.25rem;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover,th .z-header-text-wrapper.z-has-options:focus-visible,th .z-header-text-wrapper.z-has-options.z-popover-open{background-color:color-mix(in srgb,var(--foreground) 8%,transparent);outline:none}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-sizing:border-box;border:1px solid var(--primary);border-radius:.375rem;background-color:var(--card);box-shadow:0 5px 20px #0003;overflow:hidden;pointer-events:none;z-index:10000!important}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background))}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-table-drag-preview{box-sizing:border-box;border-radius:.375rem;background-color:var(--card);border:1px solid var(--primary);box-shadow:0 10px 24px #00000029;overflow:hidden;z-index:10000!important;pointer-events:none}.z-table-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.z-table-drag-source{display:none!important}.z-virtual-row.z-table-drag-source{display:block!important;pointer-events:none;outline:1px solid var(--border);outline-offset:-1px}.z-virtual-row.z-table-drag-source td{color:color-mix(in srgb,var(--foreground) 45%,transparent);border-color:var(--border)!important;background-color:var(--card)!important}.z-virtual-row.z-table-drag-source td>*{opacity:.45}.z-column-drop-list{min-height:3.125rem;overscroll-behavior:contain}.z-column-drop-list.cdk-drop-list-dragging{overflow:clip}:host td.z-cell-active,:host td.z-cell-active.z-row-hover{background-color:var(--z-cell-selection-background)!important}:host td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected,:host-context(.z-table-pointer-dragging) td.z-cell-range-selected.z-row-hover,:host-context(.z-table-pointer-dragging) td.z-cell-active,:host-context(.z-table-pointer-dragging) td.z-cell-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected:before{background-color:transparent}:host .z-tbody-wrapper tbody tr.z-group-child-row:has(+tr.z-group-row)>td.z-at-bottom{border-bottom-color:var(--border)}:host .z-virtual-row:has(tr.z-group-child-row):has(+.z-virtual-row tr.z-group-row) tr.z-group-child-row>td.z-at-bottom{border-bottom-color:var(--border)}.z-virtual-row td.z-virtual-column-spacer{height:auto}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 .125rem .125rem 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ZTableComponent, selector: "z-table", inputs: ["zClass", "zConfig", "zLoading", "zKey", "zVariant"], outputs: ["zChange", "zControl"], exportAs: ["zTable"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: 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: 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: "directive", type: ZTableDraggableDirective, selector: "[z-table-draggable]", inputs: ["z-table-draggable", "z-table-drag-table-id", "z-table-drag-item-id", "z-table-drag-disabled", "z-table-drag-handle"] }, { kind: "directive", type: ZTableDropTargetDirective, selector: "[z-table-drop-target]", inputs: ["z-table-drop-target", "z-table-drop-table-id", "z-table-drop-item-id", "z-table-drop-disabled"], outputs: ["zTableDropped"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zOnBlur", "zOnFocus", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "zType", "zIcon", "zIconSize", "zSize", "zMessage", "zDescription"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zAnimatedType", "zAnimate", "zAnimationTrigger", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zAlign", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest", "zColorConfig"], outputs: ["zOnSearch", "zOnChange", "zOnBlur", "zOnFocus", "zOnKeydown", "zOnEnter", "zOnColorCollapse", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zBodyClass", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zDisableShadow", "zHeaderBorder", "zFooterBorder", "zHideFooter", "zHideHeader", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zAfterClose", "zScrollbar", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zOnPageChange", "zPageIndexChange", "zPageSizeChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "component", type: ZTableEditCellComponent, selector: "z-table-edit-cell", inputs: ["zRow", "zRowId", "zRowIndex", "zColumnId", "zValue", "zEditConfig", "zRowUpdate"], outputs: ["zChange"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zPopoverTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zTriggerRef", "zManualClose", "zOutsideClickClose", "zScrollClose", "zSticky", "zShowArrow", "zKeyboardSafe"], outputs: ["zShow", "zHide", "zHideStart", "zControl", "zPositionChange", "zOutsideClick"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zAnimatedTypeIcon", "zAnimateIcon", "zAnimationTriggerIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTooltipPosition", "zTrigger", "zTooltipTrigger", "zTooltipType", "zTooltipSize", "zClass", "zTooltipClass", "zShowDelay", "zTooltipShowDelay", "zHideDelay", "zTooltipHideDelay", "zArrow", "zTooltipArrow", "zDisabled", "zTooltipDisabled", "zOffset", "zTooltipOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableResizeDirective, selector: "[z-table-resize],[zTableResize]", inputs: ["zTableResize"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip", "zTriggerElement"] }, { kind: "component", type: ZTableActionsComponent, selector: "z-table-actions", inputs: ["zConfig", "zRow", "zRowId", "zDropdownButtonSize"], outputs: ["zActionClick"] }, { kind: "component", type: ZTableContentEditorComponent, selector: "z-table-content-editor", inputs: ["zValue", "zConfig"], outputs: ["zCommit", "zCancel"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellBottomPipe, name: "zTableCellBottom" }, { kind: "pipe", type: ZTableCellClickablePipe, name: "zTableCellClickable" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellEditPipe, name: "zTableCellEdit" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableCellPinPipe, name: "zTableCellPin" }, { kind: "pipe", type: ZTableCellVisiblePipe, name: "zTableCellVisible" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableRowConfigPipe, name: "zTableRowConfig" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableSubTableConfigPipe, name: "zTableSubTableConfig" }, { kind: "pipe", type: ZTableCellRenderPipe, name: "zTableCellRender" }, { kind: "pipe", type: ZFormatNumPipe, name: "zFormatNum" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9548
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { zClass: { classPropertyName: "zClass", publicName: "zClass", isSignal: true, isRequired: false, transformFunction: null }, zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null }, zVariant: { classPropertyName: "zVariant", publicName: "zVariant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative py-1" }, providers: [TranslatePipe, ZTableDragService], viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tableContainer", first: true, predicate: ["tableContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }, { propertyName: "virtualRowElements", predicate: ["virtualRow"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n #tableContainer\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.width]=\"zConfig().width\"\n [style.height]=\"zConfig().height\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\"\n [attr.tabindex]=\"canUseContentRowInsert() || canUseCellSelection() ? 0 : null\"\n (keydown)=\"onContentTableKeydown($event)\">\n <ng-template #contentRowMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n @if (contentRowMenu()?.columnId) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"copyContentCell()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n }\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"copyContentRows()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_row' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"contentRowClipboard().length === 0\"\n (click)=\"pasteContentRows('below')\">\n <z-icon zType=\"lucideClipboardPaste\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_paste_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowUp()\"\n (click)=\"moveContentRow('up')\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_up' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowDown()\"\n (click)=\"moveContentRow('down')\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_down' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('above')\">\n <z-icon zType=\"lucideArrowUpToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_above' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('below')\">\n <z-icon zType=\"lucideArrowDownToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"deleteContentRows()\">\n <z-icon zType=\"lucideTrash2\" zSize=\"12\" />\n <span>\n {{\n contentRowDeleteCount() > 1\n ? ('i18n_z_ui_table_delete_selected_rows' | translate)\n : ('i18n_z_ui_table_delete_row' | translate)\n }}\n </span>\n </button>\n </div>\n </ng-template>\n\n <ng-template #cellMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(false)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n @if (cellSelectionOptions().copyWithHeaders) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(true)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell_with_headers' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"contentRowMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-content-row-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"contentRowMenu()?.left ?? -9999\"\n [style.top.px]=\"contentRowMenu()?.top ?? -9999\"\n (zControl)=\"setContentRowMenuControl($event)\"\n (zHide)=\"clearContentRowMenu()\"></div>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"cellMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-cell-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"cellMenu()?.left ?? -9999\"\n [style.top.px]=\"cellMenu()?.top ?? -9999\"\n (zControl)=\"setCellMenuControl($event)\"\n (zHide)=\"clearCellMenu()\"></div>\n\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @if (canUseVirtualColumns()) {\n @for (column of leftLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualLeftSpacerWidth()\" />\n }\n @for (column of virtualCenterColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualRightSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualRightSpacerWidth()\" />\n }\n @for (column of rightLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n } @else {\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-card\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @if (canUseVirtualColumns()) {\n <tr>\n @for (header of leftHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: leftHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @for (header of virtualCenterHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: virtualCenterHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualRightSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n @for (header of rightHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: rightHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n <!-- Column Resizer -->\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n } @else {\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"\n handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\n \"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon\n zType=\"lucideChevronsLeftRight\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n @let virtualFlatItem = virtualGroupedFlatItems()[virtualItem.index];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drop-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drag-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n [z-table-drop-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @let groupHeaderItem = _getVirtualGroupHeader(virtualFlatItem);\n @if (groupHeaderItem) {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"groupHeaderItem.expanded\"\n [attr.data-group-id]=\"groupHeaderItem.id\"\n [attr.data-group-depth]=\"groupHeaderItem.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + groupHeaderItem.depth * 0.75\"\n [attr.aria-expanded]=\"groupHeaderItem.expanded\"\n (click)=\"toggleGroupRow(groupHeaderItem.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"groupHeaderItem.expanded\" />\n <span class=\"z-group-row-label\">\n @if (groupHeaderItem.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ groupHeaderItem.columnLabel }}:</span>\n }\n {{ groupHeaderItem.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ groupHeaderItem.count }}</span>\n }\n @if (groupHeaderItem.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n [zWave]=\"false\"\n z-tooltip\n zSizeIcon=\"14\"\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(groupHeaderItem.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @for (row of groupRows; track row.id) {\n @let virtualGroupDepth = _getVirtualGroupDepth(virtualFlatItem);\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n <tr\n z-table-row-hover\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [style.--z-row-height]=\"dynamicSize() ? 'auto' : virtualRowHeight() + 'px'\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"_isVirtualGroupChild(virtualFlatItem)\"\n [style.--z-group-content-indent]=\"\n virtualGroupDepth > 0 ? 1.75 + (virtualGroupDepth - 1) * 0.75 + 'rem' : null\n \"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : visibleCells\n : zConfig().columns\n : columnSizingInfo()\n : colDomWidthMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"\n cellPinned === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cellPinned === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [style.--z-cell-range-top]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\n \"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\n \"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground inline-flex size-7 items-center justify-center rounded-md\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfoVirtual.contentEnabled) {\n @let contentConfigVirtual = editInfoVirtual.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfigVirtual\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfigVirtual.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n class=\"z-table-drag-body\"\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row let-isGroupChild=\"isGroupChild\" let-groupDepth=\"groupDepth\">\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n @let rowPinned = row.getIsPinned();\n <tr\n z-table-row-hover\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"row.id\"\n [z-table-drop-item-id]=\"row.id\"\n [z-table-drag-disabled]=\"!isRowDragEnabled()\"\n [z-table-drop-disabled]=\"!isRowDragEnabled()\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-row-id]=\"row.id\"\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : pinnedRowHeights() : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"isGroupChild\"\n [style.--z-group-content-indent]=\"groupDepth > 0 ? 1.75 + (groupDepth - 1) * 0.75 + 'rem' : null\"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n [class.z-pinned-top]=\"rowPinned === 'top'\"\n [class.z-pinned-bottom]=\"rowPinned === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() && rowPinned === 'top' && (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n rowPinned === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @let cellRowPinned = cell.row.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : visibleCells : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"cellPinned === 'right' && cell.column.getIsFirstColumn('right')\"\n [class.z-sticky-right-last]=\"cellPinned === 'right' && cell.column.getIsLastColumn('right')\"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style.--z-cell-range-top]=\"(selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"(selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.cursor-not-allowed]=\"!isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (\n (cell.column.id === 'actionRowPin' || cell.column.id === 'actions') &&\n cell.column.id !== actionColumnInfo()?.columnId\n ) {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfo.contentEnabled) {\n @let contentConfig = editInfo.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfig\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfig.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"(cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length) {\n @let resolvedSubTableConfig = row | zTableSubTableConfig: zConfig();\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"renderedColumnCount()\" class=\"p-0\">\n @if (zConfig().expandedRowTemplate) {\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n } @else if (resolvedSubTableConfig) {\n <div class=\"z-sub-table-container\">\n <z-table [zConfig]=\"$any(resolvedSubTableConfig)\" zVariant=\"borderless\" />\n </div>\n }\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (item of groupedCenterRowItems(); track item.type === 'group' ? item.id : item.row.id) {\n @if (item.type === 'group') {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"item.expanded\"\n [attr.data-group-id]=\"item.id\"\n [attr.data-group-depth]=\"item.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + item.depth * 0.75\"\n [attr.aria-expanded]=\"item.expanded\"\n (click)=\"toggleGroupRow(item.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"item.expanded\" />\n <span class=\"z-group-row-label\">\n @if (item.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ item.columnLabel }}:</span>\n }\n {{ item.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ item.count }}</span>\n }\n @if (item.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n zSizeIcon=\"14\"\n [zWave]=\"false\"\n z-tooltip\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(item.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @if (item.type === 'row') {\n <ng-container\n *ngTemplateOutlet=\"\n rowTemplate;\n context: {\n $implicit: item.row,\n isGroupChild: item.isGroupChild,\n groupDepth: item.groupDepth,\n }\n \" />\n }\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n footer.column.id === firstVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n footer.column.getIsPinned() !== false ||\n virtualCenterColumnVisibilityMap()[footer.column.id];\n @if (rowSpan && shouldRender && isVirtualColumnVisible) {\n <th\n [attr.data-column-id]=\"footer.column.id\"\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n class=\"z-template-content\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [class]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n footer.column.id === lastVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (paginationTotal() | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"paginationTotal()\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Floating Bulk Action Bar -->\n<div class=\"z-bulk-action-bar-origin\" cdkOverlayOrigin #bulkBarOrigin=\"cdkOverlayOrigin\"></div>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"bulkBarOrigin\"\n [cdkConnectedOverlayOpen]=\"bulkBarMounted()\"\n [cdkConnectedOverlayPositions]=\"bulkBarPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n cdkConnectedOverlayPanelClass=\"z-bulk-action-bar-overlay\">\n @if (bulkBarConfig(); as config) {\n <div class=\"z-bulk-action-bar-inner\" [class.z-leaving]=\"bulkBarClosing()\">\n <div class=\"z-bulk-action-bar-count\">\n {{ bulkBarContext()?.selectedRowIds?.length ?? 0 | zFormatNum }}\n {{ config.selectedLabel ?? ('i18n_z_ui_table_selected' | translate) }}\n </div>\n\n <div class=\"z-bulk-action-bar-divider\"></div>\n\n @for (item of bulkBarItems(); track item.action.key) {\n <button\n type=\"button\"\n z-button\n zSize=\"sm\"\n [zType]=\"item.buttonType\"\n [zDisabled]=\"item.disabled\"\n class=\"z-bulk-action-bar-button\"\n [disabled]=\"item.disabled\"\n (click)=\"onBulkActionClick(item.action)\">\n @if (item.action.icon) {\n <z-icon [zType]=\"item.action.icon\" [zSize]=\"item.action.iconSize ?? '14'\" />\n }\n @if (item.action.label) {\n <span>{{ item.action.label | translate }}</span>\n }\n </button>\n }\n </div>\n }\n</ng-template>\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n <!-- T\u1EA1m t\u1EAFt \u0111i\u1EC1u ki\u1EC7n: @if (zConfig().enableSettings) -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n [cdkDropListAutoScrollDisabled]=\"true\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select' && columnId !== 'rowDrag') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragDisabled]=\"isGroupedColumn\"\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragPreviewContainer=\"global\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-default items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\"\n [class.cursor-pointer]=\"!isGroupedColumn\"\n [class.cursor-default]=\"isGroupedColumn\"\n [class.opacity-40]=\"isGroupedColumn\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[0.625rem] font-medium\">\n {{\n isPinned === 'left' ? ('i18n_z_ui_table_left' | translate) : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n [disabled]=\"isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow-x:visible;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border);--z-cell-selection-background: color-mix(in srgb, var(--primary) 12%, transparent)}.z-table-container.z-column-resizing table,.z-table-toolbar{min-width:0}.z-bulk-action-bar-origin{position:fixed;left:50%;bottom:1rem;width:1px;height:1px;pointer-events:none}:host ::ng-deep .z-bulk-action-bar-overlay{max-width:calc(100vw - 2rem);margin-bottom:1rem;transform-origin:center bottom}.z-bulk-action-bar-inner{border:thin solid color-mix(in srgb,var(--border) 88%,transparent);border-radius:.4rem;background:color-mix(in srgb,var(--card) 96%,var(--background));color:var(--foreground);box-shadow:0 1rem 2.5rem color-mix(in srgb,var(--foreground) 20%,transparent),0 .25rem .75rem color-mix(in srgb,var(--foreground) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--background) 65%,transparent) inset;display:flex;align-items:center;gap:.5rem;width:max-content;min-height:2.25rem;max-width:calc(100vw - 2rem);padding:.5rem;transform-origin:center bottom;animation:z-bulk-action-bar-enter .18s cubic-bezier(.16,1,.3,1);will-change:opacity,transform}.z-bulk-action-bar-inner.z-leaving{pointer-events:none;animation:z-bulk-action-bar-exit .14s ease-in forwards}.z-bulk-action-bar-count{padding:0 .25rem;white-space:nowrap;font-size:.8125rem;font-weight:500;color:var(--foreground)}.z-bulk-action-bar-divider{width:1px;height:1.5rem;flex:none;background:color-mix(in srgb,var(--border) 88%,transparent)}.z-bulk-action-bar-button{flex:none}@keyframes z-bulk-action-bar-enter{0%{opacity:0;transform:translateY(.875rem)}to{opacity:1;transform:translateY(0)}}@keyframes z-bulk-action-bar-exit{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(.875rem)}}@media(prefers-reduced-motion:reduce){.z-bulk-action-bar-inner{animation:none}}:host ::ng-deep .z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;-webkit-user-select:text;user-select:text}:host ::ng-deep .z-table-cell-text>*,:host ::ng-deep .z-table-cell-text *{display:inline-block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.z-template-content{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.z-template-content>*{min-width:0;max-width:100%}.z-template-content>[class*=flex]{min-width:0;max-width:100%}.z-template-content>[class*=flex]>*{min-width:0;flex-shrink:1}.z-template-content>[class*=grid]{min-width:0;max-width:100%}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%}.z-sub-table-container{padding:.75rem 1rem .75rem 3rem;background-color:var(--background)}.z-table-content-trigger{display:block;width:100%;min-width:0;min-height:1.5rem;padding:0;border:0;border-radius:0;background:transparent;color:inherit;font:inherit;line-height:1.5rem;text-align:inherit;outline:0;cursor:text}.z-table-content-trigger:hover{color:var(--primary)}.z-content-edit-active,td:has(>.z-table-content-trigger:focus-visible){position:relative;z-index:2}.z-content-edit-active:after,td:has(>.z-table-content-trigger:focus-visible):after{position:absolute;z-index:3;inset:0;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-cell-selectable{-webkit-user-select:none;user-select:none}td.z-cell-range-selected{position:relative;isolation:isolate}td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);content:\"\";pointer-events:none}td.z-cell-range-selected>*{position:relative;z-index:1}td.z-cell-range-top:before{border-top:1px solid var(--primary)}td.z-cell-range-right:before{border-right:1px solid var(--primary)}td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}td.z-cell-range-left:before{border-left:1px solid var(--primary)}td.z-cell-active{position:relative;z-index:3;background-color:var(--z-cell-selection-background)!important}td.z-cell-active:after{position:absolute;z-index:4;inset:-1px;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-content-edit-active{-webkit-user-select:text;user-select:text}td.z-cell-active.z-row-hover,td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}.z-tfoot-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-column-menu-item{line-height:1.25rem}.z-column-menu-item:focus-visible,.z-column-menu-item.z-popover-open{background-color:var(--muted);outline:none}.z-table-drag-preview-row{border:0;border-radius:.125rem;background-color:var(--background);box-shadow:0 8px 20px #0000001f,inset 0 0 0 1px color-mix(in srgb,var(--primary) 45%,var(--border))}.z-table-drag-preview-row table,.z-table-drag-preview-row tbody,.z-table-drag-preview-row tr{height:100%}.z-table-drag-preview-row td{vertical-align:middle!important;background-color:var(--background)}[data-z-table-drag-handle]{touch-action:none;-webkit-user-select:none;user-select:none}.z-table-drag-placeholder{width:100%;min-height:2.625rem;box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background));pointer-events:none}.z-table-drag-placeholder-row>td{padding:0!important;border:0!important;background:transparent!important}.z-table-drag-placeholder-row .z-table-drag-placeholder{border-radius:0;background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.z-table-drag-placeholder-virtual{position:absolute;top:0;left:0;z-index:20;border-radius:0}.z-tbody-wrapper tr.z-group-row td{background:var(--card);border-top:none;border-bottom:1px solid var(--border)}.z-virtual-row tr.z-group-row td{background:var(--card);border-top:none;border-bottom:none}.z-group-row-sticky-wrapper{display:inline-flex;align-items:center;translate:var(--z-scroll-x, 0px) 0}.z-table-container:not(.z-hide-horizontal-border) .z-virtual-row:after{content:\"\";position:absolute;right:0;bottom:0;left:0;z-index:3;height:1px;background:var(--border);pointer-events:none}.z-tbody-wrapper tr.z-group-child-row td.z-at-left-edge,.z-virtual-row tr.z-group-child-row td.z-at-left-edge{border-left:2px solid var(--primary)!important;padding-left:var(--z-group-content-indent, .75rem)}.z-group-row-trigger{display:inline-flex;min-height:2rem;max-width:100%;align-items:center;gap:.5rem;padding:.25rem .5rem .25rem .75rem;color:var(--primary);font-size:.8125rem;font-weight:600;vertical-align:middle;cursor:pointer}.z-group-row-remove{margin-left:.125rem;color:var(--muted-foreground);vertical-align:middle}.z-group-row-remove:hover,.z-group-row-remove:focus-visible{color:var(--destructive);background:color-mix(in srgb,var(--destructive) 10%,transparent);outline:none}.z-group-row-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.z-group-row-column-label{color:color-mix(in srgb,var(--primary) 65%,var(--foreground));font-weight:500;margin-right:.25rem}.z-group-row-count{display:inline-flex;min-width:1.25rem;height:1.25rem;align-items:center;justify-content:center;padding:0 .375rem;border-radius:999px;background:var(--primary);color:var(--primary-foreground);font-size:.6875rem;font-weight:600}.z-group-row-loading{display:inline-flex;align-items:center;margin-left:.25rem}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{position:absolute!important;z-index:0!important;box-sizing:border-box!important;top:var(--z-cell-range-top, 0)!important;right:var(--z-cell-range-right, 0)!important;bottom:var(--z-cell-range-bottom, 0)!important;left:var(--z-cell-range-left, 0)!important;width:auto!important;background-color:var(--z-cell-selection-background)!important;box-shadow:none!important;content:\"\"!important;pointer-events:none!important;opacity:1!important}:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:after{display:none!important}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-top:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-top:before{border-top:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-right:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-right:before{border-right:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-bottom:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-left:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{background-color:transparent!important}:host td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);box-shadow:none;content:\"\";pointer-events:none;opacity:1}:host td.z-cell-range-top:before{border-top:1px solid var(--primary)}:host td.z-cell-range-right:before{border-right:1px solid var(--primary)}:host td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host td.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) td.z-row-hover{background-color:var(--background)!important}:host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow:hidden;border-radius:.3125rem;border:thin solid var(--border);background-color:var(--card)}.z-table-container:focus{outline:none}.z-table-container.z-table-borderless{border:none;border-radius:0;box-shadow:none!important;background-color:transparent}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:.875rem}.z-table-toolbar{min-width:0}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-thead-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:.5rem;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;width:100%;max-width:100%;min-width:0;min-height:6.25rem;display:flex;flex-direction:column;overflow:hidden}.z-tbody-wrapper{flex:1;display:flex;flex-direction:column;min-height:0;width:100%}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%;transition:opacity .2s ease-in-out}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:6.25rem;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:.5rem 12px;height:var(--z-row-height, 2.375rem);vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom-color:transparent}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:.75rem}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:.5rem 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{border-left:.125rem solid var(--primary)!important;padding-left:.75rem!important}tbody tr.z-context-menu-active,tbody tr.z-context-menu-active td{background-color:var(--muted)!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td{background-color:var(--muted)!important}tr.z-expanded-row>td{border-left:.125rem solid var(--primary);background-color:var(--background)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:.5rem;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:.1875rem;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1.875rem;width:1.875rem;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:.5rem 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:.75rem;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:.125rem 4px;border-radius:.25rem;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:.25rem;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover,th .z-header-text-wrapper.z-has-options:focus-visible,th .z-header-text-wrapper.z-has-options.z-popover-open{background-color:color-mix(in srgb,var(--foreground) 8%,transparent);outline:none}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-sizing:border-box;border:1px solid var(--primary);border-radius:.375rem;background-color:var(--card);box-shadow:0 5px 20px #0003;overflow:hidden;pointer-events:none;z-index:10000!important}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background))}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-table-drag-preview{box-sizing:border-box;border-radius:.375rem;background-color:var(--card);border:1px solid var(--primary);box-shadow:0 10px 24px #00000029;overflow:hidden;z-index:10000!important;pointer-events:none}.z-table-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.z-table-drag-source{display:none!important}.z-virtual-row.z-table-drag-source{display:block!important;pointer-events:none;outline:1px solid var(--border);outline-offset:-1px}.z-virtual-row.z-table-drag-source td{color:color-mix(in srgb,var(--foreground) 45%,transparent);border-color:var(--border)!important;background-color:var(--card)!important}.z-virtual-row.z-table-drag-source td>*{opacity:.45}.z-column-drop-list{min-height:3.125rem;overscroll-behavior:contain}.z-column-drop-list.cdk-drop-list-dragging{overflow:clip}:host td.z-cell-active,:host td.z-cell-active.z-row-hover{background-color:var(--z-cell-selection-background)!important}:host td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected,:host-context(.z-table-pointer-dragging) td.z-cell-range-selected.z-row-hover,:host-context(.z-table-pointer-dragging) td.z-cell-active,:host-context(.z-table-pointer-dragging) td.z-cell-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected:before{background-color:transparent}:host .z-tbody-wrapper tbody tr.z-group-child-row:has(+tr.z-group-row)>td.z-at-bottom{border-bottom-color:var(--border)}:host .z-virtual-row:has(tr.z-group-child-row):has(+.z-virtual-row tr.z-group-row) tr.z-group-child-row>td.z-at-bottom{border-bottom-color:var(--border)}.z-virtual-row td.z-virtual-column-spacer{height:auto}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 .125rem .125rem 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ZTableComponent, selector: "z-table", inputs: ["zClass", "zConfig", "zLoading", "zKey", "zVariant"], outputs: ["zChange", "zControl"], exportAs: ["zTable"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: 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: 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: "directive", type: ZTableDraggableDirective, selector: "[z-table-draggable]", inputs: ["z-table-draggable", "z-table-drag-table-id", "z-table-drag-item-id", "z-table-drag-disabled", "z-table-drag-handle"] }, { kind: "directive", type: ZTableDropTargetDirective, selector: "[z-table-drop-target]", inputs: ["z-table-drop-target", "z-table-drop-table-id", "z-table-drop-item-id", "z-table-drop-disabled"], outputs: ["zTableDropped"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zChange", "zGroupChange", "zOnBlur", "zOnFocus", "zControl", "zEvent", "zCheckedChange", "zGroupValueChange"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "zType", "zIcon", "zIconSize", "zSize", "zMessage", "zDescription"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zAnimatedType", "zAnimate", "zAnimationTrigger", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zAlign", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest", "zColorConfig"], outputs: ["zOnSearch", "zOnChange", "zOnBlur", "zOnFocus", "zOnKeydown", "zOnEnter", "zOnColorCollapse", "zControl", "zEvent"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zBodyClass", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zDisableShadow", "zHeaderBorder", "zFooterBorder", "zHideFooter", "zHideHeader", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zAfterClose", "zScrollbar", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zOnPageChange", "zPageIndexChange", "zPageSizeChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "component", type: ZTableEditCellComponent, selector: "z-table-edit-cell", inputs: ["zRow", "zRowId", "zRowIndex", "zColumnId", "zValue", "zEditConfig", "zRowUpdate"], outputs: ["zChange"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zPopoverTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zTriggerRef", "zManualClose", "zOutsideClickClose", "zScrollClose", "zSticky", "zShowArrow", "zKeyboardSafe"], outputs: ["zShow", "zHide", "zHideStart", "zControl", "zPositionChange", "zOutsideClick"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zAnimatedTypeIcon", "zAnimateIcon", "zAnimationTriggerIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTooltipPosition", "zTrigger", "zTooltipTrigger", "zTooltipType", "zTooltipSize", "zClass", "zTooltipClass", "zShowDelay", "zTooltipShowDelay", "zHideDelay", "zTooltipHideDelay", "zArrow", "zTooltipArrow", "zDisabled", "zTooltipDisabled", "zOffset", "zTooltipOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableResizeDirective, selector: "[z-table-resize],[zTableResize]", inputs: ["zTableResize"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip", "zTriggerElement"] }, { kind: "component", type: ZTableActionsComponent, selector: "z-table-actions", inputs: ["zConfig", "zRow", "zRowId", "zDropdownButtonSize"], outputs: ["zActionClick"] }, { kind: "component", type: ZTableContentEditorComponent, selector: "z-table-content-editor", inputs: ["zValue", "zConfig"], outputs: ["zCommit", "zCancel"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellBottomPipe, name: "zTableCellBottom" }, { kind: "pipe", type: ZTableCellClickablePipe, name: "zTableCellClickable" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellEditPipe, name: "zTableCellEdit" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableCellPinPipe, name: "zTableCellPin" }, { kind: "pipe", type: ZTableCellVisiblePipe, name: "zTableCellVisible" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableRowConfigPipe, name: "zTableRowConfig" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableSubTableConfigPipe, name: "zTableSubTableConfig" }, { kind: "pipe", type: ZTableCellRenderPipe, name: "zTableCellRender" }, { kind: "pipe", type: ZFormatNumPipe, name: "zFormatNum" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9575
9549
  }
9576
9550
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZTableComponent, decorators: [{
9577
9551
  type: Component,
@@ -9628,7 +9602,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
9628
9602
  TranslatePipe,
9629
9603
  ], standalone: true, providers: [TranslatePipe, ZTableDragService], changeDetection: ChangeDetectionStrategy.OnPush, host: {
9630
9604
  class: 'z-table block relative py-1',
9631
- }, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n #tableContainer\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.width]=\"zConfig().width\"\n [style.height]=\"zConfig().height\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\"\n [attr.tabindex]=\"canUseContentRowInsert() || canUseCellSelection() ? 0 : null\"\n (keydown)=\"onContentTableKeydown($event)\">\n <ng-template #contentRowMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n @if (contentRowMenu()?.columnId) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"copyContentCell()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n }\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"copyContentRows()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_row' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"contentRowClipboard().length === 0\"\n (click)=\"pasteContentRows('below')\">\n <z-icon zType=\"lucideClipboardPaste\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_paste_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowUp()\"\n (click)=\"moveContentRow('up')\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_up' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowDown()\"\n (click)=\"moveContentRow('down')\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_down' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('above')\">\n <z-icon zType=\"lucideArrowUpToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_above' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('below')\">\n <z-icon zType=\"lucideArrowDownToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"deleteContentRows()\">\n <z-icon zType=\"lucideTrash2\" zSize=\"12\" />\n <span>\n {{\n contentRowDeleteCount() > 1\n ? ('i18n_z_ui_table_delete_selected_rows' | translate)\n : ('i18n_z_ui_table_delete_row' | translate)\n }}\n </span>\n </button>\n </div>\n </ng-template>\n\n <ng-template #cellMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(false)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n @if (cellSelectionOptions().copyWithHeaders) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(true)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell_with_headers' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"contentRowMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-content-row-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"contentRowMenu()?.left ?? -9999\"\n [style.top.px]=\"contentRowMenu()?.top ?? -9999\"\n (zControl)=\"setContentRowMenuControl($event)\"\n (zHide)=\"clearContentRowMenu()\"></div>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"cellMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-cell-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"cellMenu()?.left ?? -9999\"\n [style.top.px]=\"cellMenu()?.top ?? -9999\"\n (zControl)=\"setCellMenuControl($event)\"\n (zHide)=\"clearCellMenu()\"></div>\n\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @if (canUseVirtualColumns()) {\n @for (column of leftLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualLeftSpacerWidth()\" />\n }\n @for (column of virtualCenterColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualRightSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualRightSpacerWidth()\" />\n }\n @for (column of rightLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n } @else {\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-card\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @if (canUseVirtualColumns()) {\n <tr>\n @for (header of leftHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: leftHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @for (header of virtualCenterHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: virtualCenterHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualRightSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n @for (header of rightHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: rightHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n <!-- Column Resizer -->\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n } @else {\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"\n handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\n \"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon\n zType=\"lucideChevronsLeftRight\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n @let virtualFlatItem = virtualGroupedFlatItems()[virtualItem.index];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drop-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drag-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n [z-table-drop-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @let groupHeaderItem = _getVirtualGroupHeader(virtualFlatItem);\n @if (groupHeaderItem) {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"groupHeaderItem.expanded\"\n [attr.data-group-id]=\"groupHeaderItem.id\"\n [attr.data-group-depth]=\"groupHeaderItem.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + groupHeaderItem.depth * 0.75\"\n [attr.aria-expanded]=\"groupHeaderItem.expanded\"\n (click)=\"toggleGroupRow(groupHeaderItem.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"groupHeaderItem.expanded\" />\n <span class=\"z-group-row-label\">\n @if (groupHeaderItem.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ groupHeaderItem.columnLabel }}:</span>\n }\n {{ groupHeaderItem.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ groupHeaderItem.count }}</span>\n }\n @if (groupHeaderItem.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n [zWave]=\"false\"\n z-tooltip\n zSizeIcon=\"14\"\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(groupHeaderItem.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @for (row of groupRows; track row.id) {\n @let virtualGroupDepth = _getVirtualGroupDepth(virtualFlatItem);\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n <tr\n z-table-row-hover\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [style.--z-row-height]=\"dynamicSize() ? 'auto' : virtualRowHeight() + 'px'\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"_isVirtualGroupChild(virtualFlatItem)\"\n [style.--z-group-content-indent]=\"\n virtualGroupDepth > 0 ? 1.75 + (virtualGroupDepth - 1) * 0.75 + 'rem' : null\n \"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : visibleCells\n : zConfig().columns\n : columnSizingInfo()\n : nativeRenderedColumnSizeMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"\n cellPinned === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cellPinned === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [style.--z-cell-range-top]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\n \"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\n \"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground inline-flex size-7 items-center justify-center rounded-md\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfoVirtual.contentEnabled) {\n @let contentConfigVirtual = editInfoVirtual.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfigVirtual\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfigVirtual.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n class=\"z-table-drag-body\"\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row let-isGroupChild=\"isGroupChild\" let-groupDepth=\"groupDepth\">\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n @let rowPinned = row.getIsPinned();\n <tr\n z-table-row-hover\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"row.id\"\n [z-table-drop-item-id]=\"row.id\"\n [z-table-drag-disabled]=\"!isRowDragEnabled()\"\n [z-table-drop-disabled]=\"!isRowDragEnabled()\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-row-id]=\"row.id\"\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : pinnedRowHeights() : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"isGroupChild\"\n [style.--z-group-content-indent]=\"groupDepth > 0 ? 1.75 + (groupDepth - 1) * 0.75 + 'rem' : null\"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n [class.z-pinned-top]=\"rowPinned === 'top'\"\n [class.z-pinned-bottom]=\"rowPinned === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() && rowPinned === 'top' && (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n rowPinned === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @let cellRowPinned = cell.row.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : visibleCells : zConfig().columns : columnSizingInfo() : nativeRenderedColumnSizeMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"cellPinned === 'right' && cell.column.getIsFirstColumn('right')\"\n [class.z-sticky-right-last]=\"cellPinned === 'right' && cell.column.getIsLastColumn('right')\"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style.--z-cell-range-top]=\"(selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"(selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.cursor-not-allowed]=\"!isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (\n (cell.column.id === 'actionRowPin' || cell.column.id === 'actions') &&\n cell.column.id !== actionColumnInfo()?.columnId\n ) {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfo.contentEnabled) {\n @let contentConfig = editInfo.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfig\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfig.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"(cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length) {\n @let resolvedSubTableConfig = row | zTableSubTableConfig: zConfig();\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"renderedColumnCount()\" class=\"p-0\">\n @if (zConfig().expandedRowTemplate) {\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n } @else if (resolvedSubTableConfig) {\n <div class=\"z-sub-table-container\">\n <z-table [zConfig]=\"$any(resolvedSubTableConfig)\" zVariant=\"borderless\" />\n </div>\n }\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (item of groupedCenterRowItems(); track item.type === 'group' ? item.id : item.row.id) {\n @if (item.type === 'group') {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"item.expanded\"\n [attr.data-group-id]=\"item.id\"\n [attr.data-group-depth]=\"item.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + item.depth * 0.75\"\n [attr.aria-expanded]=\"item.expanded\"\n (click)=\"toggleGroupRow(item.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"item.expanded\" />\n <span class=\"z-group-row-label\">\n @if (item.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ item.columnLabel }}:</span>\n }\n {{ item.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ item.count }}</span>\n }\n @if (item.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n zSizeIcon=\"14\"\n [zWave]=\"false\"\n z-tooltip\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(item.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @if (item.type === 'row') {\n <ng-container\n *ngTemplateOutlet=\"\n rowTemplate;\n context: {\n $implicit: item.row,\n isGroupChild: item.isGroupChild,\n groupDepth: item.groupDepth,\n }\n \" />\n }\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n footer.column.id === firstVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n footer.column.getIsPinned() !== false ||\n virtualCenterColumnVisibilityMap()[footer.column.id];\n @if (rowSpan && shouldRender && isVirtualColumnVisible) {\n <th\n [attr.data-column-id]=\"footer.column.id\"\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n class=\"z-template-content\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [class]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n footer.column.id === lastVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (paginationTotal() | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"paginationTotal()\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Floating Bulk Action Bar -->\n<div class=\"z-bulk-action-bar-origin\" cdkOverlayOrigin #bulkBarOrigin=\"cdkOverlayOrigin\"></div>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"bulkBarOrigin\"\n [cdkConnectedOverlayOpen]=\"bulkBarMounted()\"\n [cdkConnectedOverlayPositions]=\"bulkBarPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n cdkConnectedOverlayPanelClass=\"z-bulk-action-bar-overlay\">\n @if (bulkBarConfig(); as config) {\n <div class=\"z-bulk-action-bar-inner\" [class.z-leaving]=\"bulkBarClosing()\">\n <div class=\"z-bulk-action-bar-count\">\n {{ bulkBarContext()?.selectedRowIds?.length ?? 0 | zFormatNum }}\n {{ config.selectedLabel ?? ('i18n_z_ui_table_selected' | translate) }}\n </div>\n\n <div class=\"z-bulk-action-bar-divider\"></div>\n\n @for (item of bulkBarItems(); track item.action.key) {\n <button\n type=\"button\"\n z-button\n zSize=\"sm\"\n [zType]=\"item.buttonType\"\n [zDisabled]=\"item.disabled\"\n class=\"z-bulk-action-bar-button\"\n [disabled]=\"item.disabled\"\n (click)=\"onBulkActionClick(item.action)\">\n @if (item.action.icon) {\n <z-icon [zType]=\"item.action.icon\" [zSize]=\"item.action.iconSize ?? '14'\" />\n }\n @if (item.action.label) {\n <span>{{ item.action.label | translate }}</span>\n }\n </button>\n }\n </div>\n }\n</ng-template>\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n <!-- T\u1EA1m t\u1EAFt \u0111i\u1EC1u ki\u1EC7n: @if (zConfig().enableSettings) -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n [cdkDropListAutoScrollDisabled]=\"true\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select' && columnId !== 'rowDrag') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragDisabled]=\"isGroupedColumn\"\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragPreviewContainer=\"global\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-default items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\"\n [class.cursor-pointer]=\"!isGroupedColumn\"\n [class.cursor-default]=\"isGroupedColumn\"\n [class.opacity-40]=\"isGroupedColumn\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[0.625rem] font-medium\">\n {{\n isPinned === 'left' ? ('i18n_z_ui_table_left' | translate) : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n [disabled]=\"isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow-x:visible;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border);--z-cell-selection-background: color-mix(in srgb, var(--primary) 12%, transparent)}.z-table-container.z-column-resizing table,.z-table-toolbar{min-width:0}.z-bulk-action-bar-origin{position:fixed;left:50%;bottom:1rem;width:1px;height:1px;pointer-events:none}:host ::ng-deep .z-bulk-action-bar-overlay{max-width:calc(100vw - 2rem);margin-bottom:1rem;transform-origin:center bottom}.z-bulk-action-bar-inner{border:thin solid color-mix(in srgb,var(--border) 88%,transparent);border-radius:.4rem;background:color-mix(in srgb,var(--card) 96%,var(--background));color:var(--foreground);box-shadow:0 1rem 2.5rem color-mix(in srgb,var(--foreground) 20%,transparent),0 .25rem .75rem color-mix(in srgb,var(--foreground) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--background) 65%,transparent) inset;display:flex;align-items:center;gap:.5rem;width:max-content;min-height:2.25rem;max-width:calc(100vw - 2rem);padding:.5rem;transform-origin:center bottom;animation:z-bulk-action-bar-enter .18s cubic-bezier(.16,1,.3,1);will-change:opacity,transform}.z-bulk-action-bar-inner.z-leaving{pointer-events:none;animation:z-bulk-action-bar-exit .14s ease-in forwards}.z-bulk-action-bar-count{padding:0 .25rem;white-space:nowrap;font-size:.8125rem;font-weight:500;color:var(--foreground)}.z-bulk-action-bar-divider{width:1px;height:1.5rem;flex:none;background:color-mix(in srgb,var(--border) 88%,transparent)}.z-bulk-action-bar-button{flex:none}@keyframes z-bulk-action-bar-enter{0%{opacity:0;transform:translateY(.875rem)}to{opacity:1;transform:translateY(0)}}@keyframes z-bulk-action-bar-exit{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(.875rem)}}@media(prefers-reduced-motion:reduce){.z-bulk-action-bar-inner{animation:none}}:host ::ng-deep .z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;-webkit-user-select:text;user-select:text}:host ::ng-deep .z-table-cell-text>*,:host ::ng-deep .z-table-cell-text *{display:inline-block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.z-template-content{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.z-template-content>*{min-width:0;max-width:100%}.z-template-content>[class*=flex]{min-width:0;max-width:100%}.z-template-content>[class*=flex]>*{min-width:0;flex-shrink:1}.z-template-content>[class*=grid]{min-width:0;max-width:100%}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%}.z-sub-table-container{padding:.75rem 1rem .75rem 3rem;background-color:var(--background)}.z-table-content-trigger{display:block;width:100%;min-width:0;min-height:1.5rem;padding:0;border:0;border-radius:0;background:transparent;color:inherit;font:inherit;line-height:1.5rem;text-align:inherit;outline:0;cursor:text}.z-table-content-trigger:hover{color:var(--primary)}.z-content-edit-active,td:has(>.z-table-content-trigger:focus-visible){position:relative;z-index:2}.z-content-edit-active:after,td:has(>.z-table-content-trigger:focus-visible):after{position:absolute;z-index:3;inset:0;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-cell-selectable{-webkit-user-select:none;user-select:none}td.z-cell-range-selected{position:relative;isolation:isolate}td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);content:\"\";pointer-events:none}td.z-cell-range-selected>*{position:relative;z-index:1}td.z-cell-range-top:before{border-top:1px solid var(--primary)}td.z-cell-range-right:before{border-right:1px solid var(--primary)}td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}td.z-cell-range-left:before{border-left:1px solid var(--primary)}td.z-cell-active{position:relative;z-index:3;background-color:var(--z-cell-selection-background)!important}td.z-cell-active:after{position:absolute;z-index:4;inset:-1px;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-content-edit-active{-webkit-user-select:text;user-select:text}td.z-cell-active.z-row-hover,td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}.z-tfoot-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-column-menu-item{line-height:1.25rem}.z-column-menu-item:focus-visible,.z-column-menu-item.z-popover-open{background-color:var(--muted);outline:none}.z-table-drag-preview-row{border:0;border-radius:.125rem;background-color:var(--background);box-shadow:0 8px 20px #0000001f,inset 0 0 0 1px color-mix(in srgb,var(--primary) 45%,var(--border))}.z-table-drag-preview-row table,.z-table-drag-preview-row tbody,.z-table-drag-preview-row tr{height:100%}.z-table-drag-preview-row td{vertical-align:middle!important;background-color:var(--background)}[data-z-table-drag-handle]{touch-action:none;-webkit-user-select:none;user-select:none}.z-table-drag-placeholder{width:100%;min-height:2.625rem;box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background));pointer-events:none}.z-table-drag-placeholder-row>td{padding:0!important;border:0!important;background:transparent!important}.z-table-drag-placeholder-row .z-table-drag-placeholder{border-radius:0;background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.z-table-drag-placeholder-virtual{position:absolute;top:0;left:0;z-index:20;border-radius:0}.z-tbody-wrapper tr.z-group-row td{background:var(--card);border-top:none;border-bottom:1px solid var(--border)}.z-virtual-row tr.z-group-row td{background:var(--card);border-top:none;border-bottom:none}.z-group-row-sticky-wrapper{display:inline-flex;align-items:center;translate:var(--z-scroll-x, 0px) 0}.z-table-container:not(.z-hide-horizontal-border) .z-virtual-row:after{content:\"\";position:absolute;right:0;bottom:0;left:0;z-index:3;height:1px;background:var(--border);pointer-events:none}.z-tbody-wrapper tr.z-group-child-row td.z-at-left-edge,.z-virtual-row tr.z-group-child-row td.z-at-left-edge{border-left:2px solid var(--primary)!important;padding-left:var(--z-group-content-indent, .75rem)}.z-group-row-trigger{display:inline-flex;min-height:2rem;max-width:100%;align-items:center;gap:.5rem;padding:.25rem .5rem .25rem .75rem;color:var(--primary);font-size:.8125rem;font-weight:600;vertical-align:middle;cursor:pointer}.z-group-row-remove{margin-left:.125rem;color:var(--muted-foreground);vertical-align:middle}.z-group-row-remove:hover,.z-group-row-remove:focus-visible{color:var(--destructive);background:color-mix(in srgb,var(--destructive) 10%,transparent);outline:none}.z-group-row-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.z-group-row-column-label{color:color-mix(in srgb,var(--primary) 65%,var(--foreground));font-weight:500;margin-right:.25rem}.z-group-row-count{display:inline-flex;min-width:1.25rem;height:1.25rem;align-items:center;justify-content:center;padding:0 .375rem;border-radius:999px;background:var(--primary);color:var(--primary-foreground);font-size:.6875rem;font-weight:600}.z-group-row-loading{display:inline-flex;align-items:center;margin-left:.25rem}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{position:absolute!important;z-index:0!important;box-sizing:border-box!important;top:var(--z-cell-range-top, 0)!important;right:var(--z-cell-range-right, 0)!important;bottom:var(--z-cell-range-bottom, 0)!important;left:var(--z-cell-range-left, 0)!important;width:auto!important;background-color:var(--z-cell-selection-background)!important;box-shadow:none!important;content:\"\"!important;pointer-events:none!important;opacity:1!important}:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:after{display:none!important}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-top:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-top:before{border-top:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-right:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-right:before{border-right:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-bottom:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-left:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{background-color:transparent!important}:host td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);box-shadow:none;content:\"\";pointer-events:none;opacity:1}:host td.z-cell-range-top:before{border-top:1px solid var(--primary)}:host td.z-cell-range-right:before{border-right:1px solid var(--primary)}:host td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host td.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) td.z-row-hover{background-color:var(--background)!important}:host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow:hidden;border-radius:.3125rem;border:thin solid var(--border);background-color:var(--card)}.z-table-container:focus{outline:none}.z-table-container.z-table-borderless{border:none;border-radius:0;box-shadow:none!important;background-color:transparent}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:.875rem}.z-table-toolbar{min-width:0}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-thead-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:.5rem;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;width:100%;max-width:100%;min-width:0;min-height:6.25rem;display:flex;flex-direction:column;overflow:hidden}.z-tbody-wrapper{flex:1;display:flex;flex-direction:column;min-height:0;width:100%}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%;transition:opacity .2s ease-in-out}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:6.25rem;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:.5rem 12px;height:var(--z-row-height, 2.375rem);vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom-color:transparent}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:.75rem}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:.5rem 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{border-left:.125rem solid var(--primary)!important;padding-left:.75rem!important}tbody tr.z-context-menu-active,tbody tr.z-context-menu-active td{background-color:var(--muted)!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td{background-color:var(--muted)!important}tr.z-expanded-row>td{border-left:.125rem solid var(--primary);background-color:var(--background)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:.5rem;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:.1875rem;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1.875rem;width:1.875rem;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:.5rem 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:.75rem;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:.125rem 4px;border-radius:.25rem;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:.25rem;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover,th .z-header-text-wrapper.z-has-options:focus-visible,th .z-header-text-wrapper.z-has-options.z-popover-open{background-color:color-mix(in srgb,var(--foreground) 8%,transparent);outline:none}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-sizing:border-box;border:1px solid var(--primary);border-radius:.375rem;background-color:var(--card);box-shadow:0 5px 20px #0003;overflow:hidden;pointer-events:none;z-index:10000!important}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background))}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-table-drag-preview{box-sizing:border-box;border-radius:.375rem;background-color:var(--card);border:1px solid var(--primary);box-shadow:0 10px 24px #00000029;overflow:hidden;z-index:10000!important;pointer-events:none}.z-table-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.z-table-drag-source{display:none!important}.z-virtual-row.z-table-drag-source{display:block!important;pointer-events:none;outline:1px solid var(--border);outline-offset:-1px}.z-virtual-row.z-table-drag-source td{color:color-mix(in srgb,var(--foreground) 45%,transparent);border-color:var(--border)!important;background-color:var(--card)!important}.z-virtual-row.z-table-drag-source td>*{opacity:.45}.z-column-drop-list{min-height:3.125rem;overscroll-behavior:contain}.z-column-drop-list.cdk-drop-list-dragging{overflow:clip}:host td.z-cell-active,:host td.z-cell-active.z-row-hover{background-color:var(--z-cell-selection-background)!important}:host td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected,:host-context(.z-table-pointer-dragging) td.z-cell-range-selected.z-row-hover,:host-context(.z-table-pointer-dragging) td.z-cell-active,:host-context(.z-table-pointer-dragging) td.z-cell-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected:before{background-color:transparent}:host .z-tbody-wrapper tbody tr.z-group-child-row:has(+tr.z-group-row)>td.z-at-bottom{border-bottom-color:var(--border)}:host .z-virtual-row:has(tr.z-group-child-row):has(+.z-virtual-row tr.z-group-row) tr.z-group-child-row>td.z-at-bottom{border-bottom-color:var(--border)}.z-virtual-row td.z-virtual-column-spacer{height:auto}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 .125rem .125rem 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
9605
+ }, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (isSearchEnabled() || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (isSearchEnabled()) {\n @let config = searchConfig();\n <z-input\n [class]=\"config?.width ?? 'w-64'\"\n [zSize]=\"config?.size ?? 'sm'\"\n [zPlaceholder]=\"config?.placeholder ?? 'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"config?.debounceTime ?? 300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">\n {{ 'i18n_z_ui_table_settings' | translate }}\n </z-button>\n }\n </div>\n}\n\n<div\n #tableContainer\n [class]=\"classTable()\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.width]=\"zConfig().width\"\n [style.height]=\"zConfig().height\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\"\n [attr.tabindex]=\"canUseContentRowInsert() || canUseCellSelection() ? 0 : null\"\n (keydown)=\"onContentTableKeydown($event)\">\n <ng-template #contentRowMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n @if (contentRowMenu()?.columnId) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"copyContentCell()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n }\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"copyContentRows()\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_row' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"contentRowClipboard().length === 0\"\n (click)=\"pasteContentRows('below')\">\n <z-icon zType=\"lucideClipboardPaste\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_paste_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowUp()\"\n (click)=\"moveContentRow('up')\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_up' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canMoveContentRowDown()\"\n (click)=\"moveContentRow('down')\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_move_row_down' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('above')\">\n <z-icon zType=\"lucideArrowUpToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_above' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"insertContentRow('below')\">\n <z-icon zType=\"lucideArrowDownToLine\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_insert_row_below' | translate }}</span>\n </button>\n <div class=\"border-border my-0 border-t\"></div>\n <button\n type=\"button\"\n class=\"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none\"\n (click)=\"deleteContentRows()\">\n <z-icon zType=\"lucideTrash2\" zSize=\"12\" />\n <span>\n {{\n contentRowDeleteCount() > 1\n ? ('i18n_z_ui_table_delete_selected_rows' | translate)\n : ('i18n_z_ui_table_delete_row' | translate)\n }}\n </span>\n </button>\n </div>\n </ng-template>\n\n <ng-template #cellMenuContent>\n <div class=\"flex min-w-36 flex-col gap-0.5 p-1\">\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(false)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell' | translate }}</span>\n </button>\n @if (cellSelectionOptions().copyWithHeaders) {\n <button\n type=\"button\"\n class=\"text-foreground hover:bg-muted focus:bg-muted flex min-h-6 cursor-pointer items-center gap-1.5 rounded px-1.5 py-0.5 text-[0.6875rem] outline-none disabled:pointer-events-none disabled:opacity-40\"\n [disabled]=\"!canCopyCellMenuSelection()\"\n (click)=\"copySelectedCells(true)\">\n <z-icon zType=\"lucideCopy\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_copy_cell_with_headers' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"contentRowMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-content-row-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"contentRowMenu()?.left ?? -9999\"\n [style.top.px]=\"contentRowMenu()?.top ?? -9999\"\n (zControl)=\"setContentRowMenuControl($event)\"\n (zHide)=\"clearContentRowMenu()\"></div>\n\n <div\n class=\"pointer-events-none fixed size-px opacity-0\"\n z-popover\n [zPopoverContent]=\"cellMenuContent\"\n zTrigger=\"manual\"\n zPosition=\"bottom-left\"\n zClass=\"z-table-cell-menu\"\n [zOffset]=\"0\"\n [zManualClose]=\"true\"\n [zScrollClose]=\"true\"\n [style.left.px]=\"cellMenu()?.left ?? -9999\"\n [style.top.px]=\"cellMenu()?.top ?? -9999\"\n (zControl)=\"setCellMenuControl($event)\"\n (zHide)=\"clearCellMenu()\"></div>\n\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @if (canUseVirtualColumns()) {\n @for (column of leftLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualLeftSpacerWidth()\" />\n }\n @for (column of virtualCenterColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n @if (virtualRightSpacerWidth() > 0) {\n <col [style.width.px]=\"virtualRightSpacerWidth()\" />\n }\n @for (column of rightLeafColumns(); track column.id) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n } @else {\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n @let customWidth = column.id | zTableColumnConfig: zConfig().columns : 'width';\n <col [style.width]=\"customWidth || 'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-card\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @if (canUseVirtualColumns()) {\n <tr>\n @for (header of leftHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: leftHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualLeftSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @for (header of virtualCenterHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: virtualCenterHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n @if (virtualRightSpacerWidth() > 0) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n @for (header of rightHeaderRow(); track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: rightHeaderRow() : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') === 'text-right'\n \">\n <!-- Column Options Popover Template -->\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"14\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideChevronsLeftRight\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n <!-- Dropdown indicator when has options (between text and sort icon) -->\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n <!-- Sort Icon (outside wrapper, no hover background) -->\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n <!-- Column Resizer -->\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n } @else {\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableCellRender: headerGroup.headers : zConfig().columns : 'header';\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"\n header.column\n | zTablePinningStyles: header : 'header' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass') +\n ' ' +\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass')\n \"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableCellPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableCellPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableCellPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"header.column.id === 'rowDrag'\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"\n header.column.id === 'actions' || header.column.id === actionColumnInfo()?.columnId\n \"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground opacity-70\" />\n </div>\n } @else if (header.column.id === 'select') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection()\"\n (zChange)=\"canUseRowSelection() && table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <div\n class=\"flex items-center\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else {\n <div\n class=\"flex w-full items-center gap-1\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n @let columnEnableOrdering =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableOrdering';\n @let columnEnablePinning =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enablePinning';\n @let effectiveEnableOrdering = columnEnableOrdering || zConfig().enableColumnOrdering;\n @let effectiveEnablePinning = columnEnablePinning || zConfig().enableColumnPinning;\n <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-1 py-1\">\n @if (effectiveEnableOrdering) {\n <button\n type=\"button\"\n [disabled]=\"isFirstMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnLeft(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_left' | translate }}</span>\n </button>\n <button\n type=\"button\"\n [disabled]=\"isLastMovableColumn(header.column.id) || header.column.getIsPinned()\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"moveColumnRight(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none disabled:pointer-events-none disabled:opacity-40\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_move_right' | translate }}</span>\n </button>\n }\n @if (effectiveEnableOrdering && header.column.getCanPin() && effectiveEnablePinning) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && effectiveEnablePinning) {\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_left' | translate }}</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"\n handleColumnPin(header.column.id, 'right'); colOptionsPopover.hideImmediate()\n \"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucidePin\" zSize=\"15\" class=\"-rotate-90\" />\n <span>{{ 'i18n_z_ui_table_pin_right' | translate }}</span>\n </button>\n }\n @if (\n header.column.getIsPinned() &&\n header.column.id !== 'expand' &&\n header.column.id !== actionColumnInfo()?.columnId\n ) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"handleColumnPin(header.column.id, false); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideX\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n }\n @if (groupingHeaderMenuMap()[header.id]) {\n @let isActiveGroupingColumn = groupingColumnMap()[header.column.id];\n <div class=\"border-border my-0.5 border-t\"></div>\n @if (!isActiveGroupingColumn) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"groupByColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRows3\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_group_by_column' | translate }}</span>\n </button>\n }\n @let groupMenuStateBoundary = isActiveGroupingColumn;\n @if (groupMenuStateBoundary) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"removeGroupingColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-destructive hover:bg-destructive/10 focus:bg-destructive/10 mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideUngroup\" zSize=\"15\" />\n <span>{{ 'i18n_z_ui_table_clear_grouping' | translate }}</span>\n </button>\n }\n }\n <div class=\"border-border my-0.5 border-t\"></div>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeColumn(header.column.id); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon\n zType=\"lucideChevronsLeftRight\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_column' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"autosizeAllColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_autosize_all_columns' | translate }}</span>\n </button>\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"resetColumns(); colOptionsPopover.hideImmediate()\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideRotateCcw\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span>{{ 'i18n_z_ui_table_reset_columns' | translate }}</span>\n </button>\n @if (hideableColumns().length > 0) {\n <button\n type=\"button\"\n (mouseenter)=\"hideActiveColumnVisibilityPopover()\"\n (click)=\"openSettingsDrawerFromColumnMenu(colOptionsPopover)\"\n class=\"z-column-menu-item text-foreground hover:bg-muted focus:bg-muted mx-1 flex min-h-7 cursor-pointer items-center gap-2 rounded px-2 py-1 text-xs outline-none\">\n <z-icon zType=\"lucideColumns3\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n <span class=\"truncate\">\n {{ 'i18n_z_ui_table_choose_columns' | translate }}\n </span>\n </button>\n }\n </div>\n </ng-template>\n\n @let columnEnableHeaderMenu =\n header.column.id | zTableColumnConfig: zConfig().columns : 'enableHeaderMenu';\n @let hasColumnOptions =\n columnEnableHeaderMenu !== false &&\n (groupingHeaderMenuMap()[header.id] ||\n (header.column.getCanPin() && effectiveEnablePinning) ||\n effectiveEnableOrdering ||\n hideableColumns().length > 0);\n <div\n class=\"z-header-text-wrapper inline-flex max-w-full items-center gap-1 rounded px-1.5 py-1\"\n [class.cursor-pointer]=\"hasColumnOptions\"\n [class.z-has-options]=\"hasColumnOptions\"\n [attr.z-popover]=\"hasColumnOptions ? '' : null\"\n #colOptionsPopover=\"zPopover\"\n #headerTextWrapper\n z-popover\n [zPopoverContent]=\"hasColumnOptions ? colOptionsPopoverContent : null\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n [zOffset]=\"5\"\n (zOutsideClick)=\"hideColumnPopoversOnOutsideClick(colOptionsPopover)\"\n (mousedown)=\"$event.preventDefault()\"\n [attr.tabindex]=\"hasColumnOptions ? 0 : null\"\n [attr.role]=\"hasColumnOptions ? 'button' : null\"\n [attr.aria-haspopup]=\"hasColumnOptions ? 'menu' : null\">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <div\n class=\"z-template-content\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n </div>\n } @else if (headerContent | zTableHasIcon) {\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\"\n [zTriggerElement]=\"headerTextWrapper\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \" />\n } @else {\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [zTriggerElement]=\"headerTextWrapper\"\n [innerHTML]=\"headerContent | translate | zSafeHtml\"\n [class]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentClass'\n \"\n [ngStyle]=\"\n header.column.id | zTableColumnConfig: zConfig().columns : 'headerContentStyle'\n \"></span>\n }\n </ng-container>\n @if (hasColumnOptions) {\n <z-icon zType=\"lucideChevronDown\" zSize=\"15\" class=\"text-muted-foreground shrink-0\" />\n }\n </div>\n @if ((header.column.getCanSort() && !hasBodyRowSpan()) || header.column.getCanFilter()) {\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n }\n </div>\n }\n @if (header.column.getCanResize() && zConfig().enableColumnResizing !== false) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n [zTableResize]=\"header\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (isLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"animate-in fade-in flex h-full flex-col duration-200\">\n @for (i of skeletonRows(); track $index; let last = $last) {\n <div\n class=\"border-border box-border flex flex-1 flex-col items-start justify-center gap-1.5 px-2\"\n [class.border-b]=\"!last\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" zHeight=\"22px\" zRadius=\"4px\" />\n <z-skeleton zType=\"bar\" zWidth=\"50%\" zHeight=\"14px\" zRadius=\"4px\" />\n </div>\n }\n </div>\n } @else {\n <!-- Spinner Loading -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" [zText]=\"'i18n_z_ui_table_loading' | translate\" />\n </div>\n }\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <z-empty zIcon=\"lucideSearchX\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_results' | translate\" />\n } @else {\n <z-empty zIcon=\"lucidePackageOpen\" zSize=\"sm\" [zMessage]=\"'i18n_z_ui_table_no_data' | translate\" />\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n @let virtualFlatItem = virtualGroupedFlatItems()[virtualItem.index];\n <div\n #virtualRow\n class=\"z-virtual-row\"\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drop-item-id]=\"groupRows[0]?.id ?? ''\"\n [z-table-drag-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n [z-table-drop-disabled]=\"!isVirtualRowDragEnabled() || groupRows.length !== 1\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-index]=\"virtualItem.index\"\n [style.height.px]=\"\n dynamicSize() ? null : (dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight())\n \"\n [style.transform]=\"'translate3d(0,' + virtualItem.start + 'px,0)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n @let groupHeaderItem = _getVirtualGroupHeader(virtualFlatItem);\n @if (groupHeaderItem) {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"groupHeaderItem.expanded\"\n [attr.data-group-id]=\"groupHeaderItem.id\"\n [attr.data-group-depth]=\"groupHeaderItem.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + groupHeaderItem.depth * 0.75\"\n [attr.aria-expanded]=\"groupHeaderItem.expanded\"\n (click)=\"toggleGroupRow(groupHeaderItem.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"groupHeaderItem.expanded\" />\n <span class=\"z-group-row-label\">\n @if (groupHeaderItem.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ groupHeaderItem.columnLabel }}:</span>\n }\n {{ groupHeaderItem.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ groupHeaderItem.count }}</span>\n }\n @if (groupHeaderItem.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n [zWave]=\"false\"\n z-tooltip\n zSizeIcon=\"14\"\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(groupHeaderItem.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @for (row of groupRows; track row.id) {\n @let virtualGroupDepth = _getVirtualGroupDepth(virtualFlatItem);\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n <tr\n z-table-row-hover\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [style.height.px]=\"dynamicSize() ? null : virtualRowHeight()\"\n [style.min-height.px]=\"dynamicSize() ? virtualRowHeight() : null\"\n [style.--z-row-height]=\"dynamicSize() ? 'auto' : virtualRowHeight() + 'px'\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"_isVirtualGroupChild(virtualFlatItem)\"\n [style.--z-group-content-indent]=\"\n virtualGroupDepth > 0 ? 1.75 + (virtualGroupDepth - 1) * 0.75 + 'rem' : null\n \"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles\n : cell\n : 'body'\n : visibleCells\n : zConfig().columns\n : columnSizingInfo()\n : colDomWidthMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"\n cellPinned === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cellPinned === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === actionColumnInfo()?.columnId\"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [style.--z-cell-range-top]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\n \"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\n \"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground inline-flex size-7 items-center justify-center rounded-md\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisible = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisible) {\n @let editInfoVirtual = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfoVirtual.enabled) {\n <!-- Editable Cell (Virtual) -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfoVirtual.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfoVirtual.contentEnabled) {\n @let contentConfigVirtual = editInfoVirtual.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfigVirtual\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfigVirtual.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"\n cell.column.columnDef.cell;\n props: cell.getContext();\n let cellContent\n \">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickable = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickable && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"\n cellContent;\n context: { $implicit: cell.getContext() }\n \" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIcon = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableIcon && onCellClick(row, cell.column.id, cell.getValue())\n \" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefault =\n cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefault && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td\n class=\"z-virtual-column-spacer pointer-events-none border-0 p-0\"\n [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody\n class=\"z-table-drag-body\"\n [class.z-has-vertical-scroll]=\"hasVerticalScroll()\"\n [class.z-last-row-touches-bottom]=\"lastRowTouchesBottom()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row let-isGroupChild=\"isGroupChild\" let-groupDepth=\"groupDepth\">\n @let visibleCells = row.getVisibleCells();\n @let rowSelected = canUseRowSelection() && row.getIsSelected();\n @let rowSomeSelected = canUseRowSelection() && row.getIsSomeSelected();\n @let rowPinned = row.getIsPinned();\n <tr\n z-table-row-hover\n z-table-draggable=\"row\"\n z-table-drop-target=\"row\"\n [z-table-drag-table-id]=\"dragInstanceId\"\n [z-table-drop-table-id]=\"dragInstanceId\"\n [z-table-drag-item-id]=\"row.id\"\n [z-table-drop-item-id]=\"row.id\"\n [z-table-drag-disabled]=\"!isRowDragEnabled()\"\n [z-table-drop-disabled]=\"!isRowDragEnabled()\"\n (zTableDropped)=\"_handleDragDrop($event)\"\n [attr.data-row-id]=\"row.id\"\n [class]=\"row | zTableRowConfig: zConfig() : 'rowClass'\"\n [style]=\"row | zTableRowConfig: zConfig() : 'rowStyle'\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : pinnedRowHeights() : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-group-child-row]=\"isGroupChild\"\n [style.--z-group-content-indent]=\"groupDepth > 0 ? 1.75 + (groupDepth - 1) * 0.75 + 'rem' : null\"\n [class.z-selected]=\"rowSelected\"\n [class.z-indeterminate-selected]=\"rowSomeSelected && !rowSelected\"\n [class.z-context-menu-active]=\"contentRowMenu()?.row?.id === row.id\"\n [class.z-pinned-top]=\"rowPinned === 'top'\"\n [class.z-pinned-bottom]=\"rowPinned === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() && rowPinned === 'top' && (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n rowPinned === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\"\n (contextmenu)=\"openContentRowMenu($event, row)\">\n @for (cell of visibleCells; track cell.id) {\n @let cellPinned = cell.column.getIsPinned();\n @let cellRowPinned = cell.row.getIsPinned();\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n cell.column.id === firstVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></td>\n }\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableCellRender: visibleCells : zConfig().columns : 'body';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n cellPinned !== false ||\n virtualCenterColumnVisibilityMap()[cell.column.id];\n @if (shouldRenderRowSpan && shouldRenderColSpan && isVirtualColumnVisible) {\n @let cellSelectionId = row.id + '::' + cell.column.id;\n <td\n [attr.data-column-id]=\"cell.column.id\"\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : visibleCells : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"cell | zTableCellConfig: zConfig() : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig() : 'cellStyle'\"\n [class.z-sticky-left]=\"cellPinned === 'left'\"\n [class.z-sticky-right]=\"cellPinned === 'right'\"\n [class.z-sticky-left-last]=\"cellPinned === 'left' && cell.column.getIsLastColumn('left')\"\n [class.z-sticky-right-first]=\"cellPinned === 'right' && cell.column.getIsFirstColumn('right')\"\n [class.z-sticky-right-last]=\"cellPinned === 'right' && cell.column.getIsLastColumn('right')\"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-drag]=\"cell.column.id === 'rowDrag'\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"\n cell.column.id === 'actions' || cell.column.id === actionColumnInfo()?.columnId\n \"\n [class.z-cell-selectable]=\"canUseCellSelection()\"\n [class.z-cell-active]=\"\n !hasMultipleSelectedCells() &&\n activeCell()?.rowId === row.id &&\n activeCell()?.columnId === cell.column.id\n \"\n [class.z-cell-range-selected]=\"visualSelectedCellIds().has(cellSelectionId)\"\n [class.z-cell-range-top]=\"selectedCellEdgeIds().top.has(cellSelectionId)\"\n [class.z-cell-range-right]=\"selectedCellEdgeIds().right.has(cellSelectionId)\"\n [class.z-cell-range-bottom]=\"selectedCellEdgeIds().bottom.has(cellSelectionId)\"\n [class.z-cell-range-left]=\"selectedCellEdgeIds().left.has(cellSelectionId)\"\n [class.z-content-edit-active]=\"\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n \"\n [class.z-at-bottom]=\"cell | zTableCellBottom: zConfig().columns : renderedBodyRows()\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [style.--z-cell-range-top]=\"(selectedCellRangeInsets().get(cellSelectionId)?.top ?? 0) + '%'\"\n [style.--z-cell-range-right]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.right ?? 0) + '%'\n \"\n [style.--z-cell-range-bottom]=\"\n (selectedCellRangeInsets().get(cellSelectionId)?.bottom ?? 0) + '%'\n \"\n [style.--z-cell-range-left]=\"(selectedCellRangeInsets().get(cellSelectionId)?.left ?? 0) + '%'\"\n (pointerdown)=\"onCellPointerDown($event, row, cell.column.id)\"\n (pointerenter)=\"onCellPointerEnter($event, row, cell.column.id)\"\n (contextmenu)=\"openCellMenu($event, row, cell.column.id)\">\n @if (cell.column.id === 'rowDrag') {\n <div class=\"flex items-center justify-center\">\n <button\n data-z-table-drag-handle\n type=\"button\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground inline-flex size-7 items-center justify-center rounded-md transition-colors\"\n [class.cursor-grab]=\"isRowDragEnabled()\"\n [class.cursor-not-allowed]=\"!isRowDragEnabled()\"\n [class.opacity-40]=\"!isRowDragEnabled()\">\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" />\n </button>\n </div>\n } @else if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n [zChecked]=\"canUseRowSelection() && cell.row.getIsSelected()\"\n [zIndeterminate]=\"\n canUseRowSelection() && cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\n \"\n [zDisabled]=\"!canUseRowSelection() || !cell.row.getCanSelect()\"\n (zChange)=\"canUseRowSelection() && cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.getCanExpand()) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (\n (cell.column.id === 'actionRowPin' || cell.column.id === 'actions') &&\n cell.column.id !== actionColumnInfo()?.columnId\n ) {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-1 p-1\">\n @if (cellRowPinned !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_top' | translate }}</span>\n </button>\n }\n @if (cellRowPinned !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_pin_bottom' | translate }}</span>\n </button>\n }\n @if (cellRowPinned) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>{{ 'i18n_z_ui_table_unpin' | translate }}</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cellRowPinned\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else if (cell.column.id === actionColumnInfo()?.columnId && actionColumnInfo()) {\n <z-table-actions\n [zConfig]=\"$any(actionColumnInfo()!.config)\"\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n (zActionClick)=\"onActionClick($event)\" />\n } @else {\n @let isCellVisibleNormal = cell | zTableCellVisible: zConfig().columns;\n @if (isCellVisibleNormal) {\n @let editInfo = cell.getContext() | zTableCellEdit: zConfig().columns;\n @if (editInfo.enabled) {\n <!-- Editable Cell -->\n <z-table-edit-cell\n [zRow]=\"cell.row.original\"\n [zRowId]=\"cell.row.id\"\n [zRowIndex]=\"cell.row.index\"\n [zColumnId]=\"cell.column.id\"\n [zValue]=\"cell.getValue()\"\n [zRowUpdate]=\"_rowUpdate()\"\n [zEditConfig]=\"$any(editInfo.config)\"\n (zChange)=\"onCellEdit($any($event))\" />\n } @else if (editInfo.contentEnabled) {\n @let contentConfig = editInfo.contentConfig!;\n @if (\n activeContentEditCell()?.rowId === row.id &&\n activeContentEditCell()?.columnId === cell.column.id\n ) {\n <z-table-content-editor\n [zValue]=\"cell.getValue()\"\n [zConfig]=\"contentConfig\"\n (zCommit)=\"onContentEditCommit($event, row, cell.column.id, cell.getValue())\"\n (zCancel)=\"onContentEditCancel()\" />\n } @else {\n <button\n type=\"button\"\n class=\"z-table-content-trigger\"\n [attr.data-z-table-edit-row]=\"row.id\"\n [attr.data-z-table-edit-column]=\"cell.column.id\"\n (keydown)=\"onContentEditKeydown($event, row.id, cell.column.id)\"\n (click)=\"onContentEditStart($event, row.id, cell.column.id)\">\n {{ contentConfig.displayValue }}\n </button>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n @let isClickableTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-template-content\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableTpl && onCellClick(row, cell.column.id, cell.getValue())\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n @let isClickableIconTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig() : 'contentTooltip'\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"isClickableIconTpl && onCellClick(row, cell.column.id, cell.getValue())\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n @let isClickableDefaultTpl = cell.column.id | zTableCellClickable: zConfig().columns;\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"(cell | zTableCellConfig: zConfig() : 'contentTooltip') || cellContent\"\n [innerHTML]=\"cellContent | translate | zSafeHtml\"\n [class]=\"cell | zTableCellConfig: zConfig() : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig() : 'contentStyle'\"\n (click)=\"\n isClickableDefaultTpl && onCellClick(row, cell.column.id, cell.getValue())\n \"></div>\n }\n </ng-container>\n }\n }\n }\n </td>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n cell.column.id === lastVirtualCenterColumnId()\n ) {\n <td class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length) {\n @let resolvedSubTableConfig = row | zTableSubTableConfig: zConfig();\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"renderedColumnCount()\" class=\"p-0\">\n @if (zConfig().expandedRowTemplate) {\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n } @else if (resolvedSubTableConfig) {\n <div class=\"z-sub-table-container\">\n <z-table [zConfig]=\"$any(resolvedSubTableConfig)\" zVariant=\"borderless\" />\n </div>\n }\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (item of groupedCenterRowItems(); track item.type === 'group' ? item.id : item.row.id) {\n @if (item.type === 'group') {\n <tr\n class=\"z-group-row\"\n [class.z-group-row-expanded]=\"item.expanded\"\n [attr.data-group-id]=\"item.id\"\n [attr.data-group-depth]=\"item.depth\">\n <td [attr.colspan]=\"renderedColumnCount()\">\n <div class=\"z-group-row-sticky-wrapper\">\n <button\n type=\"button\"\n class=\"z-group-row-trigger\"\n [style.padding-left.rem]=\"0.75 + item.depth * 0.75\"\n [attr.aria-expanded]=\"item.expanded\"\n (click)=\"toggleGroupRow(item.id)\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"item.expanded\" />\n <span class=\"z-group-row-label\">\n @if (item.columnLabel) {\n <span class=\"z-group-row-column-label\">{{ item.columnLabel }}:</span>\n }\n {{ item.label }}\n </span>\n @if (groupingConfig().showCount !== false) {\n <span class=\"z-group-row-count\">{{ item.count }}</span>\n }\n @if (item.loading) {\n <z-loading class=\"z-group-row-loading\" [zLoading]=\"true\" zSize=\"xs\" />\n }\n </button>\n @if (!isServerGrouping()) {\n <button\n type=\"button\"\n z-button\n zType=\"ghost\"\n zSize=\"sm\"\n zTypeIcon=\"lucideX\"\n zSizeIcon=\"14\"\n [zWave]=\"false\"\n z-tooltip\n [zContent]=\"'i18n_z_ui_table_clear_grouping' | translate\"\n [zAlwaysShow]=\"true\"\n zTooltipPosition=\"top\"\n class=\"z-group-row-remove\"\n (click)=\"removeGroupingColumn(item.columnId)\"></button>\n }\n </div>\n </td>\n </tr>\n }\n @if (item.type === 'row') {\n <ng-container\n *ngTemplateOutlet=\"\n rowTemplate;\n context: {\n $implicit: item.row,\n isGroupChild: item.isGroupChild,\n groupDepth: item.groupDepth,\n }\n \" />\n }\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"renderedTableWidth()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @if (\n canUseVirtualColumns() &&\n virtualLeftSpacerWidth() > 0 &&\n footer.column.id === firstVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualLeftSpacerWidth()\"></th>\n }\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableCellRender: footerGroup.headers : zConfig().columns : 'footer';\n @let isVirtualColumnVisible =\n !canUseVirtualColumns() ||\n footer.column.getIsPinned() !== false ||\n virtualCenterColumnVisibilityMap()[footer.column.id];\n @if (rowSpan && shouldRender && isVirtualColumnVisible) {\n <th\n [attr.data-column-id]=\"footer.column.id\"\n [ngStyle]=\"\n footer.column\n | zTablePinningStyles: footer : 'footer' : table : zConfig().columns : columnSizingInfo() : colDomWidthMap()\n \"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n footer | zTableCellPin: 'isLastLeftPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-first]=\"\n footer | zTableCellPin: 'isFirstRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-sticky-right-last]=\"\n footer | zTableCellPin: 'isLastRightPinned' : zConfig().columns : 'footer'\n \"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n class=\"z-template-content\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n </div>\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \"\n [class]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\n \"\n [ngStyle]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\n \">\n {{ footerContent | translate }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \"\n [class]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentClass'\"\n [ngStyle]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContentStyle'\">\n {{ $any(configFooterContent) | translate }}\n </span>\n </div>\n }\n </th>\n }\n @if (\n canUseVirtualColumns() &&\n virtualRightSpacerWidth() > 0 &&\n footer.column.id === lastVirtualCenterColumnId()\n ) {\n <th class=\"pointer-events-none border-0 p-0\" [style.width.px]=\"virtualRightSpacerWidth()\"></th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().pagination?.enabled !== false) {\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"truncate text-sm text-gray-500\">\n {{ 'i18n_z_ui_table_total_rows' | translate: { total: (paginationTotal() | zFormatNum) } }}\n </div>\n <z-pagination\n [zTotal]=\"paginationTotal()\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || isLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Floating Bulk Action Bar -->\n<div class=\"z-bulk-action-bar-origin\" cdkOverlayOrigin #bulkBarOrigin=\"cdkOverlayOrigin\"></div>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"bulkBarOrigin\"\n [cdkConnectedOverlayOpen]=\"bulkBarMounted()\"\n [cdkConnectedOverlayPositions]=\"bulkBarPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n cdkConnectedOverlayPanelClass=\"z-bulk-action-bar-overlay\">\n @if (bulkBarConfig(); as config) {\n <div class=\"z-bulk-action-bar-inner\" [class.z-leaving]=\"bulkBarClosing()\">\n <div class=\"z-bulk-action-bar-count\">\n {{ bulkBarContext()?.selectedRowIds?.length ?? 0 | zFormatNum }}\n {{ config.selectedLabel ?? ('i18n_z_ui_table_selected' | translate) }}\n </div>\n\n <div class=\"z-bulk-action-bar-divider\"></div>\n\n @for (item of bulkBarItems(); track item.action.key) {\n <button\n type=\"button\"\n z-button\n zSize=\"sm\"\n [zType]=\"item.buttonType\"\n [zDisabled]=\"item.disabled\"\n class=\"z-bulk-action-bar-button\"\n [disabled]=\"item.disabled\"\n (click)=\"onBulkActionClick(item.action)\">\n @if (item.action.icon) {\n <z-icon [zType]=\"item.action.icon\" [zSize]=\"item.action.iconSize ?? '14'\" />\n }\n @if (item.action.label) {\n <span>{{ item.action.label | translate }}</span>\n }\n </button>\n }\n </div>\n }\n</ng-template>\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n [zTitle]=\"'i18n_z_ui_table_settings_title' | translate\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n [zCancelText]=\"'i18n_z_ui_drawer_close' | translate\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_display_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_display_settings_desc' | translate }}</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n [zChecked]=\"showHeaderFooterShadow()\"\n [zText]=\"'i18n_z_ui_table_header_footer_shadow' | translate\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n <!-- T\u1EA1m t\u1EAFt \u0111i\u1EC1u ki\u1EC7n: @if (zConfig().enableSettings) -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">{{ 'i18n_z_ui_table_column_settings' | translate }}</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">{{ 'i18n_z_ui_table_column_settings_desc' | translate }}</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div\n cdkDropList\n #columnDropList=\"cdkDropList\"\n [cdkDropListAutoScrollDisabled]=\"true\"\n (cdkDropListDropped)=\"onPendingColumnDrop($event)\"\n class=\"z-column-drop-list space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select' && columnId !== 'rowDrag') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragDisabled]=\"isGroupedColumn\"\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n cdkDragPreviewContainer=\"global\"\n cdkDragPreviewClass=\"z-drag-preview\"\n class=\"z-drag-item border-border bg-card hover:border-primary flex cursor-default items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon\n cdkDragHandle\n zType=\"lucideGripVertical\"\n zSize=\"14\"\n class=\"text-muted-foreground shrink-0\"\n [class.cursor-pointer]=\"!isGroupedColumn\"\n [class.cursor-default]=\"isGroupedColumn\"\n [class.opacity-40]=\"isGroupedColumn\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n (mousedown)=\"$event.stopPropagation()\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">({{ parents | translate }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\" (mousedown)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n [disabled]=\"!isVisible || isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">\n {{ 'i18n_z_ui_table_pinned_columns' | translate }}\n </h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = pendingVisibleColumns().includes(columnId);\n @let isVisibilityDisabled = pendingColumnVisibilityDisabledMap()[columnId];\n @let isGroupedColumn = groupingColumnMap()[columnId];\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n [disabled]=\"isVisibilityDisabled\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-default rounded disabled:opacity-50\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns | translate }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[0.625rem]\">\n ({{ pinnedParents | translate }})\n </span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[0.625rem] font-medium\">\n {{\n isPinned === 'left' ? ('i18n_z_ui_table_left' | translate) : ('i18n_z_ui_table_right' | translate)\n }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n [disabled]=\"isGroupedColumn\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow-x:visible;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border);--z-cell-selection-background: color-mix(in srgb, var(--primary) 12%, transparent)}.z-table-container.z-column-resizing table,.z-table-toolbar{min-width:0}.z-bulk-action-bar-origin{position:fixed;left:50%;bottom:1rem;width:1px;height:1px;pointer-events:none}:host ::ng-deep .z-bulk-action-bar-overlay{max-width:calc(100vw - 2rem);margin-bottom:1rem;transform-origin:center bottom}.z-bulk-action-bar-inner{border:thin solid color-mix(in srgb,var(--border) 88%,transparent);border-radius:.4rem;background:color-mix(in srgb,var(--card) 96%,var(--background));color:var(--foreground);box-shadow:0 1rem 2.5rem color-mix(in srgb,var(--foreground) 20%,transparent),0 .25rem .75rem color-mix(in srgb,var(--foreground) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--background) 65%,transparent) inset;display:flex;align-items:center;gap:.5rem;width:max-content;min-height:2.25rem;max-width:calc(100vw - 2rem);padding:.5rem;transform-origin:center bottom;animation:z-bulk-action-bar-enter .18s cubic-bezier(.16,1,.3,1);will-change:opacity,transform}.z-bulk-action-bar-inner.z-leaving{pointer-events:none;animation:z-bulk-action-bar-exit .14s ease-in forwards}.z-bulk-action-bar-count{padding:0 .25rem;white-space:nowrap;font-size:.8125rem;font-weight:500;color:var(--foreground)}.z-bulk-action-bar-divider{width:1px;height:1.5rem;flex:none;background:color-mix(in srgb,var(--border) 88%,transparent)}.z-bulk-action-bar-button{flex:none}@keyframes z-bulk-action-bar-enter{0%{opacity:0;transform:translateY(.875rem)}to{opacity:1;transform:translateY(0)}}@keyframes z-bulk-action-bar-exit{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(.875rem)}}@media(prefers-reduced-motion:reduce){.z-bulk-action-bar-inner{animation:none}}:host ::ng-deep .z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;-webkit-user-select:text;user-select:text}:host ::ng-deep .z-table-cell-text>*,:host ::ng-deep .z-table-cell-text *{display:inline-block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}.z-template-content{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.z-template-content>*{min-width:0;max-width:100%}.z-template-content>[class*=flex]{min-width:0;max-width:100%}.z-template-content>[class*=flex]>*{min-width:0;flex-shrink:1}.z-template-content>[class*=grid]{min-width:0;max-width:100%}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%}.z-sub-table-container{padding:.75rem 1rem .75rem 3rem;background-color:var(--background)}.z-table-content-trigger{display:block;width:100%;min-width:0;min-height:1.5rem;padding:0;border:0;border-radius:0;background:transparent;color:inherit;font:inherit;line-height:1.5rem;text-align:inherit;outline:0;cursor:text}.z-table-content-trigger:hover{color:var(--primary)}.z-content-edit-active,td:has(>.z-table-content-trigger:focus-visible){position:relative;z-index:2}.z-content-edit-active:after,td:has(>.z-table-content-trigger:focus-visible):after{position:absolute;z-index:3;inset:0;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-cell-selectable{-webkit-user-select:none;user-select:none}td.z-cell-range-selected{position:relative;isolation:isolate}td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);content:\"\";pointer-events:none}td.z-cell-range-selected>*{position:relative;z-index:1}td.z-cell-range-top:before{border-top:1px solid var(--primary)}td.z-cell-range-right:before{border-right:1px solid var(--primary)}td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}td.z-cell-range-left:before{border-left:1px solid var(--primary)}td.z-cell-active{position:relative;z-index:3;background-color:var(--z-cell-selection-background)!important}td.z-cell-active:after{position:absolute;z-index:4;inset:-1px;border:1px solid var(--primary);content:\"\";pointer-events:none}td.z-content-edit-active{-webkit-user-select:text;user-select:text}td.z-cell-active.z-row-hover,td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}.z-tfoot-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-column-menu-item{line-height:1.25rem}.z-column-menu-item:focus-visible,.z-column-menu-item.z-popover-open{background-color:var(--muted);outline:none}.z-table-drag-preview-row{border:0;border-radius:.125rem;background-color:var(--background);box-shadow:0 8px 20px #0000001f,inset 0 0 0 1px color-mix(in srgb,var(--primary) 45%,var(--border))}.z-table-drag-preview-row table,.z-table-drag-preview-row tbody,.z-table-drag-preview-row tr{height:100%}.z-table-drag-preview-row td{vertical-align:middle!important;background-color:var(--background)}[data-z-table-drag-handle]{touch-action:none;-webkit-user-select:none;user-select:none}.z-table-drag-placeholder{width:100%;min-height:2.625rem;box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background));pointer-events:none}.z-table-drag-placeholder-row>td{padding:0!important;border:0!important;background:transparent!important}.z-table-drag-placeholder-row .z-table-drag-placeholder{border-radius:0;background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.z-table-drag-placeholder-virtual{position:absolute;top:0;left:0;z-index:20;border-radius:0}.z-tbody-wrapper tr.z-group-row td{background:var(--card);border-top:none;border-bottom:1px solid var(--border)}.z-virtual-row tr.z-group-row td{background:var(--card);border-top:none;border-bottom:none}.z-group-row-sticky-wrapper{display:inline-flex;align-items:center;translate:var(--z-scroll-x, 0px) 0}.z-table-container:not(.z-hide-horizontal-border) .z-virtual-row:after{content:\"\";position:absolute;right:0;bottom:0;left:0;z-index:3;height:1px;background:var(--border);pointer-events:none}.z-tbody-wrapper tr.z-group-child-row td.z-at-left-edge,.z-virtual-row tr.z-group-child-row td.z-at-left-edge{border-left:2px solid var(--primary)!important;padding-left:var(--z-group-content-indent, .75rem)}.z-group-row-trigger{display:inline-flex;min-height:2rem;max-width:100%;align-items:center;gap:.5rem;padding:.25rem .5rem .25rem .75rem;color:var(--primary);font-size:.8125rem;font-weight:600;vertical-align:middle;cursor:pointer}.z-group-row-remove{margin-left:.125rem;color:var(--muted-foreground);vertical-align:middle}.z-group-row-remove:hover,.z-group-row-remove:focus-visible{color:var(--destructive);background:color-mix(in srgb,var(--destructive) 10%,transparent);outline:none}.z-group-row-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.z-group-row-column-label{color:color-mix(in srgb,var(--primary) 65%,var(--foreground));font-weight:500;margin-right:.25rem}.z-group-row-count{display:inline-flex;min-width:1.25rem;height:1.25rem;align-items:center;justify-content:center;padding:0 .375rem;border-radius:999px;background:var(--primary);color:var(--primary-foreground);font-size:.6875rem;font-weight:600}.z-group-row-loading{display:inline-flex;align-items:center;margin-left:.25rem}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{position:absolute!important;z-index:0!important;box-sizing:border-box!important;top:var(--z-cell-range-top, 0)!important;right:var(--z-cell-range-right, 0)!important;bottom:var(--z-cell-range-bottom, 0)!important;left:var(--z-cell-range-left, 0)!important;width:auto!important;background-color:var(--z-cell-selection-background)!important;box-shadow:none!important;content:\"\"!important;pointer-events:none!important;opacity:1!important}:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:after{display:none!important}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-top:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-top:before{border-top:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-right:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-right:before{border-right:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-bottom:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-left:before,:host .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-right-first.z-cell-range-selected:before,:host-context(.z-table-pointer-dragging) .z-tbody-wrapper td.z-sticky-left-last.z-cell-range-selected:before{background-color:transparent!important}:host td.z-cell-range-selected:before{position:absolute;z-index:0;box-sizing:border-box;top:var(--z-cell-range-top, 0);right:var(--z-cell-range-right, 0);bottom:var(--z-cell-range-bottom, 0);left:var(--z-cell-range-left, 0);background-color:var(--z-cell-selection-background);box-shadow:none;content:\"\";pointer-events:none;opacity:1}:host td.z-cell-range-top:before{border-top:1px solid var(--primary)}:host td.z-cell-range-right:before{border-right:1px solid var(--primary)}:host td.z-cell-range-bottom:before{border-bottom:1px solid var(--primary)}:host td.z-cell-range-left:before{border-left:1px solid var(--primary)}:host-context(.z-table-pointer-dragging) td.z-row-hover{background-color:var(--background)!important}:host{display:flex;flex-direction:column;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px;--z-shadow-left-right: -1.875rem;--z-shadow-left-width: 1.875rem;--z-shadow-left-opacity: 0;--z-shadow-right-left: -1.875rem;--z-shadow-right-width: 1.875rem;--z-shadow-right-opacity: 0;--z-sticky-left-border-color: transparent;--z-sticky-right-border-color: var(--border)}.z-table-container{display:flex;flex-direction:column;position:relative;box-sizing:border-box;contain:inline-size;width:100%;max-width:100%;min-width:0;height:100%;overflow:hidden;border-radius:.3125rem;border:thin solid var(--border);background-color:var(--card)}.z-table-container:focus{outline:none}.z-table-container.z-table-borderless{border:none;border-radius:0;box-shadow:none!important;background-color:transparent}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:.875rem}.z-table-toolbar{min-width:0}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-thead-wrapper{flex-shrink:0;width:100%;max-width:100%;min-width:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-thead-wrapper th{height:auto;padding:.5rem;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;width:100%;max-width:100%;min-width:0;min-height:6.25rem;display:flex;flex-direction:column;overflow:hidden}.z-tbody-wrapper{flex:1;display:flex;flex-direction:column;min-height:0;width:100%}.z-tbody-scrollbar{flex:1;width:100%;max-width:100%;min-width:0;height:100%;transition:opacity .2s ease-in-out}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:6.25rem;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:.5rem 12px;height:var(--z-row-height, 2.375rem);vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper tbody.z-has-vertical-scroll td.z-at-bottom,.z-tbody-wrapper tbody.z-last-row-touches-bottom td.z-at-bottom{border-bottom-color:transparent}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:.75rem}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:.5rem 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{border-left:.125rem solid var(--primary)!important;padding-left:.75rem!important}tbody tr.z-context-menu-active,tbody tr.z-context-menu-active td{background-color:var(--muted)!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,var(--background))!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,var(--background))!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,var(--background))!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td{background-color:var(--muted)!important}tr.z-expanded-row>td{border-left:.125rem solid var(--primary);background-color:var(--background)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:.5rem;background:transparent;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:5}thead th .z-resizer:after{content:\"\";position:absolute;right:0;top:0;height:100%;width:.1875rem;background:#0000001a;opacity:0;transition:opacity .2s ease}thead th .z-resizer:after:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-is-resizing:after{opacity:1;background:var(--primary);width:.1875rem}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th .z-resizer.z-resizer-left:after{right:auto;left:0}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible;border-right:thin solid var(--z-sticky-left-border-color)}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:var(--z-shadow-left-right);width:var(--z-shadow-left-width);pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-left-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-thead-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tbody-wrapper td.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-left-last:after,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-left-last:after{box-shadow:inset 10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-left,.z-tbody-wrapper.z-scroll-left,.z-tfoot-wrapper.z-scroll-left{--z-shadow-left-opacity: 1}.z-thead-wrapper.z-scroll-left:where(.dark,.dark *),.z-tbody-wrapper.z-scroll-left:where(.dark,.dark *),.z-tfoot-wrapper.z-scroll-left:where(.dark,.dark *){--z-sticky-left-border-color: var(--border)}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible;border-left:thin solid var(--z-sticky-right-border-color)}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:var(--z-shadow-right-left);width:var(--z-shadow-right-width);pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:var(--z-shadow-right-opacity)}:host-context(.dark) .z-thead-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-thead-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tbody-wrapper td.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper th.z-sticky-right-first:before,:host-context(.dark) .z-tfoot-wrapper td.z-sticky-right-first:before{box-shadow:inset -10px 0 10px -8px #0000004d}.z-thead-wrapper.z-scroll-right,.z-tbody-wrapper.z-scroll-right,.z-tfoot-wrapper.z-scroll-right{--z-shadow-right-opacity: 1}.z-thead-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tbody-wrapper.z-scroll-right:not(:where(.dark,.dark *)),.z-tfoot-wrapper.z-scroll-right:not(:where(.dark,.dark *)){--z-sticky-right-border-color: transparent}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1.875rem;width:1.875rem;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:hidden;overflow-y:hidden;touch-action:pan-y pinch-zoom}.z-tfoot-wrapper th{height:auto;padding:.5rem 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:.75rem;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:.125rem 4px;border-radius:.25rem;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th .z-header-text-wrapper{transition:background-color .15s ease;border-radius:.25rem;min-width:0;overflow:hidden;flex-shrink:1}th .z-header-text-wrapper.z-has-options:hover,th .z-header-text-wrapper.z-has-options:focus-visible,th .z-header-text-wrapper.z-has-options.z-popover-open{background-color:color-mix(in srgb,var(--foreground) 8%,transparent);outline:none}th .z-header-text-wrapper.z-has-options:active{background-color:color-mix(in srgb,var(--foreground) 12%,transparent)}.cdk-drag-preview,.z-drag-preview{box-sizing:border-box;border:1px solid var(--primary);border-radius:.375rem;background-color:var(--card);box-shadow:0 5px 20px #0003;overflow:hidden;pointer-events:none;z-index:10000!important}.cdk-drag-preview:where(.dark,.dark *),.z-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.cdk-drag-placeholder{box-sizing:border-box;border:2px dashed var(--primary);border-radius:.375rem;background-color:color-mix(in srgb,var(--primary) 10%,var(--background))}.cdk-drag-animating,.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .1s cubic-bezier(0,0,.2,1)}.z-drag-item.cdk-drag-dragging{transition:none!important}.z-table-drag-preview{box-sizing:border-box;border-radius:.375rem;background-color:var(--card);border:1px solid var(--primary);box-shadow:0 10px 24px #00000029;overflow:hidden;z-index:10000!important;pointer-events:none}.z-table-drag-preview:where(.dark,.dark *){box-shadow:0 5px 20px #00000080}.z-table-drag-source{display:none!important}.z-virtual-row.z-table-drag-source{display:block!important;pointer-events:none;outline:1px solid var(--border);outline-offset:-1px}.z-virtual-row.z-table-drag-source td{color:color-mix(in srgb,var(--foreground) 45%,transparent);border-color:var(--border)!important;background-color:var(--card)!important}.z-virtual-row.z-table-drag-source td>*{opacity:.45}.z-column-drop-list{min-height:3.125rem;overscroll-behavior:contain}.z-column-drop-list.cdk-drop-list-dragging{overflow:clip}:host td.z-cell-active,:host td.z-cell-active.z-row-hover{background-color:var(--z-cell-selection-background)!important}:host td.z-content-edit-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected,:host-context(.z-table-pointer-dragging) td.z-cell-range-selected.z-row-hover,:host-context(.z-table-pointer-dragging) td.z-cell-active,:host-context(.z-table-pointer-dragging) td.z-cell-active.z-row-hover{background-color:var(--card)!important}:host-context(.z-table-pointer-dragging) td.z-cell-range-selected:before{background-color:transparent}:host .z-tbody-wrapper tbody tr.z-group-child-row:has(+tr.z-group-row)>td.z-at-bottom{border-bottom-color:var(--border)}:host .z-virtual-row:has(tr.z-group-child-row):has(+.z-virtual-row tr.z-group-row) tr.z-group-child-row>td.z-at-bottom{border-bottom-color:var(--border)}.z-virtual-row td.z-virtual-column-spacer{height:auto}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 .125rem .125rem 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
9632
9606
  }], ctorParameters: () => [], propDecorators: { zChange: [{ type: i0.Output, args: ["zChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }], zClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "zClass", required: false }] }], zConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zConfig", required: false }] }], zLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLoading", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], zVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "zVariant", required: false }] }], theadWrapper: [{ type: i0.ViewChild, args: ['theadWrapper', { isSignal: true }] }], tableContainer: [{ type: i0.ViewChild, args: ['tableContainer', { isSignal: true }] }], tbodyContainer: [{ type: i0.ViewChild, args: ['tbodyContainer', { isSignal: true }] }], tbodyWrapper: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tbodyScrollbar: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tfootWrapper: [{ type: i0.ViewChild, args: ['tfootWrapper', { isSignal: true }] }], expandedRowTemplate: [{ type: i0.ViewChild, args: ['expandedRowTemplate', { isSignal: true }] }], virtualRowElements: [{ type: i0.ViewChildren, args: ['virtualRow', { isSignal: true }] }] } });
9633
9607
 
9634
9608
  /**