Conversation
To further eliminate the need to pass the parser object to visualization components, the "focus" path is now computed on the fly. That means that every node computes for itself whether it should be open and whether it should be highlighted. This is possible because the tree adapter can answer the question whether a node contains children in cursor range or not. This commit also changes the behavior for nodes that have been opened via focus: If nodes loose focus, they used to stay open. Now, they will close as long as they haven't been explicitly opened.
See the comment in `focusNodes` for more information.
| nodes.values().next().value.current.scrollIntoView(); | ||
| } else if (size > 1) { | ||
| const rootRect = root.getBoundingClientRect(); | ||
| const center = (rootRect.y + rootRect.height) / 2 + rootRect.y; |
There was a problem hiding this comment.
🐛 Bug
The calculation of the center on line 37 is incorrect. It adds rootRect.y twice, which will result in an incorrect vertical center. This will lead to inaccurate determination of which element to scroll into view.
| const center = (rootRect.y + rootRect.height) / 2 + rootRect.y; | |
| const center = (rootRect.y + rootRect.height) / 2; |
| switch (currentState) { | ||
| case OPEN_STATES.DEFAULT: | ||
| case OPEN_STATES.CLOSED: | ||
| switch (event) { | ||
| case EVENTS.DEEP_OPEN: | ||
| return OPEN_STATES.DEEP_OPEN; | ||
| case EVENTS.GAIN_FOCUS: | ||
| return OPEN_STATES.FOCUS_OPEN; | ||
| case EVENTS.LOOSE_FOCUS: | ||
| return OPEN_STATES.DEFAULT; | ||
| } | ||
| break; | ||
|
|
||
| _shouldAutoFocus(thisProps, nextProps) { | ||
| const {focusPath: thisFocusPath} = thisProps; | ||
| const {settings: nextSettings, focusPath: nextFocusPath} = nextProps; | ||
| case OPEN_STATES.OPEN: | ||
| switch (event) { | ||
| case EVENTS.DEEP_OPEN: | ||
| return OPEN_STATES.DEEP_OPEN; | ||
| case EVENTS.GAIN_FOCUS: | ||
| case EVENTS.LOOSE_FOCUS: | ||
| return currentState; | ||
| } | ||
| break; | ||
|
|
||
| return ( | ||
| thisFocusPath !== nextFocusPath && | ||
| nextFocusPath.indexOf(nextProps.value) > -1 && | ||
| nextSettings.autofocus | ||
| ); | ||
| } | ||
| case OPEN_STATES.DEEP_OPEN: | ||
| return OPEN_STATES.DEEP_OPEN; | ||
|
|
||
| componentDidMount() { | ||
| if (this.props.settings.autofocus) { | ||
| this._scrollIntoView(); | ||
| } | ||
| case OPEN_STATES.FOCUS_OPEN: | ||
| switch (event) { | ||
| case EVENTS.GAIN_FOCUS: | ||
| return OPEN_STATES.FOCUS_OPEN; | ||
| case EVENTS.LOOSE_FOCUS: | ||
| return OPEN_STATES.DEFAULT; | ||
| case EVENTS.DEEP_OPEN: | ||
| return OPEN_STATES.DEEP_OPEN; | ||
| } | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
🐛 Bug
The function lacks a default case in the switch statements, which could lead to undefined behavior if an unexpected state or event is passed.
| switch (currentState) { | |
| case OPEN_STATES.DEFAULT: | |
| case OPEN_STATES.CLOSED: | |
| switch (event) { | |
| case EVENTS.DEEP_OPEN: | |
| return OPEN_STATES.DEEP_OPEN; | |
| case EVENTS.GAIN_FOCUS: | |
| return OPEN_STATES.FOCUS_OPEN; | |
| case EVENTS.LOOSE_FOCUS: | |
| return OPEN_STATES.DEFAULT; | |
| } | |
| break; | |
| _shouldAutoFocus(thisProps, nextProps) { | |
| const {focusPath: thisFocusPath} = thisProps; | |
| const {settings: nextSettings, focusPath: nextFocusPath} = nextProps; | |
| case OPEN_STATES.OPEN: | |
| switch (event) { | |
| case EVENTS.DEEP_OPEN: | |
| return OPEN_STATES.DEEP_OPEN; | |
| case EVENTS.GAIN_FOCUS: | |
| case EVENTS.LOOSE_FOCUS: | |
| return currentState; | |
| } | |
| break; | |
| return ( | |
| thisFocusPath !== nextFocusPath && | |
| nextFocusPath.indexOf(nextProps.value) > -1 && | |
| nextSettings.autofocus | |
| ); | |
| } | |
| case OPEN_STATES.DEEP_OPEN: | |
| return OPEN_STATES.DEEP_OPEN; | |
| componentDidMount() { | |
| if (this.props.settings.autofocus) { | |
| this._scrollIntoView(); | |
| } | |
| case OPEN_STATES.FOCUS_OPEN: | |
| switch (event) { | |
| case EVENTS.GAIN_FOCUS: | |
| return OPEN_STATES.FOCUS_OPEN; | |
| case EVENTS.LOOSE_FOCUS: | |
| return OPEN_STATES.DEFAULT; | |
| case EVENTS.DEEP_OPEN: | |
| return OPEN_STATES.DEEP_OPEN; | |
| } | |
| break; | |
| } | |
| } | |
| function transition(currentState, event) { | |
| switch (currentState) { | |
| // ... existing cases ... | |
| default: | |
| console.warn(`Unexpected state: ${currentState}`); | |
| return currentState; | |
| } | |
| } |
| case OPEN_STATES.DEEP_OPEN: | ||
| return OPEN_STATES.DEEP_OPEN; |
There was a problem hiding this comment.
🐛 Bug
The OPEN_STATES.DEEP_OPEN case always returns OPEN_STATES.DEEP_OPEN without considering the event. This might lead to unexpected behavior if other events should affect this state.
| case OPEN_STATES.DEEP_OPEN: | |
| return OPEN_STATES.DEEP_OPEN; | |
| case OPEN_STATES.DEEP_OPEN: | |
| switch (event) { | |
| case EVENTS.LOOSE_FOCUS: | |
| return OPEN_STATES.OPEN; | |
| default: | |
| return OPEN_STATES.DEEP_OPEN; | |
| } |
| if (!isInRange && wasInRange && ownOpenState === OPEN_STATES.CLOSED) { | ||
| setOwnOpenState(OPEN_STATES.DEFAULT); | ||
| } |
There was a problem hiding this comment.
🐛 Bug
The condition if (!isInRange && wasInRange && ownOpenState === OPEN_STATES.CLOSED) sets ownOpenState to OPEN_STATES.DEFAULT, which might lead to unexpected behavior. If an element is closed and goes out of range, it should probably remain closed rather than reverting to the default state.
| if (!isInRange && wasInRange && ownOpenState === OPEN_STATES.CLOSED) { | |
| setOwnOpenState(OPEN_STATES.DEFAULT); | |
| } | |
| if (!isInRange && wasInRange && ownOpenState === OPEN_STATES.CLOSED) { | |
| // Do nothing, keep the closed state | |
| } |
✨
Description by Cal
This PR introduces a new feature for handling selected nodes in a tree visualization, including context management and focus handling.
Diagrams of code changes
sequenceDiagram participant User participant PR participant TreeVisualization participant ASTOutput participant TreeAdapter User->>PR: Initiates PR review PR->>TreeVisualization: Adds new functionality TreeVisualization->>ASTOutput: Updates visualization ASTOutput->>TreeAdapter: Enhances tree adaptation TreeAdapter->>TreeVisualization: Provides improved node handling TreeVisualization->>User: Displays enhanced AST visualizationKey Issues
None
Files Changed
File: /website/src/components/visualization/SelectedNodeContext.js
Added context management for selected nodes.File: /website/src/components/visualization/Tree.js
Updated tree component to utilize selected node context and manage focus.File: /website/src/components/visualization/focusNodes.js
Implemented logic to manage focus on tree nodes.File: /website/src/components/visualization/tree/Element.js
Refactored element rendering to support focus and selection.File: /website/src/core/TreeAdapter.js
Enhanced tree adapter to support range and focus management.