Workspace settings dialog improvements

This commit is contained in:
Hakan Shehu
2024-11-04 22:28:26 +01:00
parent aa8e831454
commit f2a23b39a9
3 changed files with 105 additions and 98 deletions

View File

@@ -1,57 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from "@/lib/utils"
import { cn } from '@/lib/utils';
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
},
},
defaultVariants: {
variant: "default",
size: "default",
variant: 'default',
size: 'default',
},
}
)
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
);
},
);
Button.displayName = 'Button';
export { Button, buttonVariants }
export { Button, buttonVariants };

View File

@@ -524,7 +524,7 @@ const SidebarMenuItem = React.forwardRef<
SidebarMenuItem.displayName = 'SidebarMenuItem';
const sidebarMenuButtonVariants = cva(
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-0 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
{
variants: {
variant: {
@@ -734,7 +734,7 @@ const SidebarMenuSubButton = React.forwardRef<
data-size={size}
data-active={isActive}
className={cn(
'flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',
'flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-0 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
size === 'sm' && 'text-xs',
size === 'md' && 'text-sm',

View File

@@ -5,17 +5,23 @@ import {
DialogTitle,
} from '@/renderer/components/ui/dialog';
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/renderer/components/ui/tabs';
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarHeader,
} from '@/renderer/components/ui/sidebar';
import { Avatar } from '@/renderer/components/avatars/avatar';
import { useWorkspace } from '@/renderer/contexts/workspace';
import { WorkspaceUpdate } from '@/renderer/components/workspaces/workspace-update';
import { WorkspaceUsers } from '@/renderer/components/workspaces/workspace-users';
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
import { Info, Trash2, Users } from 'lucide-react';
import { match } from 'ts-pattern';
interface WorkspaceSettingsDialogProps {
open: boolean;
@@ -27,81 +33,82 @@ export const WorkspaceSettingsDialog = ({
onOpenChange,
}: WorkspaceSettingsDialogProps) => {
const workspace = useWorkspace();
const [tab, setTab] = React.useState<'info' | 'users' | 'delete'>('info');
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
className="md:min-h-3/4 md:max-h-3/4 p-3 md:h-3/4 md:w-3/4 md:max-w-full"
className="md:min-h-3/4 md:max-h-3/4 overflow-hidden p-0 md:h-3/4 md:w-3/4 md:max-w-full"
aria-describedby={undefined}
>
<VisuallyHidden>
<DialogTitle>Workspace Settings</DialogTitle>
</VisuallyHidden>
<Tabs
defaultValue="info"
className="grid h-full max-h-full grid-cols-[240px_minmax(0,1fr)] overflow-hidden"
>
<TabsList className="flex h-full max-h-full flex-col items-start justify-start gap-1 rounded-none border-r border-r-gray-100 bg-white pr-3">
<div className="mb-1 flex h-10 w-full items-center justify-between bg-gray-50 p-1 text-foreground/80">
<div className="flex items-center gap-2">
<Avatar
id={workspace.id}
name={workspace.name}
avatar={workspace.avatar}
size="small"
/>
<span>{workspace.name}</span>
</div>
</div>
<TabsTrigger
key={`tab-trigger-info`}
className="w-full justify-start p-2 hover:bg-gray-50"
value="info"
>
<Info className="mr-2 size-4" />
Info
</TabsTrigger>
<TabsTrigger
key={`tab-trigger-collaborators`}
className="w-full justify-start p-2 hover:bg-gray-50"
value="users"
>
<Users className="mr-2 size-4" />
Users
</TabsTrigger>
<TabsTrigger
key={`tab-trigger-delete`}
className="w-full justify-start p-2 hover:bg-gray-50"
value="delete"
>
<Trash2 className="mr-2 size-4" />
Delete
</TabsTrigger>
</TabsList>
<div className="overflow-auto p-4">
<TabsContent
key="tab-content-info"
className="focus-visible:ring-0 focus-visible:ring-offset-0"
value="info"
>
<WorkspaceUpdate />
</TabsContent>
<TabsContent
key="tab-content-collaborators"
className="focus-visible:ring-0 focus-visible:ring-offset-0"
value="users"
>
<WorkspaceUsers />
</TabsContent>
<TabsContent
key="tab-content-delete"
className="focus-visible:ring-0 focus-visible:ring-offset-0"
value="delete"
>
<p>Coming soon.</p>
</TabsContent>
<SidebarProvider>
<Sidebar collapsible="none">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<div className="flex flex-row items-center gap-2">
<Avatar
id={workspace.id}
avatar={workspace.avatar}
name={workspace.name}
className="size-6"
/>
<span className="truncate font-semibold">
{workspace.name}
</span>
</div>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
isActive={tab === 'info'}
onClick={() => setTab('info')}
>
<Info className="mr-2 size-4" />
<span>Info</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton
isActive={tab === 'users'}
onClick={() => setTab('users')}
>
<Users className="mr-2 size-4" />
<span>Users</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton
isActive={tab === 'delete'}
onClick={() => setTab('delete')}
>
<Trash2 className="mr-2 size-4" />
<span>Delete</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<div className="flex-1 overflow-auto p-7">
{match(tab)
.with('info', () => <WorkspaceUpdate />)
.with('users', () => <WorkspaceUsers />)
.with('delete', () => <p>Coming soon.</p>)
.exhaustive()}
</div>
</Tabs>
</SidebarProvider>
</DialogContent>
</Dialog>
);