@carbon/utilities 0.18.0-rc.0 → 0.18.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,6 +1,6 @@
1
1
  //#region src/makeDraggable/makeDraggable.d.ts
2
2
  /**
3
- * Copyright IBM Corp. 2025, 2025
3
+ * Copyright IBM Corp. 2025, 2026
4
4
  *
5
5
  * This source code is licensed under the Apache-2.0 license found in the
6
6
  * LICENSE file in the root directory of this source tree.
@@ -41,6 +41,7 @@ declare const makeDraggable: ({
41
41
  shiftDragStep
42
42
  }: DraggableProps) => {
43
43
  cleanup: () => void;
44
+ init: () => void;
44
45
  };
45
46
  //#endregion
46
47
  export { makeDraggable };
@@ -9,8 +9,49 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
9
9
  el.style.cursor = "default";
10
10
  } else el.style.cursor = "move";
11
11
  let isDragging = false;
12
- let offsetX = 0;
13
- let offsetY = 0;
12
+ let currentX = 0;
13
+ let currentY = 0;
14
+ let initialMouseX = 0;
15
+ let initialMouseY = 0;
16
+ let baseMatrix = null;
17
+ /**
18
+ * Syncs position and extracts base matrix from computed style.
19
+ * Reads from getComputedStyle() to include all transform sources (inline, classes, etc.).
20
+ */
21
+ const syncTransformState = () => {
22
+ const transformString = window.getComputedStyle(el).transform;
23
+ if (!transformString || transformString === "none") {
24
+ currentX = 0;
25
+ currentY = 0;
26
+ baseMatrix = null;
27
+ return;
28
+ }
29
+ const matrix = new DOMMatrix(transformString);
30
+ currentX = matrix.m41;
31
+ currentY = matrix.m42;
32
+ if (matrix.a === 1 && matrix.b === 0 && matrix.c === 0 && matrix.d === 1 && matrix.e === 0 && matrix.f === 0) baseMatrix = null;
33
+ else baseMatrix = new DOMMatrix([
34
+ matrix.a,
35
+ matrix.b,
36
+ matrix.c,
37
+ matrix.d,
38
+ 0,
39
+ 0
40
+ ]);
41
+ };
42
+ syncTransformState();
43
+ /**
44
+ * Applies transform by combining translation with base matrix using matrix multiplication.
45
+ */
46
+ const applyTransform = (x, y) => {
47
+ if (baseMatrix) {
48
+ const translationMatrix = new DOMMatrix();
49
+ translationMatrix.m41 = x;
50
+ translationMatrix.m42 = y;
51
+ const combined = translationMatrix.multiply(baseMatrix);
52
+ el.style.transform = combined.toString();
53
+ } else el.style.transform = `translate(${x}px, ${y}px)`;
54
+ };
14
55
  const dispatch = (type, detail) => {
15
56
  const eventInit = {
16
57
  detail,
@@ -19,9 +60,13 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
19
60
  el.dispatchEvent(new CustomEvent(type, eventInit));
20
61
  };
21
62
  const onKeyDown = (e) => {
22
- if (e.key === "Enter") isDragging = !isDragging;
23
- if (isDragging) dispatch("dragstart", { keyboard: true });
24
- else dispatch("dragend", { keyboard: true });
63
+ if (e.key === "Enter") {
64
+ isDragging = !isDragging;
65
+ if (isDragging) {
66
+ syncTransformState();
67
+ dispatch("dragstart", { keyboard: true });
68
+ } else dispatch("dragend", { keyboard: true });
69
+ }
25
70
  if (!isDragging) return;
26
71
  const distance = e.shiftKey ? shiftDragStep ?? 32 : dragStep ?? 8;
27
72
  switch (e.key) {
@@ -30,16 +75,20 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
30
75
  e.preventDefault();
31
76
  break;
32
77
  case "ArrowLeft":
33
- el.style.left = `${el.offsetLeft - distance}px`;
78
+ currentX -= distance;
79
+ applyTransform(currentX, currentY);
34
80
  break;
35
81
  case "ArrowRight":
36
- el.style.left = `${el.offsetLeft + distance}px`;
82
+ currentX += distance;
83
+ applyTransform(currentX, currentY);
37
84
  break;
38
85
  case "ArrowUp":
39
- el.style.top = `${el.offsetTop - distance}px`;
86
+ currentY -= distance;
87
+ applyTransform(currentX, currentY);
40
88
  break;
41
89
  case "ArrowDown":
42
- el.style.top = `${el.offsetTop + distance}px`;
90
+ currentY += distance;
91
+ applyTransform(currentX, currentY);
43
92
  break;
44
93
  }
45
94
  };
@@ -47,8 +96,9 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
47
96
  const target = e.target;
48
97
  if (!(target instanceof Node)) return;
49
98
  if (!(dragHandle ? dragHandle.contains(target) : el.contains(target))) return;
50
- offsetX = e.clientX - el.offsetLeft;
51
- offsetY = e.clientY - el.offsetTop;
99
+ syncTransformState();
100
+ initialMouseX = e.clientX;
101
+ initialMouseY = e.clientY;
52
102
  isDragging = true;
53
103
  dispatch("dragstart", { mouse: true });
54
104
  document.addEventListener("mousemove", onMouseMove);
@@ -56,11 +106,16 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
56
106
  };
