@svgrid/grid 2.7.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { Gn as e, fo as t, po as n } from "./src-COC0vnpg.js";
1
+ import { Gn as e, fo as t, po as n } from "./src-B_YS5AOc.js";
2
2
  import { E as r, F as i, G as a, H as o, I as s, L as c, N as l, Q as u, U as d, V as f, X as p, Y as m, Z as h, _ as g, at as _, b as v, d as y, et as b, f as x, ft as S, g as ee, h as C, it as w, j as T, k as E, lt as D, m as O, mt as te, nt as k, o as ne, pt as A, st as j, t as M, tt as N, u as P, ut as re, v as F, w as ie } from "./disclose-version-Dr7rEVv-.js";
3
3
  import { S as ae, _ as oe, j as se, k as ce } from "./editor-registry-BhgE3S84.js";
4
4
  import { o as le } from "./SvDateTimePicker-BWwpfB_o.js";
@@ -1,4 +1,4 @@
1
- import { Gn as e, fo as t, po as n } from "./src-pYGV2rMR.js";
1
+ import { Gn as e, fo as t, po as n } from "./src-DYXXpuSk.js";
2
2
  import { S as r, _ as i, j as a, k as o } from "./editor-registry-BhgE3S84.js";
3
3
  import { o as s } from "./SvDateTimePicker-Bivn8dAP.js";
4
4
  import * as c from "svelte/internal/client";
@@ -57,11 +57,11 @@ function e(e) {
57
57
  !n || e.pointerId !== n.pointerId || c(!1);
58
58
  }
