@spark-ui/components 17.13.1 → 17.13.2

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
  import { IconButtonProps } from '../icon-button';
2
2
  /** A button to scroll to the next page of items. Renders a <button> element. */
3
3
  export declare const ScrollingListNextButton: {
4
- ({ "aria-label": ariaLabel, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
4
+ ({ "aria-label": ariaLabel, onClick, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
5
5
  displayName: string;
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import { IconButtonProps } from '../icon-button';
2
2
  /** A button to scroll to the previous page of items. Renders a <button> element. */
3
3
  export declare const ScrollingListPrevButton: {
4
- ({ "aria-label": ariaLabel, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
4
+ ({ "aria-label": ariaLabel, onClick, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
5
5
  displayName: string;
6
6
  };
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../chunk-C91j1N6u.js`);const e=require(`../slot/index.js`),t=require(`../icon-CRPcdgYp.js`),n=require(`../button-Tv2N8_24.js`),r=require(`../icon-button-CTe0v0g7.js`);let i=require(`class-variance-authority`),a=require(`react`),o=require(`react/jsx-runtime`),s=require(`@spark-ui/hooks/use-scroll-overflow`),c=require(`@spark-ui/icons/ArrowVerticalRight`),l=require(`@spark-ui/icons/ArrowVerticalLeft`),u=require(`react-snap-carousel`);var d=(0,a.createContext)(null),f=({snapType:e=`none`,snapStop:t=`normal`,scrollBehavior:n=`smooth`,loop:r=!1,gap:c=16,withFade:l=!1,scrollPadding:f=0,children:p,className:m,...h})=>{let g=(0,a.useRef)(null),_=(0,a.useRef)(null),v=(0,u.useSnapCarousel)(),{overflow:y,refresh:b}=(0,s.useScrollOverflow)(g,{precisionTreshold:1}),{activePageIndex:x,pages:S,refresh:C}=v,w=S[x],T=w?[w[0]+1,w[w.length-1]+1]:[0,0],E=(0,a.useCallback)(()=>{C&&g.current&&setTimeout(()=>{C()},0)},[C]);(0,a.useEffect)(()=>{E()},[p,E]),(0,a.useLayoutEffect)(()=>{g.current&&requestAnimationFrame(()=>{b()})},[p,b]);let D=()=>{_.current?.focus()};return(0,o.jsxs)(d,{value:{...v,snapType:e,snapStop:t,skipKeyboardNavigation:D,scrollBehavior:n,visibleItemsRange:T,loop:r,gap:c,withFade:l,scrollPadding:f,scrollAreaRef:g,overflow:y},children:[(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list`,className:(0,i.cx)(`gap-lg group/scrolling-list relative flex flex-col default:w-full`,m),...h,children:p}),(0,o.jsx)(`span`,{ref:_,className:`size-0 overflow-hidden`,tabIndex:-1})]})};f.displayName=`ScrollingList`;var p=({children:e,visibility:t=`always`,className:n,...r})=>(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list-controls`,className:(0,i.cx)(`default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden`,n),style:{"--scrolling-list-controls-opacity":t===`hover`?`0`:`1`},"data-orientation":`horizontal`,...r,children:e});p.displayName=`ScrollingList.Controls`;function m(e,t){let[n,r]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{let n=e=>{r(!0);let n=e.target,i=t.current;if(n&&i){let e=n.getBoundingClientRect(),t=i.getBoundingClientRect();e.left>=t.left&&e.right<=t.right&&e.top>=t.top&&e.bottom<=t.bottom||n.scrollIntoView({behavior:`smooth`,inline:`center`,block:`nearest`})}},i=t=>{e.current&&!e.current.contains(t.relatedTarget)&&r(!1)},a=e.current;return a&&(a.addEventListener(`focusin`,n),a.addEventListener(`focusout`,i)),()=>{a&&(a.removeEventListener(`focusin`,n),a.removeEventListener(`focusout`,i))}},[e,t]),n}var h=({asChild:t=!1,children:n,index:r=0,className:s=``,...c})=>{let l=(0,a.useContext)(d),u=(0,a.useRef)(null),f=l.snapPointIndexes.has(r);return m(u,l.scrollAreaRef),(0,o.jsx)(t?e.Slot:`div`,{"data-spark-component":`scrolling-list-item`,role:`listitem`,ref:u,className:(0,i.cx)(`default:w-auto default:shrink-0`,{"snap-start":f,"snap-normal":f&&l.snapStop===`normal`,"snap-always":f&&l.snapStop===`always`},s),...c,children:n})};h.displayName=`ScrollingList.Item`;function g(...e){return t=>{e.forEach(e=>{typeof e==`function`?e(t):e&&typeof e==`object`&&`current`in e&&(e.current=t)})}}var _=({children:e,ref:t,className:n=``,...r})=>{let s=(0,a.useContext)(d),c={mandatory:`x mandatory`,proximity:`x proximity`,none:`none`},l=e=>{!s.loop&&!s.hasPrevPage||(e.preventDefault(),s.goTo(s.hasPrevPage?s.activePageIndex-1:s.pages.length-1,{behavior:s.scrollBehavior}))},u=e=>{!s.loop&&!s.hasNextPage||(e.preventDefault(),s.goTo(s.hasNextPage?s.activePageIndex+1:0,{behavior:s.scrollBehavior}))},f=e=>{e.key===`ArrowLeft`&&l(e),e.key===`ArrowRight`&&u(e)},p={scrollSnapType:c[s.snapType],scrollPaddingInline:`var(--scrolling-list-px)`,"--scrolling-list-px":`${s.scrollPadding}px`,"--scrolling-list-gap":`${s.gap}px`,...s.withFade&&{maskImage:`linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))`,maskSize:`calc(100% + ${s.overflow.left?`0px`:`44px`} + ${s.overflow.right?`0px`:`44px`}) 100%`,maskPosition:`${s.overflow.left?`0px`:`-44px`} 0`}};return(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list-items`,id:`scrolling-list-items`,role:`list`,className:(0,i.cx)(`relative transition-all duration-300`,`u-no-scrollbar overflow-x-auto scroll-smooth`,`w-full gap-(--scrolling-list-gap) default:flex default:flex-row`,`focus-visible:u-outline`,n),ref:g(s.scrollAreaRef,s.scrollRef,t),style:p,onKeyDown:f,...r,children:a.Children.map(e,(e,t)=>(0,a.isValidElement)(e)?(0,a.cloneElement)(e,{index:t}):e)})};_.displayName=`ScrollingList.Items`;var v=({"aria-label":e,...n})=>{let s=(0,a.useContext)(d),l=()=>{s.hasNextPage?s.next({behavior:s.scrollBehavior}):s.goTo(0,{behavior:s.scrollBehavior})},u=!(s.overflow.left||s.overflow.right)||!s.loop&&!s.overflow.right;return(0,o.jsx)(r.t,{"data-spark-component":`scrolling-list-next-button`,size:`sm`,intent:`surface`,design:`filled`,className:(0,i.cx)(`pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible`,`group-hover/scrolling-list:opacity-none focus-visible:opacity-none`),onClick:l,disabled:u,"aria-label":e,"aria-controls":`scrolling-list-items`,...n,children:(0,o.jsx)(t.t,{children:(0,o.jsx)(c.ArrowVerticalRight,{})})})};v.displayName=`ScrollingList.NextButton`;var y=({"aria-label":e,...n})=>{let s=(0,a.useContext)(d),c=()=>{s.activePageIndex===0&&(s.scrollAreaRef.current?.scrollLeft||0)>0?s.goTo(0,{behavior:s.scrollBehavior}):s.hasPrevPage?s.prev({behavior:s.scrollBehavior}):s.goTo(s.pages.length-1,{behavior:s.scrollBehavior})},u=!(s.overflow.left||s.overflow.right)||!s.loop&&!s.overflow.left;return(0,o.jsx)(r.t,{"data-spark-component":`scrolling-list-prev-button`,size:`sm`,intent:`surface`,design:`filled`,className:(0,i.cx)(`pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible`,`group-hover/scrolling-list:opacity-none focus-visible:opacity-none`),onClick:c,disabled:u,"aria-label":e,"aria-controls":`scrolling-list-items`,...n,children:(0,o.jsx)(t.t,{children:(0,o.jsx)(l.ArrowVerticalLeft,{})})})};y.displayName=`ScrollingList.PrevButton`;var b=({children:e,...t})=>{let r=(0,a.useContext)(d);return(0,o.jsx)(n.t,{type:`button`,design:`tinted`,intent:`surface`,tabIndex:0,className:(0,i.cx)(`z-raised absolute top-1/2 left-0 -translate-y-1/2`,`not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0`),onClick:r.skipKeyboardNavigation,...t,children:e})};b.displayName=`ScrollingList.SkipButton`;var x=Object.assign(f,{Controls:p,NextButton:v,PrevButton:y,Item:h,Items:_,SkipButton:b});x.displayName=`ScrollingList`,exports.ScrollingList=x;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../chunk-C91j1N6u.js`);const e=require(`../slot/index.js`),t=require(`../icon-CRPcdgYp.js`),n=require(`../button-Tv2N8_24.js`),r=require(`../icon-button-CTe0v0g7.js`);let i=require(`class-variance-authority`),a=require(`react`),o=require(`react/jsx-runtime`),s=require(`@spark-ui/hooks/use-scroll-overflow`),c=require(`@spark-ui/icons/ArrowVerticalRight`),l=require(`@spark-ui/icons/ArrowVerticalLeft`),u=require(`react-snap-carousel`);var d=(0,a.createContext)(null),f=({snapType:e=`none`,snapStop:t=`normal`,scrollBehavior:n=`smooth`,loop:r=!1,gap:c=16,withFade:l=!1,scrollPadding:f=0,children:p,className:m,...h})=>{let g=(0,a.useRef)(null),_=(0,a.useRef)(null),v=(0,u.useSnapCarousel)(),{overflow:y,refresh:b}=(0,s.useScrollOverflow)(g,{precisionTreshold:1}),{activePageIndex:x,pages:S,refresh:C}=v,w=S[x],T=w?[w[0]+1,w[w.length-1]+1]:[0,0],E=(0,a.useCallback)(()=>{C&&g.current&&setTimeout(()=>{C()},0)},[C]);(0,a.useEffect)(()=>{E()},[p,E]),(0,a.useLayoutEffect)(()=>{g.current&&requestAnimationFrame(()=>{b()})},[p,b]);let D=()=>{_.current?.focus()};return(0,o.jsxs)(d,{value:{...v,snapType:e,snapStop:t,skipKeyboardNavigation:D,scrollBehavior:n,visibleItemsRange:T,loop:r,gap:c,withFade:l,scrollPadding:f,scrollAreaRef:g,overflow:y},children:[(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list`,className:(0,i.cx)(`gap-lg group/scrolling-list relative flex flex-col default:w-full`,m),...h,children:p}),(0,o.jsx)(`span`,{ref:_,className:`size-0 overflow-hidden`,tabIndex:-1})]})};f.displayName=`ScrollingList`;var p=({children:e,visibility:t=`always`,className:n,...r})=>(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list-controls`,className:(0,i.cx)(`default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden`,n),style:{"--scrolling-list-controls-opacity":t===`hover`?`0`:`1`},"data-orientation":`horizontal`,...r,children:e});p.displayName=`ScrollingList.Controls`;function m(e,t){let[n,r]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{let n=e=>{r(!0);let n=e.target,i=t.current;if(n&&i){let e=n.getBoundingClientRect(),t=i.getBoundingClientRect();e.left>=t.left&&e.right<=t.right&&e.top>=t.top&&e.bottom<=t.bottom||n.scrollIntoView({behavior:`smooth`,inline:`center`,block:`nearest`})}},i=t=>{e.current&&!e.current.contains(t.relatedTarget)&&r(!1)},a=e.current;return a&&(a.addEventListener(`focusin`,n),a.addEventListener(`focusout`,i)),()=>{a&&(a.removeEventListener(`focusin`,n),a.removeEventListener(`focusout`,i))}},[e,t]),n}var h=({asChild:t=!1,children:n,index:r=0,className:s=``,...c})=>{let l=(0,a.useContext)(d),u=(0,a.useRef)(null),f=l.snapPointIndexes.has(r);return m(u,l.scrollAreaRef),(0,o.jsx)(t?e.Slot:`div`,{"data-spark-component":`scrolling-list-item`,role:`listitem`,ref:u,className:(0,i.cx)(`default:w-auto default:shrink-0`,{"snap-start":f,"snap-normal":f&&l.snapStop===`normal`,"snap-always":f&&l.snapStop===`always`},s),...c,children:n})};h.displayName=`ScrollingList.Item`;function g(...e){return t=>{e.forEach(e=>{typeof e==`function`?e(t):e&&typeof e==`object`&&`current`in e&&(e.current=t)})}}var _=({children:e,ref:t,className:n=``,...r})=>{let s=(0,a.useContext)(d),c={mandatory:`x mandatory`,proximity:`x proximity`,none:`none`},l=e=>{!s.loop&&!s.hasPrevPage||(e.preventDefault(),s.goTo(s.hasPrevPage?s.activePageIndex-1:s.pages.length-1,{behavior:s.scrollBehavior}))},u=e=>{!s.loop&&!s.hasNextPage||(e.preventDefault(),s.goTo(s.hasNextPage?s.activePageIndex+1:0,{behavior:s.scrollBehavior}))},f=e=>{e.key===`ArrowLeft`&&l(e),e.key===`ArrowRight`&&u(e)},p={scrollSnapType:c[s.snapType],scrollPaddingInline:`var(--scrolling-list-px)`,"--scrolling-list-px":`${s.scrollPadding}px`,"--scrolling-list-gap":`${s.gap}px`,...s.withFade&&{maskImage:`linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))`,maskSize:`calc(100% + ${s.overflow.left?`0px`:`44px`} + ${s.overflow.right?`0px`:`44px`}) 100%`,maskPosition:`${s.overflow.left?`0px`:`-44px`} 0`}};return(0,o.jsx)(`div`,{"data-spark-component":`scrolling-list-items`,id:`scrolling-list-items`,role:`list`,className:(0,i.cx)(`relative transition-all duration-300`,`u-no-scrollbar overflow-x-auto scroll-smooth`,`w-full gap-(--scrolling-list-gap) default:flex default:flex-row`,`focus-visible:u-outline`,n),ref:g(s.scrollAreaRef,s.scrollRef,t),style:p,onKeyDown:f,...r,children:a.Children.map(e,(e,t)=>(0,a.isValidElement)(e)?(0,a.cloneElement)(e,{index:t}):e)})};_.displayName=`ScrollingList.Items`;var v=({"aria-label":e,onClick:n,...s})=>{let l=(0,a.useContext)(d),u=e=>{l.hasNextPage?l.next({behavior:l.scrollBehavior}):l.goTo(0,{behavior:l.scrollBehavior}),n?.(e)},f=!(l.overflow.left||l.overflow.right)||!l.loop&&!l.overflow.right;return(0,o.jsx)(r.t,{"data-spark-component":`scrolling-list-next-button`,size:`sm`,intent:`surface`,design:`filled`,className:(0,i.cx)(`pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible`,`group-hover/scrolling-list:opacity-none focus-visible:opacity-none`),onClick:u,disabled:f,"aria-label":e,"aria-controls":`scrolling-list-items`,...s,children:(0,o.jsx)(t.t,{children:(0,o.jsx)(c.ArrowVerticalRight,{})})})};v.displayName=`ScrollingList.NextButton`;var y=({"aria-label":e,onClick:n,...s})=>{let c=(0,a.useContext)(d),u=e=>{c.activePageIndex===0&&(c.scrollAreaRef.current?.scrollLeft||0)>0?c.goTo(0,{behavior:c.scrollBehavior}):c.hasPrevPage?c.prev({behavior:c.scrollBehavior}):c.goTo(c.pages.length-1,{behavior:c.scrollBehavior}),n?.(e)},f=!(c.overflow.left||c.overflow.right)||!c.loop&&!c.overflow.left;return(0,o.jsx)(r.t,{"data-spark-component":`scrolling-list-prev-button`,size:`sm`,intent:`surface`,design:`filled`,className:(0,i.cx)(`pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible`,`group-hover/scrolling-list:opacity-none focus-visible:opacity-none`),onClick:u,disabled:f,"aria-label":e,"aria-controls":`scrolling-list-items`,...s,children:(0,o.jsx)(t.t,{children:(0,o.jsx)(l.ArrowVerticalLeft,{})})})};y.displayName=`ScrollingList.PrevButton`;var b=({children:e,...t})=>{let r=(0,a.useContext)(d);return(0,o.jsx)(n.t,{type:`button`,design:`tinted`,intent:`surface`,tabIndex:0,className:(0,i.cx)(`z-raised absolute top-1/2 left-0 -translate-y-1/2`,`not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0`),onClick:r.skipKeyboardNavigation,...t,children:e})};b.displayName=`ScrollingList.SkipButton`;var x=Object.assign(f,{Controls:p,NextButton:v,PrevButton:y,Item:h,Items:_,SkipButton:b});x.displayName=`ScrollingList`,exports.ScrollingList=x;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/scrolling-list/ScrollingList.tsx","../../src/scrolling-list/ScrollingListControls.tsx","../../src/scrolling-list/useFocusWithinScroll.tsx","../../src/scrolling-list/ScrollingListItem.tsx","../../src/scrolling-list/ScrollingListItems.tsx","../../src/scrolling-list/ScrollingListNextButton.tsx","../../src/scrolling-list/ScrollingListPrevButton.tsx","../../src/scrolling-list/ScrollingListSkipButton.tsx","../../src/scrolling-list/index.ts"],"sourcesContent":["import { ScrollOverflow, useScrollOverflow } from '@spark-ui/hooks/use-scroll-overflow'\nimport { cx } from 'class-variance-authority'\nimport {\n ComponentPropsWithRef,\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n} from 'react'\nimport { SnapCarouselResult, useSnapCarousel } from 'react-snap-carousel'\n\ntype SnapType = 'mandatory' | 'proximity' | 'none'\ntype ScrollBehavior = 'smooth' | 'instant'\ntype SnapStop = 'normal' | 'always'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n /**\n * CSS scroll snap behavior.\n * - `mandatory` to force snapping on each \"page\".\n * - `proximity` to force snapping only when scroll position is near the edge of a \"page\". Behavior can change depending on each browser.\n * - `none` to disabled scroll snapping.\n */\n snapType?: SnapType\n /**\n * Defines whether or not the scroll container is allowed to \"pass over\" possible snap positions.\n */\n snapStop?: SnapStop\n scrollBehavior?: ScrollBehavior\n /**\n * Add a fade effect to indicate content overflow.\n */\n withFade?: boolean\n children?: ReactNode\n /**\n * When `true`, allow previous and next buttons to be used when reaching the edges of the list.\n */\n loop?: boolean\n /**\n * Space (in pixels) between items.\n */\n gap?: number\n /**\n * Offset (in pixels) of the left of the optimal viewing region of the list.\n */\n scrollPadding?: number\n className?: string\n}\n\ninterface ScrollingListContextState extends SnapCarouselResult {\n snapType: SnapType\n snapStop: SnapStop\n scrollBehavior: ScrollBehavior\n visibleItemsRange: readonly [number, number]\n loop: boolean\n gap: number\n withFade: boolean\n scrollPadding: number\n scrollAreaRef: RefObject<HTMLDivElement | null>\n overflow: ScrollOverflow\n skipKeyboardNavigation: () => void\n}\n\nexport const ScrollingListContext = createContext<ScrollingListContextState>(\n null as unknown as ScrollingListContextState\n)\n\nexport const ScrollingList = ({\n snapType = 'none',\n snapStop = 'normal',\n scrollBehavior = 'smooth',\n loop = false,\n gap = 16,\n withFade = false,\n scrollPadding = 0,\n children,\n className,\n ...rest\n}: Props) => {\n const scrollAreaRef = useRef<HTMLDivElement>(null)\n const skipAnchorRef = useRef<HTMLButtonElement>(null)\n\n const snapCarouselAPI = useSnapCarousel()\n\n const { overflow, refresh: refreshOverflow } = useScrollOverflow(scrollAreaRef, {\n precisionTreshold: 1,\n })\n\n const { activePageIndex, pages, refresh } = snapCarouselAPI\n\n const visibleItems = pages[activePageIndex] as number[]\n\n const visibleItemsRange = visibleItems\n ? ([visibleItems[0]! + 1, visibleItems[visibleItems.length - 1]! + 1] as const)\n : ([0, 0] as const)\n\n // Force refresh of the carousel API when children change\n const forceRefresh = useCallback(() => {\n if (refresh && scrollAreaRef.current) {\n // Small delay to ensure DOM is updated\n setTimeout(() => {\n refresh()\n }, 0)\n }\n }, [refresh])\n\n useEffect(() => {\n forceRefresh()\n }, [children, forceRefresh])\n\n useLayoutEffect(() => {\n if (scrollAreaRef.current) {\n // Use requestAnimationFrame to ensure proper timing with the render cycle\n // This prevents race conditions that occur when the console is closed\n requestAnimationFrame(() => {\n refreshOverflow()\n })\n }\n }, [children, refreshOverflow])\n\n const skipKeyboardNavigation = () => {\n skipAnchorRef.current?.focus()\n }\n\n const ctxValue: ScrollingListContextState = {\n ...snapCarouselAPI,\n snapType,\n snapStop,\n skipKeyboardNavigation,\n scrollBehavior,\n visibleItemsRange,\n loop,\n gap,\n withFade,\n scrollPadding,\n scrollAreaRef,\n overflow,\n }\n\n return (\n <ScrollingListContext value={ctxValue}>\n <div\n data-spark-component=\"scrolling-list\"\n className={cx(\n 'gap-lg group/scrolling-list relative flex flex-col default:w-full',\n className\n )}\n {...rest}\n >\n {children}\n </div>\n <span ref={skipAnchorRef} className=\"size-0 overflow-hidden\" tabIndex={-1} />\n </ScrollingListContext>\n )\n}\n\nScrollingList.displayName = 'ScrollingList'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'\n\ninterface ScrollingListControls extends ComponentPropsWithoutRef<'div'> {\n /**\n * Visibility behavior of the control buttons:\n * - `always`: buttons are always visible.\n * - `hover`: buttons only appear on hover.\n *\n * a11y: `hover` is dangerous for accessibility as it disabled controls for touch screen users.\n * When using it, you must provide an alternative control outside of the list to replace them.\n */\n visibility?: 'hover' | 'always'\n children: ReactNode\n}\n\n/** Container for navigation controls (previous/next buttons) of the scrolling list. Renders a <div> element. */\nexport const ScrollingListControls = ({\n children,\n visibility = 'always',\n className,\n ...rest\n}: ScrollingListControls) => {\n return (\n <div\n data-spark-component=\"scrolling-list-controls\"\n className={cx(\n 'default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden',\n className\n )}\n style={\n {\n '--scrolling-list-controls-opacity': visibility === 'hover' ? '0' : '1',\n } as CSSProperties\n }\n data-orientation=\"horizontal\"\n {...rest}\n >\n {children}\n </div>\n )\n}\n\nScrollingListControls.displayName = 'ScrollingList.Controls'\n","import { RefObject, useEffect, useState } from 'react'\n\nexport function useFocusWithinScroll<T extends HTMLElement | null>(\n ref: RefObject<T>, // The container to detect focus within\n scrollRef: RefObject<HTMLDivElement | null> // The scrollable container\n) {\n const [isFocusWithin, setIsFocusWithin] = useState(false)\n\n useEffect(() => {\n const handleFocusIn = (event: FocusEvent) => {\n setIsFocusWithin(true)\n\n const focusedElement = event.target as HTMLElement\n const scrollContainer = scrollRef.current\n\n if (focusedElement && scrollContainer) {\n const focusRect = focusedElement.getBoundingClientRect()\n const scrollRect = scrollContainer.getBoundingClientRect()\n\n // Check if the focused element is fully visible inside the scroll container\n const isFullyVisible =\n focusRect.left >= scrollRect.left &&\n focusRect.right <= scrollRect.right &&\n focusRect.top >= scrollRect.top &&\n focusRect.bottom <= scrollRect.bottom\n\n if (!isFullyVisible) {\n focusedElement.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })\n }\n }\n }\n\n const handleFocusOut = (event: FocusEvent) => {\n if (ref.current && !ref.current.contains(event.relatedTarget as Node)) {\n setIsFocusWithin(false)\n }\n }\n\n const node = ref.current\n if (node) {\n node.addEventListener('focusin', handleFocusIn)\n node.addEventListener('focusout', handleFocusOut)\n }\n\n return () => {\n if (node) {\n node.removeEventListener('focusin', handleFocusIn)\n node.removeEventListener('focusout', handleFocusOut)\n }\n }\n }, [ref, scrollRef])\n\n return isFocusWithin\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, ReactNode, useContext, useRef } from 'react'\n\nimport { Slot } from '../slot'\nimport { ScrollingListContext } from './ScrollingList'\nimport { useFocusWithinScroll } from './useFocusWithinScroll'\n\nexport interface ScrollingListItemProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n children?: ReactNode\n /**\n * DO NOT USE. This prop is automatically managed by the parent ScrollingList.ListItems\n */\n index?: number\n className?: string\n}\n\n/** A single item in the scrolling list. Renders a <div> element. */\nexport const ScrollingListItem = ({\n asChild = false,\n children,\n index = 0,\n className = '',\n ...rest\n}: ScrollingListItemProps) => {\n const ctx = useContext(ScrollingListContext)\n const itemRef = useRef<HTMLDivElement>(null)\n\n const isSnapPoint = ctx.snapPointIndexes.has(index)\n\n useFocusWithinScroll(itemRef, ctx.scrollAreaRef)\n\n const Component = asChild ? Slot : 'div'\n\n return (\n <Component\n data-spark-component=\"scrolling-list-item\"\n role=\"listitem\"\n ref={itemRef}\n className={cx(\n 'default:w-auto default:shrink-0',\n {\n 'snap-start': isSnapPoint,\n 'snap-normal': isSnapPoint && ctx.snapStop === 'normal',\n 'snap-always': isSnapPoint && ctx.snapStop === 'always',\n },\n className\n )}\n {...rest}\n >\n {children}\n </Component>\n )\n}\n\nScrollingListItem.displayName = 'ScrollingList.Item'\n","import { cx } from 'class-variance-authority'\nimport {\n Children,\n cloneElement,\n ComponentPropsWithRef,\n CSSProperties,\n isValidElement,\n KeyboardEvent,\n ReactNode,\n Ref,\n RefObject,\n useContext,\n} from 'react'\n\nimport { ScrollingListContext } from './ScrollingList'\nimport { ScrollingListItemProps } from './ScrollingListItem'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n children?: ReactNode\n className?: string\n}\n\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined | null)[]): Ref<T> {\n return (value: T | null) => {\n refs.forEach(ref => {\n if (typeof ref === 'function') {\n ref(value)\n } else if (ref && typeof ref === 'object' && 'current' in ref) {\n ;(ref as RefObject<T | null>).current = value\n }\n })\n }\n}\n\n/** The scrollable container for the list items. Renders a <div> element. */\nexport const ScrollingListItems = ({ children, ref, className = '', ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n const snapConfig = {\n mandatory: 'x mandatory',\n proximity: 'x proximity',\n none: 'none',\n }\n\n const handleLeftArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasPrevPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasPrevPage ? ctx.activePageIndex - 1 : ctx.pages.length - 1, {\n behavior: ctx.scrollBehavior,\n })\n }\n\n const handleRightArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasNextPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasNextPage ? ctx.activePageIndex + 1 : 0, { behavior: ctx.scrollBehavior })\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n handleLeftArrow(event)\n }\n\n if (event.key === 'ArrowRight') {\n handleRightArrow(event)\n }\n }\n\n interface CustomCSSProperties extends CSSProperties {\n '--scrolling-list-gap'?: string\n '--scrolling-list-px'?: string\n }\n\n const inlineStyles: CustomCSSProperties = {\n scrollSnapType: snapConfig[ctx.snapType],\n scrollPaddingInline: 'var(--scrolling-list-px)',\n '--scrolling-list-px': `${ctx.scrollPadding}px`,\n '--scrolling-list-gap': `${ctx.gap}px`,\n ...(ctx.withFade && {\n maskImage:\n 'linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))',\n maskSize: `calc(100% + ${ctx.overflow.left ? '0px' : '44px'} + ${ctx.overflow.right ? '0px' : '44px'}) 100%`,\n maskPosition: `${ctx.overflow.left ? '0px' : '-44px'} 0`,\n }),\n }\n\n return (\n <div\n data-spark-component=\"scrolling-list-items\"\n id=\"scrolling-list-items\"\n role=\"list\"\n className={cx(\n 'relative transition-all duration-300',\n 'u-no-scrollbar overflow-x-auto scroll-smooth',\n 'w-full gap-(--scrolling-list-gap) default:flex default:flex-row',\n 'focus-visible:u-outline',\n className\n )}\n ref={mergeRefs<HTMLDivElement>(ctx.scrollAreaRef, ctx.scrollRef, ref)}\n style={inlineStyles}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {Children.map(children, (child, index) =>\n isValidElement<ScrollingListItemProps>(child) ? cloneElement(child, { index }) : child\n )}\n </div>\n )\n}\n\nScrollingListItems.displayName = 'ScrollingList.Items'\n","import { ArrowVerticalRight } from '@spark-ui/icons/ArrowVerticalRight'\nimport { cx } from 'class-variance-authority'\nimport { useContext } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the next page of items. Renders a <button> element. */\nexport const ScrollingListNextButton = ({ 'aria-label': ariaLabel, ...rest }: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handleNextPage = () => {\n if (ctx.hasNextPage) {\n ctx.next({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n }\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.right)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-next-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handleNextPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalRight />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListNextButton.displayName = 'ScrollingList.NextButton'\n","import { ArrowVerticalLeft } from '@spark-ui/icons/ArrowVerticalLeft'\nimport { cx } from 'class-variance-authority'\nimport { useContext } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the previous page of items. Renders a <button> element. */\nexport const ScrollingListPrevButton = ({\n 'aria-label': ariaLabel,\n\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handlePrevPage = () => {\n const shouldSnapFirstPage =\n ctx.activePageIndex === 0 && (ctx.scrollAreaRef.current?.scrollLeft || 0) > 0\n\n if (shouldSnapFirstPage) {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n } else if (ctx.hasPrevPage) {\n ctx.prev({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(ctx.pages.length - 1, { behavior: ctx.scrollBehavior })\n }\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.left)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-prev-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handlePrevPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalLeft />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListPrevButton.displayName = 'ScrollingList.PrevButton'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, useContext } from 'react'\n\nimport { Button } from '../button'\nimport { ScrollingListContext } from './ScrollingList'\n\ninterface Props extends ComponentPropsWithoutRef<'button'> {\n children: string\n}\n\n/** A button to skip keyboard navigation, improving accessibility. Renders a <button> element. */\nexport const ScrollingListSkipButton = ({ children, ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n return (\n <Button\n type=\"button\"\n design=\"tinted\"\n intent=\"surface\"\n tabIndex={0}\n className={cx(\n 'z-raised absolute top-1/2 left-0 -translate-y-1/2',\n 'not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0'\n )}\n onClick={ctx.skipKeyboardNavigation}\n {...rest}\n >\n {children}\n </Button>\n )\n}\n\nScrollingListSkipButton.displayName = 'ScrollingList.SkipButton'\n","import { ScrollingList as Root } from './ScrollingList'\nimport { ScrollingListControls as Controls } from './ScrollingListControls'\nimport { ScrollingListItem as Item } from './ScrollingListItem'\nimport { ScrollingListItems as Items } from './ScrollingListItems'\nimport { ScrollingListNextButton as NextButton } from './ScrollingListNextButton'\nimport { ScrollingListPrevButton as PrevButton } from './ScrollingListPrevButton'\nimport { ScrollingListSkipButton as SkipButton } from './ScrollingListSkipButton'\n\n/**\n * A horizontal scrollable list component with optional snap points and navigation controls.\n */\nexport const ScrollingList: typeof Root & {\n Controls: typeof Controls\n NextButton: typeof NextButton\n PrevButton: typeof PrevButton\n Item: typeof Item\n Items: typeof Items\n SkipButton: typeof SkipButton\n} = Object.assign(Root, {\n Controls,\n NextButton,\n PrevButton,\n Item,\n Items,\n SkipButton,\n})\n\nScrollingList.displayName = 'ScrollingList'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/scrolling-list/ScrollingList.tsx","../../src/scrolling-list/ScrollingListControls.tsx","../../src/scrolling-list/useFocusWithinScroll.tsx","../../src/scrolling-list/ScrollingListItem.tsx","../../src/scrolling-list/ScrollingListItems.tsx","../../src/scrolling-list/ScrollingListNextButton.tsx","../../src/scrolling-list/ScrollingListPrevButton.tsx","../../src/scrolling-list/ScrollingListSkipButton.tsx","../../src/scrolling-list/index.ts"],"sourcesContent":["import { ScrollOverflow, useScrollOverflow } from '@spark-ui/hooks/use-scroll-overflow'\nimport { cx } from 'class-variance-authority'\nimport {\n ComponentPropsWithRef,\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n} from 'react'\nimport { SnapCarouselResult, useSnapCarousel } from 'react-snap-carousel'\n\ntype SnapType = 'mandatory' | 'proximity' | 'none'\ntype ScrollBehavior = 'smooth' | 'instant'\ntype SnapStop = 'normal' | 'always'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n /**\n * CSS scroll snap behavior.\n * - `mandatory` to force snapping on each \"page\".\n * - `proximity` to force snapping only when scroll position is near the edge of a \"page\". Behavior can change depending on each browser.\n * - `none` to disabled scroll snapping.\n */\n snapType?: SnapType\n /**\n * Defines whether or not the scroll container is allowed to \"pass over\" possible snap positions.\n */\n snapStop?: SnapStop\n scrollBehavior?: ScrollBehavior\n /**\n * Add a fade effect to indicate content overflow.\n */\n withFade?: boolean\n children?: ReactNode\n /**\n * When `true`, allow previous and next buttons to be used when reaching the edges of the list.\n */\n loop?: boolean\n /**\n * Space (in pixels) between items.\n */\n gap?: number\n /**\n * Offset (in pixels) of the left of the optimal viewing region of the list.\n */\n scrollPadding?: number\n className?: string\n}\n\ninterface ScrollingListContextState extends SnapCarouselResult {\n snapType: SnapType\n snapStop: SnapStop\n scrollBehavior: ScrollBehavior\n visibleItemsRange: readonly [number, number]\n loop: boolean\n gap: number\n withFade: boolean\n scrollPadding: number\n scrollAreaRef: RefObject<HTMLDivElement | null>\n overflow: ScrollOverflow\n skipKeyboardNavigation: () => void\n}\n\nexport const ScrollingListContext = createContext<ScrollingListContextState>(\n null as unknown as ScrollingListContextState\n)\n\nexport const ScrollingList = ({\n snapType = 'none',\n snapStop = 'normal',\n scrollBehavior = 'smooth',\n loop = false,\n gap = 16,\n withFade = false,\n scrollPadding = 0,\n children,\n className,\n ...rest\n}: Props) => {\n const scrollAreaRef = useRef<HTMLDivElement>(null)\n const skipAnchorRef = useRef<HTMLButtonElement>(null)\n\n const snapCarouselAPI = useSnapCarousel()\n\n const { overflow, refresh: refreshOverflow } = useScrollOverflow(scrollAreaRef, {\n precisionTreshold: 1,\n })\n\n const { activePageIndex, pages, refresh } = snapCarouselAPI\n\n const visibleItems = pages[activePageIndex] as number[]\n\n const visibleItemsRange = visibleItems\n ? ([visibleItems[0]! + 1, visibleItems[visibleItems.length - 1]! + 1] as const)\n : ([0, 0] as const)\n\n // Force refresh of the carousel API when children change\n const forceRefresh = useCallback(() => {\n if (refresh && scrollAreaRef.current) {\n // Small delay to ensure DOM is updated\n setTimeout(() => {\n refresh()\n }, 0)\n }\n }, [refresh])\n\n useEffect(() => {\n forceRefresh()\n }, [children, forceRefresh])\n\n useLayoutEffect(() => {\n if (scrollAreaRef.current) {\n // Use requestAnimationFrame to ensure proper timing with the render cycle\n // This prevents race conditions that occur when the console is closed\n requestAnimationFrame(() => {\n refreshOverflow()\n })\n }\n }, [children, refreshOverflow])\n\n const skipKeyboardNavigation = () => {\n skipAnchorRef.current?.focus()\n }\n\n const ctxValue: ScrollingListContextState = {\n ...snapCarouselAPI,\n snapType,\n snapStop,\n skipKeyboardNavigation,\n scrollBehavior,\n visibleItemsRange,\n loop,\n gap,\n withFade,\n scrollPadding,\n scrollAreaRef,\n overflow,\n }\n\n return (\n <ScrollingListContext value={ctxValue}>\n <div\n data-spark-component=\"scrolling-list\"\n className={cx(\n 'gap-lg group/scrolling-list relative flex flex-col default:w-full',\n className\n )}\n {...rest}\n >\n {children}\n </div>\n <span ref={skipAnchorRef} className=\"size-0 overflow-hidden\" tabIndex={-1} />\n </ScrollingListContext>\n )\n}\n\nScrollingList.displayName = 'ScrollingList'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'\n\ninterface ScrollingListControls extends ComponentPropsWithoutRef<'div'> {\n /**\n * Visibility behavior of the control buttons:\n * - `always`: buttons are always visible.\n * - `hover`: buttons only appear on hover.\n *\n * a11y: `hover` is dangerous for accessibility as it disabled controls for touch screen users.\n * When using it, you must provide an alternative control outside of the list to replace them.\n */\n visibility?: 'hover' | 'always'\n children: ReactNode\n}\n\n/** Container for navigation controls (previous/next buttons) of the scrolling list. Renders a <div> element. */\nexport const ScrollingListControls = ({\n children,\n visibility = 'always',\n className,\n ...rest\n}: ScrollingListControls) => {\n return (\n <div\n data-spark-component=\"scrolling-list-controls\"\n className={cx(\n 'default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden',\n className\n )}\n style={\n {\n '--scrolling-list-controls-opacity': visibility === 'hover' ? '0' : '1',\n } as CSSProperties\n }\n data-orientation=\"horizontal\"\n {...rest}\n >\n {children}\n </div>\n )\n}\n\nScrollingListControls.displayName = 'ScrollingList.Controls'\n","import { RefObject, useEffect, useState } from 'react'\n\nexport function useFocusWithinScroll<T extends HTMLElement | null>(\n ref: RefObject<T>, // The container to detect focus within\n scrollRef: RefObject<HTMLDivElement | null> // The scrollable container\n) {\n const [isFocusWithin, setIsFocusWithin] = useState(false)\n\n useEffect(() => {\n const handleFocusIn = (event: FocusEvent) => {\n setIsFocusWithin(true)\n\n const focusedElement = event.target as HTMLElement\n const scrollContainer = scrollRef.current\n\n if (focusedElement && scrollContainer) {\n const focusRect = focusedElement.getBoundingClientRect()\n const scrollRect = scrollContainer.getBoundingClientRect()\n\n // Check if the focused element is fully visible inside the scroll container\n const isFullyVisible =\n focusRect.left >= scrollRect.left &&\n focusRect.right <= scrollRect.right &&\n focusRect.top >= scrollRect.top &&\n focusRect.bottom <= scrollRect.bottom\n\n if (!isFullyVisible) {\n focusedElement.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })\n }\n }\n }\n\n const handleFocusOut = (event: FocusEvent) => {\n if (ref.current && !ref.current.contains(event.relatedTarget as Node)) {\n setIsFocusWithin(false)\n }\n }\n\n const node = ref.current\n if (node) {\n node.addEventListener('focusin', handleFocusIn)\n node.addEventListener('focusout', handleFocusOut)\n }\n\n return () => {\n if (node) {\n node.removeEventListener('focusin', handleFocusIn)\n node.removeEventListener('focusout', handleFocusOut)\n }\n }\n }, [ref, scrollRef])\n\n return isFocusWithin\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, ReactNode, useContext, useRef } from 'react'\n\nimport { Slot } from '../slot'\nimport { ScrollingListContext } from './ScrollingList'\nimport { useFocusWithinScroll } from './useFocusWithinScroll'\n\nexport interface ScrollingListItemProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n children?: ReactNode\n /**\n * DO NOT USE. This prop is automatically managed by the parent ScrollingList.ListItems\n */\n index?: number\n className?: string\n}\n\n/** A single item in the scrolling list. Renders a <div> element. */\nexport const ScrollingListItem = ({\n asChild = false,\n children,\n index = 0,\n className = '',\n ...rest\n}: ScrollingListItemProps) => {\n const ctx = useContext(ScrollingListContext)\n const itemRef = useRef<HTMLDivElement>(null)\n\n const isSnapPoint = ctx.snapPointIndexes.has(index)\n\n useFocusWithinScroll(itemRef, ctx.scrollAreaRef)\n\n const Component = asChild ? Slot : 'div'\n\n return (\n <Component\n data-spark-component=\"scrolling-list-item\"\n role=\"listitem\"\n ref={itemRef}\n className={cx(\n 'default:w-auto default:shrink-0',\n {\n 'snap-start': isSnapPoint,\n 'snap-normal': isSnapPoint && ctx.snapStop === 'normal',\n 'snap-always': isSnapPoint && ctx.snapStop === 'always',\n },\n className\n )}\n {...rest}\n >\n {children}\n </Component>\n )\n}\n\nScrollingListItem.displayName = 'ScrollingList.Item'\n","import { cx } from 'class-variance-authority'\nimport {\n Children,\n cloneElement,\n ComponentPropsWithRef,\n CSSProperties,\n isValidElement,\n KeyboardEvent,\n ReactNode,\n Ref,\n RefObject,\n useContext,\n} from 'react'\n\nimport { ScrollingListContext } from './ScrollingList'\nimport { ScrollingListItemProps } from './ScrollingListItem'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n children?: ReactNode\n className?: string\n}\n\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined | null)[]): Ref<T> {\n return (value: T | null) => {\n refs.forEach(ref => {\n if (typeof ref === 'function') {\n ref(value)\n } else if (ref && typeof ref === 'object' && 'current' in ref) {\n ;(ref as RefObject<T | null>).current = value\n }\n })\n }\n}\n\n/** The scrollable container for the list items. Renders a <div> element. */\nexport const ScrollingListItems = ({ children, ref, className = '', ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n const snapConfig = {\n mandatory: 'x mandatory',\n proximity: 'x proximity',\n none: 'none',\n }\n\n const handleLeftArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasPrevPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasPrevPage ? ctx.activePageIndex - 1 : ctx.pages.length - 1, {\n behavior: ctx.scrollBehavior,\n })\n }\n\n const handleRightArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasNextPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasNextPage ? ctx.activePageIndex + 1 : 0, { behavior: ctx.scrollBehavior })\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n handleLeftArrow(event)\n }\n\n if (event.key === 'ArrowRight') {\n handleRightArrow(event)\n }\n }\n\n interface CustomCSSProperties extends CSSProperties {\n '--scrolling-list-gap'?: string\n '--scrolling-list-px'?: string\n }\n\n const inlineStyles: CustomCSSProperties = {\n scrollSnapType: snapConfig[ctx.snapType],\n scrollPaddingInline: 'var(--scrolling-list-px)',\n '--scrolling-list-px': `${ctx.scrollPadding}px`,\n '--scrolling-list-gap': `${ctx.gap}px`,\n ...(ctx.withFade && {\n maskImage:\n 'linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))',\n maskSize: `calc(100% + ${ctx.overflow.left ? '0px' : '44px'} + ${ctx.overflow.right ? '0px' : '44px'}) 100%`,\n maskPosition: `${ctx.overflow.left ? '0px' : '-44px'} 0`,\n }),\n }\n\n return (\n <div\n data-spark-component=\"scrolling-list-items\"\n id=\"scrolling-list-items\"\n role=\"list\"\n className={cx(\n 'relative transition-all duration-300',\n 'u-no-scrollbar overflow-x-auto scroll-smooth',\n 'w-full gap-(--scrolling-list-gap) default:flex default:flex-row',\n 'focus-visible:u-outline',\n className\n )}\n ref={mergeRefs<HTMLDivElement>(ctx.scrollAreaRef, ctx.scrollRef, ref)}\n style={inlineStyles}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {Children.map(children, (child, index) =>\n isValidElement<ScrollingListItemProps>(child) ? cloneElement(child, { index }) : child\n )}\n </div>\n )\n}\n\nScrollingListItems.displayName = 'ScrollingList.Items'\n","import { ArrowVerticalRight } from '@spark-ui/icons/ArrowVerticalRight'\nimport { cx } from 'class-variance-authority'\nimport { useContext, MouseEvent } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the next page of items. Renders a <button> element. */\nexport const ScrollingListNextButton = ({\n 'aria-label': ariaLabel,\n onClick,\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handleNextPage = (e: MouseEvent<HTMLButtonElement>) => {\n if (ctx.hasNextPage) {\n ctx.next({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n }\n\n onClick?.(e)\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.right)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-next-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handleNextPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalRight />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListNextButton.displayName = 'ScrollingList.NextButton'\n","import { ArrowVerticalLeft } from '@spark-ui/icons/ArrowVerticalLeft'\nimport { cx } from 'class-variance-authority'\nimport { useContext, MouseEvent } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the previous page of items. Renders a <button> element. */\nexport const ScrollingListPrevButton = ({\n 'aria-label': ariaLabel,\n onClick,\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handlePrevPage = (e: MouseEvent<HTMLButtonElement>) => {\n const shouldSnapFirstPage =\n ctx.activePageIndex === 0 && (ctx.scrollAreaRef.current?.scrollLeft || 0) > 0\n\n if (shouldSnapFirstPage) {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n } else if (ctx.hasPrevPage) {\n ctx.prev({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(ctx.pages.length - 1, { behavior: ctx.scrollBehavior })\n }\n\n onClick?.(e)\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.left)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-prev-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handlePrevPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalLeft />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListPrevButton.displayName = 'ScrollingList.PrevButton'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, useContext } from 'react'\n\nimport { Button } from '../button'\nimport { ScrollingListContext } from './ScrollingList'\n\ninterface Props extends ComponentPropsWithoutRef<'button'> {\n children: string\n}\n\n/** A button to skip keyboard navigation, improving accessibility. Renders a <button> element. */\nexport const ScrollingListSkipButton = ({ children, ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n return (\n <Button\n type=\"button\"\n design=\"tinted\"\n intent=\"surface\"\n tabIndex={0}\n className={cx(\n 'z-raised absolute top-1/2 left-0 -translate-y-1/2',\n 'not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0'\n )}\n onClick={ctx.skipKeyboardNavigation}\n {...rest}\n >\n {children}\n </Button>\n )\n}\n\nScrollingListSkipButton.displayName = 'ScrollingList.SkipButton'\n","import { ScrollingList as Root } from './ScrollingList'\nimport { ScrollingListControls as Controls } from './ScrollingListControls'\nimport { ScrollingListItem as Item } from './ScrollingListItem'\nimport { ScrollingListItems as Items } from './ScrollingListItems'\nimport { ScrollingListNextButton as NextButton } from './ScrollingListNextButton'\nimport { ScrollingListPrevButton as PrevButton } from './ScrollingListPrevButton'\nimport { ScrollingListSkipButton as SkipButton } from './ScrollingListSkipButton'\n\n/**\n * A horizontal scrollable list component with optional snap points and navigation controls.\n */\nexport const ScrollingList: typeof Root & {\n Controls: typeof Controls\n NextButton: typeof NextButton\n PrevButton: typeof PrevButton\n Item: typeof Item\n Items: typeof Items\n SkipButton: typeof SkipButton\n} = Object.assign(Root, {\n Controls,\n NextButton,\n PrevButton,\n Item,\n Items,\n SkipButton,\n})\n\nScrollingList.displayName = 'ScrollingList'\n"],"mappings":"ggBAiEA,IAAa,GAAA,EAAA,EAAA,eACX,KACD,CAEY,GAAiB,CAC5B,WAAW,OACX,WAAW,SACX,iBAAiB,SACjB,OAAO,GACP,MAAM,GACN,WAAW,GACX,gBAAgB,EAChB,WACA,YACA,GAAG,KACQ,CACX,IAAM,GAAA,EAAA,EAAA,QAAuC,KAAK,CAC5C,GAAA,EAAA,EAAA,QAA0C,KAAK,CAE/C,GAAA,EAAA,EAAA,kBAAmC,CAEnC,CAAE,WAAU,QAAS,IAAA,EAAA,EAAA,mBAAsC,EAAe,CAC9E,kBAAmB,EACpB,CAAC,CAEI,CAAE,kBAAiB,QAAO,WAAY,EAEtC,EAAe,EAAM,GAErB,EAAoB,EACrB,CAAC,EAAa,GAAM,EAAG,EAAa,EAAa,OAAS,GAAM,EAAE,CAClE,CAAC,EAAG,EAAE,CAGL,GAAA,EAAA,EAAA,iBAAiC,CACjC,GAAW,EAAc,SAE3B,eAAiB,CACf,GAAS,EACR,EAAE,EAEN,CAAC,EAAQ,CAAC,EAEb,EAAA,EAAA,eAAgB,CACd,GAAc,EACb,CAAC,EAAU,EAAa,CAAC,EAE5B,EAAA,EAAA,qBAAsB,CAChB,EAAc,SAGhB,0BAA4B,CAC1B,GAAiB,EACjB,EAEH,CAAC,EAAU,EAAgB,CAAC,CAE/B,IAAM,MAA+B,CACnC,EAAc,SAAS,OAAO,EAkBhC,OACE,EAAA,EAAA,MAAC,EAAD,CAAsB,MAhBoB,CAC1C,GAAG,EACH,WACA,WACA,yBACA,iBACA,oBACA,OACA,MACA,WACA,gBACA,gBACA,WACD,UAGC,EACE,EAAA,EAAA,KAAC,MAAD,CACE,uBAAqB,iBACrB,WAAA,EAAA,EAAA,IACE,oEACA,EACD,CACD,GAAI,EAEH,WACG,CAAA,EACN,EAAA,EAAA,KAAC,OAAD,CAAM,IAAK,EAAe,UAAU,yBAAyB,SAAU,GAAM,CAAA,CACxD,IAI3B,EAAc,YAAc,gBC7I5B,IAAa,GAAyB,CACpC,WACA,aAAa,SACb,YACA,GAAG,MAGD,EAAA,EAAA,KAAC,MAAD,CACE,uBAAqB,0BACrB,WAAA,EAAA,EAAA,IACE,gHACA,EACD,CACD,MACE,CACE,oCAAqC,IAAe,QAAU,IAAM,IACrE,CAEH,mBAAiB,aACjB,GAAI,EAEH,WACG,CAAA,CAIV,EAAsB,YAAc,yBCzCpC,SAAgB,EACd,EACA,EACA,CACA,GAAM,CAAC,EAAe,IAAA,EAAA,EAAA,UAA6B,GAAM,CA8CzD,OA5CA,EAAA,EAAA,eAAgB,CACd,IAAM,EAAiB,GAAsB,CAC3C,EAAiB,GAAK,CAEtB,IAAM,EAAiB,EAAM,OACvB,EAAkB,EAAU,QAElC,GAAI,GAAkB,EAAiB,CACrC,IAAM,EAAY,EAAe,uBAAuB,CAClD,EAAa,EAAgB,uBAAuB,CAIxD,EAAU,MAAQ,EAAW,MAC7B,EAAU,OAAS,EAAW,OAC9B,EAAU,KAAO,EAAW,KAC5B,EAAU,QAAU,EAAW,QAG/B,EAAe,eAAe,CAAE,SAAU,SAAU,OAAQ,SAAU,MAAO,UAAW,CAAC,GAKzF,EAAkB,GAAsB,CACxC,EAAI,SAAW,CAAC,EAAI,QAAQ,SAAS,EAAM,cAAsB,EACnE,EAAiB,GAAM,EAIrB,EAAO,EAAI,QAMjB,OALI,IACF,EAAK,iBAAiB,UAAW,EAAc,CAC/C,EAAK,iBAAiB,WAAY,EAAe,MAGtC,CACP,IACF,EAAK,oBAAoB,UAAW,EAAc,CAClD,EAAK,oBAAoB,WAAY,EAAe,IAGvD,CAAC,EAAK,EAAU,CAAC,CAEb,EC/BT,IAAa,GAAqB,CAChC,UAAU,GACV,WACA,QAAQ,EACR,YAAY,GACZ,GAAG,KACyB,CAC5B,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAqB,CACtC,GAAA,EAAA,EAAA,QAAiC,KAAK,CAEtC,EAAc,EAAI,iBAAiB,IAAI,EAAM,CAMnD,OAJA,EAAqB,EAAS,EAAI,cAAc,EAK9C,EAAA,EAAA,KAHgB,EAAU,EAAA,KAAO,MAGjC,CACE,uBAAqB,sBACrB,KAAK,WACL,IAAK,EACL,WAAA,EAAA,EAAA,IACE,kCACA,CACE,aAAc,EACd,cAAe,GAAe,EAAI,WAAa,SAC/C,cAAe,GAAe,EAAI,WAAa,SAChD,CACD,EACD,CACD,GAAI,EAEH,WACS,CAAA,EAIhB,EAAkB,YAAc,qBCpChC,SAAgB,EAAa,GAAG,EAA6C,CAC3E,MAAQ,IAAoB,CAC1B,EAAK,QAAQ,GAAO,CACd,OAAO,GAAQ,WACjB,EAAI,EAAM,CACD,GAAO,OAAO,GAAQ,UAAY,YAAa,IACtD,EAA4B,QAAU,IAE1C,EAKN,IAAa,GAAsB,CAAE,WAAU,MAAK,YAAY,GAAI,GAAG,KAAkB,CACvF,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAqB,CAEtC,EAAa,CACjB,UAAW,cACX,UAAW,cACX,KAAM,OACP,CAEK,EAAmB,GAAyC,CAC5D,CAAC,EAAI,MAAQ,CAAC,EAAI,cAEtB,EAAM,gBAAgB,CACtB,EAAI,KAAK,EAAI,YAAc,EAAI,gBAAkB,EAAI,EAAI,MAAM,OAAS,EAAG,CACzE,SAAU,EAAI,eACf,CAAC,GAGE,EAAoB,GAAyC,CAC7D,CAAC,EAAI,MAAQ,CAAC,EAAI,cAEtB,EAAM,gBAAgB,CACtB,EAAI,KAAK,EAAI,YAAc,EAAI,gBAAkB,EAAI,EAAG,CAAE,SAAU,EAAI,eAAgB,CAAC,GAGrF,EAAiB,GAAyC,CAC1D,EAAM,MAAQ,aAChB,EAAgB,EAAM,CAGpB,EAAM,MAAQ,cAChB,EAAiB,EAAM,EASrB,EAAoC,CACxC,eAAgB,EAAW,EAAI,UAC/B,oBAAqB,2BACrB,sBAAuB,GAAG,EAAI,cAAc,IAC5C,uBAAwB,GAAG,EAAI,IAAI,IACnC,GAAI,EAAI,UAAY,CAClB,UACE,2HACF,SAAU,eAAe,EAAI,SAAS,KAAO,MAAQ,OAAO,KAAK,EAAI,SAAS,MAAQ,MAAQ,OAAO,QACrG,aAAc,GAAG,EAAI,SAAS,KAAO,MAAQ,QAAQ,IACtD,CACF,CAED,OACE,EAAA,EAAA,KAAC,MAAD,CACE,uBAAqB,uBACrB,GAAG,uBACH,KAAK,OACL,WAAA,EAAA,EAAA,IACE,uCACA,+CACA,kEACA,0BACA,EACD,CACD,IAAK,EAA0B,EAAI,cAAe,EAAI,UAAW,EAAI,CACrE,MAAO,EACP,UAAW,EACX,GAAI,WAEH,EAAA,SAAS,IAAI,GAAW,EAAO,KAAA,EAAA,EAAA,gBACS,EAAM,EAAA,EAAA,EAAA,cAAgB,EAAO,CAAE,QAAO,CAAC,CAAG,EAClF,CACG,CAAA,EAIV,EAAmB,YAAc,sBCvGjC,IAAa,GAA2B,CACtC,aAAc,EACd,UACA,GAAG,KACkB,CACrB,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAqB,CAEtC,EAAkB,GAAqC,CACvD,EAAI,YACN,EAAI,KAAK,CAAE,SAAU,EAAI,eAAgB,CAAC,CAE1C,EAAI,KAAK,EAAG,CAAE,SAAU,EAAI,eAAgB,CAAC,CAG/C,IAAU,EAAE,EAIR,EAAa,EADK,EAAI,SAAS,MAAQ,EAAI,SAAS,QAClB,CAAC,EAAI,MAAQ,CAAC,EAAI,SAAS,MAEnE,OACE,EAAA,EAAA,KAAC,EAAA,EAAD,CACE,uBAAqB,6BACrB,KAAK,KACL,OAAO,UACP,OAAO,SACP,WAAA,EAAA,EAAA,IACE,+FACA,qEACD,CACD,QAAS,EACT,SAAU,EACV,aAAY,EACZ,gBAAc,uBACd,GAAI,YAEJ,EAAA,EAAA,KAAC,EAAA,EAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAA,mBAAD,EAAsB,CAAA,CACjB,CAAA,CACI,CAAA,EAIjB,EAAwB,YAAc,2BC3CtC,IAAa,GAA2B,CACtC,aAAc,EACd,UACA,GAAG,KACkB,CACrB,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAqB,CAEtC,EAAkB,GAAqC,CAEzD,EAAI,kBAAoB,IAAM,EAAI,cAAc,SAAS,YAAc,GAAK,EAG5E,EAAI,KAAK,EAAG,CAAE,SAAU,EAAI,eAAgB,CAAC,CACpC,EAAI,YACb,EAAI,KAAK,CAAE,SAAU,EAAI,eAAgB,CAAC,CAE1C,EAAI,KAAK,EAAI,MAAM,OAAS,EAAG,CAAE,SAAU,EAAI,eAAgB,CAAC,CAGlE,IAAU,EAAE,EAIR,EAAa,EADK,EAAI,SAAS,MAAQ,EAAI,SAAS,QAClB,CAAC,EAAI,MAAQ,CAAC,EAAI,SAAS,KAEnE,OACE,EAAA,EAAA,KAAC,EAAA,EAAD,CACE,uBAAqB,6BACrB,KAAK,KACL,OAAO,UACP,OAAO,SACP,WAAA,EAAA,EAAA,IACE,+FACA,qEACD,CACD,QAAS,EACT,SAAU,EACV,aAAY,EACZ,gBAAc,uBACd,GAAI,YAEJ,EAAA,EAAA,KAAC,EAAA,EAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAA,kBAAD,EAAqB,CAAA,CAChB,CAAA,CACI,CAAA,EAIjB,EAAwB,YAAc,2BC9CtC,IAAa,GAA2B,CAAE,WAAU,GAAG,KAAkB,CACvE,IAAM,GAAA,EAAA,EAAA,YAAiB,EAAqB,CAE5C,OACE,EAAA,EAAA,KAAC,EAAA,EAAD,CACE,KAAK,SACL,OAAO,SACP,OAAO,UACP,SAAU,EACV,WAAA,EAAA,EAAA,IACE,oDACA,6FACD,CACD,QAAS,EAAI,uBACb,GAAI,EAEH,WACM,CAAA,EAIb,EAAwB,YAAc,2BCrBtC,IAAa,EAOT,OAAO,OAAO,EAAM,CACtB,SAAA,EACA,WAAA,EACA,WAAA,EACA,KAAA,EACA,MAAA,EACA,WAAA,EACD,CAAC,CAEF,EAAc,YAAc"}
@@ -153,42 +153,42 @@ var D = ({ children: e, ref: t, className: n = "", ...r }) => {
153
153
  D.displayName = "ScrollingList.Items";
154
154
  //#endregion
155
155
  //#region src/scrolling-list/ScrollingListNextButton.tsx
156
- var O = ({ "aria-label": e, ...n }) => {
157
- let a = u(x), o = () => {
158
- a.hasNextPage ? a.next({ behavior: a.scrollBehavior }) : a.goTo(0, { behavior: a.scrollBehavior });
159
- }, s = !(a.overflow.left || a.overflow.right) || !a.loop && !a.overflow.right;
156
+ var O = ({ "aria-label": e, onClick: n, ...a }) => {
157
+ let o = u(x), s = (e) => {
158
+ o.hasNextPage ? o.next({ behavior: o.scrollBehavior }) : o.goTo(0, { behavior: o.scrollBehavior }), n?.(e);
159
+ }, c = !(o.overflow.left || o.overflow.right) || !o.loop && !o.overflow.right;
160
160
  return /* @__PURE__ */ h(r, {
161
161
  "data-spark-component": "scrolling-list-next-button",
162
162
  size: "sm",
163
163
  intent: "surface",
164
164
  design: "filled",
165
165
  className: i("pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible", "group-hover/scrolling-list:opacity-none focus-visible:opacity-none"),
166
- onClick: o,
167
- disabled: s,
166
+ onClick: s,
167
+ disabled: c,
168
168
  "aria-label": e,
169
169
  "aria-controls": "scrolling-list-items",
170
- ...n,
170
+ ...a,
171
171
  children: /* @__PURE__ */ h(t, { children: /* @__PURE__ */ h(v, {}) })
172
172
  });
173
173
  };
174
174
  O.displayName = "ScrollingList.NextButton";
175
175
  //#endregion
176
176
  //#region src/scrolling-list/ScrollingListPrevButton.tsx
177
- var k = ({ "aria-label": e, ...n }) => {
178
- let a = u(x), o = () => {
179
- a.activePageIndex === 0 && (a.scrollAreaRef.current?.scrollLeft || 0) > 0 ? a.goTo(0, { behavior: a.scrollBehavior }) : a.hasPrevPage ? a.prev({ behavior: a.scrollBehavior }) : a.goTo(a.pages.length - 1, { behavior: a.scrollBehavior });
180
- }, s = !(a.overflow.left || a.overflow.right) || !a.loop && !a.overflow.left;
177
+ var k = ({ "aria-label": e, onClick: n, ...a }) => {
178
+ let o = u(x), s = (e) => {
179
+ o.activePageIndex === 0 && (o.scrollAreaRef.current?.scrollLeft || 0) > 0 ? o.goTo(0, { behavior: o.scrollBehavior }) : o.hasPrevPage ? o.prev({ behavior: o.scrollBehavior }) : o.goTo(o.pages.length - 1, { behavior: o.scrollBehavior }), n?.(e);
180
+ }, c = !(o.overflow.left || o.overflow.right) || !o.loop && !o.overflow.left;
181
181
  return /* @__PURE__ */ h(r, {
182
182
  "data-spark-component": "scrolling-list-prev-button",
183
183
  size: "sm",
184
184
  intent: "surface",
185
185
  design: "filled",
186
186
  className: i("pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible", "group-hover/scrolling-list:opacity-none focus-visible:opacity-none"),
187
- onClick: o,
188
- disabled: s,
187
+ onClick: s,
188
+ disabled: c,
189
189
  "aria-label": e,
190
190
  "aria-controls": "scrolling-list-items",
191
- ...n,
191
+ ...a,
192
192
  children: /* @__PURE__ */ h(t, { children: /* @__PURE__ */ h(y, {}) })
193
193
  });
194
194
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/scrolling-list/ScrollingList.tsx","../../src/scrolling-list/ScrollingListControls.tsx","../../src/scrolling-list/useFocusWithinScroll.tsx","../../src/scrolling-list/ScrollingListItem.tsx","../../src/scrolling-list/ScrollingListItems.tsx","../../src/scrolling-list/ScrollingListNextButton.tsx","../../src/scrolling-list/ScrollingListPrevButton.tsx","../../src/scrolling-list/ScrollingListSkipButton.tsx","../../src/scrolling-list/index.ts"],"sourcesContent":["import { ScrollOverflow, useScrollOverflow } from '@spark-ui/hooks/use-scroll-overflow'\nimport { cx } from 'class-variance-authority'\nimport {\n ComponentPropsWithRef,\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n} from 'react'\nimport { SnapCarouselResult, useSnapCarousel } from 'react-snap-carousel'\n\ntype SnapType = 'mandatory' | 'proximity' | 'none'\ntype ScrollBehavior = 'smooth' | 'instant'\ntype SnapStop = 'normal' | 'always'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n /**\n * CSS scroll snap behavior.\n * - `mandatory` to force snapping on each \"page\".\n * - `proximity` to force snapping only when scroll position is near the edge of a \"page\". Behavior can change depending on each browser.\n * - `none` to disabled scroll snapping.\n */\n snapType?: SnapType\n /**\n * Defines whether or not the scroll container is allowed to \"pass over\" possible snap positions.\n */\n snapStop?: SnapStop\n scrollBehavior?: ScrollBehavior\n /**\n * Add a fade effect to indicate content overflow.\n */\n withFade?: boolean\n children?: ReactNode\n /**\n * When `true`, allow previous and next buttons to be used when reaching the edges of the list.\n */\n loop?: boolean\n /**\n * Space (in pixels) between items.\n */\n gap?: number\n /**\n * Offset (in pixels) of the left of the optimal viewing region of the list.\n */\n scrollPadding?: number\n className?: string\n}\n\ninterface ScrollingListContextState extends SnapCarouselResult {\n snapType: SnapType\n snapStop: SnapStop\n scrollBehavior: ScrollBehavior\n visibleItemsRange: readonly [number, number]\n loop: boolean\n gap: number\n withFade: boolean\n scrollPadding: number\n scrollAreaRef: RefObject<HTMLDivElement | null>\n overflow: ScrollOverflow\n skipKeyboardNavigation: () => void\n}\n\nexport const ScrollingListContext = createContext<ScrollingListContextState>(\n null as unknown as ScrollingListContextState\n)\n\nexport const ScrollingList = ({\n snapType = 'none',\n snapStop = 'normal',\n scrollBehavior = 'smooth',\n loop = false,\n gap = 16,\n withFade = false,\n scrollPadding = 0,\n children,\n className,\n ...rest\n}: Props) => {\n const scrollAreaRef = useRef<HTMLDivElement>(null)\n const skipAnchorRef = useRef<HTMLButtonElement>(null)\n\n const snapCarouselAPI = useSnapCarousel()\n\n const { overflow, refresh: refreshOverflow } = useScrollOverflow(scrollAreaRef, {\n precisionTreshold: 1,\n })\n\n const { activePageIndex, pages, refresh } = snapCarouselAPI\n\n const visibleItems = pages[activePageIndex] as number[]\n\n const visibleItemsRange = visibleItems\n ? ([visibleItems[0]! + 1, visibleItems[visibleItems.length - 1]! + 1] as const)\n : ([0, 0] as const)\n\n // Force refresh of the carousel API when children change\n const forceRefresh = useCallback(() => {\n if (refresh && scrollAreaRef.current) {\n // Small delay to ensure DOM is updated\n setTimeout(() => {\n refresh()\n }, 0)\n }\n }, [refresh])\n\n useEffect(() => {\n forceRefresh()\n }, [children, forceRefresh])\n\n useLayoutEffect(() => {\n if (scrollAreaRef.current) {\n // Use requestAnimationFrame to ensure proper timing with the render cycle\n // This prevents race conditions that occur when the console is closed\n requestAnimationFrame(() => {\n refreshOverflow()\n })\n }\n }, [children, refreshOverflow])\n\n const skipKeyboardNavigation = () => {\n skipAnchorRef.current?.focus()\n }\n\n const ctxValue: ScrollingListContextState = {\n ...snapCarouselAPI,\n snapType,\n snapStop,\n skipKeyboardNavigation,\n scrollBehavior,\n visibleItemsRange,\n loop,\n gap,\n withFade,\n scrollPadding,\n scrollAreaRef,\n overflow,\n }\n\n return (\n <ScrollingListContext value={ctxValue}>\n <div\n data-spark-component=\"scrolling-list\"\n className={cx(\n 'gap-lg group/scrolling-list relative flex flex-col default:w-full',\n className\n )}\n {...rest}\n >\n {children}\n </div>\n <span ref={skipAnchorRef} className=\"size-0 overflow-hidden\" tabIndex={-1} />\n </ScrollingListContext>\n )\n}\n\nScrollingList.displayName = 'ScrollingList'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'\n\ninterface ScrollingListControls extends ComponentPropsWithoutRef<'div'> {\n /**\n * Visibility behavior of the control buttons:\n * - `always`: buttons are always visible.\n * - `hover`: buttons only appear on hover.\n *\n * a11y: `hover` is dangerous for accessibility as it disabled controls for touch screen users.\n * When using it, you must provide an alternative control outside of the list to replace them.\n */\n visibility?: 'hover' | 'always'\n children: ReactNode\n}\n\n/** Container for navigation controls (previous/next buttons) of the scrolling list. Renders a <div> element. */\nexport const ScrollingListControls = ({\n children,\n visibility = 'always',\n className,\n ...rest\n}: ScrollingListControls) => {\n return (\n <div\n data-spark-component=\"scrolling-list-controls\"\n className={cx(\n 'default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden',\n className\n )}\n style={\n {\n '--scrolling-list-controls-opacity': visibility === 'hover' ? '0' : '1',\n } as CSSProperties\n }\n data-orientation=\"horizontal\"\n {...rest}\n >\n {children}\n </div>\n )\n}\n\nScrollingListControls.displayName = 'ScrollingList.Controls'\n","import { RefObject, useEffect, useState } from 'react'\n\nexport function useFocusWithinScroll<T extends HTMLElement | null>(\n ref: RefObject<T>, // The container to detect focus within\n scrollRef: RefObject<HTMLDivElement | null> // The scrollable container\n) {\n const [isFocusWithin, setIsFocusWithin] = useState(false)\n\n useEffect(() => {\n const handleFocusIn = (event: FocusEvent) => {\n setIsFocusWithin(true)\n\n const focusedElement = event.target as HTMLElement\n const scrollContainer = scrollRef.current\n\n if (focusedElement && scrollContainer) {\n const focusRect = focusedElement.getBoundingClientRect()\n const scrollRect = scrollContainer.getBoundingClientRect()\n\n // Check if the focused element is fully visible inside the scroll container\n const isFullyVisible =\n focusRect.left >= scrollRect.left &&\n focusRect.right <= scrollRect.right &&\n focusRect.top >= scrollRect.top &&\n focusRect.bottom <= scrollRect.bottom\n\n if (!isFullyVisible) {\n focusedElement.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })\n }\n }\n }\n\n const handleFocusOut = (event: FocusEvent) => {\n if (ref.current && !ref.current.contains(event.relatedTarget as Node)) {\n setIsFocusWithin(false)\n }\n }\n\n const node = ref.current\n if (node) {\n node.addEventListener('focusin', handleFocusIn)\n node.addEventListener('focusout', handleFocusOut)\n }\n\n return () => {\n if (node) {\n node.removeEventListener('focusin', handleFocusIn)\n node.removeEventListener('focusout', handleFocusOut)\n }\n }\n }, [ref, scrollRef])\n\n return isFocusWithin\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, ReactNode, useContext, useRef } from 'react'\n\nimport { Slot } from '../slot'\nimport { ScrollingListContext } from './ScrollingList'\nimport { useFocusWithinScroll } from './useFocusWithinScroll'\n\nexport interface ScrollingListItemProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n children?: ReactNode\n /**\n * DO NOT USE. This prop is automatically managed by the parent ScrollingList.ListItems\n */\n index?: number\n className?: string\n}\n\n/** A single item in the scrolling list. Renders a <div> element. */\nexport const ScrollingListItem = ({\n asChild = false,\n children,\n index = 0,\n className = '',\n ...rest\n}: ScrollingListItemProps) => {\n const ctx = useContext(ScrollingListContext)\n const itemRef = useRef<HTMLDivElement>(null)\n\n const isSnapPoint = ctx.snapPointIndexes.has(index)\n\n useFocusWithinScroll(itemRef, ctx.scrollAreaRef)\n\n const Component = asChild ? Slot : 'div'\n\n return (\n <Component\n data-spark-component=\"scrolling-list-item\"\n role=\"listitem\"\n ref={itemRef}\n className={cx(\n 'default:w-auto default:shrink-0',\n {\n 'snap-start': isSnapPoint,\n 'snap-normal': isSnapPoint && ctx.snapStop === 'normal',\n 'snap-always': isSnapPoint && ctx.snapStop === 'always',\n },\n className\n )}\n {...rest}\n >\n {children}\n </Component>\n )\n}\n\nScrollingListItem.displayName = 'ScrollingList.Item'\n","import { cx } from 'class-variance-authority'\nimport {\n Children,\n cloneElement,\n ComponentPropsWithRef,\n CSSProperties,\n isValidElement,\n KeyboardEvent,\n ReactNode,\n Ref,\n RefObject,\n useContext,\n} from 'react'\n\nimport { ScrollingListContext } from './ScrollingList'\nimport { ScrollingListItemProps } from './ScrollingListItem'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n children?: ReactNode\n className?: string\n}\n\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined | null)[]): Ref<T> {\n return (value: T | null) => {\n refs.forEach(ref => {\n if (typeof ref === 'function') {\n ref(value)\n } else if (ref && typeof ref === 'object' && 'current' in ref) {\n ;(ref as RefObject<T | null>).current = value\n }\n })\n }\n}\n\n/** The scrollable container for the list items. Renders a <div> element. */\nexport const ScrollingListItems = ({ children, ref, className = '', ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n const snapConfig = {\n mandatory: 'x mandatory',\n proximity: 'x proximity',\n none: 'none',\n }\n\n const handleLeftArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasPrevPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasPrevPage ? ctx.activePageIndex - 1 : ctx.pages.length - 1, {\n behavior: ctx.scrollBehavior,\n })\n }\n\n const handleRightArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasNextPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasNextPage ? ctx.activePageIndex + 1 : 0, { behavior: ctx.scrollBehavior })\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n handleLeftArrow(event)\n }\n\n if (event.key === 'ArrowRight') {\n handleRightArrow(event)\n }\n }\n\n interface CustomCSSProperties extends CSSProperties {\n '--scrolling-list-gap'?: string\n '--scrolling-list-px'?: string\n }\n\n const inlineStyles: CustomCSSProperties = {\n scrollSnapType: snapConfig[ctx.snapType],\n scrollPaddingInline: 'var(--scrolling-list-px)',\n '--scrolling-list-px': `${ctx.scrollPadding}px`,\n '--scrolling-list-gap': `${ctx.gap}px`,\n ...(ctx.withFade && {\n maskImage:\n 'linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))',\n maskSize: `calc(100% + ${ctx.overflow.left ? '0px' : '44px'} + ${ctx.overflow.right ? '0px' : '44px'}) 100%`,\n maskPosition: `${ctx.overflow.left ? '0px' : '-44px'} 0`,\n }),\n }\n\n return (\n <div\n data-spark-component=\"scrolling-list-items\"\n id=\"scrolling-list-items\"\n role=\"list\"\n className={cx(\n 'relative transition-all duration-300',\n 'u-no-scrollbar overflow-x-auto scroll-smooth',\n 'w-full gap-(--scrolling-list-gap) default:flex default:flex-row',\n 'focus-visible:u-outline',\n className\n )}\n ref={mergeRefs<HTMLDivElement>(ctx.scrollAreaRef, ctx.scrollRef, ref)}\n style={inlineStyles}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {Children.map(children, (child, index) =>\n isValidElement<ScrollingListItemProps>(child) ? cloneElement(child, { index }) : child\n )}\n </div>\n )\n}\n\nScrollingListItems.displayName = 'ScrollingList.Items'\n","import { ArrowVerticalRight } from '@spark-ui/icons/ArrowVerticalRight'\nimport { cx } from 'class-variance-authority'\nimport { useContext } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the next page of items. Renders a <button> element. */\nexport const ScrollingListNextButton = ({ 'aria-label': ariaLabel, ...rest }: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handleNextPage = () => {\n if (ctx.hasNextPage) {\n ctx.next({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n }\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.right)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-next-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handleNextPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalRight />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListNextButton.displayName = 'ScrollingList.NextButton'\n","import { ArrowVerticalLeft } from '@spark-ui/icons/ArrowVerticalLeft'\nimport { cx } from 'class-variance-authority'\nimport { useContext } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the previous page of items. Renders a <button> element. */\nexport const ScrollingListPrevButton = ({\n 'aria-label': ariaLabel,\n\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handlePrevPage = () => {\n const shouldSnapFirstPage =\n ctx.activePageIndex === 0 && (ctx.scrollAreaRef.current?.scrollLeft || 0) > 0\n\n if (shouldSnapFirstPage) {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n } else if (ctx.hasPrevPage) {\n ctx.prev({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(ctx.pages.length - 1, { behavior: ctx.scrollBehavior })\n }\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.left)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-prev-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handlePrevPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalLeft />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListPrevButton.displayName = 'ScrollingList.PrevButton'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, useContext } from 'react'\n\nimport { Button } from '../button'\nimport { ScrollingListContext } from './ScrollingList'\n\ninterface Props extends ComponentPropsWithoutRef<'button'> {\n children: string\n}\n\n/** A button to skip keyboard navigation, improving accessibility. Renders a <button> element. */\nexport const ScrollingListSkipButton = ({ children, ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n return (\n <Button\n type=\"button\"\n design=\"tinted\"\n intent=\"surface\"\n tabIndex={0}\n className={cx(\n 'z-raised absolute top-1/2 left-0 -translate-y-1/2',\n 'not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0'\n )}\n onClick={ctx.skipKeyboardNavigation}\n {...rest}\n >\n {children}\n </Button>\n )\n}\n\nScrollingListSkipButton.displayName = 'ScrollingList.SkipButton'\n","import { ScrollingList as Root } from './ScrollingList'\nimport { ScrollingListControls as Controls } from './ScrollingListControls'\nimport { ScrollingListItem as Item } from './ScrollingListItem'\nimport { ScrollingListItems as Items } from './ScrollingListItems'\nimport { ScrollingListNextButton as NextButton } from './ScrollingListNextButton'\nimport { ScrollingListPrevButton as PrevButton } from './ScrollingListPrevButton'\nimport { ScrollingListSkipButton as SkipButton } from './ScrollingListSkipButton'\n\n/**\n * A horizontal scrollable list component with optional snap points and navigation controls.\n */\nexport const ScrollingList: typeof Root & {\n Controls: typeof Controls\n NextButton: typeof NextButton\n PrevButton: typeof PrevButton\n Item: typeof Item\n Items: typeof Items\n SkipButton: typeof SkipButton\n} = Object.assign(Root, {\n Controls,\n NextButton,\n PrevButton,\n Item,\n Items,\n SkipButton,\n})\n\nScrollingList.displayName = 'ScrollingList'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/scrolling-list/ScrollingList.tsx","../../src/scrolling-list/ScrollingListControls.tsx","../../src/scrolling-list/useFocusWithinScroll.tsx","../../src/scrolling-list/ScrollingListItem.tsx","../../src/scrolling-list/ScrollingListItems.tsx","../../src/scrolling-list/ScrollingListNextButton.tsx","../../src/scrolling-list/ScrollingListPrevButton.tsx","../../src/scrolling-list/ScrollingListSkipButton.tsx","../../src/scrolling-list/index.ts"],"sourcesContent":["import { ScrollOverflow, useScrollOverflow } from '@spark-ui/hooks/use-scroll-overflow'\nimport { cx } from 'class-variance-authority'\nimport {\n ComponentPropsWithRef,\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n} from 'react'\nimport { SnapCarouselResult, useSnapCarousel } from 'react-snap-carousel'\n\ntype SnapType = 'mandatory' | 'proximity' | 'none'\ntype ScrollBehavior = 'smooth' | 'instant'\ntype SnapStop = 'normal' | 'always'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n /**\n * CSS scroll snap behavior.\n * - `mandatory` to force snapping on each \"page\".\n * - `proximity` to force snapping only when scroll position is near the edge of a \"page\". Behavior can change depending on each browser.\n * - `none` to disabled scroll snapping.\n */\n snapType?: SnapType\n /**\n * Defines whether or not the scroll container is allowed to \"pass over\" possible snap positions.\n */\n snapStop?: SnapStop\n scrollBehavior?: ScrollBehavior\n /**\n * Add a fade effect to indicate content overflow.\n */\n withFade?: boolean\n children?: ReactNode\n /**\n * When `true`, allow previous and next buttons to be used when reaching the edges of the list.\n */\n loop?: boolean\n /**\n * Space (in pixels) between items.\n */\n gap?: number\n /**\n * Offset (in pixels) of the left of the optimal viewing region of the list.\n */\n scrollPadding?: number\n className?: string\n}\n\ninterface ScrollingListContextState extends SnapCarouselResult {\n snapType: SnapType\n snapStop: SnapStop\n scrollBehavior: ScrollBehavior\n visibleItemsRange: readonly [number, number]\n loop: boolean\n gap: number\n withFade: boolean\n scrollPadding: number\n scrollAreaRef: RefObject<HTMLDivElement | null>\n overflow: ScrollOverflow\n skipKeyboardNavigation: () => void\n}\n\nexport const ScrollingListContext = createContext<ScrollingListContextState>(\n null as unknown as ScrollingListContextState\n)\n\nexport const ScrollingList = ({\n snapType = 'none',\n snapStop = 'normal',\n scrollBehavior = 'smooth',\n loop = false,\n gap = 16,\n withFade = false,\n scrollPadding = 0,\n children,\n className,\n ...rest\n}: Props) => {\n const scrollAreaRef = useRef<HTMLDivElement>(null)\n const skipAnchorRef = useRef<HTMLButtonElement>(null)\n\n const snapCarouselAPI = useSnapCarousel()\n\n const { overflow, refresh: refreshOverflow } = useScrollOverflow(scrollAreaRef, {\n precisionTreshold: 1,\n })\n\n const { activePageIndex, pages, refresh } = snapCarouselAPI\n\n const visibleItems = pages[activePageIndex] as number[]\n\n const visibleItemsRange = visibleItems\n ? ([visibleItems[0]! + 1, visibleItems[visibleItems.length - 1]! + 1] as const)\n : ([0, 0] as const)\n\n // Force refresh of the carousel API when children change\n const forceRefresh = useCallback(() => {\n if (refresh && scrollAreaRef.current) {\n // Small delay to ensure DOM is updated\n setTimeout(() => {\n refresh()\n }, 0)\n }\n }, [refresh])\n\n useEffect(() => {\n forceRefresh()\n }, [children, forceRefresh])\n\n useLayoutEffect(() => {\n if (scrollAreaRef.current) {\n // Use requestAnimationFrame to ensure proper timing with the render cycle\n // This prevents race conditions that occur when the console is closed\n requestAnimationFrame(() => {\n refreshOverflow()\n })\n }\n }, [children, refreshOverflow])\n\n const skipKeyboardNavigation = () => {\n skipAnchorRef.current?.focus()\n }\n\n const ctxValue: ScrollingListContextState = {\n ...snapCarouselAPI,\n snapType,\n snapStop,\n skipKeyboardNavigation,\n scrollBehavior,\n visibleItemsRange,\n loop,\n gap,\n withFade,\n scrollPadding,\n scrollAreaRef,\n overflow,\n }\n\n return (\n <ScrollingListContext value={ctxValue}>\n <div\n data-spark-component=\"scrolling-list\"\n className={cx(\n 'gap-lg group/scrolling-list relative flex flex-col default:w-full',\n className\n )}\n {...rest}\n >\n {children}\n </div>\n <span ref={skipAnchorRef} className=\"size-0 overflow-hidden\" tabIndex={-1} />\n </ScrollingListContext>\n )\n}\n\nScrollingList.displayName = 'ScrollingList'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react'\n\ninterface ScrollingListControls extends ComponentPropsWithoutRef<'div'> {\n /**\n * Visibility behavior of the control buttons:\n * - `always`: buttons are always visible.\n * - `hover`: buttons only appear on hover.\n *\n * a11y: `hover` is dangerous for accessibility as it disabled controls for touch screen users.\n * When using it, you must provide an alternative control outside of the list to replace them.\n */\n visibility?: 'hover' | 'always'\n children: ReactNode\n}\n\n/** Container for navigation controls (previous/next buttons) of the scrolling list. Renders a <div> element. */\nexport const ScrollingListControls = ({\n children,\n visibility = 'always',\n className,\n ...rest\n}: ScrollingListControls) => {\n return (\n <div\n data-spark-component=\"scrolling-list-controls\"\n className={cx(\n 'default:px-md pointer-events-none absolute inset-0 flex flex-row items-center justify-between overflow-hidden',\n className\n )}\n style={\n {\n '--scrolling-list-controls-opacity': visibility === 'hover' ? '0' : '1',\n } as CSSProperties\n }\n data-orientation=\"horizontal\"\n {...rest}\n >\n {children}\n </div>\n )\n}\n\nScrollingListControls.displayName = 'ScrollingList.Controls'\n","import { RefObject, useEffect, useState } from 'react'\n\nexport function useFocusWithinScroll<T extends HTMLElement | null>(\n ref: RefObject<T>, // The container to detect focus within\n scrollRef: RefObject<HTMLDivElement | null> // The scrollable container\n) {\n const [isFocusWithin, setIsFocusWithin] = useState(false)\n\n useEffect(() => {\n const handleFocusIn = (event: FocusEvent) => {\n setIsFocusWithin(true)\n\n const focusedElement = event.target as HTMLElement\n const scrollContainer = scrollRef.current\n\n if (focusedElement && scrollContainer) {\n const focusRect = focusedElement.getBoundingClientRect()\n const scrollRect = scrollContainer.getBoundingClientRect()\n\n // Check if the focused element is fully visible inside the scroll container\n const isFullyVisible =\n focusRect.left >= scrollRect.left &&\n focusRect.right <= scrollRect.right &&\n focusRect.top >= scrollRect.top &&\n focusRect.bottom <= scrollRect.bottom\n\n if (!isFullyVisible) {\n focusedElement.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })\n }\n }\n }\n\n const handleFocusOut = (event: FocusEvent) => {\n if (ref.current && !ref.current.contains(event.relatedTarget as Node)) {\n setIsFocusWithin(false)\n }\n }\n\n const node = ref.current\n if (node) {\n node.addEventListener('focusin', handleFocusIn)\n node.addEventListener('focusout', handleFocusOut)\n }\n\n return () => {\n if (node) {\n node.removeEventListener('focusin', handleFocusIn)\n node.removeEventListener('focusout', handleFocusOut)\n }\n }\n }, [ref, scrollRef])\n\n return isFocusWithin\n}\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, ReactNode, useContext, useRef } from 'react'\n\nimport { Slot } from '../slot'\nimport { ScrollingListContext } from './ScrollingList'\nimport { useFocusWithinScroll } from './useFocusWithinScroll'\n\nexport interface ScrollingListItemProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n children?: ReactNode\n /**\n * DO NOT USE. This prop is automatically managed by the parent ScrollingList.ListItems\n */\n index?: number\n className?: string\n}\n\n/** A single item in the scrolling list. Renders a <div> element. */\nexport const ScrollingListItem = ({\n asChild = false,\n children,\n index = 0,\n className = '',\n ...rest\n}: ScrollingListItemProps) => {\n const ctx = useContext(ScrollingListContext)\n const itemRef = useRef<HTMLDivElement>(null)\n\n const isSnapPoint = ctx.snapPointIndexes.has(index)\n\n useFocusWithinScroll(itemRef, ctx.scrollAreaRef)\n\n const Component = asChild ? Slot : 'div'\n\n return (\n <Component\n data-spark-component=\"scrolling-list-item\"\n role=\"listitem\"\n ref={itemRef}\n className={cx(\n 'default:w-auto default:shrink-0',\n {\n 'snap-start': isSnapPoint,\n 'snap-normal': isSnapPoint && ctx.snapStop === 'normal',\n 'snap-always': isSnapPoint && ctx.snapStop === 'always',\n },\n className\n )}\n {...rest}\n >\n {children}\n </Component>\n )\n}\n\nScrollingListItem.displayName = 'ScrollingList.Item'\n","import { cx } from 'class-variance-authority'\nimport {\n Children,\n cloneElement,\n ComponentPropsWithRef,\n CSSProperties,\n isValidElement,\n KeyboardEvent,\n ReactNode,\n Ref,\n RefObject,\n useContext,\n} from 'react'\n\nimport { ScrollingListContext } from './ScrollingList'\nimport { ScrollingListItemProps } from './ScrollingListItem'\n\ninterface Props extends ComponentPropsWithRef<'div'> {\n children?: ReactNode\n className?: string\n}\n\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined | null)[]): Ref<T> {\n return (value: T | null) => {\n refs.forEach(ref => {\n if (typeof ref === 'function') {\n ref(value)\n } else if (ref && typeof ref === 'object' && 'current' in ref) {\n ;(ref as RefObject<T | null>).current = value\n }\n })\n }\n}\n\n/** The scrollable container for the list items. Renders a <div> element. */\nexport const ScrollingListItems = ({ children, ref, className = '', ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n const snapConfig = {\n mandatory: 'x mandatory',\n proximity: 'x proximity',\n none: 'none',\n }\n\n const handleLeftArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasPrevPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasPrevPage ? ctx.activePageIndex - 1 : ctx.pages.length - 1, {\n behavior: ctx.scrollBehavior,\n })\n }\n\n const handleRightArrow = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!ctx.loop && !ctx.hasNextPage) return\n\n event.preventDefault()\n ctx.goTo(ctx.hasNextPage ? ctx.activePageIndex + 1 : 0, { behavior: ctx.scrollBehavior })\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n handleLeftArrow(event)\n }\n\n if (event.key === 'ArrowRight') {\n handleRightArrow(event)\n }\n }\n\n interface CustomCSSProperties extends CSSProperties {\n '--scrolling-list-gap'?: string\n '--scrolling-list-px'?: string\n }\n\n const inlineStyles: CustomCSSProperties = {\n scrollSnapType: snapConfig[ctx.snapType],\n scrollPaddingInline: 'var(--scrolling-list-px)',\n '--scrolling-list-px': `${ctx.scrollPadding}px`,\n '--scrolling-list-gap': `${ctx.gap}px`,\n ...(ctx.withFade && {\n maskImage:\n 'linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 44px, rgba(0, 0, 0, 1) calc(100% - 44px), rgba(0, 0, 0, 0))',\n maskSize: `calc(100% + ${ctx.overflow.left ? '0px' : '44px'} + ${ctx.overflow.right ? '0px' : '44px'}) 100%`,\n maskPosition: `${ctx.overflow.left ? '0px' : '-44px'} 0`,\n }),\n }\n\n return (\n <div\n data-spark-component=\"scrolling-list-items\"\n id=\"scrolling-list-items\"\n role=\"list\"\n className={cx(\n 'relative transition-all duration-300',\n 'u-no-scrollbar overflow-x-auto scroll-smooth',\n 'w-full gap-(--scrolling-list-gap) default:flex default:flex-row',\n 'focus-visible:u-outline',\n className\n )}\n ref={mergeRefs<HTMLDivElement>(ctx.scrollAreaRef, ctx.scrollRef, ref)}\n style={inlineStyles}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {Children.map(children, (child, index) =>\n isValidElement<ScrollingListItemProps>(child) ? cloneElement(child, { index }) : child\n )}\n </div>\n )\n}\n\nScrollingListItems.displayName = 'ScrollingList.Items'\n","import { ArrowVerticalRight } from '@spark-ui/icons/ArrowVerticalRight'\nimport { cx } from 'class-variance-authority'\nimport { useContext, MouseEvent } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the next page of items. Renders a <button> element. */\nexport const ScrollingListNextButton = ({\n 'aria-label': ariaLabel,\n onClick,\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handleNextPage = (e: MouseEvent<HTMLButtonElement>) => {\n if (ctx.hasNextPage) {\n ctx.next({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n }\n\n onClick?.(e)\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.right)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-next-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handleNextPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalRight />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListNextButton.displayName = 'ScrollingList.NextButton'\n","import { ArrowVerticalLeft } from '@spark-ui/icons/ArrowVerticalLeft'\nimport { cx } from 'class-variance-authority'\nimport { useContext, MouseEvent } from 'react'\n\nimport { Icon } from '../icon'\nimport { IconButton, IconButtonProps } from '../icon-button'\nimport { ScrollingListContext } from './ScrollingList'\n\n/** A button to scroll to the previous page of items. Renders a <button> element. */\nexport const ScrollingListPrevButton = ({\n 'aria-label': ariaLabel,\n onClick,\n ...rest\n}: IconButtonProps) => {\n const ctx = useContext(ScrollingListContext)\n\n const handlePrevPage = (e: MouseEvent<HTMLButtonElement>) => {\n const shouldSnapFirstPage =\n ctx.activePageIndex === 0 && (ctx.scrollAreaRef.current?.scrollLeft || 0) > 0\n\n if (shouldSnapFirstPage) {\n ctx.goTo(0, { behavior: ctx.scrollBehavior })\n } else if (ctx.hasPrevPage) {\n ctx.prev({ behavior: ctx.scrollBehavior })\n } else {\n ctx.goTo(ctx.pages.length - 1, { behavior: ctx.scrollBehavior })\n }\n\n onClick?.(e)\n }\n\n const listHasOverflow = ctx.overflow.left || ctx.overflow.right\n const isDisabled = !listHasOverflow || (!ctx.loop && !ctx.overflow.left)\n\n return (\n <IconButton\n data-spark-component=\"scrolling-list-prev-button\"\n size=\"sm\"\n intent=\"surface\"\n design=\"filled\"\n className={cx(\n 'pointer-events-auto opacity-(--scrolling-list-controls-opacity) shadow-sm disabled:invisible',\n 'group-hover/scrolling-list:opacity-none focus-visible:opacity-none'\n )}\n onClick={handlePrevPage}\n disabled={isDisabled}\n aria-label={ariaLabel}\n aria-controls=\"scrolling-list-items\"\n {...rest}\n >\n <Icon>\n <ArrowVerticalLeft />\n </Icon>\n </IconButton>\n )\n}\n\nScrollingListPrevButton.displayName = 'ScrollingList.PrevButton'\n","import { cx } from 'class-variance-authority'\nimport { ComponentPropsWithoutRef, useContext } from 'react'\n\nimport { Button } from '../button'\nimport { ScrollingListContext } from './ScrollingList'\n\ninterface Props extends ComponentPropsWithoutRef<'button'> {\n children: string\n}\n\n/** A button to skip keyboard navigation, improving accessibility. Renders a <button> element. */\nexport const ScrollingListSkipButton = ({ children, ...rest }: Props) => {\n const ctx = useContext(ScrollingListContext)\n\n return (\n <Button\n type=\"button\"\n design=\"tinted\"\n intent=\"surface\"\n tabIndex={0}\n className={cx(\n 'z-raised absolute top-1/2 left-0 -translate-y-1/2',\n 'not-focus-visible:pointer-events-none not-focus-visible:size-0 not-focus-visible:opacity-0'\n )}\n onClick={ctx.skipKeyboardNavigation}\n {...rest}\n >\n {children}\n </Button>\n )\n}\n\nScrollingListSkipButton.displayName = 'ScrollingList.SkipButton'\n","import { ScrollingList as Root } from './ScrollingList'\nimport { ScrollingListControls as Controls } from './ScrollingListControls'\nimport { ScrollingListItem as Item } from './ScrollingListItem'\nimport { ScrollingListItems as Items } from './ScrollingListItems'\nimport { ScrollingListNextButton as NextButton } from './ScrollingListNextButton'\nimport { ScrollingListPrevButton as PrevButton } from './ScrollingListPrevButton'\nimport { ScrollingListSkipButton as SkipButton } from './ScrollingListSkipButton'\n\n/**\n * A horizontal scrollable list component with optional snap points and navigation controls.\n */\nexport const ScrollingList: typeof Root & {\n Controls: typeof Controls\n NextButton: typeof NextButton\n PrevButton: typeof PrevButton\n Item: typeof Item\n Items: typeof Items\n SkipButton: typeof SkipButton\n} = Object.assign(Root, {\n Controls,\n NextButton,\n PrevButton,\n Item,\n Items,\n SkipButton,\n})\n\nScrollingList.displayName = 'ScrollingList'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spark-ui/components",
3
- "version": "17.13.1",
3
+ "version": "17.13.2",
4
4
  "license": "MIT",
5
5
  "description": "Spark (Leboncoin design system) components.",
6
6
  "exports": {
@@ -48,9 +48,9 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@base-ui/react": "^1.5.0",
51
- "@spark-ui/hooks": "17.13.1",
52
- "@spark-ui/icons": "17.13.1",
53
- "@spark-ui/internal-utils": "17.13.1",
51
+ "@spark-ui/hooks": "17.13.2",
52
+ "@spark-ui/icons": "17.13.2",
53
+ "@spark-ui/internal-utils": "17.13.2",
54
54
  "@zag-js/pagination": "1.30.0",
55
55
  "@zag-js/react": "1.30.0",
56
56
  "class-variance-authority": "0.7.1",
@@ -62,7 +62,7 @@
62
62
  "react-snap-carousel": "0.5.1"
63
63
  },
64
64
  "peerDependencies": {
65
- "@spark-ui/theme-utils": "17.13.1",
65
+ "@spark-ui/theme-utils": "17.13.2",
66
66
  "react": "19.2.4",
67
67
  "react-dom": "19.2.4",
68
68
  "tailwindcss": "4.1.18"