docs-please 0.2.0-beta.0 → 0.2.2-beta.0

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 (51) hide show
  1. package/app/app.config.ts +9 -1
  2. package/app/assets/css/main.css +2 -2
  3. package/app/components/app/AppHeader.vue +92 -37
  4. package/app/components/app/AppHeaderCenter.vue +3 -0
  5. package/app/components/app/AppHeaderLogo.vue +43 -0
  6. package/app/components/app/AppHeaderSearch.vue +3 -0
  7. package/app/components/app/AppSearch.vue +189 -0
  8. package/app/components/app/AppSearchButton.vue +51 -0
  9. package/app/components/content/Accordion.vue +117 -0
  10. package/app/components/content/AccordionItem.vue +27 -0
  11. package/app/components/content/Badge.vue +42 -0
  12. package/app/components/content/Collapsible.vue +56 -0
  13. package/app/components/content/ProseKbd.vue +9 -0
  14. package/app/components/content/ProseTable.vue +2 -2
  15. package/app/components/content/ProseTh.vue +1 -1
  16. package/app/components/content/ProseTr.vue +1 -1
  17. package/app/components/ui/command/Command.vue +86 -0
  18. package/app/components/ui/command/CommandDialog.vue +21 -0
  19. package/app/components/ui/command/CommandEmpty.vue +23 -0
  20. package/app/components/ui/command/CommandGroup.vue +44 -0
  21. package/app/components/ui/command/CommandInput.vue +35 -0
  22. package/app/components/ui/command/CommandItem.vue +75 -0
  23. package/app/components/ui/command/CommandList.vue +21 -0
  24. package/app/components/ui/command/CommandSeparator.vue +20 -0
  25. package/app/components/ui/command/CommandShortcut.vue +14 -0
  26. package/app/components/ui/command/index.ts +25 -0
  27. package/app/components/ui/dialog/Dialog.vue +19 -0
  28. package/app/components/ui/dialog/DialogClose.vue +15 -0
  29. package/app/components/ui/dialog/DialogContent.vue +53 -0
  30. package/app/components/ui/dialog/DialogDescription.vue +23 -0
  31. package/app/components/ui/dialog/DialogFooter.vue +15 -0
  32. package/app/components/ui/dialog/DialogHeader.vue +17 -0
  33. package/app/components/ui/dialog/DialogOverlay.vue +21 -0
  34. package/app/components/ui/dialog/DialogScrollContent.vue +59 -0
  35. package/app/components/ui/dialog/DialogTitle.vue +23 -0
  36. package/app/components/ui/dialog/DialogTrigger.vue +15 -0
  37. package/app/components/ui/dialog/index.ts +10 -0
  38. package/app/components/ui/kbd/Kbd.vue +21 -0
  39. package/app/components/ui/kbd/KbdGroup.vue +17 -0
  40. package/app/components/ui/kbd/index.ts +2 -0
  41. package/app/composables/useContentSearch.ts +52 -0
  42. package/app/layouts/default.vue +1 -0
  43. package/app/layouts/docs.vue +1 -0
  44. package/app/utils/navigation.ts +7 -0
  45. package/app/utils/prerender.ts +12 -0
  46. package/nuxt.config.ts +4 -0
  47. package/nuxt.schema.ts +44 -0
  48. package/package.json +9 -3
  49. package/server/routes/raw/[...slug].md.get.ts +45 -0
  50. package/utils/git.ts +59 -0
  51. package/utils/meta.ts +29 -0
