vue2-excel-grid 2.5.1 → 2.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-excel-grid",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Excel-style grid component for Vue 2.6.14 with virtual scrolling",
5
5
  "license": "MIT",
6
6
  "author": "Fozilbek Raimberdiyev",
@@ -992,6 +992,7 @@ const SIZE_CONFIG = {
992
992
  const ROW_HEADER_WIDTH = 50
993
993
  const DEFAULT_COL_WIDTH = 100
994
994
  const OUTLINE_GUTTER = 20
995
+ const FIT_COLUMNS_MIN_WIDTH = 40
995
996
 
996
997
  // Classifies a col.width spec so resolveColWidth() knows how to turn it into a
997
998
  // concrete px number. Plain numbers/px strings resolve instantly with no DOM
@@ -1257,6 +1258,29 @@ const ExcelGridComponent = {
1257
1258
  /** Auto-fit all column widths to content (same as double-clicking each resize handle). Re-applies on data/columns change. */
1258
1259
  autoWidth: { type: Boolean, default: false },
1259
1260
 
1261
+ /**
1262
+ * Forces every visible leaf column to fit within a single viewport —
1263
+ * no horizontal scroll. Column widths are still resolved (`col.width`,
1264
+ * `%`/`px`/other CSS lengths, `DEFAULT_COL_WIDTH` when omitted) but only
1265
+ * as RELATIVE WEIGHTS: all resolved widths are scaled by one shared
1266
+ * factor so their sum exactly fills the available width, keeping each
1267
+ * column's proportion to the others. Each column still gets at least
1268
+ * `40px` — if there are enough columns that even that minimum doesn't
1269
+ * fit, horizontal scroll returns for that (rare) case rather than
1270
+ * shrinking columns illegibly thin.
1271
+ *
1272
+ * Highest-priority width control: overrides `col.width` as an absolute
1273
+ * value, `autoWidth`, manual column resize (drag handle — disabled
1274
+ * while this is on), and double-click auto-fit. Pinned columns count
1275
+ * toward the fit too — the goal is the whole row fitting, regardless
1276
+ * of pin state.
1277
+ * @type {Boolean}
1278
+ * @default false
1279
+ * @example
1280
+ * <excel-grid :columns="columns" v-model="data" fit-columns />
1281
+ */
1282
+ fitColumns: { type: Boolean, default: false },
1283
+
1260
1284
  /**
1261
1285
  * Allow columns to be reordered by dragging their headers.
1262
1286
  * On drop, emits 'update:columns' (compatible with .sync) and 'column-reorder'.
@@ -2409,6 +2433,26 @@ const ExcelGridComponent = {
2409
2433
  if (v) this._applyAutoWidth()
2410
2434
  },
2411
2435
 
2436
+ fitColumns(v) {
2437
+ if (v) {
2438
+ this._applyFitColumns()
2439
+ } else {
2440
+ // Back to normal per-column resolution (same as the columns
2441
+ // watcher's non-fit branch, without keyToWidth — fitColumns already
2442
+ // discarded any remembered manual-resize widths when it turned on).
2443
+ const newColWidths = {}
2444
+ const newRelativeSpecs = {}
2445
+ this.leafCols.forEach(col => {
2446
+ newColWidths[col._flatIndex] = this.resolveColWidth(col.width, this._relativeWidthContainer)
2447
+ const parsed = parseColWidthSpec(col.width)
2448
+ if (parsed.type !== 'px') newRelativeSpecs[col._flatIndex] = col.width
2449
+ })
2450
+ this.colWidths = newColWidths
2451
+ this.relativeColSpecs = newRelativeSpecs
2452
+ this._applyAutoWidth()
2453
+ }
2454
+ },
2455
+
2412
2456
  columns: {
2413
2457
  immediate: true,
2414
2458
  handler(cols) {
@@ -2431,21 +2475,30 @@ const ExcelGridComponent = {
2431
2475
  this.headerRows = buildHeaderRows(cols, this.headerDepth)
2432
2476
  this._maskCache = {}
2433
2477
 
2434
- // Rebuild colWidths by new flat index, using col.key to look up saved widths
2435
- const newColWidths = {}
2436
- const newRelativeSpecs = {}
2437
- this.leafCols.forEach(col => {
2438
- if (keyToWidth[col.key] !== undefined) {
2439
- newColWidths[col._flatIndex] = keyToWidth[col.key]
2440
- if (keyToSpec[col.key] !== undefined) newRelativeSpecs[col._flatIndex] = keyToSpec[col.key]
2441
- } else {
2442
- newColWidths[col._flatIndex] = this.resolveColWidth(col.width, this._relativeWidthContainer)
2443
- const parsed = parseColWidthSpec(col.width)
2444
- if (parsed.type !== 'px') newRelativeSpecs[col._flatIndex] = col.width
2445
- }
2446
- })
2447
- this.colWidths = newColWidths
2448
- this.relativeColSpecs = newRelativeSpecs
2478
+ if (this.fitColumns) {
2479
+ // fitColumns is the highest-priority width control — it overrides
2480
+ // even remembered manual-resize widths (keyToWidth above), so it
2481
+ // deliberately does NOT participate in that preservation logic.
2482
+ this.colWidths = {}
2483
+ this.relativeColSpecs = {}
2484
+ this._applyFitColumns()
2485
+ } else {
2486
+ // Rebuild colWidths by new flat index, using col.key to look up saved widths
2487
+ const newColWidths = {}
2488
+ const newRelativeSpecs = {}
2489
+ this.leafCols.forEach(col => {
2490
+ if (keyToWidth[col.key] !== undefined) {
2491
+ newColWidths[col._flatIndex] = keyToWidth[col.key]
2492
+ if (keyToSpec[col.key] !== undefined) newRelativeSpecs[col._flatIndex] = keyToSpec[col.key]
2493
+ } else {
2494
+ newColWidths[col._flatIndex] = this.resolveColWidth(col.width, this._relativeWidthContainer)
2495
+ const parsed = parseColWidthSpec(col.width)
2496
+ if (parsed.type !== 'px') newRelativeSpecs[col._flatIndex] = col.width
2497
+ }
2498
+ })
2499
+ this.colWidths = newColWidths
2500
+ this.relativeColSpecs = newRelativeSpecs
2501
+ }
2449
2502
 
2450
2503
  this.computeColumnTotals()
2451
2504
  this._recomputeRowFilters()
@@ -2799,7 +2852,8 @@ const ExcelGridComponent = {
2799
2852
  const pw = this.$el.parentElement.clientWidth
2800
2853
  if (pw !== this.hostContainerWidth && this._widthSyncChainOk()) {
2801
2854
  this.hostContainerWidth = pw
2802
- this._resolveRelativeColWidths()
2855
+ if (this.fitColumns) this._applyFitColumns()
2856
+ else this._resolveRelativeColWidths()
2803
2857
  }
2804
2858
  }
2805
2859
  const sbw = el.offsetWidth - el.clientWidth
@@ -5780,7 +5834,9 @@ const ExcelGridComponent = {
5780
5834
  // RESIZE (3.5)
5781
5835
  // =========================================================
5782
5836
  startColResize(e, ci) {
5783
- if (this.internalLocked) return
5837
+ // fitColumns owns column widths entirely — manual resize would break
5838
+ // its "always fits, never scrolls" invariant.
5839
+ if (this.internalLocked || this.fitColumns) return
5784
5840
  const rect = this.$refs.container.getBoundingClientRect()
5785
5841
  this.resizing = {
5786
5842
  active: true,
@@ -5865,6 +5921,7 @@ const ExcelGridComponent = {
5865
5921
  },
5866
5922
 
5867
5923
  autoFitColumn(ci) {
5924
+ if (this.fitColumns) return
5868
5925
  const col = this.leafCols[ci]
5869
5926
  if (!col) return
5870
5927
  let maxLen = (col.title || '').length
@@ -5880,7 +5937,8 @@ const ExcelGridComponent = {
5880
5937
  },
5881
5938
 
5882
5939
  _applyAutoWidth() {
5883
- if (!this.autoWidth || !this.leafCols.length) return
5940
+ // fitColumns is the higher-priority width control — see its own JSDoc.
5941
+ if (!this.autoWidth || this.fitColumns || !this.leafCols.length) return
5884
5942
  this.leafCols.forEach((col, ci) => {
5885
5943
  let maxLen = (col.title || '').length
5886
5944
  for (let ri = 0; ri < Math.min(this.totalRows, 500); ri++) {
@@ -5893,6 +5951,40 @@ const ExcelGridComponent = {
5893
5951
  this._cellStyleCache = Object.create(null)
5894
5952
  },
5895
5953
 
5954
+ // fitColumns prop — ignores col.width as an absolute value, using it only
5955
+ // as a relative WEIGHT so every visible leaf column (pinned or not)
5956
+ // scales by one shared factor to exactly fill _relativeWidthContainer
5957
+ // (the same circular-dependency-safe reference %/CSS-length widths
5958
+ // already resolve against — see its declaration comment). Mirrors
5959
+ // naturalGridWidth's formula in reverse: that computed derives the
5960
+ // grid's natural width FROM the column widths; this derives the column
5961
+ // widths FROM the available width.
5962
+ _applyFitColumns() {
5963
+ if (!this.fitColumns || !this.leafCols.length) return
5964
+ const containerWidth = this._relativeWidthContainer
5965
+ if (!containerWidth) return // not measured yet — re-run once hostContainerWidth arrives
5966
+ const outlineW = this.rowGroups.length ? OUTLINE_GUTTER : 0
5967
+ const available = containerWidth - ROW_HEADER_WIDTH - outlineW - this.vScrollbarW - this.shellBorderW
5968
+ if (available <= 0) return
5969
+
5970
+ const visible = []
5971
+ let totalWeight = 0
5972
+ this.leafCols.forEach((col, ci) => {
5973
+ if (this.isColHidden(ci)) return
5974
+ const w = this.resolveColWidth(col.width, containerWidth)
5975
+ visible.push({ ci, weight: w })
5976
+ totalWeight += w
5977
+ })
5978
+ if (!visible.length || totalWeight <= 0) return
5979
+
5980
+ const scale = available / totalWeight
5981
+ visible.forEach(({ ci, weight }) => {
5982
+ this.$set(this.colWidths, ci, Math.max(FIT_COLUMNS_MIN_WIDTH, weight * scale))
5983
+ this.$delete(this.relativeColSpecs, ci)
5984
+ })
5985
+ this._cellStyleCache = Object.create(null)
5986
+ },
5987
+
5896
5988
  ctxHideRow() {
5897
5989
  const targetRow = this.contextMenu.row
5898
5990
  // Row header: row >= 0 AND col = -1 (no column context)