@trackunit/react-components 0.5.0 → 0.5.1

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.cjs.js CHANGED
@@ -13,8 +13,8 @@ var stringTs = require('string-ts');
13
13
  var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
14
14
  var reactSlot = require('@radix-ui/react-slot');
15
15
  var isEqual = require('lodash/isEqual');
16
- var reactRouter = require('@tanstack/react-router');
17
16
  var usehooksTs = require('usehooks-ts');
17
+ var reactRouter = require('@tanstack/react-router');
18
18
  var react = require('@floating-ui/react');
19
19
  var omit = require('lodash/omit');
20
20
  var tailwindMerge = require('tailwind-merge');
@@ -1309,6 +1309,62 @@ const getWindowSize = () => {
1309
1309
  }
1310
1310
  };
1311
1311
 
1312
+ const SCROLL_DEBOUNCE_TIME = 50;
1313
+ /**
1314
+ * Hook for getting detecting scroll values.
1315
+ *
1316
+ * @param {useRef} elementRef - Ref hook holding the element that needs to be observed during scrolling
1317
+ * @returns {object} An object containing if the element is scrollable, is at the top, is at the bottom, and its current scroll position.
1318
+ */
1319
+ const useScrollDetection = (elementRef) => {
1320
+ const [isScrollable, setIsScrollable] = React.useState(false);
1321
+ const [isAtTop, setIsAtTop] = React.useState(true);
1322
+ const [isAtBottom, setIsAtBottom] = React.useState(false);
1323
+ const [scrollPosition, setScrollPosition] = React.useState({ top: 0, bottom: 0 });
1324
+ const observerRef = React.useRef();
1325
+ const checkScrollable = React.useCallback(() => {
1326
+ const element = elementRef.current;
1327
+ if (!element) {
1328
+ return;
1329
+ }
1330
+ const hasOverflow = element.scrollHeight > element.clientHeight;
1331
+ setIsScrollable(hasOverflow);
1332
+ }, [elementRef]);
1333
+ const checkScrollPosition = React.useCallback(() => {
1334
+ const element = elementRef.current;
1335
+ if (!element) {
1336
+ return;
1337
+ }
1338
+ const { scrollTop, scrollHeight, clientHeight } = element;
1339
+ setIsAtTop(scrollTop === 0);
1340
+ setIsAtBottom(Math.abs(scrollHeight - scrollTop - clientHeight) < 1);
1341
+ setScrollPosition(prev => ({ ...prev, top: scrollTop, bottom: clientHeight - scrollTop }));
1342
+ }, [elementRef]);
1343
+ const debouncedCheckScrollPosition = usehooksTs.useDebounceCallback(checkScrollPosition, SCROLL_DEBOUNCE_TIME);
1344
+ React.useEffect(() => {
1345
+ const element = elementRef.current;
1346
+ if (!element) {
1347
+ return;
1348
+ }
1349
+ // Initial checks
1350
+ checkScrollable();
1351
+ checkScrollPosition();
1352
+ observerRef.current = new ResizeObserver(() => {
1353
+ checkScrollable();
1354
+ checkScrollPosition();
1355
+ });
1356
+ observerRef.current.observe(element);
1357
+ element.addEventListener("scroll", debouncedCheckScrollPosition);
1358
+ return () => {
1359
+ if (observerRef.current) {
1360
+ observerRef.current.disconnect();
1361
+ }
1362
+ element.removeEventListener("scroll", debouncedCheckScrollPosition);
1363
+ };
1364
+ }, [elementRef, checkScrollable, checkScrollPosition, debouncedCheckScrollPosition]);
1365
+ return { isScrollable, isAtTop, isAtBottom, scrollPosition };
1366
+ };
1367
+
1312
1368
  /**
1313
1369
  * A useRef that updates its given value whenever it changes.
1314
1370
  *
@@ -4923,6 +4979,7 @@ exports.usePopoverContext = usePopoverContext;
4923
4979
  exports.usePrevious = usePrevious;
4924
4980
  exports.usePrompt = usePrompt;
4925
4981
  exports.useResize = useResize;
4982
+ exports.useScrollDetection = useScrollDetection;
4926
4983
  exports.useSelfUpdatingRef = useSelfUpdatingRef;
4927
4984
  exports.useTimeout = useTimeout;
4928
4985
  exports.useViewportSize = useViewportSize;
package/index.esm.js CHANGED
@@ -12,8 +12,8 @@ import { snakeCase, titleCase } from 'string-ts';
12
12
  import { cvaMerge } from '@trackunit/css-class-variance-utilities';
13
13
  import { Slottable, Slot } from '@radix-ui/react-slot';
14
14
  import isEqual from 'lodash/isEqual';
15
+ import { useDebounceCallback, useCopyToClipboard } from 'usehooks-ts';
15
16
  import { Link, useBlocker } from '@tanstack/react-router';
16
- import { useCopyToClipboard } from 'usehooks-ts';
17
17
  import { useFloating, autoUpdate, offset, flip, shift, size, useClick, useDismiss, useHover as useHover$1, useRole, useInteractions, useMergeRefs, FloatingPortal, FloatingFocusManager, arrow, useTransitionStatus, FloatingArrow } from '@floating-ui/react';
18
18
  import omit from 'lodash/omit';
19
19
  import { twMerge } from 'tailwind-merge';
@@ -1289,6 +1289,62 @@ const getWindowSize = () => {
1289
1289
  }
1290
1290
  };
1291
1291
 
1292
+ const SCROLL_DEBOUNCE_TIME = 50;
1293
+ /**
1294
+ * Hook for getting detecting scroll values.
1295
+ *
1296
+ * @param {useRef} elementRef - Ref hook holding the element that needs to be observed during scrolling
1297
+ * @returns {object} An object containing if the element is scrollable, is at the top, is at the bottom, and its current scroll position.
1298
+ */
1299
+ const useScrollDetection = (elementRef) => {
1300
+ const [isScrollable, setIsScrollable] = useState(false);
1301
+ const [isAtTop, setIsAtTop] = useState(true);
1302
+ const [isAtBottom, setIsAtBottom] = useState(false);
1303
+ const [scrollPosition, setScrollPosition] = useState({ top: 0, bottom: 0 });
1304
+ const observerRef = useRef();
1305
+ const checkScrollable = useCallback(() => {
1306
+ const element = elementRef.current;
1307
+ if (!element) {
1308
+ return;
1309
+ }
1310
+ const hasOverflow = element.scrollHeight > element.clientHeight;
1311
+ setIsScrollable(hasOverflow);
1312
+ }, [elementRef]);
1313
+ const checkScrollPosition = useCallback(() => {
1314
+ const element = elementRef.current;
1315
+ if (!element) {
1316
+ return;
1317
+ }
1318
+ const { scrollTop, scrollHeight, clientHeight } = element;
1319
+ setIsAtTop(scrollTop === 0);
1320
+ setIsAtBottom(Math.abs(scrollHeight - scrollTop - clientHeight) < 1);
1321
+ setScrollPosition(prev => ({ ...prev, top: scrollTop, bottom: clientHeight - scrollTop }));
1322
+ }, [elementRef]);
1323
+ const debouncedCheckScrollPosition = useDebounceCallback(checkScrollPosition, SCROLL_DEBOUNCE_TIME);
1324
+ useEffect(() => {
1325
+ const element = elementRef.current;
1326
+ if (!element) {
1327
+ return;
1328
+ }
1329
+ // Initial checks
1330
+ checkScrollable();
1331
+ checkScrollPosition();
1332
+ observerRef.current = new ResizeObserver(() => {
1333
+ checkScrollable();
1334
+ checkScrollPosition();
1335
+ });
1336
+ observerRef.current.observe(element);
1337
+ element.addEventListener("scroll", debouncedCheckScrollPosition);
1338
+ return () => {
1339
+ if (observerRef.current) {
1340
+ observerRef.current.disconnect();
1341
+ }
1342
+ element.removeEventListener("scroll", debouncedCheckScrollPosition);
1343
+ };
1344
+ }, [elementRef, checkScrollable, checkScrollPosition, debouncedCheckScrollPosition]);
1345
+ return { isScrollable, isAtTop, isAtBottom, scrollPosition };
1346
+ };
1347
+
1292
1348
  /**
1293
1349
  * A useRef that updates its given value whenever it changes.
1294
1350
  *
@@ -4806,4 +4862,4 @@ const cvaClickable = cvaMerge([
4806
4862
  },
4807
4863
  });
4808
4864
 
4809
- export { Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, EmptyState, EmptyValue, ExternalLink, Heading, Icon, IconButton, Indicator, KPICard, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, Timeline, TimelineElement, ToggleGroup, ToggleItem, Tooltip, ValueBar, VirtualizedList, WidgetBody, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaIconButton, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInteractableItem, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, setLocalStorage, useClickOutside, useContinuousTimeout, useDebounce, useDevicePixelRatio, useGeometry, useHover, useIsFirstRender, useIsFullscreen, useIsTextCutOff, useLocalStorage, useLocalStorageReducer, useOptionallyElevatedState, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useResize, useSelfUpdatingRef, useTimeout, useViewportSize, useWindowActivity };
4865
+ export { Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, EmptyState, EmptyValue, ExternalLink, Heading, Icon, IconButton, Indicator, KPICard, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, Timeline, TimelineElement, ToggleGroup, ToggleItem, Tooltip, ValueBar, VirtualizedList, WidgetBody, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaIconButton, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInteractableItem, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, setLocalStorage, useClickOutside, useContinuousTimeout, useDebounce, useDevicePixelRatio, useGeometry, useHover, useIsFirstRender, useIsFullscreen, useIsTextCutOff, useLocalStorage, useLocalStorageReducer, useOptionallyElevatedState, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useResize, useScrollDetection, useSelfUpdatingRef, useTimeout, useViewportSize, useWindowActivity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -13,6 +13,7 @@ export * from "./useIsTextCutOff";
13
13
  export * from "./useOptionallyElevatedState";
14
14
  export * from "./usePrevious";
15
15
  export * from "./useResize";
16
+ export * from "./useScrollDetection";
16
17
  export * from "./useSelfUpdatingRef";
17
18
  export * from "./useTimeout";
18
19
  export * from "./useViewportSize";
@@ -0,0 +1,16 @@
1
+ import { type RefObject } from "react";
2
+ /**
3
+ * Hook for getting detecting scroll values.
4
+ *
5
+ * @param {useRef} elementRef - Ref hook holding the element that needs to be observed during scrolling
6
+ * @returns {object} An object containing if the element is scrollable, is at the top, is at the bottom, and its current scroll position.
7
+ */
8
+ export declare const useScrollDetection: (elementRef: RefObject<HTMLElement>) => {
9
+ isScrollable: boolean;
10
+ isAtTop: boolean;
11
+ isAtBottom: boolean;
12
+ scrollPosition: {
13
+ top: number;
14
+ bottom: number;
15
+ };
16
+ };