UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

133 lines 6.17 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /* eslint-disable @typescript-eslint/no-deprecated */ /** @packageDocumentation * @module Tree */ import "../../common/DisposePolyfill.js"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { MutableTreeModel, PagedTreeNodeLoader, TreeEventHandler, TreeModelSource, } from "@itwin/components-react"; import { PresentationTreeDataProvider } from "../DataProvider.js"; import { ReportingTreeNodeLoader } from "../ReportingTreeNodeLoader.js"; import { useFilteredNodeLoader, useNodeHighlightingProps } from "./UseControlledTreeFiltering.js"; import { useTreeReload } from "./UseTreeReload.js"; /** * Custom hook that creates and manages state for [ControlledTree]($components-react) component based on presentation data. * @public * @deprecated in 5.7. All tree-related APIs have been deprecated in favor of the new generation hierarchy * building APIs (see https://github.com/iTwin/presentation/blob/33e79ee8d77f30580a9bab81a72884bda008db25/README.md#the-packages). */ export function usePresentationTreeState({ onHierarchyLimitExceeded, onNodeLoaded, eventHandlerFactory, seedTreeModel, filteringParams, ...dataProviderProps }) { const firstRenderRef = useRef(true); const treeStateProps = useMemo(() => ({ ...dataProviderProps, treeModel: firstRenderRef.current ? seedTreeModel : undefined, }), Object.values(dataProviderProps)); const { state, onReload } = useTreeState({ treeStateProps, onNodeLoaded, onHierarchyLimitExceeded }); const renderedItems = useRef(undefined); /* c8 ignore next 3 */ const onItemsRendered = useCallback((items) => { renderedItems.current = items; }, []); useTreeReload({ pageSize: dataProviderProps.pagingSize, modelSource: state?.nodeLoader.modelSource, dataProviderProps: treeStateProps, ruleset: dataProviderProps.ruleset, onReload, renderedItems, }); const filteredTree = usePresentationTreeFiltering({ dataProvider: state?.nodeLoader.dataProvider, filter: filteringParams?.filter, activeMatchIndex: filteringParams?.activeMatchIndex, }); const activeNodeLoader = filteredTree?.filteredNodeLoader ?? state?.nodeLoader; const eventHandler = useEventHandler(eventHandlerFactory, activeNodeLoader); firstRenderRef.current = false; if (!activeNodeLoader || !eventHandler) { return undefined; } return { nodeLoader: activeNodeLoader, eventHandler, onItemsRendered, filteringResult: filteredTree ? { isFiltering: filteredTree.isFiltering, filteredProvider: filteredTree.filteredProvider, highlightProps: filteredTree.highlightProps, matchesCount: filteredTree.matchesCount, } : undefined, }; } function useTreeState(props) { const [state, setState] = useState(); const onNodeLoadedRef = useLatest(props.onNodeLoaded); const onHierarchyLimitExceededRef = useLatest(props.onHierarchyLimitExceeded); const prevStateRef = useLatest(state); useEffect(() => { const { treeModel, ...providerProps } = props.treeStateProps; const modelSource = new TreeModelSource(new MutableTreeModel(treeModel)); const dataProvider = new PresentationTreeDataProvider({ ...providerProps, onHierarchyLimitExceeded: () => onHierarchyLimitExceededRef.current?.() }); const pagedLoader = new PagedTreeNodeLoader(dataProvider, modelSource, providerProps.pagingSize); const nodeLoader = new ReportingTreeNodeLoader(pagedLoader, (nodeLoadedProps) => onNodeLoadedRef.current?.(nodeLoadedProps)); const newState = { modelSource, nodeLoader, dataProvider, }; setState(newState); return () => { // eslint-disable-next-line react-hooks/exhaustive-deps prevStateRef.current?.dataProvider[Symbol.dispose](); }; }, [props.treeStateProps, onNodeLoadedRef, onHierarchyLimitExceededRef, prevStateRef]); const onReload = useCallback((reloadedTree) => { prevStateRef.current?.dataProvider[Symbol.dispose](); const { modelSource, dataProvider } = reloadedTree; const pagedLoader = new PagedTreeNodeLoader(dataProvider, modelSource, dataProvider.pagingSize); const nodeLoader = new ReportingTreeNodeLoader(pagedLoader, (nodeLoadedProps) => onNodeLoadedRef.current?.(nodeLoadedProps)); setState({ dataProvider, nodeLoader }); }, [onNodeLoadedRef, prevStateRef]); return { state, onReload }; } function useEventHandler(factory, nodeLoader) { const [state, setState] = useState(); useEffect(() => { if (!nodeLoader) { return; } const params = { modelSource: nodeLoader.modelSource, nodeLoader }; const newHandler = factory ? factory(params) : new TreeEventHandler(params); setState(newHandler); return () => { newHandler?.dispose(); }; }, [factory, nodeLoader]); return state; } function usePresentationTreeFiltering({ activeMatchIndex, ...rest }) { const { filteredNodeLoader, filteredProvider, isFiltering, matchesCount } = useFilteredNodeLoader(rest); const highlightProps = useNodeHighlightingProps(rest.filter, filteredProvider, activeMatchIndex); return rest.filter && rest.dataProvider ? { highlightProps, filteredNodeLoader, filteredProvider, isFiltering, matchesCount, } : undefined; } function useLatest(value) { const ref = useRef(value); useEffect(() => { ref.current = value; }, [value]); return ref; } //# sourceMappingURL=UsePresentationTreeState.js.map