57
107
  const onMouseMove = (e) => {
58
108
  if (!isDragging) return;
59
- el.style.left = `${e.clientX - offsetX}px`;
60
- el.style.top = `${e.clientY - offsetY}px`;
109
+ const dx = e.clientX - initialMouseX;
110
+ const dy = e.clientY - initialMouseY;
111
+ applyTransform(currentX + dx, currentY + dy);
61
112
  };
62
- const onMouseUp = () => {
113
+ const onMouseUp = (e) => {
63
114
  if (!isDragging) return;
115
+ const dx = e.clientX - initialMouseX;
116
+ const dy = e.clientY - initialMouseY;
117
+ currentX += dx;
118
+ currentY += dy;
64
119
  isDragging = false;
65
120
  dispatch("dragend", { mouse: true });
66
121
  document.removeEventListener("mousemove", onMouseMove);
@@ -75,7 +130,18 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
75
130
  document.removeEventListener("mousemove", onMouseMove);
76
131
  document.removeEventListener("mouseup", onMouseUp);
77
132
  };
78
- return { cleanup: draggableCleanup };
133
+ /**
134
+ * Re-initialize the draggable position from the element's current computed transform.
135
+ * Call this if the element has been repositioned externally (e.g., via CSS animation,
136
+ * class changes, or other scripts) to prevent position jumps on the next drag.
137
+ */
138
+ const init = () => {
139
+ syncTransformState();
140
+ };
141
+ return {
142
+ cleanup: draggableCleanup,
143
+ init
144
+ };
79
145
  };
80
146
 
81
147
  //#endregion
@@ -11,8 +11,49 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
11
11
  el.style.cursor = "default";
12
12
  } else el.style.cursor = "move";
13
13
  let isDragging = false;
