Compare commits

..

1 Commits

Author SHA1 Message Date
Kyle Corbitt
9051d80775 Sidebar styling
Unify the menu styles between the UserMenu and ProjectMenu
2023-08-09 16:47:09 -07:00
5 changed files with 90 additions and 123 deletions

View File

@@ -1,11 +1,10 @@
import { Box, type BoxProps } from "@chakra-ui/react";
import { Box, type BoxProps, forwardRef } from "@chakra-ui/react";
import { useRouter } from "next/router";
const NavSidebarOption = ({
activeHrefPattern,
disableHoverEffect,
...props
}: { activeHrefPattern?: string; disableHoverEffect?: boolean } & BoxProps) => {
const NavSidebarOption = forwardRef<
{ activeHrefPattern?: string; disableHoverEffect?: boolean } & BoxProps,
"div"
>(({ activeHrefPattern, disableHoverEffect, ...props }, ref) => {
const router = useRouter();
const isActive = activeHrefPattern && router.pathname.startsWith(activeHrefPattern);
return (
@@ -18,10 +17,13 @@ const NavSidebarOption = ({
cursor="pointer"
borderRadius={4}
{...props}
ref={ref}
>
{props.children}
</Box>
);
};
});
NavSidebarOption.displayName = "NavSidebarOption";
export default NavSidebarOption;

View File

@@ -15,8 +15,7 @@ import {
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { AiFillCaretDown } from "react-icons/ai";
import { BsGear, BsPlus } from "react-icons/bs";
import { BsChevronRight, BsGear, BsPlus } from "react-icons/bs";
import { type Project } from "@prisma/client";
import { useAppStore } from "~/state/store";
@@ -68,14 +67,9 @@ export default function ProjectMenu() {
>
PROJECT
</Text>
<NavSidebarOption>
<Popover
placement="bottom-start"
isOpen={popover.isOpen}
onClose={popover.onClose}
closeOnBlur
>
<PopoverTrigger>
<Popover placement="right" isOpen={popover.isOpen} onClose={popover.onClose} closeOnBlur>
<PopoverTrigger>
<NavSidebarOption>
<HStack w="full" onClick={popover.onToggle}>
<Flex
p={1}
@@ -92,46 +86,41 @@ export default function ProjectMenu() {
<Text fontSize="sm" display={{ base: "none", md: "block" }} py={1} flex={1}>
{selectedProject?.name}
</Text>
<Icon as={AiFillCaretDown} boxSize={3} size="xs" color="gray.500" mr={2} />
<Icon as={BsChevronRight} boxSize={4} color="gray.500" />
</HStack>
</PopoverTrigger>
<PopoverContent
_focusVisible={{ boxShadow: "unset" }}
minW={0}
borderColor="blue.400"
w="full"
>
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
PROJECTS
</Text>
<Divider />
<VStack spacing={0} w="full">
{projects?.map((proj) => (
<ProjectOption
key={proj.id}
proj={proj}
isActive={proj.id === selectedProjectId}
onClose={popover.onClose}
/>
))}
</VStack>
<HStack
as={Button}
variant="ghost"
colorScheme="blue"
color="blue.400"
pr={8}
w="full"
onClick={createProject}
>
<Icon as={isLoading ? Spinner : BsPlus} boxSize={6} />
<Text>New project</Text>
</HStack>
</NavSidebarOption>
</PopoverTrigger>
<PopoverContent _focusVisible={{ outline: "unset" }} ml={-1}>
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
PROJECTS
</Text>
<Divider />
<VStack spacing={0} w="full">
{projects?.map((proj) => (
<ProjectOption
key={proj.id}
proj={proj}
isActive={proj.id === selectedProjectId}
onClose={popover.onClose}
/>
))}
</VStack>
</PopoverContent>
</Popover>
</NavSidebarOption>
<HStack
as={Button}
variant="ghost"
colorScheme="blue"
color="blue.400"
pr={8}
w="full"
onClick={createProject}
>
<Icon as={isLoading ? Spinner : BsPlus} boxSize={6} />
<Text>New project</Text>
</HStack>
</VStack>
</PopoverContent>
</Popover>
</VStack>
);
}

View File

@@ -9,7 +9,6 @@ import {
PopoverContent,
Link,
type StackProps,
Box,
} from "@chakra-ui/react";
import { type Session } from "next-auth";
import { signOut } from "next-auth/react";
@@ -27,30 +26,28 @@ export default function UserMenu({ user, ...rest }: { user: Session } & StackPro
<>
<Popover placement="right">
<PopoverTrigger>
<Box>
<NavSidebarOption>
<HStack
// Weird values to make mobile look right; can clean up when we make the sidebar disappear on mobile
py={2}
px={1}
spacing={3}
{...rest}
>
{profileImage}
<VStack spacing={0} align="start" flex={1} flexShrink={1}>
<Text fontWeight="bold" fontSize="sm">
{user.user.name}
</Text>
<Text color="gray.500" fontSize="xs">
{/* {user.user.email} */}
</Text>
</VStack>
<Icon as={BsChevronRight} boxSize={4} color="gray.500" />
</HStack>
</NavSidebarOption>
</Box>
<NavSidebarOption>
<HStack
// Weird values to make mobile look right; can clean up when we make the sidebar disappear on mobile
py={2}
px={1}
spacing={3}
{...rest}
>
{profileImage}
<VStack spacing={0} align="start" flex={1} flexShrink={1}>
<Text fontWeight="bold" fontSize="sm">
{user.user.name}
</Text>
<Text color="gray.500" fontSize="xs">
{/* {user.user.email} */}
</Text>
</VStack>
<Icon as={BsChevronRight} boxSize={4} color="gray.500" />
</HStack>
</NavSidebarOption>
</PopoverTrigger>
<PopoverContent _focusVisible={{ boxShadow: "unset", outline: "unset" }} maxW="200px">
<PopoverContent _focusVisible={{ outline: "unset" }} ml={-1}>
<VStack align="stretch" spacing={0}>
{/* sign out */}
<HStack

View File

@@ -1,13 +0,0 @@
import { PersistOptions } from "zustand/middleware/persist";
import { State } from "./store";
export const stateToPersist = {
selectedProjectId: null as string | null,
};
export const persistOptions: PersistOptions<State, typeof stateToPersist> = {
name: "persisted-app-store",
partialize: (state) => ({
selectedProjectId: state.selectedProjectId,
}),
};

View File

@@ -1,13 +1,11 @@
import { type StateCreator, create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { persist } from "zustand/middleware";
import { createSelectors } from "./createSelectors";
import {
type SharedVariantEditorSlice,
createVariantEditorSlice,
} from "./sharedVariantEditor.slice";
import { type APIClient } from "~/utils/api";
import { persistOptions, stateToPersist } from "./persist";
export type State = {
drawerOpen: boolean;
@@ -25,36 +23,30 @@ export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], []
export type SetFn = Parameters<SliceCreator<unknown>>[0];
export type GetFn = Parameters<SliceCreator<unknown>>[1];
const useBaseStore = create<
State,
[["zustand/persist", typeof stateToPersist], ["zustand/immer", never]]
>(
persist(
immer((set, get, ...rest) => ({
api: null,
setApi: (api) =>
set((state) => {
state.api = api;
}),
const useBaseStore = create<State, [["zustand/immer", never]]>(
immer((set, get, ...rest) => ({
api: null,
setApi: (api) =>
set((state) => {
state.api = api;
}),
drawerOpen: false,
openDrawer: () =>
set((state) => {
state.drawerOpen = true;
}),
closeDrawer: () =>
set((state) => {
state.drawerOpen = false;
}),
sharedVariantEditor: createVariantEditorSlice(set, get, ...rest),
selectedProjectId: null,
setselectedProjectId: (id: string) =>
set((state) => {
state.selectedProjectId = id;
}),
})),
persistOptions,
),
drawerOpen: false,
openDrawer: () =>
set((state) => {
state.drawerOpen = true;
}),
closeDrawer: () =>
set((state) => {
state.drawerOpen = false;
}),
sharedVariantEditor: createVariantEditorSlice(set, get, ...rest),
selectedProjectId: null,
setselectedProjectId: (id: string) =>
set((state) => {
state.selectedProjectId = id;
}),
})),
);
export const useAppStore = createSelectors(useBaseStore);