hamzus-ui 0.0.206 → 0.0.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export * as Tooltip from "./src/components/hamzus-ui/AdvancedTooltip";
62
62
 
63
63
  // layout
64
64
  export { default as ScrollArea } from "./src/components/hamzus-ui/ScrollArea/ScrollArea.svelte"
65
+ export { default as Scrollbar } from "./src/components/hamzus-ui/Scrollbar/Scrollbar.svelte"
65
66
  export * as Sidebar from "./src/layout/Sidebar"
66
67
  export * as SidebarV2 from "./src/components/hamzus-ui/SidebarV2"
67
68
 
package/index.js CHANGED
@@ -59,6 +59,7 @@ export * as Tooltip from "./src/components/hamzus-ui/AdvancedTooltip"
59
59
 
60
60
  // layout
61
61
  export { default as ScrollArea } from "./src/components/hamzus-ui/ScrollArea/ScrollArea.svelte"
62
+ export { default as Scrollbar } from "./src/components/hamzus-ui/Scrollbar/Scrollbar.svelte"
62
63
  export * as Sidebar from "./src/layout/Sidebar"
63
64
  export * as SidebarV2 from "./src/components/hamzus-ui/SidebarV2"
64
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hamzus-ui",
3
- "version": "0.0.206",
3
+ "version": "0.0.207",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "svelte": "index.js",
@@ -0,0 +1,308 @@
1
+ <script>
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { browser } from '$app/environment';
4
+
5
+ export let element = null;
6
+ export let thickness = 15;
7
+ export let padding = 1;
8
+
9
+ let thumbHeight = 0;
10
+ let thumbTop = 0;
11
+ let hasScrollY = false;
12
+
13
+ let thumbWidth = 0;
14
+ let thumbLeft = 0;
15
+ let hasScrollX = false;
16
+
17
+ // ── Mesure ──────────────────────────────────────────────────
18
+
19
+ function measure(el) {
20
+ const { clientHeight, scrollHeight, scrollTop,
21
+ clientWidth, scrollWidth, scrollLeft } = el;
22
+
23
+ hasScrollY = scrollHeight > clientHeight;
24
+ thumbHeight = hasScrollY ? (clientHeight / scrollHeight) * clientHeight : clientHeight;
25
+ thumbTop = hasScrollY
26
+ ? (scrollTop / (scrollHeight - clientHeight)) * (clientHeight - thumbHeight)
27
+ : 0;
28
+
29
+ hasScrollX = scrollWidth > clientWidth;
30
+ thumbWidth = hasScrollX ? (clientWidth / scrollWidth) * clientWidth : clientWidth;
31
+ thumbLeft = hasScrollX
32
+ ? (scrollLeft / (scrollWidth - clientWidth)) * (clientWidth - thumbWidth)
33
+ : 0;
34
+ }
35
+
36
+ // ── Press & hold sur la track ───────────────────────────────
37
+
38
+ let trackPressInterval = null;
39
+
40
+ function stopTrackPress() {
41
+ if (trackPressInterval) {
42
+ clearInterval(trackPressInterval);
43
+ trackPressInterval = null;
44
+ }
45
+ }
46
+
47
+ function onTrackYMouseDown(e) {
48
+ if (!element || e.target !== e.currentTarget) return;
49
+ e.preventDefault();
50
+ const target = e.currentTarget;
51
+
52
+ function step() {
53
+ if (!element) return;
54
+ const rect = target.getBoundingClientRect();
55
+ const clickY = e.clientY - rect.top;
56
+ const direction = clickY < thumbTop ? -1 : 1;
57
+ element.scrollTop += direction * element.clientHeight * 0.8;
58
+ measure(element);
59
+ if (direction === -1 && thumbTop <= clickY) stopTrackPress();
60
+ if (direction === 1 && thumbTop + thumbHeight >= clickY) stopTrackPress();
61
+ }
62
+
63
+ step();
64
+ trackPressInterval = setInterval(step, 120);
65
+ window.addEventListener('mouseup', stopTrackPress, { once: true });
66
+ }
67
+
68
+ function onTrackXMouseDown(e) {
69
+ if (!element || e.target !== e.currentTarget) return;
70
+ e.preventDefault();
71
+ const target = e.currentTarget;
72
+
73
+ function step() {
74
+ if (!element) return;
75
+ const rect = target.getBoundingClientRect();
76
+ const clickX = e.clientX - rect.left;
77
+ const direction = clickX < thumbLeft ? -1 : 1;
78
+ element.scrollLeft += direction * element.clientWidth * 0.8;
79
+ measure(element);
80
+ if (direction === -1 && thumbLeft <= clickX) stopTrackPress();
81
+ if (direction === 1 && thumbLeft + thumbWidth >= clickX) stopTrackPress();
82
+ }
83
+
84
+ step();
85
+ trackPressInterval = setInterval(step, 120);
86
+ window.addEventListener('mouseup', stopTrackPress, { once: true });
87
+ }
88
+
89
+ // ── Drag vertical ───────────────────────────────────────────
90
+
91
+ let draggingY = false;
92
+ let dragStartY = 0;
93
+ let dragStartScrollTop = 0;
94
+
95
+ function onThumbYMouseDown(e) {
96
+ if (!element) return;
97
+ e.preventDefault();
98
+ draggingY = true;
99
+ dragStartY = e.clientY;
100
+ dragStartScrollTop = element.scrollTop;
101
+ }
102
+
103
+ function onMouseMoveY(e) {
104
+ if (!draggingY || !element) return;
105
+ const { clientHeight, scrollHeight } = element;
106
+ const thumbH = (clientHeight / scrollHeight) * clientHeight;
107
+ const ratio = (scrollHeight - clientHeight) / (clientHeight - thumbH);
108
+ element.scrollTop = dragStartScrollTop + (e.clientY - dragStartY) * ratio;
109
+ }
110
+
111
+ function onMouseUpY() { draggingY = false; }
112
+
113
+ // ── Drag horizontal ─────────────────────────────────────────
114
+
115
+ let draggingX = false;
116
+ let dragStartX = 0;
117
+ let dragStartScrollLeft = 0;
118
+
119
+ function onThumbXMouseDown(e) {
120
+ if (!element) return;
121
+ e.preventDefault();
122
+ draggingX = true;
123
+ dragStartX = e.clientX;
124
+ dragStartScrollLeft = element.scrollLeft;
125
+ }
126
+
127
+ function onMouseMoveX(e) {
128
+ if (!draggingX || !element) return;
129
+ const { clientWidth, scrollWidth } = element;
130
+ const thumbW = (clientWidth / scrollWidth) * clientWidth;
131
+ const ratio = (scrollWidth - clientWidth) / (clientWidth - thumbW);
132
+ element.scrollLeft = dragStartScrollLeft + (e.clientX - dragStartX) * ratio;
133
+ }
134
+
135
+ function onMouseUpX() { draggingX = false; }
136
+
137
+ // ── Listeners globaux (window) ──────────────────────────────
138
+
139
+ function onWindowMouseMove(e) {
140
+ onMouseMoveY(e);
141
+ onMouseMoveX(e);
142
+ }
143
+
144
+ function onWindowMouseUp() {
145
+ onMouseUpY();
146
+ onMouseUpX();
147
+ }
148
+
149
+ function addWindowListeners() {
150
+ window.addEventListener('mousemove', onWindowMouseMove);
151
+ window.addEventListener('mouseup', onWindowMouseUp);
152
+ }
153
+
154
+ function removeWindowListeners() {
155
+ window.removeEventListener('mousemove', onWindowMouseMove);
156
+ window.removeEventListener('mouseup', onWindowMouseUp);
157
+ }
158
+
159
+ // ── Observer setup ──────────────────────────────────────────
160
+
161
+ let resizeObserver = null;
162
+
163
+ function handleScroll() {
164
+ if (element) measure(element);
165
+ }
166
+
167
+ function setupObserver(el) {
168
+ cleanupObserver();
169
+ measure(el);
170
+
171
+ resizeObserver = new ResizeObserver(() => measure(el));
172
+ resizeObserver.observe(el);
173
+ for (const child of Array.from(el.children)) {
174
+ resizeObserver.observe(child);
175
+ }
176
+ el.addEventListener('scroll', handleScroll);
177
+ }
178
+
179
+ function cleanupObserver() {
180
+ if (element) element.removeEventListener('scroll', handleScroll);
181
+ resizeObserver?.disconnect();
182
+ resizeObserver = null;
183
+ }
184
+
185
+ // $: réactif à `element` — mais uniquement côté client
186
+ $: if (browser && element) {
187
+ setupObserver(element);
188
+ } else if (browser && !element) {
189
+ cleanupObserver();
190
+ stopTrackPress();
191
+ thumbHeight = 0; thumbTop = 0; hasScrollY = false;
192
+ thumbWidth = 0; thumbLeft = 0; hasScrollX = false;
193
+ }
194
+
195
+ // Les listeners window uniquement après le montage (côté client)
196
+ onMount(() => {
197
+ addWindowListeners();
198
+ return () => {
199
+ removeWindowListeners();
200
+ cleanupObserver();
201
+ stopTrackPress();
202
+ };
203
+ });
204
+
205
+ onDestroy(() => {
206
+ // onDestroy peut être appelé côté serveur — ne rien faire si pas browser
207
+ if (!browser) return;
208
+ removeWindowListeners();
209
+ cleanupObserver();
210
+ stopTrackPress();
211
+ });
212
+ </script>
213
+
214
+ <div
215
+ class="scroll-provider"
216
+ style="--track-padding:{padding}px;--track-thickness:{thickness}px;--thumb-width:{thumbWidth}px;--thumb-height:{thumbHeight}px;--thumb-left:{thumbLeft}px;--thumb-top:{thumbTop}px;"
217
+ >
218
+ {#if hasScrollX}
219
+ <div class="track-h" on:mousedown={onTrackXMouseDown}>
220
+ <span
221
+ class="thumb"
222
+ class:dragging={draggingX}
223
+ on:mousedown={onThumbXMouseDown}
224
+ />
225
+ </div>
226
+ {/if}
227
+
228
+ {#if hasScrollY}
229
+ <div class="track-v" on:mousedown={onTrackYMouseDown}>
230
+ <span
231
+ class="thumb"
232
+ class:dragging={draggingY}
233
+ on:mousedown={onThumbYMouseDown}
234
+ />
235
+ </div>
236
+ {/if}
237
+ </div>
238
+
239
+ <style>
240
+ .scroll-provider {
241
+ position: absolute;
242
+ top: 0px;
243
+ left: 0px;
244
+ width: 100%;
245
+ height: 100%;
246
+ pointer-events: none;
247
+ }
248
+
249
+ .track-h,
250
+ .track-v {
251
+ pointer-events: all;
252
+ }
253
+
254
+ .thumb {
255
+ position: absolute;
256
+ display: flex;
257
+ background-color: var(--bg-blur);
258
+ cursor: grab;
259
+ transition: background-color 0.15s;
260
+ }
261
+
262
+ .thumb:hover,
263
+ .thumb.dragging {
264
+ background-color: var(--accent-b);
265
+ cursor: grabbing;
266
+ }
267
+
268
+ .track-h {
269
+ position: absolute;
270
+ bottom: 0px;
271
+ left: 0px;
272
+ width: 100%;
273
+ height: var(--track-thickness);
274
+ }
275
+
276
+ .track-h .thumb {
277
+ width: calc(var(--thumb-width) - var(--track-padding) * 2);
278
+ height: calc(100% - var(--track-padding) * 2);
279
+ left: calc(var(--thumb-left) + var(--track-padding));
280
+ top: calc(0px + var(--track-padding));
281
+ border-radius: var(--radius-s);
282
+ }
283
+
284
+ .track-v {
285
+ position: absolute;
286
+ top: 0px;
287
+ right: 0px;
288
+ height: 100%;
289
+ width: var(--track-thickness);
290
+ }
291
+
292
+ .track-v .thumb {
293
+ height: calc(var(--thumb-height) - var(--track-padding) * 2);
294
+ width: calc(100% - var(--track-padding) * 2);
295
+ top: calc(var(--thumb-top) + 1px);
296
+ left: calc(0px + 1px);
297
+ border-radius: var(--radius-s);
298
+ }
299
+
300
+ @media (max-width:750px) {
301
+ .track-h {
302
+ height: 5px;
303
+ }
304
+ .track-v {
305
+ width: 5px;
306
+ }
307
+ }
308
+ </style>