added sliding sidebar on smaller viewports

pull/3/head
TZGyn 2 years ago
parent ec37dd3eef
commit 015646841f
Signed by: TZGyn
GPG Key ID: 122EAF77AE81FD4A

Binary file not shown.

@ -0,0 +1,70 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button'
import * as Avatar from '$lib/components/ui/avatar'
import * as DropdownMenu from '$lib/components/ui/dropdown-menu'
import * as AlertDialog from '$lib/components/ui/alert-dialog'
import { Loader2, User } from 'lucide-svelte'
import { goto } from '$app/navigation'
export let email: string = ''
let dialogOpen = false
let isLoading = false
const logout = async () => {
isLoading = true
await fetch('/api/logout', { method: 'post' })
isLoading = false
dialogOpen = false
goto('/login')
}
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Avatar.Root>
<Avatar.Image src="" alt="@shadcn" />
<Avatar.Fallback><User /></Avatar.Fallback>
</Avatar.Root>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.Label>{email}</DropdownMenu.Label>
<DropdownMenu.Separator />
<DropdownMenu.Item on:click={() => goto('/profile')}>
Profile
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item
on:click={() => (dialogOpen = true)}
class="text-destructive data-[highlighted]:bg-destructive">
Log Out
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
<AlertDialog.Root bind:open={dialogOpen}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Are you absolutely sure?</AlertDialog.Title>
<AlertDialog.Description>
You are about to log out of this account.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel disabled={isLoading}
>Cancel</AlertDialog.Cancel>
<Button
on:click={logout}
class="flex gap-2"
disabled={isLoading}>
{#if isLoading}
<Loader2 class="animate-spin" />
{/if}
Log Out
</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>

@ -1,31 +1,21 @@
<script lang="ts">
import UserIcon from './UserIcon.svelte'
import { Separator } from '$lib/components/ui/separator'
import ThemeToggle from './theme-toggle.svelte'
import { Button } from '$lib/components/ui/button'
import * as Avatar from '$lib/components/ui/avatar'
import * as DropdownMenu from '$lib/components/ui/dropdown-menu'
import * as AlertDialog from '$lib/components/ui/alert-dialog'
import { Loader2, User } from 'lucide-svelte'
import { goto } from '$app/navigation'
import { cn } from '$lib/utils'
export let email: string = ''
let dialogOpen = false
let isLoading = false
const logout = async () => {
isLoading = true
await fetch('/api/logout', { method: 'post' })
isLoading = false
dialogOpen = false
goto('/login')
}
let className: string | undefined = undefined
export { className as class }
</script>
<div
class="flex h-full min-w-[350px] flex-col justify-between border-r p-4">
class={cn(
'flex h-full min-w-[350px] flex-col justify-between border-r p-4',
className,
)}>
<div>
<div class="flex items-center justify-between pb-16">
<div class="text-xl font-bold">
@ -49,55 +39,7 @@
<div class="flex flex-col gap-4">
<Separator />
<div class="flex items-center justify-between">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Avatar.Root>
<Avatar.Image src="" alt="@shadcn" />
<Avatar.Fallback><User /></Avatar.Fallback>
</Avatar.Root>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.Label>{email}</DropdownMenu.Label>
<DropdownMenu.Separator />
<DropdownMenu.Item on:click={() => goto('/profile')}>
Profile
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item
on:click={() => (dialogOpen = true)}
class="text-destructive data-[highlighted]:bg-destructive">
Log Out
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
<div>
<AlertDialog.Root bind:open={dialogOpen}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title
>Are you absolutely sure?</AlertDialog.Title>
<AlertDialog.Description>
You are about to log out of this account.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel disabled={isLoading}
>Cancel</AlertDialog.Cancel>
<Button
on:click={logout}
class="flex gap-2"
disabled={isLoading}>
{#if isLoading}
<Loader2 class="animate-spin" />
{/if}
Log Out
</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
</div>
<UserIcon {email} />
</div>
</div>
</div>

@ -0,0 +1,106 @@
import { Dialog as SheetPrimitive } from "bits-ui";
import { tv, type VariantProps } from "tailwind-variants";
import Portal from "./sheet-portal.svelte";
import Overlay from "./sheet-overlay.svelte";
import Content from "./sheet-content.svelte";
import Header from "./sheet-header.svelte";
import Footer from "./sheet-footer.svelte";
import Title from "./sheet-title.svelte";
import Description from "./sheet-description.svelte";
const Root = SheetPrimitive.Root;
const Close = SheetPrimitive.Close;
const Trigger = SheetPrimitive.Trigger;
export {
Root,
Close,
Trigger,
Portal,
Overlay,
Content,
Header,
Footer,
Title,
Description,
//
Root as Sheet,
Close as SheetClose,
Trigger as SheetTrigger,
Portal as SheetPortal,
Overlay as SheetOverlay,
Content as SheetContent,
Header as SheetHeader,
Footer as SheetFooter,
Title as SheetTitle,
Description as SheetDescription
};
export const sheetVariants = tv({
base: "fixed z-50 gap-4 bg-background p-6 shadow-lg",
variants: {
side: {
top: "inset-x-0 top-0 border-b",
bottom: "inset-x-0 bottom-0 border-t",
left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"
}
},
defaultVariants: {
side: "right"
}
});
export const sheetTransitions = {
top: {
in: {
y: "-100%",
duration: 500,
opacity: 1
},
out: {
y: "-100%",
duration: 300,
opacity: 1
}
},
bottom: {
in: {
y: "100%",
duration: 500,
opacity: 1
},
out: {
y: "100%",
duration: 300,
opacity: 1
}
},
left: {
in: {
x: "-100%",
duration: 500,
opacity: 1
},
out: {
x: "-100%",
duration: 300,
opacity: 1
}
},
right: {
in: {
x: "100%",
duration: 500,
opacity: 1
},
out: {
x: "100%",
duration: 300,
opacity: 1
}
}
};
export type Side = VariantProps<typeof sheetVariants>["side"];

@ -0,0 +1,47 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import {
SheetOverlay,
SheetPortal,
sheetTransitions,
sheetVariants,
type Side
} from ".";
import { X } from "lucide-svelte";
import { cn } from "$lib/utils";
import { fly } from "svelte/transition";
type $$Props = SheetPrimitive.ContentProps & {
side?: Side;
};
let className: $$Props["class"] = undefined;
export let side: $$Props["side"] = "right";
export { className as class };
export let inTransition: $$Props["inTransition"] = fly;
export let inTransitionConfig: $$Props["inTransitionConfig"] =
sheetTransitions[side ? side : "right"]["in"];
export let outTransition: $$Props["outTransition"] = fly;
export let outTransitionConfig: $$Props["outTransitionConfig"] =
sheetTransitions[side ? side : "right"]["out"];
</script>
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(sheetVariants({ side }), className)}
{...$$restProps}
>
<slot />
<SheetPrimitive.Close
class="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 class="h-4 w-4" />
<span class="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>

@ -0,0 +1,16 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.DescriptionProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Description
class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
>
<slot />
</SheetPrimitive.Description>

@ -0,0 +1,19 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div
class={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...$$restProps}
>
<slot />
</div>

@ -0,0 +1,16 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
{...$$restProps}
>
<slot />
</div>

@ -0,0 +1,24 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { fade } from "svelte/transition";
type $$Props = SheetPrimitive.OverlayProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = fade;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150
};
export { className as class };
</script>
<SheetPrimitive.Overlay
{transition}
{transitionConfig}
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ",
className
)}
{...$$restProps}
/>

