@coreui/data-grid 0.2.0-rc.0 → 0.4.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.
Files changed (143) hide show
  1. package/README.md +4 -2
  2. package/dist/css/data-grid.css +11 -11
  3. package/dist/css/data-grid.css.map +1 -1
  4. package/dist/css/data-grid.min.css +2 -2
  5. package/dist/css/data-grid.min.css.map +1 -1
  6. package/dist/css/data-grid.standalone.css +1066 -0
  7. package/dist/css/data-grid.standalone.css.map +1 -0
  8. package/dist/css/data-grid.standalone.min.css +5 -0
  9. package/dist/css/data-grid.standalone.min.css.map +1 -0
  10. package/dist/js/clipboard.cjs +2 -3
  11. package/dist/js/clipboard.mjs +2 -3
  12. package/dist/js/csv.cjs +5 -6
  13. package/dist/js/csv.cjs.map +1 -1
  14. package/dist/js/csv.mjs +5 -6
  15. package/dist/js/csv.mjs.map +1 -1
  16. package/dist/js/data-grid.esm.js +5685 -5422
  17. package/dist/js/data-grid.esm.js.map +1 -1
  18. package/dist/js/data-grid.esm.min.js +2 -2
  19. package/dist/js/data-grid.esm.min.js.map +1 -1
  20. package/dist/js/data-grid.js +5685 -5423
  21. package/dist/js/data-grid.js.map +1 -1
  22. package/dist/js/data-grid.lite.esm.js +2707 -2629
  23. package/dist/js/data-grid.lite.esm.js.map +1 -1
  24. package/dist/js/data-grid.lite.esm.min.js +2 -2
  25. package/dist/js/data-grid.lite.esm.min.js.map +1 -1
  26. package/dist/js/data-grid.lite.js +2707 -2630
  27. package/dist/js/data-grid.lite.js.map +1 -1
  28. package/dist/js/data-grid.lite.min.js +2 -2
  29. package/dist/js/data-grid.lite.min.js.map +1 -1
  30. package/dist/js/data-grid.lite.mjs +2707 -2629
  31. package/dist/js/data-grid.lite.mjs.map +1 -1
  32. package/dist/js/data-grid.min.js +2 -2
  33. package/dist/js/data-grid.min.js.map +1 -1
  34. package/dist/js/data-grid.mjs +5685 -5422
  35. package/dist/js/data-grid.mjs.map +1 -1
  36. package/dist/js/direction.cjs +25 -0
  37. package/dist/js/direction.cjs.map +1 -0
  38. package/dist/js/direction.mjs +23 -0
  39. package/dist/js/direction.mjs.map +1 -0
  40. package/dist/js/drag.cjs +20 -12
  41. package/dist/js/drag.cjs.map +1 -1
  42. package/dist/js/drag.mjs +20 -12
  43. package/dist/js/drag.mjs.map +1 -1
  44. package/dist/js/{export-table-CNAKStzj.mjs → export-table-BggGRmzQ.mjs} +3 -4
  45. package/dist/js/{export-table-CNAKStzj.mjs.map → export-table-BggGRmzQ.mjs.map} +1 -1
  46. package/dist/js/{export-table-doIwvE5U.cjs → export-table-BiB3_lVo.cjs} +3 -4
  47. package/dist/js/{export-table-doIwvE5U.cjs.map → export-table-BiB3_lVo.cjs.map} +1 -1
  48. package/dist/js/filters.cjs +2 -3
  49. package/dist/js/filters.mjs +2 -3
  50. package/dist/js/labels.cjs +2 -3
  51. package/dist/js/labels.mjs +2 -3
  52. package/dist/js/print.cjs +2 -3
  53. package/dist/js/print.mjs +2 -3
  54. package/dist/js/selector.cjs +22 -0
  55. package/dist/js/selector.cjs.map +1 -0
  56. package/dist/js/selector.mjs +21 -0
  57. package/dist/js/selector.mjs.map +1 -0
  58. package/dist/js/validate.cjs +2 -3
  59. package/dist/js/validate.mjs +2 -3
  60. package/dist/js/xlsx.cjs +5 -7
  61. package/dist/js/xlsx.cjs.map +1 -1
  62. package/dist/js/xlsx.mjs +5 -7
  63. package/dist/js/xlsx.mjs.map +1 -1
  64. package/dist/types/base-component.d.mts +29 -0
  65. package/dist/types/data-grid.d.mts +141 -0
  66. package/dist/types/dom/data.d.mts +15 -0
  67. package/dist/types/dom/event-handler.d.mts +22 -0
  68. package/dist/types/dom/manipulator.d.mts +16 -0
  69. package/dist/types/engine/active-cell.d.mts +33 -0
  70. package/dist/types/engine/active-cell.d.ts +1 -0
  71. package/dist/types/engine/data-source.d.mts +19 -0
  72. package/dist/types/engine/features-lite.d.mts +25 -0
  73. package/dist/types/engine/features.d.mts +35 -0
  74. package/dist/types/engine/scheduler.d.mts +11 -0
  75. package/dist/types/engine/table.d.mts +9 -0
  76. package/dist/types/engine/table.d.ts +1 -1
  77. package/dist/types/engine/validate.d.mts +20 -1
  78. package/dist/types/engine/virtualizer.d.mts +23 -0
  79. package/dist/types/filters.d.mts +13 -1
  80. package/dist/types/icons.d.mts +23 -0
  81. package/dist/types/index-lite.d.mts +19 -1
  82. package/dist/types/index.d.mts +21 -1
  83. package/dist/types/labels.d.mts +9 -1
  84. package/dist/types/types.d.mts +284 -0
  85. package/dist/types/ui/body.d.mts +37 -0
  86. package/dist/types/ui/column-chooser.d.mts +27 -0
  87. package/dist/types/ui/editor.d.mts +8 -0
  88. package/dist/types/ui/filter-menu.d.mts +21 -0
  89. package/dist/types/ui/header.d.mts +51 -0
  90. package/dist/types/ui/loading.d.mts +12 -0
  91. package/dist/types/ui/menu.d.mts +17 -0
  92. package/dist/types/ui/pagination.d.mts +17 -0
  93. package/dist/types/ui/pinning.d.mts +17 -0
  94. package/dist/types/ui/toolbar.d.mts +42 -0
  95. package/dist/types/ui/tooltip.d.mts +10 -0
  96. package/dist/types/util/clipboard.d.mts +29 -1
  97. package/dist/types/util/config.d.mts +28 -0
  98. package/dist/types/util/csv.d.mts +19 -1
  99. package/dist/types/util/debounce.d.mts +7 -0
  100. package/dist/types/util/direction.d.mts +17 -0
  101. package/dist/types/util/direction.d.ts +17 -0
  102. package/dist/types/util/drag.d.mts +48 -1
  103. package/dist/types/util/export-table.d.mts +47 -0
  104. package/dist/types/util/index.d.mts +66 -0
  105. package/dist/types/util/print.d.mts +32 -1
  106. package/dist/types/util/sanitizer.d.mts +15 -0
  107. package/dist/types/util/selector.d.mts +12 -0
  108. package/dist/types/util/selector.d.ts +12 -0
  109. package/dist/types/util/xlsx.d.mts +17 -1
  110. package/dist/types/util/zip.d.mts +11 -0
  111. package/package.json +42 -16
  112. package/scss/_base.scss +13 -0
  113. package/scss/_config.scss +14 -0
  114. package/scss/_functions.scss +1 -0
  115. package/scss/_mixins.scss +1 -0
  116. package/scss/base/_buttons.scss +113 -0
  117. package/scss/base/_forms.scss +157 -0
  118. package/scss/base/_helpers.scss +24 -0
  119. package/scss/base/_pagination.scss +100 -0
  120. package/scss/base/_reboot.scss +93 -0
  121. package/scss/base/_root.scss +85 -0
  122. package/scss/base/_spinner.scss +45 -0
  123. package/scss/base/_table.scss +78 -0
  124. package/scss/components/_data-grid.scss +807 -0
  125. package/scss/data-grid.scss +7 -743
  126. package/scss/data-grid.standalone.scss +16 -0
  127. package/scss/functions/_defaults.scss +25 -0
  128. package/scss/mixins/_tokens.scss +12 -0
  129. package/src/data-grid.ts +22 -6
  130. package/src/engine/active-cell.ts +6 -3
  131. package/src/engine/table.ts +9 -1
  132. package/src/index-lite.ts +1 -1
  133. package/src/index.ts +1 -1
  134. package/src/ui/column-chooser.ts +4 -4
  135. package/src/ui/filter-menu.ts +5 -5
  136. package/src/ui/menu.ts +4 -4
  137. package/src/ui/pagination.ts +1 -1
  138. package/src/ui/tooltip.ts +2 -2
  139. package/src/util/csv.ts +2 -2
  140. package/src/util/direction.ts +38 -0
  141. package/src/util/drag.ts +45 -9
  142. package/src/util/selector.ts +16 -0
  143. package/src/util/xlsx.ts +1 -1