@@ -0,0 +1,56 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes, VNode } from 'vue'
3
+ import { ref } from 'vue'
4
+ import { ChevronDown } from 'lucide-vue-next'
5
+ import { cn } from '~/lib/utils'
6
+ import {
7
+ Collapsible as UICollapsible,
8
+ CollapsibleContent as UICollapsibleContent,
9
+ CollapsibleTrigger as UICollapsibleTrigger,
10
+ } from '~/components/ui/collapsible'
11
+
12
+ export interface CollapsibleProps {
13
+ /**
14
+ * The trigger label text.
15
+ * @default 'Show props'
16
+ */
17
+ label?: string
18
+ /**
19
+ * Whether the collapsible is open by default.
20
+ * @default false
21
+ */
22
+ defaultOpen?: boolean
23
+ class?: HTMLAttributes['class']
24
+ }
25
+
26
+ const props = withDefaults(defineProps<CollapsibleProps>(), {
27
+ label: 'Show props',
28
+ defaultOpen: false,
29
+ })
30
+
31
+ defineSlots<{
32
+ default(): VNode[]
33
+ }>()
34
+
35
+ const isOpen = ref(props.defaultOpen)
36
+ </script>
37
+
38
+ <template>
39
+ <UICollapsible
40
+ v-model:open="isOpen"
41
+ :class="cn('mt-4', props.class)"
42
+ >
43
+ <UICollapsibleTrigger
44
+ class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
45
+ >
46
+ <ChevronDown
47
+ class="size-4 transition-transform duration-200"
48
+ :class="{ 'rotate-180': isOpen }"
49
+ />
50
+ <span>{{ label }}</span>
51
+ </UICollapsibleTrigger>
52
+ <UICollapsibleContent class="mt-2 text-left [&_[data-slot=prose-table]]:my-0 [&_[data-slot=prose-table]]:border-0 [&_[data-slot=prose-table]]:rounded-none">
53
+ <slot />
54
+ </UICollapsibleContent>
55
+ </UICollapsible>
56
+ </template>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ import { Kbd } from '~/components/ui/kbd'
3
+ </script>
4
+
5
+ <template>
6
+ <Kbd>
7
+ <slot />
8
+ </Kbd>
9
+ </template>
@@ -12,10 +12,10 @@ const props = defineProps<{
12
12
  </script>
13
13
 
14
14
  <template>
15
- <div class="no-scrollbar my-6 w-full overflow-y-auto rounded-lg border">
15
+ <div data-slot="prose-table" class="no-scrollbar my-6 w-full overflow-hidden rounded-lg border">
16
16
  <table
17
17
  :class="cn(
18
- 'relative w-full overflow-hidden border-none text-sm [&_tbody_tr:last-child]:border-b-0',
18
+ 'w-full border-collapse text-sm',
19
19
  props.class,
20
20
  )"
21
21
  v-bind="$attrs"
@@ -2,7 +2,7 @@
2
2
  </script>
3
3
 
4
4
  <template>
5
- <th class="px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right">
5
+ <th class="border-b px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right">
6
6
  <slot />
7
7
  </th>
8
8
  </template>
@@ -2,7 +2,7 @@
2
2
  </script>
3
3
 
4
4
  <template>
5
- <tr class="last:border-b-none m-0 border-b">
5
+ <tr class="m-0 border-b last:border-b-0">
6
6
  <slot />
7
7
  </tr>
