ml-ui-lib 1.0.47 → 1.0.48

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.
@@ -119,10 +119,10 @@
119
119
  .spb-header {
120
120
  display: flex;
121
121
  align-items: center;
122
- justify-content: space-between;
122
+ justify-content: end;
123
123
  padding: 12px 16px;
124
- border-bottom: 1px solid #f1f1f1;
125
- min-height: 48px;
124
+ /* border-bottom: 1px solid #f1f1f1; */
125
+ min-height: 0px;
126
126
  }
127
127
 
128
128
  .spb-title {
@@ -136,19 +136,30 @@
136
136
  padding: 16px;
137
137
  overflow-y: auto;
138
138
  flex: 1 1 auto;
139
+
140
+ /* ✅ hide scrollbar (Firefox) */
141
+ scrollbar-width: none;
142
+
143
+ /* ✅ prevent layout shift on some browsers */
144
+ -ms-overflow-style: none;
145
+ }
146
+
147
+ /* ✅ Chrome, Safari, Edge */
148
+ .spb-body::-webkit-scrollbar {
149
+ display: none;
139
150
  }
140
151
 
141
152
  /* Desktop close button */
142
153
  .spb-close-btn {
143
154
  background: none;
144
155
  border: none;
145
- font-size: 20px;
156
+ font-size: 25px;
146
157
  line-height: 1;
147
158
  cursor: pointer;
148
- padding: 5px 10px;
159
+ padding: 30px 0px 25px 10px;
149
160
  border-radius: 6px;
150
161
  color: #111827;
151
- margin-right: 7px;
162
+ margin-right: 0px;
152
163
  }
153
164
 
154
165
  .spb-close-btn:hover {
@@ -188,9 +199,9 @@
188
199
  cursor: grabbing;
189
200
  }
190
201
 
