@ai-matrx/design-system 0.18.22 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/content-transfer/alchemy.svg +1 -1
- package/dist/content-transfer/copy-transform.svg +1 -0
- package/dist/content-transfer/icon.cjs +8 -2
- package/dist/content-transfer/icon.cjs.map +1 -1
- package/dist/content-transfer/icon.d.cts +5 -3
- package/dist/content-transfer/icon.d.ts +5 -3
- package/dist/content-transfer/icon.js +8 -2
- package/dist/content-transfer/icon.js.map +1 -1
- package/dist/content-transfer/portal.svg +1 -0
- package/dist/content-transfer.cjs +923 -304
- package/dist/content-transfer.cjs.map +1 -1
- package/dist/content-transfer.css +17 -74
- package/dist/content-transfer.d.cts +14 -3
- package/dist/content-transfer.d.ts +14 -3
- package/dist/content-transfer.js +929 -305
- package/dist/content-transfer.js.map +1 -1
- package/dist/data-table/copy-helpers.d.cts +1 -1
- package/dist/data-table/copy-helpers.d.ts +1 -1
- package/dist/data-table/data-row-inspector.cjs +990 -397
- package/dist/data-table/data-row-inspector.cjs.map +1 -1
- package/dist/data-table/data-row-inspector.js +988 -390
- package/dist/data-table/data-row-inspector.js.map +1 -1
- package/dist/data-table/data-row-window.cjs +989 -396
- package/dist/data-table/data-row-window.cjs.map +1 -1
- package/dist/data-table/data-row-window.js +987 -389
- package/dist/data-table/data-row-window.js.map +1 -1
- package/dist/data-table/filter-engine.d.cts +1 -1
- package/dist/data-table/filter-engine.d.ts +1 -1
- package/dist/data-table/host.d.cts +1 -1
- package/dist/data-table/host.d.ts +1 -1
- package/dist/data-table/index.cjs +1628 -1007
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.d.cts +2 -2
- package/dist/data-table/index.d.ts +2 -2
- package/dist/data-table/index.js +1480 -854
- package/dist/data-table/index.js.map +1 -1
- package/dist/data-table/infer-filter.d.cts +1 -1
- package/dist/data-table/infer-filter.d.ts +1 -1
- package/dist/data-table/layered-filters.d.cts +1 -1
- package/dist/data-table/layered-filters.d.ts +1 -1
- package/dist/data-table/pagination.cjs +1 -1
- package/dist/data-table/pagination.cjs.map +1 -1
- package/dist/data-table/pagination.js +1 -1
- package/dist/data-table/pagination.js.map +1 -1
- package/dist/data-table/query-control.d.cts +1 -1
- package/dist/data-table/query-control.d.ts +1 -1
- package/dist/data-table/scroll-pagination.cjs +82 -54
- package/dist/data-table/scroll-pagination.cjs.map +1 -1
- package/dist/data-table/scroll-pagination.js +82 -54
- package/dist/data-table/scroll-pagination.js.map +1 -1
- package/dist/data-table/types.d.cts +1 -1
- package/dist/data-table/types.d.ts +1 -1
- package/dist/data-table/url-state.d.cts +1 -1
- package/dist/data-table/url-state.d.ts +1 -1
- package/dist/data-table/uuid-cell.cjs +1 -1
- package/dist/data-table/uuid-cell.cjs.map +1 -1
- package/dist/data-table/uuid-cell.js +1 -1
- package/dist/data-table/uuid-cell.js.map +1 -1
- package/dist/data-table/xlsx.d.cts +1 -1
- package/dist/data-table/xlsx.d.ts +1 -1
- package/dist/index.cjs +527 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +531 -0
- package/dist/index.js.map +1 -1
- package/dist/tap-target.cjs +636 -0
- package/dist/tap-target.cjs.map +1 -0
- package/dist/tap-target.css +151 -0
- package/dist/tap-target.d.cts +131 -0
- package/dist/tap-target.d.ts +131 -0
- package/dist/tap-target.js +608 -0
- package/dist/tap-target.js.map +1 -0
- package/dist/{types-Cbnj1Qlm.d.ts → types-D1m14boM.d.ts} +1 -1
- package/dist/{types-CWW4Tur-.d.cts → types-Di6QT0P1.d.cts} +1 -1
- package/package.json +14 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-table/useScrollPagination.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport type ScrollPaginationContainer =\n | HTMLElement\n | RefObject<HTMLElement | null>\n | null;\n\nexport interface UseScrollPaginationOptions {\n /** Every visible desktop/mobile scroll container that may present this table. */\n containers: readonly ScrollPaginationContainer[];\n /** Immutable identity of every source-affecting query value. */\n queryKey: string;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n loading: boolean;\n /** Automatic scrolling never retries a failed append. */\n error: Error | null;\n loadNextPage: () => Promise<void> | void;\n /** Manual mode exposes the explicit request only; it installs no listeners. */\n mode?: \"scroll\" | \"manual\";\n /** Distance from the bottom at which a qualifying user scroll may append. */\n thresholdPx?: number;\n /** How long an input may authorize the following real scroll movement. */\n intentTimeoutMs?: number;\n enabled?: boolean;\n}\n\nexport interface UseScrollPaginationResult {\n /** For the table's accessible Load more / Retry control. */\n requestNextPage: () => Promise<void>;\n canLoadMore: boolean;\n /** Includes an unexpected loader rejection until the person explicitly retries. */\n error: Error | null;\n}\n\ninterface ScrollIntent {\n queryKey: string;\n container: HTMLElement;\n startTop: number;\n sequence: number;\n expiresAt: number;\n}\n\nconst DEFAULT_THRESHOLD_PX = 96;\nconst DEFAULT_INTENT_TIMEOUT_MS = 1_200;\n\nfunction resolveContainer(container: ScrollPaginationContainer): HTMLElement | null {\n if (!container) return null;\n return container instanceof HTMLElement ? container : container.current;\n}\n\nfunction isVisibleContainer(container: HTMLElement): boolean {\n if (!container.isConnected) return false;\n for (let node: HTMLElement | null = container; node; node = node.parentElement) {\n const style = window.getComputedStyle(node);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n return target.isContentEditable || Boolean(target.closest(\"input, textarea, select, [contenteditable='true']\"));\n}\n\nfunction isDownwardKeyboardInput(event: KeyboardEvent): boolean {\n if (isEditableTarget(event.target)) return false;\n return [\"ArrowDown\", \"PageDown\", \"End\", \" \", \"Spacebar\"].includes(event.key);\n}\n\nfunction isNearBottom(container: HTMLElement, thresholdPx: number): boolean {\n return container.scrollHeight - container.clientHeight - container.scrollTop <= thresholdPx;\n}\n\nfunction isNativeScrollbarPointer(event: PointerEvent, container: HTMLElement): boolean {\n // Overlay scrollbars expose no measurable gutter. Do not guess: Load more is\n // the accessible, reliable fallback there. A content click must never grant\n // permission for a later programmatic scroll to append rows.\n if (event.pointerType !== \"mouse\" || event.button !== 0 || !event.isPrimary) return false;\n const gutterWidth = container.offsetWidth - container.clientWidth;\n if (gutterWidth <= 0 || container.scrollHeight <= container.clientHeight) return false;\n const rect = container.getBoundingClientRect();\n return event.clientX >= rect.right - gutterWidth && event.clientX <= rect.right;\n}\n\n/**\n * Gates automatic append behind a fresh, real downward user scroll. The hook\n * owns neither fetching nor row state: callers feed it the data hook receipt\n * and render its explicit request as the accessible fallback control.\n */\nexport function useScrollPagination({\n containers,\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode = \"scroll\",\n thresholdPx = DEFAULT_THRESHOLD_PX,\n intentTimeoutMs = DEFAULT_INTENT_TIMEOUT_MS,\n enabled = true,\n}: UseScrollPaginationOptions): UseScrollPaginationResult {\n const [requestError, setRequestError] = useState<Error | null>(null);\n const latestRef = useRef({\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n });\n latestRef.current = {\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n };\n\n const intentRef = useRef<ScrollIntent | null>(null);\n const inputSequenceRef = useRef(0);\n const consumedSequenceRef = useRef(0);\n const requestGenerationRef = useRef(0);\n const inFlightGenerationRef = useRef<number | null>(null);\n const requestErrorRef = useRef<Error | null>(null);\n const mountedRef = useRef(false);\n // Listener effects reattach when consumers recreate their container arrays.\n // Keep the last real scroll position with the element, not with one effect.\n const observedTopRef = useRef<WeakMap<HTMLElement, number>>(new WeakMap());\n const effectiveError = error ?? requestError;\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const requestNextPage = async () => {\n const current = latestRef.current;\n if (\n !current.enabled ||\n (!current.hasNextPage && effectiveError === null) ||\n current.loading ||\n current.isFetchingNextPage ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n const generation = ++requestGenerationRef.current;\n inFlightGenerationRef.current = generation;\n requestErrorRef.current = null;\n setRequestError(null);\n try {\n await current.loadNextPage();\n } catch (raw) {\n const nextError = raw instanceof Error ? raw : new Error(String(raw));\n if (inFlightGenerationRef.current === generation && latestRef.current.queryKey === current.queryKey) {\n requestErrorRef.current = nextError;\n if (mountedRef.current) setRequestError(nextError);\n }\n } finally {\n if (inFlightGenerationRef.current === generation) {\n inFlightGenerationRef.current = null;\n intentRef.current = null;\n }\n }\n };\n\n useEffect(() => {\n intentRef.current = null;\n observedTopRef.current = new WeakMap();\n consumedSequenceRef.current = inputSequenceRef.current;\n requestErrorRef.current = null;\n setRequestError(null);\n // A query change makes every existing completion stale. Its finally block\n // cannot clear a new request because generations are monotonically unique.\n inFlightGenerationRef.current = null;\n requestGenerationRef.current += 1;\n }, [queryKey]);\n\n useEffect(() => {\n if (mode !== \"scroll\" || !enabled || typeof window === \"undefined\") return;\n\n const attachable = containers\n .map(resolveContainer)\n .filter((container): container is HTMLElement => container !== null);\n\n const setIntent = (container: HTMLElement, startTop: number) => {\n const current = latestRef.current;\n if (!isVisibleContainer(container) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;\n inputSequenceRef.current += 1;\n intentRef.current = {\n queryKey: latestRef.current.queryKey,\n container,\n // The compositor can advance downward before the passive input\n // listener, while DOM/test callers can move upward before its scroll\n // notification. Either way, the lower position is the only safe\n // baseline for proving that this downward intent caused movement.\n startTop: Math.min(startTop, container.scrollTop),\n sequence: inputSequenceRef.current,\n expiresAt: Date.now() + intentTimeoutMs,\n };\n };\n\n const handleScroll = (container: HTMLElement) => {\n const intent = intentRef.current;\n const current = latestRef.current;\n if (\n !intent ||\n intent.queryKey !== current.queryKey ||\n intent.container !== container ||\n Date.now() > intent.expiresAt ||\n container.scrollTop <= intent.startTop ||\n intent.sequence <= consumedSequenceRef.current ||\n !isVisibleContainer(container) ||\n !isNearBottom(container, current.thresholdPx) ||\n !current.hasNextPage ||\n current.loading ||\n current.isFetchingNextPage ||\n current.error !== null ||\n requestErrorRef.current !== null ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n consumedSequenceRef.current = intent.sequence;\n void requestNextPage();\n };\n\n const cleanups = attachable.map((container) => {\n // A passive wheel listener may run after the compositor has already\n // advanced scrollTop, but before the asynchronous scroll notification.\n // Preserve the last *observed* position so that ordering still proves\n // movement from this trusted input without authorizing scroll-only code.\n if (!observedTopRef.current.has(container)) observedTopRef.current.set(container, container.scrollTop);\n const lastObservedTop = () => observedTopRef.current.get(container) ?? container.scrollTop;\n const onWheel = (event: WheelEvent) => {\n if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) {\n setIntent(container, lastObservedTop());\n }\n };\n const onTouchStart = (event: TouchEvent) => {\n const touch = event.touches.item(0);\n if (touch) container.dataset.matrxPaginationTouchY = String(touch.clientY);\n };\n const onTouchMove = (event: TouchEvent) => {\n const touch = event.touches.item(0);\n const startY = Number(container.dataset.matrxPaginationTouchY);\n if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) {\n setIntent(container, lastObservedTop());\n }\n };\n const onTouchEnd = () => {\n delete container.dataset.matrxPaginationTouchY;\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (isDownwardKeyboardInput(event)) setIntent(container, lastObservedTop());\n };\n const onPointerDown = (event: PointerEvent) => {\n if (isNativeScrollbarPointer(event, container)) setIntent(container, lastObservedTop());\n };\n const onScroll = () => {\n handleScroll(container);\n observedTopRef.current.set(container, container.scrollTop);\n };\n\n container.addEventListener(\"wheel\", onWheel, { passive: true });\n container.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n container.addEventListener(\"touchmove\", onTouchMove, { passive: true });\n container.addEventListener(\"touchend\", onTouchEnd, { passive: true });\n container.addEventListener(\"touchcancel\", onTouchEnd, { passive: true });\n container.addEventListener(\"keydown\", onKeyDown);\n container.addEventListener(\"pointerdown\", onPointerDown, { passive: true });\n container.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => {\n delete container.dataset.matrxPaginationTouchY;\n container.removeEventListener(\"wheel\", onWheel);\n container.removeEventListener(\"touchstart\", onTouchStart);\n container.removeEventListener(\"touchmove\", onTouchMove);\n container.removeEventListener(\"touchend\", onTouchEnd);\n container.removeEventListener(\"touchcancel\", onTouchEnd);\n container.removeEventListener(\"keydown\", onKeyDown);\n container.removeEventListener(\"pointerdown\", onPointerDown);\n container.removeEventListener(\"scroll\", onScroll);\n };\n });\n return () => cleanups.forEach((cleanup) => cleanup());\n }, [containers, enabled, intentTimeoutMs, mode]);\n\n return {\n requestNextPage,\n canLoadMore: enabled && !loading && !isFetchingNextPage && (hasNextPage || effectiveError !== null),\n error: effectiveError,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/useScrollPagination.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport type ScrollPaginationContainer =\n | HTMLElement\n | RefObject<HTMLElement | null>\n | null;\n\nexport interface UseScrollPaginationOptions {\n /** Every visible desktop/mobile scroll container that may present this table. */\n containers: readonly ScrollPaginationContainer[];\n /** Immutable identity of every source-affecting query value. */\n queryKey: string;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n loading: boolean;\n /** Automatic scrolling never retries a failed append. */\n error: Error | null;\n loadNextPage: () => Promise<void> | void;\n /** Manual mode exposes the explicit request only; it installs no listeners. */\n mode?: \"scroll\" | \"manual\";\n /** Distance from the bottom at which a qualifying user scroll may append. */\n thresholdPx?: number;\n /** How long an input may authorize the following real scroll movement. */\n intentTimeoutMs?: number;\n enabled?: boolean;\n}\n\nexport interface UseScrollPaginationResult {\n /** For the table's accessible Load more / Retry control. */\n requestNextPage: () => Promise<void>;\n canLoadMore: boolean;\n /** Includes an unexpected loader rejection until the person explicitly retries. */\n error: Error | null;\n}\n\ninterface ScrollOwner { element: HTMLElement; document: boolean; }\ninterface ScrollIntent {\n queryKey: string;\n source: HTMLElement;\n owner: ScrollOwner;\n startTop: number;\n sequence: number;\n expiresAt: number;\n}\n\nconst DEFAULT_THRESHOLD_PX = 96;\nconst DEFAULT_INTENT_TIMEOUT_MS = 1_200;\n\nfunction resolveContainer(container: ScrollPaginationContainer): HTMLElement | null {\n if (!container) return null;\n return container instanceof HTMLElement ? container : container.current;\n}\n\nfunction isVisibleContainer(container: HTMLElement): boolean {\n if (!container.isConnected) return false;\n for (let node: HTMLElement | null = container; node; node = node.parentElement) {\n const style = window.getComputedStyle(node);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n return target.isContentEditable || Boolean(target.closest(\"input, textarea, select, [contenteditable='true']\"));\n}\n\nfunction isDownwardKeyboardInput(event: KeyboardEvent): boolean {\n if (isEditableTarget(event.target)) return false;\n return [\"ArrowDown\", \"PageDown\", \"End\", \" \", \"Spacebar\"].includes(event.key);\n}\n\nfunction isNearBottom(container: HTMLElement, thresholdPx: number): boolean {\n return container.scrollHeight - container.clientHeight - container.scrollTop <= thresholdPx;\n}\n\nfunction isVerticallyScrollable(element: HTMLElement): boolean {\n const overflowY = window.getComputedStyle(element).overflowY;\n return (overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\") && element.scrollHeight > element.clientHeight;\n}\nfunction resolveScrollOwner(source: HTMLElement): ScrollOwner {\n for (let node: HTMLElement | null = source; node; node = node.parentElement) {\n if ((node === source && node.scrollHeight > node.clientHeight) || isVerticallyScrollable(node)) {\n return { element: node, document: false };\n }\n }\n return { element: document.scrollingElement instanceof HTMLElement ? document.scrollingElement : document.documentElement, document: true };\n}\nfunction ownerScrollTop(owner: ScrollOwner): number {\n return owner.document\n ? window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0\n : owner.element.scrollTop;\n}\nfunction sourceBottomIsNearOwnerViewport(source: HTMLElement, owner: ScrollOwner, thresholdPx: number): boolean {\n if (!owner.document && owner.element === source) return isNearBottom(source, thresholdPx);\n const sourceRect = source.getBoundingClientRect();\n const ownerRect = owner.document ? { top: 0, bottom: window.innerHeight } : owner.element.getBoundingClientRect();\n return sourceRect.bottom > ownerRect.top && sourceRect.top < ownerRect.bottom && sourceRect.bottom <= ownerRect.bottom + thresholdPx;\n}\n\nfunction isNativeScrollbarPointer(event: PointerEvent, container: HTMLElement): boolean {\n // Overlay scrollbars expose no measurable gutter. Do not guess: Load more is\n // the accessible, reliable fallback there. A content click must never grant\n // permission for a later programmatic scroll to append rows.\n if (event.pointerType !== \"mouse\" || event.button !== 0 || !event.isPrimary) return false;\n const gutterWidth = container.offsetWidth - container.clientWidth;\n if (gutterWidth <= 0 || container.scrollHeight <= container.clientHeight) return false;\n const rect = container.getBoundingClientRect();\n return event.clientX >= rect.right - gutterWidth && event.clientX <= rect.right;\n}\n\n/**\n * Gates automatic append behind a fresh, real downward user scroll. The hook\n * owns neither fetching nor row state: callers feed it the data hook receipt\n * and render its explicit request as the accessible fallback control.\n */\nexport function useScrollPagination({\n containers,\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode = \"scroll\",\n thresholdPx = DEFAULT_THRESHOLD_PX,\n intentTimeoutMs = DEFAULT_INTENT_TIMEOUT_MS,\n enabled = true,\n}: UseScrollPaginationOptions): UseScrollPaginationResult {\n const [requestError, setRequestError] = useState<Error | null>(null);\n const latestRef = useRef({\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n });\n latestRef.current = {\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n };\n\n const intentRef = useRef<ScrollIntent | null>(null);\n const inputSequenceRef = useRef(0);\n const consumedSequenceRef = useRef(0);\n const requestGenerationRef = useRef(0);\n const inFlightGenerationRef = useRef<number | null>(null);\n const requestErrorRef = useRef<Error | null>(null);\n const mountedRef = useRef(false);\n // Listener effects reattach when consumers recreate their container arrays.\n // Keep the last real scroll position with the element, not with one effect.\n const observedTopRef = useRef<WeakMap<HTMLElement, number>>(new WeakMap());\n const effectiveError = error ?? requestError;\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const requestNextPage = async () => {\n const current = latestRef.current;\n if (\n !current.enabled ||\n (!current.hasNextPage && effectiveError === null) ||\n current.loading ||\n current.isFetchingNextPage ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n const generation = ++requestGenerationRef.current;\n inFlightGenerationRef.current = generation;\n requestErrorRef.current = null;\n setRequestError(null);\n try {\n await current.loadNextPage();\n } catch (raw) {\n const nextError = raw instanceof Error ? raw : new Error(String(raw));\n if (inFlightGenerationRef.current === generation && latestRef.current.queryKey === current.queryKey) {\n requestErrorRef.current = nextError;\n if (mountedRef.current) setRequestError(nextError);\n }\n } finally {\n if (inFlightGenerationRef.current === generation) {\n inFlightGenerationRef.current = null;\n intentRef.current = null;\n }\n }\n };\n\n useEffect(() => {\n intentRef.current = null;\n observedTopRef.current = new WeakMap();\n consumedSequenceRef.current = inputSequenceRef.current;\n requestErrorRef.current = null;\n setRequestError(null);\n // A query change makes every existing completion stale. Its finally block\n // cannot clear a new request because generations are monotonically unique.\n inFlightGenerationRef.current = null;\n requestGenerationRef.current += 1;\n }, [queryKey]);\n\n useEffect(() => {\n if (mode !== \"scroll\" || !enabled || typeof window === \"undefined\") return;\n const attachable = containers.map(resolveContainer).filter((container): container is HTMLElement => container !== null);\n const ownerCleanups = new Map<HTMLElement, () => void>();\n const handleScroll = (source: HTMLElement, owner: ScrollOwner) => {\n const intent = intentRef.current;\n const current = latestRef.current;\n if (!intent || intent.queryKey !== current.queryKey || intent.source !== source || intent.owner.element !== owner.element || intent.owner.document !== owner.document || Date.now() > intent.expiresAt || ownerScrollTop(owner) <= intent.startTop || intent.sequence <= consumedSequenceRef.current || !isVisibleContainer(source) || !sourceBottomIsNearOwnerViewport(source, owner, current.thresholdPx) || !current.hasNextPage || current.loading || current.isFetchingNextPage || current.error !== null || requestErrorRef.current !== null || inFlightGenerationRef.current !== null) return;\n consumedSequenceRef.current = intent.sequence;\n void requestNextPage();\n };\n const ensureOwnerListener = (owner: ScrollOwner) => {\n if (ownerCleanups.has(owner.element)) return;\n const onOwnerScroll = () => {\n const intent = intentRef.current;\n if (intent && intent.owner.element === owner.element && intent.owner.document === owner.document) handleScroll(intent.source, owner);\n observedTopRef.current.set(owner.element, ownerScrollTop(owner));\n };\n if (owner.document) window.addEventListener(\"scroll\", onOwnerScroll, { passive: true });\n else owner.element.addEventListener(\"scroll\", onOwnerScroll, { passive: true });\n ownerCleanups.set(owner.element, () => {\n if (owner.document) window.removeEventListener(\"scroll\", onOwnerScroll);\n else owner.element.removeEventListener(\"scroll\", onOwnerScroll);\n });\n };\n const setIntent = (source: HTMLElement, startTop: number) => {\n const current = latestRef.current;\n const owner = resolveScrollOwner(source);\n if (!isVisibleContainer(source) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;\n inputSequenceRef.current += 1;\n const currentOwnerTop = ownerScrollTop(owner);\n const observedOwnerTop = observedTopRef.current.get(owner.element) ?? currentOwnerTop;\n observedTopRef.current.set(owner.element, currentOwnerTop);\n intentRef.current = { queryKey: current.queryKey, source, owner, startTop: Math.min(observedOwnerTop, currentOwnerTop), sequence: inputSequenceRef.current, expiresAt: Date.now() + intentTimeoutMs };\n ensureOwnerListener(owner);\n };\n const cleanups = attachable.map((source) => {\n if (!observedTopRef.current.has(source)) observedTopRef.current.set(source, source.scrollTop);\n const lastObservedTop = () => observedTopRef.current.get(source) ?? source.scrollTop;\n const onWheel = (event: WheelEvent) => { if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) setIntent(source, lastObservedTop()); };\n const onTouchStart = (event: TouchEvent) => { const touch = event.touches.item(0); if (touch) source.dataset.matrxPaginationTouchY = String(touch.clientY); };\n const onTouchMove = (event: TouchEvent) => { const touch = event.touches.item(0); const startY = Number(source.dataset.matrxPaginationTouchY); if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) setIntent(source, lastObservedTop()); };\n const onTouchEnd = () => { delete source.dataset.matrxPaginationTouchY; };\n const onKeyDown = (event: KeyboardEvent) => { if (isDownwardKeyboardInput(event)) setIntent(source, lastObservedTop()); };\n const onPointerDown = (event: PointerEvent) => { if (isNativeScrollbarPointer(event, source)) setIntent(source, lastObservedTop()); };\n const onScroll = () => { handleScroll(source, { element: source, document: false }); observedTopRef.current.set(source, source.scrollTop); };\n source.addEventListener(\"wheel\", onWheel, { passive: true }); source.addEventListener(\"touchstart\", onTouchStart, { passive: true }); source.addEventListener(\"touchmove\", onTouchMove, { passive: true }); source.addEventListener(\"touchend\", onTouchEnd, { passive: true }); source.addEventListener(\"touchcancel\", onTouchEnd, { passive: true }); source.addEventListener(\"keydown\", onKeyDown); source.addEventListener(\"pointerdown\", onPointerDown, { passive: true }); source.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => { delete source.dataset.matrxPaginationTouchY; source.removeEventListener(\"wheel\", onWheel); source.removeEventListener(\"touchstart\", onTouchStart); source.removeEventListener(\"touchmove\", onTouchMove); source.removeEventListener(\"touchend\", onTouchEnd); source.removeEventListener(\"touchcancel\", onTouchEnd); source.removeEventListener(\"keydown\", onKeyDown); source.removeEventListener(\"pointerdown\", onPointerDown); source.removeEventListener(\"scroll\", onScroll); };\n });\n return () => { cleanups.forEach((cleanup) => cleanup()); ownerCleanups.forEach((cleanup) => cleanup()); };\n }, [containers, enabled, intentTimeoutMs, mode]);\n\n return {\n requestNextPage,\n canLoadMore: enabled && !loading && !isFetchingNextPage && (hasNextPage || effectiveError !== null),\n error: effectiveError,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -28,6 +28,27 @@ function isDownwardKeyboardInput(event) {
|
|
|
28
28
|
function isNearBottom(container, thresholdPx) {
|
|
29
29
|
return container.scrollHeight - container.clientHeight - container.scrollTop <= thresholdPx;
|
|
30
30
|
}
|
|
31
|
+
function isVerticallyScrollable(element) {
|
|
32
|
+
const overflowY = window.getComputedStyle(element).overflowY;
|
|
33
|
+
return (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && element.scrollHeight > element.clientHeight;
|
|
34
|
+
}
|
|
35
|
+
function resolveScrollOwner(source) {
|
|
36
|
+
for (let node = source; node; node = node.parentElement) {
|
|
37
|
+
if (node === source && node.scrollHeight > node.clientHeight || isVerticallyScrollable(node)) {
|
|
38
|
+
return { element: node, document: false };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { element: document.scrollingElement instanceof HTMLElement ? document.scrollingElement : document.documentElement, document: true };
|
|
42
|
+
}
|
|
43
|
+
function ownerScrollTop(owner) {
|
|
44
|
+
return owner.document ? window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0 : owner.element.scrollTop;
|
|
45
|
+
}
|
|
46
|
+
function sourceBottomIsNearOwnerViewport(source, owner, thresholdPx) {
|
|
47
|
+
if (!owner.document && owner.element === source) return isNearBottom(source, thresholdPx);
|
|
48
|
+
const sourceRect = source.getBoundingClientRect();
|
|
49
|
+
const ownerRect = owner.document ? { top: 0, bottom: window.innerHeight } : owner.element.getBoundingClientRect();
|
|
50
|
+
return sourceRect.bottom > ownerRect.top && sourceRect.top < ownerRect.bottom && sourceRect.bottom <= ownerRect.bottom + thresholdPx;
|
|
51
|
+
}
|
|
31
52
|
function isNativeScrollbarPointer(event, container) {
|
|
32
53
|
if (event.pointerType !== "mouse" || event.button !== 0 || !event.isPrimary) return false;
|
|
33
54
|
const gutterWidth = container.offsetWidth - container.clientWidth;
|
|
@@ -122,84 +143,91 @@ function useScrollPagination({
|
|
|
122
143
|
useEffect(() => {
|
|
123
144
|
if (mode !== "scroll" || !enabled || typeof window === "undefined") return;
|
|
124
145
|
const attachable = containers.map(resolveContainer).filter((container) => container !== null);
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
if (!isVisibleContainer(container) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;
|
|
128
|
-
inputSequenceRef.current += 1;
|
|
129
|
-
intentRef.current = {
|
|
130
|
-
queryKey: latestRef.current.queryKey,
|
|
131
|
-
container,
|
|
132
|
-
// The compositor can advance downward before the passive input
|
|
133
|
-
// listener, while DOM/test callers can move upward before its scroll
|
|
134
|
-
// notification. Either way, the lower position is the only safe
|
|
135
|
-
// baseline for proving that this downward intent caused movement.
|
|
136
|
-
startTop: Math.min(startTop, container.scrollTop),
|
|
137
|
-
sequence: inputSequenceRef.current,
|
|
138
|
-
expiresAt: Date.now() + intentTimeoutMs
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
|
-
const handleScroll = (container) => {
|
|
146
|
+
const ownerCleanups = /* @__PURE__ */ new Map();
|
|
147
|
+
const handleScroll = (source, owner) => {
|
|
142
148
|
const intent = intentRef.current;
|
|
143
149
|
const current = latestRef.current;
|
|
144
|
-
if (!intent || intent.queryKey !== current.queryKey || intent.
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
150
|
+
if (!intent || intent.queryKey !== current.queryKey || intent.source !== source || intent.owner.element !== owner.element || intent.owner.document !== owner.document || Date.now() > intent.expiresAt || ownerScrollTop(owner) <= intent.startTop || intent.sequence <= consumedSequenceRef.current || !isVisibleContainer(source) || !sourceBottomIsNearOwnerViewport(source, owner, current.thresholdPx) || !current.hasNextPage || current.loading || current.isFetchingNextPage || current.error !== null || requestErrorRef.current !== null || inFlightGenerationRef.current !== null) return;
|
|
147
151
|
consumedSequenceRef.current = intent.sequence;
|
|
148
152
|
void requestNextPage();
|
|
149
153
|
};
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
const
|
|
154
|
+
const ensureOwnerListener = (owner) => {
|
|
155
|
+
if (ownerCleanups.has(owner.element)) return;
|
|
156
|
+
const onOwnerScroll = () => {
|
|
157
|
+
const intent = intentRef.current;
|
|
158
|
+
if (intent && intent.owner.element === owner.element && intent.owner.document === owner.document) handleScroll(intent.source, owner);
|
|
159
|
+
observedTopRef.current.set(owner.element, ownerScrollTop(owner));
|
|
160
|
+
};
|
|
161
|
+
if (owner.document) window.addEventListener("scroll", onOwnerScroll, { passive: true });
|
|
162
|
+
else owner.element.addEventListener("scroll", onOwnerScroll, { passive: true });
|
|
163
|
+
ownerCleanups.set(owner.element, () => {
|
|
164
|
+
if (owner.document) window.removeEventListener("scroll", onOwnerScroll);
|
|
165
|
+
else owner.element.removeEventListener("scroll", onOwnerScroll);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const setIntent = (source, startTop) => {
|
|
169
|
+
const current = latestRef.current;
|
|
170
|
+
const owner = resolveScrollOwner(source);
|
|
171
|
+
if (!isVisibleContainer(source) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;
|
|
172
|
+
inputSequenceRef.current += 1;
|
|
173
|
+
const currentOwnerTop = ownerScrollTop(owner);
|
|
174
|
+
const observedOwnerTop = observedTopRef.current.get(owner.element) ?? currentOwnerTop;
|
|
175
|
+
observedTopRef.current.set(owner.element, currentOwnerTop);
|
|
176
|
+
intentRef.current = { queryKey: current.queryKey, source, owner, startTop: Math.min(observedOwnerTop, currentOwnerTop), sequence: inputSequenceRef.current, expiresAt: Date.now() + intentTimeoutMs };
|
|
177
|
+
ensureOwnerListener(owner);
|
|
178
|
+
};
|
|
179
|
+
const cleanups = attachable.map((source) => {
|
|
180
|
+
if (!observedTopRef.current.has(source)) observedTopRef.current.set(source, source.scrollTop);
|
|
181
|
+
const lastObservedTop = () => observedTopRef.current.get(source) ?? source.scrollTop;
|
|
153
182
|
const onWheel = (event) => {
|
|
154
|
-
if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX))
|
|
155
|
-
setIntent(container, lastObservedTop());
|
|
156
|
-
}
|
|
183
|
+
if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) setIntent(source, lastObservedTop());
|
|
157
184
|
};
|
|
158
185
|
const onTouchStart = (event) => {
|
|
159
186
|
const touch = event.touches.item(0);
|
|
160
|
-
if (touch)
|
|
187
|
+
if (touch) source.dataset.matrxPaginationTouchY = String(touch.clientY);
|
|
161
188
|
};
|
|
162
189
|
const onTouchMove = (event) => {
|
|
163
190
|
const touch = event.touches.item(0);
|
|
164
|
-
const startY = Number(
|
|
165
|
-
if (touch && Number.isFinite(startY) && startY - touch.clientY > 0)
|
|
166
|
-
setIntent(container, lastObservedTop());
|
|
167
|
-
}
|
|
191
|
+
const startY = Number(source.dataset.matrxPaginationTouchY);
|
|
192
|
+
if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) setIntent(source, lastObservedTop());
|
|
168
193
|
};
|
|
169
194
|
const onTouchEnd = () => {
|
|
170
|
-
delete
|
|
195
|
+
delete source.dataset.matrxPaginationTouchY;
|
|
171
196
|
};
|
|
172
197
|
const onKeyDown = (event) => {
|
|
173
|
-
if (isDownwardKeyboardInput(event)) setIntent(
|
|
198
|
+
if (isDownwardKeyboardInput(event)) setIntent(source, lastObservedTop());
|
|
174
199
|
};
|
|
175
200
|
const onPointerDown = (event) => {
|
|
176
|
-
if (isNativeScrollbarPointer(event,
|
|
201
|
+
if (isNativeScrollbarPointer(event, source)) setIntent(source, lastObservedTop());
|
|
177
202
|
};
|
|
178
203
|
const onScroll = () => {
|
|
179
|
-
handleScroll(
|
|
180
|
-
observedTopRef.current.set(
|
|
204
|
+
handleScroll(source, { element: source, document: false });
|
|
205
|
+
observedTopRef.current.set(source, source.scrollTop);
|
|
181
206
|
};
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
207
|
+
source.addEventListener("wheel", onWheel, { passive: true });
|
|
208
|
+
source.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
209
|
+
source.addEventListener("touchmove", onTouchMove, { passive: true });
|
|
210
|
+
source.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
211
|
+
source.addEventListener("touchcancel", onTouchEnd, { passive: true });
|
|
212
|
+
source.addEventListener("keydown", onKeyDown);
|
|
213
|
+
source.addEventListener("pointerdown", onPointerDown, { passive: true });
|
|
214
|
+
source.addEventListener("scroll", onScroll, { passive: true });
|
|
190
215
|
return () => {
|
|
191
|
-
delete
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
216
|
+
delete source.dataset.matrxPaginationTouchY;
|
|
217
|
+
source.removeEventListener("wheel", onWheel);
|
|
218
|
+
source.removeEventListener("touchstart", onTouchStart);
|
|
219
|
+
source.removeEventListener("touchmove", onTouchMove);
|
|
220
|
+
source.removeEventListener("touchend", onTouchEnd);
|
|
221
|
+
source.removeEventListener("touchcancel", onTouchEnd);
|
|
222
|
+
source.removeEventListener("keydown", onKeyDown);
|
|
223
|
+
source.removeEventListener("pointerdown", onPointerDown);
|
|
224
|
+
source.removeEventListener("scroll", onScroll);
|
|
200
225
|
};
|
|
201
226
|
});
|
|
202
|
-
return () =>
|
|
227
|
+
return () => {
|
|
228
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
229
|
+
ownerCleanups.forEach((cleanup) => cleanup());
|
|
230
|
+
};
|
|
203
231
|
}, [containers, enabled, intentTimeoutMs, mode]);
|
|
204
232
|
return {
|
|
205
233
|
requestNextPage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-table/useScrollPagination.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport type ScrollPaginationContainer =\n | HTMLElement\n | RefObject<HTMLElement | null>\n | null;\n\nexport interface UseScrollPaginationOptions {\n /** Every visible desktop/mobile scroll container that may present this table. */\n containers: readonly ScrollPaginationContainer[];\n /** Immutable identity of every source-affecting query value. */\n queryKey: string;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n loading: boolean;\n /** Automatic scrolling never retries a failed append. */\n error: Error | null;\n loadNextPage: () => Promise<void> | void;\n /** Manual mode exposes the explicit request only; it installs no listeners. */\n mode?: \"scroll\" | \"manual\";\n /** Distance from the bottom at which a qualifying user scroll may append. */\n thresholdPx?: number;\n /** How long an input may authorize the following real scroll movement. */\n intentTimeoutMs?: number;\n enabled?: boolean;\n}\n\nexport interface UseScrollPaginationResult {\n /** For the table's accessible Load more / Retry control. */\n requestNextPage: () => Promise<void>;\n canLoadMore: boolean;\n /** Includes an unexpected loader rejection until the person explicitly retries. */\n error: Error | null;\n}\n\ninterface ScrollIntent {\n queryKey: string;\n container: HTMLElement;\n startTop: number;\n sequence: number;\n expiresAt: number;\n}\n\nconst DEFAULT_THRESHOLD_PX = 96;\nconst DEFAULT_INTENT_TIMEOUT_MS = 1_200;\n\nfunction resolveContainer(container: ScrollPaginationContainer): HTMLElement | null {\n if (!container) return null;\n return container instanceof HTMLElement ? container : container.current;\n}\n\nfunction isVisibleContainer(container: HTMLElement): boolean {\n if (!container.isConnected) return false;\n for (let node: HTMLElement | null = container; node; node = node.parentElement) {\n const style = window.getComputedStyle(node);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n return target.isContentEditable || Boolean(target.closest(\"input, textarea, select, [contenteditable='true']\"));\n}\n\nfunction isDownwardKeyboardInput(event: KeyboardEvent): boolean {\n if (isEditableTarget(event.target)) return false;\n return [\"ArrowDown\", \"PageDown\", \"End\", \" \", \"Spacebar\"].includes(event.key);\n}\n\nfunction isNearBottom(container: HTMLElement, thresholdPx: number): boolean {\n return container.scrollHeight - container.clientHeight - container.scrollTop <= thresholdPx;\n}\n\nfunction isNativeScrollbarPointer(event: PointerEvent, container: HTMLElement): boolean {\n // Overlay scrollbars expose no measurable gutter. Do not guess: Load more is\n // the accessible, reliable fallback there. A content click must never grant\n // permission for a later programmatic scroll to append rows.\n if (event.pointerType !== \"mouse\" || event.button !== 0 || !event.isPrimary) return false;\n const gutterWidth = container.offsetWidth - container.clientWidth;\n if (gutterWidth <= 0 || container.scrollHeight <= container.clientHeight) return false;\n const rect = container.getBoundingClientRect();\n return event.clientX >= rect.right - gutterWidth && event.clientX <= rect.right;\n}\n\n/**\n * Gates automatic append behind a fresh, real downward user scroll. The hook\n * owns neither fetching nor row state: callers feed it the data hook receipt\n * and render its explicit request as the accessible fallback control.\n */\nexport function useScrollPagination({\n containers,\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode = \"scroll\",\n thresholdPx = DEFAULT_THRESHOLD_PX,\n intentTimeoutMs = DEFAULT_INTENT_TIMEOUT_MS,\n enabled = true,\n}: UseScrollPaginationOptions): UseScrollPaginationResult {\n const [requestError, setRequestError] = useState<Error | null>(null);\n const latestRef = useRef({\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n });\n latestRef.current = {\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n };\n\n const intentRef = useRef<ScrollIntent | null>(null);\n const inputSequenceRef = useRef(0);\n const consumedSequenceRef = useRef(0);\n const requestGenerationRef = useRef(0);\n const inFlightGenerationRef = useRef<number | null>(null);\n const requestErrorRef = useRef<Error | null>(null);\n const mountedRef = useRef(false);\n // Listener effects reattach when consumers recreate their container arrays.\n // Keep the last real scroll position with the element, not with one effect.\n const observedTopRef = useRef<WeakMap<HTMLElement, number>>(new WeakMap());\n const effectiveError = error ?? requestError;\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const requestNextPage = async () => {\n const current = latestRef.current;\n if (\n !current.enabled ||\n (!current.hasNextPage && effectiveError === null) ||\n current.loading ||\n current.isFetchingNextPage ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n const generation = ++requestGenerationRef.current;\n inFlightGenerationRef.current = generation;\n requestErrorRef.current = null;\n setRequestError(null);\n try {\n await current.loadNextPage();\n } catch (raw) {\n const nextError = raw instanceof Error ? raw : new Error(String(raw));\n if (inFlightGenerationRef.current === generation && latestRef.current.queryKey === current.queryKey) {\n requestErrorRef.current = nextError;\n if (mountedRef.current) setRequestError(nextError);\n }\n } finally {\n if (inFlightGenerationRef.current === generation) {\n inFlightGenerationRef.current = null;\n intentRef.current = null;\n }\n }\n };\n\n useEffect(() => {\n intentRef.current = null;\n observedTopRef.current = new WeakMap();\n consumedSequenceRef.current = inputSequenceRef.current;\n requestErrorRef.current = null;\n setRequestError(null);\n // A query change makes every existing completion stale. Its finally block\n // cannot clear a new request because generations are monotonically unique.\n inFlightGenerationRef.current = null;\n requestGenerationRef.current += 1;\n }, [queryKey]);\n\n useEffect(() => {\n if (mode !== \"scroll\" || !enabled || typeof window === \"undefined\") return;\n\n const attachable = containers\n .map(resolveContainer)\n .filter((container): container is HTMLElement => container !== null);\n\n const setIntent = (container: HTMLElement, startTop: number) => {\n const current = latestRef.current;\n if (!isVisibleContainer(container) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;\n inputSequenceRef.current += 1;\n intentRef.current = {\n queryKey: latestRef.current.queryKey,\n container,\n // The compositor can advance downward before the passive input\n // listener, while DOM/test callers can move upward before its scroll\n // notification. Either way, the lower position is the only safe\n // baseline for proving that this downward intent caused movement.\n startTop: Math.min(startTop, container.scrollTop),\n sequence: inputSequenceRef.current,\n expiresAt: Date.now() + intentTimeoutMs,\n };\n };\n\n const handleScroll = (container: HTMLElement) => {\n const intent = intentRef.current;\n const current = latestRef.current;\n if (\n !intent ||\n intent.queryKey !== current.queryKey ||\n intent.container !== container ||\n Date.now() > intent.expiresAt ||\n container.scrollTop <= intent.startTop ||\n intent.sequence <= consumedSequenceRef.current ||\n !isVisibleContainer(container) ||\n !isNearBottom(container, current.thresholdPx) ||\n !current.hasNextPage ||\n current.loading ||\n current.isFetchingNextPage ||\n current.error !== null ||\n requestErrorRef.current !== null ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n consumedSequenceRef.current = intent.sequence;\n void requestNextPage();\n };\n\n const cleanups = attachable.map((container) => {\n // A passive wheel listener may run after the compositor has already\n // advanced scrollTop, but before the asynchronous scroll notification.\n // Preserve the last *observed* position so that ordering still proves\n // movement from this trusted input without authorizing scroll-only code.\n if (!observedTopRef.current.has(container)) observedTopRef.current.set(container, container.scrollTop);\n const lastObservedTop = () => observedTopRef.current.get(container) ?? container.scrollTop;\n const onWheel = (event: WheelEvent) => {\n if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) {\n setIntent(container, lastObservedTop());\n }\n };\n const onTouchStart = (event: TouchEvent) => {\n const touch = event.touches.item(0);\n if (touch) container.dataset.matrxPaginationTouchY = String(touch.clientY);\n };\n const onTouchMove = (event: TouchEvent) => {\n const touch = event.touches.item(0);\n const startY = Number(container.dataset.matrxPaginationTouchY);\n if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) {\n setIntent(container, lastObservedTop());\n }\n };\n const onTouchEnd = () => {\n delete container.dataset.matrxPaginationTouchY;\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (isDownwardKeyboardInput(event)) setIntent(container, lastObservedTop());\n };\n const onPointerDown = (event: PointerEvent) => {\n if (isNativeScrollbarPointer(event, container)) setIntent(container, lastObservedTop());\n };\n const onScroll = () => {\n handleScroll(container);\n observedTopRef.current.set(container, container.scrollTop);\n };\n\n container.addEventListener(\"wheel\", onWheel, { passive: true });\n container.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n container.addEventListener(\"touchmove\", onTouchMove, { passive: true });\n container.addEventListener(\"touchend\", onTouchEnd, { passive: true });\n container.addEventListener(\"touchcancel\", onTouchEnd, { passive: true });\n container.addEventListener(\"keydown\", onKeyDown);\n container.addEventListener(\"pointerdown\", onPointerDown, { passive: true });\n container.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => {\n delete container.dataset.matrxPaginationTouchY;\n container.removeEventListener(\"wheel\", onWheel);\n container.removeEventListener(\"touchstart\", onTouchStart);\n container.removeEventListener(\"touchmove\", onTouchMove);\n container.removeEventListener(\"touchend\", onTouchEnd);\n container.removeEventListener(\"touchcancel\", onTouchEnd);\n container.removeEventListener(\"keydown\", onKeyDown);\n container.removeEventListener(\"pointerdown\", onPointerDown);\n container.removeEventListener(\"scroll\", onScroll);\n };\n });\n return () => cleanups.forEach((cleanup) => cleanup());\n }, [containers, enabled, intentTimeoutMs, mode]);\n\n return {\n requestNextPage,\n canLoadMore: enabled && !loading && !isFetchingNextPage && (hasNextPage || effectiveError !== null),\n error: effectiveError,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/useScrollPagination.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\n\nexport type ScrollPaginationContainer =\n | HTMLElement\n | RefObject<HTMLElement | null>\n | null;\n\nexport interface UseScrollPaginationOptions {\n /** Every visible desktop/mobile scroll container that may present this table. */\n containers: readonly ScrollPaginationContainer[];\n /** Immutable identity of every source-affecting query value. */\n queryKey: string;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n loading: boolean;\n /** Automatic scrolling never retries a failed append. */\n error: Error | null;\n loadNextPage: () => Promise<void> | void;\n /** Manual mode exposes the explicit request only; it installs no listeners. */\n mode?: \"scroll\" | \"manual\";\n /** Distance from the bottom at which a qualifying user scroll may append. */\n thresholdPx?: number;\n /** How long an input may authorize the following real scroll movement. */\n intentTimeoutMs?: number;\n enabled?: boolean;\n}\n\nexport interface UseScrollPaginationResult {\n /** For the table's accessible Load more / Retry control. */\n requestNextPage: () => Promise<void>;\n canLoadMore: boolean;\n /** Includes an unexpected loader rejection until the person explicitly retries. */\n error: Error | null;\n}\n\ninterface ScrollOwner { element: HTMLElement; document: boolean; }\ninterface ScrollIntent {\n queryKey: string;\n source: HTMLElement;\n owner: ScrollOwner;\n startTop: number;\n sequence: number;\n expiresAt: number;\n}\n\nconst DEFAULT_THRESHOLD_PX = 96;\nconst DEFAULT_INTENT_TIMEOUT_MS = 1_200;\n\nfunction resolveContainer(container: ScrollPaginationContainer): HTMLElement | null {\n if (!container) return null;\n return container instanceof HTMLElement ? container : container.current;\n}\n\nfunction isVisibleContainer(container: HTMLElement): boolean {\n if (!container.isConnected) return false;\n for (let node: HTMLElement | null = container; node; node = node.parentElement) {\n const style = window.getComputedStyle(node);\n if (style.display === \"none\" || style.visibility === \"hidden\") return false;\n }\n return true;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n return target.isContentEditable || Boolean(target.closest(\"input, textarea, select, [contenteditable='true']\"));\n}\n\nfunction isDownwardKeyboardInput(event: KeyboardEvent): boolean {\n if (isEditableTarget(event.target)) return false;\n return [\"ArrowDown\", \"PageDown\", \"End\", \" \", \"Spacebar\"].includes(event.key);\n}\n\nfunction isNearBottom(container: HTMLElement, thresholdPx: number): boolean {\n return container.scrollHeight - container.clientHeight - container.scrollTop <= thresholdPx;\n}\n\nfunction isVerticallyScrollable(element: HTMLElement): boolean {\n const overflowY = window.getComputedStyle(element).overflowY;\n return (overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\") && element.scrollHeight > element.clientHeight;\n}\nfunction resolveScrollOwner(source: HTMLElement): ScrollOwner {\n for (let node: HTMLElement | null = source; node; node = node.parentElement) {\n if ((node === source && node.scrollHeight > node.clientHeight) || isVerticallyScrollable(node)) {\n return { element: node, document: false };\n }\n }\n return { element: document.scrollingElement instanceof HTMLElement ? document.scrollingElement : document.documentElement, document: true };\n}\nfunction ownerScrollTop(owner: ScrollOwner): number {\n return owner.document\n ? window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0\n : owner.element.scrollTop;\n}\nfunction sourceBottomIsNearOwnerViewport(source: HTMLElement, owner: ScrollOwner, thresholdPx: number): boolean {\n if (!owner.document && owner.element === source) return isNearBottom(source, thresholdPx);\n const sourceRect = source.getBoundingClientRect();\n const ownerRect = owner.document ? { top: 0, bottom: window.innerHeight } : owner.element.getBoundingClientRect();\n return sourceRect.bottom > ownerRect.top && sourceRect.top < ownerRect.bottom && sourceRect.bottom <= ownerRect.bottom + thresholdPx;\n}\n\nfunction isNativeScrollbarPointer(event: PointerEvent, container: HTMLElement): boolean {\n // Overlay scrollbars expose no measurable gutter. Do not guess: Load more is\n // the accessible, reliable fallback there. A content click must never grant\n // permission for a later programmatic scroll to append rows.\n if (event.pointerType !== \"mouse\" || event.button !== 0 || !event.isPrimary) return false;\n const gutterWidth = container.offsetWidth - container.clientWidth;\n if (gutterWidth <= 0 || container.scrollHeight <= container.clientHeight) return false;\n const rect = container.getBoundingClientRect();\n return event.clientX >= rect.right - gutterWidth && event.clientX <= rect.right;\n}\n\n/**\n * Gates automatic append behind a fresh, real downward user scroll. The hook\n * owns neither fetching nor row state: callers feed it the data hook receipt\n * and render its explicit request as the accessible fallback control.\n */\nexport function useScrollPagination({\n containers,\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode = \"scroll\",\n thresholdPx = DEFAULT_THRESHOLD_PX,\n intentTimeoutMs = DEFAULT_INTENT_TIMEOUT_MS,\n enabled = true,\n}: UseScrollPaginationOptions): UseScrollPaginationResult {\n const [requestError, setRequestError] = useState<Error | null>(null);\n const latestRef = useRef({\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n });\n latestRef.current = {\n queryKey,\n hasNextPage,\n isFetchingNextPage,\n loading,\n error,\n loadNextPage,\n mode,\n thresholdPx,\n enabled,\n };\n\n const intentRef = useRef<ScrollIntent | null>(null);\n const inputSequenceRef = useRef(0);\n const consumedSequenceRef = useRef(0);\n const requestGenerationRef = useRef(0);\n const inFlightGenerationRef = useRef<number | null>(null);\n const requestErrorRef = useRef<Error | null>(null);\n const mountedRef = useRef(false);\n // Listener effects reattach when consumers recreate their container arrays.\n // Keep the last real scroll position with the element, not with one effect.\n const observedTopRef = useRef<WeakMap<HTMLElement, number>>(new WeakMap());\n const effectiveError = error ?? requestError;\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const requestNextPage = async () => {\n const current = latestRef.current;\n if (\n !current.enabled ||\n (!current.hasNextPage && effectiveError === null) ||\n current.loading ||\n current.isFetchingNextPage ||\n inFlightGenerationRef.current !== null\n ) {\n return;\n }\n const generation = ++requestGenerationRef.current;\n inFlightGenerationRef.current = generation;\n requestErrorRef.current = null;\n setRequestError(null);\n try {\n await current.loadNextPage();\n } catch (raw) {\n const nextError = raw instanceof Error ? raw : new Error(String(raw));\n if (inFlightGenerationRef.current === generation && latestRef.current.queryKey === current.queryKey) {\n requestErrorRef.current = nextError;\n if (mountedRef.current) setRequestError(nextError);\n }\n } finally {\n if (inFlightGenerationRef.current === generation) {\n inFlightGenerationRef.current = null;\n intentRef.current = null;\n }\n }\n };\n\n useEffect(() => {\n intentRef.current = null;\n observedTopRef.current = new WeakMap();\n consumedSequenceRef.current = inputSequenceRef.current;\n requestErrorRef.current = null;\n setRequestError(null);\n // A query change makes every existing completion stale. Its finally block\n // cannot clear a new request because generations are monotonically unique.\n inFlightGenerationRef.current = null;\n requestGenerationRef.current += 1;\n }, [queryKey]);\n\n useEffect(() => {\n if (mode !== \"scroll\" || !enabled || typeof window === \"undefined\") return;\n const attachable = containers.map(resolveContainer).filter((container): container is HTMLElement => container !== null);\n const ownerCleanups = new Map<HTMLElement, () => void>();\n const handleScroll = (source: HTMLElement, owner: ScrollOwner) => {\n const intent = intentRef.current;\n const current = latestRef.current;\n if (!intent || intent.queryKey !== current.queryKey || intent.source !== source || intent.owner.element !== owner.element || intent.owner.document !== owner.document || Date.now() > intent.expiresAt || ownerScrollTop(owner) <= intent.startTop || intent.sequence <= consumedSequenceRef.current || !isVisibleContainer(source) || !sourceBottomIsNearOwnerViewport(source, owner, current.thresholdPx) || !current.hasNextPage || current.loading || current.isFetchingNextPage || current.error !== null || requestErrorRef.current !== null || inFlightGenerationRef.current !== null) return;\n consumedSequenceRef.current = intent.sequence;\n void requestNextPage();\n };\n const ensureOwnerListener = (owner: ScrollOwner) => {\n if (ownerCleanups.has(owner.element)) return;\n const onOwnerScroll = () => {\n const intent = intentRef.current;\n if (intent && intent.owner.element === owner.element && intent.owner.document === owner.document) handleScroll(intent.source, owner);\n observedTopRef.current.set(owner.element, ownerScrollTop(owner));\n };\n if (owner.document) window.addEventListener(\"scroll\", onOwnerScroll, { passive: true });\n else owner.element.addEventListener(\"scroll\", onOwnerScroll, { passive: true });\n ownerCleanups.set(owner.element, () => {\n if (owner.document) window.removeEventListener(\"scroll\", onOwnerScroll);\n else owner.element.removeEventListener(\"scroll\", onOwnerScroll);\n });\n };\n const setIntent = (source: HTMLElement, startTop: number) => {\n const current = latestRef.current;\n const owner = resolveScrollOwner(source);\n if (!isVisibleContainer(source) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;\n inputSequenceRef.current += 1;\n const currentOwnerTop = ownerScrollTop(owner);\n const observedOwnerTop = observedTopRef.current.get(owner.element) ?? currentOwnerTop;\n observedTopRef.current.set(owner.element, currentOwnerTop);\n intentRef.current = { queryKey: current.queryKey, source, owner, startTop: Math.min(observedOwnerTop, currentOwnerTop), sequence: inputSequenceRef.current, expiresAt: Date.now() + intentTimeoutMs };\n ensureOwnerListener(owner);\n };\n const cleanups = attachable.map((source) => {\n if (!observedTopRef.current.has(source)) observedTopRef.current.set(source, source.scrollTop);\n const lastObservedTop = () => observedTopRef.current.get(source) ?? source.scrollTop;\n const onWheel = (event: WheelEvent) => { if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) setIntent(source, lastObservedTop()); };\n const onTouchStart = (event: TouchEvent) => { const touch = event.touches.item(0); if (touch) source.dataset.matrxPaginationTouchY = String(touch.clientY); };\n const onTouchMove = (event: TouchEvent) => { const touch = event.touches.item(0); const startY = Number(source.dataset.matrxPaginationTouchY); if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) setIntent(source, lastObservedTop()); };\n const onTouchEnd = () => { delete source.dataset.matrxPaginationTouchY; };\n const onKeyDown = (event: KeyboardEvent) => { if (isDownwardKeyboardInput(event)) setIntent(source, lastObservedTop()); };\n const onPointerDown = (event: PointerEvent) => { if (isNativeScrollbarPointer(event, source)) setIntent(source, lastObservedTop()); };\n const onScroll = () => { handleScroll(source, { element: source, document: false }); observedTopRef.current.set(source, source.scrollTop); };\n source.addEventListener(\"wheel\", onWheel, { passive: true }); source.addEventListener(\"touchstart\", onTouchStart, { passive: true }); source.addEventListener(\"touchmove\", onTouchMove, { passive: true }); source.addEventListener(\"touchend\", onTouchEnd, { passive: true }); source.addEventListener(\"touchcancel\", onTouchEnd, { passive: true }); source.addEventListener(\"keydown\", onKeyDown); source.addEventListener(\"pointerdown\", onPointerDown, { passive: true }); source.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => { delete source.dataset.matrxPaginationTouchY; source.removeEventListener(\"wheel\", onWheel); source.removeEventListener(\"touchstart\", onTouchStart); source.removeEventListener(\"touchmove\", onTouchMove); source.removeEventListener(\"touchend\", onTouchEnd); source.removeEventListener(\"touchcancel\", onTouchEnd); source.removeEventListener(\"keydown\", onKeyDown); source.removeEventListener(\"pointerdown\", onPointerDown); source.removeEventListener(\"scroll\", onScroll); };\n });\n return () => { cleanups.forEach((cleanup) => cleanup()); ownerCleanups.forEach((cleanup) => cleanup()); };\n }, [containers, enabled, intentTimeoutMs, mode]);\n\n return {\n requestNextPage,\n canLoadMore: enabled && !loading && !isFetchingNextPage && (hasNextPage || effectiveError !== null),\n error: effectiveError,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'react';
|
|
2
|
-
export { A as AnyOfColumnSearch,
|
|
2
|
+
export { A as AnyOfColumnSearch, h as CellEditType, i as CellEditsMap, d as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, M as MatrxColumnDef, e as MatrxDataTableCopyConfig, j as MatrxDataTableDetailConfig, k as MatrxDataTableEditConfig, l as MatrxDataTableEmptyState, m as MatrxDataTableFilterUi, n as MatrxDataTableHierarchyConfig, o as MatrxDataTableHierarchyMove, p as MatrxDataTableMobileCardControls, f as MatrxDataTableProps, q as MatrxDataTableQueryControl, c as MatrxDataTableQueryState, r as MatrxDataTableRecordControls, s as MatrxDataTableSelectionConfig, t as MatrxDataTableSourceProcessing, u as MatrxDataTableToolbar, v as MatrxDataTableUrlStateConfig, w as MatrxDataTableWindowConfig, x as MatrxTablePagination, y as SortDirection, S as SortState, b as TableSavedViewsProps, T as TableSearchMatchMode, z as TableViewQuery, g as TableViewSnapshot, B as TextFilterMode, D as ToolbarFacet } from '../types-Di6QT0P1.cjs';
|
|
3
3
|
export { AgentPayloadInput } from './copy-types.cjs';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'react';
|
|
2
|
-
export { A as AnyOfColumnSearch,
|
|
2
|
+
export { A as AnyOfColumnSearch, h as CellEditType, i as CellEditsMap, d as ColumnFilterKind, a as ColumnFilterValue, C as ColumnFiltersState, M as MatrxColumnDef, e as MatrxDataTableCopyConfig, j as MatrxDataTableDetailConfig, k as MatrxDataTableEditConfig, l as MatrxDataTableEmptyState, m as MatrxDataTableFilterUi, n as MatrxDataTableHierarchyConfig, o as MatrxDataTableHierarchyMove, p as MatrxDataTableMobileCardControls, f as MatrxDataTableProps, q as MatrxDataTableQueryControl, c as MatrxDataTableQueryState, r as MatrxDataTableRecordControls, s as MatrxDataTableSelectionConfig, t as MatrxDataTableSourceProcessing, u as MatrxDataTableToolbar, v as MatrxDataTableUrlStateConfig, w as MatrxDataTableWindowConfig, x as MatrxTablePagination, y as SortDirection, S as SortState, b as TableSavedViewsProps, T as TableSearchMatchMode, z as TableViewQuery, g as TableViewSnapshot, B as TextFilterMode, D as ToolbarFacet } from '../types-D1m14boM.js';
|
|
3
3
|
export { AgentPayloadInput } from './copy-types.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SortState,
|
|
1
|
+
import { S as SortState, c as MatrxDataTableQueryState } from '../types-Di6QT0P1.cjs';
|
|
2
2
|
import { UrlHistoryMode } from '@ai-matrx/kit/url-state';
|
|
3
3
|
import 'react';
|
|
4
4
|
import './copy-types.cjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SortState,
|
|
1
|
+
import { S as SortState, c as MatrxDataTableQueryState } from '../types-D1m14boM.js';
|
|
2
2
|
import { UrlHistoryMode } from '@ai-matrx/kit/url-state';
|
|
3
3
|
import 'react';
|
|
4
4
|
import './copy-types.js';
|
|
@@ -41,7 +41,7 @@ var import_react3 = require("react");
|
|
|
41
41
|
// src/data-table/icons.tsx
|
|
42
42
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
43
43
|
function Icon({ children, ...props }) {
|
|
44
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...props, children });
|
|
44
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children });
|
|
45
45
|
}
|
|
46
46
|
var Check = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { ...p, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m5 12 4 4L19 6" }) });
|
|
47
47
|
var PanelRight = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Icon, { ...p, children: [
|