apexgantt 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,6 +67,7 @@ The layout can be configured by either setting the properties in the table below
67
67
  | barTextColor | `#FFFFFF` | Text color for timeline bar |
68
68
  | cellBorderColor | `#eff0f0` | Border color for all table cells and timeline cells |
69
69
  | cellBorderWidth | `1px` | Border width for all table cells and timeline cells |
70
+ | columnConfig | `undefined` | Custom column widths |
70
71
  | enableToolbar | `false` | Enable/disable graph toolbar |
71
72
  | enableResize | `true` | Enable/disable gantt sidebar resize |
72
73
  | enableExport | `true` | Enable/disable gantt export options |
@@ -185,6 +186,44 @@ Each tasks should be in below format
185
186
  ];
186
187
  ```
187
188
 
189
+ ## Column Configuration
190
+
191
+ Customize task table column widths:
192
+
193
+ ```js
194
+ import {ColumnKey} from 'apexgantt';
195
+
196
+ const gantt = new ApexGantt(element, {
197
+ series: tasks,
198
+ columnConfig: [
199
+ {
200
+ key: ColumnKey.Name,
201
+ title: 'Task Name',
202
+ minWidth: '100px',
203
+ flexGrow: 3,
204
+ },
205
+ {
206
+ key: ColumnKey.StartTime,
207
+ title: 'Start',
208
+ minWidth: '100px',
209
+ flexGrow: 1.5,
210
+ },
211
+ {
212
+ key: ColumnKey.Duration,
213
+ title: 'Duration',
214
+ minWidth: '80px',
215
+ flexGrow: 1,
216
+ },
217
+ {
218
+ key: ColumnKey.Progress,
219
+ title: 'Progress',
220
+ minWidth: '80px',
221
+ flexGrow: 1,
222
+ },
223
+ ],
224
+ });
225
+ ```
226
+
188
227
  ## Data Parsing
189
228
 
190
229
  Map your existing data structure to ApexGantt format without manual transformation.
@@ -5965,6 +5965,13 @@ const _Watermark = class _Watermark {
5965
5965
  _Watermark.WATERMARK_CLASS = "apexgantt-watermark";
5966
5966
  _Watermark.WATERMARK_TEXT = "Powered by apexcharts.com";
5967
5967
  let Watermark = _Watermark;
5968
+ function getParentElement(element) {
5969
+ const rootNode = element.getRootNode();
5970
+ if (rootNode instanceof ShadowRoot) {
5971
+ return rootNode.host;
5972
+ }
5973
+ return element.parentElement;
5974
+ }
5968
5975
  function getCumulativeTransform(element) {
5969
5976
  let scaleX = 1;
5970
5977
  let scaleY = 1;
@@ -5996,7 +6003,7 @@ function getCumulativeTransform(element) {
5996
6003
  console.warn("Failed to parse transform matrix:", transform2, e);
5997
6004
  }
5998
6005
  }
5999
- currentElement = currentElement.parentElement;
6006
+ currentElement = getParentElement(currentElement);
6000
6007
  }
6001
6008
  return { scaleX, scaleY, translateX, translateY };
6002
6009
  }
@@ -13903,7 +13910,8 @@ const ColumnList = [
13903
13910
  "name"
13904
13911
  /* Name */
13905
13912
  ],
13906
- width: "160px"
13913
+ minWidth: "120px",
13914
+ flexGrow: 3
13907
13915
  },
13908
13916
  {
13909
13917
  key: "startTime",
@@ -13911,7 +13919,8 @@ const ColumnList = [
13911
13919
  "startTime"
13912
13920
  /* StartTime */
13913
13921
  ],
13914
- width: "100px"
13922
+ minWidth: "70px",
13923
+ flexGrow: 1.5
13915
13924
  },
13916
13925
  {
13917
13926
  key: "duration",
@@ -13919,7 +13928,8 @@ const ColumnList = [
13919
13928
  "duration"
13920
13929
  /* Duration */
13921
13930
  ],
13922
- width: "80px"
13931
+ minWidth: "50px",
13932
+ flexGrow: 1
13923
13933
  },
13924
13934
  {
13925
13935
  key: "progress",
@@ -13927,9 +13937,17 @@ const ColumnList = [
13927
13937
  "progress"
13928
13938
  /* Progress */
13929
13939
  ],
13930
- width: "80px"
13940
+ minWidth: "50px",
13941
+ flexGrow: 1
13931
13942
  }
13932
13943
  ];
13944
+ function generateGridTemplateColumns(columns) {
13945
+ return columns.map((col) => {
13946
+ const minWidth = col.minWidth || "30px";
13947
+ const flexGrow = col.flexGrow || 1;
13948
+ return `minmax(${minWidth}, ${flexGrow}fr)`;
13949
+ }).join(" ");
13950
+ }
13933
13951
  function getTaskTextByColumn(task, columnKey, inputDateFormat) {
13934
13952
  const taskColumnValue = task[columnKey];
13935
13953
  if (columnKey === "startTime") {
@@ -14057,6 +14075,40 @@ class Tasks {
14057
14075
  this.options = options;
14058
14076
  this.chartContext = chartContext;
14059
14077
  this.dataManager = dataManager;
14078
+ this.effectiveColumnList = this.mergeColumnConfig();
14079
+ this.injectDynamicColumnStyles();
14080
+ }
14081
+ /**
14082
+ * Merges user column config with defaults
14083
+ */
14084
+ mergeColumnConfig() {
14085
+ if (!this.options.columnConfig || this.options.columnConfig.length === 0) {
14086
+ return ColumnList;
14087
+ }
14088
+ return ColumnList.map((defaultCol) => {
14089
+ var _a;
14090
+ const userCol = (_a = this.options.columnConfig) == null ? void 0 : _a.find((col) => col.key === defaultCol.key);
14091
+ return userCol ? { ...defaultCol, ...userCol } : defaultCol;
14092
+ });
14093
+ }
14094
+ /**
14095
+ * Dynamic CSS for column widths based on configuration
14096
+ */
14097
+ injectDynamicColumnStyles() {
14098
+ const gridTemplate = generateGridTemplateColumns(this.effectiveColumnList);
14099
+ const chartInstanceId = this.chartContext.getInstanceId();
14100
+ const dynamicStyles = `
14101
+ .tasks-container .tasks-header[data-chart-instance="${chartInstanceId}"] .tasks-header-row {
14102
+ grid-template-columns: ${gridTemplate};
14103
+ }
14104
+
14105
+ .tasks-container .tasks-data-row[data-chart-instance="${chartInstanceId}"] {
14106
+ grid-template-columns: ${gridTemplate};
14107
+ }
14108
+ `;
14109
+ this.chartContext.injectStyles(dynamicStyles, `tasks-dynamic-columns-${chartInstanceId}`, {
14110
+ priority: "high"
14111
+ });
14060
14112
  }
14061
14113
  generateBody(tasks, reRender) {
14062
14114
  const bodyContainer = createBox(this.chartContext, { className: "tasks-data-container" });
@@ -14069,19 +14121,13 @@ class Tasks {
14069
14121
  const { headerBackground, rowHeight, fontColor } = this.options;
14070
14122
  const chartInstanceId = this.chartContext.getInstanceId();
14071
14123
  headerContainer.setAttribute("data-chart-instance", chartInstanceId);
14072
- forEach(headerList, (header, index) => {
14073
- const columnDef = ColumnList[index];
14124
+ forEach(headerList, (header) => {
14074
14125
  const headerCell = createBox(this.chartContext, {
14075
14126
  className: "tasks-header-cell",
14076
14127
  content: header
14077
14128
  });
14078
14129
  headerCell.style.height = `${rowHeight * 2}px`;
14079
14130
  headerCell.style.color = fontColor;
14080
- if (columnDef == null ? void 0 : columnDef.width) {
14081
- headerCell.style.width = columnDef.width;
14082
- headerCell.style.minWidth = columnDef.width;
14083
- headerCell.style.flexBasis = columnDef.width;
14084
- }
14085
14131
  headerRow.append(headerCell);
14086
14132
  });
14087
14133
  headerContainer.append(headerRow);
@@ -14095,7 +14141,7 @@ class Tasks {
14095
14141
  row.setAttribute("data-taskid", task.id);
14096
14142
  row.setAttribute("data-chart-instance", chartInstanceId);
14097
14143
  row.style.height = `${rowHeight}px`;
14098
- forEach(ColumnList, ({ key, width: width2 }) => {
14144
+ forEach(this.effectiveColumnList, ({ key }) => {
14099
14145
  const cell = createBox(this.chartContext, {
14100
14146
  className: "tasks-data-cell",
14101
14147
  content: getTaskTextByColumn(task, key, this.options.inputDateFormat)
@@ -14104,11 +14150,6 @@ class Tasks {
14104
14150
  cell.setAttribute("data-chart-instance", chartInstanceId);
14105
14151
  cell.style.height = `${rowHeight}px`;
14106
14152
  cell.style.color = fontColor;
14107
- if (width2) {
14108
- cell.style.width = width2;
14109
- cell.style.minWidth = width2;
14110
- cell.style.flexBasis = width2;
14111
- }
14112
14153
  if (key === ColumnKey.Name) {
14113
14154
  cell.style.paddingLeft = `${task.level * 15}px`;
14114
14155
  cell.style.textAlign = "left";
@@ -14167,7 +14208,7 @@ class Tasks {
14167
14208
  row.setAttribute("data-taskid", `empty-${index}`);
14168
14209
  row.setAttribute("data-chart-instance", chartInstanceId);
14169
14210
  row.style.height = `${rowHeight}px`;
14170
- forEach(ColumnList, ({ key, width: width2 }) => {
14211
+ forEach(this.effectiveColumnList, ({ key }) => {
14171
14212
  const cell = createBox(this.chartContext, {
14172
14213
  className: "tasks-data-cell",
14173
14214
  content: ""
@@ -14176,17 +14217,12 @@ class Tasks {
14176
14217
  cell.setAttribute("data-chart-instance", chartInstanceId);
14177
14218
  cell.style.height = `${rowHeight}px`;
14178
14219
  cell.style.color = fontColor;
14179
- if (width2) {
14180
- cell.style.width = width2;
14181
- cell.style.minWidth = width2;
14182
- cell.style.flexBasis = width2;
14183
- }
14184
14220
  row.append(cell);
14185
14221
  });
14186
14222
  return row;
14187
14223
  }
14188
14224
  render(reRender) {
14189
- const headerRow = this.generateHeader(ColumnList.map((col) => col.title));
14225
+ const headerRow = this.generateHeader(this.effectiveColumnList.map((col) => col.title));
14190
14226
  const dataRows = this.generateBody(this.dataManager.getFlatVisibleTasks(), reRender);
14191
14227
  return [headerRow, dataRows];
14192
14228
  }
@@ -15293,7 +15329,7 @@ const TableStyle = `
15293
15329
  }
