Skip to content

Fade transitions between pages #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

alecande11
Copy link
Contributor

@alecande11 alecande11 commented Jun 29, 2025

Summary by CodeRabbit

  • New Features

    • Added animated transitions for tab changes in the Home screen, providing smooth sliding effects based on navigation direction.
    • Introduced animated route transitions throughout the app, enhancing navigation with horizontal sliding effects.
    • Added the ability to track and access the previous navigation location and path.
  • Refactor

    • Centralized route configuration and replaced switch statements with dynamic route rendering.
    • Improved tab and route management for better maintainability.
  • Style

    • Updated Home screen container styling to ensure full-height layout.
  • Bug Fixes

    • Ensured proper cleanup of input focus timing to prevent potential issues on component unmount or effect re-run.

@alecande11 alecande11 requested a review from simcheolhwan as a code owner June 29, 2025 11:55
Copy link

coderabbitai bot commented Jun 29, 2025

Walkthrough

Animated transitions were introduced for both tab and route navigation using @react-spring/web, replacing static content switching with smooth sliding animations. Navigation logic was refactored to track previous locations, enabling directional animations. Supporting hooks and context properties were added, and minor style and focus timing adjustments were made to enhance user experience.

Changes

File(s) Change Summary
src/components/form/hooks.ts Delayed autofocus by 250ms using setTimeout in useAutoFocus, with cleanup on unmount.
src/lib/router/MemoryRouter.tsx, src/lib/router/RouterContext.ts Added previousLocation state to router context and provider; updated navigation logic to track and expose previous location; added usePreviousPath hook.
src/pages/wallet/tabs/Home.module.css Added .container class with height: 100%.
src/pages/wallet/tabs/Home.tsx Refactored the Home component to use animated tab transitions, dynamic tab metadata, and directional sliding based on navigation history.
src/public/app/Routes.tsx Refactored routing to use a configuration array, added animated route transitions with sliding effects, normalized certain paths, and used previous path tracking for animation direction.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant RouterContext
    participant AnimationEngine

    User->>UI: Navigate to new tab/route
    UI->>RouterContext: Get current and previous location
    UI->>AnimationEngine: Determine animation direction (left/right)
    AnimationEngine->>UI: Animate transition (slide in/out)
    UI->>User: Display animated new content
Loading

Poem

In the garden of code where the widgets reside,
Tabs now slide smoothly, no need to hide.
Routes dance and shimmer with each navigation,
History remembered for perfect animation.
With a hop and a spring, transitions delight—
This rabbit’s code brings motion to light!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
packages/widget-react/src/lib/router/MemoryRouter.tsx (1)

47-49: Consider updating previousLocation in reset function.

The reset function clears the history but doesn't update previousLocation, which could lead to stale data after a reset. Consider setting it to null or the current location before resetting.

 const reset = useCallback((path: string, state?: object) => {
+  setPreviousLocation(history[history.length - 1])
   setHistory([{ path, state }])
 }, [])
🧹 Nitpick comments (3)
packages/widget-react/src/public/app/Routes.tsx (3)

78-83: Optimize route lookup for better performance.

