From 4d245d30127f9fff49b45ed7144cb63c1070e9cf Mon Sep 17 00:00:00 2001 From: jagnani73 Date: Tue, 5 Dec 2023 13:31:39 +0530 Subject: [PATCH] feat: hooks + consistent stories --- .storybook/preview.tsx | 11 +- .../Atoms/Address/Address.stories.tsx | 10 +- .../Address/{Address.tsx => AddressView.tsx} | 4 +- .../AddressAvatar/AddressAvatar.stories.tsx | 98 +++-- ...ddressAvatar.tsx => AddressAvatarView.tsx} | 2 +- .../Atoms/NetPriceDelta/NetPriceDelta.tsx | 34 -- .../Atoms/ThemeChange/ThemeChange.stories.tsx | 349 ------------------ .../Atoms/TokenAvatar/TokenAvatar.stories.tsx | 30 +- .../{TokenAvatar.tsx => TokenAvatarView.tsx} | 2 +- .../AccountCardView/AccountCardView.tsx | 6 +- .../NFTDetailsView.stories.tsx} | 10 +- .../NFTDetailsView.tsx} | 4 +- .../NFTWalletTokenListView.tsx | 4 +- .../AddressActivityListView.stories.tsx | 2 +- .../AddressActivityListView.tsx | 6 +- .../TokenBalancesListView.tsx | 10 +- .../TokenTransfersListView.stories.tsx | 2 +- .../TokenTransfersListView.tsx | 14 +- .../BalancePriceDelta.tsx | 6 +- .../{Atoms/CopyImage => Shared}/CopyImage.tsx | 8 +- .../IconWrapper => Shared}/IconWrapper.tsx | 4 +- src/components/Shared/index.tsx | 3 + .../ThemeConfig/ThemeConfig.stories.tsx | 46 +++ .../ThemeConfig/ThemeConfigView.tsx | 198 ++++++++++ src/components/ui/tableHeaderSorting.tsx | 2 +- src/utils/functions/index.ts | 1 - src/utils/functions/update-theme.ts | 152 -------- src/utils/store/Theme.tsx | 242 ++++++++++++ src/utils/store/index.tsx | 3 + src/utils/types/atoms.types.ts | 22 -- src/utils/types/organisms.types.ts | 2 +- src/utils/types/shared.types.ts | 90 +++++ 32 files changed, 704 insertions(+), 673 deletions(-) rename src/components/Atoms/Address/{Address.tsx => AddressView.tsx} (81%) rename src/components/Atoms/AddressAvatar/{AddressAvatar.tsx => AddressAvatarView.tsx} (96%) delete mode 100644 src/components/Atoms/NetPriceDelta/NetPriceDelta.tsx delete mode 100644 src/components/Atoms/ThemeChange/ThemeChange.stories.tsx rename src/components/Atoms/TokenAvatar/{TokenAvatar.tsx => TokenAvatarView.tsx} (98%) rename src/components/Organisms/NFTs/{NFTDetailView/NFTDetailView.stories.tsx => NFTDetailsView/NFTDetailsView.stories.tsx} (57%) rename src/components/Organisms/NFTs/{NFTDetailView/NFTDetailView.tsx => NFTDetailsView/NFTDetailsView.tsx} (96%) rename src/components/{Atoms/BalancePriceDelta => Shared}/BalancePriceDelta.tsx (78%) rename src/components/{Atoms/CopyImage => Shared}/CopyImage.tsx (79%) rename src/components/{Atoms/IconWrapper => Shared}/IconWrapper.tsx (87%) create mode 100644 src/components/Shared/index.tsx create mode 100644 src/components/ThemeConfig/ThemeConfig.stories.tsx create mode 100644 src/components/ThemeConfig/ThemeConfigView.tsx delete mode 100644 src/utils/functions/update-theme.ts create mode 100644 src/utils/store/Theme.tsx create mode 100644 src/utils/store/index.tsx create mode 100644 src/utils/types/shared.types.ts diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 86b2be7b..a2512674 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,7 +1,10 @@ import "../src/tailwind-output.css"; import { themes } from "@storybook/theming"; -import { CovalentProvider } from "../src/utils/store/Covalent"; -import { ChainsProvider } from "../src/utils/store/Chains"; +import { + CovalentProvider, + ChainsProvider, + ThemeProvider, +} from "../src/utils/store"; // import { useDarkMode } from 'storybook-dark-mode' // uncomment out this one line for dark mode export const parameters = { @@ -58,7 +61,9 @@ const preview: Preview = { apikey={import.meta.env.STORYBOOK_COVALENT_API_KEY} > - + + + ), diff --git a/src/components/Atoms/Address/Address.stories.tsx b/src/components/Atoms/Address/Address.stories.tsx index 9da75cab..77272d57 100644 --- a/src/components/Atoms/Address/Address.stories.tsx +++ b/src/components/Atoms/Address/Address.stories.tsx @@ -1,16 +1,16 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { Address } from "./Address"; +import { AddressView } from "./AddressView"; -const meta: Meta = { +const meta: Meta = { title: "Atoms/Address", - component: Address, + component: AddressView, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const AddressDisplay: Story = { +export const Address: Story = { args: { address: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", }, diff --git a/src/components/Atoms/Address/Address.tsx b/src/components/Atoms/Address/AddressView.tsx similarity index 81% rename from src/components/Atoms/Address/Address.tsx rename to src/components/Atoms/Address/AddressView.tsx index 7d192a0b..68f2e861 100644 --- a/src/components/Atoms/Address/Address.tsx +++ b/src/components/Atoms/Address/AddressView.tsx @@ -1,8 +1,8 @@ import { copyToClipboard, truncate } from "@/utils/functions"; -import { IconWrapper } from "@/components/Atoms/IconWrapper/IconWrapper"; +import { IconWrapper } from "@/components/Shared"; import { type AddressProps } from "@/utils/types/atoms.types"; -export const Address: React.FC = ({ address }) => { +export const AddressView: React.FC = ({ address }) => { return (

