primitives

Sheet

Sheet — базовый примитив RevueUI (Reka UI + shadcn-совместимый API).

Загрузка превью…

src/ui/sheet/Sheet.vue

<script setup lang="ts">
/**
 * Порт базового радикс-слоя (registry/bases/radix/ui/sheet.tsx, MIT).
 * Radix Dialog (переиспользуется как Sheet) -> reka-ui Dialog, один в один.
 * Наш примитив dialog уже портирован (packages/ui/src/ui/dialog) —
 * Sheet переиспользует те же reka-ui Dialog* компоненты.
 */
import { DialogRoot } from "reka-ui"
</script>

<template>
  <DialogRoot data-slot="sheet">
    <slot />
  </DialogRoot>
</template>

src/ui/sheet/SheetClose.vue

<script setup lang="ts">
import { DialogClose } from "reka-ui"
</script>

<template>
  <DialogClose data-slot="sheet-close">
    <slot />
  </DialogClose>
</template>

src/ui/sheet/SheetContent.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { DialogClose, DialogContent } from "reka-ui"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import SheetOverlay from "./SheetOverlay.vue"
import SheetPortal from "./SheetPortal.vue"

const props = withDefaults(
  defineProps<{
    class?: HTMLAttributes["class"]
    side?: "top" | "right" | "bottom" | "left"
    showCloseButton?: boolean
  }>(),
  {
    side: "right",
    showCloseButton: true,
  }
)
</script>

<template>
  <SheetPortal>
    <SheetOverlay />
    <DialogContent
      data-slot="sheet-content"
      :data-side="props.side"
      :class="
        cn(
          'cn-sheet-content data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10',
          props.class
        )
      "
    >
      <slot />
      <DialogClose v-if="props.showCloseButton" data-slot="sheet-close" as-child>
        <Button variant="ghost" class="cn-sheet-close" size="icon-sm">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="24"
            height="24"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path d="M18 6 6 18" />
            <path d="m6 6 12 12" />
          </svg>
          <span class="sr-only">Close</span>
        </Button>
      </DialogClose>
    </DialogContent>
  </SheetPortal>
</template>

src/ui/sheet/SheetDescription.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { DialogDescription } from "reka-ui"
import { cn } from "@/lib/utils"

const props = defineProps<{
  class?: HTMLAttributes["class"]
}>()
</script>

<template>
  <DialogDescription
    data-slot="sheet-description"
    :class="cn('cn-sheet-description', props.class)"
  >
    <slot />
  </DialogDescription>
</template>

src/ui/sheet/SheetFooter.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"

const props = defineProps<{
  class?: HTMLAttributes["class"]
}>()
</script>

<template>
  <div
    data-slot="sheet-footer"
    :class="cn('cn-sheet-footer mt-auto flex flex-col', props.class)"
  >
    <slot />
  </div>
</template>

src/ui/sheet/SheetHeader.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"

const props = defineProps<{
  class?: HTMLAttributes["class"]
}>()
</script>

<template>
  <div data-slot="sheet-header" :class="cn('cn-sheet-header flex flex-col', props.class)">
    <slot />
  </div>
</template>

src/ui/sheet/SheetOverlay.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { DialogOverlay } from "reka-ui"
import { cn } from "@/lib/utils"

const props = defineProps<{
  class?: HTMLAttributes["class"]
}>()
</script>

<template>
  <DialogOverlay
    data-slot="sheet-overlay"
    :class="
      cn(
        'cn-sheet-overlay fixed inset-0 z-50 duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
        props.class
      )
    "
  />
</template>

src/ui/sheet/SheetPortal.vue

<script setup lang="ts">
import { DialogPortal } from "reka-ui"
</script>

<template>
  <DialogPortal data-slot="sheet-portal">
    <slot />
  </DialogPortal>
</template>

src/ui/sheet/SheetTitle.vue

<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { DialogTitle } from "reka-ui"
import { cn } from "@/lib/utils"

const props = defineProps<{
  class?: HTMLAttributes["class"]
}>()
</script>

<template>
  <DialogTitle
    data-slot="sheet-title"
    :class="cn('cn-sheet-title cn-font-heading', props.class)"
  >
    <slot />
  </DialogTitle>
</template>

src/ui/sheet/SheetTrigger.vue

<script setup lang="ts">
import { DialogTrigger } from "reka-ui"
</script>

<template>
  <DialogTrigger data-slot="sheet-trigger">
    <slot />
  </DialogTrigger>
</template>

src/ui/sheet/index.ts

export { default as Sheet } from "./Sheet.vue"
export { default as SheetTrigger } from "./SheetTrigger.vue"
export { default as SheetClose } from "./SheetClose.vue"
export { default as SheetPortal } from "./SheetPortal.vue"
export { default as SheetOverlay } from "./SheetOverlay.vue"
export { default as SheetContent } from "./SheetContent.vue"
export { default as SheetHeader } from "./SheetHeader.vue"
export { default as SheetFooter } from "./SheetFooter.vue"
export { default as SheetTitle } from "./SheetTitle.vue"
export { default as SheetDescription } from "./SheetDescription.vue"

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/sheet.json

Зависимости реестра

npm-зависимости

  • reka-ui

Источник: порт из ReUI (Keenthemes, MIT)