Files

82 lines
2.3 KiB
Vue
Raw Permalink Normal View History

2025-10-20 08:57:51 +02:00
<script setup lang="ts">
2026-02-17 10:35:03 +01:00
import type { HTMLAttributes, Ref } from "vue"
import { defaultDocument, useEventListener, useMediaQuery, useVModel } from "@vueuse/core"
import { TooltipProvider } from "reka-ui"
import { computed, ref } from "vue"
import { cn } from "@/lib/utils"
import { provideSidebarContext, SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON } from "./utils"
2025-10-20 08:57:51 +02:00
const props = withDefaults(defineProps<{
defaultOpen?: boolean
open?: boolean
2026-02-17 10:35:03 +01:00
class?: HTMLAttributes["class"]
2025-10-20 08:57:51 +02:00
}>(), {
2026-02-17 10:35:03 +01:00
defaultOpen: !defaultDocument?.cookie.includes(`${SIDEBAR_COOKIE_NAME}=false`),
2025-10-20 08:57:51 +02:00
open: undefined,
})
const emits = defineEmits<{
2026-02-17 10:35:03 +01:00
"update:open": [open: boolean]
2025-10-20 08:57:51 +02:00
}>()
2026-02-17 10:35:03 +01:00
const isMobile = useMediaQuery("(max-width: 768px)")
2025-10-20 08:57:51 +02:00
const openMobile = ref(false)
2026-02-17 10:35:03 +01:00
const open = useVModel(props, "open", emits, {
2025-10-20 08:57:51 +02:00
defaultValue: props.defaultOpen ?? false,
passive: (props.open === undefined) as false,
}) as Ref<boolean>
function setOpen(value: boolean) {
open.value = value // emits('update:open', value)
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
}
function setOpenMobile(value: boolean) {
openMobile.value = value
}
// Helper to toggle the sidebar.
function toggleSidebar() {
return isMobile.value ? setOpenMobile(!openMobile.value) : setOpen(!open.value)
}
2026-02-17 10:35:03 +01:00
useEventListener("keydown", (event: KeyboardEvent) => {
2025-10-20 08:57:51 +02:00
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
event.preventDefault()
toggleSidebar()
}
})
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
2026-02-17 10:35:03 +01:00
const state = computed(() => open.value ? "expanded" : "collapsed")
2025-10-20 08:57:51 +02:00
provideSidebarContext({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
})
</script>
<template>
<TooltipProvider :delay-duration="0">
<div
:style="{
'--sidebar-width': SIDEBAR_WIDTH,
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
}"
2026-02-17 10:35:03 +01:00
:class="cn('group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar', props.class)"
2025-10-20 08:57:51 +02:00
v-bind="$attrs"
2026-02-17 10:35:03 +01:00
>
2025-10-20 08:57:51 +02:00
<slot />
</div>
</TooltipProvider>
</template>