socialcalc-ai 1.0.1 → 1.0.3

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
@@ -530,6 +530,22 @@ import {
530
530
 
531
531
  ---
532
532
 
533
+ ## 📦 Release Notes
534
+
535
+ ### v1.0.3
536
+ - **Node.js ESM & Runtime Compatibility**: Enhanced UMD root resolution across all core modules to support `globalThis`, eliminating undefined root errors in Node.js ES module loaders.
537
+ - **TypeScript Declarations**: Bundled official type declarations (`index.d.ts`) covering all core exports, plugin modules, and React/Ionic UI modal components.
538
+ - **Packaging Refinements**: Added `.npmignore` to exclude test files, scratch scripts, and developer metadata from distributed tarballs.
539
+
540
+ ### v1.0.2
541
+ - **Cell Edit Modal Fix**: Fixed cell click interceptor and mouse delegates so clicking any cell reliably triggers the `CellEditModal` bottom sheet.
542
+ - **Scroll Distortion Fix**: Resolved table border collapse and column misalignment when scrolling past multi-row (`rowspan`) and multi-column (`colspan`) blocks.
543
+ - **Formula Support**: Enhanced modal editing to support formula values (`=SUM(...)`) seamlessly alongside raw numbers, text, and HTML formatting.
544
+ - **Strict Column Sizing**: Enforced CSS pixel widths and `box-sizing: border-box` containment across `<colgroup>` and sizing rows to preserve layout integrity.
545
+ - **Safety Guards**: Added null guards and try/catch protection across SocialCalc workbook control callbacks.
546
+
547
+ ---
548
+
533
549
  ## 📜 License & Acknowledgments
534
550
 
535
551
  - **Original SocialCalc Engine**: Created by Dan Bricklin (co-creator of VisiCalc) and Socialtext.
package/core/constants.js CHANGED
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcConstants = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Initialize SocialCalc namespace if not exists
17
17
  var SocialCalc;
package/core/core.js CHANGED
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcCore = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Get SocialCalc namespace
17
17
  var SocialCalc;
@@ -5186,6 +5186,7 @@
5186
5186
  if (context.showRCHeaders) {
5187
5187
  newcol = document.createElement("col");
5188
5188
  newcol.width = context.rownamewidth;
5189
+ newcol.style.width = context.rownamewidth + "px";
5189
5190
  result.appendChild(newcol);
5190
5191
  }
5191
5192
 
@@ -5197,12 +5198,16 @@
5197
5198
  ) {
5198
5199
  newcol = document.createElement("col");
5199
5200
  t = context.colwidth[colnum];
5200
- if (t) newcol.width = t;
5201
+ if (t) {
5202
+ newcol.width = t;
5203
+ newcol.style.width = t + "px";
5204
+ }
5201
5205
  result.appendChild(newcol);
5202
5206
  }
5203
5207
  if (colpane < context.colpanes.length - 1) {
5204
5208
  newcol = document.createElement("col");
5205
5209
  newcol.width = context.defaultpanedividerwidth;
5210
+ newcol.style.width = context.defaultpanedividerwidth + "px";
5206
5211
  result.appendChild(newcol);
5207
5212
  }
5208
5213
  }
@@ -5218,6 +5223,8 @@
5218
5223
  if (context.showRCHeaders) {
5219
5224
  newcell = document.createElement("td");
5220
5225
  newcell.style.width = context.rownamewidth + "px";
5226
+ newcell.style.minWidth = context.rownamewidth + "px";
5227
+ newcell.style.maxWidth = context.rownamewidth + "px";
5221
5228
  newcell.height = "1";
5222
5229
  result.appendChild(newcell);
5223
5230
  }
@@ -5230,13 +5237,21 @@
5230
5237
  ) {
5231
5238
  newcell = document.createElement("td");
5232
5239
  t = context.colwidth[colnum];
5233
- if (t) newcell.width = t;
5240
+ if (t) {
5241
+ newcell.width = t;
5242
+ newcell.style.width = t + "px";
5243
+ newcell.style.minWidth = t + "px";
5244
+ newcell.style.maxWidth = t + "px";
5245
+ }
5234
5246
  newcell.height = "1";
5235
5247
  result.appendChild(newcell);
5236
5248
  }
5237
5249
  if (colpane < context.colpanes.length - 1) {
5238
5250
  newcell = document.createElement("td");
5239
5251
  newcell.width = context.defaultpanedividerwidth;
5252
+ newcell.style.width = context.defaultpanedividerwidth + "px";
5253
+ newcell.style.minWidth = context.defaultpanedividerwidth + "px";
5254
+ newcell.style.maxWidth = context.defaultpanedividerwidth + "px";
5240
5255
  newcell.height = "1";
5241
5256
  result.appendChild(newcell);
5242
5257
  }
@@ -5289,8 +5304,8 @@
5289
5304
  if (rownum === firstVisibleRow && colnum === firstVisibleCol) {
5290
5305
  // This is the visible start of the scrolled spanned cell in this pane!
5291
5306
  isSpanContinuation = true;
5292
- spanColSpan = Math.min(spanEndCol, panecollimits ? panecollimits.last : spanEndCol) - firstVisibleCol + 1;
5293
- spanRowSpan = Math.min(spanEndRow, panerowlimits ? panerowlimits.last : spanEndRow) - firstVisibleRow + 1;
5307
+ spanColSpan = Math.max(1, Math.min(spanEndCol, panecollimits ? panecollimits.last : spanEndCol) - firstVisibleCol + 1);
5308
+ spanRowSpan = Math.max(1, Math.min(spanEndRow, panerowlimits ? panerowlimits.last : spanEndRow) - firstVisibleRow + 1);
5294
5309
  cell = originCell;
5295
5310
  } else {
5296
5311
  // Covered by the visible span in this pane -- skip
@@ -5416,6 +5431,9 @@
5416
5431
  } else stylestr += "text-align:left;";
5417
5432
  }
5418
5433
 
5434
+ var activeColSpan = isSpanContinuation ? spanColSpan : (result.colSpan || cell.colspan || 1);
5435
+ var activeRowSpan = isSpanContinuation ? spanRowSpan : (result.rowSpan || cell.rowspan || 1);
5436
+
5419
5437
  num = cell.bt;
