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 +16 -0
- package/core/constants.js +1 -1
- package/core/core.js +29 -9
- package/core/format-number.js +1 -1
- package/core/formula.js +1 -1
- package/core/spreadsheet-control.js +6 -1
- package/core/table-editor.js +115 -64
- package/modules/init.js +4 -1
- package/modules/listeners.js +58 -20
- package/modules/plugin-manager.js +3 -0
- package/modules/row-col-headers.js +33 -6
- package/modules/touch-scroll.js +90 -27
- package/package.json +1 -1
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)
|
|
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)
|
|
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 +
|
|
5449
|
+
colnum + activeColSpan,
|
|
5432
5450
|
rowpane,
|
|
5433
5451
|
colpane
|
|
5434
5452
|
)
|
|
5435
5453
|
)
|
|
5436
|
-
t = SocialCalc.crToCoord(colnum +
|
|
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 +
|
|
5467
|
+
rownum + activeRowSpan,
|
|
5450
5468
|
colnum,
|
|
5451
5469
|
rowpane,
|
|
5452
5470
|
colpane
|
|
5453
5471
|
)
|
|
5454
5472
|
)
|
|
5455
|
-
t = SocialCalc.crToCoord(colnum, rownum +
|
|
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.
|
package/core/format-number.js
CHANGED
|
@@ -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
|
|
package/core/table-editor.js
CHANGED
|
@@ -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.
|
|
1600
|
+
SocialCalc.ProcessEditorMouseDownHandler,
|
|
1590
1601
|
false
|
|
1591
1602
|
);
|
|
1592
1603
|
element.addEventListener(
|
|
1593
1604
|
"dblclick",
|
|
1594
|
-
SocialCalc.
|
|
1605
|
+
SocialCalc.ProcessEditorDblClickHandler,
|
|
1595
1606
|
false
|
|
1596
1607
|
);
|
|
1597
1608
|
} else if (element.attachEvent) {
|
|
1598
1609
|
// IE 5+
|
|
1599
|
-
element.attachEvent("onmousedown", SocialCalc.
|
|
1600
|
-
element.attachEvent("ondblclick", SocialCalc.
|
|
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
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
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();
|
|
3924
|
+
context.CalculateColWidthData();
|
|
3911
3925
|
|
|
3912
3926
|
newbottomrow = context.RenderRow(context.rowpanes[rowpane].last, rowpane);
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
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])
|
|
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
|
-
|
|
3936
|
-
|
|
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
|
-
|
|
3964
|
+
if (oldchild && newrow) {
|
|
3965
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
3966
|
+
}
|
|
3939
3967
|
}
|
|
3940
3968
|
}
|
|
3941
3969
|
|
|
3942
|
-
//
|
|
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;
|
|
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;
|
|
3987
|
+
) {
|
|
3988
|
+
continue;
|
|
3989
|
+
}
|
|
3960
3990
|
cell = sheetobj.cells[coord];
|
|
3961
|
-
if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
|
|
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
|
-
|
|
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
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
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();
|
|
4047
|
+
context.CalculateColWidthData();
|
|
4019
4048
|
|
|
4020
4049
|
newrow = context.RenderRow(context.rowpanes[rowpane].first, rowpane);
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
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])
|
|
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
|
-
|
|
4044
|
-
|
|
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
|
-
|
|
4088
|
+
if (oldchild && newrow) {
|
|
4089
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
4090
|
+
}
|
|
4047
4091
|
}
|
|
4048
4092
|
}
|
|
4049
4093
|
|
|
4050
|
-
//
|
|
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;
|
|
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;
|
|
4068
|
-
rownum = context.coordToCR[coord].row - 0;
|
|
4069
|
-
if (
|
|
4070
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
}
|
package/modules/listeners.js
CHANGED
|
@@ -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)
|
|
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
|
|
236
|
-
|
|
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
|
-
|
|
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
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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
|
|
460
|
-
if (
|
|
461
|
-
cmd = `set ${coord}
|
|
487
|
+
const isNum = !isNaN(Number(newValue)) && newValue.trim() !== "";
|
|
488
|
+
if (isNum) {
|
|
489
|
+
cmd = `set ${coord} value n ${newValue}`;
|
|
462
490
|
} else {
|
|
463
|
-
|
|
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
|
-
|
|
120
|
+
const txt = c.textContent ? c.textContent.trim() : "";
|
|
121
|
+
if (txt === colLetter) {
|
|
121
122
|
const rect = c.getBoundingClientRect();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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="
|
|
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
|
package/modules/touch-scroll.js
CHANGED
|
@@ -108,8 +108,8 @@ function getEditor() {
|
|
|
108
108
|
|
|
109
109
|
function editorCanScroll(editor) {
|
|
110
110
|
if (!editor) return false;
|
|
111
|
-
|
|
112
|
-
if (editor.
|
|
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
|
-
|
|
203
|
-
|
|
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 (
|
|
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
|
-
//
|
|
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
|
|
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 —
|
|
391
|
-
el.addEventListener("touchstart", handleTouchStart, { passive:
|
|
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