diff --git a/package.json b/package.json index d2f8e187..22f84647 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,9 @@ "type": "module", "packageManager": "yarn@4.5.1", "dependencies": { - "@chakra-ui/icons": "^2.1.1", - "@chakra-ui/react": "^2.8.2", + "@chakra-ui/react": "^3.1.2", "@chakra-ui/system": "^2.6.2", "@emotion/react": "^11.13.3", - "@emotion/styled": "^11.13.0", "@eslint/compat": "^1.1.1", "@vitejs/plugin-react": "^4.3.1", "dataloader": "^2.2.2", @@ -18,16 +16,18 @@ "eslint-plugin-libram": "^0.4.17", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-unused-imports": "^4.1.4", - "framer-motion": "^11.3.29", "globals": "^15.9.0", "html-entities": "^2.5.2", "kolmafia": "npm:tome-kolmafia-mock@5.28118.0", "libram": "^0.9.18", + "lucide-react": "^0.460.0", + "next-themes": "^0.4.3", "postcss": "^8.4.41", "prettier": "^3.3.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", + "react-icons": "^5.3.0", "react-router-dom": "^6.26.1", "tome-kolmafia-lib": "^5.28118.0", "tome-kolmafia-react": "^0.2.5", diff --git a/src/App.tsx b/src/App.tsx index e7d6383c..91c47883 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,48 +1,12 @@ -import { ChakraProvider, extendTheme } from "@chakra-ui/react"; +import { ChakraProvider } from "@chakra-ui/react"; import { RefreshContextProvider } from "tome-kolmafia-react"; import NagContextProvider from "./contexts/NagContextProvider"; import Layout from "./Layout"; - -const bulleted = { - container: { - paddingTop: "0.125rem", - paddingLeft: "1.25rem", - }, - item: { - textIndent: "-0.375rem", - _before: { - content: '"●"', - verticalAlign: "middle", - fontFamily: "Arial, Helvetica, sans-serif", - fontSize: "0.75rem", - lineHeight: 0, - display: "inline-block", - width: "0.375rem", - // This is a hackish tweak... - marginTop: "-3px", - }, - }, -}; - -const theme = extendTheme({ - lineHeights: { - none: 1, - shorter: 1.05, - short: 1.1, - base: 1.15, - tall: 1.25, - taller: 1.5, - }, - components: { - List: { - variants: { bulleted }, - }, - }, -}); +import { system } from "./theme"; const App = () => ( - + diff --git a/src/Layout.tsx b/src/Layout.tsx index fc3359a1..d51f985b 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -1,11 +1,4 @@ -import { - Box, - Container, - Divider, - Flex, - Stack, - useToast, -} from "@chakra-ui/react"; +import { Box, Container, Flex, Separator, Stack } from "@chakra-ui/react"; import { useCallback, useContext, useEffect, useState } from "react"; import { setGlobalErrorHandler } from "tome-kolmafia-lib"; import { RefreshContext } from "tome-kolmafia-react"; @@ -15,6 +8,7 @@ import ChatButton from "./components/ChatButton"; import LocationBar from "./components/LocationBar"; import PrefsButton from "./components/PrefsButton"; import RefreshButton from "./components/RefreshButton"; +import { toaster, Toaster } from "./components/ui/toaster"; import { addDevelopmentListeners } from "./prefs/addListeners"; import NagSection from "./sections/NagSection"; import QuestSection from "./sections/QuestSection"; @@ -52,26 +46,23 @@ const Layout = () => { } }, [triggerHardRefresh]); - const toast = useToast(); useEffect(() => { setGlobalErrorHandler((err) => { console.error(err); - toast({ + toaster.error({ title: "Error updating.", description: err !== null && (typeof err === "object" || typeof err === "string") ? err.toString() : "Unknown error.", - status: "error", duration: 10000, - isClosable: true, - containerStyle: { maxW: "95vw" }, }); }); - }, [toast]); + }, []); return ( { { - + @@ -109,6 +100,7 @@ const Layout = () => { /> + ); }; diff --git a/src/components/AdviceTip.tsx b/src/components/AdviceTip.tsx deleted file mode 100644 index 5e37a905..00000000 --- a/src/components/AdviceTip.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Tooltip, TooltipProps } from "@chakra-ui/react"; -import { FC } from "react"; - -const AdviceTip: FC = ({ ...props }) => ( - -); - -export default AdviceTip; diff --git a/src/components/AdviceTooltip.tsx b/src/components/AdviceTooltip.tsx index 53467255..01497714 100644 --- a/src/components/AdviceTooltip.tsx +++ b/src/components/AdviceTooltip.tsx @@ -1,39 +1,23 @@ -import { Box, Text, TooltipProps } from "@chakra-ui/react"; -import { FC, ReactNode } from "react"; +import { FC } from "react"; -import AdviceTip from "./AdviceTip"; +import { Tooltip, TooltipProps } from "./ui/tooltip"; -interface AdviceTooltipProps extends Omit { - text: string | JSX.Element; - label: ReactNode; -} -/** - * A tooltip generated on text for a quick descriptive tooltip. - * @param text The text you want displayed inside this tooltip. - * @param label The text you want displayed that users hover over to get the tooltip - * @returns A FC Tooltip object where the label generates the tooltip on hoverover. - */ - -const AdviceTooltip: FC = ({ text, label, ...props }) => { - const toolTip = ( - - {typeof text === "string" ? {text} : text} - - ); - - return ( - - - {label} - - - ); -}; +const AdviceTooltip: FC = ({ content, ...props }) => ( + +); export default AdviceTooltip; diff --git a/src/components/AdviceTooltipIcon.tsx b/src/components/AdviceTooltipIcon.tsx index d64e5719..3c15edc5 100644 --- a/src/components/AdviceTooltipIcon.tsx +++ b/src/components/AdviceTooltipIcon.tsx @@ -1,19 +1,12 @@ -import { InfoIcon, Text } from "@chakra-ui/icons"; -import { - ComponentWithAs, - HStack, - Icon, - IconProps, - Image, - VStack, -} from "@chakra-ui/react"; +import { HStack, Icon, Image, Text, VStack } from "@chakra-ui/react"; +import { Info } from "lucide-react"; import { FC } from "react"; -import AdviceTip from "./AdviceTip"; +import AdviceTooltip from "./AdviceTooltip"; interface AdviceTooltipIconProps { - text: string; - icon?: ComponentWithAs<"svg", IconProps>; + advice: string; + icon?: typeof Info; } /** * A tooltip generated on an icon hoverover with a skull that states whatever text you want for the player. @@ -22,7 +15,10 @@ interface AdviceTooltipIconProps { * @returns A FC Tooltip object where the displayed icon generates the tooltip on hoverover. */ -const AdviceTooltipIcon: FC = ({ text, icon }) => { +const AdviceTooltipIcon: FC = ({ + advice, + icon: TooltipIcon = Info, +}) => { const toolTip = ( = ({ text, icon }) => { boxSize="30px" fit="contain" /> - - - {text} + + + {advice} ); return ( - - - + + + + + ); }; diff --git a/src/components/AdviceTooltipText.tsx b/src/components/AdviceTooltipText.tsx new file mode 100644 index 00000000..a0c34b4f --- /dev/null +++ b/src/components/AdviceTooltipText.tsx @@ -0,0 +1,43 @@ +import { Box, Text } from "@chakra-ui/react"; +import { FC } from "react"; + +import AdviceTooltip from "./AdviceTooltip"; +import { TooltipProps } from "./ui/tooltip"; + +interface AdviceTooltipProps extends Omit { + advice: string | JSX.Element; +} +/** + * A tooltip generated on text for a quick descriptive tooltip. + * @param text The text you want displayed inside this tooltip. + * @param label The text you want displayed that users hover over to get the tooltip + * @returns A FC Tooltip object where the label generates the tooltip on hoverover. + */ + +const AdviceTooltipText: FC = ({ + advice, + children, + ...props +}) => { + const toolTip = ( + + {typeof advice === "string" ? {advice} : advice} + + ); + + return ( + + + {children} + + + ); +}; + +export default AdviceTooltipText; diff --git a/src/components/AsyncButton.tsx b/src/components/AsyncButton.tsx index f13e179c..89b01b8d 100644 --- a/src/components/AsyncButton.tsx +++ b/src/components/AsyncButton.tsx @@ -1,6 +1,6 @@ -import { ButtonProps, forwardRef, Tooltip, useToast } from "@chakra-ui/react"; +import { ButtonProps, Text } from "@chakra-ui/react"; import { - FC, + forwardRef, MouseEvent, useCallback, useContext, @@ -11,17 +11,18 @@ import { remoteCliExecute } from "tome-kolmafia-lib"; import { RefreshContext } from "tome-kolmafia-react"; import HeaderButton from "./HeaderButton"; +import { toaster } from "./ui/toaster"; +import { Tooltip } from "./ui/tooltip"; export interface AsyncButtonProps extends ButtonProps { href?: string; command?: string; } -const AsyncButton: FC = forwardRef( +const AsyncButton = forwardRef( ({ href, command, onClick, children, ...props }, ref) => { const { triggerHardRefresh } = useContext(RefreshContext); const [isLoading, setIsLoading] = useState(false); - const toast = useToast(); const onClickWithCommand = useMemo( () => @@ -29,17 +30,15 @@ const AsyncButton: FC = forwardRef( ? async () => { const result = await remoteCliExecute(command); if (result === false) { - toast({ + toaster.error({ title: "Command failed.", description: `Failed to execute "${command}".`, - status: "error", duration: 5000, - isClosable: true, }); } } : onClick, - [command, onClick, toast], + [command, onClick], ); const handleClick = useCallback( @@ -57,15 +56,19 @@ const AsyncButton: FC = forwardRef( return ( {command ? ( - - {children} + + {typeof children === "string" ? ( + {children} + ) : ( + children + )} ) : ( children diff --git a/src/components/AsyncLink.tsx b/src/components/AsyncLink.tsx index e015ff99..89245156 100644 --- a/src/components/AsyncLink.tsx +++ b/src/components/AsyncLink.tsx @@ -1,6 +1,5 @@ -import { Link, LinkProps, Spinner, Tooltip, useToast } from "@chakra-ui/react"; +import { Link, LinkProps, Spinner } from "@chakra-ui/react"; import { - FC, forwardRef, MouseEvent, useCallback, @@ -11,16 +10,18 @@ import { import { remoteCliExecute } from "tome-kolmafia-lib"; import { RefreshContext } from "tome-kolmafia-react"; +import { toaster } from "./ui/toaster"; +import { Tooltip } from "./ui/tooltip"; + export interface AsyncLinkProps extends Omit { href?: string; command?: string; } -const AsyncLink: FC = forwardRef( +const AsyncLink = forwardRef( ({ href, command, onClick, children, ...props }, ref) => { const { triggerHardRefresh } = useContext(RefreshContext); const [isLoading, setIsLoading] = useState(false); - const toast = useToast(); const onClickWithCommand = useMemo( () => @@ -28,17 +29,15 @@ const AsyncLink: FC = forwardRef( ? async () => { const result = await remoteCliExecute(command); if (result === false) { - toast({ + toaster.error({ title: "Command failed.", description: `Failed to execute "${command}".`, - status: "error", duration: 5000, - isClosable: true, }); } } : onClick, - [command, onClick, toast], + [command, onClick], ); const handleClick = useCallback( @@ -65,12 +64,12 @@ const AsyncLink: FC = forwardRef( {...props} textDecoration="none !important" pointerEvents="none" - color="gray.500" + color="gray.solid" > {children} ) : command ? ( - + {link} ) : ( diff --git a/src/components/AutocompleteInput.tsx b/src/components/AutocompleteInput.tsx index 5f30d01e..e8b97c75 100644 --- a/src/components/AutocompleteInput.tsx +++ b/src/components/AutocompleteInput.tsx @@ -1,13 +1,4 @@ -import { - Input, - InputProps, - List, - ListItem, - Popover, - PopoverBody, - PopoverContent, - PopoverTrigger, -} from "@chakra-ui/react"; +import { Input, InputProps, List } from "@chakra-ui/react"; import { ChangeEvent, forwardRef, @@ -17,6 +8,13 @@ import { useState, } from "react"; +import { + PopoverBody, + PopoverContent, + PopoverRoot, + PopoverTrigger, +} from "./ui/popover"; + function mod(n: number, k: number): number { const naive = n % k; return naive < 0 ? naive + k : naive; @@ -106,14 +104,15 @@ const AutocompleteInput = forwardRef( ); return ( - = 3 && matchingValues.length > 0} + = 3 && matchingValues.length > 0} autoFocus={false} - placement="top" + positioning={{ placement: "top" }} > ( - + {matchingValues.map((name, index) => { const highlight = autoIndex !== null && mod(autoIndex, matchingValues.length) === index; return ( - ( borderRadius="3px" > {name} - + ); })} - + - + ); }, ); diff --git a/src/components/BrandHeading.tsx b/src/components/BrandHeading.tsx index d7a9087d..8526d307 100644 --- a/src/components/BrandHeading.tsx +++ b/src/components/BrandHeading.tsx @@ -2,16 +2,16 @@ import { Heading, Image } from "@chakra-ui/react"; import { FC } from "react"; const BrandHeading: FC = () => ( - + Y O diff --git a/src/components/ChatButton.tsx b/src/components/ChatButton.tsx index e97a5ea8..69505ffb 100644 --- a/src/components/ChatButton.tsx +++ b/src/components/ChatButton.tsx @@ -1,5 +1,5 @@ -import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons"; import { ButtonProps, IconButton } from "@chakra-ui/react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; import { FC } from "react"; export interface ChatButtonProps extends ButtonProps { @@ -8,14 +8,16 @@ export interface ChatButtonProps extends ButtonProps { const ChatButton: FC = ({ direction, ...props }) => ( : } + asChild aria-label="Refresh" - size="xs" - fontSize="20px" + size="2xs" + p={1} variant="outline" backgroundColor="white" {...props} - /> + > + {direction === "left" ? : } + ); export default ChatButton; diff --git a/src/components/Chevrons.tsx b/src/components/Chevrons.tsx index de6c9ce2..d783aa47 100644 --- a/src/components/Chevrons.tsx +++ b/src/components/Chevrons.tsx @@ -1,31 +1,42 @@ -import { ChevronRightIcon } from "@chakra-ui/icons"; -import { Text, TextProps } from "@chakra-ui/react"; -import { FC } from "react"; +import { Icon, Text, TextProps } from "@chakra-ui/react"; +import { ChevronRight } from "lucide-react"; +import { FC, forwardRef } from "react"; -interface ChevronProps extends TextProps { +export interface ChevronsProps extends TextProps { usesLeft: number; totalUses: number; } /** * Generate fading chevrons to describe # of a resource left out of total casts - * @returns Three objects colored by availability of the resource + * @returns Three objects colored by availability of the resource * @param usesLeft How many casts/uses you have left of the resource * @param totalUses Total number of uses the users has */ -const Chevrons: FC = ({ usesLeft, totalUses, ...props }) => { +const Chevrons: FC = forwardRef< + HTMLParagraphElement, + ChevronsProps +>(({ usesLeft, totalUses, ...props }, ref) => { return ( - + {new Array(totalUses).fill(null).map((_, index) => ( - + > + + ))} ); -}; +}); export default Chevrons; diff --git a/src/components/ChevronsListIcon.tsx b/src/components/ChevronsListIcon.tsx index bfb5d37e..c6f2762e 100644 --- a/src/components/ChevronsListIcon.tsx +++ b/src/components/ChevronsListIcon.tsx @@ -1,21 +1,22 @@ -import { IconProps, ListIcon } from "@chakra-ui/react"; +import { List } from "@chakra-ui/react"; import { FC } from "react"; -import Chevrons from "./Chevrons"; +import Chevrons, { ChevronsProps } from "./Chevrons"; -interface ChevronsListIconProps extends IconProps { +interface ChevronsListProps extends ChevronsProps { usesLeft: number; totalUses: number; } -const ChevronsListIcon: FC = (props) => ( - = (props) => ( + + > + + ); -export default ChevronsListIcon; +export default ChevronsList; diff --git a/src/components/ElementName.tsx b/src/components/ElementName.tsx index b31c159d..0fd62fb4 100644 --- a/src/components/ElementName.tsx +++ b/src/components/ElementName.tsx @@ -9,11 +9,11 @@ export interface ElementNameProps { const ElementName: FC = ({ element, children }) => { const elementColors: Record = { - cold: "blue.500", - hot: "red.500", - spooky: "gray.500", - stench: "green.500", - sleaze: "purple.500", + cold: "blue.solid", + hot: "red.solid", + spooky: "gray.solid", + stench: "green.solid", + sleaze: "purple.solid", }; return ( diff --git a/src/components/HeaderButton.tsx b/src/components/HeaderButton.tsx index 169795b9..54f28523 100644 --- a/src/components/HeaderButton.tsx +++ b/src/components/HeaderButton.tsx @@ -1,7 +1,7 @@ -import { Button, ButtonProps } from "@chakra-ui/react"; import { forwardRef } from "react"; import MainLink from "./MainLink"; +import { Button, ButtonProps } from "./ui/button"; interface HeaderButtonProps extends ButtonProps { href?: string; @@ -12,7 +12,7 @@ const HeaderButton = forwardRef( const button = (