The routes.find() call inside the transition function will execute for every animated route on every render. Consider memoizing this lookup or restructuring to avoid repeated searches.

  return (
    <div style={{ position: "relative", height: "100%", overflow: "hidden" }}>
      {transitions((style, animatedPath) => {
        const route = routes.find((r) => r.path === animatedPath)
-       if (!route) return null
+       if (!route) {
+         console.warn(`Route not found for path: ${animatedPath}`)
+         return null
+       }
        if (!address && !route.renderWithoutAddress) return null

68-68: Consider additional scenarios for skipping animations.

The skipAnimation flag only checks for missing prevPath, but other scenarios like initial app load or programmatic navigation might also benefit from skipping animations.

- skipAnimation: !prevPath,
+ skipAnimation: !prevPath || prevPath === path,

73-74: Address the FIXME comment about route blocking.

The FIXME comment suggests uncertainty about blocking all routes when no address is connected. This could impact user experience and should be clarified based on the app's requirements.

Do you want me to help analyze the routing requirements and propose a more definitive approach for handling unauthenticated access to different routes?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dea7178 and 9004f2a.

📒 Files selected for processing (6)
  • packages/widget-react/src/components/form/hooks.ts (1 hunks)
  • packages/widget-react/src/lib/router/MemoryRouter.tsx (2 hunks)
  • packages/widget-react/src/lib/router/RouterContext.ts (2 hunks)
  • packages/widget-react/src/pages/wallet/tabs/Home.module.css (1 hunks)
  • packages/widget-react/src/pages/wallet/tabs/Home.tsx (2 hunks)
  • packages/widget-react/src/public/app/Routes.tsx (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/widget-react/src/lib/router/MemoryRouter.tsx (1)
packages/widget-react/src/lib/router/RouterContext.ts (2)
  • HistoryEntry (3-6)
  • RouterContext (16-16)
🔇 Additional comments (13)
packages/widget-react/src/pages/wallet/tabs/Home.module.css (1)

1-3: LGTM! CSS addition supports animation requirements.

The new .container class with 100% height is necessary for the absolute positioning of animated tab content.

packages/widget-react/src/components/form/hooks.ts (1)

9-13: LGTM! Proper timeout handling with cleanup.

The 250ms delay prevents focus from interfering with page transitions, and the timeout is correctly cleared on unmount.

packages/widget-react/src/lib/router/RouterContext.ts (2)

10-10: LGTM! Interface extension supports navigation history tracking.

The previousLocation property is properly typed as nullable to handle initial state.


27-30: LGTM! Hook follows established patterns.

The usePreviousPath hook correctly uses optional chaining to handle null previousLocation.

packages/widget-react/src/lib/router/MemoryRouter.tsx (3)

17-18: LGTM! Clear explanation for separate state management.

The comment effectively explains why history array cannot be relied upon for tracking the previous location.


21-45: LGTM! Correct timing for capturing previous location.

Setting previousLocation before updating history ensures the current location is captured before navigation changes.


52-52: LGTM! Context value properly includes previousLocation.

The previousLocation is correctly added to the context provider value.

packages/widget-react/src/pages/wallet/tabs/Home.tsx (3)

13-17: LGTM! Clean tab configuration structure.

The centralized tabs array improves maintainability and makes it easy to add new tabs.


27-42: LGTM! Well-implemented animation logic.

The direction calculation and transition configuration properly handle:

  • Directional sliding based on tab order
  • Skip animation when navigating from non-tab routes
  • Smooth spring physics with appropriate tension/friction values

67-78: LGTM! Proper implementation of animated tab content.

The animated transitions correctly use:

  • Absolute positioning to enable sliding animations
  • forceMount to keep tab content in DOM during transitions
  • Proper style spreading for animation properties
packages/widget-react/src/public/app/Routes.tsx (3)

19-32: LGTM! Excellent refactoring of route configuration.

The centralized route configuration array is a significant improvement over the previous switch statement approach. The addition of rerender and renderWithoutAddress flags provides clear control over component behavior.


63-69: Well-implemented spring transition configuration.

The transition configuration provides smooth slide animations with appropriate timing. The opacity and transform combinations create a polished user experience.


41-42: Nested tab normalization is intentional and correct

The “/nfts” and “/activity” paths are virtual child‐routes of the Home component (path="/") and are normalized to “/” in Routes.tsx (lines 41–42) so that switching between the Home, NFTs, and Activity tabs doesn’t trigger a full page transition. This matches the useEffect navigation logic and preserves the intended animation direction. No changes required.

Comment on lines +58 to +61
// Compute transition direction
const currentIndex = routes.findIndex((r) => r.path === path)
const prevIndex = routes.findIndex((r) => r.path === prevPath)
const direction = currentIndex >= prevIndex ? 1 : -1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle edge cases when routes are not found in the array.

The findIndex method returns -1 when a route is not found, which could lead to unexpected animation directions. Consider adding validation to ensure both routes exist before calculating direction.

  // Compute transition direction
  const currentIndex = routes.findIndex((r) => r.path === path)
  const prevIndex = routes.findIndex((r) => r.path === prevPath)
- const direction = currentIndex >= prevIndex ? 1 : -1
+ const direction = currentIndex === -1 || prevIndex === -1 ? 1 : (currentIndex >= prevIndex ? 1 : -1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Compute transition direction
const currentIndex = routes.findIndex((r) => r.path === path)
const prevIndex = routes.findIndex((r) => r.path === prevPath)
const direction = currentIndex >= prevIndex ? 1 : -1
// Compute transition direction
const currentIndex = routes.findIndex((r) => r.path === path)
const prevIndex = routes.findIndex((r) => r.path === prevPath)
const direction = currentIndex === -1 || prevIndex === -1
? 1
: (currentIndex >= prevIndex ? 1 : -1)
🤖 Prompt for AI Agents
In packages/widget-react/src/public/app/Routes.tsx around lines 58 to 61, the
code calculates transition direction using indices from routes.findIndex, but
does not handle cases where a route is not found (findIndex returns -1). To fix
this, add validation to check if both currentIndex and prevIndex are not -1
before computing direction. If either is -1, handle the case appropriately, such
as defaulting direction to 0 or skipping the animation logic.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/widget-react/src/pages/wallet/tabs/Home.tsx (1)

67-78: Consider accessibility implications of forceMount.

The animated content implementation is functionally correct, but using forceMount on all tab contents might cause accessibility issues as screen readers could announce all tab panels simultaneously.

Consider implementing a more accessibility-friendly approach:

-<Tabs.Content forceMount key={item} value={item} asChild>
+<Tabs.Content key={item} value={item} asChild>
  <animated.div style={{ ...style, position: "absolute", width: "100%" }}>
    {tab?.component}
  </animated.div>
</Tabs.Content>

And handle the animation lifecycle more explicitly to ensure only the active and transitioning tabs are mounted.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abad9a8 and 6561f7d.

📒 Files selected for processing (2)
  • packages/widget-react/src/pages/wallet/tabs/Home.tsx (2 hunks)
  • packages/widget-react/src/public/app/Routes.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/widget-react/src/public/app/Routes.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/widget-react/src/pages/wallet/tabs/Home.tsx (3)

1-1: LGTM! Clean imports for animation functionality.

The imports are well-organized and include all necessary dependencies for the animated tab transitions.

Also applies to: 4-5


13-17: LGTM! Excellent refactor to dynamic tab configuration.

The tabs array provides a clean, maintainable structure for tab metadata. This approach makes it easy to add, remove, or modify tabs in the future.


33-42: LGTM! Well-configured animation with performance optimization.

The transition configuration is well-thought-out:

  • Uses transform and opacity for smooth performance
  • Includes skipAnimation logic to avoid unnecessary animations
  • Spring physics configuration provides natural-feeling transitions

Comment on lines +26 to +31
const direction = useMemo(() => {
const currentIndex = tabs.findIndex((t) => t.value === path)
const prevIndex = tabs.findIndex((t) => t.value === prevPath)

return currentIndex > prevIndex ? 1 : -1
}, [path, prevPath])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle edge case when tab indices are not found.

The direction calculation doesn't handle the case where findIndex returns -1 (when a path is not found in the tabs array). This could lead to incorrect animation direction.

 const direction = useMemo(() => {
   const currentIndex = tabs.findIndex((t) => t.value === path)
   const prevIndex = tabs.findIndex((t) => t.value === prevPath)

+  // Handle case where indices are not found
+  if (currentIndex === -1 || prevIndex === -1) {
+    return 1 // Default to forward direction
+  }
+
   return currentIndex > prevIndex ? 1 : -1
 }, [path, prevPath])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const direction = useMemo(() => {
const currentIndex = tabs.findIndex((t) => t.value === path)
const prevIndex = tabs.findIndex((t) => t.value === prevPath)
return currentIndex > prevIndex ? 1 : -1
}, [path, prevPath])
const direction = useMemo(() => {
const currentIndex = tabs.findIndex((t) => t.value === path)
const prevIndex = tabs.findIndex((t) => t.value === prevPath)
// Handle case where indices are not found
if (currentIndex === -1 || prevIndex === -1) {
return 1 // Default to forward direction
}
return currentIndex > prevIndex ? 1 : -1
}, [path, prevPath])
🤖 Prompt for AI Agents
In packages/widget-react/src/pages/wallet/tabs/Home.tsx around lines 26 to 31,
the direction calculation does not handle cases where findIndex returns -1 if a
path is not found in the tabs array. Update the useMemo logic to check if either
currentIndex or prevIndex is -1 and handle this edge case explicitly, for
example by returning a default direction or skipping the animation, to avoid
incorrect animation direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant