@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
133 lines • 6.17 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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