8
8
  </template>
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ import type { ListboxRootEmits, ListboxRootProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { ListboxRoot, useFilter, useForwardPropsEmits } from "reka-ui"
6
+ import { reactive, ref, watch } from "vue"
7
+ import { cn } from '~/lib/utils'
8
+ import { provideCommandContext } from "."
9
+
10
+ const props = withDefaults(defineProps<ListboxRootProps & { class?: HTMLAttributes["class"] }>(), {
11
+ modelValue: "",
12
+ })
13
+
14
+ const emits = defineEmits<ListboxRootEmits>()
15
+
16
+ const delegatedProps = reactiveOmit(props, "class")
17
+
18
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
19
+
20
+ const allItems = ref<Map<string, string>>(new Map())
21
+ const allGroups = ref<Map<string, Set<string>>>(new Map())
22
+
23
+ const { contains } = useFilter({ sensitivity: "base" })
24
+ const filterState = reactive({
25
+ search: "",
26
+ filtered: {
27
+ /** The count of all visible items. */
28
+ count: 0,
29
+ /** Map from visible item id to its search score. */
30
+ items: new Map() as Map<string, number>,
31
+ /** Set of groups with at least one visible item. */
32
+ groups: new Set() as Set<string>,
33
+ },
34
+ })
35
+
36
+ function filterItems() {
37
+ if (!filterState.search) {
38
+ filterState.filtered.count = allItems.value.size
39
+ // Do nothing, each item will know to show itself because search is empty
40
+ return
41
+ }
42
+
43
+ // Reset the groups
44
+ filterState.filtered.groups = new Set()
45
+ let itemCount = 0
46
+
47
+ // Check which items should be included
48
+ for (const [id, value] of allItems.value) {
49
+ const score = contains(value, filterState.search)
50
+ filterState.filtered.items.set(id, score ? 1 : 0)
51
+ if (score)
52
+ itemCount++
53
+ }
54
+
55
+ // Check which groups have at least 1 item shown
56
+ for (const [groupId, group] of allGroups.value) {
57
+ for (const itemId of group) {
58
+ if (filterState.filtered.items.get(itemId)! > 0) {
59
+ filterState.filtered.groups.add(groupId)
60
+ break
61
+ }
62
+ }
63
+ }
64
+
65
+ filterState.filtered.count = itemCount
66
+ }
67
+
68
+ watch(() => filterState.search, () => {
69
+ filterItems()
70
+ })
71
+
72
+ provideCommandContext({
73
+ allItems,
74
+ allGroups,
75
+ filterState,
76
+ })
77
+ </script>
78
+
79
+ <template>
80
+ <ListboxRoot
81
+ v-bind="forwarded"
82
+ :class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', props.class)"
83
+ >
84
+ <slot />
85
+ </ListboxRoot>
86
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { DialogRootEmits, DialogRootProps } from "reka-ui"
3
+ import { useForwardPropsEmits } from "reka-ui"
4
+ import { Dialog, DialogContent } from '~/components/ui/dialog'
5
+ import Command from "./Command.vue"
6
+
7
+ const props = defineProps<DialogRootProps>()
8
+ const emits = defineEmits<DialogRootEmits>()
9
+
10
+ const forwarded = useForwardPropsEmits(props, emits)
11
+ </script>
12
+
13
+ <template>
14
+ <Dialog v-bind="forwarded">
15
+ <DialogContent class="overflow-hidden p-0 shadow-lg">
16
+ <Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
17
+ <slot />
18
+ </Command>
19
+ </DialogContent>
20
+ </Dialog>
21
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { Primitive } from "reka-ui"
6
+ import { computed } from "vue"
7
+ import { cn } from '~/lib/utils'
8
+ import { useCommand } from "."
9
+
10
+ const props = defineProps<PrimitiveProps & { class?: HTMLAttributes["class"] }>()
11
+
12
+ const delegatedProps = reactiveOmit(props, "class")
13
+
14
+ const { filterState } = useCommand()
15
+ const isRender = computed(() => !!filterState.search && filterState.filtered.count === 0,
16
+ )
17
+ </script>
18
+
19
+ <template>
20
+ <Primitive v-if="isRender" v-bind="delegatedProps" :class="cn('py-6 text-center text-sm', props.class)">
21
+ <slot />
22
+ </Primitive>
23
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import type { ListboxGroupProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { ListboxGroup, ListboxGroupLabel, useId } from "reka-ui"
6
+ import { computed, onMounted, onUnmounted } from "vue"
7
+ import { cn } from '~/lib/utils'
8
+ import { provideCommandGroupContext, useCommand } from "."
9
+
10
+ const props = defineProps<ListboxGroupProps & {
11
+ class?: HTMLAttributes["class"]
12
+ heading?: string
13
+ }>()
14
+
15
+ const delegatedProps = reactiveOmit(props, "class")
16
+
17
+ const { allGroups, filterState } = useCommand()
18
+ const id = useId()
19
+
20
+ const isRender = computed(() => !filterState.search ? true : filterState.filtered.groups.has(id))
21
+
22
+ provideCommandGroupContext({ id })
23
+ onMounted(() => {
24
+ if (!allGroups.value.has(id))
25
+ allGroups.value.set(id, new Set())
26
+ })
27
+ onUnmounted(() => {
28
+ allGroups.value.delete(id)
29
+ })
30
+ </script>
31
+
32
+ <template>
33
+ <ListboxGroup
34
+ v-bind="delegatedProps"
35
+ :id="id"
36
+ :class="cn('overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground', props.class)"
37
+ :hidden="isRender ? undefined : true"
38
+ >
39
+ <ListboxGroupLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
40
+ {{ heading }}
41
+ </ListboxGroupLabel>
42
+ <slot />
43
+ </ListboxGroup>
44
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import type { ListboxFilterProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { Search } from "lucide-vue-next"
6
+ import { ListboxFilter, useForwardProps } from "reka-ui"
7
+ import { cn } from '~/lib/utils'
8
+ import { useCommand } from "."
9
+
10
+ defineOptions({
11
+ inheritAttrs: false,
12
+ })
13
+
14
+ const props = defineProps<ListboxFilterProps & {
15
+ class?: HTMLAttributes["class"]
16
+ }>()
17
+
18
+ const delegatedProps = reactiveOmit(props, "class")
19
+
20
+ const forwardedProps = useForwardProps(delegatedProps)
21
+
22
+ const { filterState } = useCommand()
23
+ </script>
24
+
25
+ <template>
26
+ <div class="flex items-center border-b px-3" cmdk-input-wrapper>
27
+ <Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
28
+ <ListboxFilter
29
+ v-bind="{ ...forwardedProps, ...$attrs }"
30
+ v-model="filterState.search"
31
+ auto-focus
32
+ :class="cn('flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
33
+ />
34
+ </div>
35
+ </template>
@@ -0,0 +1,75 @@
1
+ <script setup lang="ts">
2
+ import type { ListboxItemEmits, ListboxItemProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit, useCurrentElement } from "@vueuse/core"
5
+ import { ListboxItem, useForwardPropsEmits, useId } from "reka-ui"
6
+ import { computed, onMounted, onUnmounted, ref } from "vue"
7
+ import { cn } from '~/lib/utils'
8
+ import { useCommand, useCommandGroup } from "."
9
+
10
+ const props = defineProps<ListboxItemProps & { class?: HTMLAttributes["class"] }>()
11
+ const emits = defineEmits<ListboxItemEmits>()
12
+
13
+ const delegatedProps = reactiveOmit(props, "class")
14
+
15
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
16
+
17
+ const id = useId()
18
+ const { filterState, allItems, allGroups } = useCommand()
19
+ const groupContext = useCommandGroup()
20
+
21
+ const isRender = computed(() => {
22
+ if (!filterState.search) {
23
+ return true
24
+ }
25
+ else {
26
+ const filteredCurrentItem = filterState.filtered.items.get(id)
27
+ // If the filtered items is undefined means not in the all times map yet
28
+ // Do the first render to add into the map
29
+ if (filteredCurrentItem === undefined) {
30
+ return true
31
+ }
32
+
33
+ // Check with filter
34
+ return filteredCurrentItem > 0
35
+ }
36
+ })
37
+
38
+ const itemRef = ref()
39
+ const currentElement = useCurrentElement(itemRef)
40
+ onMounted(() => {
41
+ if (!(currentElement.value instanceof HTMLElement))
42
+ return
43
+
44
+ // textValue to perform filter
45
+ allItems.value.set(id, currentElement.value.textContent ?? props?.value!.toString())
46
+
47
+ const groupId = groupContext?.id
48
+ if (groupId) {
49
+ if (!allGroups.value.has(groupId)) {
50
+ allGroups.value.set(groupId, new Set([id]))
51
+ }
52
+ else {
53
+ allGroups.value.get(groupId)?.add(id)
54
+ }
55
+ }
56
+ })
57
+ onUnmounted(() => {
58
+ allItems.value.delete(id)
59
+ })
60
+ </script>
61
+
62
+ <template>
63
+ <ListboxItem
64
+ v-if="isRender"
65
+ v-bind="forwarded"
66
+ :id="id"
67
+ ref="itemRef"
68
+ :class="cn('relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', props.class)"
69
+ @select="() => {
70
+ filterState.search = ''
71
+ }"
72
+ >
73
+ <slot />
74
+ </ListboxItem>
75
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { ListboxContentProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { ListboxContent, useForwardProps } from "reka-ui"
6
+ import { cn } from '~/lib/utils'
7
+
8
+ const props = defineProps<ListboxContentProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+
12
+ const forwarded = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <ListboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', props.class)">
17
+ <div role="presentation">
18
+ <slot />
19
+ </div>
20
+ </ListboxContent>
21
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import type { SeparatorProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { Separator } from "reka-ui"
6
+ import { cn } from '~/lib/utils'
7
+
8
+ const props = defineProps<SeparatorProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+ </script>
12
+
13
+ <template>
14
+ <Separator
15
+ v-bind="delegatedProps"
16
+ :class="cn('-mx-1 h-px bg-border', props.class)"
17
+ >
18
+ <slot />
19
+ </Separator>
20
+ </template>
@@ -0,0 +1,14 @@
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
+ <span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', props.class)">
12
+ <slot />
13
+ </span>
14
+ </template>
@@ -0,0 +1,25 @@
1
+ import type { Ref } from "vue"
2
+ import { createContext } from "reka-ui"
3
+
4
+ export { default as Command } from "./Command.vue"
5
+ export { default as CommandDialog } from "./CommandDialog.vue"
6
+ export { default as CommandEmpty } from "./CommandEmpty.vue"
7
+ export { default as CommandGroup } from "./CommandGroup.vue"
8
+ export { default as CommandInput } from "./CommandInput.vue"
9
+ export { default as CommandItem } from "./CommandItem.vue"
10
+ export { default as CommandList } from "./CommandList.vue"
11
+ export { default as CommandSeparator } from "./CommandSeparator.vue"
12
+ export { default as CommandShortcut } from "./CommandShortcut.vue"
13
+
14
+ export const [useCommand, provideCommandContext] = createContext<{
15
+ allItems: Ref<Map<string, string>>
16
+ allGroups: Ref<Map<string, Set<string>>>
17
+ filterState: {
18
+ search: string
19
+ filtered: { count: number, items: Map<string, number>, groups: Set<string> }
20
+ }
21
+ }>("Command")
22
+
23
+ export const [useCommandGroup, provideCommandGroupContext] = createContext<{
24
+ id?: string
25
+ }>("CommandGroup")
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import type { DialogRootEmits, DialogRootProps } from "reka-ui"
3
+ import { DialogRoot, useForwardPropsEmits } from "reka-ui"
4
+
5
+ const props = defineProps<DialogRootProps>()
6
+ const emits = defineEmits<DialogRootEmits>()
7
+
8
+ const forwarded = useForwardPropsEmits(props, emits)
9
+ </script>
10
+
11
+ <template>
12
+ <DialogRoot
13
+ v-slot="slotProps"
14
+ data-slot="dialog"
15
+ v-bind="forwarded"
16
+ >
17
+ <slot v-bind="slotProps" />
18
+ </DialogRoot>
19
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import type { DialogCloseProps } from "reka-ui"
3
+ import { DialogClose } from "reka-ui"
4
+
5
+ const props = defineProps<DialogCloseProps>()
6
+ </script>
7
+
8
+ <template>
9
+ <DialogClose
10
+ data-slot="dialog-close"
11
+ v-bind="props"
12
+ >
13
+ <slot />
14
+ </DialogClose>
15
+ </template>
@@ -0,0 +1,53 @@
1
+ <script setup lang="ts">
2
+ import type { DialogContentEmits, DialogContentProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { X } from "lucide-vue-next"
6
+ import {
7
+ DialogClose,
8
+ DialogContent,
9
+ DialogPortal,
10
+ useForwardPropsEmits,
11
+ } from "reka-ui"
12
+ import { cn } from '~/lib/utils'
13
+ import DialogOverlay from "./DialogOverlay.vue"
14
+
15
+ defineOptions({
16
+ inheritAttrs: false,
17
+ })
18
+
19
+ const props = withDefaults(defineProps<DialogContentProps & { class?: HTMLAttributes["class"], showCloseButton?: boolean }>(), {
20
+ showCloseButton: true,
21
+ })
22
+ const emits = defineEmits<DialogContentEmits>()
23
+
24
+ const delegatedProps = reactiveOmit(props, "class")
25
+
26
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
27
+ </script>
28
+
29
+ <template>
30
+ <DialogPortal>
31
+ <DialogOverlay />
32
+ <DialogContent
33
+ data-slot="dialog-content"
34
+ v-bind="{ ...$attrs, ...forwarded }"
35
+ :class="
36
+ cn(
37
+ 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
38
+ props.class,
39
+ )"
40
+ >
41
+ <slot />
42
+
43
+ <DialogClose
44
+ v-if="showCloseButton"
45
+ data-slot="dialog-close"
46
+ class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
47
+ >
48
+ <X />
49
+ <span class="sr-only">Close</span>
50
+ </DialogClose>
51
+ </DialogContent>
52
+ </DialogPortal>
53
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import type { DialogDescriptionProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { DialogDescription, useForwardProps } from "reka-ui"
6
+ import { cn } from '~/lib/utils'
7
+
8
+ const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+
12
+ const forwardedProps = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <DialogDescription
17
+ data-slot="dialog-description"
18
+ v-bind="forwardedProps"
19
+ :class="cn('text-muted-foreground text-sm', props.class)"
20
+ >
21
+ <slot />
22
+ </DialogDescription>
23
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from "vue"
3
+ import { cn } from '~/lib/utils'
4
+
5
+ const props = defineProps<{ class?: HTMLAttributes["class"] }>()
6
+ </script>
7
+
8
+ <template>
9
+ <div
10
+ data-slot="dialog-footer"
11
+ :class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)"
12
+ >
13
+ <slot />
14
+ </div>
15
+ </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-slot="dialog-header"
13
+ :class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { DialogOverlayProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { DialogOverlay } from "reka-ui"
6
+ import { cn } from '~/lib/utils'
7
+
8
+ const props = defineProps<DialogOverlayProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+ </script>
12
+
13
+ <template>
14
+ <DialogOverlay
15
+ data-slot="dialog-overlay"
16
+ v-bind="delegatedProps"
17
+ :class="cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)"
18
+ >
19
+ <slot />
20
+ </DialogOverlay>
21
+ </template>