UNPKG

@wordpress/compose

Version:
8 lines (7 loc) 8.62 kB
{ "version": 3, "sources": ["../../../src/hooks/use-fixed-window-list/index.js"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useState, useLayoutEffect } from '@wordpress/element';\nimport { getScrollContainer } from '@wordpress/dom';\nimport { PAGEUP, PAGEDOWN, HOME, END } from '@wordpress/keycodes';\n\n/**\n * Internal dependencies\n */\nimport { debounce } from '../../utils/debounce';\n\nconst DEFAULT_INIT_WINDOW_SIZE = 30;\n\n/**\n * @typedef {Object} WPFixedWindowList\n *\n * @property {number} visibleItems Items visible in the current viewport\n * @property {number} start Start index of the window\n * @property {number} end End index of the window\n * @property {(index:number)=>boolean} itemInView Returns true if item is in the window\n */\n\n/**\n * @typedef {Object} WPFixedWindowListOptions\n *\n * @property {number} [windowOverscan] Renders windowOverscan number of items before and after the calculated visible window.\n * @property {boolean} [useWindowing] When false avoids calculating the window size\n * @property {number} [initWindowSize] Initial window size to use on first render before we can calculate the window size.\n * @property {any} [expandedState] Used to recalculate the window size when the expanded state of a list changes.\n */\n\n/**\n *\n * @param {React.RefObject<HTMLElement>} elementRef Used to find the closest scroll container that contains element.\n * @param { number } itemHeight Fixed item height in pixels\n * @param { number } totalItems Total items in list\n * @param { WPFixedWindowListOptions } [options] Options object\n * @return {[ WPFixedWindowList, setFixedListWindow:(nextWindow:WPFixedWindowList)=>void]} Array with the fixed window list and setter\n */\nexport default function useFixedWindowList(\n\telementRef,\n\titemHeight,\n\ttotalItems,\n\toptions\n) {\n\tconst initWindowSize = options?.initWindowSize ?? DEFAULT_INIT_WINDOW_SIZE;\n\tconst useWindowing = options?.useWindowing ?? true;\n\n\tconst [ fixedListWindow, setFixedListWindow ] = useState( {\n\t\tvisibleItems: initWindowSize,\n\t\tstart: 0,\n\t\tend: initWindowSize,\n\t\titemInView: ( /** @type {number} */ index ) => {\n\t\t\treturn index >= 0 && index <= initWindowSize;\n\t\t},\n\t} );\n\n\tuseLayoutEffect( () => {\n\t\tif ( ! useWindowing ) {\n\t\t\treturn;\n\t\t}\n\t\tconst scrollContainer = getScrollContainer( elementRef.current );\n\t\t/**\n\t\t * Measures and sets the window of items to render based on the scroll position\n\t\t *\n\t\t * @param {boolean} [initRender] Indicates if this is the initial render\n\t\t * @return {void}\n\t\t */\n\t\tconst measureWindow = ( initRender ) => {\n\t\t\tif ( ! scrollContainer ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst visibleItems = Math.ceil(\n\t\t\t\tscrollContainer.clientHeight / itemHeight\n\t\t\t);\n\t\t\t// Aim to keep opening list view fast, afterward we can optimize for scrolling.\n\t\t\tconst windowOverscan = initRender\n\t\t\t\t? visibleItems\n\t\t\t\t: options?.windowOverscan ?? visibleItems;\n\t\t\tconst firstViewableIndex = Math.floor(\n\t\t\t\tscrollContainer.scrollTop / itemHeight\n\t\t\t);\n\t\t\tconst start = Math.max( 0, firstViewableIndex - windowOverscan );\n\t\t\tconst end = Math.min(\n\t\t\t\ttotalItems - 1,\n\t\t\t\tfirstViewableIndex + visibleItems + windowOverscan\n\t\t\t);\n\t\t\tsetFixedListWindow( ( lastWindow ) => {\n\t\t\t\tconst nextWindow = {\n\t\t\t\t\tvisibleItems,\n\t\t\t\t\tstart,\n\t\t\t\t\tend,\n\t\t\t\t\titemInView: ( /** @type {number} */ index ) => {\n\t\t\t\t\t\treturn start <= index && index <= end;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\tlastWindow.start !== nextWindow.start ||\n\t\t\t\t\tlastWindow.end !== nextWindow.end ||\n\t\t\t\t\tlastWindow.visibleItems !== nextWindow.visibleItems\n\t\t\t\t) {\n\t\t\t\t\treturn nextWindow;\n\t\t\t\t}\n\t\t\t\treturn lastWindow;\n\t\t\t} );\n\t\t};\n\n\t\tmeasureWindow( true );\n\t\tconst debounceMeasureList = debounce( () => {\n\t\t\tmeasureWindow();\n\t\t}, 16 );\n\t\tscrollContainer?.addEventListener( 'scroll', debounceMeasureList );\n\t\tscrollContainer?.ownerDocument?.defaultView?.addEventListener(\n\t\t\t'resize',\n\t\t\tdebounceMeasureList\n\t\t);\n\t\tscrollContainer?.ownerDocument?.defaultView?.addEventListener(\n\t\t\t'resize',\n\t\t\tdebounceMeasureList\n\t\t);\n\n\t\treturn () => {\n\t\t\tscrollContainer?.removeEventListener(\n\t\t\t\t'scroll',\n\t\t\t\tdebounceMeasureList\n\t\t\t);\n\t\t\tscrollContainer?.ownerDocument?.defaultView?.removeEventListener(\n\t\t\t\t'resize',\n\t\t\t\tdebounceMeasureList\n\t\t\t);\n\t\t};\n\t}, [\n\t\titemHeight,\n\t\telementRef,\n\t\ttotalItems,\n\t\toptions?.expandedState,\n\t\toptions?.windowOverscan,\n\t\tuseWindowing,\n\t] );\n\n\tuseLayoutEffect( () => {\n\t\tif ( ! useWindowing ) {\n\t\t\treturn;\n\t\t}\n\t\tconst scrollContainer = getScrollContainer( elementRef.current );\n\t\tconst handleKeyDown = ( /** @type {KeyboardEvent} */ event ) => {\n\t\t\tswitch ( event.keyCode ) {\n\t\t\t\tcase HOME: {\n\t\t\t\t\treturn scrollContainer?.scrollTo( { top: 0 } );\n\t\t\t\t}\n\t\t\t\tcase END: {\n\t\t\t\t\treturn scrollContainer?.scrollTo( {\n\t\t\t\t\t\ttop: totalItems * itemHeight,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t\tcase PAGEUP: {\n\t\t\t\t\treturn scrollContainer?.scrollTo( {\n\t\t\t\t\t\ttop:\n\t\t\t\t\t\t\tscrollContainer.scrollTop -\n\t\t\t\t\t\t\tfixedListWindow.visibleItems * itemHeight,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t\tcase PAGEDOWN: {\n\t\t\t\t\treturn scrollContainer?.scrollTo( {\n\t\t\t\t\t\ttop:\n\t\t\t\t\t\t\tscrollContainer.scrollTop +\n\t\t\t\t\t\t\tfixedListWindow.visibleItems * itemHeight,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tscrollContainer?.ownerDocument?.defaultView?.addEventListener(\n\t\t\t'keydown',\n\t\t\thandleKeyDown\n\t\t);\n\t\treturn () => {\n\t\t\tscrollContainer?.ownerDocument?.defaultView?.removeEventListener(\n\t\t\t\t'keydown',\n\t\t\t\thandleKeyDown\n\t\t\t);\n\t\t};\n\t}, [\n\t\ttotalItems,\n\t\titemHeight,\n\t\telementRef,\n\t\tfixedListWindow.visibleItems,\n\t\tuseWindowing,\n\t\toptions?.expandedState,\n\t] );\n\n\treturn [ fixedListWindow, setFixedListWindow ];\n}\n"], "mappings": "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", "names": [] }