Update Shadcn Sidebar (#73)

Migrated Sidebar to use Shadcn UI's
This commit is contained in:
Giancarlo Buomprisco
2024-10-25 09:43:34 +02:00
committed by GitHub
parent df944bb1e5
commit 14c2220904
48 changed files with 1863 additions and 543 deletions

View File

@@ -14,6 +14,7 @@
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-collapsible": "1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
@@ -42,14 +43,14 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@radix-ui/react-icons": "^1.3.0",
"@tanstack/react-query": "5.59.15",
"@tanstack/react-query": "5.59.16",
"@tanstack/react-table": "^8.20.5",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"class-variance-authority": "^0.7.0",
"date-fns": "^4.1.0",
"eslint": "^8.57.0",
"next": "15.0.0",
"next": "15.0.1",
"next-themes": "0.3.0",
"prettier": "^3.3.3",
"react-day-picker": "^8.10.1",
@@ -103,6 +104,8 @@
"./switch": "./src/shadcn/switch.tsx",
"./breadcrumb": "./src/shadcn/breadcrumb.tsx",
"./chart": "./src/shadcn/chart.tsx",
"./skeleton": "./src/shadcn/skeleton.tsx",
"./shadcn-sidebar": "./src/shadcn/sidebar.tsx",
"./utils": "./src/lib/utils/index.ts",
"./if": "./src/makerkit/if.tsx",
"./trans": "./src/makerkit/trans.tsx",

View File

@@ -0,0 +1,21 @@
import * as React from 'react';
const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined,
);
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
mql.addEventListener('change', onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener('change', onChange);
}, []);
return !!isMobile;
}

View File

@@ -5,6 +5,30 @@ const RouteMatchingEnd = z
.default(false)
.optional();
const Divider = z.object({
divider: z.literal(true),
});
const RouteChildren = z.array(
z.object({
label: z.string(),
path: z.string(),
Icon: z.custom<React.ReactNode>(),
end: RouteMatchingEnd,
children: z
.array(
z.object({
label: z.string(),
path: z.string(),
Icon: z.custom<React.ReactNode>(),
end: RouteMatchingEnd,
}),
)
.default([])
.optional(),
}),
);
export const NavigationConfigSchema = z.object({
style: z.enum(['custom', 'sidebar', 'header']).default('sidebar'),
sidebarCollapsed: z
@@ -14,28 +38,14 @@ export const NavigationConfigSchema = z.object({
.transform((value) => value === `true`),
routes: z.array(
z.union([
z.object({
label: z.string(),
path: z.string(),
Icon: z.custom<React.ReactNode>(),
end: RouteMatchingEnd,
}),
z.object({
label: z.string(),
collapsible: z.boolean().optional(),
collapsed: z.boolean().optional(),
children: z.array(
z.object({
label: z.string(),
path: z.string(),
Icon: z.custom<React.ReactNode>(),
end: RouteMatchingEnd,
}),
),
}),
z.object({
divider: z.literal(true),
children: RouteChildren,
renderAction: z.custom<React.ReactNode>().optional(),
}),
Divider,
]),
),
});

View File

@@ -29,12 +29,7 @@ function PageWithSidebar(props: PageProps) {
const { Navigation, Children, MobileNavigation } = getSlotsFromPage(props);
return (
<div
className={cn(
'flex bg-gray-50/95 dark:bg-background/85',
props.className,
)}
>
<div className={cn('flex', props.className)}>
{Navigation}
<div
@@ -47,7 +42,7 @@ function PageWithSidebar(props: PageProps) {
<div
className={
'flex flex-1 flex-col overflow-y-auto bg-background px-4 lg:m-1.5 lg:ml-0 lg:rounded-lg lg:border lg:px-0'
'flex flex-1 flex-col overflow-y-auto bg-background px-4 lg:px-0'
}
>
{Children}

View File

@@ -12,6 +12,7 @@ type TextProps = {
type ProfileAvatarProps = (SessionProps | TextProps) & {
className?: string;
fallbackClassName?: string;
};
export function ProfileAvatar(props: ProfileAvatarProps) {
@@ -23,8 +24,13 @@ export function ProfileAvatar(props: ProfileAvatarProps) {
if ('text' in props) {
return (
<Avatar className={avatarClassName}>
<AvatarFallback>
<span className={'uppercase'}>{props.text.slice(0, 1)}</span>
<AvatarFallback
className={cn(
props.fallbackClassName,
'uppercase animate-in fade-in',
)}
>
{props.text.slice(0, 1)}
</AvatarFallback>
</Avatar>
);
@@ -36,7 +42,9 @@ export function ProfileAvatar(props: ProfileAvatarProps) {
<Avatar className={avatarClassName}>
<AvatarImage src={props.pictureUrl ?? undefined} />
<AvatarFallback className={'animate-in fade-in'}>
<AvatarFallback
className={cn(props.fallbackClassName, 'animate-in fade-in')}
>
<span suppressHydrationWarning className={'uppercase'}>
{initials}
</span>

View File

@@ -26,6 +26,11 @@ export type SidebarConfig = z.infer<typeof NavigationConfigSchema>;
export { SidebarContext };
/**
* @deprecated
* This component is deprecated and will be removed in a future version.
* Please use the Shadcn Sidebar component instead.
*/
export function Sidebar(props: {
collapsed?: boolean;
expandOnHover?: boolean;
@@ -338,17 +343,6 @@ export function SidebarNavigation({
</SidebarGroup>
);
}
return (
<SidebarItem
key={item.path}
end={item.end}
path={item.path}
Icon={item.Icon}
>
<Trans i18nKey={item.label} defaults={item.label} />
</SidebarItem>
);
})}
</>
);

View File

@@ -0,0 +1,11 @@
"use client"
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
const Collapsible = CollapsiblePrimitive.Root
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
export { Collapsible, CollapsibleTrigger, CollapsibleContent }

View File

@@ -3,9 +3,8 @@
import * as React from 'react';
import * as SheetPrimitive from '@radix-ui/react-dialog';
import { cva } from 'class-variance-authority';
import type { VariantProps } from 'class-variance-authority';
import { X } from 'lucide-react';
import { Cross2Icon } from '@radix-ui/react-icons';
import { type VariantProps, cva } from 'class-variance-authority';
import { cn } from '../lib/utils';
@@ -33,7 +32,7 @@ const SheetOverlay = React.forwardRef<
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out',
{
variants: {
side: {
@@ -42,7 +41,7 @@ const sheetVariants = cva(
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
},
defaultVariants: {
@@ -66,11 +65,11 @@ const SheetContent = React.forwardRef<
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
{children}
</SheetPrimitive.Content>
</SheetPortal>
));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
import { cn } from '../lib/utils';
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('animate-pulse rounded-md bg-primary/10', className)}
{...props}
/>
);
}
export { Skeleton };