5420
5438
  if (num && (!isSpanContinuation || (originCR && rownum === originCR.row))) {
5421
5439
  stylestr += "border-top:" + sheetobj.borderstyles[num] + ";";
@@ -5428,12 +5446,12 @@
5428
5446
  if (
5429
5447
  context.CellInPane(
5430
5448
  rownum,
5431
- colnum + (isSpanContinuation ? spanColSpan : (cell.colspan || 1)),
5449
+ colnum + activeColSpan,
5432
5450
  rowpane,
5433
5451
  colpane
5434
5452
  )
5435
5453
  )
5436
- t = SocialCalc.crToCoord(colnum + (isSpanContinuation ? spanColSpan : (cell.colspan || 1)), rownum);
5454
+ t = SocialCalc.crToCoord(colnum + activeColSpan, rownum);
5437
5455
  else t = "nomatch";
5438
5456
  if (context.cellskip[t]) t = context.cellskip[t];
5439
5457
  if (!sheetobj.cells[t] || !sheetobj.cells[t].bl)
@@ -5446,13 +5464,13 @@
5446
5464
  } else if (context.showGrid) {
5447
5465
  if (
5448
5466
  context.CellInPane(
5449
- rownum + (isSpanContinuation ? spanRowSpan : (cell.rowspan || 1)),
5467
+ rownum + activeRowSpan,
5450
5468
  colnum,
5451
5469
  rowpane,
5452
5470
  colpane
5453
5471
  )
5454
5472
  )
5455
- t = SocialCalc.crToCoord(colnum, rownum + (isSpanContinuation ? spanRowSpan : (cell.rowspan || 1)));
5473
+ t = SocialCalc.crToCoord(colnum, rownum + activeRowSpan);
5456
5474
  else t = "nomatch";
5457
5475
  if (context.cellskip[t]) t = context.cellskip[t];
5458
5476
  if (!sheetobj.cells[t] || !sheetobj.cells[t].bt)
@@ -5483,6 +5501,8 @@
5483
5501
  }
5484
5502
 
5485
5503
  result.style.cssText = stylestr;
5504
+ result.style.boxSizing = "border-box";
5505
+ result.style.overflow = "hidden";
5486
5506
 
5487
5507
  //!!!!!!!!!
5488
5508
  // NOTE: csss and cssc are not supported yet.
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcFormatNumber = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Get SocialCalc namespace
17
17
  var SocialCalc;
package/core/formula.js CHANGED
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcFormula = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Get SocialCalc namespace
17
17
  var SocialCalc;
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcSpreadsheetControl = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Get SocialCalc namespace
17
17
  var SocialCalc;
@@ -7014,8 +7014,13 @@ SocialCalc.SpreadsheetCmdTable = {
7014
7014
  // this is for checkmark toggling
7015
7015
  SocialCalc.Callbacks.ToggleCell = function (cellname) {
7016
7016
  var control = SocialCalc.GetCurrentWorkBookControl();
7017
+ if (!control || !control.currentSheetButton || !control.workbook || !control.workbook.sheetArr) {
7018
+ return;
7019
+ }
7017
7020
  var sheetid = control.currentSheetButton.id;
7021
+ if (!control.workbook.sheetArr[sheetid]) return;
7018
7022
  var sheetobj = control.workbook.sheetArr[sheetid].sheet;
7023
+ if (!sheetobj || !sheetobj.cells) return;
7019
7024
  var cell = sheetobj.cells[cellname];
7020
7025
  var sheetname = sheetobj.sheetname;
7021
7026
 
@@ -11,7 +11,7 @@
11
11
  } else {
12
12
  root.SocialCalcTableEditor = factory();
13
13
  }
14
- })(typeof self !== "undefined" ? self : this, function () {
14
+ })(typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : this), function () {
15
15
 
16
16
  // Get SocialCalc namespace
17
17
  var SocialCalc;
@@ -1582,22 +1582,33 @@
1582
1582
  mouseinfo.registeredElements.push({ element: element, editor: editor });
1583
1583
  }
1584
1584
 
1585
+ if (!SocialCalc.ProcessEditorMouseDownHandler) {
1586
+ SocialCalc.ProcessEditorMouseDownHandler = function (e) {
1587
+ return SocialCalc.ProcessEditorMouseDown(e);
1588
+ };
1589
+ }
1590
+ if (!SocialCalc.ProcessEditorDblClickHandler) {
1591
+ SocialCalc.ProcessEditorDblClickHandler = function (e) {
1592
+ return SocialCalc.ProcessEditorDblClick(e);
1593
+ };
1594
+ }
1595
+
1585
1596
  if (element.addEventListener) {
1586
1597
  // DOM Level 2 -- Firefox, et al
1587
1598
  element.addEventListener(
1588
1599
  "mousedown",
1589
- SocialCalc.ProcessEditorMouseDown,
1600
+ SocialCalc.ProcessEditorMouseDownHandler,
1590
1601
  false
1591
1602
  );
1592
1603
  element.addEventListener(
1593
1604
  "dblclick",
1594
- SocialCalc.ProcessEditorDblClick,
1605
+ SocialCalc.ProcessEditorDblClickHandler,
1595
1606
  false
1596
1607
  );
1597
1608
  } else if (element.attachEvent) {
1598
1609
  // IE 5+
1599
- element.attachEvent("onmousedown", SocialCalc.ProcessEditorMouseDown);
1600
- element.attachEvent("ondblclick", SocialCalc.ProcessEditorDblClick);
1610
+ element.attachEvent("onmousedown", SocialCalc.ProcessEditorMouseDownHandler);
1611
+ element.attachEvent("ondblclick", SocialCalc.ProcessEditorDblClickHandler);
1601
1612
  } else {
1602
1613
  // don't handle this
1603
1614
  throw "Browser not supported";
@@ -1629,21 +1640,21 @@
1629
1640
  // DOM Level 2
1630
1641
  oldelement.removeEventListener(
1631
1642
  "mousedown",
1632
- SocialCalc.ProcessEditorMouseDown,
1643
+ SocialCalc.ProcessEditorMouseDownHandler || SocialCalc.ProcessEditorMouseDown,
1633
1644
  false
1634
1645
  );
1635
1646
  oldelement.removeEventListener(
1636
1647
  "dblclick",
1637
- SocialCalc.ProcessEditorDblClick,
1648
+ SocialCalc.ProcessEditorDblClickHandler || SocialCalc.ProcessEditorDblClick,
1638
1649
  false
1639
1650
  );
1640
1651
  } else if (oldelement.detachEvent) {
1641
1652
  // IE
1642
1653
  oldelement.detachEvent(
1643
1654
  "onmousedown",
1644
- SocialCalc.ProcessEditorMouseDown
1655
+ SocialCalc.ProcessEditorMouseDownHandler || SocialCalc.ProcessEditorMouseDown
1645
1656
  );
1646
- oldelement.detachEvent("ondblclick", SocialCalc.ProcessEditorDblClick);
1657
+ oldelement.detachEvent("ondblclick", SocialCalc.ProcessEditorDblClickHandler || SocialCalc.ProcessEditorDblClick);
1647
1658
  }