14
- let offsetX = 0;
15
- let offsetY = 0;
14
+ let currentX = 0;
15
+ let currentY = 0;
16
+ let initialMouseX = 0;
17
+ let initialMouseY = 0;
18
+ let baseMatrix = null;
19
+ /**
20
+ * Syncs position and extracts base matrix from computed style.
21
+ * Reads from getComputedStyle() to include all transform sources (inline, classes, etc.).
22
+ */
23
+ const syncTransformState = () => {
24
+ const transformString = window.getComputedStyle(el).transform;
25
+ if (!transformString || transformString === "none") {
26
+ currentX = 0;
27
+ currentY = 0;
28
+ baseMatrix = null;
29
+ return;
30
+ }
31
+ const matrix = new DOMMatrix(transformString);
32
+ currentX = matrix.m41;
33
+ currentY = matrix.m42;
34
+ if (matrix.a === 1 && matrix.b === 0 && matrix.c === 0 && matrix.d === 1 && matrix.e === 0 && matrix.f === 0) baseMatrix = null;
35
+ else baseMatrix = new DOMMatrix([
36
+ matrix.a,
37
+ matrix.b,
38
+ matrix.c,
39
+ matrix.d,
40
+ 0,
41
+ 0
42
+ ]);
43
+ };
44
+ syncTransformState();
45
+ /**
46
+ * Applies transform by combining translation with base matrix using matrix multiplication.
47
+ */
48
+ const applyTransform = (x, y) => {
49
+ if (baseMatrix) {
50
+ const translationMatrix = new DOMMatrix();
51
+ translationMatrix.m41 = x;
52
+ translationMatrix.m42 = y;
53
+ const combined = translationMatrix.multiply(baseMatrix);
54
+ el.style.transform = combined.toString();
55
+ } else el.style.transform = `translate(${x}px, ${y}px)`;
56
+ };
16
57
  const dispatch = (type, detail) => {
17
58
  const eventInit = {
18
59
  detail,
@@ -21,9 +62,13 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
21
62
  el.dispatchEvent(new CustomEvent(type, eventInit));
22
63
  };
23
64
  const onKeyDown = (e) => {
24
- if (e.key === "Enter") isDragging = !isDragging;
25
- if (isDragging) dispatch("dragstart", { keyboard: true });
26
- else dispatch("dragend", { keyboard: true });
65
+ if (e.key === "Enter") {
66
+ isDragging = !isDragging;
67
+ if (isDragging) {
68
+ syncTransformState();
69
+ dispatch("dragstart", { keyboard: true });
70
+ } else dispatch("dragend", { keyboard: true });
71
+ }
27
72
  if (!isDragging) return;
28
73
  const distance = e.shiftKey ? shiftDragStep ?? 32 : dragStep ?? 8;
29
74
  switch (e.key) {
@@ -32,16 +77,20 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
32
77
  e.preventDefault();
33
78
  break;
34
79
  case "ArrowLeft":
35
- el.style.left = `${el.offsetLeft - distance}px`;
80
+ currentX -= distance;
81
+ applyTransform(currentX, currentY);
36
82
  break;
37
83
  case "ArrowRight":
38
- el.style.left = `${el.offsetLeft + distance}px`;
84
+ currentX += distance;
85
+ applyTransform(currentX, currentY);
39
86
  break;
40
87
  case "ArrowUp":
41
- el.style.top = `${el.offsetTop - distance}px`;
88
+ currentY -= distance;
89
+ applyTransform(currentX, currentY);
42
90
  break;
43
91
  case "ArrowDown":
44
- el.style.top = `${el.offsetTop + distance}px`;
92
+ currentY += distance;
93
+ applyTransform(currentX, currentY);
45
94
  break;
46
95
  }
47
96
  };
@@ -49,8 +98,9 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
49
98
  const target = e.target;
50
99
  if (!(target instanceof Node)) return;
51
100
  if (!(dragHandle ? dragHandle.contains(target) : el.contains(target))) return;
52
- offsetX = e.clientX - el.offsetLeft;
53
- offsetY = e.clientY - el.offsetTop;
101
+ syncTransformState();
102
+ initialMouseX = e.clientX;
103
+ initialMouseY = e.clientY;
54
104
  isDragging = true;
55
105
  dispatch("dragstart", { mouse: true });
56
106
  document.addEventListener("mousemove", onMouseMove);
@@ -58,11 +108,16 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
58
108
  };
59
109
  const onMouseMove = (e) => {
60
110
  if (!isDragging) return;
61
- el.style.left = `${e.clientX - offsetX}px`;
62
- el.style.top = `${e.clientY - offsetY}px`;
111
+ const dx = e.clientX - initialMouseX;
112
+ const dy = e.clientY - initialMouseY;
113
+ applyTransform(currentX + dx, currentY + dy);
63
114
  };
64
- const onMouseUp = () => {
115
+ const onMouseUp = (e) => {
65
116
  if (!isDragging) return;
117
+ const dx = e.clientX - initialMouseX;
118
+ const dy = e.clientY - initialMouseY;
119
+ currentX += dx;
120
+ currentY += dy;
66
121
  isDragging = false;
67
122
  dispatch("dragend", { mouse: true });
68
123
  document.removeEventListener("mousemove", onMouseMove);
@@ -77,7 +132,18 @@ const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDra
77
132
  document.removeEventListener("mousemove", onMouseMove);
78
133
  document.removeEventListener("mouseup", onMouseUp);
79
134
  };
80
- return { cleanup: draggableCleanup };
135
+ /**
136
+ * Re-initialize the draggable position from the element's current computed transform.
137
+ * Call this if the element has been repositioned externally (e.g., via CSS animation,
138
+ * class changes, or other scripts) to prevent position jumps on the next drag.
139
+ */
140
+ const init = () => {
141
+ syncTransformState();
142
+ };
143
+ return {
144
+ cleanup: draggableCleanup,
145
+ init
146
+ };
81
147
  };
82
148
 
83
149
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/utilities",
3
3
  "description": "Utilities and helpers to drive consistency across software products using the Carbon Design System",
4
- "version": "0.18.0-rc.0",
4
+ "version": "0.18.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -66,5 +66,5 @@
66
66
  "@ibm/telemetry-js": "^1.6.1",
67
67
  "@internationalized/number": "^3.6.1"
68
68
  },
69
- "gitHead": "d901529b78f5b70b49309ad45604fa5ec99a2e69"
69
+ "gitHead": "aeaaa31889990fc76c7e0e8a29ec0bf96ce33f82"
70
70
  }