15294
15330
 
15295
15331
  .tasks-header-row {
15296
- display: flex;
15332
+ display: grid;
15297
15333
  width: 100%;
15298
15334
  }
15299
15335
 
@@ -15308,7 +15344,6 @@ const TableStyle = `
15308
15344
  border: var(--cell-border-width, 1px) solid var(--cell-border-color, #eff0f0);
15309
15345
  color: var(--text-color, #000);
15310
15346
  box-sizing: border-box;
15311
- flex-shrink: 0;
15312
15347
  font-weight: 600;
15313
15348
  }
15314
15349
 
@@ -15325,7 +15360,7 @@ const TableStyle = `
15325
15360
  }
15326
15361
 
15327
15362
  .tasks-data-row {
15328
- display: flex;
15363
+ display: grid;
15329
15364
  width: 100%;
15330
15365
  border-bottom: var(--cell-border-width, 1px) solid var(--cell-border-color, #eff0f0);
15331
15366
  box-sizing: border-box;
@@ -15343,7 +15378,6 @@ const TableStyle = `
15343
15378
  border-right: var(--cell-border-width, 1px) solid var(--cell-border-color, #eff0f0);
15344
15379
  color: var(--text-color, #000);
15345
15380
  box-sizing: border-box;
15346
- flex-shrink: 0;
15347
15381
  }
15348
15382
 
15349
15383
  .tasks-data-row .tasks-data-cell:first-child {
@@ -16240,7 +16274,7 @@ class SplitView {
16240
16274
 
16241
16275
  .split-view-container .split-left-container {
16242
16276
  flex-grow: 0 !important;
16243
- flex-shrink: 0 !important;
16277
+ flex-shrink: 1 !important;
16244
16278
  overflow: visible;
16245
16279
  display: flex !important;
16246
16280
  flex-direction: column;
@@ -16398,8 +16432,8 @@ class SplitView {
16398
16432
  if (!containerRect)
16399
16433
  return;
16400
16434
  const newWidth = moveEvent.clientX - containerRect.left;
16401
- const minWidth = 50;
16402
- const maxWidth = containerRect.width - 100;
16435
+ const minWidth = 0;
16436
+ const maxWidth = containerRect.width - 50;
16403
16437
  const clampedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
16404
16438
  this.leftContainer.style.flexBasis = `${clampedWidth}px`;
16405
16439
  this.dispatchResizeEvent();
@@ -16428,25 +16462,29 @@ class SplitView {
16428
16462
  switch (e.key) {
16429
16463
  case "ArrowLeft":
16430
16464
  e.preventDefault();
16431
- newWidth = Math.max(50, currentWidth - step);
16465
+ newWidth = Math.max(0, currentWidth - step);
16432
16466
  this.leftContainer.style.flexBasis = `${newWidth}px`;
16467
+ this.dispatchResizeEvent();
16433
16468
  break;
16434
16469
  case "ArrowRight":
16435
16470
  e.preventDefault();
16436
16471
  newWidth = currentWidth + step;
16437
16472
  this.leftContainer.style.flexBasis = `${newWidth}px`;
16473
+ this.dispatchResizeEvent();
16438
16474
  break;
16439
16475
  case "Home":
16440
16476
  e.preventDefault();
16441
- this.leftContainer.style.flexBasis = "50px";
16477
+ this.leftContainer.style.flexBasis = "0px";
16478
+ this.dispatchResizeEvent();
16442
16479
  break;
16443
16480
  case "End": {
16444
16481
  e.preventDefault();
16445
16482
  const container = this.splitBarContainer.parentElement;
16446
16483
  if (container) {
16447
- const maxWidth = container.clientWidth - 100;
16484
+ const maxWidth = container.clientWidth - 50;
16448
16485
  this.leftContainer.style.flexBasis = `${maxWidth}px`;
16449
16486
  }
16487
+ this.dispatchResizeEvent();
16450
16488
  break;
16451
16489
  }
16452
16490
  }
@@ -16714,6 +16752,9 @@ class ApexGantt extends BaseChart {
16714
16752
  this.isSyncingScroll = false;
16715
16753
  this.scrollbarResizeObserver = null;
16716
16754
  this.splitBarResizeHandler = null;
16755
+ this.containerResizeObserver = null;
16756
+ this.lastKnownWidth = 0;
16757
+ this.resizeDebounceTimer = null;
16717
16758
  const themeDefaults = getDefaultOptions(options == null ? void 0 : options.theme);
16718
16759
  let processedSeries;
16719
16760
  if (options == null ? void 0 : options.parsing) {
@@ -16951,18 +16992,13 @@ class ApexGantt extends BaseChart {
16951
16992
  const existingDimensions = this.hasExplicitDimensions();
16952
16993
  const normalizedHeight = this.normalizeDimension(height2);
16953
16994
  const normalizedWidth = this.normalizeDimension(width2);
16995
+ this.element.style.width = normalizedWidth;
16954
16996
  if (normalizedHeight === "100%" && existingDimensions.height) {
16955
16997
  const computedHeight = window.getComputedStyle(this.element).height;
16956
16998
  this.element.style.height = computedHeight;
16957
16999
  } else {
16958
17000
  this.element.style.height = normalizedHeight;
16959
17001
  }
16960
- if (normalizedWidth === "100%" && existingDimensions.width) {
16961
- const computedWidth = window.getComputedStyle(this.element).width;
16962
- this.element.style.width = computedWidth;
16963
- } else {
16964
- this.element.style.width = normalizedWidth;
16965
- }
16966
17002
  this.element.style.display = "flex";
16967
17003
  this.element.style.flexDirection = "column";
16968
17004
  this.element.style.boxSizing = "border-box";
@@ -16996,14 +17032,7 @@ class ApexGantt extends BaseChart {
16996
17032
  this.setupDependencyArrowEvents();
16997
17033
  this.renderDependencyArrows();
16998
17034
  requestAnimationFrame(() => {
16999
- this.syncTasksColumnWidths();
17000
- this.compensateForScrollbar();
17001
- this.updateHorizontalScrollbarContent();
17002
- this.setupTimelineHorizontalScroll();
17003
- this.positionHorizontalScrollbar();
17004
- this.setupScrollbarResizeObserver();
17005
- this.disableHeaderMousewheelScroll();
17006
- this.fillEmptyRowsAfterRender();
17035
+ this.performAfterActions();
17007
17036
  });
17008
17037
  if (isReRender) {
17009
17038
  requestAnimationFrame(() => {
@@ -17012,6 +17041,18 @@ class ApexGantt extends BaseChart {
17012
17041
  }
17013
17042
  return this.element;
17014
17043
  }
17044
+ performAfterActions() {
17045
+ this.syncTasksColumnWidths();
17046
+ this.compensateForScrollbar();
17047
+ this.updateHorizontalScrollbarContent();
17048
+ this.setupTimelineHorizontalScroll();
17049
+ this.positionHorizontalScrollbar();
17050
+ this.setupContainerResizeObserver();
17051
+ this.setupScrollbarResizeObserver();
17052
+ this.setupRowBackgroundColors();
17053
+ this.disableHeaderMousewheelScroll();
17054
+ this.fillEmptyRowsAfterRender();
17055
+ }
17015
17056
  /**
17016
17057
  * Setup proper positioning for chart container to support dialogs
17017
17058
  */
@@ -17350,15 +17391,7 @@ class ApexGantt extends BaseChart {
17350
17391
  }
17351
17392
  }
17352
17393
  requestAnimationFrame(() => {
17353
- this.syncTasksColumnWidths();
17354
- this.compensateForScrollbar();
17355
- this.updateHorizontalScrollbarContent();
17356
- this.setupTimelineHorizontalScroll();
17357
- this.positionHorizontalScrollbar();
17358
- this.setupScrollbarResizeObserver();
17359
- this.setupRowBackgroundColors();
17360
- this.disableHeaderMousewheelScroll();
17361
- this.fillEmptyRowsAfterRender();
17394
+ this.performAfterActions();
17362
17395
  });
17363
17396
  }
17364
17397
  updateToolbarAfterZoom() {
@@ -17635,7 +17668,7 @@ class ApexGantt extends BaseChart {
17635
17668
  row.setAttribute("data-taskid", `empty-${index}`);
17636
17669
  row.setAttribute("data-chart-instance", chartInstanceId);
17637
17670
  row.style.height = `${rowHeight}px`;
17638
- ColumnList.forEach(({ key, width: width2 }) => {
17671
+ ColumnList.forEach(({ key }) => {
17639
17672
  const cell = createBox(this.chartContext, {
17640
17673
  className: "tasks-data-cell",
17641
17674
  content: ""
@@ -17644,11 +17677,6 @@ class ApexGantt extends BaseChart {
17644
17677
  cell.setAttribute("data-chart-instance", chartInstanceId);
17645
17678
  cell.style.height = `${rowHeight}px`;
17646
17679
  cell.style.color = fontColor;
17647
- if (width2) {
17648
- cell.style.width = width2;
17649
- cell.style.minWidth = width2;
17650
- cell.style.flexBasis = width2;
17651
- }
17652
17680
  row.appendChild(cell);
17653
17681
  });
17654
17682
  return row;
@@ -17756,8 +17784,69 @@ class ApexGantt extends BaseChart {
17756
17784
  }
17757
17785
  return value;
17758
17786
  }
17787
+ /**
17788
+ * resize observer for container to handle responsive width changes
17789
+ */
17790
+ setupContainerResizeObserver() {
17791
+ if (!this.element) {
17792
+ return;
17793
+ }
17794
+ if (this.containerResizeObserver) {
17795
+ this.containerResizeObserver.disconnect();
17796
+ }
17797
+ const normalizedWidth = this.normalizeDimension(this.options.width);
17798
+ const isPercentageWidth = typeof normalizedWidth === "string" && normalizedWidth.includes("%");
17799
+ if (!isPercentageWidth) {
17800
+ return;
17801
+ }
17802
+ this.lastKnownWidth = this.element.offsetWidth;
17803
+ this.containerResizeObserver = new ResizeObserver((entries) => {
17804
+ for (const entry of entries) {
17805
+ const newWidth = entry.contentRect.width;
17806
+ if (Math.abs(newWidth - this.lastKnownWidth) > 1) {
17807
+ this.lastKnownWidth = newWidth;
17808
+ this.handleContainerResize();
17809
+ }
17810
+ }
17811
+ });
17812
+ this.containerResizeObserver.observe(this.element);
17813
+ }
17814
+ handleContainerResize() {
17815
+ if (this.resizeDebounceTimer !== null) {
17816
+ window.clearTimeout(this.resizeDebounceTimer);
17817
+ }
17818
+ this.resizeDebounceTimer = window.setTimeout(() => {
17819
+ this.performResize();
17820
+ this.resizeDebounceTimer = null;
17821
+ }, 150);
17822
+ }
17823
+ performResize() {
17824
+ if (!this.element || this.isDestroyed()) {
17825
+ return;
17826
+ }
17827
+ const normalizedWidth = this.normalizeDimension(this.options.width);
17828
+ if (typeof normalizedWidth === "string" && normalizedWidth.includes("%")) {
17829
+ const computedWidth = window.getComputedStyle(this.element.parentElement || this.element).width;
17830
+ const currentElementWidth = window.getComputedStyle(this.element).width;
17831
+ if (computedWidth !== currentElementWidth) {
17832
+ requestAnimationFrame(() => {
17833
+ this.positionHorizontalScrollbar();
17834
+ this.updateHorizontalScrollbarContent();
17835
+ this.syncTasksColumnWidths();
17836
+ });
17837
+ }
17838
+ }
17839
+ }
17759
17840
  destroy() {
17760
17841
  try {
17842
+ if (this.containerResizeObserver) {
17843
+ this.containerResizeObserver.disconnect();
17844
+ this.containerResizeObserver = null;
17845
+ }
17846
+ if (this.resizeDebounceTimer !== null) {
17847
+ window.clearTimeout(this.resizeDebounceTimer);
17848
+ this.resizeDebounceTimer = null;
17849
+ }
17761
17850
  if (this.zoomHandler) {
17762
17851
  const chartInstanceId = this.getInstanceId();
17763
17852
  const timelineElement = this.chartContext.querySelector(