From 084b1a46ddb87e9a91ba8156bdaa4efa081f11fa Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sat, 7 Dec 2024 19:09:21 +0800 Subject: [PATCH 1/9] chore: buttons --- app/history/components/HistoryContent.tsx | 9 +- app/home/HomePage.tsx | 19 ++-- app/markets/components/MarketTableBody.tsx | 6 +- app/markets/components/markets.tsx | 17 ++-- app/positions/components/PositionsContent.tsx | 57 ++++++----- .../components/PositionsSummaryTable.tsx | 13 +-- docs/Styling.md | 68 +++++++++++-- package.json | 1 + src/components/Button/Button.test.tsx | 41 -------- src/components/Button/Button.tsx | 46 --------- src/components/common/Button.tsx | 96 +++++++++++++++++++ src/components/common/PrimaryButton.tsx | 28 ------ src/components/common/index.ts | 1 + src/components/supplyModal.tsx | 25 ++--- src/utils/cn.ts | 11 +++ yarn.lock | 8 ++ 16 files changed, 262 insertions(+), 184 deletions(-) delete mode 100644 src/components/Button/Button.test.tsx delete mode 100644 src/components/Button/Button.tsx create mode 100644 src/components/common/Button.tsx delete mode 100644 src/components/common/PrimaryButton.tsx create mode 100644 src/components/common/index.ts create mode 100644 src/utils/cn.ts diff --git a/app/history/components/HistoryContent.tsx b/app/history/components/HistoryContent.tsx index 2d9acf3c..c7863f48 100644 --- a/app/history/components/HistoryContent.tsx +++ b/app/history/components/HistoryContent.tsx @@ -1,6 +1,7 @@ 'use client'; -import PrimaryButton from '@/components/common/PrimaryButton'; +import Link from 'next/link'; +import { Button } from '@/components/common/Button'; import Header from '@/components/layout/header/Header'; import LoadingScreen from '@/components/Status/LoadingScreen'; import useUserPositions from '@/hooks/useUserPositions'; @@ -28,7 +29,11 @@ export default function HistoryContent({ account }: { account: string }) { )}
- Back to Portfolio + + +
diff --git a/app/home/HomePage.tsx b/app/home/HomePage.tsx index b5644165..59256d0f 100644 --- a/app/home/HomePage.tsx +++ b/app/home/HomePage.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react'; import { useAccount } from 'wagmi'; -import PrimaryButton from '@/components/common/PrimaryButton'; +import Link from 'next/link'; +import { Button } from '@/components/common/Button'; import Header from '@/components/layout/header/Header'; export default function HomePage() { @@ -105,12 +106,16 @@ export default function HomePage() {
- - Why Monarch - - - Get Started - + + + + + +
diff --git a/app/markets/components/MarketTableBody.tsx b/app/markets/components/MarketTableBody.tsx index 2892f5f2..786ab765 100644 --- a/app/markets/components/MarketTableBody.tsx +++ b/app/markets/components/MarketTableBody.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { Tooltip, Button } from '@nextui-org/react'; +import { Tooltip } from '@nextui-org/react'; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; import { FaShieldAlt } from 'react-icons/fa'; import { GoStarFill, GoStar } from 'react-icons/go'; +import { Button } from '@/components/common/Button'; import OracleVendorBadge from '@/components/OracleVendorBadge'; import { formatReadable } from '@/utils/balance'; import { getNetworkImg } from '@/utils/networks'; @@ -159,7 +160,8 @@ export function MarketTableBody({
- +
diff --git a/app/positions/components/PositionsContent.tsx b/app/positions/components/PositionsContent.tsx index bcf0be9c..a13eb204 100644 --- a/app/positions/components/PositionsContent.tsx +++ b/app/positions/components/PositionsContent.tsx @@ -7,6 +7,7 @@ import { useParams } from 'next/navigation'; import { FaHistory, FaGift, FaPlus, FaCircle } from 'react-icons/fa'; import { useAccount } from 'wagmi'; import { Avatar } from '@/components/Avatar/Avatar'; +import { Button } from '@/components/common/Button'; import Header from '@/components/layout/header/Header'; import EmptyScreen from '@/components/Status/EmptyScreen'; import LoadingScreen from '@/components/Status/LoadingScreen'; @@ -64,37 +65,38 @@ export default function Positions() {
- - + - - + {isOwner && ( - - + )}
@@ -126,13 +128,16 @@ export default function Positions() { ) : !hasSuppliedMarkets ? (
- - +
) : ( diff --git a/app/positions/components/PositionsSummaryTable.tsx b/app/positions/components/PositionsSummaryTable.tsx index c879c5ae..1f209d8c 100644 --- a/app/positions/components/PositionsSummaryTable.tsx +++ b/app/positions/components/PositionsSummaryTable.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState, useEffect } from 'react'; -import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from '@nextui-org/react'; +import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem } from '@nextui-org/react'; import { ChevronDownIcon, ChevronUpIcon } from '@radix-ui/react-icons'; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; @@ -10,6 +10,7 @@ import { TokenIcon } from '@/components/TokenIcon'; import { formatReadable, formatBalance } from '@/utils/balance'; import { getNetworkImg } from '@/utils/networks'; import { MarketPosition, GroupedPosition, WarningWithDetail } from '@/utils/types'; +import { Button } from '@/components/common/Button'; import { MarketAssetIndicator, MarketOracleIndicator, @@ -230,9 +231,9 @@ export function PositionsSummaryTable({ @@ -249,10 +250,10 @@ export function PositionsSummaryTable({ @@ -366,9 +367,8 @@ export function PositionsSummaryTable({
diff --git a/docs/Styling.md b/docs/Styling.md index 0d1b8746..86994414 100644 --- a/docs/Styling.md +++ b/docs/Styling.md @@ -1,11 +1,67 @@ -# Styling and CSS guidelines +# Styling and CSS Guidelines + +## Core Components + +Use these shared components instead of raw HTML elements: +- `Button`: For all clickable actions +- `Modal`: For all modal dialogs +- `Card`: For contained content sections +- `Typography`: For text elements ## Colors +- Background: `bg-main` +- Surface elements (cards, buttons): `bg-surface` +- Text: + - Primary: `text-primary` + - Secondary: `text-secondary` + +## Spacing System +- `space-xs`: 0.5rem (8px) +- `space-sm`: 1rem (16px) +- `space-md`: 1.5rem (24px) +- `space-lg`: 2rem (32px) +- `space-xl`: 3rem (48px) + +## Typography +- Font Sizes: + - `text-xs`: 0.75rem (12px) + - `text-sm`: 0.875rem (14px) + - `text-base`: 1rem (16px) + - `text-lg`: 1.125rem (18px) + - `text-xl`: 1.25rem (20px) +- Font Weights: + - Normal: 400 (`font-normal`) + - Medium: 500 (`font-medium`) + - Semibold: 600 (`font-semibold`) + - Bold: 700 (`font-bold`) + +## Rounded Corners +- Small: `rounded-sm` +- Base: `rounded-base` + +## Shadows +- Light: `shadow-sm` +- Base: `shadow-base` +- Heavy: `shadow-lg` + +## Layout +- Use `PageContainer` for consistent page layout +- Use `Section` for consistent vertical spacing +- Maximum content width: `max-w-7xl` +- Standard padding: `px-4 sm:px-6 lg:px-8` -- background: `bg-main` -- card, button: `bg-surface` +## Component Guidelines +1. Buttons: + - Use `rounded-base` for consistency + - Include hover and active states + - Maintain consistent padding: `px-4 py-2` -## Tailwind-Compatible Component +2. Modals: + - Always use `rounded-lg` + - Standard padding: `p-6` + - Consistent max-width: `max-w-lg` -- primary: `text-primary` -- secondary: `text-secondary` +3. Cards: + - Use `rounded-base` + - Consistent padding: `p-4` + - Standard shadow: `shadow-base` diff --git a/package.json b/package.json index 335a3c0b..e0f60157 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "sharp": "^0.33.5", "shikiji": "^0.9.17", "shikiji-core": "^0.9.17", + "tailwind-merge": "^2.5.5", "unified": "^11.0.4", "viem": "2.x", "wagmi": "^2.10.2", diff --git a/src/components/Button/Button.test.tsx b/src/components/Button/Button.test.tsx deleted file mode 100644 index 99ca7a11..00000000 --- a/src/components/Button/Button.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @jest-environment jsdom - */ -import React from 'react'; -import { SymbolIcon } from '@radix-ui/react-icons'; -import { render, screen, within } from '@testing-library/react'; -import Button from './Button'; - -describe('Button', () => { - it('should render Button', () => { - render( - ); -} diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx new file mode 100644 index 00000000..4b3dc7ea --- /dev/null +++ b/src/components/common/Button.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { extendVariants, Button as NextUIButton } from '@nextui-org/react'; +import { cn } from '@/utils/cn'; + +export const Button = extendVariants(NextUIButton, { + variants: { + // Color variants + color: { + primary: "bg-monarch-orange text-white hover:bg-monarch-orange/90", + secondary: "bg-surface text-foreground hover:bg-surface/90", + surface: "bg-surface opacity-80 hover:opacity-100", + hovered: "bg-hovered opacity-80 hover:opacity-100", + success: "bg-success text-white hover:bg-success/90", + warning: "bg-warning text-white hover:bg-warning/90", + danger: "bg-danger text-white hover:bg-danger/90", + }, + // Visual style variants + variant: { + solid: "border-none", + bordered: "border-2 bg-transparent", + light: "bg-surface opacity-80 hover:opacity-100", + flat: "bg-transparent hover:bg-surface/10", + ghost: "bg-transparent hover:bg-surface/5", + shadow: "shadow-lg hover:shadow-xl", + highlight: "bg-hovered text-foreground hover:bg-primary hover:text-white transition-all duration-200 ease-in-out", + }, + // Size variants + size: { + sm: "px-3 py-1.5 text-xs min-w-[64px] h-8", + md: "px-4 py-2 text-sm min-w-[80px] h-10", + lg: "px-6 py-3 text-md min-w-[96px] h-12", + }, + // Loading state + isLoading: { + true: "cursor-not-allowed", + }, + // Full width option + fullWidth: { + true: "w-full", + }, + // Rounded corners + radius: { + none: "rounded-none", + sm: "rounded-sm", + base: "rounded-sm", + lg: "rounded-sm", + full: "rounded-sm", + }, + }, + defaultVariants: { + color: "primary", + variant: "solid", + size: "md", + radius: "base", + fullWidth: false, + }, + compoundVariants: [ + // Bordered variant compounds + { + variant: "bordered", + color: "primary", + class: "border-monarch-orange text-monarch-orange hover:bg-monarch-orange/10", + }, + { + variant: "bordered", + color: "secondary", + class: "border-surface text-foreground hover:bg-surface/10", + }, + // Light variant compounds - override background based on color + { + variant: "light", + color: "surface", + class: "bg-surface", + }, + { + variant: "light", + color: "hovered", + class: "bg-hovered", + }, + // Disabled state compounds + { + isDisabled: "true", + class: "opacity-50 cursor-not-allowed pointer-events-none", + }, + // Loading state styling + { + isLoading: true, + class: "gap-2 [&>span]:opacity-0 [&>svg]:opacity-0 [&>*:not(.loading-spinner)]:opacity-0", + }, + ], +}); + +// Re-export the component type +export type ButtonProps = React.ComponentProps; + +Button.displayName = 'Button'; diff --git a/src/components/common/PrimaryButton.tsx b/src/components/common/PrimaryButton.tsx deleted file mode 100644 index 0645c88e..00000000 --- a/src/components/common/PrimaryButton.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import Link from 'next/link'; - -type PrimaryButtonProps = { - href: string; - children: React.ReactNode; - className?: string; - isSecondary?: boolean; -}; - -export default function PrimaryButton({ - href, - children, - className = '', - isSecondary, -}: PrimaryButtonProps) { - return ( - - - - ); -} diff --git a/src/components/common/index.ts b/src/components/common/index.ts new file mode 100644 index 00000000..8b166a86 --- /dev/null +++ b/src/components/common/index.ts @@ -0,0 +1 @@ +export * from './Button'; diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index 41756b73..9daa32ff 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -9,6 +9,7 @@ import { useAccount, useBalance, useSwitchChain } from 'wagmi'; import morphoBundlerAbi from '@/abis/bundlerV2'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; +import { Button } from './common'; import { useERC20Approval } from '@/hooks/useERC20Approval'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { usePermit2 } from '@/hooks/usePermit2'; @@ -456,31 +457,31 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element
{needSwitchChain ? ( - + ) : (!permit2Authorized && !useEth) || (!usePermit2Setting && !isApproved) ? ( - + ) : ( - + )} diff --git a/src/utils/cn.ts b/src/utils/cn.ts new file mode 100644 index 00000000..d7c8a538 --- /dev/null +++ b/src/utils/cn.ts @@ -0,0 +1,11 @@ +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +/** + * Combines multiple class names using clsx and merges Tailwind classes using tailwind-merge + * @param inputs - Class names to combine + * @returns Combined and merged class names + */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/yarn.lock b/yarn.lock index abd89802..ddb43faf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15323,6 +15323,7 @@ __metadata: stylelint-config-idiomatic-order: "npm:^10.0.0" stylelint-config-standard: "npm:^35.0.0" stylelint-order: "npm:^6.0.4" + tailwind-merge: "npm:^2.5.5" tailwindcss: "npm:^3.4.0" ts-jest: "npm:^29.1.1" typescript: "npm:~5.3.3" @@ -18372,6 +18373,13 @@ __metadata: languageName: node linkType: hard +"tailwind-merge@npm:^2.5.5": + version: 2.5.5 + resolution: "tailwind-merge@npm:2.5.5" + checksum: 10c0/32614dd2b4ddd4fab070d5ec569e6da00e2b34269b9ac2f2ff16733cef29a076c8e2210fbfc1904d7983a8fdb6b3e63d18ca117645f21b12ca7bcf8fe3507241 + languageName: node + linkType: hard + "tailwind-variants@npm:^0.1.20": version: 0.1.20 resolution: "tailwind-variants@npm:0.1.20" From 4ddb53eb4dddc930e9ea2ed17a233e5493a4b660 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 12:42:11 +0800 Subject: [PATCH 2/9] chore: fix refetch --- app/markets/components/markets.tsx | 2 +- src/components/common/Button.tsx | 1 - src/contexts/MarketsContext.tsx | 11 ++++++++--- src/utils/cn.ts | 11 ----------- 4 files changed, 9 insertions(+), 16 deletions(-) delete mode 100644 src/utils/cn.ts diff --git a/app/markets/components/markets.tsx b/app/markets/components/markets.tsx index c3c1acd7..56072cb1 100644 --- a/app/markets/components/markets.tsx +++ b/app/markets/components/markets.tsx @@ -404,7 +404,7 @@ export default function Markets() { className="text-secondary" onClick={handleRefresh} > - + Refresh diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx index 4b3dc7ea..fa444b36 100644 --- a/src/components/common/Button.tsx +++ b/src/components/common/Button.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { extendVariants, Button as NextUIButton } from '@nextui-org/react'; -import { cn } from '@/utils/cn'; export const Button = extendVariants(NextUIButton, { variants: { diff --git a/src/contexts/MarketsContext.tsx b/src/contexts/MarketsContext.tsx index 9483c4dd..02d29f10 100644 --- a/src/contexts/MarketsContext.tsx +++ b/src/contexts/MarketsContext.tsx @@ -109,19 +109,24 @@ export function MarketsProvider({ children }: MarketsProviderProps) { }; }); + console.log('fetched', processedMarkets.length, 'markets'); setMarkets(processedMarkets); } catch (_error) { setError(_error); } finally { - setLoading(false); - setIsRefetching(false); + if (isRefetch) { + setIsRefetching(false); + } else { + setLoading(false); + } } }, [liquidatedMarketIds], ); useEffect(() => { - if (!liquidationsLoading) { + if (!liquidationsLoading && markets.length === 0) { + console.log('triggering fetch markets'); fetchMarkets().catch(console.error); } }, [liquidationsLoading, fetchMarkets]); diff --git a/src/utils/cn.ts b/src/utils/cn.ts deleted file mode 100644 index d7c8a538..00000000 --- a/src/utils/cn.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type ClassValue, clsx } from 'clsx'; -import { twMerge } from 'tailwind-merge'; - -/** - * Combines multiple class names using clsx and merges Tailwind classes using tailwind-merge - * @param inputs - Class names to combine - * @returns Combined and merged class names - */ -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} From fc8a666546759174676aa36062d0dbdf48f3d57b Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 12:49:46 +0800 Subject: [PATCH 3/9] feat: update reward page to use new buttons --- app/market/[chainId]/[marketid]/content.tsx | 14 +++-- app/rewards/components/MarketProgram.tsx | 21 +++---- app/rewards/components/UniformProgram.tsx | 69 ++++++++++----------- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/app/market/[chainId]/[marketid]/content.tsx b/app/market/[chainId]/[marketid]/content.tsx index 66965adf..2b4173ea 100644 --- a/app/market/[chainId]/[marketid]/content.tsx +++ b/app/market/[chainId]/[marketid]/content.tsx @@ -1,7 +1,6 @@ 'use client'; import { useState, useCallback } from 'react'; -import { Button } from '@nextui-org/button'; import { Card, CardHeader, CardBody } from '@nextui-org/card'; import { Spinner } from '@nextui-org/spinner'; import { ExternalLinkIcon, ChevronLeftIcon } from '@radix-ui/react-icons'; @@ -9,6 +8,7 @@ import Image from 'next/image'; import Link from 'next/link'; import { useParams, useRouter, useSearchParams } from 'next/navigation'; import { formatUnits } from 'viem'; +import { Button } from '@/components/common'; import { OracleFeedInfo } from '@/components/FeedInfo/OracleFeedInfo'; import Header from '@/components/layout/header/Header'; import OracleVendorBadge from '@/components/OracleVendorBadge'; @@ -126,20 +126,24 @@ function MarketContent() {
diff --git a/app/rewards/components/MarketProgram.tsx b/app/rewards/components/MarketProgram.tsx index c8dd8f1c..946b274c 100644 --- a/app/rewards/components/MarketProgram.tsx +++ b/app/rewards/components/MarketProgram.tsx @@ -14,6 +14,7 @@ import { getNetworkImg } from '@/utils/networks'; import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; import { MarketProgramType } from '@/utils/types'; +import { Button } from '@/components/common/Button'; type MarketProgramProps = { account: string; @@ -201,18 +202,12 @@ export default function MarketProgram({ )} - -
- +
diff --git a/app/rewards/components/UniformProgram.tsx b/app/rewards/components/UniformProgram.tsx index f4557145..fd7a0e2e 100644 --- a/app/rewards/components/UniformProgram.tsx +++ b/app/rewards/components/UniformProgram.tsx @@ -12,6 +12,7 @@ import { formatReadable, formatBalance } from '@/utils/balance'; import { getNetworkImg } from '@/utils/networks'; import { findToken } from '@/utils/tokens'; import { UniformRewardType } from '@/utils/types'; +import { Button } from '@/components/common/Button'; type UniformProgramProps = { account: string; @@ -92,7 +93,7 @@ export default function UniformProgram({ Pending Claimed Total - Action + Action {rewardsData.map((reward, index) => ( @@ -157,42 +158,36 @@ export default function UniformProgram({ )} - -
- -
+ + ))} From 63ef9243af049450da40d6f3f6cb94fa96378cbe Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 13:34:22 +0800 Subject: [PATCH 4/9] feat: button usage --- app/history/components/HistoryContent.tsx | 2 +- app/home/HomePage.tsx | 8 +- app/market/[chainId]/[marketid]/content.tsx | 8 +- app/markets/components/MarketTableBody.tsx | 2 +- app/markets/components/markets.tsx | 3 +- app/positions/components/PositionsContent.tsx | 19 +-- .../components/PositionsSummaryTable.tsx | 8 +- .../components/RebalanceActionInput.tsx | 5 +- app/positions/components/RebalanceCart.tsx | 15 +- app/positions/components/RebalanceModal.tsx | 20 +-- .../components/SuppliedMarketsDetail.tsx | 7 +- app/rewards/components/MarketProgram.tsx | 8 +- app/rewards/components/UniformProgram.tsx | 4 +- docs/Styling.md | 135 ++++++++++++------ src/components/common/Button.tsx | 93 ++++-------- src/components/supplyModal.tsx | 2 +- 16 files changed, 161 insertions(+), 178 deletions(-) diff --git a/app/history/components/HistoryContent.tsx b/app/history/components/HistoryContent.tsx index c7863f48..03e2619d 100644 --- a/app/history/components/HistoryContent.tsx +++ b/app/history/components/HistoryContent.tsx @@ -30,7 +30,7 @@ export default function HistoryContent({ account }: { account: string }) {
- diff --git a/app/home/HomePage.tsx b/app/home/HomePage.tsx index 59256d0f..84760f62 100644 --- a/app/home/HomePage.tsx +++ b/app/home/HomePage.tsx @@ -1,8 +1,8 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import { useAccount } from 'wagmi'; import Link from 'next/link'; +import { useAccount } from 'wagmi'; import { Button } from '@/components/common/Button'; import Header from '@/components/layout/header/Header'; @@ -107,13 +107,13 @@ export default function HomePage() {
- -
diff --git a/app/market/[chainId]/[marketid]/content.tsx b/app/market/[chainId]/[marketid]/content.tsx index 2b4173ea..d971e9cb 100644 --- a/app/market/[chainId]/[marketid]/content.tsx +++ b/app/market/[chainId]/[marketid]/content.tsx @@ -124,18 +124,12 @@ function MarketContent() {
{/* navigation bottons */}
- - @@ -129,12 +121,7 @@ export default function Positions() {
- diff --git a/app/positions/components/PositionsSummaryTable.tsx b/app/positions/components/PositionsSummaryTable.tsx index 1f209d8c..bdec7514 100644 --- a/app/positions/components/PositionsSummaryTable.tsx +++ b/app/positions/components/PositionsSummaryTable.tsx @@ -6,11 +6,11 @@ import Image from 'next/image'; import { IoRefreshOutline, IoChevronDownOutline } from 'react-icons/io5'; import { toast } from 'react-toastify'; import { useAccount } from 'wagmi'; +import { Button } from '@/components/common/Button'; import { TokenIcon } from '@/components/TokenIcon'; import { formatReadable, formatBalance } from '@/utils/balance'; import { getNetworkImg } from '@/utils/networks'; import { MarketPosition, GroupedPosition, WarningWithDetail } from '@/utils/types'; -import { Button } from '@/components/common/Button'; import { MarketAssetIndicator, MarketOracleIndicator, @@ -233,7 +233,7 @@ export function PositionsSummaryTable({ size="sm" className="font-zen text-secondary opacity-80 transition-all duration-200 ease-in-out hover:opacity-100" > - + {earningsPeriod} @@ -253,7 +253,7 @@ export function PositionsSummaryTable({ onClick={handleManualRefresh} className="font-zen text-secondary opacity-80 transition-all duration-200 ease-in-out hover:opacity-100" > - + Refresh
@@ -367,7 +367,7 @@ export function PositionsSummaryTable({
diff --git a/app/positions/components/RebalanceCart.tsx b/app/positions/components/RebalanceCart.tsx index 1bbbc631..61a1825a 100644 --- a/app/positions/components/RebalanceCart.tsx +++ b/app/positions/components/RebalanceCart.tsx @@ -1,14 +1,7 @@ import React from 'react'; -import { - Table, - TableHeader, - TableColumn, - TableBody, - TableRow, - TableCell, - Button, -} from '@nextui-org/react'; +import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from '@nextui-org/react'; import { formatUnits } from 'viem'; +import { Button } from '@/components/common'; import { Market } from '@/utils/types'; import { GroupedPosition, RebalanceAction } from '@/utils/types'; import { MarketBadge } from './MarketBadge'; @@ -71,10 +64,10 @@ export function RebalanceCart({ diff --git a/app/positions/components/RebalanceModal.tsx b/app/positions/components/RebalanceModal.tsx index ad579967..a20094ed 100644 --- a/app/positions/components/RebalanceModal.tsx +++ b/app/positions/components/RebalanceModal.tsx @@ -5,13 +5,13 @@ import { ModalHeader, ModalBody, ModalFooter, - Button, Spinner, } from '@nextui-org/react'; import { GrRefresh } from 'react-icons/gr'; import { toast } from 'react-toastify'; import { parseUnits, formatUnits } from 'viem'; import { useAccount, useSwitchChain } from 'wagmi'; +import { Button } from '@/components/common'; import { useMarkets } from '@/hooks/useMarkets'; import { usePagination } from '@/hooks/usePagination'; import { useRebalance } from '@/hooks/useRebalance'; @@ -278,15 +278,15 @@ export function RebalanceModal({ Rebalance {groupedPosition.loanAsset ?? 'Unknown'} Position {isRefetching && }
- +
@@ -344,18 +344,18 @@ export function RebalanceModal({ diff --git a/app/positions/components/SuppliedMarketsDetail.tsx b/app/positions/components/SuppliedMarketsDetail.tsx index fe9d512a..90764d7c 100644 --- a/app/positions/components/SuppliedMarketsDetail.tsx +++ b/app/positions/components/SuppliedMarketsDetail.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import { Tooltip, Button } from '@nextui-org/react'; +import { Tooltip } from '@nextui-org/react'; import { motion } from 'framer-motion'; import Link from 'next/link'; import { IoWarningOutline } from 'react-icons/io5'; +import { Button } from '@/components/common'; import OracleVendorBadge from '@/components/OracleVendorBadge'; import { TokenIcon } from '@/components/TokenIcon'; import { formatReadable, formatBalance } from '@/utils/balance'; @@ -204,8 +205,8 @@ export function SuppliedMarketsDetail({
+ +// Table Action + + +// Navigation + + +// Secondary Action + + +// Utility Action + +``` 2. Modals: + - Always use `rounded-lg` - Standard padding: `p-6` - Consistent max-width: `max-w-lg` diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx index fa444b36..c607b211 100644 --- a/src/components/common/Button.tsx +++ b/src/components/common/Button.tsx @@ -3,93 +3,58 @@ import { extendVariants, Button as NextUIButton } from '@nextui-org/react'; export const Button = extendVariants(NextUIButton, { variants: { - // Color variants - color: { - primary: "bg-monarch-orange text-white hover:bg-monarch-orange/90", - secondary: "bg-surface text-foreground hover:bg-surface/90", - surface: "bg-surface opacity-80 hover:opacity-100", - hovered: "bg-hovered opacity-80 hover:opacity-100", - success: "bg-success text-white hover:bg-success/90", - warning: "bg-warning text-white hover:bg-warning/90", - danger: "bg-danger text-white hover:bg-danger/90", - }, - // Visual style variants + // Main variant types that define the button's purpose and behavior variant: { - solid: "border-none", - bordered: "border-2 bg-transparent", - light: "bg-surface opacity-80 hover:opacity-100", - flat: "bg-transparent hover:bg-surface/10", - ghost: "bg-transparent hover:bg-surface/5", - shadow: "shadow-lg hover:shadow-xl", - highlight: "bg-hovered text-foreground hover:bg-primary hover:text-white transition-all duration-200 ease-in-out", + default: 'bg-surface hover:bg-surface/80 transition-all duration-200 ease-in-out', // Default surface-colored button + cta: 'bg-monarch-orange text-white hover:bg-monarch-orange/80 transition-all duration-200 ease-in-out', // Primary CTA with orange background + secondary: 'bg-hovered text-foreground ', + interactive: + 'bg-hovered text-foreground hover:bg-primary hover:text-white transition-all duration-200 ease-in-out', // Starts subtle, strong hover effect + ghost: 'bg-transparent hover:bg-surface/5 transition-all duration-200 ease-in-out', // Most subtle variant }, // Size variants size: { - sm: "px-3 py-1.5 text-xs min-w-[64px] h-8", - md: "px-4 py-2 text-sm min-w-[80px] h-10", - lg: "px-6 py-3 text-md min-w-[96px] h-12", + sm: 'px-3 py-1.5 text-xs min-w-[64px] h-8', + md: 'px-4 py-2 text-sm min-w-[80px] h-10', + lg: 'px-6 py-3 text-md min-w-[96px] h-12', }, - // Loading state - isLoading: { - true: "cursor-not-allowed", + // Rounded corners + radius: { + none: 'rounded-none', + sm: 'rounded-sm', + base: 'rounded-sm', + lg: 'rounded-sm', + full: 'rounded-sm', }, // Full width option fullWidth: { - true: "w-full", + true: 'w-full', }, - // Rounded corners - radius: { - none: "rounded-none", - sm: "rounded-sm", - base: "rounded-sm", - lg: "rounded-sm", - full: "rounded-sm", + // Loading state + isLoading: { + true: 'cursor-not-allowed', }, }, defaultVariants: { - color: "primary", - variant: "solid", - size: "md", - radius: "base", + variant: 'default', + size: 'md', + radius: 'base', fullWidth: false, }, compoundVariants: [ - // Bordered variant compounds + // Disabled state { - variant: "bordered", - color: "primary", - class: "border-monarch-orange text-monarch-orange hover:bg-monarch-orange/10", + isDisabled: 'true', + class: 'opacity-50 cursor-not-allowed pointer-events-none', }, - { - variant: "bordered", - color: "secondary", - class: "border-surface text-foreground hover:bg-surface/10", - }, - // Light variant compounds - override background based on color - { - variant: "light", - color: "surface", - class: "bg-surface", - }, - { - variant: "light", - color: "hovered", - class: "bg-hovered", - }, - // Disabled state compounds - { - isDisabled: "true", - class: "opacity-50 cursor-not-allowed pointer-events-none", - }, - // Loading state styling + // Loading state { isLoading: true, - class: "gap-2 [&>span]:opacity-0 [&>svg]:opacity-0 [&>*:not(.loading-spinner)]:opacity-0", + class: 'gap-2 [&>span]:opacity-0 [&>svg]:opacity-0 [&>*:not(.loading-spinner)]:opacity-0', }, ], }); -// Re-export the component type export type ButtonProps = React.ComponentProps; Button.displayName = 'Button'; diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index 9daa32ff..edf04a0e 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -9,7 +9,6 @@ import { useAccount, useBalance, useSwitchChain } from 'wagmi'; import morphoBundlerAbi from '@/abis/bundlerV2'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; -import { Button } from './common'; import { useERC20Approval } from '@/hooks/useERC20Approval'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { usePermit2 } from '@/hooks/usePermit2'; @@ -19,6 +18,7 @@ import { getExplorerURL } from '@/utils/external'; import { getBundlerV2, getIRMTitle, MONARCH_TX_IDENTIFIER } from '@/utils/morpho'; import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; +import { Button } from './common'; import OracleVendorBadge from './OracleVendorBadge'; import { SupplyProcessModal } from './SupplyProcessModal'; From c703d7bfa55ba0ddd0653102f5a3daff5fd26dbc Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 16:06:01 +0800 Subject: [PATCH 5/9] feat: block --- src/components/SupplyProcessModal.tsx | 51 +++--------------- src/components/common/MarketInfoBlock.tsx | 65 +++++++++++++++++++++++ src/components/supplyModal.tsx | 4 +- src/components/withdrawModal.tsx | 8 +-- 4 files changed, 76 insertions(+), 52 deletions(-) create mode 100644 src/components/common/MarketInfoBlock.tsx diff --git a/src/components/SupplyProcessModal.tsx b/src/components/SupplyProcessModal.tsx index 10d49af1..931b95a2 100644 --- a/src/components/SupplyProcessModal.tsx +++ b/src/components/SupplyProcessModal.tsx @@ -7,6 +7,7 @@ import { formatUnits } from 'viem'; import { formatBalance } from '@/utils/balance'; import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; +import { MarketAmountBlock } from './common/MarketInfoBlock'; type MarketSupply = { market: Market; @@ -103,7 +104,7 @@ export function SupplyProcessModal({ initial={{ scale: 0.95 }} animate={{ scale: 1 }} exit={{ scale: 0.95 }} - className="relative w-full max-w-lg rounded-lg bg-white p-4 shadow-xl dark:bg-gray-900" + className="relative w-full max-w-lg rounded bg-white p-4 shadow-xl dark:bg-gray-900" >
@@ -176,7 +139,7 @@ export function SupplyProcessModal({ return (
+
+ {collateralToken?.img && ( +
+ {market.collateralAsset.symbol} +
+ )} +
+
+ + {market.collateralAsset.symbol} + + + {formatUnits(BigInt(market.lltv), 16)}% LTV + +
+ { amount ? + {formatBalance(amount, market.loanAsset.decimals)}{' '} + {market.loanAsset.symbol} + : } +
+
+
+
+ {(market.state.supplyApy * 100).toFixed(2)}% +
+
Supply APY
+
+
+ ); +} diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index edf04a0e..84dec9ae 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -469,7 +469,7 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element disabled={!isConnected || isLoadingPermit2} onClick={() => void approveAndSupply()} className="ml-2 min-w-32" - variant="solid" + variant="cta" > Approve and Supply @@ -478,7 +478,7 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element disabled={!isConnected || supplyPending || inputError !== null} onClick={() => void signAndSupply()} className="ml-2 min-w-32" - variant="solid" + variant="cta" > {useEth ? 'Supply' : 'Sign and Supply'} diff --git a/src/components/withdrawModal.tsx b/src/components/withdrawModal.tsx index b868e3f1..35decf62 100644 --- a/src/components/withdrawModal.tsx +++ b/src/components/withdrawModal.tsx @@ -9,6 +9,7 @@ import { useAccount, useSwitchChain } from 'wagmi'; import morphoAbi from '@/abis/morpho'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; +import { MarketAmountBlock } from '@/components/common/MarketInfoBlock'; import { useTransactionWithToast } from '@/hooks/useTransactionWithToast'; import { formatBalance, formatReadable, min } from '@/utils/balance'; import { MORPHO } from '@/utils/morpho'; @@ -127,12 +128,7 @@ export function WithdrawModal({ position, onClose, refetch }: ModalProps): JSX.E

-
-

Market ID:

-

- {position.market.uniqueKey.slice(2, 8)} -

-
+

Available Liquidity:

From 34f7052d5ae64e11a67c4813006683efe14829f5 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 16:27:49 +0800 Subject: [PATCH 6/9] chore: modal styling --- app/positions/components/FromAndToMarkets.tsx | 4 +-- app/positions/components/RebalanceModal.tsx | 4 +-- src/components/SupplyProcessModal.tsx | 5 +-- src/components/common/MarketInfoBlock.tsx | 34 ++++++++----------- src/components/supplyModal.tsx | 30 ++++++---------- src/components/withdrawModal.tsx | 4 +-- 6 files changed, 31 insertions(+), 50 deletions(-) diff --git a/app/positions/components/FromAndToMarkets.tsx b/app/positions/components/FromAndToMarkets.tsx index a54a8f37..ec560a48 100644 --- a/app/positions/components/FromAndToMarkets.tsx +++ b/app/positions/components/FromAndToMarkets.tsx @@ -92,7 +92,7 @@ export function FromAndToMarkets({

Your Market Positions

onFromFilterChange(e.target.value)} className="mb-2" @@ -228,7 +228,7 @@ export function FromAndToMarkets({

Available Markets for Rebalancing

onToFilterChange(e.target.value)} className="mb-2" diff --git a/app/positions/components/RebalanceModal.tsx b/app/positions/components/RebalanceModal.tsx index a20094ed..892c089c 100644 --- a/app/positions/components/RebalanceModal.tsx +++ b/app/positions/components/RebalanceModal.tsx @@ -274,7 +274,7 @@ export function RebalanceModal({ > -
+
Rebalance {groupedPosition.loanAsset ?? 'Unknown'} Position {isRefetching && }
@@ -289,7 +289,7 @@ export function RebalanceModal({ -
+

Optimize your {groupedPosition.loanAsset} lending strategy by redistributing funds across markets, add "Rebalance" actions to fine-tune your portfolio. diff --git a/src/components/SupplyProcessModal.tsx b/src/components/SupplyProcessModal.tsx index 931b95a2..34345307 100644 --- a/src/components/SupplyProcessModal.tsx +++ b/src/components/SupplyProcessModal.tsx @@ -1,11 +1,7 @@ import React, { useMemo } from 'react'; import { Cross1Icon } from '@radix-ui/react-icons'; import { motion, AnimatePresence } from 'framer-motion'; -import Image from 'next/image'; import { FaCheckCircle, FaCircle } from 'react-icons/fa'; -import { formatUnits } from 'viem'; -import { formatBalance } from '@/utils/balance'; -import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; import { MarketAmountBlock } from './common/MarketInfoBlock'; @@ -127,6 +123,7 @@ export function SupplyProcessModal({ ); })} diff --git a/src/components/common/MarketInfoBlock.tsx b/src/components/common/MarketInfoBlock.tsx index de75f629..526b6b3c 100644 --- a/src/components/common/MarketInfoBlock.tsx +++ b/src/components/common/MarketInfoBlock.tsx @@ -1,25 +1,20 @@ import React from 'react'; import Image from 'next/image'; -import { Market } from '@/utils/types'; +import { formatUnits } from 'viem'; import { formatBalance } from '@/utils/balance'; import { findToken } from '@/utils/tokens'; -import { formatUnits } from 'viem'; +import { Market } from '@/utils/types'; import OracleVendorBadge from '../OracleVendorBadge'; -interface MarketAmountBlockProps { +type MarketAmountBlockProps = { market: Market; amount?: bigint; lltv?: string; apy?: string; className?: string; -} - -export function MarketAmountBlock({ - market, - amount, - className = '' -}: MarketAmountBlockProps): JSX.Element { +}; +export function MarketAmountBlock({ market, amount }: MarketAmountBlockProps): JSX.Element { const collateralToken = findToken(market.collateralAsset.address, market.morphoBlue.chain.id); return ( @@ -41,23 +36,22 @@ export function MarketAmountBlock({ )}

- - {market.collateralAsset.symbol} - + {market.collateralAsset.symbol} {formatUnits(BigInt(market.lltv), 16)}% LTV
- { amount ? - {formatBalance(amount, market.loanAsset.decimals)}{' '} - {market.loanAsset.symbol} - : } + {amount ? ( + + {formatBalance(amount, market.loanAsset.decimals)} {market.loanAsset.symbol} + + ) : ( + + )}
-
- {(market.state.supplyApy * 100).toFixed(2)}% -
+
{(market.state.supplyApy * 100).toFixed(2)}%
Supply APY
diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index 84dec9ae..e2303a35 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -4,7 +4,7 @@ import { Cross1Icon, ExternalLinkIcon } from '@radix-ui/react-icons'; import Image from 'next/image'; import Link from 'next/link'; import { toast } from 'react-toastify'; -import { Address, encodeFunctionData, formatUnits } from 'viem'; +import { Address, encodeFunctionData } from 'viem'; import { useAccount, useBalance, useSwitchChain } from 'wagmi'; import morphoBundlerAbi from '@/abis/bundlerV2'; import Input from '@/components/Input/Input'; @@ -19,6 +19,7 @@ import { getBundlerV2, getIRMTitle, MONARCH_TX_IDENTIFIER } from '@/utils/morpho import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; import { Button } from './common'; +import { MarketAmountBlock } from './common/MarketInfoBlock'; import OracleVendorBadge from './OracleVendorBadge'; import { SupplyProcessModal } from './SupplyProcessModal'; @@ -38,7 +39,6 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element const { address: account, isConnected, chainId } = useAccount(); - const collateralToken = findToken(market.collateralAsset.address, market.morphoBlue.chain.id); const loanToken = findToken(market.loanAsset.address, market.morphoBlue.chain.id); const { switchChain } = useSwitchChain(); @@ -331,7 +331,7 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element {' '} -
+
Supply {loanToken ? loanToken.symbol : market.loanAsset.symbol} {loanToken?.img && {loanToken.symbol}}
@@ -341,8 +341,12 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element You are supplying {market.loanAsset.symbol} to the following market:{' '}

-
-
+
+ + +
Details
+ +

Market ID:

@@ -350,21 +354,7 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element

-
-

Collateral Token:

-
-

{market.collateralAsset.symbol}

-
- {collateralToken?.img && ( - {collateralToken.symbol} - )}{' '} -
-
-
-
-

LLTV:

-

{formatUnits(BigInt(market.lltv), 16)} %

-
+

Oracle:

{' '} -
+
Withdraw {loanToken ? loanToken.symbol : position.market.loanAsset.symbol} {loanToken?.img && {loanToken.symbol}}
From 45338ad20cb24ff96628b606458ff91632263367 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 16:49:24 +0800 Subject: [PATCH 7/9] chore: paddings --- app/markets/components/marketsTable.tsx | 2 +- src/components/supplyModal.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/markets/components/marketsTable.tsx b/app/markets/components/marketsTable.tsx index 52e19c91..0a6f4f5a 100644 --- a/app/markets/components/marketsTable.tsx +++ b/app/markets/components/marketsTable.tsx @@ -49,7 +49,7 @@ function MarketsTable({ const totalPages = Math.ceil(markets.length / entriesPerPage); return ( -
+
diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index e2303a35..6b3264c5 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -337,7 +337,6 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element

- {' '} You are supplying {market.loanAsset.symbol} to the following market:{' '}

From d2d87344defa8cc5d5c20445df014fd090bfc397 Mon Sep 17 00:00:00 2001 From: Anton Cheng Date: Sun, 8 Dec 2024 20:00:59 +0800 Subject: [PATCH 8/9] chore: review feedbacks --- app/positions/components/RebalanceModal.tsx | 2 +- app/positions/components/RebalanceProcessModal.tsx | 2 +- app/positions/components/SuppliedMarketsDetail.tsx | 2 -- src/components/supplyModal.tsx | 2 +- src/components/withdrawModal.tsx | 2 +- src/contexts/MarketsContext.tsx | 2 -- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/positions/components/RebalanceModal.tsx b/app/positions/components/RebalanceModal.tsx index 892c089c..6d35f756 100644 --- a/app/positions/components/RebalanceModal.tsx +++ b/app/positions/components/RebalanceModal.tsx @@ -268,7 +268,7 @@ export function RebalanceModal({ isDismissable={false} size="5xl" classNames={{ - base: 'min-w-[1250px] z-[1000] p-4', + base: 'min-w-[1250px] z-[1000] p-4 rounded', backdrop: showProcessModal && 'z-[999]', }} > diff --git a/app/positions/components/RebalanceProcessModal.tsx b/app/positions/components/RebalanceProcessModal.tsx index ac273426..5385095a 100644 --- a/app/positions/components/RebalanceProcessModal.tsx +++ b/app/positions/components/RebalanceProcessModal.tsx @@ -65,7 +65,7 @@ export function RebalanceProcessModal({
) : (!permit2Authorized && !useEth) || (!usePermit2Setting && !isApproved) ? (