Skip to content
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

Rework Address Panel #37

Merged
merged 2 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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