@ -0,0 +1,13 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.PortalProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<slot />
</SheetPrimitive.Portal>

@ -0,0 +1,16 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.TitleProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Title
class={cn("text-lg font-semibold text-foreground", className)}
{...$$restProps}
>
<slot />
</SheetPrimitive.Title>

@ -1,13 +1,61 @@
<script lang="ts">
import Sidebar from '$lib/components/sidebar.svelte'
import type { PageData } from './$types'
import * as Sheet from '$lib/components/ui/sheet'
import { Button } from '$lib/components/ui/button'
import { Menu } from 'lucide-svelte'
import UserIcon from '$lib/components/UserIcon.svelte'
import { Separator } from '$lib/components/ui/separator'
export let data: PageData
let sheetOpen = false
const closeSheet = () => {
sheetOpen = false
}
</script>
<div class="flex h-screen w-full">
<Sidebar email={data.user.email} />
<div class="w-full p-4">
<Sidebar email={data.user.email} class="hidden lg:flex" />
<div class="h-full w-full">
<div class="block w-full border-b px-4 py-2 lg:hidden">
<Sheet.Root bind:open={sheetOpen}>
<Sheet.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" class="p-2">
<Menu />
</Button>
</Sheet.Trigger>
<Sheet.Content side="left" class="flex flex-col">
<Sheet.Header class="pb-16">
<Sheet.Title>Shortener</Sheet.Title>
</Sheet.Header>
<div class="flex grow flex-col gap-4">
<Button
on:click={closeSheet}
variant="ghost"
href="/links"
class="justify-start text-base">Links</Button>
<Button
on:click={closeSheet}
variant="ghost"
href="/projects"
class="justify-start text-base ">Projects</Button>
</div>
<div class="flex flex-col justify-between">
<div></div>
<div class="flex flex-col gap-4">
<Separator />
<div class="flex items-center justify-between">
<UserIcon email={data.user.email} />
</div>
</div>
</div>
</Sheet.Content>
</Sheet.Root>
</div>
<div class="h-full w-full p-4">
<slot />
</div>
</div>
</div>

Loading…
Cancel
Save