dev
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<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="empty"
|
||||
:class="cn(
|
||||
'gap-4 rounded-xl border-dashed p-6 flex w-full min-w-0 flex-1 flex-col items-center justify-center text-center text-balance',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<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="empty-content"
|
||||
:class="cn(
|
||||
'gap-2.5 text-sm flex w-full max-w-sm min-w-0 flex-col items-center text-balance',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
data-slot="empty-description"
|
||||
:class="cn(
|
||||
'text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
|
||||
$attrs.class ?? '',
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<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="empty-header"
|
||||
:class="cn(
|
||||
'gap-2 flex max-w-sm flex-col items-center',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { EmptyMediaVariants } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { emptyMediaVariants } from '.'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
variant?: EmptyMediaVariants['variant']
|
||||
}>(), {
|
||||
variant: 'default',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="empty-icon"
|
||||
:data-variant="variant"
|
||||
:class="cn(emptyMediaVariants({ variant }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="empty-title"
|
||||
:class="cn('text-sm font-medium tracking-tight cn-font-heading', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
export { default as Empty } from './Empty.vue'
|
||||
export { default as EmptyContent } from './EmptyContent.vue'
|
||||
export { default as EmptyDescription } from './EmptyDescription.vue'
|
||||
export { default as EmptyHeader } from './EmptyHeader.vue'
|
||||
export { default as EmptyMedia } from './EmptyMedia.vue'
|
||||
export { default as EmptyTitle } from './EmptyTitle.vue'
|
||||
|
||||
export const emptyMediaVariants = cva(
|
||||
'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
icon: 'bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*=size-])]:size-4',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { ItemVariants } from '.'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { itemVariants } from '.'
|
||||
|
||||
const props = withDefaults(defineProps<PrimitiveProps & {
|
||||
class?: HTMLAttributes['class']
|
||||
variant?: ItemVariants['variant']
|
||||
size?: ItemVariants['size']
|
||||
}>(), {
|
||||
as: 'div',
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-slot="item"
|
||||
:data-variant="variant"
|
||||
:data-size="size"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(itemVariants({ variant, size }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="item-actions"
|
||||
:class="cn('gap-2 flex items-center', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="item-content"
|
||||
:class="cn('gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
data-slot="item-description"
|
||||
:class="cn(
|
||||
'text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="item-footer"
|
||||
:class="cn('gap-2 flex basis-full items-center justify-between', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
role="list"
|
||||
data-slot="item-group"
|
||||
:class="cn('gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2 group/item-group flex w-full flex-col', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="item-header"
|
||||
:class="cn('gap-2 flex basis-full items-center justify-between', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { ItemMediaVariants } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { itemMediaVariants } from '.'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
variant?: ItemMediaVariants['variant']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="item-media"
|
||||
:data-variant="props.variant"
|
||||
:class="cn(itemMediaVariants({ variant }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { SeparatorProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
|
||||
const props = defineProps<
|
||||
SeparatorProps & { class?: HTMLAttributes['class'] }
|
||||
>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Separator
|
||||
data-slot="item-separator"
|
||||
orientation="horizontal"
|
||||
:class="cn('my-2', props.class)"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<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="item-title"
|
||||
:class="cn('gap-2 text-sm leading-snug font-medium underline-offset-4 cn-font-heading line-clamp-1 flex w-fit items-center', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
export { default as Item } from './Item.vue'
|
||||
export { default as ItemActions } from './ItemActions.vue'
|
||||
export { default as ItemContent } from './ItemContent.vue'
|
||||
export { default as ItemDescription } from './ItemDescription.vue'
|
||||
export { default as ItemFooter } from './ItemFooter.vue'
|
||||
export { default as ItemGroup } from './ItemGroup.vue'
|
||||
export { default as ItemHeader } from './ItemHeader.vue'
|
||||
export { default as ItemMedia } from './ItemMedia.vue'
|
||||
export { default as ItemSeparator } from './ItemSeparator.vue'
|
||||
export { default as ItemTitle } from './ItemTitle.vue'
|
||||
|
||||
export const itemVariants = cva(
|
||||
'[a]:hover:bg-muted rounded-lg border text-sm group/item flex w-full flex-wrap items-center transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'border-transparent',
|
||||
outline: 'border-border',
|
||||
muted: 'bg-muted/50 border-transparent',
|
||||
},
|
||||
size: {
|
||||
default: 'gap-2.5 px-3 py-2.5',
|
||||
sm: 'gap-2.5 px-3 py-2.5',
|
||||
xs: 'gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export const itemMediaVariants = cva(
|
||||
'gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
icon: '[&_svg:not([class*=size-])]:size-4',
|
||||
image: 'size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ItemVariants = VariantProps<typeof itemVariants>
|
||||
export type ItemMediaVariants = VariantProps<typeof itemMediaVariants>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProgressRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import {
|
||||
ProgressIndicator,
|
||||
ProgressRoot,
|
||||
} from 'reka-ui'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<ProgressRootProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
modelValue: 0,
|
||||
},
|
||||
)
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProgressRoot
|
||||
data-slot="progress"
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'bg-muted h-1 rounded-full relative flex w-full items-center overflow-x-hidden',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<ProgressIndicator
|
||||
data-slot="progress-indicator"
|
||||
class="bg-primary size-full flex-1 transition-all"
|
||||
:style="`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`"
|
||||
/>
|
||||
</ProgressRoot>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Progress } from './Progress.vue'
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
|
||||
import { Loader2Icon } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Loader2Icon role="status" aria-label="Loading" :class="cn('size-4 animate-spin', props.class)" />
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Spinner } from './Spinner.vue'
|
||||
Reference in New Issue
Block a user