( options: UseVirtualTreeOptions<T, R> )
| 203 | } |
| 204 | |
| 205 | export function useVirtualTree<T extends { id: string; children?: T[] }, R>( |
| 206 | options: UseVirtualTreeOptions<T, R> |
| 207 | ): UseVirtualTreeInstance<T> { |
| 208 | const reducer = useCallback<Reducer<TreeState<T>, TreeAction>>( |
| 209 | (state, action) => { |
| 210 | switch (action.type) { |
| 211 | case "BLUR": { |
| 212 | return { |
| 213 | ...state, |
| 214 | focusedNodeId: null, |
| 215 | }; |
| 216 | } |
| 217 | case "TOGGLE_NODE": { |
| 218 | const isCollapsed = state.collapsedState[action.id]; |
| 219 | |
| 220 | if (isCollapsed) { |
| 221 | return expandNode<T>(state, action.id); |
| 222 | } else { |
| 223 | if ( |
| 224 | action.source && |
| 225 | (action.source.shiftKey || action.source.altKey) |
| 226 | ) { |
| 227 | return toggleAllChildren<T>(state, action.id); |
| 228 | } else { |
| 229 | return collapseNode<T>(state, action.id); |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | case "COLLAPSE_ALL_NODES": { |
| 234 | // Reduce from the right, so that the |
| 235 | // focusedNodeId is set to the top-level node. |
| 236 | return state.items.reduceRight( |
| 237 | (nextState, item) => collapseNode<T>(nextState, item.id), |
| 238 | state |
| 239 | ); |
| 240 | } |
| 241 | case "FOCUS_NODE": { |
| 242 | const itemIndex = state.items.findIndex(({ id }) => id === action.id); |
| 243 | |
| 244 | if (itemIndex === -1) { |
| 245 | const node = findNodeInTreeById(state.nodes, action.id); |
| 246 | |
| 247 | if (!node) { |
| 248 | return state; |
| 249 | } |
| 250 | |
| 251 | const path = calculatePathToNode(state.nodes, node) ?? []; |
| 252 | |
| 253 | const collapsedState = path.reduce( |
| 254 | (acc, id) => ({ |
| 255 | ...acc, |
| 256 | [id]: false, |
| 257 | }), |
| 258 | state.collapsedState |
| 259 | ); |
| 260 | |
| 261 | return { |
| 262 | ...state, |
no test coverage detected