@@ -0,0 +1,25 @@
1
+ /*!
2
+ * CoreUI PRO data-grid v0.4.0 (https://coreui.io)
3
+ * Copyright 2026 The CoreUI Team (https://github.com/orgs/coreui/people)
4
+ * License (https://coreui.io/pro/license/)
5
+ */
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ // src/util/direction.ts
8
+ /**
9
+ * --------------------------------------------------------------------------
10
+ * CoreUI PRO data-grid util/direction.ts
11
+ * License (https://coreui.io/pro/license/)
12
+ * --------------------------------------------------------------------------
13
+ */
14
+ const isElementRTL = (element) => typeof getComputedStyle === "function" && element !== null && element !== void 0 && getComputedStyle(element).direction === "rtl";
15
+ const syncResizeDirection = (table, element) => {
16
+ const direction = isElementRTL(element) ? "rtl" : "ltr";
17
+ if (table.options.columnResizeDirection !== direction) table.setOptions((previous) => ({
18
+ ...previous,
19
+ columnResizeDirection: direction
20
+ }));
21
+ };
22
+ exports.isElementRTL = isElementRTL;
23
+ exports.syncResizeDirection = syncResizeDirection;
24
+
25
+ //# sourceMappingURL=direction.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direction.cjs","names":[],"sources":["../../src/util/direction.ts"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * CoreUI PRO data-grid util/direction.ts\n * License (https://coreui.io/pro/license/)\n * --------------------------------------------------------------------------\n */\n\n// Read from the element rather than the document: a grid can sit inside an RTL\n// subtree of an LTR page, which is why the vendored isRTL() (it checks\n// documentElement.dir) is not the helper for any of this.\n//\n// Angular prerenders in Node, where there is no getComputedStyle — and no\n// layout to have a direction. LTR is the right answer there: the server-side\n// pass emits logical markup either way, and the client corrects on hydration.\nexport const isElementRTL = (element: Element | null | undefined): boolean =>\n typeof getComputedStyle === 'function' &&\n element !== null &&\n element !== undefined &&\n getComputedStyle(element).direction === 'rtl'\n\ninterface ResizeDirectionTable {\n options: { columnResizeDirection?: 'ltr' | 'rtl' }\n setOptions: (updater: (previous: { columnResizeDirection?: 'ltr' | 'rtl' }) => never) => void\n}\n\n// table-core copies the options object at construction, so the direction it\n// captured goes stale the moment an app swaps language on a live grid. A\n// resize is the only place the value is read, and starting one is rare enough\n// to re-read the element here.\nexport const syncResizeDirection = (\n table: ResizeDirectionTable,\n element: Element | null | undefined\n): void => {\n const direction = isElementRTL(element) ? 'rtl' : 'ltr'\n if (table.options.columnResizeDirection !== direction) {\n table.setOptions(previous => ({ ...previous, columnResizeDirection: direction }) as never)\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAa,gBAAgB,YAC3B,OAAO,qBAAqB,cAC5B,YAAY,QACZ,YAAY,KAAA,KACZ,iBAAiB,OAAO,CAAC,CAAC,cAAc;AAW1C,MAAa,uBACX,OACA,YACS;CACT,MAAM,YAAY,aAAa,OAAO,IAAI,QAAQ;CAClD,IAAI,MAAM,QAAQ,0BAA0B,WAC1C,MAAM,YAAW,cAAa;EAAE,GAAG;EAAU,uBAAuB;CAAU,EAAW;AAE7F"}
@@ -0,0 +1,23 @@
1
+ /*!
2
+ * CoreUI PRO data-grid v0.4.0 (https://coreui.io)
3
+ * Copyright 2026 The CoreUI Team (https://github.com/orgs/coreui/people)
4
+ * License (https://coreui.io/pro/license/)
5
+ */
6
+ // src/util/direction.ts
7
+ /**
8
+ * --------------------------------------------------------------------------
9
+ * CoreUI PRO data-grid util/direction.ts
10
+ * License (https://coreui.io/pro/license/)
11
+ * --------------------------------------------------------------------------
12
+ */
13
+ const isElementRTL = (element) => typeof getComputedStyle === "function" && element !== null && element !== void 0 && getComputedStyle(element).direction === "rtl";
14
+ const syncResizeDirection = (table, element) => {
15
+ const direction = isElementRTL(element) ? "rtl" : "ltr";
16
+ if (table.options.columnResizeDirection !== direction) table.setOptions((previous) => ({
17
+ ...previous,
18
+ columnResizeDirection: direction
19
+ }));
20
+ };
21
+ export { isElementRTL, syncResizeDirection };
22
+
23
+ //# sourceMappingURL=direction.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direction.mjs","names":[],"sources":["../../src/util/direction.ts"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * CoreUI PRO data-grid util/direction.ts\n * License (https://coreui.io/pro/license/)\n * --------------------------------------------------------------------------\n */\n\n// Read from the element rather than the document: a grid can sit inside an RTL\n// subtree of an LTR page, which is why the vendored isRTL() (it checks\n// documentElement.dir) is not the helper for any of this.\n//\n// Angular prerenders in Node, where there is no getComputedStyle — and no\n// layout to have a direction. LTR is the right answer there: the server-side\n// pass emits logical markup either way, and the client corrects on hydration.\nexport const isElementRTL = (element: Element | null | undefined): boolean =>\n typeof getComputedStyle === 'function' &&\n element !== null &&\n element !== undefined &&\n getComputedStyle(element).direction === 'rtl'\n\ninterface ResizeDirectionTable {\n options: { columnResizeDirection?: 'ltr' | 'rtl' }\n setOptions: (updater: (previous: { columnResizeDirection?: 'ltr' | 'rtl' }) => never) => void\n}\n\n// table-core copies the options object at construction, so the direction it\n// captured goes stale the moment an app swaps language on a live grid. A\n// resize is the only place the value is read, and starting one is rare enough\n// to re-read the element here.\nexport const syncResizeDirection = (\n table: ResizeDirectionTable,\n element: Element | null | undefined\n): void => {\n const direction = isElementRTL(element) ? 'rtl' : 'ltr'\n if (table.options.columnResizeDirection !== direction) {\n table.setOptions(previous => ({ ...previous, columnResizeDirection: direction }) as never)\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,gBAAgB,YAC3B,OAAO,qBAAqB,cAC5B,YAAY,QACZ,YAAY,KAAA,KACZ,iBAAiB,OAAO,CAAC,CAAC,cAAc;AAW1C,MAAa,uBACX,OACA,YACS;CACT,MAAM,YAAY,aAAa,OAAO,IAAI,QAAQ;CAClD,IAAI,MAAM,QAAQ,0BAA0B,WAC1C,MAAM,YAAW,cAAa;EAAE,GAAG;EAAU,uBAAuB;CAAU,EAAW;AAE7F"}
package/dist/js/drag.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  /*!
2
- * CoreUI PRO data-grid v0.2.0-rc.0 (https://coreui.io)
2
+ * CoreUI PRO data-grid v0.4.0 (https://coreui.io)
3
3
  * Copyright 2026 The CoreUI Team (https://github.com/orgs/coreui/people)
4
4
  * License (https://coreui.io/pro/license/)
5
5
  */
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- //#region src/util/drag.ts
7
+ const require_selector = require("./selector.cjs");
8
+ // src/util/drag.ts
8
9
  /**
9
10
  * --------------------------------------------------------------------------
10
11
  * CoreUI PRO data-grid util/drag.ts
@@ -39,6 +40,7 @@ const createDragController = (options) => {
39
40
  const horizontal = axis === "x";
40
41
  let sourceHit = null;
41
42
  let dragging = false;
43
+ let axisSign = 1;
42
44
  let origin = {
43
45
  x: 0,
44
46
  y: 0
@@ -58,6 +60,11 @@ const createDragController = (options) => {
58
60
  const previewClassed = /* @__PURE__ */ new Set();
59
61
  const contentStarts = /* @__PURE__ */ new Map();
60
62
  const contentSizes = /* @__PURE__ */ new Map();
