@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 5.33 kB
Source Map (JSON)
{"version":3,"file":"VirtualScrollArea.mjs","names":[],"sources":["../../../src/base-ui/virtual/VirtualScrollArea.tsx"],"sourcesContent":["'use client';\n\nimport { createStaticStyles, cx } from 'antd-style';\nimport {\n Children,\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from 'react';\nimport { useMergeRefs } from 'react-merge-refs';\nimport { Virtualizer, type VirtualizerHandle } from 'virtua';\n\nimport {\n ScrollAreaRoot,\n type ScrollAreaRootProps,\n ScrollAreaScrollbar,\n ScrollAreaThumb,\n ScrollAreaViewport,\n type ScrollAreaViewportProps,\n} from '@/base-ui/ScrollArea/atoms';\n\n// `--lobe-virtual-scroll-inset` is the inline padding of the box the scroll area sits in: the\n// root bleeds out by it so the scrollbar hugs that box's edge, and the viewport pads the rows back.\nconst styles = createStaticStyles(({ css }) => ({\n root: css`\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-height: 0;\n margin-inline: calc(-1 * var(--lobe-virtual-scroll-inset, 0px));\n `,\n viewport: css`\n flex: 1;\n height: auto;\n min-height: 0;\n padding-inline: var(--lobe-virtual-scroll-inset, 0);\n `,\n}));\n\n// Rows mounted on the very first render, before the viewport is measured, so Base UI's\n// list navigation sees the leading rows (not a pinned footer item) when it focuses on open.\nconst INITIAL_ROW_COUNT = 20;\n\nexport interface VirtualScrollAreaProps extends Omit<ScrollAreaRootProps, 'children'> {\n children: ReactNode;\n itemSize?: number;\n keepMounted?: readonly number[];\n /**\n * Element that owns the scrolling box (e.g. a Base UI `Select.List`); it is rendered\n * through the ScrollArea viewport so its own props and role win.\n */\n viewport?: ReactElement<{ children?: ReactNode; render?: ReactElement }>;\n viewportProps?: ScrollAreaViewportProps;\n virtualizerRef?: Ref<VirtualizerHandle>;\n}\n\nexport const VirtualScrollArea = ({\n children,\n className,\n itemSize,\n keepMounted,\n viewport,\n viewportProps,\n virtualizerRef,\n ...rest\n}: VirtualScrollAreaProps) => {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const mergedViewportRef = useMergeRefs([viewportRef, viewportProps?.ref]);\n\n const list = (\n <Virtualizer\n itemSize={itemSize}\n keepMounted={keepMounted}\n ref={virtualizerRef}\n scrollRef={viewportRef}\n ssrCount={Math.min(INITIAL_ROW_COUNT, Children.count(children))}\n >\n {children}\n </Virtualizer>\n );\n const viewportElement = (\n <ScrollAreaViewport\n tabIndex={-1}\n {...viewportProps}\n className={cx(styles.viewport, viewportProps?.className as string)}\n ref={mergedViewportRef}\n />\n );\n\n return (\n <ScrollAreaRoot data-virtual=\"\" {...rest} className={cx(styles.root, className as string)}>\n {cloneElement(viewport ?? viewportElement, {\n children: list,\n ...(viewport && { render: viewportElement }),\n })}\n <ScrollAreaScrollbar>\n <ScrollAreaThumb />\n </ScrollAreaScrollbar>\n </ScrollAreaRoot>\n );\n};\n\nVirtualScrollArea.displayName = 'VirtualScrollArea';\n\n// Base UI scrolls the highlighted item into view after focusing it; while the pointer drives\n// the scroll that would yank the list back, so callers consult `pointerScrollRef` first.\nexport function usePointerScrollGuard() {\n const pointerScrollRef = useRef(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const markPointerScroll = useCallback(() => {\n pointerScrollRef.current = true;\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n pointerScrollRef.current = false;\n }, 120);\n }, []);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n return {\n markPointerScroll,\n pointerScrollRef,\n scrollGuardProps: {\n onPointerDown: markPointerScroll,\n onTouchMove: markPointerScroll,\n onWheel: markPointerScroll,\n },\n };\n}\n"],"mappings":";;;;;;;;AA2BA,MAAM,SAAS,oBAAoB,EAAE,WAAW;CAC9C,MAAM,GAAG;;;;;;;;CAQT,UAAU,GAAG;;;;;;AAMf,EAAE;AAIF,MAAM,oBAAoB;AAe1B,MAAa,qBAAqB,EAChC,UACA,WACA,UACA,aACA,UACA,eACA,gBACA,GAAG,WACyB;CAC5B,MAAM,cAAc,OAA8B,IAAI;CACtD,MAAM,oBAAoB,aAAa,CAAC,aAAa,eAAe,GAAG,CAAC;CAExE,MAAM,OACJ,oBAAC,aAAD;EACY;EACG;EACb,KAAK;EACL,WAAW;EACX,UAAU,KAAK,IAAI,mBAAmB,SAAS,MAAM,QAAQ,CAAC;EAE7D;CACU,CAAA;CAEf,MAAM,kBACJ,oBAAC,oBAAD;EACE,UAAU;EACV,GAAI;EACJ,WAAW,GAAG,OAAO,UAAU,eAAe,SAAmB;EACjE,KAAK;CACN,CAAA;CAGH,OACE,qBAAC,gBAAD;EAAgB,gBAAa;EAAG,GAAI;EAAM,WAAW,GAAG,OAAO,MAAM,SAAmB;EAAxF,UAAA,CACG,aAAa,YAAY,iBAAiB;GACzC,UAAU;GACV,GAAI,YAAY,EAAE,QAAQ,gBAAgB;EAC5C,CAAC,GACD,oBAAC,qBAAD,EAAA,UACE,oBAAC,iBAAD,CAAkB,CAAA,EACC,CAAA,CACP;;AAEpB;AAEA,kBAAkB,cAAc;AAIhC,SAAgB,wBAAwB;CACtC,MAAM,mBAAmB,OAAO,KAAK;CACrC,MAAM,aAAa,OAA6C,IAAI;CAEpE,MAAM,oBAAoB,kBAAkB;EAC1C,iBAAiB,UAAU;EAC3B,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACvD,WAAW,UAAU,iBAAiB;GACpC,iBAAiB,UAAU;EAC7B,GAAG,GAAG;CACR,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACzD;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;EACL;EACA;EACA,kBAAkB;GAChB,eAAe;GACf,aAAa;GACb,SAAS;EACX;CACF;AACF"}