Skip to content

Commit

Permalink
Rework Address Panel (#37)
Browse files Browse the repository at this point in the history
* Completely rework address panel

* Update Header.tsx
  • Loading branch information
tubarao312 authored Jan 18, 2024
1 parent f4be71f commit 99fe358
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 72 deletions.
68 changes: 63 additions & 5 deletions src/components/Graph/AnalysisWindow/AnalysisWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { FC, createContext, useRef, useState, useEffect } from "react";
import {
FC,
createContext,
useRef,
useCallback,
useState,
useEffect,
} from "react";
import clsx from "clsx";

import Draggable from "react-draggable";
import Content from "./Content";
import Overview from "./Overview";
import Header from "./Header";

import { AddressAnalysis } from "../../../api/model";
import { Transition } from "@headlessui/react";

interface AnalysisContextProps {
analysisData: AddressAnalysis | null;
Expand All @@ -21,17 +30,39 @@ interface DraggableWindowProps {
setFocusedAddressData: (data: AddressAnalysis | null) => void;
}

/** The draggable window that appears when an address is clicked on.
*
* It features two modes: basic & analysis mode.
* - **Basic Mode**: Shows a small list of entities that the address is exposed
* to and that can be expanded.
* - **Analysis Mode**: Shows a massive list of categories that the address is
* exposed to and with thorough information.
*
* @param analysisData The analysis data of the address that was clicked on
* @param setFocusedAddressData A function to set the focused address data to null when the window is closed
* @returns
*/

const DraggableWindow: FC<DraggableWindowProps> = ({
analysisData,
setFocusedAddressData,
}) => {
const [hasBeenHovered, setHasBeenHovered] = useState<boolean>(false);
const [analysisMode, setAnalysisMode] = useState<boolean>(false);
const nodeRef = useRef(null);

useEffect(() => {
setHasBeenHovered(false);
if (analysisData === null) {
setAnalysisMode(false);
}
}, [analysisData]);

const toggleAnalysisMode = useCallback(() => {
setAnalysisMode(!analysisMode);
console.log("Analysis mode toggled");
}, [analysisMode]);

if (analysisData === null) {
return null;
}
Expand All @@ -48,16 +79,43 @@ const DraggableWindow: FC<DraggableWindowProps> = ({
<div ref={nodeRef}>
<div
className={clsx(
"pointer-events-auto w-[68rem] scale-75 divide-y divide-dashed divide-gray-200 rounded-lg bg-white shadow-xl transition-opacity duration-300",
"pointer-events-auto scale-75 divide-y divide-dashed divide-gray-200 rounded-lg bg-white shadow-xl transition-opacity duration-300",
hasBeenHovered ? "opacity-30 hover:opacity-100" : "opacity-100",
)}
style={{
width: analysisMode ? "68rem" : "40rem",
transition: "width 0.5s ease-in-out, opacity 0.2s ease-in-out",
}}
onMouseEnter={() => setHasBeenHovered(true)}
>
<div className="px-4 py-5">
<Header onExit={() => setFocusedAddressData(null)} />
<Header
onExit={() => setFocusedAddressData(null)}
toggleAnalysisMode={toggleAnalysisMode}
analysisMode={analysisMode}
/>
</div>
<div className="px-4 py-5">
<Content />
<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>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Graph/AnalysisWindow/Content/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC, useState, useContext, useEffect } from "react";
import { Transition } from "@headlessui/react";
import { BLOCKCHAIN_TABS, Tab } from "./Tab";
import "./Scrollbar.css";
import "../Scrollbar.css";

import Navbar from "./Navbar";
import LoadingPulseMock from "./PulseMock";
Expand Down Expand Up @@ -79,8 +79,8 @@ const Content: FC = () => {
</div>
</div>
<div
id="content-components"
className="content-component h-[29rem] w-full overflow-scroll overflow-x-hidden pl-3"
id="scrollbar"
className="scrollbar h-[29rem] w-full overflow-scroll overflow-x-hidden pl-3"
>
<AllContentComponents tabs={tabs} selectedTab={selectedTab} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { FC, useContext, useState, useMemo } from "react";
import { FC, useContext } from "react";
import { Transition } from "@headlessui/react";
import { HashtagIcon } from "@heroicons/react/20/solid";
import { ShareIcon } from "@heroicons/react/24/solid";

import { Address } from "../../../../../../api/model";
import formatNumber from "../../../../../../utils/formatNumber";
import Delayed from "../../../../../common/Delayed";

import BigButton from "../../../../../common/BigButton";
import { AnalysisContext } from "../../../AnalysisWindow";
import { GraphContext } from "../../../../Graph";
import { ExposureTabContext } from "./ExposureTabGeneric";

import Pagination from "../../../../../common/Pagination";

// Address Row _________________________________________________________________

interface AddressRowProps {
Expand Down Expand Up @@ -64,43 +61,26 @@ const AddressRow: FC<AddressRowProps> = ({ address }) => {

const EntityView: FC = () => {
const { focusedEntity } = useContext(ExposureTabContext)!;

const addresses = focusedEntity!.entity.addresses;

// Pagination
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 6;
const currentTableData: Address[] = useMemo(() => {
const firstPageIndex = (currentPage - 1) * pageSize;
const lastPageIndex = firstPageIndex + pageSize;
return addresses.slice(firstPageIndex, lastPageIndex);
}, [currentPage, addresses]);

return (
<div className="w-auto flex-grow flex-col space-y-2">
{currentTableData.map((address, index) => (
<Delayed
waitBeforeShow={currentPage === 1 ? index * (50 - index / 3) : 0}
{focusedEntity!.entity.addresses.map((address, index) => (
<Transition
appear={true}
show={true}
enter="transition-all ease-in-out transform duration-300 origin-left"
enterFrom="-translate-x-10 -translate-y-3 opacity-0 scale-50"
enterTo="translate-y-0 opacity-100 scale-100"
leave="transition-all ease-in-out transform duration-300 origin-left"
leaveFrom="translate-y-0 opacity-100 scale-100"
leaveTo="-translate-x-10 -translate-y-3 opacity-0 scale-50"
key={address.hash}
style={{
transitionDelay: `${index * (50 - index / 10)}ms`,
}}
>
<Transition
appear={true}
show={true}
enter={`transition-all ease-in-out transform`}
enterFrom="-translate-y-10 opacity-0"
enterTo="translate-y-0 opacity-100"
key={address.hash}
>
<AddressRow address={address} />
</Transition>
</Delayed>
<AddressRow address={address} />
</Transition>
))}
<Pagination
currentPage={currentPage}
totalCount={focusedEntity!.entity.addresses.length}
pageSize={pageSize}
onPageChange={(page: number) => setCurrentPage(page)}
/>
</div>
);
};
Expand Down
42 changes: 37 additions & 5 deletions src/components/Graph/AnalysisWindow/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { FC, useContext } from "react";
import { XMarkIcon } from "@heroicons/react/20/solid";
import {
XMarkIcon,
PresentationChartLineIcon,
ArrowUturnLeftIcon,
} from "@heroicons/react/20/solid";

import LabelList from "./components/LabelList";
import CopyToClipboardIcon from "./components/CopyToClipboardIcon";
Expand All @@ -12,14 +16,22 @@ import { AnalysisContext } from "../AnalysisWindow";

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

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

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

return (
Expand All @@ -32,7 +44,7 @@ const Header: FC<HeaderProps> = ({ onExit }: HeaderProps) => {
<div className="flex flex-col gap-y-0.5">
<span className="flex flex-row items-center gap-x-1 gap-y-1">
{/* Address Hash - Sliced when in non-expanded mode*/}
<h1 className="flex flex-row font-mono font-semibold tracking-tight text-gray-800">
<h1 className="font-xs flex flex-row font-mono font-semibold tracking-tight text-gray-800">
{displayedAddress}
<EntityLogo
entity={analysisData!.labels[0]}
Expand All @@ -57,7 +69,27 @@ const Header: FC<HeaderProps> = ({ onExit }: HeaderProps) => {
</span>

{/* Exit button */}
<BigButton onClick={onExit} Icon={XMarkIcon} text="Exit" />
<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>
<BigButton onClick={onExit} Icon={XMarkIcon} text="Exit" />
</span>
</span>
);
};
Expand Down
Loading

0 comments on commit 99fe358

Please sign in to comment.