63
+ const logicalStart = (rect) => {
64
+ if (!horizontal) return rect.top;
65
+ return axisSign === 1 ? rect.left : -rect.right;
66
+ };
67
+ const logicalPointer = () => horizontal ? pointer.x * axisSign : pointer.y;
61
68
  const stopAutoScroll = () => {
62
69
  if (scrollFrame) {
63
70
  cancelAnimationFrame(scrollFrame);
@@ -94,7 +101,8 @@ const createDragController = (options) => {
94
101
  const rootRect = root.getBoundingClientRect();
95
102
  indicator.hidden = false;
96
103
  if (horizontal) {
97
- indicator.style.left = `${after ? rect.right : rect.left}px`;
104
+ const trailing = after === (axisSign === 1);
105
+ indicator.style.left = `${trailing ? rect.right : rect.left}px`;
98
106
  indicator.style.top = `${rootRect.top}px`;
99
107
  indicator.style.height = `${rootRect.height}px`;
100
108
  } else {
@@ -105,6 +113,7 @@ const createDragController = (options) => {
105
113
  };
106
114
  const startDrag = () => {
107
115
  dragging = true;
116
+ axisSign = horizontal && getComputedStyle(root).direction === "rtl" ? -1 : 1;
108
117
  const active = document.activeElement;
109
118
  if (active instanceof HTMLElement && root.contains(active)) active.blur();
110
119
  root.classList.add(CLASS_NAME_DRAG_ACTIVE);
@@ -131,13 +140,13 @@ const createDragController = (options) => {
131
140
  const scrollElement = options.getScrollElement();
132
141
  if (!scrollElement) return 0;
133
142
  const rect = scrollElement.getBoundingClientRect();
134
- return horizontal ? rect.left - scrollElement.scrollLeft : rect.top - scrollElement.scrollTop;
143
+ return horizontal ? logicalStart(rect) - axisSign * scrollElement.scrollLeft : rect.top - scrollElement.scrollTop;
135
144
  };
136
145
  const captureItem = (id, origin) => {
137
146
  const element = options.items.getElements(id)[0];
138
147
  if (!element) return false;
139
148
  const rect = element.getBoundingClientRect();
140
- const start = (horizontal ? rect.left : rect.top) - (previewDeltas.get(id) ?? 0);
149
+ const start = logicalStart(rect) - (previewDeltas.get(id) ?? 0);
141
150
  contentStarts.set(id, start - origin);
142
151
  contentSizes.set(id, horizontal ? rect.width : rect.height);
143
152
  return true;
@@ -149,7 +158,7 @@ const createDragController = (options) => {
149
158
  for (const id of options.items.getOrderedIds()) captureItem(id, origin);
150
159
  };
151
160
  const updateTarget = () => {
152
- const position = horizontal ? pointer.x : pointer.y;
161
+ const position = logicalPointer();
153
162
  const origin = contentOrigin();
154
163
  const zones = [];
155
164
  for (const id of options.items.getOrderedIds()) {
@@ -252,7 +261,7 @@ const createDragController = (options) => {
252
261
  for (const [id, delta] of next) {
253
262
  if (previewDeltas.get(id) === delta) continue;
254
263
  for (const element of options.items.getElements(id)) {
255
- element.style.transform = horizontal ? `translateX(${delta}px)` : `translateY(${delta}px)`;
264
+ element.style.transform = horizontal ? `translateX(${axisSign * delta}px)` : `translateY(${delta}px)`;
256
265
  element.classList.add(CLASS_NAME_PREVIEW);
257
266
  }
258
267
  previewClassed.add(id);
@@ -286,7 +295,7 @@ const createDragController = (options) => {
286
295
  };
287
296
  const delta = previewDeltas.get(sourceHit.id) ?? 0;
288
297
  return horizontal ? {
289
- x: rect.left - delta,
298
+ x: rect.left - axisSign * delta,
290
299
  y: rect.top
291
300
  } : {
292
301
  x: rect.left,
@@ -394,7 +403,7 @@ const SELECTOR_PREVIEW_ROWS = "thead tr, tbody tr:not(.data-grid-spacer):not(.da
394
403
  const columnItems = (root) => ({
395
404
  getOrderedIds: () => [...root.querySelectorAll("thead tr:first-child th[data-coreui-column]")].map((cell) => cell.dataset.coreuiColumn ?? ""),
396
405
  getElements(id) {
397
- const header = root.querySelector(`thead th[data-coreui-column="${id}"]`);
406
+ const header = root.querySelector(`thead th[data-coreui-column="${require_selector.escapeAttributeValue(id)}"]`);
398
407
  if (!header) return [];
399
408
  const { cellIndex } = header;
400
409
  const elements = [];
@@ -419,7 +428,7 @@ const createColumnDragController = (options) => createDragController({
419
428
  } : null;
420
429
  },
421
430
  items: columnItems(options.root),
422
- canDrop: (sourceId, targetId) => sourceId !== targetId && Boolean(options.root.querySelector(`th[data-coreui-column="${targetId}"][data-coreui-draggable]`)) && options.canDrop(sourceId, targetId),
431
+ canDrop: (sourceId, targetId) => sourceId !== targetId && Boolean(options.root.querySelector(`th[data-coreui-column="${require_selector.escapeAttributeValue(targetId)}"][data-coreui-draggable]`)) && options.canDrop(sourceId, targetId),
423
432
  preview: true,
424
433
  onDrop: (sourceKey, drop) => options.onReorder(sourceKey, drop.id, drop.after)
425
434
  });
@@ -428,7 +437,7 @@ const SELECTOR_ROW = "tr[data-coreui-index]";
428
437
  const rowItems = (root) => ({
429
438
  getOrderedIds: () => [...root.querySelectorAll(`tbody ${SELECTOR_ROW}`)].map((row) => row.dataset.coreuiIndex ?? ""),
430
439
  getElements(id) {
431
- const row = root.querySelector(`tbody tr[data-coreui-index="${id}"]`);
440
+ const row = root.querySelector(`tbody tr[data-coreui-index="${require_selector.escapeAttributeValue(id)}"]`);
432
441
  return row ? [row] : [];
433
442
  }
434
443
  });
@@ -451,7 +460,6 @@ const createRowDragController = (options) => createDragController({
451
460
  preview: true,
452
461
  onDrop: (sourceId, drop) => options.onReorder(Number(sourceId), Number(drop.id), drop.after)
453
462
  });
454
- //#endregion
455
463
  exports.createColumnDragController = createColumnDragController;
456
464
  exports.createDragController = createDragController;
457
465
  exports.createRowDragController = createRowDragController;
@@ -1 +1 @@
1
- {"version":3,"file":"drag.cjs","names":[],"sources":["../../src/util/drag.ts"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * CoreUI PRO data-grid util/drag.ts\n * License (https://coreui.io/pro/license/)\n * --------------------------------------------------------------------------\n */\n\n// The one drag implementation behind every reorder in the grid: column drag\n// (axis 'x', over the header) and row drag (axis 'y', over the body). It is\n// headless in the sense that matters — it never reads the table, only what the\n// consumer resolves for it — and pointer-based rather than HTML5 drag events,\n// which cannot render a drop indicator, cannot auto-scroll, and behave\n// differently on touch.\n//\n// The invariant that keeps it cheap: pointermove writes to the DOM directly.\n// Nothing here goes through component state, change detection or a signal, so\n// a drag costs no re-renders in any of the four editions.\n\nconst CLASS_NAME_GHOST = 'data-grid-drag-ghost'\nconst CLASS_NAME_INDICATOR = 'data-grid-drop-indicator'\nconst CLASS_NAME_DRAGGING = 'data-grid-dragging'\nconst CLASS_NAME_DRAG_ACTIVE = 'data-grid-drag-active'\nconst CLASS_NAME_PREVIEW = 'data-grid-drag-preview'\nconst CLASS_NAME_GHOST_SETTLE = 'data-grid-drag-ghost-settle'\n\n// A press has to travel this far before it becomes a drag — otherwise a click\n// on a sortable header would never sort.\nconst DRAG_THRESHOLD = 4\n// How far past a zone boundary the pointer must travel before the slot flips.\n// Swaps fire on ENTRY into a neighbour's zone, and without this band a hand\n// tremor exactly on the boundary would re-animate back and forth.\nconst DRAG_HYSTERESIS = 3\n// How close to the scroll element's edge the pointer starts auto-scrolling,\n// and the fastest it scrolls when pinned to that edge.\nconst EDGE_SIZE = 48\nconst EDGE_MAX_SPEED = 18\n// The ghost trails the pointer by this much, in px.\nconst GHOST_OFFSET = 12\n// Removal fallback for the settling ghost — a context where the transition\n// never runs (reduced motion, a hidden tab) must not leak the element.\nconst GHOST_SETTLE_FALLBACK_MS = 400\n\nexport type DragAxis = 'x' | 'y'\n\nexport interface DragHit {\n id: string\n element: HTMLElement\n}\n\nexport interface DragDrop {\n id: string\n // The source lands after the target rather than before it — which side of\n // the target's midpoint the pointer was released on.\n after: boolean\n}\n\nexport interface DragControllerOptions {\n axis: DragAxis\n root: HTMLElement\n // Delegation selector for the element a drag may start from.\n handle: string\n // Called with the handle and the element actually pressed. Returning null\n // vetoes the drag: a press on a resize handle, a column the author marked\n // immovable, a grid whose reordering is off.\n resolveSource: (handle: HTMLElement, target: HTMLElement) => DragHit | null\n items: DragItems\n // Vetoes a slot: the source itself, or an item the reorder may not cross\n // (a column in another pinned region).\n canDrop: (sourceId: string, targetId: string) => boolean\n getScrollElement: () => HTMLElement | null\n // The ghost's caption. Defaults to the source's collapsed text, which is the\n // label for a header cell but every cell run together for a row.\n ghostLabel?: (source: DragHit) => string\n // The live preview: the items between the source and the drop slot slide\n // aside and the source slides into the gap, so the grid shows the layout the\n // drop will produce instead of only pointing at it. Transforms only, so\n // nothing reflows and the real order still changes once, on drop. Replaces\n // the drop-indicator line, which only renders when this is off.\n preview?: boolean\n onDrop: (sourceId: string, drop: DragDrop) => void\n}\n\n// The reorderable items, in display order along the drag's axis. This is both\n// what the pointer is hit-tested against and what the live preview slides.\nexport interface DragItems {\n getOrderedIds: () => string[]\n // Every element that travels with that id: a column's header and body cells,\n // or simply the row.\n getElements: (id: string) => HTMLElement[]\n}\n\nexport interface DragController {\n dispose: () => void\n}\n\nconst collapse = (text: string): string => text.replace(/\\s+/g, ' ').trim()\n\n// Which slot the source lands in, expressed in the CURRENT order — the same\n// arithmetic the drop itself does, so the preview cannot disagree with it.\nconst destinationIndex = (from: number, target: number, after: boolean): number =>\n (target > from ? (after ? target : target - 1) : (after ? target + 1 : target))\n\nconst swallowClick = (event: Event) => {\n event.preventDefault()\n event.stopPropagation()\n}\n\nconst disarmSwallowClick = () => {\n document.removeEventListener('click', swallowClick, { capture: true })\n}\n\n// Server-side rendering reaches here through Angular, whose ngAfterViewInit and\n// ngOnDestroy both run during prerender. There is no pointer to track without a\n// document, so hand back an inert controller instead of touching one.\nconst INERT_CONTROLLER: DragController = { dispose() {} }\n\nexport const createDragController = (options: DragControllerOptions): DragController => {\n if (typeof document === 'undefined') {\n return INERT_CONTROLLER\n }\n\n const { axis, root } = options\n const horizontal = axis === 'x'\n\n let sourceHit: DragHit | null = null\n let dragging = false\n let origin = { x: 0, y: 0 }\n let pointer = { x: 0, y: 0 }\n let drop: DragDrop | null = null\n let ghost: HTMLElement | null = null\n let indicator: HTMLElement | null = null\n let scrollFrame = 0\n let pointerId = -1\n let previewKey = ''\n // The zone that produced the current drop, padded by the hysteresis band.\n let sticky: { lo: number; hi: number } | null = null\n // The transform currently applied to each item — the preview diffs against\n // it, and a layout captured mid-preview (a row revealed by auto-scroll) is\n // corrected back through it.\n const previewDeltas = new Map<string, number>()\n // Every id that still carries the transition class. Mid-drag un-shifts keep\n // the class so the slide BACK animates too; it only comes off at the end.\n const previewClassed = new Set<string>()\n // Each item's start in the scroll container's CONTENT coordinates — a space\n // no transform and no scroll can move. Captured at drag start, when nothing\n // is transformed yet, so the values are exact.\n const contentStarts = new Map<string, number>()\n const contentSizes = new Map<string, number>()\n\n const stopAutoScroll = () => {\n if (scrollFrame) {\n cancelAnimationFrame(scrollFrame)\n scrollFrame = 0\n }\n }\n\n const autoScroll = () => {\n scrollFrame = 0\n const scrollElement = options.getScrollElement()\n if (!scrollElement || !dragging) {\n return\n }\n\n const rect = scrollElement.getBoundingClientRect()\n const position = horizontal ? pointer.x : pointer.y\n const start = horizontal ? rect.left : rect.top\n const end = horizontal ? rect.right : rect.bottom\n\n // Speed ramps with how far into the edge zone the pointer sits, so the\n // list creeps at the boundary and races when pinned to the edge.\n let delta = 0\n if (position < start + EDGE_SIZE) {\n delta = -EDGE_MAX_SPEED * Math.min(1, (start + EDGE_SIZE - position) / EDGE_SIZE)\n } else if (position > end - EDGE_SIZE) {\n delta = EDGE_MAX_SPEED * Math.min(1, (position - (end - EDGE_SIZE)) / EDGE_SIZE)\n }\n\n if (delta !== 0) {\n if (horizontal) {\n scrollElement.scrollLeft += delta\n } else {\n scrollElement.scrollTop += delta\n }\n\n // Scrolling moved the content under a stationary pointer, and no\n // pointermove will fire to say so.\n updateTarget()\n scrollFrame = requestAnimationFrame(autoScroll)\n }\n }\n\n // The ghost trails the pointer instead of sitting under it, so the cell the\n // hit test resolves stays visible.\n const moveGhost = () => {\n if (ghost) {\n ghost.style.transform = `translate(${pointer.x + GHOST_OFFSET}px, ${pointer.y + GHOST_OFFSET}px)`\n }\n }\n\n const hideIndicator = () => {\n if (indicator) {\n indicator.hidden = true\n }\n }\n\n const showIndicator = (target: HTMLElement, after: boolean) => {\n if (!indicator) {\n return\n }\n\n const rect = target.getBoundingClientRect()\n const rootRect = root.getBoundingClientRect()\n indicator.hidden = false\n if (horizontal) {\n indicator.style.left = `${after ? rect.right : rect.left}px`\n indicator.style.top = `${rootRect.top}px`\n indicator.style.height = `${rootRect.height}px`\n } else {\n indicator.style.left = `${rootRect.left}px`\n indicator.style.top = `${after ? rect.bottom : rect.top}px`\n indicator.style.width = `${rootRect.width}px`\n }\n }\n\n const startDrag = () => {\n dragging = true\n // Chrome focuses a button on mousedown (a press on a header lands on the\n // sort button), and tearing a FOCUSED element out of the DOM — which the\n // drop's header re-append does — makes the browser reset the scroller it\n // lived in: releasing a drag with the viewport scrolled right snapped it\n // back to the left. A drag has no use for focus; drop it the moment the\n // press becomes one. Sub-threshold presses never get here, so click-to-\n // sort keeps its focus behavior.\n const active = document.activeElement\n if (active instanceof HTMLElement && root.contains(active)) {\n active.blur()\n }\n\n root.classList.add(CLASS_NAME_DRAG_ACTIVE)\n sourceHit!.element.classList.add(CLASS_NAME_DRAGGING)\n\n const rect = sourceHit!.element.getBoundingClientRect()\n ghost = document.createElement('div')\n ghost.className = CLASS_NAME_GHOST\n ghost.setAttribute('aria-hidden', 'true')\n ghost.style.width = `${rect.width}px`\n ghost.style.height = `${rect.height}px`\n ghost.textContent = options.ghostLabel ?\n options.ghostLabel(sourceHit!) :\n collapse(sourceHit!.element.textContent ?? '')\n\n // With the live preview on, the sliding items ARE the drop indicator; a\n // line has no stable place to stand, because it would be measured against\n // cells that are themselves mid-transition. The line only exists for\n // consumers that turned the preview off.\n if (!options.preview) {\n indicator = document.createElement('div')\n indicator.className = `${CLASS_NAME_INDICATOR} ${CLASS_NAME_INDICATOR}-${axis}`\n indicator.setAttribute('aria-hidden', 'true')\n indicator.hidden = true\n root.append(indicator)\n }\n\n root.append(ghost)\n moveGhost()\n captureLayout()\n }\n\n // Where the container's content coordinate 0 currently sits on screen. The\n // one live measurement the hit test needs — everything else is arithmetic on\n // the snapshot, so mid-transition rects can never feed back into the slot.\n //\n // This is the whole reason the hit test is neither document.elementFromPoint\n // nor rect-minus-delta reconstruction: the preview MOVES the cells under the\n // pointer, and a rect measured mid-transition contains only part of its\n // delta, so both models handed back zones shifted by the unfinished\n // remainder of the animation. Dragging with the direction of travel, that\n // shifted zone swallowed the next item's first half and the slot flipped\n // back and forth until the transition settled.\n const contentOrigin = (): number => {\n const scrollElement = options.getScrollElement()\n if (!scrollElement) {\n return 0\n }\n\n const rect = scrollElement.getBoundingClientRect()\n return horizontal ? rect.left - scrollElement.scrollLeft : rect.top - scrollElement.scrollTop\n }\n\n const captureItem = (id: string, origin: number): boolean => {\n const element = options.items.getElements(id)[0]\n if (!element) {\n return false\n }\n\n const rect = element.getBoundingClientRect()\n // Corrected by the applied delta for items first seen mid-preview (a row\n // revealed by auto-scroll); at drag start every delta is zero.\n const start = (horizontal ? rect.left : rect.top) - (previewDeltas.get(id) ?? 0)\n contentStarts.set(id, start - origin)\n contentSizes.set(id, horizontal ? rect.width : rect.height)\n return true\n }\n\n const captureLayout = () => {\n contentStarts.clear()\n contentSizes.clear()\n const origin = contentOrigin()\n for (const id of options.items.getOrderedIds()) {\n captureItem(id, origin)\n }\n }\n\n const updateTarget = () => {\n const position = horizontal ? pointer.x : pointer.y\n const origin = contentOrigin()\n\n const zones: Array<{ id: string; start: number; size: number }> = []\n for (const id of options.items.getOrderedIds()) {\n if (!contentStarts.has(id) && !captureItem(id, origin)) {\n continue\n }\n\n zones.push({ id, start: origin + contentStarts.get(id)!, size: contentSizes.get(id)! })\n }\n\n let hit: { id: string; after: boolean } | null = null\n const lastZone = zones[zones.length - 1]\n const fromIndex = zones.findIndex(zone => zone.id === sourceHit!.id)\n\n if (lastZone && position > lastZone.start + lastZone.size) {\n // Past the end there is no zone to be inside, but the intent could not\n // be clearer: after the last item the source may land on. Same at the\n // start edge below.\n sticky = null\n for (let index = zones.length - 1; index >= 0; index--) {\n if (options.canDrop(sourceHit!.id, zones[index].id)) {\n hit = { id: zones[index].id, after: true }\n break\n }\n }\n } else if (lastZone && position < zones[0].start) {\n sticky = null\n for (const zone of zones) {\n if (options.canDrop(sourceHit!.id, zone.id)) {\n hit = { id: zone.id, after: false }\n break\n }\n }\n } else if (drop && sticky && position >= sticky.lo - DRAG_HYSTERESIS && position <= sticky.hi + DRAG_HYSTERESIS) {\n // Still inside (or barely past) the zone that produced the current\n // drop — keep it, so boundary jitter cannot re-animate.\n hit = drop\n } else {\n for (let index = 0; index < zones.length; index++) {\n const zone = zones[index]\n const previous = zones[index - 1]\n const following = zones[index + 1]\n // Zones TILE the axis: real tables have collapsed borders between the\n // cells, and those few pixels belong to no rect. A pointer crossing\n // such a gap used to hit nothing, which cleared the preview and\n // re-applied it on the far side — replaying the slide right at the\n // boundary. Each gap is split down its middle instead.\n const lo = previous ? (previous.start + previous.size + zone.start) / 2 : zone.start\n const hi = following ? (zone.start + zone.size + following.start) / 2 : zone.start + zone.size\n if (position < lo || position > hi) {\n continue\n }\n\n if (options.canDrop(sourceHit!.id, zone.id)) {\n // Entering a neighbour's zone displaces it AT ONCE — the swap fires\n // at the boundary, not at the neighbour's midpoint, which is the\n // feel MUI and AG have and what a drag reads as: \"I am past it, so\n // it moves\". The release then just commits whatever the preview\n // shows.\n hit = {\n id: zone.id,\n after: fromIndex === -1 ? position > zone.start + (zone.size / 2) : index > fromIndex\n }\n sticky = { lo, hi }\n }\n\n break\n }\n }\n\n if (!hit) {\n drop = null\n sticky = null\n hideIndicator()\n applyPreview()\n return\n }\n\n drop = hit\n showIndicator(options.items.getElements(hit.id)[0], hit.after)\n applyPreview()\n }\n\n const clearPreview = () => {\n for (const id of previewClassed) {\n for (const element of options.items.getElements(id)) {\n element.style.transform = ''\n element.classList.remove(CLASS_NAME_PREVIEW)\n }\n }\n\n previewKey = ''\n previewDeltas.clear()\n previewClassed.clear()\n }\n\n const clearLayout = () => {\n contentStarts.clear()\n contentSizes.clear()\n }\n\n const applyPreview = () => {\n if (!options.preview || !sourceHit) {\n return\n }\n\n const ids = options.items.getOrderedIds()\n const from = ids.indexOf(sourceHit.id)\n const target = drop ? ids.indexOf(drop.id) : -1\n const to = from === -1 || target === -1 ? from : destinationIndex(from, target, drop!.after)\n\n // Keyed on the DESTINATION SLOT, not on the hit: two hits that resolve to\n // the same slot (the clamp past the end vs the last zone, jitter inside\n // the hysteresis band) must not rewrite anything. A clear-and-reapply of\n // identical transforms next to a forced layout read restarts the running\n // transition from zero — the mid-drag replay this file has already paid\n // for once.\n const key = String(to)\n if (key === previewKey) {\n return\n }\n\n previewKey = key\n\n const next = new Map<string, number>()\n if (from !== -1 && to !== from) {\n // Sizes come from the drag-start snapshot: no DOM reads may happen in\n // here, or they would flush layout between the transform writes below.\n const sizeOf = (index: number): number => contentSizes.get(ids[index]) ?? 0\n const forward = to > from\n const first = forward ? from + 1 : to\n const last = forward ? to : from - 1\n const sourceSize = sizeOf(from)\n let travelled = 0\n\n for (let index = first; index <= last; index++) {\n travelled += sizeOf(index)\n next.set(ids[index], forward ? -sourceSize : sourceSize)\n }\n\n // The source moves by the combined size of everything it passed, so no\n // gap opens anywhere and the axis keeps its total extent.\n next.set(ids[from], forward ? travelled : -travelled)\n }\n\n // Only elements whose delta actually changed are touched; everything else\n // keeps its running transition undisturbed. An un-shift keeps the\n // transition class, so sliding back home animates exactly like sliding\n // aside did — the class only comes off when the drag ends.\n for (const id of previewDeltas.keys()) {\n if (!next.has(id)) {\n for (const element of options.items.getElements(id)) {\n element.style.transform = ''\n }\n }\n }\n\n for (const [id, delta] of next) {\n if (previewDeltas.get(id) === delta) {\n continue\n }\n\n for (const element of options.items.getElements(id)) {\n element.style.transform = horizontal ? `translateX(${delta}px)` : `translateY(${delta}px)`\n element.classList.add(CLASS_NAME_PREVIEW)\n }\n\n previewClassed.add(id)\n }\n\n previewDeltas.clear()\n for (const [id, delta] of next) {\n previewDeltas.set(id, delta)\n }\n }\n\n // The gesture's closing beat: instead of vanishing at the pointer, the\n // ghost flies to where the item landed (or back home on cancel) and fades\n // out there. Purely cosmetic — the commit below never waits for it.\n const settleGhost = (x: number, y: number) => {\n if (!ghost) {\n return\n }\n\n const element = ghost\n // Taken out of cleanup's hands; it removes itself when the flight ends.\n ghost = null\n element.classList.add(CLASS_NAME_GHOST_SETTLE)\n element.style.transform = `translate(${x}px, ${y}px)`\n element.style.opacity = '0'\n\n let settled = false\n const finish = () => {\n if (!settled) {\n settled = true\n element.remove()\n }\n }\n\n element.addEventListener('transitionend', finish)\n setTimeout(finish, GHOST_SETTLE_FALLBACK_MS)\n }\n\n // Where the ghost should land: the source's current visual position for a\n // commit (the preview already put it in its slot), its untransformed home\n // for a cancel (the preview is about to slide everything back there).\n const settleTarget = (committed: boolean): { x: number; y: number } | null => {\n if (!sourceHit) {\n return null\n }\n\n const rect = sourceHit.element.getBoundingClientRect()\n if (committed) {\n return { x: rect.left, y: rect.top }\n }\n\n const delta = previewDeltas.get(sourceHit.id) ?? 0\n return horizontal ? { x: rect.left - delta, y: rect.top } : { x: rect.left, y: rect.top - delta }\n }\n\n const cleanup = () => {\n clearPreview()\n clearLayout()\n stopAutoScroll()\n root.classList.remove(CLASS_NAME_DRAG_ACTIVE)\n sourceHit?.element.classList.remove(CLASS_NAME_DRAGGING)\n ghost?.remove()\n indicator?.remove()\n ghost = null\n indicator = null\n sourceHit = null\n dragging = false\n drop = null\n sticky = null\n pointerId = -1\n document.removeEventListener('pointermove', onPointerMove)\n document.removeEventListener('pointerup', onPointerUp)\n document.removeEventListener('pointercancel', cancel)\n document.removeEventListener('keydown', onKeydown)\n }\n\n function cancel() {\n if (dragging) {\n const home = settleTarget(false)\n if (home) {\n settleGhost(home.x, home.y)\n }\n }\n\n cleanup()\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n cancel()\n }\n }\n\n function onPointerMove(event: PointerEvent) {\n if (event.pointerId !== pointerId || !sourceHit) {\n return\n }\n\n pointer = { x: event.clientX, y: event.clientY }\n\n if (!dragging) {\n const travelled = Math.max(Math.abs(pointer.x - origin.x), Math.abs(pointer.y - origin.y))\n if (travelled < DRAG_THRESHOLD) {\n return\n }\n\n startDrag()\n }\n\n // Suppresses the text selection a pointer drag would otherwise paint\n // across the header or the rows.\n event.preventDefault()\n moveGhost()\n updateTarget()\n\n if (!scrollFrame) {\n scrollFrame = requestAnimationFrame(autoScroll)\n }\n }\n\n function onPointerUp(event: PointerEvent) {\n if (event.pointerId !== pointerId) {\n return\n }\n\n const sourceId = sourceHit?.id\n const source = sourceHit?.element\n const pending = drop\n const wasDragging = dragging\n if (wasDragging) {\n const committed = Boolean(pending && pending.id !== sourceId)\n const landing = settleTarget(committed)\n if (landing) {\n settleGhost(landing.x, landing.y)\n }\n }\n\n cleanup()\n\n if (!wasDragging) {\n return\n }\n\n // A drag that ends on the element it started from is still followed by a\n // click there — which on a header cell would toggle the sort. Swallow\n // exactly that one. Releasing anywhere else fires the click on the common\n // ancestor instead (never the sort button), so arming there would only\n // risk eating an unrelated click; the 0 ms disarm covers the rest.\n if (source?.contains(event.target as Node)) {\n document.addEventListener('click', swallowClick, { capture: true, once: true })\n setTimeout(disarmSwallowClick, 0)\n }\n\n if (sourceId && pending && pending.id !== sourceId) {\n options.onDrop(sourceId, pending)\n }\n }\n\n const onPointerDown = (event: PointerEvent) => {\n // Primary button / single touch only — a right-click or a second finger\n // must not start a reorder.\n if (!event.isPrimary || (event.pointerType === 'mouse' && event.button !== 0) || sourceHit) {\n return\n }\n\n const element = (event.target as HTMLElement | null)?.closest<HTMLElement>(options.handle)\n if (!element) {\n return\n }\n\n const hit = options.resolveSource(element, event.target as HTMLElement)\n if (!hit) {\n return\n }\n\n sourceHit = hit\n pointerId = event.pointerId\n origin = { x: event.clientX, y: event.clientY }\n pointer = origin\n\n document.addEventListener('pointermove', onPointerMove)\n document.addEventListener('pointerup', onPointerUp)\n document.addEventListener('pointercancel', cancel)\n document.addEventListener('keydown', onKeydown)\n }\n\n root.addEventListener('pointerdown', onPointerDown)\n\n return {\n dispose() {\n root.removeEventListener('pointerdown', onPointerDown)\n disarmSwallowClick()\n cleanup()\n }\n }\n}\n\nconst SELECTOR_DRAGGABLE_COLUMN = 'th[data-coreui-draggable]'\nconst SELECTOR_RESIZER = '[data-coreui-resizer]'\n// Spacer and empty rows have a single colSpan cell, so a positional lookup\n// would land on the wrong thing.\nconst SELECTOR_PREVIEW_ROWS = 'thead tr, tbody tr:not(.data-grid-spacer):not(.data-grid-empty)'\n\n// A column's cells are found by POSITION, not by a data attribute: the body\n// only marks `data-coreui-column` when cellNavigation is on, while the header\n// always does — so the header cell's index is the reliable way in, and it\n// already accounts for the leading handle and select columns.\nconst columnItems = (root: HTMLElement): DragItems => ({\n getOrderedIds: () =>\n [...root.querySelectorAll<HTMLTableCellElement>('thead tr:first-child th[data-coreui-column]')]\n .map(cell => cell.dataset.coreuiColumn ?? ''),\n getElements(id) {\n const header = root.querySelector<HTMLTableCellElement>(`thead th[data-coreui-column=\"${id}\"]`)\n if (!header) {\n return []\n }\n\n const { cellIndex } = header\n const elements: HTMLElement[] = []\n for (const row of root.querySelectorAll<HTMLTableRowElement>(SELECTOR_PREVIEW_ROWS)) {\n const cell = row.children[cellIndex] as HTMLElement | undefined\n if (cell) {\n elements.push(cell)\n }\n }\n\n return elements\n }\n})\n\nexport interface ColumnDragOptions {\n root: HTMLElement\n getScrollElement: () => HTMLElement | null\n // Vetoes a target: the source itself, or a column in another pinned region —\n // reordering never crosses a pinning boundary.\n canDrop: (sourceKey: string, targetKey: string) => boolean\n onReorder: (sourceKey: string, targetKey: string, after: boolean) => void\n}\n\n// The column half of the wiring, shared so each edition's adapter is the four\n// lines that resolve its own table rather than a copy of the drag setup.\nexport const createColumnDragController = (options: ColumnDragOptions): DragController =>\n createDragController({\n axis: 'x',\n root: options.root,\n handle: SELECTOR_DRAGGABLE_COLUMN,\n getScrollElement: options.getScrollElement,\n resolveSource(element, target) {\n // A press on the resize handle is a resize, not a reorder.\n if (target.closest(SELECTOR_RESIZER)) {\n return null\n }\n\n const id = element.dataset.coreuiColumn\n return id ? { id, element } : null\n },\n items: columnItems(options.root),\n // Only a column the header marks as draggable is a slot, on top of the\n // consumer's own veto — an immovable column is not somewhere to land.\n canDrop: (sourceId, targetId) =>\n sourceId !== targetId &&\n Boolean(options.root.querySelector(`th[data-coreui-column=\"${targetId}\"][data-coreui-draggable]`)) &&\n options.canDrop(sourceId, targetId),\n preview: true,\n onDrop: (sourceKey, drop) => options.onReorder(sourceKey, drop.id, drop.after)\n })\n\nconst SELECTOR_ROW_HANDLE = '[data-coreui-row-handle]'\nconst SELECTOR_ROW = 'tr[data-coreui-index]'\n\nconst rowItems = (root: HTMLElement): DragItems => ({\n getOrderedIds: () =>\n [...root.querySelectorAll<HTMLElement>(`tbody ${SELECTOR_ROW}`)].map(row => row.dataset.coreuiIndex ?? ''),\n getElements(id) {\n const row = root.querySelector<HTMLElement>(`tbody tr[data-coreui-index=\"${id}\"]`)\n return row ? [row] : []\n }\n})\n\nexport interface RowDragOptions {\n root: HTMLElement\n getScrollElement: () => HTMLElement | null\n onReorder: (sourceIndex: number, targetIndex: number, after: boolean) => void\n}\n\n// The row half. Unlike columns, the whole row travels but only the grip cell\n// starts the drag — a row is covered in interactive content (checkboxes,\n// editors, custom cells) that a whole-row handle would swallow.\nexport const createRowDragController = (options: RowDragOptions): DragController =>\n createDragController({\n axis: 'y',\n root: options.root,\n handle: SELECTOR_ROW_HANDLE,\n getScrollElement: options.getScrollElement,\n resolveSource(handle) {\n const row = handle.closest<HTMLElement>(SELECTOR_ROW)\n const id = row?.dataset.coreuiIndex\n return row && id ? { id, element: row } : null\n },\n items: rowItems(options.root),\n canDrop: (sourceId, targetId) => sourceId !== targetId,\n // The row's own text is every cell run together; the first data cell reads\n // as the row's name in practice.\n // The row's own text is every cell run together, so the ghost carries the\n // first DATA cell instead — found by skipping the leading handle and\n // select cells rather than by data-coreui-column, which the body only\n // carries when cellNavigation is on (without it the ghost came up empty).\n ghostLabel: source =>\n collapse(\n source.element.querySelector(\n 'td:not(.data-grid-row-handle-cell):not(.data-grid-select-cell)'\n )?.textContent ?? ''\n ),\n preview: true,\n onDrop: (sourceId, drop) => options.onReorder(Number(sourceId), Number(drop.id), drop.after)\n })\n"],"mappings":"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"}
1
+ {"version":3,"file":"drag.cjs","names":["escapeAttributeValue"],"sources":["../../src/util/drag.ts"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * CoreUI PRO data-grid util/drag.ts\n * License (https://coreui.io/pro/license/)\n * --------------------------------------------------------------------------\n */\n\nimport { escapeAttributeValue } from './selector'\n\n// The one drag implementation behind every reorder in the grid: column drag\n// (axis 'x', over the header) and row drag (axis 'y', over the body). It is\n// headless in the sense that matters — it never reads the table, only what the\n// consumer resolves for it — and pointer-based rather than HTML5 drag events,\n// which cannot render a drop indicator, cannot auto-scroll, and behave\n// differently on touch.\n//\n// The invariant that keeps it cheap: pointermove writes to the DOM directly.\n// Nothing here goes through component state, change detection or a signal, so\n// a drag costs no re-renders in any of the four editions.\n\nconst CLASS_NAME_GHOST = 'data-grid-drag-ghost'\nconst CLASS_NAME_INDICATOR = 'data-grid-drop-indicator'\nconst CLASS_NAME_DRAGGING = 'data-grid-dragging'\nconst CLASS_NAME_DRAG_ACTIVE = 'data-grid-drag-active'\nconst CLASS_NAME_PREVIEW = 'data-grid-drag-preview'\nconst CLASS_NAME_GHOST_SETTLE = 'data-grid-drag-ghost-settle'\n\n// A press has to travel this far before it becomes a drag — otherwise a click\n// on a sortable header would never sort.\nconst DRAG_THRESHOLD = 4\n// How far past a zone boundary the pointer must travel before the slot flips.\n// Swaps fire on ENTRY into a neighbour's zone, and without this band a hand\n// tremor exactly on the boundary would re-animate back and forth.\nconst DRAG_HYSTERESIS = 3\n// How close to the scroll element's edge the pointer starts auto-scrolling,\n// and the fastest it scrolls when pinned to that edge.\nconst EDGE_SIZE = 48\nconst EDGE_MAX_SPEED = 18\n// The ghost trails the pointer by this much, in px.\nconst GHOST_OFFSET = 12\n// Removal fallback for the settling ghost — a context where the transition\n// never runs (reduced motion, a hidden tab) must not leak the element.\nconst GHOST_SETTLE_FALLBACK_MS = 400\n\nexport type DragAxis = 'x' | 'y'\n\nexport interface DragHit {\n id: string\n element: HTMLElement\n}\n\nexport interface DragDrop {\n id: string\n // The source lands after the target rather than before it — which side of\n // the target's midpoint the pointer was released on.\n after: boolean\n}\n\nexport interface DragControllerOptions {\n axis: DragAxis\n root: HTMLElement\n // Delegation selector for the element a drag may start from.\n handle: string\n // Called with the handle and the element actually pressed. Returning null\n // vetoes the drag: a press on a resize handle, a column the author marked\n // immovable, a grid whose reordering is off.\n resolveSource: (handle: HTMLElement, target: HTMLElement) => DragHit | null\n items: DragItems\n // Vetoes a slot: the source itself, or an item the reorder may not cross\n // (a column in another pinned region).\n canDrop: (sourceId: string, targetId: string) => boolean\n getScrollElement: () => HTMLElement | null\n // The ghost's caption. Defaults to the source's collapsed text, which is the\n // label for a header cell but every cell run together for a row.\n ghostLabel?: (source: DragHit) => string\n // The live preview: the items between the source and the drop slot slide\n // aside and the source slides into the gap, so the grid shows the layout the\n // drop will produce instead of only pointing at it. Transforms only, so\n // nothing reflows and the real order still changes once, on drop. Replaces\n // the drop-indicator line, which only renders when this is off.\n preview?: boolean\n onDrop: (sourceId: string, drop: DragDrop) => void\n}\n\n// The reorderable items, in display order along the drag's axis. This is both\n// what the pointer is hit-tested against and what the live preview slides.\nexport interface DragItems {\n getOrderedIds: () => string[]\n // Every element that travels with that id: a column's header and body cells,\n // or simply the row.\n getElements: (id: string) => HTMLElement[]\n}\n\nexport interface DragController {\n dispose: () => void\n}\n\nconst collapse = (text: string): string => text.replace(/\\s+/g, ' ').trim()\n\n// Which slot the source lands in, expressed in the CURRENT order — the same\n// arithmetic the drop itself does, so the preview cannot disagree with it.\nconst destinationIndex = (from: number, target: number, after: boolean): number =>\n (target > from ? (after ? target : target - 1) : (after ? target + 1 : target))\n\nconst swallowClick = (event: Event) => {\n event.preventDefault()\n event.stopPropagation()\n}\n\nconst disarmSwallowClick = () => {\n document.removeEventListener('click', swallowClick, { capture: true })\n}\n\n// Server-side rendering reaches here through Angular, whose ngAfterViewInit and\n// ngOnDestroy both run during prerender. There is no pointer to track without a\n// document, so hand back an inert controller instead of touching one.\nconst INERT_CONTROLLER: DragController = { dispose() {} }\n\nexport const createDragController = (options: DragControllerOptions): DragController => {\n if (typeof document === 'undefined') {\n return INERT_CONTROLLER\n }\n\n const { axis, root } = options\n const horizontal = axis === 'x'\n\n let sourceHit: DragHit | null = null\n let dragging = false\n // +1 while the axis runs the way the coordinates do, -1 under RTL, where the\n // logical column order runs against increasing x. Negating the axis keeps\n // every comparison below — the zone tiling, the hysteresis band, both edge\n // clamps, `index > fromIndex` — written once, for one direction. Only the\n // three places that touch pixels again convert back: the indicator's side,\n // the preview transform and the settling ghost.\n //\n // Read from the grid's own computed direction rather than the document's: a\n // grid can sit in an RTL subtree of an LTR page, which is exactly the case\n // the vendored isRTL() cannot see.\n let axisSign = 1\n let origin = { x: 0, y: 0 }\n let pointer = { x: 0, y: 0 }\n let drop: DragDrop | null = null\n let ghost: HTMLElement | null = null\n let indicator: HTMLElement | null = null\n let scrollFrame = 0\n let pointerId = -1\n let previewKey = ''\n // The zone that produced the current drop, padded by the hysteresis band.\n let sticky: { lo: number; hi: number } | null = null\n // The transform currently applied to each item — the preview diffs against\n // it, and a layout captured mid-preview (a row revealed by auto-scroll) is\n // corrected back through it.\n const previewDeltas = new Map<string, number>()\n // Every id that still carries the transition class. Mid-drag un-shifts keep\n // the class so the slide BACK animates too; it only comes off at the end.\n const previewClassed = new Set<string>()\n // Each item's start in the scroll container's CONTENT coordinates — a space\n // no transform and no scroll can move. Captured at drag start, when nothing\n // is transformed yet, so the values are exact.\n const contentStarts = new Map<string, number>()\n const contentSizes = new Map<string, number>()\n\n // A rect's logical start: its leading edge along the axis, in the negated\n // space when the axis is negated, so the span stays [start, start + size].\n const logicalStart = (rect: DOMRect): number => {\n if (!horizontal) {\n return rect.top\n }\n\n return axisSign === 1 ? rect.left : -rect.right\n }\n\n const logicalPointer = (): number => (horizontal ? pointer.x * axisSign : pointer.y)\n\n const stopAutoScroll = () => {\n if (scrollFrame) {\n cancelAnimationFrame(scrollFrame)\n scrollFrame = 0\n }\n }\n\n const autoScroll = () => {\n scrollFrame = 0\n const scrollElement = options.getScrollElement()\n if (!scrollElement || !dragging) {\n return\n }\n\n const rect = scrollElement.getBoundingClientRect()\n const position = horizontal ? pointer.x : pointer.y\n const start = horizontal ? rect.left : rect.top\n const end = horizontal ? rect.right : rect.bottom\n\n // Speed ramps with how far into the edge zone the pointer sits, so the\n // list creeps at the boundary and races when pinned to the edge.\n let delta = 0\n if (position < start + EDGE_SIZE) {\n delta = -EDGE_MAX_SPEED * Math.min(1, (start + EDGE_SIZE - position) / EDGE_SIZE)\n } else if (position > end - EDGE_SIZE) {\n delta = EDGE_MAX_SPEED * Math.min(1, (position - (end - EDGE_SIZE)) / EDGE_SIZE)\n }\n\n if (delta !== 0) {\n if (horizontal) {\n scrollElement.scrollLeft += delta\n } else {\n scrollElement.scrollTop += delta\n }\n\n // Scrolling moved the content under a stationary pointer, and no\n // pointermove will fire to say so.\n updateTarget()\n scrollFrame = requestAnimationFrame(autoScroll)\n }\n }\n\n // The ghost trails the pointer instead of sitting under it, so the cell the\n // hit test resolves stays visible.\n const moveGhost = () => {\n if (ghost) {\n ghost.style.transform = `translate(${pointer.x + GHOST_OFFSET}px, ${pointer.y + GHOST_OFFSET}px)`\n }\n }\n\n const hideIndicator = () => {\n if (indicator) {\n indicator.hidden = true\n }\n }\n\n const showIndicator = (target: HTMLElement, after: boolean) => {\n if (!indicator) {\n return\n }\n\n const rect = target.getBoundingClientRect()\n const rootRect = root.getBoundingClientRect()\n indicator.hidden = false\n if (horizontal) {\n // `after` is logical; which pixel edge that is flips with the axis.\n const trailing = after === (axisSign === 1)\n indicator.style.left = `${trailing ? rect.right : rect.left}px`\n indicator.style.top = `${rootRect.top}px`\n indicator.style.height = `${rootRect.height}px`\n } else {\n indicator.style.left = `${rootRect.left}px`\n indicator.style.top = `${after ? rect.bottom : rect.top}px`\n indicator.style.width = `${rootRect.width}px`\n }\n }\n\n const startDrag = () => {\n dragging = true\n axisSign = horizontal && getComputedStyle(root).direction === 'rtl' ? -1 : 1\n // Chrome focuses a button on mousedown (a press on a header lands on the\n // sort button), and tearing a FOCUSED element out of the DOM — which the\n // drop's header re-append does — makes the browser reset the scroller it\n // lived in: releasing a drag with the viewport scrolled right snapped it\n // back to the left. A drag has no use for focus; drop it the moment the\n // press becomes one. Sub-threshold presses never get here, so click-to-\n // sort keeps its focus behavior.\n const active = document.activeElement\n if (active instanceof HTMLElement && root.contains(active)) {\n active.blur()\n }\n\n root.classList.add(CLASS_NAME_DRAG_ACTIVE)\n sourceHit!.element.classList.add(CLASS_NAME_DRAGGING)\n\n const rect = sourceHit!.element.getBoundingClientRect()\n ghost = document.createElement('div')\n ghost.className = CLASS_NAME_GHOST\n ghost.setAttribute('aria-hidden', 'true')\n ghost.style.width = `${rect.width}px`\n ghost.style.height = `${rect.height}px`\n ghost.textContent = options.ghostLabel ?\n options.ghostLabel(sourceHit!) :\n collapse(sourceHit!.element.textContent ?? '')\n\n // With the live preview on, the sliding items ARE the drop indicator; a\n // line has no stable place to stand, because it would be measured against\n // cells that are themselves mid-transition. The line only exists for\n // consumers that turned the preview off.\n if (!options.preview) {\n indicator = document.createElement('div')\n indicator.className = `${CLASS_NAME_INDICATOR} ${CLASS_NAME_INDICATOR}-${axis}`\n indicator.setAttribute('aria-hidden', 'true')\n indicator.hidden = true\n root.append(indicator)\n }\n\n root.append(ghost)\n moveGhost()\n captureLayout()\n }\n\n // Where the container's content coordinate 0 currently sits on screen. The\n // one live measurement the hit test needs — everything else is arithmetic on\n // the snapshot, so mid-transition rects can never feed back into the slot.\n //\n // This is the whole reason the hit test is neither document.elementFromPoint\n // nor rect-minus-delta reconstruction: the preview MOVES the cells under the\n // pointer, and a rect measured mid-transition contains only part of its\n // delta, so both models handed back zones shifted by the unfinished\n // remainder of the animation. Dragging with the direction of travel, that\n // shifted zone swallowed the next item's first half and the slot flipped\n // back and forth until the transition settled.\n const contentOrigin = (): number => {\n const scrollElement = options.getScrollElement()\n if (!scrollElement) {\n return 0\n }\n\n const rect = scrollElement.getBoundingClientRect()\n // scrollLeft stays a physical axis in every engine (RTL just runs it\n // negative), so it enters the negated space through the sign.\n return horizontal ?\n logicalStart(rect) - (axisSign * scrollElement.scrollLeft) :\n rect.top - scrollElement.scrollTop\n }\n\n const captureItem = (id: string, origin: number): boolean => {\n const element = options.items.getElements(id)[0]\n if (!element) {\n return false\n }\n\n const rect = element.getBoundingClientRect()\n // Corrected by the applied delta for items first seen mid-preview (a row\n // revealed by auto-scroll); at drag start every delta is zero.\n const start = logicalStart(rect) - (previewDeltas.get(id) ?? 0)\n contentStarts.set(id, start - origin)\n contentSizes.set(id, horizontal ? rect.width : rect.height)\n return true\n }\n\n const captureLayout = () => {\n contentStarts.clear()\n contentSizes.clear()\n const origin = contentOrigin()\n for (const id of options.items.getOrderedIds()) {\n captureItem(id, origin)\n }\n }\n\n const updateTarget = () => {\n const position = logicalPointer()\n const origin = contentOrigin()\n\n const zones: Array<{ id: string; start: number; size: number }> = []\n for (const id of options.items.getOrderedIds()) {\n if (!contentStarts.has(id) && !captureItem(id, origin)) {\n continue\n }\n\n zones.push({ id, start: origin + contentStarts.get(id)!, size: contentSizes.get(id)! })\n }\n\n let hit: { id: string; after: boolean } | null = null\n const lastZone = zones[zones.length - 1]\n const fromIndex = zones.findIndex(zone => zone.id === sourceHit!.id)\n\n if (lastZone && position > lastZone.start + lastZone.size) {\n // Past the end there is no zone to be inside, but the intent could not\n // be clearer: after the last item the source may land on. Same at the\n // start edge below.\n sticky = null\n for (let index = zones.length - 1; index >= 0; index--) {\n if (options.canDrop(sourceHit!.id, zones[index].id)) {\n hit = { id: zones[index].id, after: true }\n break\n }\n }\n } else if (lastZone && position < zones[0].start) {\n sticky = null\n for (const zone of zones) {\n if (options.canDrop(sourceHit!.id, zone.id)) {\n hit = { id: zone.id, after: false }\n break\n }\n }\n } else if (drop && sticky && position >= sticky.lo - DRAG_HYSTERESIS && position <= sticky.hi + DRAG_HYSTERESIS) {\n // Still inside (or barely past) the zone that produced the current\n // drop — keep it, so boundary jitter cannot re-animate.\n hit = drop\n } else {\n for (let index = 0; index < zones.length; index++) {\n const zone = zones[index]\n const previous = zones[index - 1]\n const following = zones[index + 1]\n // Zones TILE the axis: real tables have collapsed borders between the\n // cells, and those few pixels belong to no rect. A pointer crossing\n // such a gap used to hit nothing, which cleared the preview and\n // re-applied it on the far side — replaying the slide right at the\n // boundary. Each gap is split down its middle instead.\n const lo = previous ? (previous.start + previous.size + zone.start) / 2 : zone.start\n const hi = following ? (zone.start + zone.size + following.start) / 2 : zone.start + zone.size\n if (position < lo || position > hi) {\n continue\n }\n\n if (options.canDrop(sourceHit!.id, zone.id)) {\n // Entering a neighbour's zone displaces it AT ONCE — the swap fires\n // at the boundary, not at the neighbour's midpoint, which is the\n // feel MUI and AG have and what a drag reads as: \"I am past it, so\n // it moves\". The release then just commits whatever the preview\n // shows.\n hit = {\n id: zone.id,\n after: fromIndex === -1 ? position > zone.start + (zone.size / 2) : index > fromIndex\n }\n sticky = { lo, hi }\n }\n\n break\n }\n }\n\n if (!hit) {\n drop = null\n sticky = null\n hideIndicator()\n applyPreview()\n return\n }\n\n drop = hit\n showIndicator(options.items.getElements(hit.id)[0], hit.after)\n applyPreview()\n }\n\n const clearPreview = () => {\n for (const id of previewClassed) {\n for (const element of options.items.getElements(id)) {\n element.style.transform = ''\n element.classList.remove(CLASS_NAME_PREVIEW)\n }\n }\n\n previewKey = ''\n previewDeltas.clear()\n previewClassed.clear()\n }\n\n const clearLayout = () => {\n contentStarts.clear()\n contentSizes.clear()\n }\n\n const applyPreview = () => {\n if (!options.preview || !sourceHit) {\n return\n }\n\n const ids = options.items.getOrderedIds()\n const from = ids.indexOf(sourceHit.id)\n const target = drop ? ids.indexOf(drop.id) : -1\n const to = from === -1 || target === -1 ? from : destinationIndex(from, target, drop!.after)\n\n // Keyed on the DESTINATION SLOT, not on the hit: two hits that resolve to\n // the same slot (the clamp past the end vs the last zone, jitter inside\n // the hysteresis band) must not rewrite anything. A clear-and-reapply of\n // identical transforms next to a forced layout read restarts the running\n // transition from zero — the mid-drag replay this file has already paid\n // for once.\n const key = String(to)\n if (key === previewKey) {\n return\n }\n\n previewKey = key\n\n const next = new Map<string, number>()\n if (from !== -1 && to !== from) {\n // Sizes come from the drag-start snapshot: no DOM reads may happen in\n // here, or they would flush layout between the transform writes below.\n const sizeOf = (index: number): number => contentSizes.get(ids[index]) ?? 0\n const forward = to > from\n const first = forward ? from + 1 : to\n const last = forward ? to : from - 1\n const sourceSize = sizeOf(from)\n let travelled = 0\n\n for (let index = first; index <= last; index++) {\n travelled += sizeOf(index)\n next.set(ids[index], forward ? -sourceSize : sourceSize)\n }\n\n // The source moves by the combined size of everything it passed, so no\n // gap opens anywhere and the axis keeps its total extent.\n next.set(ids[from], forward ? travelled : -travelled)\n }\n\n // Only elements whose delta actually changed are touched; everything else\n // keeps its running transition undisturbed. An un-shift keeps the\n // transition class, so sliding back home animates exactly like sliding\n // aside did — the class only comes off when the drag ends.\n for (const id of previewDeltas.keys()) {\n if (!next.has(id)) {\n for (const element of options.items.getElements(id)) {\n element.style.transform = ''\n }\n }\n }\n\n for (const [id, delta] of next) {\n if (previewDeltas.get(id) === delta) {\n continue\n }\n\n for (const element of options.items.getElements(id)) {\n element.style.transform = horizontal ?\n `translateX(${axisSign * delta}px)` :\n `translateY(${delta}px)`\n element.classList.add(CLASS_NAME_PREVIEW)\n }\n\n previewClassed.add(id)\n }\n\n previewDeltas.clear()\n for (const [id, delta] of next) {\n previewDeltas.set(id, delta)\n }\n }\n\n // The gesture's closing beat: instead of vanishing at the pointer, the\n // ghost flies to where the item landed (or back home on cancel) and fades\n // out there. Purely cosmetic — the commit below never waits for it.\n const settleGhost = (x: number, y: number) => {\n if (!ghost) {\n return\n }\n\n const element = ghost\n // Taken out of cleanup's hands; it removes itself when the flight ends.\n ghost = null\n element.classList.add(CLASS_NAME_GHOST_SETTLE)\n element.style.transform = `translate(${x}px, ${y}px)`\n element.style.opacity = '0'\n\n let settled = false\n const finish = () => {\n if (!settled) {\n settled = true\n element.remove()\n }\n }\n\n element.addEventListener('transitionend', finish)\n setTimeout(finish, GHOST_SETTLE_FALLBACK_MS)\n }\n\n // Where the ghost should land: the source's current visual position for a\n // commit (the preview already put it in its slot), its untransformed home\n // for a cancel (the preview is about to slide everything back there).\n const settleTarget = (committed: boolean): { x: number; y: number } | null => {\n if (!sourceHit) {\n return null\n }\n\n const rect = sourceHit.element.getBoundingClientRect()\n if (committed) {\n return { x: rect.left, y: rect.top }\n }\n\n const delta = previewDeltas.get(sourceHit.id) ?? 0\n return horizontal ?\n { x: rect.left - (axisSign * delta), y: rect.top } :\n { x: rect.left, y: rect.top - delta }\n }\n\n const cleanup = () => {\n clearPreview()\n clearLayout()\n stopAutoScroll()\n root.classList.remove(CLASS_NAME_DRAG_ACTIVE)\n sourceHit?.element.classList.remove(CLASS_NAME_DRAGGING)\n ghost?.remove()\n indicator?.remove()\n ghost = null\n indicator = null\n sourceHit = null\n dragging = false\n drop = null\n sticky = null\n pointerId = -1\n document.removeEventListener('pointermove', onPointerMove)\n document.removeEventListener('pointerup', onPointerUp)\n document.removeEventListener('pointercancel', cancel)\n document.removeEventListener('keydown', onKeydown)\n }\n\n function cancel() {\n if (dragging) {\n const home = settleTarget(false)\n if (home) {\n settleGhost(home.x, home.y)\n }\n }\n\n cleanup()\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n cancel()\n }\n }\n\n function onPointerMove(event: PointerEvent) {\n if (event.pointerId !== pointerId || !sourceHit) {\n return\n }\n\n pointer = { x: event.clientX, y: event.clientY }\n\n if (!dragging) {\n const travelled = Math.max(Math.abs(pointer.x - origin.x), Math.abs(pointer.y - origin.y))\n if (travelled < DRAG_THRESHOLD) {\n return\n }\n\n startDrag()\n }\n\n // Suppresses the text selection a pointer drag would otherwise paint\n // across the header or the rows.\n event.preventDefault()\n moveGhost()\n updateTarget()\n\n if (!scrollFrame) {\n scrollFrame = requestAnimationFrame(autoScroll)\n }\n }\n\n function onPointerUp(event: PointerEvent) {\n if (event.pointerId !== pointerId) {\n return\n }\n\n const sourceId = sourceHit?.id\n const source = sourceHit?.element\n const pending = drop\n const wasDragging = dragging\n if (wasDragging) {\n const committed = Boolean(pending && pending.id !== sourceId)\n const landing = settleTarget(committed)\n if (landing) {\n settleGhost(landing.x, landing.y)\n }\n }\n\n cleanup()\n\n if (!wasDragging) {\n return\n }\n\n // A drag that ends on the element it started from is still followed by a\n // click there — which on a header cell would toggle the sort. Swallow\n // exactly that one. Releasing anywhere else fires the click on the common\n // ancestor instead (never the sort button), so arming there would only\n // risk eating an unrelated click; the 0 ms disarm covers the rest.\n if (source?.contains(event.target as Node)) {\n document.addEventListener('click', swallowClick, { capture: true, once: true })\n setTimeout(disarmSwallowClick, 0)\n }\n\n if (sourceId && pending && pending.id !== sourceId) {\n options.onDrop(sourceId, pending)\n }\n }\n\n const onPointerDown = (event: PointerEvent) => {\n // Primary button / single touch only — a right-click or a second finger\n // must not start a reorder.\n if (!event.isPrimary || (event.pointerType === 'mouse' && event.button !== 0) || sourceHit) {\n return\n }\n\n const element = (event.target as HTMLElement | null)?.closest<HTMLElement>(options.handle)\n if (!element) {\n return\n }\n\n const hit = options.resolveSource(element, event.target as HTMLElement)\n if (!hit) {\n return\n }\n\n sourceHit = hit\n pointerId = event.pointerId\n origin = { x: event.clientX, y: event.clientY }\n pointer = origin\n\n document.addEventListener('pointermove', onPointerMove)\n document.addEventListener('pointerup', onPointerUp)\n document.addEventListener('pointercancel', cancel)\n document.addEventListener('keydown', onKeydown)\n }\n\n root.addEventListener('pointerdown', onPointerDown)\n\n return {\n dispose() {\n root.removeEventListener('pointerdown', onPointerDown)\n disarmSwallowClick()\n cleanup()\n }\n }\n}\n\nconst SELECTOR_DRAGGABLE_COLUMN = 'th[data-coreui-draggable]'\nconst SELECTOR_RESIZER = '[data-coreui-resizer]'\n// Spacer and empty rows have a single colSpan cell, so a positional lookup\n// would land on the wrong thing.\nconst SELECTOR_PREVIEW_ROWS = 'thead tr, tbody tr:not(.data-grid-spacer):not(.data-grid-empty)'\n\n// A column's cells are found by POSITION, not by a data attribute: the body\n// only marks `data-coreui-column` when cellNavigation is on, while the header\n// always does — so the header cell's index is the reliable way in, and it\n// already accounts for the leading handle and select columns.\nconst columnItems = (root: HTMLElement): DragItems => ({\n getOrderedIds: () =>\n [...root.querySelectorAll<HTMLTableCellElement>('thead tr:first-child th[data-coreui-column]')]\n .map(cell => cell.dataset.coreuiColumn ?? ''),\n getElements(id) {\n const header = root.querySelector<HTMLTableCellElement>(`thead th[data-coreui-column=\"${escapeAttributeValue(id)}\"]`)\n if (!header) {\n return []\n }\n\n const { cellIndex } = header\n const elements: HTMLElement[] = []\n for (const row of root.querySelectorAll<HTMLTableRowElement>(SELECTOR_PREVIEW_ROWS)) {\n const cell = row.children[cellIndex] as HTMLElement | undefined\n if (cell) {\n elements.push(cell)\n }\n }\n\n return elements\n }\n})\n\nexport interface ColumnDragOptions {\n root: HTMLElement\n getScrollElement: () => HTMLElement | null\n // Vetoes a target: the source itself, or a column in another pinned region —\n // reordering never crosses a pinning boundary.\n canDrop: (sourceKey: string, targetKey: string) => boolean\n onReorder: (sourceKey: string, targetKey: string, after: boolean) => void\n}\n\n// The column half of the wiring, shared so each edition's adapter is the four\n// lines that resolve its own table rather than a copy of the drag setup.\nexport const createColumnDragController = (options: ColumnDragOptions): DragController =>\n createDragController({\n axis: 'x',\n root: options.root,\n handle: SELECTOR_DRAGGABLE_COLUMN,\n getScrollElement: options.getScrollElement,\n resolveSource(element, target) {\n // A press on the resize handle is a resize, not a reorder.\n if (target.closest(SELECTOR_RESIZER)) {\n return null\n }\n\n const id = element.dataset.coreuiColumn\n return id ? { id, element } : null\n },\n items: columnItems(options.root),\n // Only a column the header marks as draggable is a slot, on top of the\n // consumer's own veto — an immovable column is not somewhere to land.\n canDrop: (sourceId, targetId) =>\n sourceId !== targetId &&\n Boolean(options.root.querySelector(`th[data-coreui-column=\"${escapeAttributeValue(targetId)}\"][data-coreui-draggable]`)) &&\n options.canDrop(sourceId, targetId),\n preview: true,\n onDrop: (sourceKey, drop) => options.onReorder(sourceKey, drop.id, drop.after)\n })\n\nconst SELECTOR_ROW_HANDLE = '[data-coreui-row-handle]'\nconst SELECTOR_ROW = 'tr[data-coreui-index]'\n\nconst rowItems = (root: HTMLElement): DragItems => ({\n getOrderedIds: () =>\n [...root.querySelectorAll<HTMLElement>(`tbody ${SELECTOR_ROW}`)].map(row => row.dataset.coreuiIndex ?? ''),\n getElements(id) {\n const row = root.querySelector<HTMLElement>(`tbody tr[data-coreui-index=\"${escapeAttributeValue(id)}\"]`)\n return row ? [row] : []\n }\n})\n\nexport interface RowDragOptions {\n root: HTMLElement\n getScrollElement: () => HTMLElement | null\n onReorder: (sourceIndex: number, targetIndex: number, after: boolean) => void\n}\n\n// The row half. Unlike columns, the whole row travels but only the grip cell\n// starts the drag — a row is covered in interactive content (checkboxes,\n// editors, custom cells) that a whole-row handle would swallow.\nexport const createRowDragController = (options: RowDragOptions): DragController =>\n createDragController({\n axis: 'y',\n root: options.root,\n handle: SELECTOR_ROW_HANDLE,\n getScrollElement: options.getScrollElement,\n resolveSource(handle) {\n const row = handle.closest<HTMLElement>(SELECTOR_ROW)\n const id = row?.dataset.coreuiIndex\n return row && id ? { id, element: row } : null\n },\n items: rowItems(options.root),\n canDrop: (sourceId, targetId) => sourceId !== targetId,\n // The row's own text is every cell run together; the first data cell reads\n // as the row's name in practice.\n // The row's own text is every cell run together, so the ghost carries the\n // first DATA cell instead — found by skipping the leading handle and\n // select cells rather than by data-coreui-column, which the body only\n // carries when cellNavigation is on (without it the ghost came up empty).\n ghostLabel: source =>\n collapse(\n source.element.querySelector(\n 'td:not(.data-grid-row-handle-cell):not(.data-grid-select-cell)'\n )?.textContent ?? ''\n ),\n preview: true,\n onDrop: (sourceId, drop) => options.onReorder(Number(sourceId), Number(drop.id), drop.after)\n })\n"],"mappings":"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"}
package/dist/js/drag.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  /*!
2
- * CoreUI PRO data-grid v0.2.0-rc.0 (https://coreui.io)
2
+ * CoreUI PRO data-grid v0.4.0 (https://coreui.io)
3
3
  * Copyright 2026 The CoreUI Team (https://github.com/orgs/coreui/people)
4
4
  * License (https://coreui.io/pro/license/)
5
5
  */
6
- //#region src/util/drag.ts
6
+ import { escapeAttributeValue } from "./selector.mjs";
7
+ // src/util/drag.ts
7
8
  /**
8
9
  * --------------------------------------------------------------------------
9
10
  * CoreUI PRO data-grid util/drag.ts
@@ -38,6 +39,7 @@ const createDragController = (options) => {
38
39
  const horizontal = axis === "x";
39
40
  let sourceHit = null;
40
41
  let dragging = false;
42
+ let axisSign = 1;
41
43
  let origin = {
42
44
  x: 0,
43
45
  y: 0
@@ -57,6 +59,11 @@ const createDragController = (options) => {
57
59
  const previewClassed = /* @__PURE__ */ new Set();
58
60
  const contentStarts = /* @__PURE__ */ new Map();
59
61
  const contentSizes = /* @__PURE__ */ new Map();
62
+ const logicalStart = (rect) => {
63
+ if (!horizontal) return rect.top;
64
+ return axisSign === 1 ? rect.left : -rect.right;
65
+ };
66
+ const logicalPointer = () => horizontal ? pointer.x * axisSign : pointer.y;
60
67
  const stopAutoScroll = () => {
61
68
  if (scrollFrame) {
62
69
  cancelAnimationFrame(scrollFrame);
@@ -93,7 +100,8 @@ const createDragController = (options) => {
93
100
  const rootRect = root.getBoundingClientRect();
94
101
  indicator.hidden = false;
95
102
  if (horizontal) {
96
- indicator.style.left = `${after ? rect.right : rect.left}px`;
103
+ const trailing = after === (axisSign === 1);
104
+ indicator.style.left = `${trailing ? rect.right : rect.left}px`;
97
105
  indicator.style.top = `${rootRect.top}px`;
98
106
  indicator.style.height = `${rootRect.height}px`;
99
107
  } else {
@@ -104,6 +112,7 @@ const createDragController = (options) => {
104
112
  };
105
113
  const startDrag = () => {
106
114
  dragging = true;
115
+ axisSign = horizontal && getComputedStyle(root).direction === "rtl" ? -1 : 1;
107
116
  const active = document.activeElement;
108
117
  if (active instanceof HTMLElement && root.contains(active)) active.blur();
109
118
  root.classList.add(CLASS_NAME_DRAG_ACTIVE);
@@ -130,13 +139,13 @@ const createDragController = (options) => {
130
139
  const scrollElement = options.getScrollElement();
131
140
  if (!scrollElement) return 0;
132
141
  const rect = scrollElement.getBoundingClientRect();
133
- return horizontal ? rect.left - scrollElement.scrollLeft : rect.top - scrollElement.scrollTop;
142
+ return horizontal ? logicalStart(rect) - axisSign * scrollElement.scrollLeft : rect.top - scrollElement.scrollTop;
134
143
  };
135
144
  const captureItem = (id, origin) => {
136
145
  const element = options.items.getElements(id)[0];
137
146
  if (!element) return false;
138
147
  const rect = element.getBoundingClientRect();
139
- const start = (horizontal ? rect.left : rect.top) - (previewDeltas.get(id) ?? 0);
148
+ const start = logicalStart(rect) - (previewDeltas.get(id) ?? 0);
140
149
  contentStarts.set(id, start - origin);
141
150
  contentSizes.set(id, horizontal ? rect.width : rect.height);
142
151
  return true;
@@ -148,7 +157,7 @@ const createDragController = (options) => {
148
157
  for (const id of options.items.getOrderedIds()) captureItem(id, origin);
149
158
  };
150
159
  const updateTarget = () => {
151
- const position = horizontal ? pointer.x : pointer.y;
160
+ const position = logicalPointer();
152
161
  const origin = contentOrigin();
153
162
  const zones = [];
154
163
  for (const id of options.items.getOrderedIds()) {
@@ -251,7 +260,7 @@ const createDragController = (options) => {
251
260
  for (const [id, delta] of next) {
252
261
  if (previewDeltas.get(id) === delta) continue;
253
262
  for (const element of options.items.getElements(id)) {
254
- element.style.transform = horizontal ? `translateX(${delta}px)` : `translateY(${delta}px)`;
263
+ element.style.transform = horizontal ? `translateX(${axisSign * delta}px)` : `translateY(${delta}px)`;
255
264
  element.classList.add(CLASS_NAME_PREVIEW);
256
265
  }
257
266
  previewClassed.add(id);
@@ -285,7 +294,7 @@ const createDragController = (options) => {
285
294
  };
286
295
  const delta = previewDeltas.get(sourceHit.id) ?? 0;
287
296
  return horizontal ? {
288
- x: rect.left - delta,
297
+ x: rect.left - axisSign * delta,
289
298
  y: rect.top
290
299
  } : {
291
300
  x: rect.left,
@@ -393,7 +402,7 @@ const SELECTOR_PREVIEW_ROWS = "thead tr, tbody tr:not(.data-grid-spacer):not(.da
393
402
  const columnItems = (root) => ({
394
403
  getOrderedIds: () => [...root.querySelectorAll("thead tr:first-child th[data-coreui-column]")].map((cell) => cell.dataset.coreuiColumn ?? ""),
395
404
  getElements(id) {
396
- const header = root.querySelector(`thead th[data-coreui-column="${id}"]`);
405
+ const header = root.querySelector(`thead th[data-coreui-column="${escapeAttributeValue(id)}"]`);
397
406
  if (!header) return [];
398
407
  const { cellIndex } = header;
399
408
  const elements = [];
@@ -418,7 +427,7 @@ const createColumnDragController = (options) => createDragController({
418
427
  } : null;
419
428
  },
420
429
  items: columnItems(options.root),
421
- canDrop: (sourceId, targetId) => sourceId !== targetId && Boolean(options.root.querySelector(`th[data-coreui-column="${targetId}"][data-coreui-draggable]`)) && options.canDrop(sourceId, targetId),
430
+ canDrop: (sourceId, targetId) => sourceId !== targetId && Boolean(options.root.querySelector(`th[data-coreui-column="${escapeAttributeValue(targetId)}"][data-coreui-draggable]`)) && options.canDrop(sourceId, targetId),
422
431
  preview: true,
423
432
  onDrop: (sourceKey, drop) => options.onReorder(sourceKey, drop.id, drop.after)
424
433
  });
@@ -427,7 +436,7 @@ const SELECTOR_ROW = "tr[data-coreui-index]";
427
436
  const rowItems = (root) => ({
428
437
  getOrderedIds: () => [...root.querySelectorAll(`tbody ${SELECTOR_ROW}`)].map((row) => row.dataset.coreuiIndex ?? ""),
429
438
  getElements(id) {
430
- const row = root.querySelector(`tbody tr[data-coreui-index="${id}"]`);
439
+ const row = root.querySelector(`tbody tr[data-coreui-index="${escapeAttributeValue(id)}"]`);
431
440
  return row ? [row] : [];
432
441
  }
433
442
  });
@@ -450,7 +459,6 @@ const createRowDragController = (options) => createDragController({
450
459
  preview: true,
451
460
  onDrop: (sourceId, drop) => options.onReorder(Number(sourceId), Number(drop.id), drop.after)
452
461
  });
453
- //#endregion
454
462
  export { createColumnDragController, createDragController, createRowDragController };
455
463
 
456
464
  //# sourceMappingURL=drag.mjs.map