1648
1659
  mouseinfo.registeredElements.splice(i, 1);
1649
1660
  }
@@ -3772,20 +3783,23 @@
3772
3783
  return;
3773
3784
  }
3774
3785
 
3775
- if ((vamount == 1 || vamount == -1) && hamount == 0) {
3776
- // special case quick scrolls
3777
- if (vamount == 1) {
3778
- editor.ScrollTableUpOneRow();
3779
- } else {
3780
- editor.ScrollTableDownOneRow();
3786
+ // Fast micro-DOM update for 1-4 row vertical scrolls (smooth touch & momentum)
3787
+ if (hamount == 0 && Math.abs(vamount) >= 1 && Math.abs(vamount) <= 4) {
3788
+ var stepDir = vamount > 0 ? 1 : -1;
3789
+ var steps = Math.abs(vamount);
3790
+ for (var s = 0; s < steps; s++) {
3791
+ if (stepDir > 0) {
3792
+ editor.ScrollTableUpOneRow();
3793
+ } else {
3794
+ editor.ScrollTableDownOneRow();
3795
+ }
3781
3796
  }
3782
3797
  if (editor.ecell) editor.SetECellHeaders("selected");
3783
3798
  editor.SchedulePositionCalculations();
3784
3799
  return;
3785
3800
  }
3786
3801
 
3787
- // Do a gross move and render
3788
-
3802
+ // Do a gross move and render for large jumps or horizontal scrolls
3789
3803
  if (vamount != 0 || hamount != 0) {
3790
3804
  context.rowpanes[vplen - 1].first += vamount;
3791
3805
  context.rowpanes[vplen - 1].last += vamount;
@@ -3892,9 +3906,8 @@
3892
3906
  var sheetobj = context.sheetobj;
3893
3907
  var tableobj = editor.fullgrid;
3894
3908
 
3895
- var tbodyobj;
3896
-
3897
- tbodyobj = tableobj.lastChild;
3909
+ var tbodyobj = tableobj.lastChild;
3910
+ if (!tbodyobj || !tbodyobj.childNodes) return editor.fullgrid;
3898
3911
 
3899
3912
  toprow = context.showRCHeaders ? 2 : 1;
3900
3913
  for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
@@ -3902,18 +3915,20 @@
3902
3915
  context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2; // skip pane and spacing row
3903
3916
  }
3904
3917
 
3918
+ if (toprow >= tbodyobj.childNodes.length) return editor.fullgrid;
3905
3919
  tbodyobj.removeChild(tbodyobj.childNodes[toprow]);
3906
3920
 
3907
3921
  context.rowpanes[rowpane].first++;
3908
3922
  context.rowpanes[rowpane].last++;
3909
3923
  editor.FitToEditTable();
3910
- context.CalculateColWidthData(); // Just in case, since normally done in RenderSheet
3924
+ context.CalculateColWidthData();
3911
3925
 
3912
3926
  newbottomrow = context.RenderRow(context.rowpanes[rowpane].last, rowpane);
3913
- tbodyobj.appendChild(newbottomrow);
3914
-
3915
- // if scrolled off a row with starting rowspans, replace rows for the largest rowspan
3927
+ if (newbottomrow) {
3928
+ tbodyobj.appendChild(newbottomrow);
3929
+ }
3916
3930
 
3931
+ // If scrolled off a row with starting rowspans OR continuation spans, refresh rows for largest span
3917
3932
  var maxrowspan = 1;
3918
3933
  oldrownum = context.rowpanes[rowpane].first - 1;
3919
3934
 
@@ -3924,7 +3939,17 @@
3924
3939
  colnum++
3925
3940
  ) {
3926
3941
  var coord = SocialCalc.crToCoord(colnum, oldrownum);
3927
- if (context.cellskip[coord]) continue;
3942
+ if (context.cellskip && context.cellskip[coord]) {
3943
+ // Cell was covered by a span starting above oldrownum
3944
+ var originCoord = context.cellskip[coord];
3945
+ var originCell = sheetobj.cells[originCoord];
3946
+ var originCR = context.coordToCR ? context.coordToCR[originCoord] : null;
3947
+ if (originCell && originCR) {
3948
+ var remainingSpan = (originCR.row - 0 + originCell.rowspan) - oldrownum;
3949
+ if (remainingSpan > maxrowspan) maxrowspan = remainingSpan;
3950
+ }
3951
+ continue;
3952
+ }
3928
3953
  cell = sheetobj.cells[coord];
3929
3954
  if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
3930
3955
  }
@@ -3932,15 +3957,17 @@
3932
3957
 
3933
3958
  if (maxrowspan > 1) {
3934
3959
  for (rownum = 1; rownum < maxrowspan; rownum++) {
3935
- if (rownum + oldrownum >= context.rowpanes[rowpane].last) break;
3936
- newrow = context.RenderRow(rownum + oldrownum, rowpane);
3960
+ var targetRow = rownum + oldrownum;
3961
+ if (targetRow > context.rowpanes[rowpane].last) break;
3962
+ newrow = context.RenderRow(targetRow, rowpane);
3937
3963
  oldchild = tbodyobj.childNodes[toprow + rownum - 1];
3938
- tbodyobj.replaceChild(newrow, oldchild);
3964
+ if (oldchild && newrow) {
3965
+ tbodyobj.replaceChild(newrow, oldchild);
3966
+ }
3939
3967
  }
3940
3968
  }
3941
3969
 
3942
- // if added a row that includes rowspans from above, update the size of those to include new row
3943
-
3970
+ // If added bottom row includes rowspans from above, update the size of those to include new row
3944
3971
  bottomrownum = context.rowpanes[rowpane].last;
3945
3972
 
3946
3973
  for (colpane = 0; colpane < context.colpanes.length; colpane++) {
@@ -3949,26 +3976,31 @@
3949
3976
  colnum <= context.colpanes[colpane].last;
3950
3977
  colnum++
3951
3978
  ) {
3979
+ if (!context.cellskip) continue;
3952
3980
  coord = context.cellskip[SocialCalc.crToCoord(colnum, bottomrownum)];
3953
- if (!coord) continue; // only look at spanned cells
3954
- rownum = context.coordToCR[coord].row - 0;
3981
+ if (!coord) continue;
3982
+ rownum = context.coordToCR ? (context.coordToCR[coord].row - 0) : null;
3955
3983
  if (
3984
+ rownum === null ||
3956
3985
  rownum == context.rowpanes[rowpane].last ||
3957
3986
  rownum < context.rowpanes[rowpane].first
3958
- )
3959
- continue; // this row (colspan) or starts above pane
3987
+ ) {
3988
+ continue;
3989
+ }
3960
3990
  cell = sheetobj.cells[coord];
3961
- if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true; // remember row num to update
3991
+ if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
3962
3992
  }