191
- .spb-close-btn {
202
+ /* .spb-close-btn {
192
203
  display: none;
193
- }
204
+ } */
194
205
 
195
206
  /* ✅ Mobile top-left close */
196
207
  .spb-mobile-close-btn {
@@ -6,7 +6,6 @@ export interface SlidingPanelProps {
6
6
  children: React.ReactNode;
7
7
  height?: string;
8
8
  width?: string;
9
- title?: string;
10
9
  closeOnOverlayClick?: boolean;
11
10
  position?: "bottom" | "top" | "left" | "right";
12
11
  }
@@ -2,13 +2,17 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import "./SlidingPanel.css";
5
- export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width = "100%", title, closeOnOverlayClick = true, position = "bottom", }) => {
5
+ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width = "100%", closeOnOverlayClick = true, position = "bottom", }) => {
6
6
  const [visible, setVisible] = useState(false);
7
7
  const [animateOpen, setAnimateOpen] = useState(false);
8
8
  const panelRef = useRef(null);
9
9
  const headerRef = useRef(null);
10
10
  const handleRef = useRef(null);
11
- // Open/close animation
11
+ let startPos = 0;
12
+ let currentPos = 0;
13
+ let isDragging = false;
14
+ const isVertical = position === "bottom" || position === "top";
15
+ // OPEN / CLOSE animation
12
16
  useEffect(() => {
13
17
  if (isOpen) {
14
18
  setVisible(true);
@@ -17,7 +21,6 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
17
21
  return () => clearTimeout(id);
18
22
  }
19
23
  else {
20
- // Slide out according to position
21
24
  if (panelRef.current) {
22
25
  switch (position) {
23
26
  case "bottom":
@@ -35,29 +38,38 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
35
38
  }
36
39
  }
37
40
  setAnimateOpen(false);
38
- // Keep in DOM until slide completes
39
41
  const id = setTimeout(() => setVisible(false), 320);
40
42
  document.body.style.overflow = "";
41
43
  return () => clearTimeout(id);
42
44
  }
43
45
  }, [isOpen, position]);
44
- // closing
46
+ // DRAG LOGIC
45
47
  useEffect(() => {
46
48
  const panel = panelRef.current;
47
49
  const handle = handleRef.current;
48
50
  const header = headerRef.current;
49
51
  if (!panel)
50
52
  return;
51
- const dragAreaElements = [handle, header].filter(Boolean);
52
- let startPos = 0;
53
- let currentPos = 0;
54
- let isDragging = false;
55
- // Determine vertical or horizontal drag based on position
56
- const isVertical = position === "bottom" || position === "top";
57
- const isHorizontal = position === "left" || position === "right";
58
- const getDiff = (current) => {
59
- return isVertical ? current - startPos : current - startPos;
53
+ // CHANGE 1: whole panel is draggable
54
+ const dragAreaElements = [panel].filter(Boolean);
55
+ // NEW: scroll + boundary detection
56
+ const body = panel.querySelector(".spb-body");
57
+ let isScrollActive = false;
58
+ const isAtBoundary = () => {
59
+ if (!body)
60
+ return true;
61
+ const atTop = body.scrollTop <= 0;
62
+ const atBottom = body.scrollTop + body.clientHeight >= body.scrollHeight - 1;
63
+ return atTop || atBottom;
60
64
  };
65
+ body?.addEventListener("scroll", () => {
66
+ isScrollActive = true;
67
+ clearTimeout(body._scrollTimer);
68
+ body._scrollTimer = setTimeout(() => {
69
+ isScrollActive = false;
70
+ }, 80);
71
+ });
72
+ const getDiff = (current) => isVertical ? current - startPos : current - startPos;
61
73
  const getTransform = (diff) => {
62
74
  switch (position) {
63
75
  case "bottom":
@@ -73,6 +85,9 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
73
85
  }
74
86
  };
75
87
  const startDrag = (pos) => {
88
+ // ✅ CHANGE 2: block drag if scrolling or not at boundary
89
+ if (isScrollActive || !isAtBoundary())
90
+ return;
76
91
  startPos = pos;
77
92
  isDragging = true;
78
93
  panel.style.transition = "none";
@@ -80,6 +95,8 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
80
95
  const moveDrag = (pos) => {
81
96
  if (!isDragging)
82
97
  return;
98
+ if (isScrollActive)
99
+ return;
83
100
  currentPos = pos;
84
101
  const diff = getDiff(currentPos);
85
102
  panel.style.transform = getTransform(diff);
@@ -90,57 +107,51 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
90
107
  isDragging = false;
91
108
  const diff = getDiff(currentPos);
92
109
  panel.style.transition = "transform 0.3s ease";
93
- const threshold = 100; // px to trigger close
110
+ const threshold = 100;
94
111
  let shouldClose = false;
95
112
  switch (position) {
96
113
  case "bottom":
97
114
  shouldClose = diff > threshold;
98
- panel.style.transform = shouldClose
99
- ? `translate(-50%, 100%)`
100
- : `translate(-50%, 0)`;
101
115
  break;
102
116
  case "top":
103
117
  shouldClose = diff < -threshold;
104
- panel.style.transform = shouldClose
105
- ? `translate(-50%, -100%)`
106
- : `translate(-50%, 0)`;
107
118
  break;
108
119
  case "left":
109
120
  shouldClose = diff < -threshold;
110
- panel.style.transform = shouldClose
111
- ? `translate(-100%, -50%)`
112
- : `translate(0, -50%)`;
113
121
  break;
114
122
  case "right":
115
123
  shouldClose = diff > threshold;
116
- panel.style.transform = shouldClose
117
- ? `translate(100%, -50%)`
118
- : `translate(0, -50%)`;
119
124
  break;
120
125
  }
121
126
  if (shouldClose) {
122
127
  switch (position) {
123
- case "right":
124
- panel.style.transform = `translate(100%, -50%)`;
125
- break;
126
- case "left":
127
- panel.style.transform = `translate(-100%, -50%)`;
128
+ case "bottom":
129
+ panel.style.transform = "translate(-50%, 100%)";
128
130
  break;
129
131
  case "top":
130
- panel.style.transform = `translate(-50%, -100%)`;
132
+ panel.style.transform = "translate(-50%, -100%)";
131
133
  break;
132
- case "bottom":
133
- panel.style.transform = `translate(-50%, 100%)`;
134
+ case "left":
135
+ panel.style.transform = "translate(-100%, -50%)";
136
+ break;
137
+ case "right":
138
+ panel.style.transform = "translate(100%, -50%)";
134
139
  break;
135
140
  }
136
- setTimeout(() => onClose(), 250); // keep in DOM while sliding
141
+ setTimeout(onClose, 250);
142
+ }
143
+ else {
144
+ panel.style.transform =
145
+ position === "bottom" || position === "top"
146
+ ? "translate(-50%, 0)"
147
+ : "translate(0, -50%)";
137
148
  }
138
149
  };
139
- // Touch events
150
+ // touch
140
151
  const onTouchStart = (e) => startDrag(isVertical ? e.touches[0].clientY : e.touches[0].clientX);
141
152
  const onTouchMove = (e) => moveDrag(isVertical ? e.touches[0].clientY : e.touches[0].clientX);
142
153
  const onTouchEnd = () => endDrag();
143
- // Mouse events
154
+ // mouse
144
155
  const onMouseDown = (e) => startDrag(isVertical ? e.clientY : e.clientX);
145
156
  const onMouseMove = (e) => moveDrag(isVertical ? e.clientY : e.clientX);
146
157
  const onMouseUp = () => endDrag();
@@ -165,6 +176,6 @@ export const SlidingPanel = ({ isOpen, onClose, children, height = "80%", width
165
176
  }, [onClose, position]);
166
177
  if (!visible && !isOpen)
167
178
  return null;
168
- return (_jsxs("div", { className: `spb-root ${animateOpen ? "spb-open" : "spb-close"}`, "aria-hidden": !isOpen, children: [_jsx("div", { className: "spb-backdrop", onClick: () => closeOnOverlayClick && onClose() }), _jsxs("div", { ref: panelRef, className: `spb-panel spb-panel--position-${position}`, style: { height, width }, role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [_jsx("div", { ref: handleRef, className: "spb-handle" }), _jsxs("div", { ref: headerRef, className: "spb-header", children: [title ? _jsx("h3", { className: "spb-title", children: title }) : _jsx("div", {}), _jsx("button", { className: "spb-close-btn", onClick: onClose, children: "\u00D7" })] }), _jsx("div", { className: "spb-body", children: children })] })] }));
179
+ return (_jsxs("div", { className: `spb-root ${animateOpen ? "spb-open" : "spb-close"}`, children: [_jsx("div", { className: "spb-backdrop", onClick: () => closeOnOverlayClick && onClose() }), _jsxs("div", { ref: panelRef, className: `spb-panel spb-panel--position-${position}`, style: { height, width }, role: "dialog", "aria-modal": "true", onClick: (e) => e.stopPropagation(), children: [_jsx("div", { ref: headerRef, className: "spb-header", children: _jsx("button", { className: "spb-close-btn", onClick: onClose, children: "\u00D7" }) }), _jsx("div", { className: "spb-body", children: children })] })] }));
169
180
  };
170
181
  export default SlidingPanel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ml-ui-lib",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",