Skip to content

Commit

Permalink
Add dynamic transitions and toggle button for modes (#50)
Browse files Browse the repository at this point in the history
Also improved mode toggling so it's more dynamic
  • Loading branch information
tubarao312 authored Feb 2, 2024
1 parent 42c62e5 commit 2a76777
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 72 deletions.
133 changes: 95 additions & 38 deletions src/components/Graph/AnalysisWindow/AnalysisWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {
FC,
createContext,
useRef,
useCallback,
useState,
useEffect,
} from "react";
import { FC, createContext, useRef, useState, useEffect } from "react";
import clsx from "clsx";
import {
PresentationChartLineIcon,
EyeIcon,
//ArrowsRightLeftIcon,
} from "@heroicons/react/20/solid";

import Draggable from "react-draggable";
import Content from "./Content";
Expand All @@ -25,6 +23,35 @@ export const AnalysisContext = createContext<AnalysisContextProps>({
address: "",
});

export enum AnalysisModeNames {
Overview = "Overview",
Advanced = "Advanced",
Transactions = "Transactions",
}

export interface AnalysisMode {
name: AnalysisModeNames;
icon: any;
component?: FC;
}

export const AnalysisModes: AnalysisMode[] = [
{
name: AnalysisModeNames.Overview,
icon: EyeIcon,
component: Overview,
},
{
name: AnalysisModeNames.Advanced,
icon: PresentationChartLineIcon,
component: Content,
},
// {
// name: AnalysisModeNames.Transactions,
// icon: ArrowsRightLeftIcon,
// },
];

interface DraggableWindowProps {
analysisData: AddressAnalysis | null;
onExit: () => void;
Expand All @@ -48,21 +75,52 @@ const DraggableWindow: FC<DraggableWindowProps> = ({
onExit,
}) => {
const [hasBeenHovered, setHasBeenHovered] = useState<boolean>(false);
const [analysisMode, setAnalysisMode] = useState<boolean>(false);
const [analysisMode, setAnalysisMode] = useState<AnalysisMode>(
AnalysisModes[0],
);
const [previousAnalysisMode, setPreviousAnalysisMode] =
useState<AnalysisMode>(analysisMode);
const nodeRef = useRef(null);

// When analysisData is none, reset the analysis mode
useEffect(() => {
setHasBeenHovered(false);
if (analysisData === null) {
setAnalysisMode(false);
setAnalysisMode(AnalysisModes[0]);
}
}, [analysisData]);

const toggleAnalysisMode = useCallback(() => {
setAnalysisMode(!analysisMode);
console.log("Analysis mode toggled");
// Whenever analysisMode changes, setTimeout to set previousAnalysisMode after 500ms. This is used for the dynamic transition
useEffect(() => {
// Whenever currentAnalysisMode changes, update previousAnalysisMode
// and then set current mode to new mode after a delay
if (analysisMode !== previousAnalysisMode) {
setTimeout(() => setPreviousAnalysisMode(analysisMode), 500); // Adjust delay based on transition duration
}
}, [analysisMode]);

// Function to determine transition classes based on direction
function getTransactionClasses(mode: AnalysisMode, leaving: boolean = false) {
const currentIndex = AnalysisModes.findIndex((m) => m === mode);
const previousIndex = AnalysisModes.findIndex(
(m) => m === previousAnalysisMode,
);

// If increasing the mode index, we want the leaving component to go to the left and the entering component to come from the right - transition-all duration-500 opacity-0 translate-x-full
// If decreasing the mode index, we want the leaving component to go to the right and the entering component to come from the left - transition-all duration-500 opacity-0 -translate-x-full

if (currentIndex > previousIndex) {
return leaving
? "transition-all duration-500 opacity-0 -translate-x-full"
: "transition-all duration-500 opacity-0 translate-x-full";
} else {
return leaving
? "transition-all duration-500 opacity-0 translate-x-full"
: "transition-all duration-500 opacity-0 -translate-x-full";
}
}

// If no analysis data, don't show the window
if (analysisData === null) {
return null;
}
Expand All @@ -79,43 +137,42 @@ const DraggableWindow: FC<DraggableWindowProps> = ({
<div ref={nodeRef}>
<div
className={clsx(
"pointer-events-auto scale-75 divide-y divide-dashed divide-gray-200 rounded-lg bg-white shadow-xl transition-opacity duration-300",
"pointer-events-auto flex h-[50rem] scale-75 flex-col divide-y divide-dashed divide-gray-200 overflow-hidden rounded-lg bg-white shadow-xl transition-opacity duration-300",
hasBeenHovered ? "opacity-30 hover:opacity-100" : "opacity-100",
)}
style={{
width: analysisMode ? "68rem" : "40rem",
width:
analysisMode.name === AnalysisModeNames.Advanced
? "68rem"
: "50rem",
transition: "width 0.5s ease-in-out, opacity 0.2s ease-in-out",
}}
onMouseEnter={() => setHasBeenHovered(true)}
>
<div className="px-4 py-5">
<div className="h-fit flex-none px-4 py-5">
<Header
onExit={onExit}
toggleAnalysisMode={toggleAnalysisMode}
setAnalysisMode={setAnalysisMode}
analysisMode={analysisMode}
/>
</div>
<div className="overflow-hidden px-4 py-5">
<Transition
appear={true}
show={analysisMode}
enter="transition-all duration-500"
enterFrom="opacity-50 translate-x-1/2"
enterTo="opacity-100 translate-x-0"
leave="hidden fixed duration-0"
>
<Content />
</Transition>
<Transition
appear={true}
show={!analysisMode}
enter="transition-all duration-500"
enterFrom="opacity-50 -translate-x-1/2"
enterTo="opacity-100 translate-x-0"
leave="hidden fixed duration-0"
>
<Overview />
</Transition>
<div className="relative flex-auto overflow-hidden">
{AnalysisModes.map((mode) => (
<Transition
key={mode.name}
appear={true}
show={analysisMode === mode}
enter="transition-all duration-500"
enterFrom={getTransactionClasses(analysisMode, false)}
enterTo="opacity-100 translate-x-0"
leave="transition-all duration-500"
leaveFrom="opacity-100 translate-x-0"
leaveTo={getTransactionClasses(analysisMode, true)}
className="absolute h-full w-full p-4"
>
{mode.component ? <mode.component /> : null}
</Transition>
))}
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Graph/AnalysisWindow/Content/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const Content: FC = () => {
const tabs = BLOCKCHAIN_TABS[analysisData.blockchain];

return (
<div className="flex divide-x divide-dashed divide-gray-200">
<div className="flex h-full divide-x divide-dashed divide-gray-200">
<div className="w-1/5">
<div className="pr-3">
<Navbar
Expand All @@ -80,7 +80,7 @@ const Content: FC = () => {
</div>
<div
id="scrollbar"
className="scrollbar h-[29rem] w-full overflow-scroll overflow-x-hidden pl-3"
className="scrollbar w-full overflow-scroll overflow-x-hidden pl-3"
>
<AllContentComponents tabs={tabs} selectedTab={selectedTab} />
</div>
Expand Down
103 changes: 74 additions & 29 deletions src/components/Graph/AnalysisWindow/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { FC, useContext } from "react";
import {
XMarkIcon,
PresentationChartLineIcon,
ArrowUturnLeftIcon,
} from "@heroicons/react/20/solid";
import clsx from "clsx";
import { XMarkIcon } from "@heroicons/react/20/solid";

import LabelList from "./components/LabelList";
import CopyToClipboardIcon from "./components/CopyToClipboardIcon";
Expand All @@ -13,25 +10,86 @@ import BigButton from "../../../common/BigButton";
import EntityLogo from "../../../common/EntityLogo";

import { AnalysisContext } from "../AnalysisWindow";
import {
AnalysisMode,
AnalysisModes,
AnalysisModeNames,
} from "../AnalysisWindow";

interface ModeButtonProps {
isActive: boolean;
analysisMode: AnalysisMode;
setAnalysisMode: (mode: AnalysisMode) => void;
}

const ModeButton: FC<ModeButtonProps> = ({
isActive,
analysisMode,
setAnalysisMode,
}) => {
return (
<button
type="button"
className={clsx(
"flex flex-row items-center gap-x-1 rounded-md px-3 py-2 text-sm font-semibold transition-all duration-300",
{
"bg-white text-gray-900 shadow-sm": isActive,
"text-gray-700 hover:bg-gray-200": !isActive,
},
)}
onClick={() => setAnalysisMode(analysisMode)}
>
<analysisMode.icon
className={clsx("h-5 w-5 rounded-full", {
"text-blue-500": isActive,
"text-gray-700": !isActive,
})}
aria-hidden="true"
/>
{analysisMode.name}
</button>
);
};

interface ModeToggleProps {
analysisMode: AnalysisMode;
setAnalysisMode: (mode: AnalysisMode) => void;
}

const ModeToggle: FC<ModeToggleProps> = ({ analysisMode, setAnalysisMode }) => {
return (
<span className="flex flex-row gap-x-0.5 rounded-md bg-gray-100 p-0.5 shadow-inner">
{AnalysisModes.map((mode) => (
<ModeButton
key={mode.name}
isActive={mode === analysisMode}
analysisMode={mode}
setAnalysisMode={setAnalysisMode}
/>
))}
</span>
);
};

interface HeaderProps {
onExit: () => void;
toggleAnalysisMode: () => void;
analysisMode: boolean;
setAnalysisMode: (mode: AnalysisMode) => void;
analysisMode: AnalysisMode;
}

const Header: FC<HeaderProps> = ({
onExit,
toggleAnalysisMode,
setAnalysisMode,
analysisMode,
}: HeaderProps) => {
// Extract analysisData from context
const { analysisData, address } = useContext(AnalysisContext);

// When minimized, the address hash should be sliced off
const displayedAddress = analysisMode
? address
: address.slice(0, 8) + "..." + address.slice(-6);
const displayedAddress =
analysisMode.name === AnalysisModeNames.Advanced
? address
: address.slice(0, 8) + "..." + address.slice(-6);
const risk = analysisData!.risk;

return (
Expand Down Expand Up @@ -70,24 +128,11 @@ const Header: FC<HeaderProps> = ({

{/* Exit button */}
<span className="flex flex-row items-center gap-x-1.5">
<button
type="button"
className="inline-flex items-center gap-x-1.5 rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white shadow-sm shadow-blue-200 transition-all duration-300 hover:bg-blue-400 "
onClick={() => toggleAnalysisMode()}
>
{analysisMode ? (
<ArrowUturnLeftIcon
className="h-5 w-5 rounded-full text-white"
aria-hidden="true"
/>
) : (
<PresentationChartLineIcon
className="h-5 w-5 rounded-full text-white"
aria-hidden="true"
/>
)}
{analysisMode ? "Overview" : "Analysis"}
</button>
{/* Mode Toggle */}
<ModeToggle
analysisMode={analysisMode}
setAnalysisMode={setAnalysisMode}
/>
<BigButton onClick={onExit} Icon={XMarkIcon} text="Exit" />
</span>
</span>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Graph/AnalysisWindow/Overview/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ const Overview: FC = () => {

// TODO - Add a max height and a scrollbar, maybe a slight fade effect to the bottom and top
return (
<div className="flex flex-col gap-y-2">
<h3 className="flex flex-row items-center gap-x-1 text-sm font-semibold tracking-wide text-gray-600">
<div className="flex h-full flex-col gap-y-2">
<h3 className="flex h-fit flex-row items-center gap-x-1 text-sm font-semibold tracking-wide text-gray-600">
<BuildingLibraryIcon className="h-5 w-5 text-gray-400" />
TOP ENTITIES
</h3>
<ul className="scrollbar flex h-96 scroll-m-28 flex-col gap-y-1.5 overflow-scroll overflow-x-hidden">
<ul className="scrollbar flex flex-grow scroll-m-28 flex-col gap-y-1.5 overflow-scroll overflow-x-hidden">
{topEntityRows.map((row, index) => (
<Transition
appear={true}
Expand Down

0 comments on commit 2a76777

Please sign in to comment.