3963
3993
  }
3964
3994
 
3965
3995
  for (rownum in rowneedsrefresh) {
3966
- newrow = context.RenderRow(rownum, rowpane);
3996
+ newrow = context.RenderRow(rownum - 0, rowpane);
3967
3997
  oldchild =
3968
3998
  tbodyobj.childNodes[
3969
3999
  toprow + (rownum - context.rowpanes[rowpane].first)
3970
4000
  ];
3971
- tbodyobj.replaceChild(newrow, oldchild);
4001
+ if (oldchild && newrow) {
4002
+ tbodyobj.replaceChild(newrow, oldchild);
4003
+ }
3972
4004
  }
3973
4005
 
3974
4006
  return tableobj;
@@ -3995,9 +4027,8 @@
3995
4027
  var sheetobj = context.sheetobj;
3996
4028
  var tableobj = editor.fullgrid;
3997
4029
 
3998
- var tbodyobj;
3999
-
4000
- tbodyobj = tableobj.lastChild;
4030
+ var tbodyobj = tableobj.lastChild;
4031
+ if (!tbodyobj || !tbodyobj.childNodes) return editor.fullgrid;
4001
4032
 
4002
4033
  toprow = context.showRCHeaders ? 2 : 1;
4003
4034
  for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
@@ -4005,23 +4036,24 @@
4005
4036
  context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2; // skip pane and spacing row
4006
4037
  }
4007
4038
 
4008
- tbodyobj.removeChild(
4009
- tbodyobj.childNodes[
4010
- toprow +
4011
- (context.rowpanes[rowpane].last - context.rowpanes[rowpane].first)
4012
- ]
4013
- );
4039
+ var removeIdx = toprow + (context.rowpanes[rowpane].last - context.rowpanes[rowpane].first);
4040
+ if (removeIdx < tbodyobj.childNodes.length) {
4041
+ tbodyobj.removeChild(tbodyobj.childNodes[removeIdx]);
4042
+ }
4014
4043
 
4015
4044
  context.rowpanes[rowpane].first--;
4016
4045
  context.rowpanes[rowpane].last--;
4017
4046
  editor.FitToEditTable();
4018
- context.CalculateColWidthData(); // Just in case, since normally done in RenderSheet
4047
+ context.CalculateColWidthData();
4019
4048
 
4020
4049
  newrow = context.RenderRow(context.rowpanes[rowpane].first, rowpane);
4021
- tbodyobj.insertBefore(newrow, tbodyobj.childNodes[toprow]);
4022
-
4023
- // if inserted a row with starting rowspans, replace rows for the largest rowspan
4050
+ if (toprow < tbodyobj.childNodes.length) {
4051
+ tbodyobj.insertBefore(newrow, tbodyobj.childNodes[toprow]);
4052
+ } else {
4053
+ tbodyobj.appendChild(newrow);
4054
+ }
4024
4055
 
4056
+ // If inserted a row with starting rowspans or continuation spans, refresh rows for largest span
4025
4057
  maxrowspan = 1;
4026
4058
  newrownum = context.rowpanes[rowpane].first;
4027
4059
 
@@ -4032,7 +4064,16 @@
4032
4064
  colnum++
4033
4065
  ) {
4034
4066
  coord = SocialCalc.crToCoord(colnum, newrownum);
4035
- if (context.cellskip[coord]) continue;
4067
+ if (context.cellskip && context.cellskip[coord]) {
4068
+ var originCoord = context.cellskip[coord];
4069
+ var originCell = sheetobj.cells[originCoord];
4070
+ var originCR = context.coordToCR ? context.coordToCR[originCoord] : null;
4071
+ if (originCell && originCR) {
4072
+ var remaining = (originCR.row - 0 + originCell.rowspan) - newrownum;
4073
+ if (remaining > maxrowspan) maxrowspan = remaining;
4074
+ }
4075
+ continue;
4076
+ }
4036
4077
  cell = sheetobj.cells[coord];
4037
4078
  if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
4038
4079
  }
@@ -4040,15 +4081,17 @@
4040
4081
 
4041
4082
  if (maxrowspan > 1) {
4042
4083
  for (rownum = 1; rownum < maxrowspan; rownum++) {
4043
- if (rownum + newrownum > context.rowpanes[rowpane].last) break;
4044
- newrow = context.RenderRow(rownum + newrownum, rowpane);
4084
+ var targetRow = rownum + newrownum;
4085
+ if (targetRow > context.rowpanes[rowpane].last) break;
4086
+ newrow = context.RenderRow(targetRow, rowpane);
4045
4087
  oldchild = tbodyobj.childNodes[toprow + rownum];
4046
- tbodyobj.replaceChild(newrow, oldchild);
4088
+ if (oldchild && newrow) {
4089
+ tbodyobj.replaceChild(newrow, oldchild);
4090
+ }
4047
4091
  }
4048
4092
  }
4049
4093
 
4050
- // if last row now includes rowspans or rowspans from above, update the size of those to remove deleted row
4051
-
4094
+ // If last row now includes rowspans or rowspans from above, update the size of those to remove deleted row
4052
4095
  bottomrownum = context.rowpanes[rowpane].last;
4053
4096
 
4054
4097
  for (colpane = 0; colpane < context.colpanes.length; colpane++) {
@@ -4060,26 +4103,34 @@
4060
4103
  coord = SocialCalc.crToCoord(colnum, bottomrownum);
4061
4104
  cell = sheetobj.cells[coord];
4062
4105
  if (cell && cell.rowspan > 1) {
4063
- rowneedsrefresh[bottomrownum] = true; // need to update this row
4106
+ rowneedsrefresh[bottomrownum] = true;
4064
4107
  continue;
4065
4108
  }
4109
+ if (!context.cellskip) continue;
4066
4110
  coord = context.cellskip[SocialCalc.crToCoord(colnum, bottomrownum)];
4067
- if (!coord) continue; // only look at spanned cells
4068
- rownum = context.coordToCR[coord].row - 0;
4069
- if (rownum == bottomrownum || rownum < context.rowpanes[rowpane].first)
4070
- continue; // this row (colspan) or starts above pane
4111
+ if (!coord) continue;
4112
+ rownum = context.coordToCR ? (context.coordToCR[coord].row - 0) : null;
4113
+ if (
4114
+ rownum === null ||
4115
+ rownum == bottomrownum ||
4116
+ rownum < context.rowpanes[rowpane].first
4117
+ ) {
4118
+ continue;
4119
+ }
4071
4120
  cell = sheetobj.cells[coord];
4072
- if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true; // remember row num to update
4121
+ if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
4073
4122
  }
4074
4123
  }
4075
4124
 
4076
4125
  for (rownum in rowneedsrefresh) {
4077
- newrow = context.RenderRow(rownum, rowpane);
4126
+ newrow = context.RenderRow(rownum - 0, rowpane);
4078
4127
  oldchild =
4079
4128
  tbodyobj.childNodes[
4080
4129
  toprow + (rownum - context.rowpanes[rowpane].first)
4081
4130
  ];
4082
- tbodyobj.replaceChild(newrow, oldchild);
4131
+ if (oldchild && newrow) {
4132
+ tbodyobj.replaceChild(newrow, oldchild);
4133
+ }
4083
4134
  }
4084
4135
 
4085
4136
  return tableobj;
package/modules/init.js CHANGED
@@ -8,9 +8,10 @@ if (typeof window !== "undefined" && window.SocialCalc) {
8
8
  SocialCalc = global.SocialCalc;
9
9
  } else {
10
10
  console.error("SocialCalc not found in global scope");
11
- SocialCalc = {}; // Fallback to prevent errors
12
11
  }
13
12
 
13
+ import { setupMouseListener } from "./listeners.js";
14
+
14
15
  export function initializeApp(data) {
15
16
  /* Initializes the spreadsheet */
16
17
  // Try multiple candidate IDs so the renderer can namespace its elements
@@ -39,6 +40,7 @@ export function initializeApp(data) {
39
40
  }
40
41
 
41
42
  spreadsheet.InitializeSpreadsheetControl(tableeditor, 0, 0, 0);
43
+ SocialCalc._activeEditor = spreadsheet.editor;
42
44
  spreadsheet.ExecuteCommand("redisplay", "");
43
45
 
44
46
  // Determine which workbook control id exists (string passed to constructor)
@@ -91,4 +93,5 @@ export function initializeApp(data) {
91
93
  }
92
94
 
93
95
  spreadsheet.DoOnResize();
96
+ setupMouseListener();
94
97
  }
@@ -30,6 +30,7 @@ export function enableCellEditModal() {
30
30
  if (typeof window !== "undefined" && window.SocialCalc) {
31
31
  window.SocialCalc.isCellEditModalEnabled = () => true;
32
32
  }
33
+ setupMouseListener();
33
34
  }
34
35
 
35
36
  /**
@@ -57,6 +58,9 @@ export function toggleCellEditModal(show) {
57
58
  if (typeof window !== "undefined" && window.SocialCalc) {
58
59
  window.SocialCalc.isCellEditModalEnabled = () => _cellEditModalEnabled;
59
60
  }
61
+ if (_cellEditModalEnabled) {
62
+ setupMouseListener();
63
+ }
60
64
  return _cellEditModalEnabled;
61
65
  }
62
66
 
@@ -167,7 +171,15 @@ export function setupMouseListener() {
167
171
  if (typeof window !== "undefined" && window.SocialCalc) {
168
172
  SocialCalc = window.SocialCalc;
169
173
  }
170
- if (!SocialCalc || !SocialCalc.EditorMouseInfo) return;
174
+ if (!SocialCalc || !SocialCalc.EditorMouseInfo) {
175
+ if (typeof window !== "undefined") {
176
+ setTimeout(setupMouseListener, 50);
177
+ }
178
+ return;
179
+ }
180
+
181
+ if (SocialCalc._customMouseListenerInstalled) return;
182
+ SocialCalc._customMouseListenerInstalled = true;
171
183
 
172
184
  const originalDblClick = SocialCalc.ProcessEditorDblClick;
173
185
 
@@ -232,8 +244,15 @@ export function setupMouseListener() {
232
244
  while (headerTd && headerTd.tagName !== "TD" && headerTd.tagName !== "TH" && headerTd !== document.body) {
233
245
  headerTd = headerTd.parentNode;
234
246
  }
235
- if (!isColHeader && headerTd && headerTd.className && typeof headerTd.className === "string" && headerTd.className.indexOf("colname") !== -1) {
236
- isColHeader = true;
247
+ if (!isColHeader && headerTd) {
248
+ if (headerTd.className && typeof headerTd.className === "string" && headerTd.className.indexOf("colname") !== -1) {
249
+ isColHeader = true;
250
+ } else if (headerTd.tagName === "TD" || headerTd.tagName === "TH") {
251
+ var headerTxt = (headerTd.textContent || "").trim().toUpperCase();
252
+ if (/^[A-Z]{1,2}$/.test(headerTxt)) {
253
+ isColHeader = true;
254
+ }
255
+ }
237
256
  }
238
257
 
239
258
  if (isRowColHeadersEnabled() && isColHeader) {
@@ -292,7 +311,11 @@ export function setupMouseListener() {
292
311
 
293
312
  // Toggle Cell Callback
294
313
  if (SocialCalc.Callbacks && SocialCalc.Callbacks.ToggleCell) {
295
- SocialCalc.Callbacks.ToggleCell(result.coord);
314
+ try {
315
+ SocialCalc.Callbacks.ToggleCell(result.coord);
316
+ } catch (err) {
317
+ // Ignore toggle cell errors in custom environments
318
+ }
296
319
  }
297
320
 
298
321
  // Move ECell (Updates Selection)
@@ -367,7 +390,7 @@ export function setupMouseListener() {
367
390
  const originalKeyPress = SocialCalc.ProcessKeyPress;
368
391
 
369
392
  SocialCalc.ProcessKeyDown = function (e) {
370
- const isModalOpen = !!document.querySelector(".sc-cell-edit-backdrop");
393
+ const isModalOpen = !!document.querySelector(".sc-cell-modal-backdrop, .sc-cell-edit-backdrop, .sc-modal-container");
371
394
  const activeElement = document.activeElement;
372
395
  const isTypingInInput =
373
396
  activeElement &&
@@ -386,7 +409,7 @@ export function setupMouseListener() {
386
409
  };
387
410
 
388
411
  SocialCalc.ProcessKeyPress = function (e) {
389
- const isModalOpen = !!document.querySelector(".sc-cell-edit-backdrop");
412
+ const isModalOpen = !!document.querySelector(".sc-cell-modal-backdrop, .sc-cell-edit-backdrop, .sc-modal-container");
390
413
  const activeElement = document.activeElement;
391
414
  const isTypingInInput =
392
415
  activeElement &&
@@ -416,8 +439,8 @@ function triggerCustomModal(editor) {
416
439
  if (!editor || !editor.ecell) return;
417
440
 
418
441
  const coord = editor.ecell.coord;
419
- const sheet = editor.context.sheetobj;
420
- const cell = sheet.GetAssuredCell(coord);
442
+ const sheet = editor.context ? editor.context.sheetobj : null;
443
+ const cell = sheet && sheet.GetAssuredCell ? sheet.GetAssuredCell(coord) : null;
421
444
 
422
445
  // Hide the native input box and cancel its timer
423
446
  if (editor.inputBox) {
@@ -442,25 +465,35 @@ function triggerCustomModal(editor) {
442
465
 
443
466
  // Determine initial text
444
467
  let initialText = "";
445
- if (cell.datavalue !== undefined && cell.datavalue !== null) {
446
- initialText = String(cell.datavalue);
447
- } else if (cell.displaystring !== undefined && cell.displaystring !== null) {
448
- initialText = String(cell.displaystring);
468
+ if (cell) {
469
+ if (cell.formula) {
470
+ initialText = "=" + cell.formula;
471
+ } else if (cell.datavalue !== undefined && cell.datavalue !== null) {
472
+ initialText = String(cell.datavalue);
473
+ } else if (cell.displaystring !== undefined && cell.displaystring !== null) {
474
+ initialText = String(cell.displaystring);
475
+ }
476
+ }
477
+ if (!initialText && SocialCalc && SocialCalc.GetCellContents && sheet) {
478
+ initialText = SocialCalc.GetCellContents(sheet, coord) || "";
449
479
  }
450
480
 
451
481
  // Callback when user confirms edit in Modal
452
482
  const handleOk = (newValue) => {
453
483
  let cmd = "";
454
- const isNum = !isNaN(Number(newValue)) && newValue.trim() !== "";
455
-
456
- if (isNum) {
457
- cmd = `set ${coord} value n ${newValue}`;
484
+ if (typeof newValue === "string" && newValue.startsWith("=")) {
485
+ cmd = `set ${coord} formula ${newValue.slice(1)}`;
458
486
  } else {
459
- const isHtml = /<[a-z][\s\S]*>/i.test(newValue);
460
- if (isHtml) {
461
- cmd = `set ${coord} text th ${newValue}\nset ${coord} textvalueformat text-html`;
487
+ const isNum = !isNaN(Number(newValue)) && newValue.trim() !== "";
488
+ if (isNum) {
489
+ cmd = `set ${coord} value n ${newValue}`;
462
490
  } else {
463
- cmd = `set ${coord} text t ${newValue}`;
491
+ const isHtml = /<[a-z][\s\S]*>/i.test(newValue);
492
+ if (isHtml) {
493
+ cmd = `set ${coord} text th ${newValue}\nset ${coord} textvalueformat text-html`;
494
+ } else {
495
+ cmd = `set ${coord} text t ${newValue}`;
496
+ }
464
497
  }
465
498
  }
466
499
 
@@ -529,3 +562,8 @@ export function setupCellChangeListener(callback) {
529
562
  };
530
563
  }
531
564
 
565
+ // Auto-initialize mouse listener if in browser environment
566
+ if (typeof window !== "undefined") {
567
+ setupMouseListener();
568
+ }
569
+
@@ -31,6 +31,9 @@ export function getActiveEditor() {
31
31
  if (typeof window !== "undefined" && window.SocialCalc) {
32
32
  SocialCalc = window.SocialCalc;
33
33
  }
34
+ if (SocialCalc && SocialCalc._activeEditor) {
35
+ return SocialCalc._activeEditor;
36
+ }
34
37
  if (SocialCalc && SocialCalc.GetCurrentWorkBookControl) {
35
38
  const ctrl = SocialCalc.GetCurrentWorkBookControl();
36
39
  if (ctrl && ctrl.workbook && ctrl.workbook.spreadsheet && ctrl.workbook.spreadsheet.editor) {
@@ -114,14 +114,17 @@ export function updateColumnResizeHandlePosition() {
114
114
 
115
115
  // 1. Try finding exact header cell from DOM for pixel-perfect coordinates
116
116
  if (editor.fullgrid) {
117
- const cells = editor.fullgrid.querySelectorAll("td");
117
+ const cells = editor.fullgrid.querySelectorAll("td, th");
118
118
  for (let i = 0; i < cells.length; i++) {
119
119
  const c = cells[i];
120
- if (c.textContent && c.textContent.trim() === colLetter && c.className && typeof c.className === "string" && c.className.indexOf("colname") !== -1) {
120
+ const txt = c.textContent ? c.textContent.trim() : "";
121
+ if (txt === colLetter) {
121
122
  const rect = c.getBoundingClientRect();
122
- colRight = Math.round(rect.right);
123
- headerBottom = Math.round(rect.bottom);
124
- break;
123
+ if (rect.width > 0 && rect.height > 0) {
124
+ colRight = Math.round(rect.right);
125
+ headerBottom = Math.round(rect.bottom);
126
+ break;
127
+ }
125
128
  }
126
129
  }
127
130
  }
@@ -151,8 +154,24 @@ export function updateColumnResizeHandlePosition() {
151
154
  handle = document.createElement("div");
152
155
  handle.id = "sc-col-resize-corner-handle";
153
156
  handle.className = "sc-col-resize-corner-handle";
157
+ handle.style.position = "fixed";
158
+ handle.style.zIndex = "99999";
159
+ handle.style.width = "24px";
160
+ handle.style.height = "24px";
161
+ handle.style.backgroundColor = "#2563eb";
162
+ handle.style.color = "#ffffff";
163
+ handle.style.borderRadius = "50%";
164
+ handle.style.border = "2px solid #ffffff";
165
+ handle.style.boxShadow = "0 2px 8px rgba(0,0,0,0.35)";
166
+ handle.style.display = "flex";
167
+ handle.style.alignItems = "center";
168
+ handle.style.justifyContent = "center";
169
+ handle.style.cursor = "col-resize";
170
+ handle.style.touchAction = "none";
171
+ handle.style.transform = "translate(-50%, -50%)";
172
+ handle.style.pointerEvents = "auto";
154
173
  handle.innerHTML = `
155
- <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
174
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
156
175
  <polyline points="8 4 4 8 8 12"></polyline>
157
176
  <polyline points="16 4 20 8 16 12"></polyline>
158
177
  <line x1="4" y1="8" x2="20" y2="8"></line>
@@ -497,8 +516,16 @@ export function isRowColHeadersEnabled() {
497
516
 
498
517
  // Window scroll and resize listeners to reposition the handle
499
518
  if (typeof window !== "undefined") {
519
+ SocialCalc = window.SocialCalc = window.SocialCalc || {};
520
+ SocialCalc.selectColumn = selectColumn;
521
+ SocialCalc.selectRow = selectRow;
522
+ SocialCalc.showColumnResizeHandle = showColumnResizeHandle;
523
+ SocialCalc.hideColumnResizeHandle = hideColumnResizeHandle;
524
+ SocialCalc.updateColumnResizeHandlePosition = updateColumnResizeHandlePosition;
525
+
500
526
  window.addEventListener("resize", updateColumnResizeHandlePosition);
501
527
  window.addEventListener("socialcalc:horizontal-scroll", updateColumnResizeHandlePosition);
528
+ window.addEventListener("socialcalc:scroll", updateColumnResizeHandlePosition);
502
529
  }
503
530
 
504
531
  // Register as a plugin in the plugin manager
@@ -108,8 +108,8 @@ function getEditor() {
108
108
 
109
109
  function editorCanScroll(editor) {
110
110
  if (!editor) return false;
111
- if (editor.busy) return false;
112
- if (editor.state !== "start") return false;
111
+ // Do not block touch scrolling during micro-renders or position steps
112
+ if (editor.busy && editor.busy !== true && editor.busy !== "render") return false;
113
113
  return true;
114
114
  }
115
115
 
@@ -175,14 +175,16 @@ function doScroll(editor, rowDelta, colDelta) {
175
175
  // ─── Replacement Touch Handlers ──────────────────────────────────────────────
176
176
 
177
177
  function handleTouchStart(event) {
178
- // Only handle single-finger touches
179
- if (event.touches.length !== 1) return;
180
-
181
- const touch = event.touches[0];
182
-
183
178
  // Cancel any running momentum
184
179
  cancelMomentum();
185
180
 
181
+ // Prevent default browser multi-touch behaviors (pinch-to-zoom, 2-finger page drag)
182
+ if (event.touches.length > 1) {
183
+ if (event.cancelable) event.preventDefault();
184
+ }
185
+
186
+ const touch = event.touches[0];
187
+
186
188
  _gesture.active = true;
187
189
  _gesture.isScrolling = false;
188
190
  _gesture.startX = touch.pageX;
@@ -193,41 +195,36 @@ function handleTouchStart(event) {
193
195
  _gesture.colDebt = 0;
194
196
  _gesture.velocitySamples = [];
195
197
  _gesture.startTime = performance.now();
196
-
197
- // Do NOT call preventDefault here — we decide in touchmove whether this
198
- // is a scroll or a tap. If it's a tap we want the event to flow normally.
198
+ _gesture.initialTouchCount = event.touches.length;
199
199
  }
200
200
 
201
201
  function handleTouchMove(event) {
202
- if (!_gesture.active) return;
203
- if (event.touches.length !== 1) return;
202
+ // CRITICAL: Always prevent default native browser scroll / container pan on the sheet
203
+ // so the entire outer sheet box is NEVER dragged or moved in any direction
204
+ if (event.cancelable) event.preventDefault();
205
+ event.stopPropagation();
206
+
207
+ if (!_gesture.active || !event.touches.length) return;
204
208
 
205
209
  const touch = event.touches[0];
206
- const dx = _gesture.lastX - touch.pageX; // positive = finger moved left (scroll right)
207
210
  const dy = _gesture.lastY - touch.pageY; // positive = finger moved up (scroll down)
208
211
 
209
212
  // Check deadzone (only once per gesture)
210
213
  if (!_gesture.isScrolling) {
211
- const totalDX = Math.abs(touch.pageX - _gesture.startX);
212
214
  const totalDY = Math.abs(touch.pageY - _gesture.startY);
213
- if (totalDX < TOUCH_SCROLL_CONFIG.scrollDeadzone &&
214
- totalDY < TOUCH_SCROLL_CONFIG.scrollDeadzone) {
215
+ if (totalDY < TOUCH_SCROLL_CONFIG.scrollDeadzone) {
215
216
  return; // still within deadzone — not scrolling yet
216
217
  }
217
218
  _gesture.isScrolling = true;
218
219
  }
219
220
 
220
- // Prevent native scroll / browser actions once we own the gesture
221
- if (event.cancelable) event.preventDefault();
222
- event.stopPropagation();
223
-
224
- // Track velocity (raw pixels)
221
+ // Track velocity (raw pixels, vertical only)
225
222
  addVelocitySample(0, dy);
226
223
 
227
- // Accumulate fractional row debt (vertical only)
224
+ // Accumulate fractional row debt (strictly vertical)
228
225
  _gesture.rowDebt += dy / TOUCH_SCROLL_CONFIG.pixelsPerRow;
229
226
 
230
- // Scroll integer rows (strictly vertical)
227
+ // Scroll integer rows (strictly vertical - colStep is always 0)
231
228
  const rowStep = Math.trunc(_gesture.rowDebt);
232
229
 
233
230
  if (rowStep !== 0) {
@@ -244,12 +241,21 @@ function handleTouchMove(event) {
244
241
 
245
242
  function handleTouchEnd(event) {
246
243
  if (!_gesture.active) return;
244
+
245
+ // If fingers still remain on screen, don't end gesture yet
246
+ if (event.touches && event.touches.length > 0) {
247
+ const touch = event.touches[0];
248
+ _gesture.lastX = touch.pageX;
249
+ _gesture.lastY = touch.pageY;
250
+ return;
251
+ }
252
+
247
253
  _gesture.active = false;
248
254
 
249
255
  const elapsed = performance.now() - _gesture.startTime;
250
256
 
251
- // If we never crossed the deadzone, treat as a tap
252
- if (!_gesture.isScrolling) {
257
+ // If we never crossed the deadzone and started with exactly 1 finger, treat as a tap
258
+ if (!_gesture.isScrolling && _gesture.initialTouchCount === 1) {
253
259
  handleTap(event, elapsed);
254
260
  return;
255
261
  }
@@ -308,6 +314,51 @@ function handleTouchCancel() {
308
314
  function handleTap(event, elapsed) {
309
315
  const now = performance.now();
310
316
  const touchinfo = SocialCalc.TouchInfo;
317
+ const touch = (event.changedTouches && event.changedTouches[0]) || (event.touches && event.touches[0]);
318
+
319
+ // Check if user tapped a column header, row header, or resize handle
320
+ if (touch && typeof document !== "undefined") {
321
+ const tappedEl = document.elementFromPoint(touch.clientX, touch.clientY);
322
+ if (tappedEl) {
323
+ // If tapped the resize handle itself, let drag handler take care of it
324
+ if (tappedEl.id === "sc-col-resize-corner-handle" || (tappedEl.closest && tappedEl.closest("#sc-col-resize-corner-handle"))) {
325
+ return;
326
+ }
327
+
328
+ // Check for Column or Row Header Cell
329
+ let headerTd = tappedEl;
330
+ while (headerTd && headerTd.tagName !== "TD" && headerTd.tagName !== "TH" && headerTd !== document.body) {
331
+ headerTd = headerTd.parentNode;
332
+ }
333
+ if (headerTd && (headerTd.tagName === "TD" || headerTd.tagName === "TH")) {
334
+ const txt = headerTd.textContent ? headerTd.textContent.trim().toUpperCase() : "";
335
+ if (/^[A-Z]{1,2}$/.test(txt)) {
336
+ let colNum = 0;
337
+ for (let i = 0; i < txt.length; i++) {
338
+ colNum = colNum * 26 + (txt.charCodeAt(i) - 64);
339
+ }
340
+ if (colNum > 0 && SocialCalc.selectColumn) {
341
+ SocialCalc.selectColumn(colNum);
342
+ return;
343
+ }
344
+ } else if (/^[0-9]+$/.test(txt)) {
345
+ const rowNum = parseInt(txt, 10);
346
+ if (rowNum > 0 && SocialCalc.selectRow) {
347
+ SocialCalc.selectRow(rowNum);
348
+ window.dispatchEvent(new CustomEvent("socialcalc:row-header-click", {
349
+ detail: {
350
+ rowNum: rowNum,
351
+ clientX: touch.clientX,
352
+ clientY: touch.clientY
353
+ }
354
+ }));
355
+ return;
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+
311
362
  const wobj = SocialCalc.FindTouchElement
312
363
  ? SocialCalc.FindTouchElement(event)
313
364
  : null;
@@ -387,14 +438,26 @@ export function enableTouchScroll() {
387
438
  el.removeEventListener("touchstart", _origProcessTouchStart, true);
388
439
  el.removeEventListener("touchmove", _origProcessTouchMove, true);
389
440
 
390
- // Add new listeners — touchmove must be non-passive so we can preventDefault
391
- el.addEventListener("touchstart", handleTouchStart, { passive: true });
441
+ // Add new listeners — both touchstart and touchmove non-passive so we can preventDefault
442
+ el.addEventListener("touchstart", handleTouchStart, { passive: false });
392
443
  el.addEventListener("touchmove", handleTouchMove, { passive: false });
393
444
  el.addEventListener("touchend", handleTouchEnd, { passive: true });
394
445
  el.addEventListener("touchcancel", handleTouchCancel, { passive: true });
395
446
  }
396
447
  }
397
448
 
449
+ // Lock outer containers against native touch dragging / pan
450
+ const outerCandidates = ["container", "tableeditor", "te_griddiv"];
451
+ for (const cid of outerCandidates) {
452
+ const cel = document.getElementById(cid);
453
+ if (cel && !cel.__touchLocked) {
454
+ cel.__touchLocked = true;
455
+ cel.addEventListener("touchmove", (e) => {
456
+ if (e.cancelable) e.preventDefault();
457
+ }, { passive: false });
458
+ }
459
+ }
460
+
398
461
  _enabled = true;
399
462
  console.log("[TouchScroll] Smooth touch scrolling enabled");
400
463
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialcalc-ai",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Modernized, modular spreadsheet calculation engine and React/Ionic UI suite with AI formula and template capabilities.",
5
5
  "type": "module",
6
6
  "main": "index.js",