@edgedev/create-edge-app 1.0.48 → 1.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.nvmrc +1 -0
  2. package/components/ui/sidebar/Sidebar.vue +85 -0
  3. package/components/ui/sidebar/SidebarContent.vue +17 -0
  4. package/components/ui/sidebar/SidebarFooter.vue +17 -0
  5. package/components/ui/sidebar/SidebarGroup.vue +17 -0
  6. package/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  7. package/components/ui/sidebar/SidebarGroupContent.vue +17 -0
  8. package/components/ui/sidebar/SidebarGroupLabel.vue +24 -0
  9. package/components/ui/sidebar/SidebarHeader.vue +17 -0
  10. package/components/ui/sidebar/SidebarInput.vue +21 -0
  11. package/components/ui/sidebar/SidebarInset.vue +20 -0
  12. package/components/ui/sidebar/SidebarMenu.vue +17 -0
  13. package/components/ui/sidebar/SidebarMenuAction.vue +34 -0
  14. package/components/ui/sidebar/SidebarMenuBadge.vue +25 -0
  15. package/components/ui/sidebar/SidebarMenuButton.vue +49 -0
  16. package/components/ui/sidebar/SidebarMenuButtonChild.vue +33 -0
  17. package/components/ui/sidebar/SidebarMenuItem.vue +17 -0
  18. package/components/ui/sidebar/SidebarMenuSkeleton.vue +33 -0
  19. package/components/ui/sidebar/SidebarMenuSub.vue +21 -0
  20. package/components/ui/sidebar/SidebarMenuSubButton.vue +35 -0
  21. package/components/ui/sidebar/SidebarMenuSubItem.vue +9 -0
  22. package/components/ui/sidebar/SidebarProvider.vue +80 -0
  23. package/components/ui/sidebar/SidebarRail.vue +32 -0
  24. package/components/ui/sidebar/SidebarSeparator.vue +18 -0
  25. package/components/ui/sidebar/SidebarTrigger.vue +26 -0
  26. package/components/ui/sidebar/index.ts +59 -0
  27. package/components/ui/sidebar/utils.ts +19 -0
  28. package/firebase.json +1 -0
  29. package/package.json +1 -1
  30. package/firestore.rules +0 -342
  31. package/firestore.rules.backup +0 -323
  32. package/functions/.env.dev +0 -7
  33. package/functions/.env.prod +0 -7
  34. package/functions/index.js +0 -7
  35. package/functions/package.json +0 -37
  36. package/functions/stripe.js +0 -103
  37. package/storage.rules +0 -61
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 22.13.1
@@ -0,0 +1,85 @@
1
+ <script setup lang="ts">
2
+ import type { SidebarProps } from '.'
3
+ import { Sheet, SheetContent } from '@/components/ui/sheet'
4
+ import { cn } from '@/lib/utils'
5
+ import { SIDEBAR_WIDTH_MOBILE, useSidebar } from './utils'
6
+
7
+ defineOptions({
8
+ inheritAttrs: false,
9
+ })
10
+
11
+ const props = withDefaults(defineProps<SidebarProps>(), {
12
+ side: 'left',
13
+ variant: 'sidebar',
14
+ collapsible: 'offcanvas',
15
+ })
16
+
17
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
18
+ </script>
19
+
20
+ <template>
21
+ <div
22
+ v-if="collapsible === 'none'"
23
+ :class="cn('flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground', props.class)"
24
+ v-bind="$attrs"
25
+ >
26
+ <slot />
27
+ </div>
28
+
29
+ <Sheet v-else-if="isMobile" :open="openMobile" v-bind="$attrs" @update:open="setOpenMobile">
30
+ <SheetContent
31
+ data-sidebar="sidebar"
32
+ data-mobile="true"
33
+ :side="side"
34
+ class="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
35
+ :style="{
36
+ '--sidebar-width': SIDEBAR_WIDTH_MOBILE,
37
+ }"
38
+ >
39
+ <div class="flex h-full w-full flex-col">
40
+ <slot />
41
+ </div>
42
+ </SheetContent>
43
+ </Sheet>
44
+
45
+ <div
46
+ v-else class="group peer hidden md:block"
47
+ :data-state="state"
48
+ :data-collapsible="state === 'collapsed' ? collapsible : ''"
49
+ :data-variant="variant"
50
+ :data-side="side"
51
+ >
52
+ <!-- This is what handles the sidebar gap on desktop -->
53
+ <div
54
+ :class="cn(
55
+ 'duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear',
56
+ 'group-data-[collapsible=offcanvas]:w-0',
57
+ 'group-data-[side=right]:rotate-180',
58
+ variant === 'floating' || variant === 'inset'
59
+ ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
60
+ : 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]',
61
+ )"
62
+ />
63
+ <div
64
+ :class="cn(
65
+ 'duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex',
66
+ side === 'left'
67
+ ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
68
+ : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
69
+ // Adjust the padding for floating and inset variants.
70
+ variant === 'floating' || variant === 'inset'
71
+ ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
72
+ : 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
73
+ props.class,
74
+ )"
75
+ v-bind="$attrs"
76
+ >
77
+ <div
78
+ data-sidebar="sidebar"
79
+ class="flex h-full w-full flex-col text-sidebar-foreground bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
80
+ >
81
+ <slot />
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="content"
13
+ :class="cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="footer"
13
+ :class="cn('flex flex-col gap-2 p-2', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="group"
13
+ :class="cn('relative flex w-full min-w-0 flex-col p-2', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from 'radix-vue'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { cn } from '@/lib/utils'
5
+ import { Primitive } from 'radix-vue'
6
+
7
+ const props = defineProps<PrimitiveProps & {
8
+ class?: HTMLAttributes['class']
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <Primitive
14
+ data-sidebar="group-action"
15
+ :as="as"
16
+ :as-child="asChild"
17
+ :class="cn(
18
+ 'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
19
+ // Increases the hit area of the button on mobile.
20
+ 'after:absolute after:-inset-2 after:md:hidden',
21
+ 'group-data-[collapsible=icon]:hidden',
22
+ props.class,
23
+ )"
24
+ >
25
+ <slot />
26
+ </Primitive>
27
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="group-content"
13
+ :class="cn('w-full text-sm', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from 'radix-vue'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { cn } from '@/lib/utils'
5
+ import { Primitive } from 'radix-vue'
6
+
7
+ const props = defineProps<PrimitiveProps & {
8
+ class?: HTMLAttributes['class']
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <Primitive
14
+ data-sidebar="group-label"
15
+ :as="as"
16
+ :as-child="asChild"
17
+ :class="cn(
18
+ 'duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
19
+ 'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
20
+ props.class)"
21
+ >
22
+ <slot />
23
+ </Primitive>
24
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="header"
13
+ :class="cn('flex flex-col gap-2 p-2', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { Input } from '@/components/ui/input'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ const props = defineProps<{
7
+ class?: HTMLAttributes['class']
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <Input
13
+ data-sidebar="input"
14
+ :class="cn(
15
+ 'h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring',
16
+ props.class,
17
+ )"
18
+ >
19
+ <slot />
20
+ </Input>
21
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <main
12
+ :class="cn(
13
+ 'relative flex min-h-svh flex-1 flex-col bg-background',
14
+ 'peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow',
15
+ props.class,
16
+ )"
17
+ >
18
+ <slot />
19
+ </main>
20
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <ul
12
+ data-sidebar="menu"
13
+ :class="cn('flex w-full min-w-0 flex-col gap-1', props.class)"
14
+ >
15
+ <slot />
16
+ </ul>
17
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+ import { Primitive, type PrimitiveProps } from 'radix-vue'
5
+
6
+ const props = withDefaults(defineProps<PrimitiveProps & {
7
+ showOnHover?: boolean
8
+ class?: HTMLAttributes['class']
9
+ }>(), {
10
+ as: 'button',
11
+ })
12
+ </script>
13
+
14
+ <template>
15
+ <Primitive
16
+ data-sidebar="menu-action"
17
+ :class="cn(
18
+ 'absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0',
19
+ // Increases the hit area of the button on mobile.
20
+ 'after:absolute after:-inset-2 after:md:hidden',
21
+ 'peer-data-[size=sm]/menu-button:top-1',
22
+ 'peer-data-[size=default]/menu-button:top-1.5',
23
+ 'peer-data-[size=lg]/menu-button:top-2.5',
24
+ 'group-data-[collapsible=icon]:hidden',
25
+ showOnHover
26
+ && 'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
27
+ props.class,
28
+ )"
29
+ :as="as"
30
+ :as-child="asChild"
31
+ >
32
+ <slot />
33
+ </Primitive>
34
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-sidebar="menu-badge"
13
+ :class="cn(
14
+ 'absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none',
15
+ 'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
16
+ 'peer-data-[size=sm]/menu-button:top-1',
17
+ 'peer-data-[size=default]/menu-button:top-1.5',
18
+ 'peer-data-[size=lg]/menu-button:top-2.5',
19
+ 'group-data-[collapsible=icon]:hidden',
20
+ props.class,
21
+ )"
22
+ >
23
+ <slot />
24
+ </div>
25
+ </template>
@@ -0,0 +1,49 @@
1
+ <script setup lang="ts">
2
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
3
+ import { type Component, computed } from 'vue'
4
+ import SidebarMenuButtonChild, { type SidebarMenuButtonProps } from './SidebarMenuButtonChild.vue'
5
+ import { useSidebar } from './utils'
6
+
7
+ defineOptions({
8
+ inheritAttrs: false,
9
+ })
10
+
11
+ const props = withDefaults(defineProps<SidebarMenuButtonProps & {
12
+ tooltip?: string | Component
13
+ }>(), {
14
+ as: 'button',
15
+ variant: 'default',
16
+ size: 'default',
17
+ })
18
+
19
+ const { isMobile, state } = useSidebar()
20
+
21
+ const delegatedProps = computed(() => {
22
+ const { tooltip, ...delegated } = props
23
+ return delegated
24
+ })
25
+ </script>
26
+
27
+ <template>
28
+ <SidebarMenuButtonChild v-if="!tooltip" v-bind="{ ...delegatedProps, ...$attrs }">
29
+ <slot />
30
+ </SidebarMenuButtonChild>
31
+
32
+ <Tooltip v-else>
33
+ <TooltipTrigger as-child>
34
+ <SidebarMenuButtonChild v-bind="{ ...delegatedProps, ...$attrs }">
35
+ <slot />
36
+ </SidebarMenuButtonChild>
37
+ </TooltipTrigger>
38
+ <TooltipContent
39
+ side="right"
40
+ align="center"
41
+ :hidden="state !== 'collapsed' || isMobile"
42
+ >
43
+ <template v-if="typeof tooltip === 'string'">
44
+ {{ tooltip }}
45
+ </template>
46
+ <component :is="tooltip" v-else />
47
+ </TooltipContent>
48
+ </Tooltip>
49
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+ import { Primitive, type PrimitiveProps } from 'radix-vue'
5
+ import { type SidebarMenuButtonVariants, sidebarMenuButtonVariants } from '.'
6
+
7
+ export interface SidebarMenuButtonProps extends PrimitiveProps {
8
+ variant?: SidebarMenuButtonVariants['variant']
9
+ size?: SidebarMenuButtonVariants['size']
10
+ isActive?: boolean
11
+ class?: HTMLAttributes['class']
12
+ }
13
+
14
+ const props = withDefaults(defineProps<SidebarMenuButtonProps>(), {
15
+ as: 'button',
16
+ variant: 'default',
17
+ size: 'default',
18
+ })
19
+ </script>
20
+
21
+ <template>
22
+ <Primitive
23
+ data-sidebar="menu-button"
24
+ :data-size="size"
25
+ :data-active="isActive"
26
+ :class="cn(sidebarMenuButtonVariants({ variant, size }), props.class)"
27
+ :as="as"
28
+ :as-child="asChild"
29
+ v-bind="$attrs"
30
+ >
31
+ <slot />
32
+ </Primitive>
33
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <li
12
+ data-sidebar="menu-item"
13
+ :class="cn('group/menu-item relative', props.class)"
14
+ >
15
+ <slot />
16
+ </li>
17
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import { Skeleton } from '@/components/ui/skeleton'
3
+ import { cn } from '@/lib/utils'
4
+ import { computed, type HTMLAttributes } from 'vue'
5
+
6
+ const props = defineProps<{
7
+ showIcon?: boolean
8
+ class?: HTMLAttributes['class']
9
+ }>()
10
+
11
+ const width = computed(() => {
12
+ return `${Math.floor(Math.random() * 40) + 50}%`;
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div
18
+ data-sidebar="menu-skeleton"
19
+ :class="cn('rounded-md h-8 flex gap-2 px-2 items-center', props.class)"
20
+ >
21
+ <Skeleton
22
+ v-if="showIcon"
23
+ class="size-4 rounded-md"
24
+ data-sidebar="menu-skeleton-icon"
25
+ />
26
+
27
+ <Skeleton
28
+ class="h-4 flex-1 max-w-[--skeleton-width]"
29
+ data-sidebar="menu-skeleton-text"
30
+ :style="{ '--skeleton-width': width }"
31
+ />
32
+ </div>
33
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <ul
12
+ data-sidebar="menu-badge"
13
+ :class="cn(
14
+ 'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5',
15
+ 'group-data-[collapsible=icon]:hidden',
16
+ props.class,
17
+ )"
18
+ >
19
+ <slot />
20
+ </ul>
21
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from 'radix-vue'
3
+ import type { HTMLAttributes } from 'vue'
4
+ import { cn } from '@/lib/utils'
5
+ import { Primitive } from 'radix-vue'
6
+
7
+ const props = withDefaults(defineProps<PrimitiveProps & {
8
+ size?: 'sm' | 'md'
9
+ isActive?: boolean
10
+ class?: HTMLAttributes['class']
11
+ }>(), {
12
+ as: 'a',
13
+ size: 'md',
14
+ })
15
+ </script>
16
+
17
+ <template>
18
+ <Primitive
19
+ data-sidebar="menu-sub-button"
20
+ :as="as"
21
+ :as-child="asChild"
22
+ :data-size="size"
23
+ :data-active="isActive"
24
+ :class="cn(
25
+ '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',
26
+ 'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
27
+ size === 'sm' && 'text-xs',
28
+ size === 'md' && 'text-sm',
29
+ 'group-data-[collapsible=icon]:hidden',
30
+ props.class,
31
+ )"
32
+ >
33
+ <slot />
34
+ </Primitive>
35
+ </template>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <li>
7
+ <slot />
8
+ </li>
9
+ </template>
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ import { cn } from '@/lib/utils'
3
+ import { useEventListener, useMediaQuery, useVModel } from '@vueuse/core'
4
+ import { TooltipProvider } from 'radix-vue'
5
+ import { computed, type HTMLAttributes, type Ref, ref } from 'vue'
6
+ import { provideSidebarContext, SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON } from './utils'
7
+
8
+ const props = withDefaults(defineProps<{
9
+ defaultOpen?: boolean
10
+ open?: boolean
11
+ class?: HTMLAttributes['class']
12
+ }>(), {
13
+ defaultOpen: true,
14
+ open: undefined,
15
+ })
16
+
17
+ const emits = defineEmits<{
18
+ 'update:open': [open: boolean]
19
+ }>()
20
+
21
+ const isMobile = useMediaQuery('(max-width: 768px)')
22
+ const openMobile = ref(false)
23
+
24
+ const open = useVModel(props, 'open', emits, {
25
+ defaultValue: props.defaultOpen ?? false,
26
+ passive: (props.open === undefined) as false,
27
+ }) as Ref<boolean>
28
+
29
+ function setOpen(value: boolean) {
30
+ open.value = value // emits('update:open', value)
31
+
32
+ // This sets the cookie to keep the sidebar state.
33
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
34
+ }
35
+
36
+ function setOpenMobile(value: boolean) {
37
+ openMobile.value = value
38
+ }
39
+
40
+ // Helper to toggle the sidebar.
41
+ function toggleSidebar() {
42
+ return isMobile.value ? setOpenMobile(!openMobile.value) : setOpen(!open.value)
43
+ }
44
+
45
+ useEventListener('keydown', (event: KeyboardEvent) => {
46
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
47
+ event.preventDefault()
48
+ toggleSidebar()
49
+ }
50
+ })
51
+
52
+ // We add a state so that we can do data-state="expanded" or "collapsed".
53
+ // This makes it easier to style the sidebar with Tailwind classes.
54
+ const state = computed(() => open.value ? 'expanded' : 'collapsed')
55
+
56
+ provideSidebarContext({
57
+ state,
58
+ open,
59
+ setOpen,
60
+ isMobile,
61
+ openMobile,
62
+ setOpenMobile,
63
+ toggleSidebar,
64
+ })
65
+ </script>
66
+
67
+ <template>
68
+ <TooltipProvider :delay-duration="0">
69
+ <div
70
+ :style="{
71
+ '--sidebar-width': SIDEBAR_WIDTH,
72
+ '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
73
+ }"
74
+ :class="cn('group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar', props.class)"
75
+ v-bind="$attrs"
76
+ >
77
+ <slot />
78
+ </div>
79
+ </TooltipProvider>
80
+ </template>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '@/lib/utils'
4
+ import { useSidebar } from './utils'
5
+
6
+ const props = defineProps<{
7
+ class?: HTMLAttributes['class']
8
+ }>()
9
+
10
+ const { toggleSidebar } = useSidebar()
11
+ </script>
12
+
13
+ <template>
14
+ <button
15
+ data-sidebar="rail"
16
+ aria-label="Toggle Sidebar"
17
+ :tabindex="-1"
18
+ title="Toggle Sidebar"
19
+ :class="cn(
20
+ 'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex',
21
+ '[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize',
22
+ '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
23
+ 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar',
24
+ '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
25
+ '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
26
+ props.class,
27
+ )"
28
+ @click="toggleSidebar"
29
+ >
30
+ <slot />
31
+ </button>
32
+ </template>