59
59
  return {
60
- start(r, i) {
60
+ start(r, i, a) {
61
61
  if (!e.managed() || n) return;
62
- let a = e.originalAt(i);
63
- if (a == null) return;
64
- let s = r.currentTarget;
62
+ let s = e.originalAt(i);
63
+ if (s == null) return;
64
+ let c = r.currentTarget;
65
65
  n = {
66
66
  pointerId: r.pointerId,
67
67
  rowIndex: i,
@@ -70,17 +70,19 @@ function e(e) {
70
70
  started: !1,
71
71
  timer: null,
72
72
  scrollFrame: null,
73
- scroller: s?.closest?.(".sv-grid-container") ?? null,
74
- root: s?.closest?.(".sv-grid-root") ?? null
75
- }, window.addEventListener("pointermove", l, { passive: !1 }), window.addEventListener("pointerup", u), window.addEventListener("pointercancel", d), window.addEventListener("touchmove", o, { passive: !1 }), n.timer = setTimeout(() => {
73
+ scroller: c?.closest?.(".sv-grid-container") ?? null,
74
+ root: c?.closest?.(".sv-grid-root") ?? null
75
+ }, window.addEventListener("pointermove", l, { passive: !1 }), window.addEventListener("pointerup", u), window.addEventListener("pointercancel", d), window.addEventListener("touchmove", o, { passive: !1 });
76
+ let f = a == null ? 0 : Math.max(0, performance.now() - a);
77
+ n.timer = setTimeout(() => {
76
78
  if (n) {
77
79
  n.started = !0, n.timer = null, n.root?.classList.add("sv-grid-row-drag-active");
78
80
  try {
79
- s?.setPointerCapture?.(r.pointerId);
81
+ c?.setPointerCapture?.(r.pointerId);
80
82
  } catch {}
81
- e.openBus(a), t.rowDragActive = !0, t.rowDropIndex = i, t.rowDropSide = "before";
83
+ e.openBus(s), t.rowDragActive = !0, t.rowDropIndex = i, t.rowDropSide = "before";
82
84
  }
83
- }, 350);
85
+ }, Math.max(0, 350 - f));
84
86
  },
85
87
  destroy() {
86
88
  c(!1);
@@ -3535,18 +3535,18 @@ function Dr(e) {
3535
3535
  }
3536
3536
  if (y) return;
3537
3537
  y = !0;
3538
- let r = e.pointerId, i = e.clientX, a = e.clientY, o = !1;
3539
- function s() {
3540
- window.removeEventListener("pointermove", c), window.removeEventListener("pointerup", l), window.removeEventListener("pointercancel", l);
3541
- }
3542
- function c(e) {
3543
- e.pointerId === r && Math.abs(e.clientY - a) + Math.abs(e.clientX - i) > 10 && (o = !0, s());
3538
+ let r = e.pointerId, i = e.clientX, a = e.clientY, o = performance.now(), s = !1;
3539
+ function c() {
3540
+ window.removeEventListener("pointermove", l), window.removeEventListener("pointerup", u), window.removeEventListener("pointercancel", u);
3544
3541
  }
3545
3542
  function l(e) {
3546
- e.pointerId === r && (o = !0, s());
3543
+ e.pointerId === r && Math.abs(e.clientY - a) + Math.abs(e.clientX - i) > 10 && (s = !0, c());
3544
+ }
3545
+ function u(e) {
3546
+ e.pointerId === r && (s = !0, c());
3547
3547
  }
3548
- window.addEventListener("pointermove", c, { passive: !0 }), window.addEventListener("pointerup", l), window.addEventListener("pointercancel", l);
3549
- let u = {
3548
+ window.addEventListener("pointermove", l, { passive: !0 }), window.addEventListener("pointerup", u), window.addEventListener("pointercancel", u);
3549
+ let d = {
3550
3550
  pointerId: e.pointerId,
3551
3551
  pointerType: e.pointerType,
3552
3552
  clientX: e.clientX,
@@ -3554,8 +3554,8 @@ function Dr(e) {
3554
3554
  currentTarget: e.currentTarget,
3555
3555
  preventDefault: () => {}
3556
3556
  };
3557
- import("./row-drag-touch-Ddaa-QeB.js").then((e) => {
3558
- y = !1, s(), !(o || !n()) && (v = e.createTouchDrag(b()), v.start(u, t));
3557
+ import("./row-drag-touch-CycRR65n.js").then((e) => {
3558
+ y = !1, c(), !(s || !n()) && (v = e.createTouchDrag(b()), v.start(d, t, o));
3559
3559
  });
3560
3560
  }
3561
3561
  return {
@@ -8188,7 +8188,7 @@ function Qs(e, t) {
8188
8188
  E(() => {
8189
8189
  u(ee) || (o.editingEnabled || o.editingCell || o.fullRowEdit) && import("./SvGridCellEditor-Ba7rY3eu.js").then((e) => B(ee, e.default, !0));
8190
8190
  }), E(() => {
8191
- (o.tooltip || o.columnMenuFor || o.contextMenuFor || o.filterMenuFor || o.operatorMenuFor || o.inSuggestFor || o.chooseColumnsPos) && !u(x) && import("./GridMenus-C4oLsJyj.js").then((e) => B(x, e.default, !0));
8191
+ (o.tooltip || o.columnMenuFor || o.contextMenuFor || o.filterMenuFor || o.operatorMenuFor || o.inSuggestFor || o.chooseColumnsPos) && !u(x) && import("./GridMenus-DMihUtma.js").then((e) => B(x, e.default, !0));
8192
8192
  });
8193
8193
  let M = J(() => !!o.pivotConfig && o.pivotModeOn), N = J(() => o.pivotResult), F = J(() => o.allRowsBeforePagination.filter((e) => !o.isGroupRow(e)).map((e) => e.original)), oe = J(() => o.filterRowValues), ce = J(() => o.filterMenuValues), ue = J(() => o.showTooltipFor), Y = J(() => o.hideTooltip), $ = J(() => o.findHits), he = J(() => o.headerHeight), _e = J(() => p.columnResize === !0), ve = J(() => o.selectionColumnWidth), xe = J(() => o.rowNumberColumnWidth), Ce = J(() => o.showRowNumbersEffective), we = J(() => o.columnMenuFor), Te = J(() => o.filterMenuFor), Ee = J(() => o.operatorMenuFor), Ae = J(() => o.viewportWidth), je = J(() => o.viewportHeight), Me = J(() => o.scrollMetrics), Pe = J(() => o.hasVerticalOverflow), Fe = J(() => o.showGlobalFilterEffective), Ie = J(() => o.showFilterRowEffective), Le = J(() => o.showInlineColumnFilterEffective), Re = J(() => o.showRowSelectionEffective), ze = J(() => o.rowSummariesEnabled), Be = J(() => o.grid), Ve = J(() => o.allColumns), He = J(() => o.headerGroups), Ue = J(() => o.groupHeaderRowsWindowed), We = J(() => o.cellPinStyle), Ge = J(() => o.isColumnPinned), Ke = J(() => o.colDragId), qe = J(() => o.colDropOnId), Je = J(() => o.colDropSide), Ye = J(() => o.onColumnHeaderDragStart), Xe = J(() => o.onColumnHeaderDragOver), Ze = J(() => o.onColumnHeaderDragLeave), Qe = J(() => o.onColumnHeaderDrop), $e = J(() => o.onColumnHeaderDragEnd), et = J(() => p.rowDragManaged === !0), tt = J(() => o.rowDropIndex), nt = J(() => o.rowDropSide), rt = J(() => o.onRowDragStart), it = J(() => o.onRowDragOver), at = J(() => o.onRowDragLeave), ot = J(() => o.onRowDrop), st = J(() => o.onRowsContainerDragOver), ct = J(() => o.onRowsContainerDrop), lt = J(() => o.onRowDragEnd), ut = J(() => o.onRowPointerDown);
8194
8194
  function dt(e) {
@@ -3537,18 +3537,18 @@ function Tn(e) {
3537
3537
  }
3538
3538
  if (y) return;
3539
3539
  y = !0;
3540
- let r = e.pointerId, i = e.clientX, a = e.clientY, o = !1;
3541
- function s() {
3542
- window.removeEventListener("pointermove", c), window.removeEventListener("pointerup", l), window.removeEventListener("pointercancel", l);
3543
- }
3544
- function c(e) {
3545
- e.pointerId === r && Math.abs(e.clientY - a) + Math.abs(e.clientX - i) > 10 && (o = !0, s());
3540
+ let r = e.pointerId, i = e.clientX, a = e.clientY, o = performance.now(), s = !1;
3541
+ function c() {
3542
+ window.removeEventListener("pointermove", l), window.removeEventListener("pointerup", u), window.removeEventListener("pointercancel", u);
3546
3543
  }
3547
3544
  function l(e) {
3548
- e.pointerId === r && (o = !0, s());
3545
+ e.pointerId === r && Math.abs(e.clientY - a) + Math.abs(e.clientX - i) > 10 && (s = !0, c());
3546
+ }
3547
+ function u(e) {
3548
+ e.pointerId === r && (s = !0, c());
3549
3549
  }
3550
- window.addEventListener("pointermove", c, { passive: !0 }), window.addEventListener("pointerup", l), window.addEventListener("pointercancel", l);
3551
- let u = {
3550
+ window.addEventListener("pointermove", l, { passive: !0 }), window.addEventListener("pointerup", u), window.addEventListener("pointercancel", u);
3551
+ let d = {
3552
3552
  pointerId: e.pointerId,
3553
3553
  pointerType: e.pointerType,
3554
3554
  clientX: e.clientX,
@@ -3556,8 +3556,8 @@ function Tn(e) {
3556
3556
  currentTarget: e.currentTarget,
3557
3557
  preventDefault: () => {}
3558
3558
  };
3559
- import("./row-drag-touch-Ddaa-QeB.js").then((e) => {
3560
- y = !1, s(), !(o || !n()) && (v = e.createTouchDrag(b()), v.start(u, t));
3559
+ import("./row-drag-touch-CycRR65n.js").then((e) => {
3560
+ y = !1, c(), !(s || !n()) && (v = e.createTouchDrag(b()), v.start(d, t, o));
3561
3561
  });
3562
3562
  }
3563
3563
  return {
@@ -8190,7 +8190,7 @@ function Jo(e, t) {
8190
8190
  V.user_effect(() => {
8191
8191
  V.get(w) || (a.editingEnabled || a.editingCell || a.fullRowEdit) && import("./SvGridCellEditor-AXL8cHGO.js").then((e) => V.set(w, e.default, !0));
8192
8192
  }), V.user_effect(() => {
8193
- (a.tooltip || a.columnMenuFor || a.contextMenuFor || a.filterMenuFor || a.operatorMenuFor || a.inSuggestFor || a.chooseColumnsPos) && !V.get(y) && import("./GridMenus-yEivjNwl.js").then((e) => V.set(y, e.default, !0));
8193
+ (a.tooltip || a.columnMenuFor || a.contextMenuFor || a.filterMenuFor || a.operatorMenuFor || a.inSuggestFor || a.chooseColumnsPos) && !V.get(y) && import("./GridMenus-nmNDj1a3.js").then((e) => V.set(y, e.default, !0));
8194
8194
  });
8195
8195
  let T = V.derived(() => !!a.pivotConfig && a.pivotModeOn), E = V.derived(() => a.pivotResult), D = V.derived(() => a.allRowsBeforePagination.filter((e) => !a.isGroupRow(e)).map((e) => e.original)), O = V.derived(() => a.filterRowValues), k = V.derived(() => a.filterMenuValues), A = V.derived(() => a.showTooltipFor), j = V.derived(() => a.hideTooltip), M = V.derived(() => a.findHits), N = V.derived(() => a.headerHeight), P = V.derived(() => o.columnResize === !0), F = V.derived(() => a.selectionColumnWidth), I = V.derived(() => a.rowNumberColumnWidth), L = V.derived(() => a.showRowNumbersEffective), ee = V.derived(() => a.columnMenuFor), R = V.derived(() => a.filterMenuFor), te = V.derived(() => a.operatorMenuFor), z = V.derived(() => a.viewportWidth), B = V.derived(() => a.viewportHeight), ne = V.derived(() => a.scrollMetrics), H = V.derived(() => a.hasVerticalOverflow), re = V.derived(() => a.showGlobalFilterEffective), ie = V.derived(() => a.showFilterRowEffective), ae = V.derived(() => a.showInlineColumnFilterEffective), U = V.derived(() => a.showRowSelectionEffective), W = V.derived(() => a.rowSummariesEnabled), oe = V.derived(() => a.grid), se = V.derived(() => a.allColumns), ce = V.derived(() => a.headerGroups), le = V.derived(() => a.groupHeaderRowsWindowed), ue = V.derived(() => a.cellPinStyle), de = V.derived(() => a.isColumnPinned), fe = V.derived(() => a.colDragId), pe = V.derived(() => a.colDropOnId), me = V.derived(() => a.colDropSide), he = V.derived(() => a.onColumnHeaderDragStart), ge = V.derived(() => a.onColumnHeaderDragOver), _e = V.derived(() => a.onColumnHeaderDragLeave), ve = V.derived(() => a.onColumnHeaderDrop), ye = V.derived(() => a.onColumnHeaderDragEnd), be = V.derived(() => o.rowDragManaged === !0), xe = V.derived(() => a.rowDropIndex), Se = V.derived(() => a.rowDropSide), Ce = V.derived(() => a.onRowDragStart), we = V.derived(() => a.onRowDragOver), Te = V.derived(() => a.onRowDragLeave), Ee = V.derived(() => a.onRowDrop), De = V.derived(() => a.onRowsContainerDragOver), Oe = V.derived(() => a.onRowsContainerDrop), ke = V.derived(() => a.onRowDragEnd), Ae = V.derived(() => a.onRowPointerDown);
8196
8196
  function je(e) {
@@ -1,5 +1,5 @@
1
1
  import { _ as ee, c as te, d as e, f as ne, h as re, i as t, l as n, m as r, n as i, o as a, p as o, r as s, s as c, t as l, u } from "./export-format-Cv1Dll6k.js";
2
- import { $ as d, $a as f, $i as p, $n as m, $o as h, $r as g, $t as _, A as v, Aa as y, Ai as b, An as x, Ao as S, Ar as C, At as w, B as T, Ba as E, Bi as D, Bn as O, Bo as k, Br as A, Bt as j, C as M, Ca as N, Ci as P, Cn as F, Co as I, Cr as L, Ct as R, D as z, Da as B, Di as V, Dn as H, Do as U, Dr as W, Dt as G, E as K, Ea as q, Ei as J, En as Y, Eo as X, Er as ie, Et as ae, F as oe, Fa as se, Fi as ce, Fn as le, Fo as ue, Fr as de, Ft as fe, G as pe, Ga as me, Gi as he, Gn as ge, Go as _e, Gr as ve, Gt as ye, H as be, Ha as xe, Hi as Se, Hn as Ce, Ho as we, Hr as Te, Ht as Ee, I as De, Ia as Oe, Ii as ke, In as Ae, Io as je, Ir as Me, It as Ne, J as Pe, Ja as Fe, Ji as Ie, Jn as Le, Jo as Re, Jr as ze, Jt as Be, K as Ve, Ka as He, Ki as Ue, Kn as We, Ko as Ge, Kr as Ke, Kt as qe, L as Je, La as Ye, Li as Xe, Ln as Ze, Lo as Qe, Lr as $e, Lt as et, M as tt, Ma as nt, Mi as rt, Mn as it, Mo as at, Mr as ot, Mt as st, N as ct, Na as lt, Ni as ut, Nn as dt, No as ft, Nr as pt, Nt as mt, O as ht, Oa as gt, Oi as _t, On as vt, Oo as yt, Or as bt, Ot as xt, P as St, Pa as Ct, Pi as wt, Pn as Tt, Po as Et, Pr as Dt, Pt as Ot, Q as kt, Qa as At, Qi as jt, Qn as Mt, Qo as Nt, Qr as Pt, Qt as Ft, R as It, Ra as Lt, Ri as Rt, Rn as zt, Ro as Bt, Rr as Vt, Rt as Ht, S as Ut, Sa as Wt, Si as Gt, Sn as Kt, So as qt, Sr as Jt, St as Yt, T as Xt, Ta as Zt, Ti as Qt, Tn as $t, To as en, Tr as tn, Tt as nn, U as rn, Ua as an, Ui as on, Un as sn, Uo as cn, Ur as ln, Ut as un, V as dn, Va as fn, Vi as pn, Vn as mn, Vo as hn, Vr as gn, Vt as _n, W as vn, Wa as yn, Wi as bn, Wn as xn, Wo as Sn, Wr as Cn, Wt as wn, X as Tn, Xa as En, Xi as Dn, Xn as On, Xo as kn, Xr as An, Xt as jn, Y as Mn, Ya as Nn, Yi as Pn, Yn as Fn, Yo as In, Yr as Ln, Yt as Rn, Z as zn, Za as Bn, Zi as Vn, Zn as Hn, Zo as Un, Zr as Wn, Zt as Gn, _ as Kn, _a as qn, _i as Jn, _n as Yn, _o as Xn, _r as Zn, _t as Qn, a as $n, aa as er, ai as tr, an as nr, ao as rr, ar as ir, at as ar, b as or, ba as sr, bi as cr, bn as lr, bo as ur, br as dr, bt as fr, c as pr, ca as mr, ci as hr, cn as gr, co as _r, cr as vr, ct as yr, d as br, da as xr, di as Sr, dn as Cr, do as wr, dr as Tr, dt as Er, ea as Dr, ei as Or, en as kr, eo as Ar, er as jr, es as Mr, et as Nr, f as Pr, fa as Fr, fi as Ir, fn as Lr, fo as Rr, fr as zr, ft as Br, g as Vr, ga as Hr, gi as Ur, gn as Wr, go as Gr, gr as Kr, gt as qr, h as Jr, ha as Yr, hi as Xr, hn as Zr, ho as Z, hr as Qr, ht as $r, i as ei, ia as ti, ii as ni, in as ri, io as ii, ir as ai, it as oi, j as si, ja as ci, ji as li, jn as ui, jo as di, jr as fi, jt as pi, k as mi, ka as hi, ki as gi, kn as _i, ko as vi, kr as yi, kt as bi, l as xi, la as Si, li as Ci, ln as wi, lo as Ti, lr as Ei, lt as Di, m as Oi, ma as ki, mi as Ai, mn as ji, mo as Mi, mr as Ni, mt as Pi, n as Fi, na as Ii, ni as Li, nn as Ri, no as zi, nr as Bi, nt as Vi, o as Hi, oa as Ui, oi as Wi, on as Gi, oo as Ki, or as qi, ot as Ji, p as Yi, pa as Xi, pi as Zi, pn as Qi, po as $i, pr as ea, pt as ta, q as na, qa as ra, qi as ia, qn as aa, qo as oa, qr as sa, qt as ca, r as la, ra as ua, ri as da, rn as fa, ro as pa, rr as ma, rt as ha, s as ga, sa as _a, si as va, sn as ya, so as ba, sr as xa, st as Sa, t as Ca, ta as wa, ti as Ta, tn as Ea, to as Da, tr as Oa, ts as ka, tt as Aa, u as ja, ua as Ma, ui as Na, un as Pa, uo as Fa, ur as Ia, ut as La, v as Ra, va as za, vi as Ba, vn as Va, vo as Ha, vr as Ua, vt as Wa, w as Ga, wa as Ka, wi as qa, wn as Ja, wo as Ya, wr as Xa, wt as Za, x as Qa, xa as $a, xi as eo, xn as to, xo as no, xr as ro, xt as io, y as ao, ya as oo, yi as so, yn as co, yo as lo, yr as uo, yt as fo, z as po, za as mo, zi as ho, zn as go, zo as _o, zr as vo, zt as yo } from "./src-COC0vnpg.js";
2
+ import { $ as d, $a as f, $i as p, $n as m, $o as h, $r as g, $t as _, A as v, Aa as y, Ai as b, An as x, Ao as S, Ar as C, At as w, B as T, Ba as E, Bi as D, Bn as O, Bo as k, Br as A, Bt as j, C as M, Ca as N, Ci as P, Cn as F, Co as I, Cr as L, Ct as R, D as z, Da as B, Di as V, Dn as H, Do as U, Dr as W, Dt as G, E as K, Ea as q, Ei as J, En as Y, Eo as X, Er as ie, Et as ae, F as oe, Fa as se, Fi as ce, Fn as le, Fo as ue, Fr as de, Ft as fe, G as pe, Ga as me, Gi as he, Gn as ge, Go as _e, Gr as ve, Gt as ye, H as be, Ha as xe, Hi as Se, Hn as Ce, Ho as we, Hr as Te, Ht as Ee, I as De, Ia as Oe, Ii as ke, In as Ae, Io as je, Ir as Me, It as Ne, J as Pe, Ja as Fe, Ji as Ie, Jn as Le, Jo as Re, Jr as ze, Jt as Be, K as Ve, Ka as He, Ki as Ue, Kn as We, Ko as Ge, Kr as Ke, Kt as qe, L as Je, La as Ye, Li as Xe, Ln as Ze, Lo as Qe, Lr as $e, Lt as et, M as tt, Ma as nt, Mi as rt, Mn as it, Mo as at, Mr as ot, Mt as st, N as ct, Na as lt, Ni as ut, Nn as dt, No as ft, Nr as pt, Nt as mt, O as ht, Oa as gt, Oi as _t, On as vt, Oo as yt, Or as bt, Ot as xt, P as St, Pa as Ct, Pi as wt, Pn as Tt, Po as Et, Pr as Dt, Pt as Ot, Q as kt, Qa as At, Qi as jt, Qn as Mt, Qo as Nt, Qr as Pt, Qt as Ft, R as It, Ra as Lt, Ri as Rt, Rn as zt, Ro as Bt, Rr as Vt, Rt as Ht, S as Ut, Sa as Wt, Si as Gt, Sn as Kt, So as qt, Sr as Jt, St as Yt, T as Xt, Ta as Zt, Ti as Qt, Tn as $t, To as en, Tr as tn, Tt as nn, U as rn, Ua as an, Ui as on, Un as sn, Uo as cn, Ur as ln, Ut as un, V as dn, Va as fn, Vi as pn, Vn as mn, Vo as hn, Vr as gn, Vt as _n, W as vn, Wa as yn, Wi as bn, Wn as xn, Wo as Sn, Wr as Cn, Wt as wn, X as Tn, Xa as En, Xi as Dn, Xn as On, Xo as kn, Xr as An, Xt as jn, Y as Mn, Ya as Nn, Yi as Pn, Yn as Fn, Yo as In, Yr as Ln, Yt as Rn, Z as zn, Za as Bn, Zi as Vn, Zn as Hn, Zo as Un, Zr as Wn, Zt as Gn, _ as Kn, _a as qn, _i as Jn, _n as Yn, _o as Xn, _r as Zn, _t as Qn, a as $n, aa as er, ai as tr, an as nr, ao as rr, ar as ir, at as ar, b as or, ba as sr, bi as cr, bn as lr, bo as ur, br as dr, bt as fr, c as pr, ca as mr, ci as hr, cn as gr, co as _r, cr as vr, ct as yr, d as br, da as xr, di as Sr, dn as Cr, do as wr, dr as Tr, dt as Er, ea as Dr, ei as Or, en as kr, eo as Ar, er as jr, es as Mr, et as Nr, f as Pr, fa as Fr, fi as Ir, fn as Lr, fo as Rr, fr as zr, ft as Br, g as Vr, ga as Hr, gi as Ur, gn as Wr, go as Gr, gr as Kr, gt as qr, h as Jr, ha as Yr, hi as Xr, hn as Zr, ho as Z, hr as Qr, ht as $r, i as ei, ia as ti, ii as ni, in as ri, io as ii, ir as ai, it as oi, j as si, ja as ci, ji as li, jn as ui, jo as di, jr as fi, jt as pi, k as mi, ka as hi, ki as gi, kn as _i, ko as vi, kr as yi, kt as bi, l as xi, la as Si, li as Ci, ln as wi, lo as Ti, lr as Ei, lt as Di, m as Oi, ma as ki, mi as Ai, mn as ji, mo as Mi, mr as Ni, mt as Pi, n as Fi, na as Ii, ni as Li, nn as Ri, no as zi, nr as Bi, nt as Vi, o as Hi, oa as Ui, oi as Wi, on as Gi, oo as Ki, or as qi, ot as Ji, p as Yi, pa as Xi, pi as Zi, pn as Qi, po as $i, pr as ea, pt as ta, q as na, qa as ra, qi as ia, qn as aa, qo as oa, qr as sa, qt as ca, r as la, ra as ua, ri as da, rn as fa, ro as pa, rr as ma, rt as ha, s as ga, sa as _a, si as va, sn as ya, so as ba, sr as xa, st as Sa, t as Ca, ta as wa, ti as Ta, tn as Ea, to as Da, tr as Oa, ts as ka, tt as Aa, u as ja, ua as Ma, ui as Na, un as Pa, uo as Fa, ur as Ia, ut as La, v as Ra, va as za, vi as Ba, vn as Va, vo as Ha, vr as Ua, vt as Wa, w as Ga, wa as Ka, wi as qa, wn as Ja, wo as Ya, wr as Xa, wt as Za, x as Qa, xa as $a, xi as eo, xn as to, xo as no, xr as ro, xt as io, y as ao, ya as oo, yi as so, yn as co, yo as lo, yr as uo, yt as fo, z as po, za as mo, zi as ho, zn as go, zo as _o, zr as vo, zt as yo } from "./src-B_YS5AOc.js";
3
3
  import { a as bo, i as xo, n as So, o as Co, r as wo, s as To, t as Eo } from "./editor-registry-BhgE3S84.js";
4
4
  import { A as Do, C as Oo, D as ko, E as Ao, F as jo, I as Mo, L as No, M as Po, N as Fo, O as Io, P as Lo, R as Ro, S as zo, T as Bo, _ as Vo, a as Ho, b as Uo, c as Wo, d as Go, f as Ko, g as qo, h as Jo, i as Yo, j as Xo, k as Zo, l as Qo, m as $o, n as es, o as ts, p as ns, r as rs, s as is, t as as, u as os, v as ss, w as cs, x as ls, y as us, z as ds } from "./date-format-DZT7T1wf.js";
5
5
  import { i as fs, n as ps, r as ms, t as hs } from "./popover-P-9P2YYn.js";
@@ -1,5 +1,5 @@
1
1
  import { _ as ee, c as te, d as e, f as ne, h as re, i as t, l as n, m as r, n as i, o as a, p as o, r as s, s as c, t as l, u } from "./export-format-Cv1Dll6k.js";
2
- import { $ as d, $a as f, $i as p, $n as m, $o as h, $r as g, $t as _, A as v, Aa as y, Ai as b, An as x, Ao as S, Ar as C, At as w, B as T, Ba as E, Bi as D, Bn as O, Bo as k, Br as A, Bt as j, C as M, Ca as N, Ci as P, Cn as F, Co as I, Cr as L, Ct as R, D as z, Da as B, Di as V, Dn as H, Do as U, Dr as W, Dt as G, E as K, Ea as q, Ei as J, En as Y, Eo as X, Er as ie, Et as ae, F as oe, Fa as se, Fi as ce, Fn as le, Fo as ue, Fr as de, Ft as fe, G as pe, Ga as me, Gi as he, Gn as ge, Go as _e, Gr as ve, Gt as ye, H as be, Ha as xe, Hi as Se, Hn as Ce, Ho as we, Hr as Te, Ht as Ee, I as De, Ia as Oe, Ii as ke, In as Ae, Io as je, Ir as Me, It as Ne, J as Pe, Ja as Fe, Ji as Ie, Jn as Le, Jo as Re, Jr as ze, Jt as Be, K as Ve, Ka as He, Ki as Ue, Kn as We, Ko as Ge, Kr as Ke, Kt as qe, L as Je, La as Ye, Li as Xe, Ln as Ze, Lo as Qe, Lr as $e, Lt as et, M as tt, Ma as nt, Mi as rt, Mn as it, Mo as at, Mr as ot, Mt as st, N as ct, Na as lt, Ni as ut, Nn as dt, No as ft, Nr as pt, Nt as mt, O as ht, Oa as gt, Oi as _t, On as vt, Oo as yt, Or as bt, Ot as xt, P as St, Pa as Ct, Pi as wt, Pn as Tt, Po as Et, Pr as Dt, Pt as Ot, Q as kt, Qa as At, Qi as jt, Qn as Mt, Qo as Nt, Qr as Pt, Qt as Ft, R as It, Ra as Lt, Ri as Rt, Rn as zt, Ro as Bt, Rr as Vt, Rt as Ht, S as Ut, Sa as Wt, Si as Gt, Sn as Kt, So as qt, Sr as Jt, St as Yt, T as Xt, Ta as Zt, Ti as Qt, Tn as $t, To as en, Tr as tn, Tt as nn, U as rn, Ua as an, Ui as on, Un as sn, Uo as cn, Ur as ln, Ut as un, V as dn, Va as fn, Vi as pn, Vn as mn, Vo as hn, Vr as gn, Vt as _n, W as vn, Wa as yn, Wi as bn, Wn as xn, Wo as Sn, Wr as Cn, Wt as wn, X as Tn, Xa as En, Xi as Dn, Xn as On, Xo as kn, Xr as An, Xt as jn, Y as Mn, Ya as Nn, Yi as Pn, Yn as Fn, Yo as In, Yr as Ln, Yt as Rn, Z as zn, Za as Bn, Zi as Vn, Zn as Hn, Zo as Un, Zr as Wn, Zt as Gn, _ as Kn, _a as qn, _i as Jn, _n as Yn, _o as Xn, _r as Zn, _t as Qn, a as $n, aa as er, ai as tr, an as nr, ao as rr, ar as ir, at as ar, b as or, ba as sr, bi as cr, bn as lr, bo as ur, br as dr, bt as fr, c as pr, ca as mr, ci as hr, cn as gr, co as _r, cr as vr, ct as yr, d as br, da as xr, di as Sr, dn as Cr, do as wr, dr as Tr, dt as Er, ea as Dr, ei as Or, en as kr, eo as Ar, er as jr, es as Mr, et as Nr, f as Pr, fa as Fr, fi as Ir, fn as Lr, fo as Rr, fr as zr, ft as Br, g as Vr, ga as Hr, gi as Ur, gn as Wr, go as Gr, gr as Kr, gt as qr, h as Jr, ha as Yr, hi as Xr, hn as Zr, ho as Z, hr as Qr, ht as $r, i as ei, ia as ti, ii as ni, in as ri, io as ii, ir as ai, it as oi, j as si, ja as ci, ji as li, jn as ui, jo as di, jr as fi, jt as pi, k as mi, ka as hi, ki as gi, kn as _i, ko as vi, kr as yi, kt as bi, l as xi, la as Si, li as Ci, ln as wi, lo as Ti, lr as Ei, lt as Di, m as Oi, ma as ki, mi as Ai, mn as ji, mo as Mi, mr as Ni, mt as Pi, n as Fi, na as Ii, ni as Li, nn as Ri, no as zi, nr as Bi, nt as Vi, o as Hi, oa as Ui, oi as Wi, on as Gi, oo as Ki, or as qi, ot as Ji, p as Yi, pa as Xi, pi as Zi, pn as Qi, po as $i, pr as ea, pt as ta, q as na, qa as ra, qi as ia, qn as aa, qo as oa, qr as sa, qt as ca, r as la, ra as ua, ri as da, rn as fa, ro as pa, rr as ma, rt as ha, s as ga, sa as _a, si as va, sn as ya, so as ba, sr as xa, st as Sa, t as Ca, ta as wa, ti as Ta, tn as Ea, to as Da, tr as Oa, ts as ka, tt as Aa, u as ja, ua as Ma, ui as Na, un as Pa, uo as Fa, ur as Ia, ut as La, v as Ra, va as za, vi as Ba, vn as Va, vo as Ha, vr as Ua, vt as Wa, w as Ga, wa as Ka, wi as qa, wn as Ja, wo as Ya, wr as Xa, wt as Za, x as Qa, xa as $a, xi as eo, xn as to, xo as no, xr as ro, xt as io, y as ao, ya as oo, yi as so, yn as co, yo as lo, yr as uo, yt as fo, z as po, za as mo, zi as ho, zn as go, zo as _o, zr as vo, zt as yo } from "./src-pYGV2rMR.js";
2
+ import { $ as d, $a as f, $i as p, $n as m, $o as h, $r as g, $t as _, A as v, Aa as y, Ai as b, An as x, Ao as S, Ar as C, At as w, B as T, Ba as E, Bi as D, Bn as O, Bo as k, Br as A, Bt as j, C as M, Ca as N, Ci as P, Cn as F, Co as I, Cr as L, Ct as R, D as z, Da as B, Di as V, Dn as H, Do as U, Dr as W, Dt as G, E as K, Ea as q, Ei as J, En as Y, Eo as X, Er as ie, Et as ae, F as oe, Fa as se, Fi as ce, Fn as le, Fo as ue, Fr as de, Ft as fe, G as pe, Ga as me, Gi as he, Gn as ge, Go as _e, Gr as ve, Gt as ye, H as be, Ha as xe, Hi as Se, Hn as Ce, Ho as we, Hr as Te, Ht as Ee, I as De, Ia as Oe, Ii as ke, In as Ae, Io as je, Ir as Me, It as Ne, J as Pe, Ja as Fe, Ji as Ie, Jn as Le, Jo as Re, Jr as ze, Jt as Be, K as Ve, Ka as He, Ki as Ue, Kn as We, Ko as Ge, Kr as Ke, Kt as qe, L as Je, La as Ye, Li as Xe, Ln as Ze, Lo as Qe, Lr as $e, Lt as et, M as tt, Ma as nt, Mi as rt, Mn as it, Mo as at, Mr as ot, Mt as st, N as ct, Na as lt, Ni as ut, Nn as dt, No as ft, Nr as pt, Nt as mt, O as ht, Oa as gt, Oi as _t, On as vt, Oo as yt, Or as bt, Ot as xt, P as St, Pa as Ct, Pi as wt, Pn as Tt, Po as Et, Pr as Dt, Pt as Ot, Q as kt, Qa as At, Qi as jt, Qn as Mt, Qo as Nt, Qr as Pt, Qt as Ft, R as It, Ra as Lt, Ri as Rt, Rn as zt, Ro as Bt, Rr as Vt, Rt as Ht, S as Ut, Sa as Wt, Si as Gt, Sn as Kt, So as qt, Sr as Jt, St as Yt, T as Xt, Ta as Zt, Ti as Qt, Tn as $t, To as en, Tr as tn, Tt as nn, U as rn, Ua as an, Ui as on, Un as sn, Uo as cn, Ur as ln, Ut as un, V as dn, Va as fn, Vi as pn, Vn as mn, Vo as hn, Vr as gn, Vt as _n, W as vn, Wa as yn, Wi as bn, Wn as xn, Wo as Sn, Wr as Cn, Wt as wn, X as Tn, Xa as En, Xi as Dn, Xn as On, Xo as kn, Xr as An, Xt as jn, Y as Mn, Ya as Nn, Yi as Pn, Yn as Fn, Yo as In, Yr as Ln, Yt as Rn, Z as zn, Za as Bn, Zi as Vn, Zn as Hn, Zo as Un, Zr as Wn, Zt as Gn, _ as Kn, _a as qn, _i as Jn, _n as Yn, _o as Xn, _r as Zn, _t as Qn, a as $n, aa as er, ai as tr, an as nr, ao as rr, ar as ir, at as ar, b as or, ba as sr, bi as cr, bn as lr, bo as ur, br as dr, bt as fr, c as pr, ca as mr, ci as hr, cn as gr, co as _r, cr as vr, ct as yr, d as br, da as xr, di as Sr, dn as Cr, do as wr, dr as Tr, dt as Er, ea as Dr, ei as Or, en as kr, eo as Ar, er as jr, es as Mr, et as Nr, f as Pr, fa as Fr, fi as Ir, fn as Lr, fo as Rr, fr as zr, ft as Br, g as Vr, ga as Hr, gi as Ur, gn as Wr, go as Gr, gr as Kr, gt as qr, h as Jr, ha as Yr, hi as Xr, hn as Zr, ho as Z, hr as Qr, ht as $r, i as ei, ia as ti, ii as ni, in as ri, io as ii, ir as ai, it as oi, j as si, ja as ci, ji as li, jn as ui, jo as di, jr as fi, jt as pi, k as mi, ka as hi, ki as gi, kn as _i, ko as vi, kr as yi, kt as bi, l as xi, la as Si, li as Ci, ln as wi, lo as Ti, lr as Ei, lt as Di, m as Oi, ma as ki, mi as Ai, mn as ji, mo as Mi, mr as Ni, mt as Pi, n as Fi, na as Ii, ni as Li, nn as Ri, no as zi, nr as Bi, nt as Vi, o as Hi, oa as Ui, oi as Wi, on as Gi, oo as Ki, or as qi, ot as Ji, p as Yi, pa as Xi, pi as Zi, pn as Qi, po as $i, pr as ea, pt as ta, q as na, qa as ra, qi as ia, qn as aa, qo as oa, qr as sa, qt as ca, r as la, ra as ua, ri as da, rn as fa, ro as pa, rr as ma, rt as ha, s as ga, sa as _a, si as va, sn as ya, so as ba, sr as xa, st as Sa, t as Ca, ta as wa, ti as Ta, tn as Ea, to as Da, tr as Oa, ts as ka, tt as Aa, u as ja, ua as Ma, ui as Na, un as Pa, uo as Fa, ur as Ia, ut as La, v as Ra, va as za, vi as Ba, vn as Va, vo as Ha, vr as Ua, vt as Wa, w as Ga, wa as Ka, wi as qa, wn as Ja, wo as Ya, wr as Xa, wt as Za, x as Qa, xa as $a, xi as eo, xn as to, xo as no, xr as ro, xt as io, y as ao, ya as oo, yi as so, yn as co, yo as lo, yr as uo, yt as fo, z as po, za as mo, zi as ho, zn as go, zo as _o, zr as vo, zt as yo } from "./src-DYXXpuSk.js";
3
3
  import { a as bo, i as xo, n as So, o as Co, r as wo, s as To, t as Eo } from "./editor-registry-BhgE3S84.js";
4
4
  import { A as Do, C as Oo, D as ko, E as Ao, F as jo, I as Mo, L as No, M as Po, N as Fo, O as Io, P as Lo, R as Ro, S as zo, T as Bo, _ as Vo, a as Ho, b as Uo, c as Wo, d as Go, f as Ko, g as qo, h as Jo, i as Yo, j as Xo, k as Zo, l as Qo, m as $o, n as es, o as ts, p as ns, r as rs, s as is, t as as, u as os, v as ss, w as cs, x as ls, y as us, z as ds } from "./date-format-CcP1tafP.js";
5
5
  import { i as fs, n as ps, r as ms, t as hs } from "./popover-P-9P2YYn.js";
@@ -12,7 +12,11 @@ export type TouchDragDeps = {
12
12
  };
13
13
  /** What {@link createTouchDrag} returns. */
14
14
  export type TouchDragHandle = {
15
- start: (e: PointerEvent, rowIndex: number) => void;
15
+ /**
16
+ * @param pressStartedAt `performance.now()` at the pointerdown that began
17
+ * this press. Defaults to now. See the long-press comment in `start`.
18
+ */
19
+ start: (e: PointerEvent, rowIndex: number, pressStartedAt?: number) => void;
16
20
  destroy: () => void;
17
21
  };
18
22
  export declare function createTouchDrag(deps: TouchDragDeps): TouchDragHandle;
@@ -8,8 +8,10 @@
8
8
  // `onRowDragEnd` payload cannot drift between mouse and touch.
9
9
  //
10
10
  // Loaded with `import()` on the first touch that lands on a draggable row, so
11
- // desktop users never download it. The 350 ms long press below more than covers
12
- // the fetch, and the drag cannot begin before it elapses anyway.
11
+ // desktop users never download it. The 350 ms long press below absorbs the
12
+ // fetch rather than following it: `start` takes the pointerdown timestamp and
13
+ // subtracts the time already spent waiting, so a fetch that finishes inside the
14
+ // press costs the user nothing.
13
15
  //
14
16
  // A touch that starts on a row must still be able to SCROLL the grid, so the
15
17
  // drag only begins after a long press; moving before that threshold hands the
@@ -138,7 +140,7 @@ export function createTouchDrag(deps) {
138
140
  }
139
141
  return {
140
142
  /** Begin a press that may become a drag. Caller has already checked touch. */
141
- start(e, rowIndex) {
143
+ start(e, rowIndex, pressStartedAt) {
142
144
  if (!deps.managed() || touch)
143
145
  return;
144
146
  const row = deps.originalAt(rowIndex);
@@ -160,6 +162,15 @@ export function createTouchDrag(deps) {
160
162
  window.addEventListener("pointerup", onPointerUp);
161
163
  window.addEventListener("pointercancel", onPointerCancel);
162
164
  window.addEventListener("touchmove", blockTouchScroll, { passive: false });
165
+ // The long press is measured from when the finger LANDED, not from when
166
+ // this module finished loading. `row-drag.ts` fetches it with import() on
167
+ // the first touch, and that fetch is not free - measured at ~190 ms cold.
168
+ // Starting the clock here would make the real cost `fetch + 350 ms`, so a
169
+ // user on a slow connection would have to hold noticeably longer for
170
+ // their first drag than for every one after it. Subtracting the elapsed
171
+ // time keeps it at 350 ms from the finger's point of view, which is what
172
+ // the comment at the top of this file has always claimed.
173
+ const waited = pressStartedAt == null ? 0 : Math.max(0, performance.now() - pressStartedAt);
163
174
  touch.timer = setTimeout(() => {
164
175
  if (!touch)
165
176
  return;
@@ -176,7 +187,7 @@ export function createTouchDrag(deps) {
176
187
  ctx.rowDragActive = true;
177
188
  ctx.rowDropIndex = rowIndex;
178
189
  ctx.rowDropSide = "before";
179
- }, LONG_PRESS_MS);
190
+ }, Math.max(0, LONG_PRESS_MS - waited));
180
191
  },
181
192
  destroy() {
182
193
  end(false);
package/dist/row-drag.js CHANGED
@@ -202,8 +202,8 @@ export function createRowDrag(ctx) {
202
202
  }
203
203
  // Touch drag (#66) lives in its own module and is fetched on the first touch
204
204
  // that lands on a draggable row, so desktop never downloads it. The 350 ms
205
- // long press inside it more than covers the fetch - the drag cannot start
206
- // before that elapses anyway.
205
+ // long press inside it absorbs the fetch: the press clock starts at
206
+ // pointerdown here, so the fetch is free unless it outlasts the press.
207
207
  let touchDrag = null;
208
208
  let touchLoading = false;
209
209
  function touchDeps() {
@@ -252,6 +252,10 @@ export function createRowDrag(ctx) {
252
252
  const pid = e.pointerId;
253
253
  const startX = e.clientX;
254
254
  const startY = e.clientY;
255
+ // Handed to the touch module so its long press is counted from HERE, not
256
+ // from whenever the import below resolves. Without it the first touch drag
257
+ // of a session costs `fetch + 350 ms` while every later one costs 350 ms.
258
+ const pressStartedAt = performance.now();
255
259
  let aborted = false;
256
260
  function stopWatching() {
257
261
  window.removeEventListener("pointermove", onEarlyMove);
@@ -292,7 +296,7 @@ export function createRowDrag(ctx) {
292
296
  if (aborted || !managed())
293
297
  return;
294
298
  touchDrag = m.createTouchDrag(touchDeps());
295
- touchDrag.start(snapshot, rowIndex);
299
+ touchDrag.start(snapshot, rowIndex, pressStartedAt);
296
300
  });
297
301
  }
298
302
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svgrid/grid",
3
- "version": "2.7.0",
3
+ "version": "3.0.0",
4
4
  "description": "Svelte 5-native data grid and data table. Headless-first engine with virtual scrolling for 100k+ rows, Excel-style filtering, inline editing, grouping, tree data, pivot, and server-side data. Drop-in <SvGrid> component. TypeScript, SSR-ready.",
5
5
  "author": "jQWidgets <boikom@jqwidgets.com>",
6
6
  "license": "MIT",
@@ -1,211 +1,226 @@
1
- // Touch / pointer row dragging (#66).
2
- //
3
- // HTML5 drag events do not exist for touch input on iOS Safari or Android
4
- // Chrome - `dragstart` never fires - so the drag path in `row-drag.ts` is dead
5
- // on a phone. This is a second INPUT path onto the same model: it populates the
6
- // same cross-grid `bus` and the same `rowDropIndex` / `rowDropSide`, and commits
7
- // through the same `commitDropOnRow`, so reorder, cross-grid hand-off and the
8
- // `onRowDragEnd` payload cannot drift between mouse and touch.
9
- //
10
- // Loaded with `import()` on the first touch that lands on a draggable row, so
11
- // desktop users never download it. The 350 ms long press below more than covers
12
- // the fetch, and the drag cannot begin before it elapses anyway.
13
- //
14
- // A touch that starts on a row must still be able to SCROLL the grid, so the
15
- // drag only begins after a long press; moving before that threshold hands the
16
- // gesture back to the browser.
17
-
18
- /** Everything the touch path needs from the `createRowDrag` closure. */
19
- export type TouchDragDeps = {
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- ctx: any;
22
- managed: () => boolean;
23
- originalAt: (rowIndex: number) => unknown;
24
- commitDropOnRow: (rowIndex: number, side: "before" | "after") => void;
25
- clearIndicators: () => void;
26
- cancelPendingLeave: () => void;
27
- /** Publish the dragged row on the shared cross-grid bus. */
28
- openBus: (row: unknown) => void;
29
- closeBus: () => void;
30
- };
31
-
32
- const LONG_PRESS_MS = 350;
33
- /** Movement (px) before the long press is treated as a scroll instead. */
34
- const SCROLL_SLOP_PX = 10;
35
- /** Distance from an edge (px) at which the grid starts auto-scrolling. */
36
- const EDGE_PX = 48;
37
- const EDGE_SPEED_PX = 12;
38
-
39
- type TouchState = {
40
- pointerId: number;
41
- rowIndex: number;
42
- startX: number;
43
- startY: number;
44
- started: boolean;
45
- timer: ReturnType<typeof setTimeout> | null;
46
- scrollFrame: number | null;
47
- scroller: HTMLElement | null;
48
- root: HTMLElement | null;
49
- };
50
-
51
- /** What {@link createTouchDrag} returns. */
52
- export type TouchDragHandle = {
53
- start: (e: PointerEvent, rowIndex: number) => void;
54
- destroy: () => void;
55
- };
56
-
57
- export function createTouchDrag(deps: TouchDragDeps): TouchDragHandle {
58
- const { ctx } = deps;
59
- let touch: TouchState | null = null;
60
-
61
- /** Resolve the row under a viewport point, and which half of it. */
62
- function rowAtPoint(x: number, y: number): { index: number; side: "before" | "after" } | null {
63
- const el = document.elementFromPoint(x, y) as HTMLElement | null;
64
- const tr = el?.closest?.("tr.sv-grid-row") as HTMLElement | null;
65
- if (!tr) return null;
66
- const marker = tr.querySelector<HTMLElement>("[data-svgrid-row]");
67
- const raw = marker?.dataset.svgridRow;
68
- if (raw == null) return null;
69
- const index = Number(raw);
70
- if (!Number.isFinite(index)) return null;
71
- const rect = tr.getBoundingClientRect();
72
- return { index, side: y < rect.top + rect.height / 2 ? "before" : "after" };
73
- }
74
-
75
- function stopEdgeScroll() {
76
- if (touch?.scrollFrame != null) {
77
- cancelAnimationFrame(touch.scrollFrame);
78
- touch.scrollFrame = null;
79
- }
80
- }
81
-
82
- /** Keep scrolling while the finger is parked near the top or bottom edge. */
83
- function edgeScroll(clientY: number) {
84
- const scroller = touch?.scroller;
85
- if (!touch || !scroller) return;
86
- const rect = scroller.getBoundingClientRect();
87
- let dy = 0;
88
- if (clientY < rect.top + EDGE_PX) dy = -EDGE_SPEED_PX;
89
- else if (clientY > rect.bottom - EDGE_PX) dy = EDGE_SPEED_PX;
90
- stopEdgeScroll();
91
- if (dy === 0) return;
92
- const step = () => {
93
- if (!touch?.started) return;
94
- scroller.scrollTop += dy;
95
- // Re-hit-test as rows move under a stationary finger, otherwise the
96
- // indicator freezes on whichever row happened to be there at touch time.
97
- const hit = rowAtPoint(touch.startX, clientY);
98
- if (hit) {
99
- ctx.rowDropIndex = hit.index;
100
- ctx.rowDropSide = hit.side;
101
- }
102
- touch.scrollFrame = requestAnimationFrame(step);
103
- };
104
- touch.scrollFrame = requestAnimationFrame(step);
105
- }
106
-
107
- // `pointermove.preventDefault()` does NOT stop iOS scrolling; only a
108
- // non-passive `touchmove` does. Registered for the life of the drag.
109
- function blockTouchScroll(e: TouchEvent) {
110
- if (touch?.started) e.preventDefault();
111
- }
112
-
113
- function detach() {
114
- window.removeEventListener("pointermove", onPointerMove);
115
- window.removeEventListener("pointerup", onPointerUp);
116
- window.removeEventListener("pointercancel", onPointerCancel);
117
- window.removeEventListener("touchmove", blockTouchScroll);
118
- }
119
-
120
- function end(commit: boolean) {
121
- if (!touch) return;
122
- const t = touch;
123
- if (t.timer) clearTimeout(t.timer);
124
- stopEdgeScroll();
125
- touch = null;
126
- detach();
127
- t.root?.classList.remove("sv-grid-row-drag-active");
128
- if (!t.started) return;
129
- if (commit && ctx.rowDropIndex != null) {
130
- deps.commitDropOnRow(
131
- ctx.rowDropIndex as number,
132
- (ctx.rowDropSide ?? "before") as "before" | "after",
133
- );
134
- } else {
135
- deps.clearIndicators();
136
- deps.closeBus();
137
- }
138
- }
139
-
140
- function onPointerMove(e: PointerEvent) {
141
- if (!touch || e.pointerId !== touch.pointerId) return;
142
- if (!touch.started) {
143
- // Still inside the long press: a real move means the user is scrolling.
144
- const moved = Math.abs(e.clientY - touch.startY) + Math.abs(e.clientX - touch.startX);
145
- if (moved > SCROLL_SLOP_PX) end(false);
146
- return;
147
- }
148
- e.preventDefault();
149
- const hit = rowAtPoint(e.clientX, e.clientY);
150
- if (hit) {
151
- deps.cancelPendingLeave();
152
- ctx.rowDropIndex = hit.index;
153
- ctx.rowDropSide = hit.side;
154
- }
155
- touch.startX = e.clientX;
156
- edgeScroll(e.clientY);
157
- }
158
-
159
- function onPointerUp(e: PointerEvent) {
160
- if (!touch || e.pointerId !== touch.pointerId) return;
161
- end(true);
162
- }
163
-
164
- function onPointerCancel(e: PointerEvent) {
165
- if (!touch || e.pointerId !== touch.pointerId) return;
166
- end(false);
167
- }
168
-
169
- return {
170
- /** Begin a press that may become a drag. Caller has already checked touch. */
171
- start(e: PointerEvent, rowIndex: number) {
172
- if (!deps.managed() || touch) return;
173
- const row = deps.originalAt(rowIndex);
174
- if (row == null) return;
175
- const target = e.currentTarget as HTMLElement | null;
176
- touch = {
177
- pointerId: e.pointerId,
178
- rowIndex,
179
- startX: e.clientX,
180
- startY: e.clientY,
181
- started: false,
182
- timer: null,
183
- scrollFrame: null,
184
- scroller: target?.closest?.<HTMLElement>(".sv-grid-container") ?? null,
185
- root: target?.closest?.<HTMLElement>(".sv-grid-root") ?? null,
186
- };
187
- window.addEventListener("pointermove", onPointerMove, { passive: false });
188
- window.addEventListener("pointerup", onPointerUp);
189
- window.addEventListener("pointercancel", onPointerCancel);
190
- window.addEventListener("touchmove", blockTouchScroll, { passive: false });
191
- touch.timer = setTimeout(() => {
192
- if (!touch) return;
193
- touch.started = true;
194
- touch.timer = null;
195
- touch.root?.classList.add("sv-grid-row-drag-active");
196
- try {
197
- target?.setPointerCapture?.(e.pointerId);
198
- } catch {
199
- // Not every environment implements pointer capture (tests, old WebViews).
200
- }
201
- deps.openBus(row);
202
- ctx.rowDragActive = true;
203
- ctx.rowDropIndex = rowIndex;
204
- ctx.rowDropSide = "before";
205
- }, LONG_PRESS_MS);
206
- },
207
- destroy() {
208
- end(false);
209
- },
210
- };
211
- }
1
+ // Touch / pointer row dragging (#66).
2
+ //
3
+ // HTML5 drag events do not exist for touch input on iOS Safari or Android
4
+ // Chrome - `dragstart` never fires - so the drag path in `row-drag.ts` is dead
5
+ // on a phone. This is a second INPUT path onto the same model: it populates the
6
+ // same cross-grid `bus` and the same `rowDropIndex` / `rowDropSide`, and commits
7
+ // through the same `commitDropOnRow`, so reorder, cross-grid hand-off and the
8
+ // `onRowDragEnd` payload cannot drift between mouse and touch.
9
+ //
10
+ // Loaded with `import()` on the first touch that lands on a draggable row, so
11
+ // desktop users never download it. The 350 ms long press below absorbs the
12
+ // fetch rather than following it: `start` takes the pointerdown timestamp and
13
+ // subtracts the time already spent waiting, so a fetch that finishes inside the
14
+ // press costs the user nothing.
15
+ //
16
+ // A touch that starts on a row must still be able to SCROLL the grid, so the
17
+ // drag only begins after a long press; moving before that threshold hands the
18
+ // gesture back to the browser.
19
+
20
+ /** Everything the touch path needs from the `createRowDrag` closure. */
21
+ export type TouchDragDeps = {
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ ctx: any;
24
+ managed: () => boolean;
25
+ originalAt: (rowIndex: number) => unknown;
26
+ commitDropOnRow: (rowIndex: number, side: "before" | "after") => void;
27
+ clearIndicators: () => void;
28
+ cancelPendingLeave: () => void;
29
+ /** Publish the dragged row on the shared cross-grid bus. */
30
+ openBus: (row: unknown) => void;
31
+ closeBus: () => void;
32
+ };
33
+
34
+ const LONG_PRESS_MS = 350;
35
+ /** Movement (px) before the long press is treated as a scroll instead. */
36
+ const SCROLL_SLOP_PX = 10;
37
+ /** Distance from an edge (px) at which the grid starts auto-scrolling. */
38
+ const EDGE_PX = 48;
39
+ const EDGE_SPEED_PX = 12;
40
+
41
+ type TouchState = {
42
+ pointerId: number;
43
+ rowIndex: number;
44
+ startX: number;
45
+ startY: number;
46
+ started: boolean;
47
+ timer: ReturnType<typeof setTimeout> | null;
48
+ scrollFrame: number | null;
49
+ scroller: HTMLElement | null;
50
+ root: HTMLElement | null;
51
+ };
52
+
53
+ /** What {@link createTouchDrag} returns. */
54
+ export type TouchDragHandle = {
55
+ /**
56
+ * @param pressStartedAt `performance.now()` at the pointerdown that began
57
+ * this press. Defaults to now. See the long-press comment in `start`.
58
+ */
59
+ start: (e: PointerEvent, rowIndex: number, pressStartedAt?: number) => void;
60
+ destroy: () => void;
61
+ };
62
+
63
+ export function createTouchDrag(deps: TouchDragDeps): TouchDragHandle {
64
+ const { ctx } = deps;
65
+ let touch: TouchState | null = null;
66
+
67
+ /** Resolve the row under a viewport point, and which half of it. */
68
+ function rowAtPoint(x: number, y: number): { index: number; side: "before" | "after" } | null {
69
+ const el = document.elementFromPoint(x, y) as HTMLElement | null;
70
+ const tr = el?.closest?.("tr.sv-grid-row") as HTMLElement | null;
71
+ if (!tr) return null;
72
+ const marker = tr.querySelector<HTMLElement>("[data-svgrid-row]");
73
+ const raw = marker?.dataset.svgridRow;
74
+ if (raw == null) return null;
75
+ const index = Number(raw);
76
+ if (!Number.isFinite(index)) return null;
77
+ const rect = tr.getBoundingClientRect();
78
+ return { index, side: y < rect.top + rect.height / 2 ? "before" : "after" };
79
+ }
80
+
81
+ function stopEdgeScroll() {
82
+ if (touch?.scrollFrame != null) {
83
+ cancelAnimationFrame(touch.scrollFrame);
84
+ touch.scrollFrame = null;
85
+ }
86
+ }
87
+
88
+ /** Keep scrolling while the finger is parked near the top or bottom edge. */
89
+ function edgeScroll(clientY: number) {
90
+ const scroller = touch?.scroller;
91
+ if (!touch || !scroller) return;
92
+ const rect = scroller.getBoundingClientRect();
93
+ let dy = 0;
94
+ if (clientY < rect.top + EDGE_PX) dy = -EDGE_SPEED_PX;
95
+ else if (clientY > rect.bottom - EDGE_PX) dy = EDGE_SPEED_PX;
96
+ stopEdgeScroll();
97
+ if (dy === 0) return;
98
+ const step = () => {
99
+ if (!touch?.started) return;
100
+ scroller.scrollTop += dy;
101
+ // Re-hit-test as rows move under a stationary finger, otherwise the
102
+ // indicator freezes on whichever row happened to be there at touch time.
103
+ const hit = rowAtPoint(touch.startX, clientY);
104
+ if (hit) {
105
+ ctx.rowDropIndex = hit.index;
106
+ ctx.rowDropSide = hit.side;
107
+ }
108
+ touch.scrollFrame = requestAnimationFrame(step);
109
+ };
110
+ touch.scrollFrame = requestAnimationFrame(step);
111
+ }
112
+
113
+ // `pointermove.preventDefault()` does NOT stop iOS scrolling; only a
114
+ // non-passive `touchmove` does. Registered for the life of the drag.
115
+ function blockTouchScroll(e: TouchEvent) {
116
+ if (touch?.started) e.preventDefault();
117
+ }
118
+
119
+ function detach() {
120
+ window.removeEventListener("pointermove", onPointerMove);
121
+ window.removeEventListener("pointerup", onPointerUp);
122
+ window.removeEventListener("pointercancel", onPointerCancel);
123
+ window.removeEventListener("touchmove", blockTouchScroll);
124
+ }
125
+
126
+ function end(commit: boolean) {
127
+ if (!touch) return;
128
+ const t = touch;
129
+ if (t.timer) clearTimeout(t.timer);
130
+ stopEdgeScroll();
131
+ touch = null;
132
+ detach();
133
+ t.root?.classList.remove("sv-grid-row-drag-active");
134
+ if (!t.started) return;
135
+ if (commit && ctx.rowDropIndex != null) {
136
+ deps.commitDropOnRow(
137
+ ctx.rowDropIndex as number,
138
+ (ctx.rowDropSide ?? "before") as "before" | "after",
139
+ );
140
+ } else {
141
+ deps.clearIndicators();
142
+ deps.closeBus();
143
+ }
144
+ }
145
+
146
+ function onPointerMove(e: PointerEvent) {
147
+ if (!touch || e.pointerId !== touch.pointerId) return;
148
+ if (!touch.started) {
149
+ // Still inside the long press: a real move means the user is scrolling.
150
+ const moved = Math.abs(e.clientY - touch.startY) + Math.abs(e.clientX - touch.startX);
151
+ if (moved > SCROLL_SLOP_PX) end(false);
152
+ return;
153
+ }
154
+ e.preventDefault();
155
+ const hit = rowAtPoint(e.clientX, e.clientY);
156
+ if (hit) {
157
+ deps.cancelPendingLeave();
158
+ ctx.rowDropIndex = hit.index;
159
+ ctx.rowDropSide = hit.side;
160
+ }
161
+ touch.startX = e.clientX;
162
+ edgeScroll(e.clientY);
163
+ }
164
+
165
+ function onPointerUp(e: PointerEvent) {
166
+ if (!touch || e.pointerId !== touch.pointerId) return;
167
+ end(true);
168
+ }
169
+
170
+ function onPointerCancel(e: PointerEvent) {
171
+ if (!touch || e.pointerId !== touch.pointerId) return;
172
+ end(false);
173
+ }
174
+
175
+ return {
176
+ /** Begin a press that may become a drag. Caller has already checked touch. */
177
+ start(e: PointerEvent, rowIndex: number, pressStartedAt?: number) {
178
+ if (!deps.managed() || touch) return;
179
+ const row = deps.originalAt(rowIndex);
180
+ if (row == null) return;
181
+ const target = e.currentTarget as HTMLElement | null;
182
+ touch = {
183
+ pointerId: e.pointerId,
184
+ rowIndex,
185
+ startX: e.clientX,
186
+ startY: e.clientY,
187
+ started: false,
188
+ timer: null,
189
+ scrollFrame: null,
190
+ scroller: target?.closest?.<HTMLElement>(".sv-grid-container") ?? null,
191
+ root: target?.closest?.<HTMLElement>(".sv-grid-root") ?? null,
192
+ };
193
+ window.addEventListener("pointermove", onPointerMove, { passive: false });
194
+ window.addEventListener("pointerup", onPointerUp);
195
+ window.addEventListener("pointercancel", onPointerCancel);
196
+ window.addEventListener("touchmove", blockTouchScroll, { passive: false });
197
+ // The long press is measured from when the finger LANDED, not from when
198
+ // this module finished loading. `row-drag.ts` fetches it with import() on
199
+ // the first touch, and that fetch is not free - measured at ~190 ms cold.
200
+ // Starting the clock here would make the real cost `fetch + 350 ms`, so a
201
+ // user on a slow connection would have to hold noticeably longer for
202
+ // their first drag than for every one after it. Subtracting the elapsed
203
+ // time keeps it at 350 ms from the finger's point of view, which is what
204
+ // the comment at the top of this file has always claimed.
205
+ const waited = pressStartedAt == null ? 0 : Math.max(0, performance.now() - pressStartedAt);
206
+ touch.timer = setTimeout(() => {
207
+ if (!touch) return;
208
+ touch.started = true;
209
+ touch.timer = null;
210
+ touch.root?.classList.add("sv-grid-row-drag-active");
211
+ try {
212
+ target?.setPointerCapture?.(e.pointerId);
213
+ } catch {
214
+ // Not every environment implements pointer capture (tests, old WebViews).
215
+ }
216
+ deps.openBus(row);
217
+ ctx.rowDragActive = true;
218
+ ctx.rowDropIndex = rowIndex;
219
+ ctx.rowDropSide = "before";
220
+ }, Math.max(0, LONG_PRESS_MS - waited));
221
+ },
222
+ destroy() {
223
+ end(false);
224
+ },
225
+ };
226
+ }
@@ -6,7 +6,7 @@
6
6
  * Because the bus is shared, we can create TWO fake ctxs (two grids) and drive
7
7
  * a genuine grid-to-grid move without mounting anything.
8
8
  */
9
- import { afterEach, describe, expect, it, vi } from 'vitest'
9
+ import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
10
10
  import { createRowDrag, rowDropZone } from './row-drag'
11
11
 
12
12
  type AnyCtx = Record<string, any>
@@ -252,6 +252,19 @@ describe('createRowDrag - drop indicator during virtualized scroll (#69)', () =>
252
252
  describe('createRowDrag - touch (#66)', () => {
253
253
  const wait = (ms: number) => new Promise((r) => setTimeout(r, ms))
254
254
 
255
+ // `row-drag.ts` fetches the touch module with import() on the first touch,
256
+ // and that fetch is not free: measured at ~190 ms the first time a worker
257
+ // loads it, against the 400 ms budget the tests below allow for a 350 ms
258
+ // press. That left 50 ms of headroom and made this the only flaky suite in
259
+ // the run - green alone, red once the other 200 files were competing for the
260
+ // same worker. Warming the module here means the waits below measure the
261
+ // long press, which is what they are for, instead of racing the loader.
262
+ // The press-clock behaviour that the cold fetch exposed has its own test at
263
+ // the bottom of this block.
264
+ beforeAll(async () => {
265
+ await import('./row-drag-touch')
266
+ })
267
+
255
268
  // `stubRowAt` replaces a global. Without restoring it, every later test in
256
269
  // the worker inherits a fake elementFromPoint - which showed up as an
257
270
  // intermittent failure elsewhere in the suite, not here.
@@ -293,10 +306,15 @@ describe('createRowDrag - touch (#66)', () => {
293
306
  it('does not start until the long press elapses', async () => {
294
307
  const ctx = makeCtx([{ id: 1, title: 'a' }, { id: 2, title: 'b' }], P)
295
308
  const d = createRowDrag(ctx)
309
+ const pressedAt = performance.now()
296
310
  d.onRowPointerDown(pointer('touch', 10), 0)
297
311
  expect(ctx.rowDragActive).toBe(false)
298
312
  await wait(400)
299
313
  expect(ctx.rowDragActive).toBe(true)
314
+ // The name of this test is the assertion: it has to have WAITED. Without
315
+ // this line the test passes just as happily if the press threshold is
316
+ // deleted and the drag starts on contact.
317
+ expect(performance.now() - pressedAt).toBeGreaterThanOrEqual(340)
300
318
  d.destroyRowDrag()
301
319
  })
302
320
 
@@ -350,4 +368,34 @@ describe('createRowDrag - touch (#66)', () => {
350
368
  expect((ctx.internalData as Task[]).map((r) => r.id)).toEqual([1, 2])
351
369
  expect(ctx.rowDragActive).toBe(false)
352
370
  })
371
+
372
+ // The press threshold is counted from the pointerdown, not from the moment
373
+ // the lazily-imported touch module finished loading. Driving createTouchDrag
374
+ // directly is the only way to state that: through createRowDrag the fetch
375
+ // latency is whatever the machine happens to do that run, which is exactly
376
+ // the non-determinism this behaviour exists to cancel out.
377
+ it('counts the long press from the pointerdown, not from when the module loaded', async () => {
378
+ const { createTouchDrag } = await import('./row-drag-touch')
379
+ const rows: Task[] = [{ id: 1, title: 'a' }, { id: 2, title: 'b' }]
380
+ const ctx = makeCtx(rows, P)
381
+ const handle = createTouchDrag({
382
+ ctx,
383
+ managed: () => true,
384
+ originalAt: (i: number) => (ctx.internalData as Task[])[i],
385
+ commitDropOnRow: () => {},
386
+ clearIndicators: () => {},
387
+ cancelPendingLeave: () => {},
388
+ openBus: () => {},
389
+ closeBus: () => {},
390
+ })
391
+
392
+ // 300 ms of the 350 ms press already went on fetching this module, so only
393
+ // ~50 ms of it is left. Before the fix the clock restarted here and the
394
+ // user paid 650 ms for their first drag of the session.
395
+ handle.start(pointer('touch', 10), 0, performance.now() - 300)
396
+ expect(ctx.rowDragActive).toBe(false)
397
+ await wait(150)
398
+ expect(ctx.rowDragActive).toBe(true)
399
+ handle.destroy()
400
+ })
353
401
  })
package/src/row-drag.ts CHANGED
@@ -217,8 +217,8 @@ export function createRowDrag<TFeatures, TData>(ctx: any) {
217
217
 
218
218
  // Touch drag (#66) lives in its own module and is fetched on the first touch
219
219
  // that lands on a draggable row, so desktop never downloads it. The 350 ms
220
- // long press inside it more than covers the fetch - the drag cannot start
221
- // before that elapses anyway.
220
+ // long press inside it absorbs the fetch: the press clock starts at
221
+ // pointerdown here, so the fetch is free unless it outlasts the press.
222
222
  let touchDrag: import("./row-drag-touch").TouchDragHandle | null = null;
223
223
  let touchLoading = false;
224
224
 
@@ -268,6 +268,10 @@ export function createRowDrag<TFeatures, TData>(ctx: any) {
268
268
  const pid = e.pointerId;
269
269
  const startX = e.clientX;
270
270
  const startY = e.clientY;
271
+ // Handed to the touch module so its long press is counted from HERE, not
272
+ // from whenever the import below resolves. Without it the first touch drag
273
+ // of a session costs `fetch + 350 ms` while every later one costs 350 ms.
274
+ const pressStartedAt = performance.now();
271
275
  let aborted = false;
272
276
  function stopWatching() {
273
277
  window.removeEventListener("pointermove", onEarlyMove);
@@ -306,7 +310,7 @@ export function createRowDrag<TFeatures, TData>(ctx: any) {
306
310
  stopWatching();
307
311
  if (aborted || !managed()) return;
308
312
  touchDrag = m.createTouchDrag(touchDeps());
309
- touchDrag.start(snapshot, rowIndex);
313
+ touchDrag.start(snapshot, rowIndex, pressStartedAt);
310
314
  });
311
315
  }
312
316