{truncate(address)}

diff --git a/src/components/Atoms/AddressAvatar/AddressAvatar.stories.tsx b/src/components/Atoms/AddressAvatar/AddressAvatar.stories.tsx index 60410f44..d05e4e02 100644 --- a/src/components/Atoms/AddressAvatar/AddressAvatar.stories.tsx +++ b/src/components/Atoms/AddressAvatar/AddressAvatar.stories.tsx @@ -1,65 +1,63 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { AddressAvatar } from "./AddressAvatar"; +import { AddressAvatarView } from "./AddressAvatarView"; import { type AddressAvatarProps } from "@/utils/types/atoms.types"; import { GRK_SIZES } from "@/utils/constants/shared.constants"; -const meta: Meta = { - title: "Atoms/AddressAvatar", - component: AddressAvatar, +const meta: Meta = { + title: "Atoms/Address Avatar", + component: AddressAvatarView, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; -const render = ({ size, type, address, rounded }: AddressAvatarProps) => { - return ( - <> -
- -
- -
- - - - - -
- - ); -}; - -export const AvatarSizes: Story = { +export const AddressAvatar: Story = { args: { size: GRK_SIZES.SMALL, type: "fingerprint", address: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", }, - render: render, + render: ({ size, type, address, rounded }: AddressAvatarProps) => { + return ( + <> +
+ +
+ +
+ + + + + +
+ + ); + }, }; diff --git a/src/components/Atoms/AddressAvatar/AddressAvatar.tsx b/src/components/Atoms/AddressAvatar/AddressAvatarView.tsx similarity index 96% rename from src/components/Atoms/AddressAvatar/AddressAvatar.tsx rename to src/components/Atoms/AddressAvatar/AddressAvatarView.tsx index 410376cb..8aef716e 100644 --- a/src/components/Atoms/AddressAvatar/AddressAvatar.tsx +++ b/src/components/Atoms/AddressAvatar/AddressAvatarView.tsx @@ -6,7 +6,7 @@ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { type AddressAvatarProps } from "@/utils/types/atoms.types"; import { useMemo } from "react"; -export const AddressAvatar: React.FC = ({ +export const AddressAvatarView: React.FC = ({ address, type, size, diff --git a/src/components/Atoms/NetPriceDelta/NetPriceDelta.tsx b/src/components/Atoms/NetPriceDelta/NetPriceDelta.tsx deleted file mode 100644 index a4fadffa..00000000 --- a/src/components/Atoms/NetPriceDelta/NetPriceDelta.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useMemo } from "react"; -import { type NetPriceDeltaProps } from "@/utils/types/atoms.types"; - -export const NetPriceDelta: React.FC = ({ - numerator, - denominator, -}) => { - const DELTA = useMemo( - () => ((numerator - denominator) / denominator) * 100, - [numerator, denominator] - ); - - const AMOUNT = useMemo(() => (DELTA / 100) * denominator, [DELTA]); - - const SUMMARY_STRING = useMemo( - () => - `${DELTA.toLocaleString("en", { - maximumFractionDigits: 2, - })}% (${AMOUNT.toLocaleString("en", { - style: "currency", - currency: "USD", - maximumFractionDigits: 2, - })})`, - [AMOUNT, DELTA] - ); - - if (DELTA > 1) { - return ▲{SUMMARY_STRING}; - } else if (DELTA < 1) { - return ▼{SUMMARY_STRING}; - } - - return -; -}; diff --git a/src/components/Atoms/ThemeChange/ThemeChange.stories.tsx b/src/components/Atoms/ThemeChange/ThemeChange.stories.tsx deleted file mode 100644 index 507b4f53..00000000 --- a/src/components/Atoms/ThemeChange/ThemeChange.stories.tsx +++ /dev/null @@ -1,349 +0,0 @@ -import { type Meta, type StoryObj } from "@storybook/react"; - -import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { updateTheme } from "@/utils/functions"; -import { useEffect } from "react"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Checkbox } from "@/components/ui/checkbox"; -import { COLORS, GRK_SIZES } from "@/utils/constants/shared.constants"; - -const meta: Meta = { - title: "ThemeConfig", - component: Button, - argTypes: { - default_color: { - options: [ - "slate", - "stone", - "red", - "orange", - "amber", - "yellow", - "lime", - "green", - "emerald", - "cyan", - "sky", - "blue", - "indigo", - "violet", - "purple", - "fuchsia", - "pink", - "rose", - ], - control: { type: "radio" }, - }, - border_radius: { - options: ["none", "small", "medium", "large", "full"], - control: { type: "radio" }, - }, - }, -}; -export default meta; - -type Story = StoryObj; - -const renderDarkLightMode = (changeDarkMode: any, changeLightMode: any) => { - return ( -
-

- Pick dark/light mode -

-
- - -
-
- ); -}; - -const renderColors = (changeOnlyColor: any) => { - const color500Array = Object.keys(COLORS).map((key) => [ - key, - COLORS[key]["500"], - ]); - return ( -
-

- Pick a color -

-
- {color500Array.map((color, index) => { - return ( -
changeOnlyColor(color[0])} - className={`neo-shadow cursor-pointer rounded p-2 text-white`} - > - {" "} - {color[0]} -
- ); - })} -
-
- ); -}; - -const renderButtons = () => { - return ( -
-

- Buttons -

-
-
-

Default

- -
- -
-

Secondary

- -
- -
-

Link

- -
- -
-

Destructive

- -
- -
-

Outline

- -
- -
-

Ghost

- -
-
-

Disabled

- -
-
-
- ); -}; - -const renderBadges = () => { - return ( -
-
- Badges -
-
-
-

Default

- Badge -
- -
-

Secondary

- Badge -
-
-

Destructive

- Badge -
-
-

Outline

- Badge -
-
-
- ); -}; - -const renderCheckboxes = () => { - return ( -
-
- Checkboxes -
-
-
-

Default

- Checkbox -
- -
-

Checked

- Checkbox -
- -
-

Disabled

- Checkbox -
-
-
- ); -}; - -const renderSkeleton = () => { - return ( -
-
- Skeleton -
-
-
-

Large

- -
-
-

Medium

- -
-
-

Small

- -
-
-

Extra Small

- -
-
-

Extra Extra Small

- -
-
-
- ); -}; - -const renderNeoBrutalism = ({ changeToNeo, changeToClassic }: any) => { - return ( -
- - -
- ); -}; - -export const ChangeColor: Story = { - render: ({ default_color, border_radius }) => { - function changeColor(accentcolor: string, border_radius: string) { - const theme = { - accentcolor: accentcolor, - borderradius: border_radius, - }; - updateTheme(theme); - } - - function changeOnlyColor(accentcolor: string) { - const theme = { - accentcolor: accentcolor, - }; - updateTheme(theme); - } - function changeDarkMode() { - const body = document.body; - const root = document.documentElement; - - body.classList.add("dark"); - root.classList.add("dark"); - } - function changeLightMode() { - const body = document.body; - const root = document.documentElement; - - body.classList.remove("dark"); - root.classList.remove("dark"); - } - - function changeToNeo() { - const theme = { - backgroundColor: { - light: "#eff6ff", - dark: "#1d4ed8", - }, - borderColor: { - light: "#bfdbfe", - dark: "#1e40af", - }, - secondary: { - light: "#64748b", - }, - surfaceColor: { - light: "#bfdbfe", - dark: "#bfdbfe", - }, - secondaryColor: { - light: "#64748b", - dark: "#64748b", - }, - }; - updateTheme(theme); - const body = document.body; - body.classList.add("neo"); - } - - function changeToClassic() { - const theme = { - backgroundColor: { - light: "#ffffff", - dark: "#030711", - }, - borderColor: { - light: "#e5e7eb", - dark: "#1f2937", - }, - surfaceColor: { - light: "#e5e7eb", - dark: "#e5e7eb", - }, - secondaryColor: { - light: "#94a3b8", - dark: "#94a3b8", - }, - }; - updateTheme(theme); - const body = document.body; - body.classList.remove("neo"); - } - - useEffect(() => { - // changeSurfaceColor(); - }, []); - - useEffect(() => { - changeColor(default_color, border_radius); - }, [default_color, border_radius]); - - return ( -
- {renderDarkLightMode(changeDarkMode, changeLightMode)} - {renderColors(changeOnlyColor)} - {/* {renderSurfaceColors(changeSurfaceColor)} */} - {renderNeoBrutalism({ changeToClassic, changeToNeo })} - {renderButtons()} - {renderBadges()} - {renderCheckboxes()} - {renderSkeleton()} -
- ); - }, - args: { - default_color: "red", - border_radius: "medium", - }, -}; diff --git a/src/components/Atoms/TokenAvatar/TokenAvatar.stories.tsx b/src/components/Atoms/TokenAvatar/TokenAvatar.stories.tsx index 4c76c86d..c79258d2 100644 --- a/src/components/Atoms/TokenAvatar/TokenAvatar.stories.tsx +++ b/src/components/Atoms/TokenAvatar/TokenAvatar.stories.tsx @@ -1,17 +1,17 @@ import { type Meta, type StoryObj } from "@storybook/react"; import { GRK_SIZES } from "@/utils/constants/shared.constants"; -import { TokenAvatar } from "./TokenAvatar"; +import { TokenAvatarView } from "./TokenAvatarView"; -const meta: Meta = { +const meta: Meta = { title: "Atoms/TokenAvatar", - component: TokenAvatar, + component: TokenAvatarView, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const TokenAvatarDisplay: Story = { +export const TokenAvatar: Story = { args: { size: GRK_SIZES.SMALL, }, @@ -19,36 +19,36 @@ export const TokenAvatarDisplay: Story = { return ( <>
-
- - - - -
- - - - = ({ +export const TokenAvatarView: React.FC = ({ token_url, sub_url, size, diff --git a/src/components/Molecules/AccountCardView/AccountCardView.tsx b/src/components/Molecules/AccountCardView/AccountCardView.tsx index 0e55478b..5a485a34 100644 --- a/src/components/Molecules/AccountCardView/AccountCardView.tsx +++ b/src/components/Molecules/AccountCardView/AccountCardView.tsx @@ -8,8 +8,8 @@ import { import QRCode from "react-qr-code"; import { useState } from "react"; import { useToast } from "../../../utils/hooks/use-toast"; -import { AddressAvatar } from "../../Atoms/AddressAvatar/AddressAvatar"; -import { IconWrapper } from "../../Atoms/IconWrapper/IconWrapper"; +import { AddressAvatarView } from "../../Atoms/AddressAvatar/AddressAvatarView"; +import { IconWrapper } from "../../Shared"; import { GRK_SIZES } from "@/utils/constants/shared.constants"; import { type AccountCardViewProps } from "@/utils/types/molecules.types"; @@ -34,7 +34,7 @@ export const AccountCardView: React.FC = ({ return ( <>
- ; +type Story = StoryObj; -const meta: Meta = { +const meta: Meta = { title: "Organisms/NFTs", - component: NFTDetailView, + component: NFTDetailsView, }; export default meta; -export const NFTDetail: Story = { +export const NFTDetails: Story = { args: { chain_name: "eth-mainnet", collection_address: "0xe785e82358879f061bc3dcac6f0444462d4b5330", diff --git a/src/components/Organisms/NFTs/NFTDetailView/NFTDetailView.tsx b/src/components/Organisms/NFTs/NFTDetailsView/NFTDetailsView.tsx similarity index 96% rename from src/components/Organisms/NFTs/NFTDetailView/NFTDetailView.tsx rename to src/components/Organisms/NFTs/NFTDetailsView/NFTDetailsView.tsx index ce80d6b6..100e85aa 100644 --- a/src/components/Organisms/NFTs/NFTDetailView/NFTDetailView.tsx +++ b/src/components/Organisms/NFTs/NFTDetailsView/NFTDetailsView.tsx @@ -4,11 +4,11 @@ import { useCovalent } from "@/utils/store/Covalent"; import { NFTSalesCountView } from "@/components/Molecules/NFTs/NFTSalesCountView/NFTSalesCountView"; import { NFTFloorPriceView } from "@/components/Molecules/NFTs/NFTFloorPriceView/NFTFloorPriceView"; import { type Option, Some, None } from "@/utils/option"; -import { type NFTDetailViewProps } from "@/utils/types/organisms.types"; +import { type NFTDetailsViewProps } from "@/utils/types/organisms.types"; import { type NftTokenContract } from "@covalenthq/client-sdk"; import { useEffect, useState } from "react"; -export const NFTDetailView: React.FC = ({ +export const NFTDetailsView: React.FC = ({ chain_name, collection_address, token_id, diff --git a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx index 242adac1..0a2482ed 100644 --- a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx +++ b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx @@ -18,7 +18,7 @@ import { AccountCardView } from "@/components/Molecules/AccountCardView/AccountC import { Skeleton } from "@/components/ui/skeleton"; import { useCovalent } from "@/utils/store/Covalent"; import { type NFTWalletTokenListViewProps } from "@/utils/types/organisms.types"; -import { TokenAvatar } from "@/components/Atoms/TokenAvatar/TokenAvatar"; +import { TokenAvatarView } from "@/components/Atoms/TokenAvatar/TokenAvatarView"; export const NFTWalletTokenListView: React.FC = ({ chain_names, @@ -140,7 +140,7 @@ export const NFTWalletTokenListView: React.FC = ({ borderColor: `${chainColor}`, }} > - ; const meta: Meta = { - title: "Organisms/Address Activity", + title: "Organisms/Token Balances", component: AddressActivityListView, }; diff --git a/src/components/Organisms/TokenBalances/AddressActivityListView/AddressActivityListView.tsx b/src/components/Organisms/TokenBalances/AddressActivityListView/AddressActivityListView.tsx index 96e88570..c491891b 100644 --- a/src/components/Organisms/TokenBalances/AddressActivityListView/AddressActivityListView.tsx +++ b/src/components/Organisms/TokenBalances/AddressActivityListView/AddressActivityListView.tsx @@ -21,9 +21,9 @@ import { Checkbox } from "@/components/ui/checkbox"; import { AccountCardView } from "@/components/Molecules/AccountCardView/AccountCardView"; import { Skeleton } from "@/components/ui/skeleton"; import { timestampParser } from "@/utils/functions"; -import { TokenAvatar } from "@/components/Atoms/TokenAvatar/TokenAvatar"; +import { TokenAvatarView } from "@/components/Atoms/TokenAvatar/TokenAvatarView"; import { TableHeaderSorting } from "@/components/ui/tableHeaderSorting"; -import { IconWrapper } from "@/components/Atoms/IconWrapper/IconWrapper"; +import { IconWrapper } from "@/components/Shared"; import { GRK_SIZES } from "@/utils/constants/shared.constants"; import { useCovalent } from "@/utils/store/Covalent"; import { type AddressActivityListViewProps } from "@/utils/types/organisms.types"; @@ -122,7 +122,7 @@ export const AddressActivityListView: React.FC< cell: ({ row }) => { return (
- = ({ return (
- = ({ return (
- {" "} = ({ return (
- ; const meta: Meta = { - title: "Organisms/Token Transfers", + title: "Organisms/Token Balances", component: TokenTransfersListView, }; diff --git a/src/components/Organisms/TokenBalances/TokenTransfersListView/TokenTransfersListView.tsx b/src/components/Organisms/TokenBalances/TokenTransfersListView/TokenTransfersListView.tsx index 4f4ae9de..9505f793 100644 --- a/src/components/Organisms/TokenBalances/TokenTransfersListView/TokenTransfersListView.tsx +++ b/src/components/Organisms/TokenBalances/TokenTransfersListView/TokenTransfersListView.tsx @@ -26,7 +26,7 @@ import { } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { Skeleton } from "@/components/ui/skeleton"; -import { TokenAvatar } from "../../../Atoms/TokenAvatar/TokenAvatar"; +import { TokenAvatarView } from "../../../Atoms/TokenAvatar/TokenAvatarView"; import { timestampParser, truncate, @@ -44,8 +44,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { AddressAvatar } from "@/components/Atoms/AddressAvatar/AddressAvatar"; -import { IconWrapper } from "@/components/Atoms/IconWrapper/IconWrapper"; +import { AddressAvatarView } from "@/components/Atoms/AddressAvatar/AddressAvatarView"; +import { IconWrapper } from "@/components/Shared"; import { GRK_SIZES } from "@/utils/constants/shared.constants"; import { type TokenTransfersListViewProps, @@ -99,7 +99,7 @@ const columns: ColumnDef[] = [ cell: ({ row }) => { return (
- [] = [ cell: ({ row }) => { return (
- = ({ ) ?? null; return ( <> - = ({ Some: (result) => { return ( <> - = ({ +const BalancePriceDelta: React.FC = ({ numerator, denominator, }) => { @@ -26,3 +26,5 @@ export const BalancePriceDelta: React.FC = ({ return -; }; + +export default BalancePriceDelta; diff --git a/src/components/Atoms/CopyImage/CopyImage.tsx b/src/components/Shared/CopyImage.tsx similarity index 79% rename from src/components/Atoms/CopyImage/CopyImage.tsx rename to src/components/Shared/CopyImage.tsx index e08a439f..1d43a69f 100644 --- a/src/components/Atoms/CopyImage/CopyImage.tsx +++ b/src/components/Shared/CopyImage.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; -import { IconWrapper } from "../IconWrapper/IconWrapper"; -import { type CopyImageProps } from "@/utils/types/atoms.types"; +import { IconWrapper } from "."; +import { type CopyImageProps } from "@/utils/types/shared.types"; import { copyToClipboard } from "@/utils/functions"; -export const CopyImage: React.FC = ({ url }) => { +const CopyImage: React.FC = ({ url }) => { const [copied, showCopied] = useState(false); return copied ? ( @@ -26,3 +26,5 @@ export const CopyImage: React.FC = ({ url }) => { /> ); }; + +export default CopyImage; diff --git a/src/components/Atoms/IconWrapper/IconWrapper.tsx b/src/components/Shared/IconWrapper.tsx similarity index 87% rename from src/components/Atoms/IconWrapper/IconWrapper.tsx rename to src/components/Shared/IconWrapper.tsx index 85bea7dc..fbe3af62 100644 --- a/src/components/Atoms/IconWrapper/IconWrapper.tsx +++ b/src/components/Shared/IconWrapper.tsx @@ -1,6 +1,6 @@ import { type IconWrapperPropsType } from "@/utils/types/atoms.types"; -export const IconWrapper: React.FC = ({ +const IconWrapper: React.FC = ({ class_name, icon_class_name, on_click, @@ -23,3 +23,5 @@ export const IconWrapper: React.FC = ({
); }; + +export default IconWrapper; diff --git a/src/components/Shared/index.tsx b/src/components/Shared/index.tsx new file mode 100644 index 00000000..f8458170 --- /dev/null +++ b/src/components/Shared/index.tsx @@ -0,0 +1,3 @@ +export { default as BalancePriceDelta } from "./BalancePriceDelta"; +export { default as CopyImage } from "./CopyImage"; +export { default as IconWrapper } from "./IconWrapper"; diff --git a/src/components/ThemeConfig/ThemeConfig.stories.tsx b/src/components/ThemeConfig/ThemeConfig.stories.tsx new file mode 100644 index 00000000..99453fdf --- /dev/null +++ b/src/components/ThemeConfig/ThemeConfig.stories.tsx @@ -0,0 +1,46 @@ +import { type Meta, type StoryObj } from "@storybook/react"; +import { ThemeConfigView } from "./ThemeConfigView"; + +const meta: Meta = { + title: "Theme Config", + component: ThemeConfigView, + argTypes: { + default_color: { + options: [ + "slate", + "stone", + "red", + "orange", + "amber", + "yellow", + "lime", + "green", + "emerald", + "cyan", + "sky", + "blue", + "indigo", + "violet", + "purple", + "fuchsia", + "pink", + "rose", + ], + control: { type: "radio" }, + }, + border_radius: { + options: ["none", "small", "medium", "large", "full"], + control: { type: "radio" }, + }, + }, +}; +export default meta; + +type Story = StoryObj; + +export const ThemeConfig: Story = { + args: { + default_color: "red", + border_radius: "medium", + }, +}; diff --git a/src/components/ThemeConfig/ThemeConfigView.tsx b/src/components/ThemeConfig/ThemeConfigView.tsx new file mode 100644 index 00000000..e41cc1f8 --- /dev/null +++ b/src/components/ThemeConfig/ThemeConfigView.tsx @@ -0,0 +1,198 @@ +import { type AddressAvatarProps } from "@/utils/types/atoms.types"; +import { useMemo } from "react"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Checkbox } from "@/components/ui/checkbox"; +import { COLORS, GRK_SIZES } from "@/utils/constants/shared.constants"; +import { useTheme } from "@/utils/store"; +import { Button } from "../ui/button"; + +export const ThemeConfigView: React.FC = ({}) => { + const { changeAccent, changeMode, changeStyle } = useTheme(); + + const ACCENTS_500 = useMemo(() => { + return Object.keys(COLORS).map((key) => [key, COLORS[key]["500"]]); + }, []); + + return ( +
+
+

+ Pick dark/light mode +

+
+ + +
+
+ +
+

+ Pick an accent color +

+
+ {ACCENTS_500.map((color, index) => { + return ( +
changeAccent(color[0])} + className={`neo-shadow cursor-pointer rounded p-2 text-white`} + > + {color[0]} +
+ ); + })} +
+
+ +
+

+ Pick a style +

+
+ + +
+
+ +
+

+ Buttons +

+
+
+

Default

+ +
+ +
+

Secondary

+ +
+ +
+

Link

+ +
+ +
+

Destructive

+ +
+ +
+

Outline

+ +
+ +
+

Ghost

+ +
+
+

Disabled

+ +
+
+
+ +
+
+ Badges +
+
+
+

Default

+ Badge +
+ +
+

Secondary

+ Badge +
+
+

Destructive

+ Badge +
+
+

Outline

+ Badge +
+
+
+ +
+
+ Checkboxes +
+
+
+

Default

+ Checkbox +
+ +
+

Checked

+ Checkbox +
+ +
+

Disabled

+ Checkbox +
+
+
+ +
+
+ Skeleton +
+
+
+

Large

+ +
+
+

Medium

+ +
+
+

Small

+ +
+
+

Extra Small

+ +
+
+

Extra Extra Small

+ +
+
+
+
+ ); +}; diff --git a/src/components/ui/tableHeaderSorting.tsx b/src/components/ui/tableHeaderSorting.tsx index 3b386175..538cb817 100644 --- a/src/components/ui/tableHeaderSorting.tsx +++ b/src/components/ui/tableHeaderSorting.tsx @@ -1,5 +1,5 @@ import { type Column } from "@tanstack/react-table"; -import { IconWrapper } from "../Atoms/IconWrapper/IconWrapper"; +import { IconWrapper } from "../Shared"; export interface TableHeaderSortingProps { header_name: string; diff --git a/src/utils/functions/index.ts b/src/utils/functions/index.ts index e7487748..8909eded 100644 --- a/src/utils/functions/index.ts +++ b/src/utils/functions/index.ts @@ -8,4 +8,3 @@ export { rootColor } from "./root-color"; export { stringToColor } from "./string-to-color"; export { timestampParser } from "./timestamp-parser"; export { truncate } from "./truncate"; -export { updateTheme } from "./update-theme"; diff --git a/src/utils/functions/update-theme.ts b/src/utils/functions/update-theme.ts deleted file mode 100644 index 366dd325..00000000 --- a/src/utils/functions/update-theme.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { COLORS } from "../constants/shared.constants"; -import { hexToHsl } from "../functions"; - -type THEME = { - accentcolor?: string; - surfaceColor?: { - light?: string; - dark?: string; - }; - borderradius?: string; - backgroundColor?: { - light: string; - dark: string; - }; - borderColor?: { - light: string; - dark: string; - }; - secondaryColor?: { - light: string; - dark: string; - }; -}; - -export const updateTheme = (theme: THEME) => { - if (typeof document === "undefined") return; - const root = document.documentElement; - - if (theme.backgroundColor?.light) { - root.style.setProperty( - "--grk-background", - hexToHsl(theme.backgroundColor.light) - ); - } - if (theme.backgroundColor?.dark) { - root.style.setProperty( - "--grk-background-dark", - hexToHsl(theme.backgroundColor.dark) - ); - } - - if (theme.secondaryColor?.light) { - root.style.setProperty( - "--grk-secondary", - hexToHsl(theme.secondaryColor.light) - ); - } - if (theme.secondaryColor?.dark) { - root.style.setProperty( - "--grk-secondary-dark", - hexToHsl(theme.secondaryColor.dark) - ); - } - - if (theme.surfaceColor?.light) { - root.style.setProperty( - "--grk-surface", - hexToHsl(theme.surfaceColor.light) - ); - } - if (theme.surfaceColor?.dark) { - root.style.setProperty( - "--grk-surface-dark", - hexToHsl(theme.surfaceColor.dark) - ); - } - if (theme.borderColor?.light) { - root.style.setProperty( - "--grk-border", - hexToHsl(theme.borderColor.light) - ); - } - if (theme.borderColor?.dark) { - root.style.setProperty( - "--grk-border-dark", - hexToHsl(theme.borderColor.dark) - ); - } - - if (theme.accentcolor) { - const root = document.documentElement; - const classes = root.classList; - - if (classes.contains("dark")) { - classes.forEach((className) => { - if (className !== "dark") { - classes.remove(className); - } - }); - } else { - root.className = ""; - } - - root.classList.add(theme.accentcolor); - - root.style.setProperty( - "--grk-accent", - hexToHsl(COLORS[theme.accentcolor][500]) - ); - root.style.setProperty( - "--grk-accent-foreground", - hexToHsl(COLORS[theme.accentcolor][200]) - ); - root.style.setProperty( - "--grk-accent-dark", - hexToHsl(COLORS[theme.accentcolor][800]) - ); - - root.style.setProperty( - "--grk-muted", - hexToHsl(COLORS[theme.accentcolor][200]) - ); - root.style.setProperty( - "--grk-muted-foreground", - hexToHsl(COLORS[theme.accentcolor][500]) - ); - root.style.setProperty( - "--grk-muted-dark", - hexToHsl(COLORS[theme.accentcolor][200]) - ); - root.style.setProperty( - "--grk-muted-foreground-dark", - hexToHsl(COLORS[theme.accentcolor][200]) - ); - } - - // To change the broder radius - if (theme.borderradius) { - let borderradius = ""; - switch (theme.borderradius) { - case "none": - borderradius = "0.0rem"; - break; - case "small": - borderradius = "0.25rem"; - break; - case "medium": - borderradius = "0.5rem"; - break; - case "large": - borderradius = "0.75rem"; - break; - case "full": - borderradius = "1rem"; - break; - default: - borderradius = "0.5rem"; - break; - } - root.style.setProperty("--grk-radius", borderradius); - } -}; diff --git a/src/utils/store/Theme.tsx b/src/utils/store/Theme.tsx new file mode 100644 index 00000000..6c041f02 --- /dev/null +++ b/src/utils/store/Theme.tsx @@ -0,0 +1,242 @@ +import { createContext, useCallback, useContext, useEffect } from "react"; +import { hexToHsl } from "../functions"; +import { COLORS } from "../constants/shared.constants"; +import { + type ThemeContextType, + type ThemeColors, + type ThemeProviderProps, +} from "../types/shared.types"; + +const ThemeContext = createContext({} as ThemeContextType); + +export const ThemeProvider: React.FC = ({ + children, + theme: { + mode = "light", + style = "classic", + accent = "slate", + border_radius = "medium", + }, +}) => { + const changeBorderRadius = useCallback((border_radius: string) => { + let _border_radius: string | null = null; + switch (border_radius) { + case "none": + _border_radius = "0.0rem"; + break; + case "small": + _border_radius = "0.25rem"; + break; + case "medium": + _border_radius = "0.5rem"; + break; + case "large": + _border_radius = "0.75rem"; + break; + case "full": + border_radius = "1rem"; + break; + default: + _border_radius = "0.5rem"; + break; + } + const root = document.documentElement; + root.style.setProperty("--grk-radius", _border_radius); + }, []); + + const changeMode = useCallback((mode: "dark" | "light") => { + const body = document.body; + const root = document.documentElement; + + if (mode === "dark") { + body.classList.add("dark"); + root.classList.add("dark"); + return; + } + body.classList.remove("dark"); + root.classList.remove("dark"); + }, []); + + const changeStyle = useCallback((style: "classic" | "neo") => { + let themeColors: ThemeColors; + const body = document.body; + switch (style) { + case "classic": { + themeColors = { + background_color: { + light: "#ffffff", + dark: "#030711", + }, + border_color: { + light: "#e5e7eb", + dark: "#1f2937", + }, + surface_color: { + light: "#e5e7eb", + dark: "#e5e7eb", + }, + secondary_color: { + light: "#94a3b8", + dark: "#94a3b8", + }, + }; + body.classList.remove("neo"); + break; + } + case "neo": { + themeColors = { + background_color: { + light: "#eff6ff", + dark: "#1d4ed8", + }, + border_color: { + light: "#bfdbfe", + dark: "#1e40af", + }, + secondary_color: { + light: "#64748b", + dark: "#64748b", + }, + surface_color: { + light: "#bfdbfe", + dark: "#bfdbfe", + }, + }; + body.classList.add("neo"); + break; + } + } + changeColors(themeColors); + }, []); + + const changeColors = useCallback((themeColors: ThemeColors) => { + if (typeof document === "undefined") { + return; + } + + const root = document.documentElement; + + if (themeColors.background_color?.light) { + root.style.setProperty( + "--grk-background", + hexToHsl(themeColors.background_color.light) + ); + } + if (themeColors.background_color?.dark) { + root.style.setProperty( + "--grk-background-dark", + hexToHsl(themeColors.background_color.dark) + ); + } + + if (themeColors.secondary_color?.light) { + root.style.setProperty( + "--grk-secondary", + hexToHsl(themeColors.secondary_color.light) + ); + } + if (themeColors.secondary_color?.dark) { + root.style.setProperty( + "--grk-secondary-dark", + hexToHsl(themeColors.secondary_color.dark) + ); + } + + if (themeColors.surface_color?.light) { + root.style.setProperty( + "--grk-surface", + hexToHsl(themeColors.surface_color.light) + ); + } + if (themeColors.surface_color?.dark) { + root.style.setProperty( + "--grk-surface-dark", + hexToHsl(themeColors.surface_color.dark) + ); + } + + if (themeColors.border_color?.light) { + root.style.setProperty( + "--grk-border", + hexToHsl(themeColors.border_color.light) + ); + } + if (themeColors.border_color?.dark) { + root.style.setProperty( + "--grk-border-dark", + hexToHsl(themeColors.border_color.dark) + ); + } + }, []); + + const changeAccent = useCallback((accentColor: string) => { + const root = document.documentElement; + const classes = root.classList; + + if (classes.contains("dark")) { + classes.forEach((className) => { + if (className !== "dark") { + classes.remove(className); + } + }); + } else { + root.className = ""; + } + + root.classList.add(accentColor); + + root.style.setProperty( + "--grk-accent", + hexToHsl(COLORS[accentColor][500]) + ); + root.style.setProperty( + "--grk-accent-foreground", + hexToHsl(COLORS[accentColor][200]) + ); + root.style.setProperty( + "--grk-accent-dark", + hexToHsl(COLORS[accentColor][800]) + ); + + root.style.setProperty( + "--grk-muted", + hexToHsl(COLORS[accentColor][200]) + ); + root.style.setProperty( + "--grk-muted-foreground", + hexToHsl(COLORS[accentColor][500]) + ); + root.style.setProperty( + "--grk-muted-dark", + hexToHsl(COLORS[accentColor][200]) + ); + root.style.setProperty( + "--grk-muted-foreground-dark", + hexToHsl(COLORS[accentColor][200]) + ); + }, []); + + useEffect(() => { + if (typeof document !== "undefined") { + changeBorderRadius(border_radius); + changeAccent(accent); + changeMode(mode); + changeStyle(style); + } + }, []); + + return ( + + {children} + + ); +}; + +export const useTheme = () => useContext(ThemeContext); diff --git a/src/utils/store/index.tsx b/src/utils/store/index.tsx new file mode 100644 index 00000000..ed065393 --- /dev/null +++ b/src/utils/store/index.tsx @@ -0,0 +1,3 @@ +export { ChainsProvider, useChains } from "./Chains"; +export { CovalentProvider, useCovalent } from "./Covalent"; +export { ThemeProvider, useTheme } from "./Theme"; diff --git a/src/utils/types/atoms.types.ts b/src/utils/types/atoms.types.ts index 3c121499..bcd8ba40 100644 --- a/src/utils/types/atoms.types.ts +++ b/src/utils/types/atoms.types.ts @@ -13,28 +13,6 @@ export interface AddressAvatarProps { class_name?: string; } -export interface BalancePriceDeltaProps { - numerator: number; - denominator: number; -} - -export interface CopyImageProps { - url: string; -} - -export interface IconWrapperPropsType { - class_name?: string; - icon_class_name?: string; - on_click?: (e?: React.MouseEvent) => void; - icon_size?: string; - icon_type?: string; -} - -export interface NetPriceDeltaProps { - numerator: number; - denominator: number; -} - export interface TokenAvatarProps { token_url?: string | null; sub_url?: string | null; diff --git a/src/utils/types/organisms.types.ts b/src/utils/types/organisms.types.ts index 41b73cf7..e5562536 100644 --- a/src/utils/types/organisms.types.ts +++ b/src/utils/types/organisms.types.ts @@ -5,7 +5,7 @@ import { type Chain, } from "@covalenthq/client-sdk"; -export interface NFTDetailViewProps { +export interface NFTDetailsViewProps { chain_name: Chain; collection_address: string; token_id: string; diff --git a/src/utils/types/shared.types.ts b/src/utils/types/shared.types.ts new file mode 100644 index 00000000..b7f78ce1 --- /dev/null +++ b/src/utils/types/shared.types.ts @@ -0,0 +1,90 @@ +import { type ChainItem, type CovalentClient } from "@covalenthq/client-sdk"; + +export interface BalancePriceDeltaProps { + numerator: number; + denominator: number; +} + +export interface ChainsType { + chains: ChainItem[] | null; +} + +export interface ChainsProviderProps { + children: React.ReactNode; +} + +export interface CopyImageProps { + url: string; +} + +export interface CovalentContextType { + covalentClient: CovalentClient; +} + +export interface CovalentProviderProps { + children: React.ReactNode; + apikey: string; +} + +export interface IconWrapperPropsType { + class_name?: string; + icon_class_name?: string; + on_click?: (e?: React.MouseEvent) => void; + icon_size?: string; + icon_type?: string; +} + +export type ThemeColors = Partial<{ + surface_color: { + light: string; + dark: string; + }; + border_radius: string; + background_color: { + light: string; + dark: string; + }; + border_color: { + light: string; + dark: string; + }; + secondary_color: { + light: string; + dark: string; + }; +}>; + +export interface ThemeContextType { + changeAccent: (accent_color: string) => void; + changeBorderRadius: (border_radius: string) => void; + changeMode: (mode: "dark" | "light") => void; + changeStyle: (style: "classic" | "neo") => void; +} + +export interface ThemeProviderProps { + children: React.ReactNode; + theme: Partial<{ + mode: "dark" | "light"; + style: "classic" | "neo"; + border_radius: "none" | "small" | "medium" | "large" | "full"; + accent: + | "slate" + | "stone" + | "red" + | "orange" + | "amber" + | "yellow" + | "lime" + | "green" + | "emerald" + | "cyan" + | "sky" + | "blue" + | "indigo" + | "violet" + | "purple" + | "fuchsia" + | "pink" + | "rose"; + }>; +}