@huin-core/react-scroll-area 1.0.2 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js ADDED
@@ -0,0 +1,867 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // packages/react/scroll-area/src/index.ts
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
+ Root: () => Root,
35
+ ScrollArea: () => ScrollArea,
36
+ ScrollAreaCorner: () => ScrollAreaCorner,
37
+ ScrollAreaScrollbar: () => ScrollAreaScrollbar,
38
+ ScrollAreaThumb: () => ScrollAreaThumb,
39
+ ScrollAreaViewport: () => ScrollAreaViewport,
40
+ createScrollAreaScope: () => createScrollAreaScope
41
+ });
42
+ module.exports = __toCommonJS(src_exports);
43
+
44
+ // packages/react/scroll-area/src/ScrollArea.tsx
45
+ var React4 = __toESM(require("react"));
46
+ var import_react_primitive3 = require("@huin-core/react-primitive");
47
+ var import_react_context = require("@huin-core/react-context");
48
+ var import_react_compose_refs2 = require("@huin-core/react-compose-refs");
49
+ var import_react_direction = require("@huin-core/react-direction");
50
+
51
+ // packages/react/scroll-area/src/ScrollAreaScrollbar.tsx
52
+ var import_react2 = __toESM(require("react"));
53
+ var import_react_use_callback_ref2 = require("@huin-core/react-use-callback-ref");
54
+ var import_react_primitive2 = require("@huin-core/react-primitive");
55
+ var import_react_presence = require("@huin-core/react-presence");
56
+
57
+ // packages/react/scroll-area/src/useStateMachine.ts
58
+ var React = __toESM(require("react"));
59
+ function useStateMachine(initialState, machine) {
60
+ return React.useReducer((state, event) => {
61
+ const nextState = machine[state][event];
62
+ return nextState ?? state;
63
+ }, initialState);
64
+ }
65
+
66
+ // packages/react/scroll-area/src/ScrollAreaScrollbar.tsx
67
+ var import_primitive = require("@huin-core/primitive");
68
+
69
+ // packages/react/scroll-area/src/ScrollAreaCorner.tsx
70
+ var import_react = __toESM(require("react"));
71
+ var import_react_primitive = require("@huin-core/react-primitive");
72
+ var import_react_use_callback_ref = require("@huin-core/react-use-callback-ref");
73
+ var import_react_use_layout_effect = require("@huin-core/react-use-layout-effect");
74
+ var import_jsx_runtime = require("react/jsx-runtime");
75
+ var CORNER_NAME = "ScrollAreaCorner";
76
+ var ScrollAreaCorner = import_react.default.forwardRef((props, forwardedRef) => {
77
+ const context = useScrollAreaContext(CORNER_NAME, props.__scopeScrollArea);
78
+ const hasBothScrollbarsVisible = Boolean(
79
+ context.scrollbarX && context.scrollbarY
80
+ );
81
+ const hasCorner = context.type !== "scroll" && hasBothScrollbarsVisible;
82
+ return hasCorner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollAreaCornerImpl, { ...props, ref: forwardedRef }) : null;
83
+ });
84
+ ScrollAreaCorner.displayName = CORNER_NAME;
85
+ var ScrollAreaCornerImpl = import_react.default.forwardRef((props, forwardedRef) => {
86
+ const { __scopeScrollArea, ...cornerProps } = props;
87
+ const context = useScrollAreaContext(CORNER_NAME, __scopeScrollArea);
88
+ const [width, setWidth] = import_react.default.useState(0);
89
+ const [height, setHeight] = import_react.default.useState(0);
90
+ const hasSize = Boolean(width && height);
91
+ useResizeObserver(context.scrollbarX, () => {
92
+ const height2 = context.scrollbarX?.offsetHeight || 0;
93
+ context.onCornerHeightChange(height2);
94
+ setHeight(height2);
95
+ });
96
+ useResizeObserver(context.scrollbarY, () => {
97
+ const width2 = context.scrollbarY?.offsetWidth || 0;
98
+ context.onCornerWidthChange(width2);
99
+ setWidth(width2);
100
+ });
101
+ return hasSize ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
102
+ import_react_primitive.Primitive.div,
103
+ {
104
+ ...cornerProps,
105
+ ref: forwardedRef,
106
+ style: {
107
+ width,
108
+ height,
109
+ position: "absolute",
110
+ right: context.dir === "ltr" ? 0 : void 0,
111
+ left: context.dir === "rtl" ? 0 : void 0,
112
+ bottom: 0,
113
+ ...props.style
114
+ }
115
+ }
116
+ ) : null;
117
+ });
118
+ function useResizeObserver(element, onResize) {
119
+ const handleResize = (0, import_react_use_callback_ref.useCallbackRef)(onResize);
120
+ (0, import_react_use_layout_effect.useLayoutEffect)(() => {
121
+ let rAF = 0;
122
+ if (element) {
123
+ const resizeObserver = new ResizeObserver(() => {
124
+ cancelAnimationFrame(rAF);
125
+ rAF = window.requestAnimationFrame(handleResize);
126
+ });
127
+ resizeObserver.observe(element);
128
+ return () => {
129
+ window.cancelAnimationFrame(rAF);
130
+ resizeObserver.unobserve(element);
131
+ };
132
+ }
133
+ }, [element, handleResize]);
134
+ }
135
+
136
+ // packages/react/scroll-area/src/ScrollAreaScrollbar.tsx
137
+ var import_react_compose_refs = require("@huin-core/react-compose-refs");
138
+ var import_number = require("@huin-core/number");
139
+ var import_jsx_runtime2 = require("react/jsx-runtime");
140
+ var SCROLLBAR_NAME = "ScrollAreaScrollbar";
141
+ var ScrollAreaScrollbar = import_react2.default.forwardRef(
142
+ (props, forwardedRef) => {
143
+ const { forceMount, ...scrollbarProps } = props;
144
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
145
+ const { onScrollbarXEnabledChange, onScrollbarYEnabledChange } = context;
146
+ const isHorizontal = props.orientation === "horizontal";
147
+ import_react2.default.useEffect(() => {
148
+ isHorizontal ? onScrollbarXEnabledChange(true) : onScrollbarYEnabledChange(true);
149
+ return () => {
150
+ isHorizontal ? onScrollbarXEnabledChange(false) : onScrollbarYEnabledChange(false);
151
+ };
152
+ }, [isHorizontal, onScrollbarXEnabledChange, onScrollbarYEnabledChange]);
153
+ return context.type === "hover" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollAreaScrollbarHover, { ...scrollbarProps, ref: forwardedRef, forceMount }) : context.type === "scroll" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollAreaScrollbarScroll, { ...scrollbarProps, ref: forwardedRef, forceMount }) : context.type === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollAreaScrollbarAuto, { ...scrollbarProps, ref: forwardedRef, forceMount }) : context.type === "always" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ScrollAreaScrollbarVisible, { ...scrollbarProps, ref: forwardedRef }) : null;
154
+ }
155
+ );
156
+ ScrollAreaScrollbar.displayName = SCROLLBAR_NAME;
157
+ var ScrollAreaScrollbarHover = import_react2.default.forwardRef((props, forwardedRef) => {
158
+ const { forceMount, ...scrollbarProps } = props;
159
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
160
+ const [visible, setVisible] = import_react2.default.useState(false);
161
+ import_react2.default.useEffect(() => {
162
+ const scrollArea = context.scrollArea;
163
+ let hideTimer = 0;
164
+ if (scrollArea) {
165
+ const handlePointerEnter = () => {
166
+ window.clearTimeout(hideTimer);
167
+ setVisible(true);
168
+ };
169
+ const handlePointerLeave = () => {
170
+ hideTimer = window.setTimeout(
171
+ () => setVisible(false),
172
+ context.scrollHideDelay
173
+ );
174
+ };
175
+ scrollArea.addEventListener("pointerenter", handlePointerEnter);
176
+ scrollArea.addEventListener("pointerleave", handlePointerLeave);
177
+ return () => {
178
+ window.clearTimeout(hideTimer);
179
+ scrollArea.removeEventListener("pointerenter", handlePointerEnter);
180
+ scrollArea.removeEventListener("pointerleave", handlePointerLeave);
181
+ };
182
+ }
183
+ }, [context.scrollArea, context.scrollHideDelay]);
184
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_presence.Presence, { present: forceMount || visible, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
185
+ ScrollAreaScrollbarAuto,
186
+ {
187
+ "data-state": visible ? "visible" : "hidden",
188
+ ...scrollbarProps,
189
+ ref: forwardedRef
190
+ }
191
+ ) });
192
+ });
193
+ var ScrollAreaScrollbarScroll = import_react2.default.forwardRef((props, forwardedRef) => {
194
+ const { forceMount, ...scrollbarProps } = props;
195
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
196
+ const isHorizontal = props.orientation === "horizontal";
197
+ const debounceScrollEnd = useDebounceCallback(() => send("SCROLL_END"), 100);
198
+ const [state, send] = useStateMachine("hidden", {
199
+ hidden: {
200
+ SCROLL: "scrolling"
201
+ },
202
+ scrolling: {
203
+ SCROLL_END: "idle",
204
+ POINTER_ENTER: "interacting"
205
+ },
206
+ interacting: {
207
+ SCROLL: "interacting",
208
+ POINTER_LEAVE: "idle"
209
+ },
210
+ idle: {
211
+ HIDE: "hidden",
212
+ SCROLL: "scrolling",
213
+ POINTER_ENTER: "interacting"
214
+ }
215
+ });
216
+ import_react2.default.useEffect(() => {
217
+ if (state === "idle") {
218
+ const hideTimer = window.setTimeout(
219
+ () => send("HIDE"),
220
+ context.scrollHideDelay
221
+ );
222
+ return () => window.clearTimeout(hideTimer);
223
+ }
224
+ }, [state, context.scrollHideDelay, send]);
225
+ import_react2.default.useEffect(() => {
226
+ const viewport = context.viewport;
227
+ const scrollDirection = isHorizontal ? "scrollLeft" : "scrollTop";
228
+ if (viewport) {
229
+ let prevScrollPos = viewport[scrollDirection];
230
+ const handleScroll = () => {
231
+ const scrollPos = viewport[scrollDirection];
232
+ const hasScrollInDirectionChanged = prevScrollPos !== scrollPos;
233
+ if (hasScrollInDirectionChanged) {
234
+ send("SCROLL");
235
+ debounceScrollEnd();
236
+ }
237
+ prevScrollPos = scrollPos;
238
+ };
239
+ viewport.addEventListener("scroll", handleScroll);
240
+ return () => viewport.removeEventListener("scroll", handleScroll);
241
+ }
242
+ }, [context.viewport, isHorizontal, send, debounceScrollEnd]);
243
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_presence.Presence, { present: forceMount || state !== "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
244
+ ScrollAreaScrollbarVisible,
245
+ {
246
+ "data-state": state === "hidden" ? "hidden" : "visible",
247
+ ...scrollbarProps,
248
+ ref: forwardedRef,
249
+ onPointerEnter: (0, import_primitive.composeEventHandlers)(
250
+ props.onPointerEnter,
251
+ () => send("POINTER_ENTER")
252
+ ),
253
+ onPointerLeave: (0, import_primitive.composeEventHandlers)(
254
+ props.onPointerLeave,
255
+ () => send("POINTER_LEAVE")
256
+ )
257
+ }
258
+ ) });
259
+ });
260
+ var ScrollAreaScrollbarAuto = import_react2.default.forwardRef((props, forwardedRef) => {
261
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
262
+ const { forceMount, ...scrollbarProps } = props;
263
+ const [visible, setVisible] = import_react2.default.useState(false);
264
+ const isHorizontal = props.orientation === "horizontal";
265
+ const handleResize = useDebounceCallback(() => {
266
+ if (context.viewport) {
267
+ const isOverflowX = context.viewport.offsetWidth < context.viewport.scrollWidth;
268
+ const isOverflowY = context.viewport.offsetHeight < context.viewport.scrollHeight;
269
+ setVisible(isHorizontal ? isOverflowX : isOverflowY);
270
+ }
271
+ }, 10);
272
+ useResizeObserver(context.viewport, handleResize);
273
+ useResizeObserver(context.content, handleResize);
274
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_presence.Presence, { present: forceMount || visible, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
275
+ ScrollAreaScrollbarVisible,
276
+ {
277
+ "data-state": visible ? "visible" : "hidden",
278
+ ...scrollbarProps,
279
+ ref: forwardedRef
280
+ }
281
+ ) });
282
+ });
283
+ var ScrollAreaScrollbarVisible = import_react2.default.forwardRef((props, forwardedRef) => {
284
+ const { orientation = "vertical", ...scrollbarProps } = props;
285
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
286
+ const thumbRef = import_react2.default.useRef(null);
287
+ const pointerOffsetRef = import_react2.default.useRef(0);
288
+ const [sizes, setSizes] = import_react2.default.useState({
289
+ content: 0,
290
+ viewport: 0,
291
+ scrollbar: { size: 0, paddingStart: 0, paddingEnd: 0 }
292
+ });
293
+ const thumbRatio = getThumbRatio(sizes.viewport, sizes.content);
294
+ const commonProps = {
295
+ ...scrollbarProps,
296
+ sizes,
297
+ onSizesChange: setSizes,
298
+ hasThumb: Boolean(thumbRatio > 0 && thumbRatio < 1),
299
+ onThumbChange: (thumb) => thumbRef.current = thumb,
300
+ onThumbPointerUp: () => pointerOffsetRef.current = 0,
301
+ onThumbPointerDown: (pointerPos) => pointerOffsetRef.current = pointerPos
302
+ };
303
+ function getScrollPosition(pointerPos, dir) {
304
+ return getScrollPositionFromPointer(
305
+ pointerPos,
306
+ pointerOffsetRef.current,
307
+ sizes,
308
+ dir
309
+ );
310
+ }
311
+ if (orientation === "horizontal") {
312
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
313
+ ScrollAreaScrollbarX,
314
+ {
315
+ ...commonProps,
316
+ ref: forwardedRef,
317
+ onThumbPositionChange: () => {
318
+ if (context.viewport && thumbRef.current) {
319
+ const scrollPos = context.viewport.scrollLeft;
320
+ const offset = getThumbOffsetFromScroll(
321
+ scrollPos,
322
+ sizes,
323
+ context.dir
324
+ );
325
+ thumbRef.current.style.transform = `translate3d(${offset}px, 0, 0)`;
326
+ }
327
+ },
328
+ onWheelScroll: (scrollPos) => {
329
+ if (context.viewport) context.viewport.scrollLeft = scrollPos;
330
+ },
331
+ onDragScroll: (pointerPos) => {
332
+ if (context.viewport) {
333
+ context.viewport.scrollLeft = getScrollPosition(
334
+ pointerPos,
335
+ context.dir
336
+ );
337
+ }
338
+ }
339
+ }
340
+ );
341
+ }
342
+ if (orientation === "vertical") {
343
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
344
+ ScrollAreaScrollbarY,
345
+ {
346
+ ...commonProps,
347
+ ref: forwardedRef,
348
+ onThumbPositionChange: () => {
349
+ if (context.viewport && thumbRef.current) {
350
+ const scrollPos = context.viewport.scrollTop;
351
+ const offset = getThumbOffsetFromScroll(scrollPos, sizes);
352
+ thumbRef.current.style.transform = `translate3d(0, ${offset}px, 0)`;
353
+ }
354
+ },
355
+ onWheelScroll: (scrollPos) => {
356
+ if (context.viewport) context.viewport.scrollTop = scrollPos;
357
+ },
358
+ onDragScroll: (pointerPos) => {
359
+ if (context.viewport)
360
+ context.viewport.scrollTop = getScrollPosition(pointerPos);
361
+ }
362
+ }
363
+ );
364
+ }
365
+ return null;
366
+ });
367
+ var ScrollAreaScrollbarX = import_react2.default.forwardRef((props, forwardedRef) => {
368
+ const { sizes, onSizesChange, ...scrollbarProps } = props;
369
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
370
+ const [computedStyle, setComputedStyle] = import_react2.default.useState();
371
+ const ref = import_react2.default.useRef(null);
372
+ const composeRefs = (0, import_react_compose_refs.useComposedRefs)(
373
+ forwardedRef,
374
+ ref,
375
+ context.onScrollbarXChange
376
+ );
377
+ import_react2.default.useEffect(() => {
378
+ if (ref.current) setComputedStyle(getComputedStyle(ref.current));
379
+ }, [ref]);
380
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
381
+ ScrollAreaScrollbarImpl,
382
+ {
383
+ "data-orientation": "horizontal",
384
+ ...scrollbarProps,
385
+ ref: composeRefs,
386
+ sizes,
387
+ style: {
388
+ bottom: 0,
389
+ left: context.dir === "rtl" ? "var(--huin-core-scroll-area-corner-width)" : 0,
390
+ right: context.dir === "ltr" ? "var(--huin-core-scroll-area-corner-width)" : 0,
391
+ ["--huin-core-scroll-area-thumb-width"]: getThumbSize(sizes) + "px",
392
+ ...props.style
393
+ },
394
+ onThumbPointerDown: (pointerPos) => props.onThumbPointerDown(pointerPos.x),
395
+ onDragScroll: (pointerPos) => props.onDragScroll(pointerPos.x),
396
+ onWheelScroll: (event, maxScrollPos) => {
397
+ if (context.viewport) {
398
+ const scrollPos = context.viewport.scrollLeft + event.deltaX;
399
+ props.onWheelScroll(scrollPos);
400
+ if (isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos)) {
401
+ event.preventDefault();
402
+ }
403
+ }
404
+ },
405
+ onResize: () => {
406
+ if (ref.current && context.viewport && computedStyle) {
407
+ onSizesChange({
408
+ content: context.viewport.scrollWidth,
409
+ viewport: context.viewport.offsetWidth,
410
+ scrollbar: {
411
+ size: ref.current.clientWidth,
412
+ paddingStart: toInt(computedStyle.paddingLeft),
413
+ paddingEnd: toInt(computedStyle.paddingRight)
414
+ }
415
+ });
416
+ }
417
+ }
418
+ }
419
+ );
420
+ });
421
+ var ScrollAreaScrollbarY = import_react2.default.forwardRef((props, forwardedRef) => {
422
+ const { sizes, onSizesChange, ...scrollbarProps } = props;
423
+ const context = useScrollAreaContext(SCROLLBAR_NAME, props.__scopeScrollArea);
424
+ const [computedStyle, setComputedStyle] = import_react2.default.useState();
425
+ const ref = import_react2.default.useRef(null);
426
+ const composeRefs = (0, import_react_compose_refs.useComposedRefs)(
427
+ forwardedRef,
428
+ ref,
429
+ context.onScrollbarYChange
430
+ );
431
+ import_react2.default.useEffect(() => {
432
+ if (ref.current) setComputedStyle(getComputedStyle(ref.current));
433
+ }, [ref]);
434
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
435
+ ScrollAreaScrollbarImpl,
436
+ {
437
+ "data-orientation": "vertical",
438
+ ...scrollbarProps,
439
+ ref: composeRefs,
440
+ sizes,
441
+ style: {
442
+ top: 0,
443
+ right: context.dir === "ltr" ? 0 : void 0,
444
+ left: context.dir === "rtl" ? 0 : void 0,
445
+ bottom: "var(--huin-core-scroll-area-corner-height)",
446
+ ["--huin-core-scroll-area-thumb-height"]: getThumbSize(sizes) + "px",
447
+ ...props.style
448
+ },
449
+ onThumbPointerDown: (pointerPos) => props.onThumbPointerDown(pointerPos.y),
450
+ onDragScroll: (pointerPos) => props.onDragScroll(pointerPos.y),
451
+ onWheelScroll: (event, maxScrollPos) => {
452
+ if (context.viewport) {
453
+ const scrollPos = context.viewport.scrollTop + event.deltaY;
454
+ props.onWheelScroll(scrollPos);
455
+ if (isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos)) {
456
+ event.preventDefault();
457
+ }
458
+ }
459
+ },
460
+ onResize: () => {
461
+ if (ref.current && context.viewport && computedStyle) {
462
+ onSizesChange({
463
+ content: context.viewport.scrollHeight,
464
+ viewport: context.viewport.offsetHeight,
465
+ scrollbar: {
466
+ size: ref.current.clientHeight,
467
+ paddingStart: toInt(computedStyle.paddingTop),
468
+ paddingEnd: toInt(computedStyle.paddingBottom)
469
+ }
470
+ });
471
+ }
472
+ }
473
+ }
474
+ );
475
+ });
476
+ var ScrollAreaScrollbarImpl = import_react2.default.forwardRef((props, forwardedRef) => {
477
+ const {
478
+ __scopeScrollArea,
479
+ sizes,
480
+ hasThumb,
481
+ onThumbChange,
482
+ onThumbPointerUp,
483
+ onThumbPointerDown,
484
+ onThumbPositionChange,
485
+ onDragScroll,
486
+ onWheelScroll,
487
+ onResize,
488
+ ...scrollbarProps
489
+ } = props;
490
+ const context = useScrollAreaContext(SCROLLBAR_NAME, __scopeScrollArea);
491
+ const [scrollbar, setScrollbar] = import_react2.default.useState(null);
492
+ const composeRefs = (0, import_react_compose_refs.useComposedRefs)(
493
+ forwardedRef,
494
+ (node) => setScrollbar(node)
495
+ );
496
+ const rectRef = import_react2.default.useRef(null);
497
+ const prevWebkitUserSelectRef = import_react2.default.useRef("");
498
+ const viewport = context.viewport;
499
+ const maxScrollPos = sizes.content - sizes.viewport;
500
+ const handleWheelScroll = (0, import_react_use_callback_ref2.useCallbackRef)(onWheelScroll);
501
+ const handleThumbPositionChange = (0, import_react_use_callback_ref2.useCallbackRef)(onThumbPositionChange);
502
+ const handleResize = useDebounceCallback(onResize, 10);
503
+ function handleDragScroll(event) {
504
+ if (rectRef.current) {
505
+ const x = event.clientX - rectRef.current.left;
506
+ const y = event.clientY - rectRef.current.top;
507
+ onDragScroll({ x, y });
508
+ }
509
+ }
510
+ import_react2.default.useEffect(() => {
511
+ const handleWheel = (event) => {
512
+ const element = event.target;
513
+ const isScrollbarWheel = scrollbar?.contains(element);
514
+ if (isScrollbarWheel) handleWheelScroll(event, maxScrollPos);
515
+ };
516
+ document.addEventListener("wheel", handleWheel, { passive: false });
517
+ return () => document.removeEventListener("wheel", handleWheel, {
518
+ passive: false
519
+ });
520
+ }, [viewport, scrollbar, maxScrollPos, handleWheelScroll]);
521
+ import_react2.default.useEffect(handleThumbPositionChange, [
522
+ sizes,
523
+ handleThumbPositionChange
524
+ ]);
525
+ useResizeObserver(scrollbar, handleResize);
526
+ useResizeObserver(context.content, handleResize);
527
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
528
+ ScrollbarProvider,
529
+ {
530
+ scope: __scopeScrollArea,
531
+ scrollbar,
532
+ hasThumb,
533
+ onThumbChange: (0, import_react_use_callback_ref2.useCallbackRef)(onThumbChange),
534
+ onThumbPointerUp: (0, import_react_use_callback_ref2.useCallbackRef)(onThumbPointerUp),
535
+ onThumbPositionChange: handleThumbPositionChange,
536
+ onThumbPointerDown: (0, import_react_use_callback_ref2.useCallbackRef)(onThumbPointerDown),
537
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
538
+ import_react_primitive2.Primitive.div,
539
+ {
540
+ ...scrollbarProps,
541
+ ref: composeRefs,
542
+ style: { position: "absolute", ...scrollbarProps.style },
543
+ onPointerDown: (0, import_primitive.composeEventHandlers)(props.onPointerDown, (event) => {
544
+ const mainPointer = 0;
545
+ if (event.button === mainPointer) {
546
+ const element = event.target;
547
+ element.setPointerCapture(event.pointerId);
548
+ rectRef.current = scrollbar.getBoundingClientRect();
549
+ prevWebkitUserSelectRef.current = document.body.style.webkitUserSelect;
550
+ document.body.style.webkitUserSelect = "none";
551
+ if (context.viewport)
552
+ context.viewport.style.scrollBehavior = "auto";
553
+ handleDragScroll(event);
554
+ }
555
+ }),
556
+ onPointerMove: (0, import_primitive.composeEventHandlers)(
557
+ props.onPointerMove,
558
+ handleDragScroll
559
+ ),
560
+ onPointerUp: (0, import_primitive.composeEventHandlers)(props.onPointerUp, (event) => {
561
+ const element = event.target;
562
+ if (element.hasPointerCapture(event.pointerId)) {
563
+ element.releasePointerCapture(event.pointerId);
564
+ }
565
+ document.body.style.webkitUserSelect = prevWebkitUserSelectRef.current;
566
+ if (context.viewport) context.viewport.style.scrollBehavior = "";
567
+ rectRef.current = null;
568
+ })
569
+ }
570
+ )
571
+ }
572
+ );
573
+ });
574
+ function useDebounceCallback(callback, delay) {
575
+ const handleCallback = (0, import_react_use_callback_ref2.useCallbackRef)(callback);
576
+ const debounceTimerRef = import_react2.default.useRef(0);
577
+ import_react2.default.useEffect(
578
+ () => () => window.clearTimeout(debounceTimerRef.current),
579
+ []
580
+ );
581
+ return import_react2.default.useCallback(() => {
582
+ window.clearTimeout(debounceTimerRef.current);
583
+ debounceTimerRef.current = window.setTimeout(handleCallback, delay);
584
+ }, [handleCallback, delay]);
585
+ }
586
+ function toInt(value) {
587
+ return value ? parseInt(value, 10) : 0;
588
+ }
589
+ function getThumbRatio(viewportSize, contentSize) {
590
+ const ratio = viewportSize / contentSize;
591
+ return isNaN(ratio) ? 0 : ratio;
592
+ }
593
+ function getThumbSize(sizes) {
594
+ const ratio = getThumbRatio(sizes.viewport, sizes.content);
595
+ const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;
596
+ const thumbSize = (sizes.scrollbar.size - scrollbarPadding) * ratio;
597
+ return Math.max(thumbSize, 18);
598
+ }
599
+ function getScrollPositionFromPointer(pointerPos, pointerOffset, sizes, dir = "ltr") {
600
+ const thumbSizePx = getThumbSize(sizes);
601
+ const thumbCenter = thumbSizePx / 2;
602
+ const offset = pointerOffset || thumbCenter;
603
+ const thumbOffsetFromEnd = thumbSizePx - offset;
604
+ const minPointerPos = sizes.scrollbar.paddingStart + offset;
605
+ const maxPointerPos = sizes.scrollbar.size - sizes.scrollbar.paddingEnd - thumbOffsetFromEnd;
606
+ const maxScrollPos = sizes.content - sizes.viewport;
607
+ const scrollRange = dir === "ltr" ? [0, maxScrollPos] : [maxScrollPos * -1, 0];
608
+ const interpolate = linearScale(
609
+ [minPointerPos, maxPointerPos],
610
+ scrollRange
611
+ );
612
+ return interpolate(pointerPos);
613
+ }
614
+ function getThumbOffsetFromScroll(scrollPos, sizes, dir = "ltr") {
615
+ const thumbSizePx = getThumbSize(sizes);
616
+ const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;
617
+ const scrollbar = sizes.scrollbar.size - scrollbarPadding;
618
+ const maxScrollPos = sizes.content - sizes.viewport;
619
+ const maxThumbPos = scrollbar - thumbSizePx;
620
+ const scrollClampRange = dir === "ltr" ? [0, maxScrollPos] : [maxScrollPos * -1, 0];
621
+ const scrollWithoutMomentum = (0, import_number.clamp)(
622
+ scrollPos,
623
+ scrollClampRange
624
+ );
625
+ const interpolate = linearScale([0, maxScrollPos], [0, maxThumbPos]);
626
+ return interpolate(scrollWithoutMomentum);
627
+ }
628
+ function linearScale(input, output) {
629
+ return (value) => {
630
+ if (input[0] === input[1] || output[0] === output[1]) return output[0];
631
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
632
+ return output[0] + ratio * (value - input[0]);
633
+ };
634
+ }
635
+ function isScrollingWithinScrollbarBounds(scrollPos, maxScrollPos) {
636
+ return scrollPos > 0 && scrollPos < maxScrollPos;
637
+ }
638
+
639
+ // packages/react/scroll-area/src/ScrollArea.tsx
640
+ var import_jsx_runtime3 = require("react/jsx-runtime");
641
+ var SCROLL_AREA_NAME = "ScrollArea";
642
+ var [createScrollAreaContext, createScrollAreaScope] = (0, import_react_context.createContextScope)(SCROLL_AREA_NAME);
643
+ var [ScrollbarProvider, useScrollbarContext] = createScrollAreaContext(SCROLLBAR_NAME);
644
+ var [ScrollAreaProvider, useScrollAreaContext] = createScrollAreaContext(SCROLL_AREA_NAME);
645
+ var ScrollArea = React4.forwardRef(
646
+ (props, forwardedRef) => {
647
+ const {
648
+ __scopeScrollArea,
649
+ type = "hover",
650
+ dir,
651
+ scrollHideDelay = 600,
652
+ ...scrollAreaProps
653
+ } = props;
654
+ const [scrollArea, setScrollArea] = React4.useState(null);
655
+ const [viewport, setViewport] = React4.useState(null);
656
+ const [content, setContent] = React4.useState(null);
657
+ const [scrollbarX, setScrollbarX] = React4.useState(null);
658
+ const [scrollbarY, setScrollbarY] = React4.useState(null);
659
+ const [cornerWidth, setCornerWidth] = React4.useState(0);
660
+ const [cornerHeight, setCornerHeight] = React4.useState(0);
661
+ const [scrollbarXEnabled, setScrollbarXEnabled] = React4.useState(false);
662
+ const [scrollbarYEnabled, setScrollbarYEnabled] = React4.useState(false);
663
+ const composedRefs = (0, import_react_compose_refs2.useComposedRefs)(
664
+ forwardedRef,
665
+ (node) => setScrollArea(node)
666
+ );
667
+ const direction = (0, import_react_direction.useDirection)(dir);
668
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
669
+ ScrollAreaProvider,
670
+ {
671
+ scope: __scopeScrollArea,
672
+ type,
673
+ dir: direction,
674
+ scrollHideDelay,
675
+ scrollArea,
676
+ viewport,
677
+ onViewportChange: setViewport,
678
+ content,
679
+ onContentChange: setContent,
680
+ scrollbarX,
681
+ onScrollbarXChange: setScrollbarX,
682
+ scrollbarXEnabled,
683
+ onScrollbarXEnabledChange: setScrollbarXEnabled,
684
+ scrollbarY,
685
+ onScrollbarYChange: setScrollbarY,
686
+ scrollbarYEnabled,
687
+ onScrollbarYEnabledChange: setScrollbarYEnabled,
688
+ onCornerWidthChange: setCornerWidth,
689
+ onCornerHeightChange: setCornerHeight,
690
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
691
+ import_react_primitive3.Primitive.div,
692
+ {
693
+ dir: direction,
694
+ ...scrollAreaProps,
695
+ ref: composedRefs,
696
+ style: {
697
+ position: "relative",
698
+ // Pass corner sizes as CSS vars to reduce re-renders of context consumers
699
+ ["--huin-core-scroll-area-corner-width"]: cornerWidth + "px",
700
+ ["--huin-core-scroll-area-corner-height"]: cornerHeight + "px",
701
+ ...props.style
702
+ }
703
+ }
704
+ )
705
+ }
706
+ );
707
+ }
708
+ );
709
+ ScrollArea.displayName = SCROLL_AREA_NAME;
710
+ var Root = ScrollArea;
711
+
712
+ // packages/react/scroll-area/src/ScrollAreaViewport.tsx
713
+ var import_react3 = __toESM(require("react"));
714
+ var import_react_primitive4 = require("@huin-core/react-primitive");
715
+ var import_react_compose_refs3 = require("@huin-core/react-compose-refs");
716
+ var import_jsx_runtime4 = require("react/jsx-runtime");
717
+ var VIEWPORT_NAME = "ScrollAreaViewport";
718
+ var ScrollAreaViewport = import_react3.default.forwardRef((props, forwardedRef) => {
719
+ const { __scopeScrollArea, children, nonce, ...viewportProps } = props;
720
+ const context = useScrollAreaContext(VIEWPORT_NAME, __scopeScrollArea);
721
+ const ref = import_react3.default.useRef(null);
722
+ const composedRefs = (0, import_react_compose_refs3.useComposedRefs)(
723
+ forwardedRef,
724
+ ref,
725
+ context.onViewportChange
726
+ );
727
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
728
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
729
+ "style",
730
+ {
731
+ dangerouslySetInnerHTML: {
732
+ __html: `[data-huin-core-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-huin-core-scroll-area-viewport]::-webkit-scrollbar{display:none}`
733
+ },
734
+ nonce
735
+ }
736
+ ),
737
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
738
+ import_react_primitive4.Primitive.div,
739
+ {
740
+ "data-huin-core-scroll-area-viewport": "",
741
+ ...viewportProps,
742
+ ref: composedRefs,
743
+ style: {
744
+ /**
745
+ * We don't support `visible` because the intention is to have at least one scrollbar
746
+ * if this component is used and `visible` will behave like `auto` in that case
747
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/overflow#description
748
+ *
749
+ * We don't handle `auto` because the intention is for the native implementation
750
+ * to be hidden if using this component. We just want to ensure the node is scrollable
751
+ * so could have used either `scroll` or `auto` here. We picked `scroll` to prevent
752
+ * the browser from having to work out whether to render native scrollbars or not,
753
+ * we tell it to with the intention of hiding them in CSS.
754
+ */
755
+ overflowX: context.scrollbarXEnabled ? "scroll" : "hidden",
756
+ overflowY: context.scrollbarYEnabled ? "scroll" : "hidden",
757
+ ...props.style
758
+ },
759
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
760
+ import_react_primitive4.Primitive.div,
761
+ {
762
+ ref: context.onContentChange,
763
+ style: { minWidth: "100%", display: "table" },
764
+ children
765
+ }
766
+ )
767
+ }
768
+ )
769
+ ] });
770
+ });
771
+ ScrollAreaViewport.displayName = VIEWPORT_NAME;
772
+
773
+ // packages/react/scroll-area/src/ScrollAreaThumb.tsx
774
+ var import_react4 = __toESM(require("react"));
775
+ var import_react_presence2 = require("@huin-core/react-presence");
776
+ var import_react_compose_refs4 = require("@huin-core/react-compose-refs");
777
+ var import_react_primitive5 = require("@huin-core/react-primitive");
778
+ var import_primitive2 = require("@huin-core/primitive");
779
+ var import_jsx_runtime5 = require("react/jsx-runtime");
780
+ var THUMB_NAME = "ScrollAreaThumb";
781
+ var ScrollAreaThumb = import_react4.default.forwardRef((props, forwardedRef) => {
782
+ const { forceMount, ...thumbProps } = props;
783
+ const scrollbarContext = useScrollbarContext(
784
+ THUMB_NAME,
785
+ props.__scopeScrollArea
786
+ );
787
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_presence2.Presence, { present: forceMount || scrollbarContext.hasThumb, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScrollAreaThumbImpl, { ref: forwardedRef, ...thumbProps }) });
788
+ });
789
+ var ScrollAreaThumbImpl = import_react4.default.forwardRef((props, forwardedRef) => {
790
+ const { __scopeScrollArea, style, ...thumbProps } = props;
791
+ const scrollAreaContext = useScrollAreaContext(THUMB_NAME, __scopeScrollArea);
792
+ const scrollbarContext = useScrollbarContext(THUMB_NAME, __scopeScrollArea);
793
+ const { onThumbPositionChange } = scrollbarContext;
794
+ const composedRef = (0, import_react_compose_refs4.useComposedRefs)(
795
+ forwardedRef,
796
+ (node) => scrollbarContext.onThumbChange(node)
797
+ );
798
+ const removeUnlinkedScrollListenerRef = import_react4.default.useRef();
799
+ const debounceScrollEnd = useDebounceCallback(() => {
800
+ if (removeUnlinkedScrollListenerRef.current) {
801
+ removeUnlinkedScrollListenerRef.current();
802
+ removeUnlinkedScrollListenerRef.current = void 0;
803
+ }
804
+ }, 100);
805
+ import_react4.default.useEffect(() => {
806
+ const viewport = scrollAreaContext.viewport;
807
+ if (viewport) {
808
+ const handleScroll = () => {
809
+ debounceScrollEnd();
810
+ if (!removeUnlinkedScrollListenerRef.current) {
811
+ const listener = addUnlinkedScrollListener(
812
+ viewport,
813
+ onThumbPositionChange
814
+ );
815
+ removeUnlinkedScrollListenerRef.current = listener;
816
+ onThumbPositionChange();
817
+ }
818
+ };
819
+ onThumbPositionChange();
820
+ viewport.addEventListener("scroll", handleScroll);
821
+ return () => viewport.removeEventListener("scroll", handleScroll);
822
+ }
823
+ }, [scrollAreaContext.viewport, debounceScrollEnd, onThumbPositionChange]);
824
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
825
+ import_react_primitive5.Primitive.div,
826
+ {
827
+ "data-state": scrollbarContext.hasThumb ? "visible" : "hidden",
828
+ ...thumbProps,
829
+ ref: composedRef,
830
+ style: {
831
+ width: "var(--huin-core-scroll-area-thumb-width)",
832
+ height: "var(--huin-core-scroll-area-thumb-height)",
833
+ ...style
834
+ },
835
+ onPointerDownCapture: (0, import_primitive2.composeEventHandlers)(
836
+ props.onPointerDownCapture,
837
+ (event) => {
838
+ const thumb = event.target;
839
+ const thumbRect = thumb.getBoundingClientRect();
840
+ const x = event.clientX - thumbRect.left;
841
+ const y = event.clientY - thumbRect.top;
842
+ scrollbarContext.onThumbPointerDown({ x, y });
843
+ }
844
+ ),
845
+ onPointerUp: (0, import_primitive2.composeEventHandlers)(
846
+ props.onPointerUp,
847
+ scrollbarContext.onThumbPointerUp
848
+ )
849
+ }
850
+ );
851
+ });
852
+ ScrollAreaThumb.displayName = THUMB_NAME;
853
+ var addUnlinkedScrollListener = (node, handler = () => {
854
+ }) => {
855
+ let prevPosition = { left: node.scrollLeft, top: node.scrollTop };
856
+ let rAF = 0;
857
+ (function loop() {
858
+ const position = { left: node.scrollLeft, top: node.scrollTop };
859
+ const isHorizontalScroll = prevPosition.left !== position.left;
860
+ const isVerticalScroll = prevPosition.top !== position.top;
861
+ if (isHorizontalScroll || isVerticalScroll) handler();
862
+ prevPosition = position;
863
+ rAF = window.requestAnimationFrame(loop);
864
+ })();
865
+ return () => window.cancelAnimationFrame(rAF);
866
+ };
867
+ //# sourceMappingURL=index.js.map