socialcalc-ai 1.0.2 → 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 +5 -0
- package/core/constants.js +1 -1
- package/core/core.js +1 -1
- package/core/format-number.js +1 -1
- package/core/formula.js +1 -1
- package/core/spreadsheet-control.js +1 -1
- package/core/table-editor.js +260 -6
- package/modules/init.js +1 -0
- package/modules/listeners.js +9 -2
- 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
|
@@ -532,6 +532,11 @@ import {
|
|
|
532
532
|
|
|
533
533
|
## 📦 Release Notes
|
|
534
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
|
+
|
|
535
540
|
### v1.0.2
|
|
536
541
|
- **Cell Edit Modal Fix**: Fixed cell click interceptor and mouse delegates so clicking any cell reliably triggers the `CellEditModal` bottom sheet.
|
|
537
542
|
- **Scroll Distortion Fix**: Resolved table border collapse and column misalignment when scrolling past multi-row (`rowspan`) and multi-column (`colspan`) blocks.
|
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;
|
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;
|
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;
|
|
@@ -3783,8 +3783,23 @@
|
|
|
3783
3783
|
return;
|
|
3784
3784
|
}
|
|
3785
3785
|
|
|
3786
|
-
//
|
|
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
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
if (editor.ecell) editor.SetECellHeaders("selected");
|
|
3798
|
+
editor.SchedulePositionCalculations();
|
|
3799
|
+
return;
|
|
3800
|
+
}
|
|
3787
3801
|
|
|
3802
|
+
// Do a gross move and render for large jumps or horizontal scrolls
|
|
3788
3803
|
if (vamount != 0 || hamount != 0) {
|
|
3789
3804
|
context.rowpanes[vplen - 1].first += vamount;
|
|
3790
3805
|
context.rowpanes[vplen - 1].last += vamount;
|
|
@@ -3873,13 +3888,252 @@
|
|
|
3873
3888
|
};
|
|
3874
3889
|
|
|
3875
3890
|
SocialCalc.ScrollTableUpOneRow = function (editor) {
|
|
3876
|
-
|
|
3877
|
-
|
|
3891
|
+
var toprow,
|
|
3892
|
+
rowpane,
|
|
3893
|
+
rownum,
|
|
3894
|
+
colnum,
|
|
3895
|
+
colpane,
|
|
3896
|
+
cell,
|
|
3897
|
+
oldrownum,
|
|
3898
|
+
maxspan,
|
|
3899
|
+
newbottomrow,
|
|
3900
|
+
newrow,
|
|
3901
|
+
oldchild,
|
|
3902
|
+
bottomrownum;
|
|
3903
|
+
var rowneedsrefresh = {};
|
|
3904
|
+
|
|
3905
|
+
var context = editor.context;
|
|
3906
|
+
var sheetobj = context.sheetobj;
|
|
3907
|
+
var tableobj = editor.fullgrid;
|
|
3908
|
+
|
|
3909
|
+
var tbodyobj = tableobj.lastChild;
|
|
3910
|
+
if (!tbodyobj || !tbodyobj.childNodes) return editor.fullgrid;
|
|
3911
|
+
|
|
3912
|
+
toprow = context.showRCHeaders ? 2 : 1;
|
|
3913
|
+
for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
|
|
3914
|
+
toprow +=
|
|
3915
|
+
context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2; // skip pane and spacing row
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
if (toprow >= tbodyobj.childNodes.length) return editor.fullgrid;
|
|
3919
|
+
tbodyobj.removeChild(tbodyobj.childNodes[toprow]);
|
|
3920
|
+
|
|
3921
|
+
context.rowpanes[rowpane].first++;
|
|
3922
|
+
context.rowpanes[rowpane].last++;
|
|
3923
|
+
editor.FitToEditTable();
|
|
3924
|
+
context.CalculateColWidthData();
|
|
3925
|
+
|
|
3926
|
+
newbottomrow = context.RenderRow(context.rowpanes[rowpane].last, rowpane);
|
|
3927
|
+
if (newbottomrow) {
|
|
3928
|
+
tbodyobj.appendChild(newbottomrow);
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
// If scrolled off a row with starting rowspans OR continuation spans, refresh rows for largest span
|
|
3932
|
+
var maxrowspan = 1;
|
|
3933
|
+
oldrownum = context.rowpanes[rowpane].first - 1;
|
|
3934
|
+
|
|
3935
|
+
for (colpane = 0; colpane < context.colpanes.length; colpane++) {
|
|
3936
|
+
for (
|
|
3937
|
+
colnum = context.colpanes[colpane].first;
|
|
3938
|
+
colnum <= context.colpanes[colpane].last;
|
|
3939
|
+
colnum++
|
|
3940
|
+
) {
|
|
3941
|
+
var coord = SocialCalc.crToCoord(colnum, oldrownum);
|
|
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
|
+
}
|
|
3953
|
+
cell = sheetobj.cells[coord];
|
|
3954
|
+
if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
if (maxrowspan > 1) {
|
|
3959
|
+
for (rownum = 1; rownum < maxrowspan; rownum++) {
|
|
3960
|
+
var targetRow = rownum + oldrownum;
|
|
3961
|
+
if (targetRow > context.rowpanes[rowpane].last) break;
|
|
3962
|
+
newrow = context.RenderRow(targetRow, rowpane);
|
|
3963
|
+
oldchild = tbodyobj.childNodes[toprow + rownum - 1];
|
|
3964
|
+
if (oldchild && newrow) {
|
|
3965
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
|
|
3970
|
+
// If added bottom row includes rowspans from above, update the size of those to include new row
|
|
3971
|
+
bottomrownum = context.rowpanes[rowpane].last;
|
|
3972
|
+
|
|
3973
|
+
for (colpane = 0; colpane < context.colpanes.length; colpane++) {
|
|
3974
|
+
for (
|
|
3975
|
+
colnum = context.colpanes[colpane].first;
|
|
3976
|
+
colnum <= context.colpanes[colpane].last;
|
|
3977
|
+
colnum++
|
|
3978
|
+
) {
|
|
3979
|
+
if (!context.cellskip) continue;
|
|
3980
|
+
coord = context.cellskip[SocialCalc.crToCoord(colnum, bottomrownum)];
|
|
3981
|
+
if (!coord) continue;
|
|
3982
|
+
rownum = context.coordToCR ? (context.coordToCR[coord].row - 0) : null;
|
|
3983
|
+
if (
|
|
3984
|
+
rownum === null ||
|
|
3985
|
+
rownum == context.rowpanes[rowpane].last ||
|
|
3986
|
+
rownum < context.rowpanes[rowpane].first
|
|
3987
|
+
) {
|
|
3988
|
+
continue;
|
|
3989
|
+
}
|
|
3990
|
+
cell = sheetobj.cells[coord];
|
|
3991
|
+
if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
for (rownum in rowneedsrefresh) {
|
|
3996
|
+
newrow = context.RenderRow(rownum - 0, rowpane);
|
|
3997
|
+
oldchild =
|
|
3998
|
+
tbodyobj.childNodes[
|
|
3999
|
+
toprow + (rownum - context.rowpanes[rowpane].first)
|
|
4000
|
+
];
|
|
4001
|
+
if (oldchild && newrow) {
|
|
4002
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
return tableobj;
|
|
3878
4007
|
};
|
|
3879
4008
|
|
|
3880
4009
|
SocialCalc.ScrollTableDownOneRow = function (editor) {
|
|
3881
|
-
|
|
3882
|
-
|
|
4010
|
+
var toprow,
|
|
4011
|
+
rowpane,
|
|
4012
|
+
rownum,
|
|
4013
|
+
colnum,
|
|
4014
|
+
colpane,
|
|
4015
|
+
cell,
|
|
4016
|
+
newrownum,
|
|
4017
|
+
maxspan,
|
|
4018
|
+
newbottomrow,
|
|
4019
|
+
newrow,
|
|
4020
|
+
oldchild,
|
|
4021
|
+
bottomrownum,
|
|
4022
|
+
maxrowspan,
|
|
4023
|
+
coord;
|
|
4024
|
+
var rowneedsrefresh = {};
|
|
4025
|
+
|
|
4026
|
+
var context = editor.context;
|
|
4027
|
+
var sheetobj = context.sheetobj;
|
|
4028
|
+
var tableobj = editor.fullgrid;
|
|
4029
|
+
|
|
4030
|
+
var tbodyobj = tableobj.lastChild;
|
|
4031
|
+
if (!tbodyobj || !tbodyobj.childNodes) return editor.fullgrid;
|
|
4032
|
+
|
|
4033
|
+
toprow = context.showRCHeaders ? 2 : 1;
|
|
4034
|
+
for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
|
|
4035
|
+
toprow +=
|
|
4036
|
+
context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2; // skip pane and spacing row
|
|
4037
|
+
}
|
|
4038
|
+
|
|
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
|
+
}
|
|
4043
|
+
|
|
4044
|
+
context.rowpanes[rowpane].first--;
|
|
4045
|
+
context.rowpanes[rowpane].last--;
|
|
4046
|
+
editor.FitToEditTable();
|
|
4047
|
+
context.CalculateColWidthData();
|
|
4048
|
+
|
|
4049
|
+
newrow = context.RenderRow(context.rowpanes[rowpane].first, rowpane);
|
|
4050
|
+
if (toprow < tbodyobj.childNodes.length) {
|
|
4051
|
+
tbodyobj.insertBefore(newrow, tbodyobj.childNodes[toprow]);
|
|
4052
|
+
} else {
|
|
4053
|
+
tbodyobj.appendChild(newrow);
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
// If inserted a row with starting rowspans or continuation spans, refresh rows for largest span
|
|
4057
|
+
maxrowspan = 1;
|
|
4058
|
+
newrownum = context.rowpanes[rowpane].first;
|
|
4059
|
+
|
|
4060
|
+
for (colpane = 0; colpane < context.colpanes.length; colpane++) {
|
|
4061
|
+
for (
|
|
4062
|
+
colnum = context.colpanes[colpane].first;
|
|
4063
|
+
colnum <= context.colpanes[colpane].last;
|
|
4064
|
+
colnum++
|
|
4065
|
+
) {
|
|
4066
|
+
coord = SocialCalc.crToCoord(colnum, newrownum);
|
|
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
|
+
}
|
|
4077
|
+
cell = sheetobj.cells[coord];
|
|
4078
|
+
if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
|
|
4082
|
+
if (maxrowspan > 1) {
|
|
4083
|
+
for (rownum = 1; rownum < maxrowspan; rownum++) {
|
|
4084
|
+
var targetRow = rownum + newrownum;
|
|
4085
|
+
if (targetRow > context.rowpanes[rowpane].last) break;
|
|
4086
|
+
newrow = context.RenderRow(targetRow, rowpane);
|
|
4087
|
+
oldchild = tbodyobj.childNodes[toprow + rownum];
|
|
4088
|
+
if (oldchild && newrow) {
|
|
4089
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
// If last row now includes rowspans or rowspans from above, update the size of those to remove deleted row
|
|
4095
|
+
bottomrownum = context.rowpanes[rowpane].last;
|
|
4096
|
+
|
|
4097
|
+
for (colpane = 0; colpane < context.colpanes.length; colpane++) {
|
|
4098
|
+
for (
|
|
4099
|
+
colnum = context.colpanes[colpane].first;
|
|
4100
|
+
colnum <= context.colpanes[colpane].last;
|
|
4101
|
+
colnum++
|
|
4102
|
+
) {
|
|
4103
|
+
coord = SocialCalc.crToCoord(colnum, bottomrownum);
|
|
4104
|
+
cell = sheetobj.cells[coord];
|
|
4105
|
+
if (cell && cell.rowspan > 1) {
|
|
4106
|
+
rowneedsrefresh[bottomrownum] = true;
|
|
4107
|
+
continue;
|
|
4108
|
+
}
|
|
4109
|
+
if (!context.cellskip) continue;
|
|
4110
|
+
coord = context.cellskip[SocialCalc.crToCoord(colnum, bottomrownum)];
|
|
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
|
+
}
|
|
4120
|
+
cell = sheetobj.cells[coord];
|
|
4121
|
+
if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
for (rownum in rowneedsrefresh) {
|
|
4126
|
+
newrow = context.RenderRow(rownum - 0, rowpane);
|
|
4127
|
+
oldchild =
|
|
4128
|
+
tbodyobj.childNodes[
|
|
4129
|
+
toprow + (rownum - context.rowpanes[rowpane].first)
|
|
4130
|
+
];
|
|
4131
|
+
if (oldchild && newrow) {
|
|
4132
|
+
tbodyobj.replaceChild(newrow, oldchild);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
return tableobj;
|
|
3883
4137
|
};
|
|
3884
4138
|
|
|
3885
4139
|
// *************************************
|
package/modules/init.js
CHANGED
|
@@ -40,6 +40,7 @@ export function initializeApp(data) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
spreadsheet.InitializeSpreadsheetControl(tableeditor, 0, 0, 0);
|
|
43
|
+
SocialCalc._activeEditor = spreadsheet.editor;
|
|
43
44
|
spreadsheet.ExecuteCommand("redisplay", "");
|
|
44
45
|
|
|
45
46
|
// Determine which workbook control id exists (string passed to constructor)
|
package/modules/listeners.js
CHANGED
|
@@ -244,8 +244,15 @@ export function setupMouseListener() {
|
|
|
244
244
|
while (headerTd && headerTd.tagName !== "TD" && headerTd.tagName !== "TH" && headerTd !== document.body) {
|
|
245
245
|
headerTd = headerTd.parentNode;
|
|
246
246
|
}
|
|
247
|
-
if (!isColHeader && headerTd
|
|
248
|
-
|
|
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
|
+
}
|
|
249
256
|
}
|
|
250
257
|
|
|
251
258
|
if (isRowColHeadersEnabled() && isColHeader) {
|
|
@@ -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