Merge branch 'dev' into organize

This commit is contained in:
Mukund Shah 2023-08-23 12:18:44 +05:45 committed by GitHub
commit 28e25ac32a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 3410 additions and 1944 deletions

13
.eslintrc.cjs Normal file
View File

@ -0,0 +1,13 @@
const process = require('node:process')
process.env.ESLINT_TSCONFIG = 'tsconfig.json'
module.exports = {
extends: '@antfu',
rules: {
'symbol-description': 'off',
'no-console': 'warn',
'no-tabs': 'off',
},
}

View File

@ -1,6 +1,6 @@
name: 🐞 Bug report
description: Create a report to help us improve shadcn-vue.
title: "[Bug]: "
title: '[Bug]: '
labels: [bug]
body:
- type: markdown

37
package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "shadcn-vue",
"private": true,
"packageManager": "pnpm@8.6.3",
"license": "MIT",
"repository": "radix-vue/shadcn-vue",
"workspaces": [
"packages/*"
],
"scripts": {
"dev": "pnpm --filter shadcn-vue dev",
"prepare": "pnpm simple-git-hooks",
"lint": "eslint . --ignore-path .gitignore",
"lint:fix": "eslint . --fix --ignore-path .gitignore"
},
"devDependencies": {
"@antfu/eslint-config": "^0.39.7",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"eslint": "^8.43.0",
"lint-staged": "^14.0.0",
"pnpm": "^8.6.12",
"simple-git-hooks": "^2.9.0"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged",
"commit-msg": "pnpm commitlint --edit ${1}"
},
"lint-staged": {
"*": "eslint --fix"
}
}

View File

@ -1,11 +1,11 @@
import { cva } from "class-variance-authority";
import { cva } from 'class-variance-authority'
export const buttonClass = cva(
'flex px-2 py-2',
{
variants: {
primary: 'bg-blue-200',
secondary: 'bg-red-400'
}
}
)
secondary: 'bg-red-400',
},
},
)

View File

@ -1,32 +1,31 @@
{
"name": "radix-vue",
"name": "shadcn-vue",
"type": "module",
"version": "0.0.1",
"files": [
"dist"
],
"main": "./dist/radix-vue.umd.js",
"module": "./dist/radix-vue.es.js",
"exports": {
".": {
"import": "./dist/radix-vue.es.js",
"require": "./dist/radix-vue.umd.js"
"require": "./dist/radix-vue.umd.js",
"import": "./dist/radix-vue.es.js"
},
"./dist/style.css": "./dist/style.css"
},
"type": "module",
"main": "./dist/radix-vue.umd.js",
"module": "./dist/radix-vue.es.js",
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@headlessui-float/vue": "^0.11.2",
"@headlessui/vue": "^1.7.14",
"@morev/vue-transitions": "^2.3.6",
"@vueuse/core": "^10.2.1",
"class-variance-authority": "^0.6.1",
"radix-vue": "^0.1.6",
"vue": "^3.2.47"
"clsx": "^2.0.0",
"radix-vue": "file:../../../radix-vue/packages/radix-vue",
"vue": "^3.3.4"
},
"devDependencies": {
"@iconify/vue": "^4.1.1",

View File

@ -1,394 +1,9 @@
<script setup lang="ts">
import { ref } from "vue";
import {
Accordion,
AccordionTrigger,
AccordionItem,
AccordionContent,
} from "./components/ui/Accordion";
import { Alert, AlertDescription, AlertTitle } from "./components/ui/Alert";
import { Card as InternalCard } from "./components/Internal/Docs/Card";
import { TransitionExpand } from "@morev/vue-transitions";
import { Icon } from "@iconify/vue";
import { Button } from "./components/ui/Button";
import {
AlertDialog,
AlertDialogTitle,
AlertDialogHeader,
AlertDialogDescription,
AlertDialogFooter,
} from "./components/ui/AlertDialog";
import {
Dialog,
DialogTitle,
DialogHeader,
DialogDescription,
DialogFooter,
} from "./components/ui/Dialog";
import { Avatar, AvatarFallback, AvatarImage } from "./components/ui/Avatar";
import { Badge } from "./components/ui/Badge";
import { Checkbox } from "./components/ui/Checkbox";
import { Label } from "./components/ui/Label";
import { Input } from "./components/ui/Input";
import { Textarea } from "./components/ui/Textarea";
import { Switch } from "./components/ui/Switch";
import { Toggle } from "./components/ui/Toggle";
import { Card } from "./components/ui/Demos";
import { AspectRatio, Image } from "./components/ui/AspectRatio";
import {
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
} from "./components/ui/Collapsible";
import { Select, SelectContent, SelectItem, SelectLabel, SelectTrigger } from "./components/ui/Select";
import { Popover, PopoverContent, PopoverTrigger } from "./components/ui/Popover";
import { Separator } from "./components/ui/Separator";
import { Tooltip, TooltipTrigger } from "./components/ui/Tooltip";
import { RadioGroup, RadioGroupItem } from "./components/ui/RadioGroup";
import AccordionDemo2 from "./components/uiv2/AccordionDemo.vue"
import SwitchDemo2 from "./components/uiv2/SwitchDemo.vue"
const alertDialogIsOpen = ref(false);
const dialogIsOpen = ref(false);
const enabled = ref(false);
const checkboxEnabled = ref(false);
const checkboxEnabled2 = ref(false);
const toggleEnabled = ref(false);
const people = [
{ id: 1, name: "Durward Reynolds", unavailable: false },
{ id: 2, name: "Kenton Towne", unavailable: false },
{ id: 3, name: "Therese Wunsch", unavailable: false },
{ id: 4, name: "Benedict Kessler", unavailable: true },
{ id: 5, name: "Katelyn Rohan", unavailable: false },
];
const selectedPerson = ref(people[0]);
const radioOptions = [
"Default",
"Comfortable",
"Compact",
];
const radioValue = ref(radioOptions[0]);
const components = [
"Accordion",
"Alert",
"Alert Dialog",
"Aspect Ratio",
"Avatar",
"Badge",
"Button",
"Calendar",
"Card",
"Checkbox",
"Collapsible",
"Combobox",
"Command",
"Context Menu",
"Data Table",
"Date Picker",
"Dialog",
"Dropdown Menu",
"Form",
"Hover Card",
"Input",
"Label",
"Menubar",
"Navigation Menu",
"Popover",
"Progress",
"Radio Group",
"Scroll Area",
"Select",
"Separator",
"Sheet",
"Skeleton",
"Slider",
"Switch",
"Table",
"Tabs",
"Textarea",
"Toast",
"Toggle",
"Tooltip",
];
import AccordionDemo from './components/ui/AccordionDemo.vue'
</script>
<template>
<div class="flex flex-col items-center px-6 pb-6">
<div class="mt-32 mb-16 w-full max-w-6xl flex flex-col items-center">
<h1 class="text-7xl font-bold mb-2">shadcn vue</h1>
<p class="text-md text-neutral-700">
This is a description of easy UI that has been remade over 10000 times.
</p>
</div>
<div class="flex max-w-6xl">
<div class="w-[200px] pr-4">
<nav class="gap-0.5 flex flex-col text-sm sticky top-0 max-h-screen overflow-scroll">
<button v-for="(link, index) of components" :key="index"
:class="` group relative flex items-center gap-2.5 px-3 py-1.5 rounded-md text-gray-600 hover:text-black hover:bg-gray-200`">
<span :class="`truncate relative`">{{ link }}</span>
</button>
</nav>
</div>
<div class="flex-grow grid grid-cols-1 sm:grid-cols-2 gap-4">
<InternalCard title="Accordion">
<div class="w-full max-w-[400px]">
<Accordion>
<AccordionItem>
<AccordionTrigger> Is it accessible? </AccordionTrigger>
<transition-expand>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</transition-expand>
</AccordionItem>
<AccordionItem>
<AccordionTrigger> Is it styled? </AccordionTrigger>
<transition-expand>
<AccordionContent>
Yes. It comes with default styles that matches the other components'
aesthetic.
</AccordionContent>
</transition-expand>
</AccordionItem>
<AccordionItem>
<AccordionTrigger> Is it animated? </AccordionTrigger>
<transition-expand>
<AccordionContent>
Yes. It's animated by default, but you can disable it if you prefer.
</AccordionContent>
</transition-expand>
</AccordionItem>
</Accordion>
</div>
</InternalCard>
<InternalCard title="Accordionv22">
<AccordionDemo2 />
</InternalCard>
<InternalCard title="Switchv2">
<SwitchDemo2 />
</InternalCard>
<InternalCard title="Alert">
<div class="max-w-[400px]">
<Alert>
<Icon icon="lucide:terminal" class="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components to your app using the cli.
</AlertDescription>
</Alert>
</div>
</InternalCard>
<InternalCard title="Alert Dialog">
<Button label="Open" @click="alertDialogIsOpen = true">Show Dialog</Button>
<AlertDialog v-model="alertDialogIsOpen">
<AlertDialogHeader>
<AlertDialogTitle>Title</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter><Button variant="outline" @click="alertDialogIsOpen = false">Cancel</Button><Button>CTA
Button</Button>
</AlertDialogFooter>
</AlertDialog>
</InternalCard>
<InternalCard title="Aspect Ratio">
<AspectRatio :ratio="16 / 9" class="bg-muted">
<Image src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer" fill class="rounded-md object-cover" />
</AspectRatio>
</InternalCard>
<InternalCard title="Avatar">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</InternalCard>
<InternalCard title="Badge">
<Badge>Badge</Badge>
</InternalCard>
<InternalCard title="Button">
<Button>Button</Button>
</InternalCard>
<InternalCard title="Calendar"></InternalCard>
<InternalCard title="Card">
<Card />
</InternalCard>
<InternalCard title="Checkbox">
<div class="flex items-center space-x-2">
<Checkbox id="terms" v-model="checkboxEnabled" />
<label htmlFor="terms"
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
Accept terms and conditions
</label>
</div>
</InternalCard>
<InternalCard title="Collapsible">
<Collapsible class="w-[350px] space-y-2" v-slot="{ open }">
<div class="flex items-center justify-between space-x-4 px-4">
<h4 class="text-sm font-semibold">@peduarte starred 3 repositories</h4>
<CollapsibleTrigger>
<Button variant="outline" class="w-9 p-0 aspect-square">
<span>
<Icon v-show="!open" icon="lucide:chevrons-up-down" class="h-4 w-4 text-black" />
<Icon v-show="open" icon="lucide:x" class="h-4 w-4 text-black" />
</span>
</Button>
</CollapsibleTrigger>
</div>
<div class="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/primitives
</div>
<CollapsibleContent class="space-y-2">
<div class="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/colors
</div>
<div class="rounded-md border px-4 py-3 font-mono text-sm">
@stitches/react
</div>
</CollapsibleContent>
</Collapsible>
</InternalCard>
<InternalCard title="Combobox"></InternalCard>
<InternalCard title="Command"></InternalCard>
<InternalCard title="Context Menu"></InternalCard>
<InternalCard title="Data Table"></InternalCard>
<InternalCard title="Date Picker"></InternalCard>
<InternalCard title="Dialog">
<Button label="Open" @click="dialogIsOpen = true">Show Dialog</Button>
<Dialog v-model="dialogIsOpen">
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</DialogDescription>
</DialogHeader>
<DialogFooter><Button variant="outline" @click="dialogIsOpen = false">Cancel</Button><Button>CTA
Button</Button>
</DialogFooter>
</Dialog>
</InternalCard>
<InternalCard title="Dropdown Menu"></InternalCard>
<InternalCard title="Hover Card"></InternalCard>
<InternalCard title="Input">
<Input type="email" placeholder="Email" />
</InternalCard>
<InternalCard title="Label">
<div class="flex items-center space-x-2">
<Checkbox id="terms2" v-model="checkboxEnabled2" />
<Label htmlFor="terms2">Accept terms and conditions</Label>
</div>
</InternalCard>
<InternalCard title="Menubar"></InternalCard>
<InternalCard title="Navigation Menu"></InternalCard>
<InternalCard title="Popover">
<Popover>
<PopoverTrigger><Button variant="outline">Open popover</Button></PopoverTrigger>
<PopoverContent class="w-80">
<div class="grid gap-4">
<div class="space-y-2">
<h4 class="font-medium leading-none">Dimensions</h4>
<p class="text-sm text-muted-foreground">
Set the dimensions for the layer.
</p>
</div>
<div class="grid gap-2">
<div class="grid grid-cols-3 items-center gap-4">
<Label htmlFor="width">Width</Label>
<Input id="width" defaultValue="100%" class="col-span-2 h-8" />
</div>
<div class="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxWidth">Max. width</Label>
<Input id="maxWidth" defaultValue="300px" class="col-span-2 h-8" />
</div>
<div class="grid grid-cols-3 items-center gap-4">
<Label htmlFor="height">Height</Label>
<Input id="height" defaultValue="25px" class="col-span-2 h-8" />
</div>
<div class="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxHeight">Max. height</Label>
<Input id="maxHeight" defaultValue="none" class="col-span-2 h-8" />
</div>
</div>
</div>
</PopoverContent>
</Popover>
</InternalCard>
<InternalCard title="Progress"></InternalCard>
<InternalCard title="Radio Group">
<RadioGroup v-model="radioValue">
<RadioGroupItem v-for="option in radioOptions" :value="option" :id="option">
<Label :for="option">{{ option }}</Label>
</RadioGroupItem>
</RadioGroup>
</InternalCard>
<InternalCard title="Scroll Area"></InternalCard>
<InternalCard title="Select">
<Select v-model="selectedPerson.id">
<SelectTrigger class="w-[210px]">
{{ selectedPerson.name }}
</SelectTrigger>
<SelectContent>
<SelectLabel>Fruits</SelectLabel>
<SelectItem v-for="person in people" :key="person.id" :value="person.id" :disabled="person.unavailable">{{
person.name }}</SelectItem>
</SelectContent>
</Select>
</InternalCard>
<InternalCard title="Separator">
<div>
<div className="space-y-1">
<h4 className="text-sm font-medium leading-none">Radix Primitives</h4>
<p className="text-sm text-muted-foreground">
An open-source UI component library.
</p>
</div>
<Separator class="my-4" />
<div className="flex h-5 items-center space-x-4 text-sm">
<div>Blog</div>
<Separator orientation="vertical" />
<div>Docs</div>
<Separator orientation="vertical" />
<div>Source</div>
</div>
</div>
</InternalCard>
<InternalCard title="Sheet"></InternalCard>
<InternalCard title="Skeleton"></InternalCard>
<InternalCard title="Slider"></InternalCard>
<InternalCard title="Switch">
<div class="flex items-center space-x-2">
<Switch id="airplane-mode" v-model="enabled" />
<Label htmlFor="airplane-mode">Airplane Mode</Label>
</div>
</InternalCard>
<InternalCard title="Table"></InternalCard>
<InternalCard title="Tabs"></InternalCard>
<InternalCard title="Textarea"><Textarea placeholder="Type your message here." /></InternalCard>
<InternalCard title="Toast"></InternalCard>
<InternalCard title="Toggle">
<Toggle aria-label="Toggle italic" v-model="toggleEnabled">
<Icon icon="lucide:bold" class="h-4 w-4" />
</Toggle>
</InternalCard>
<InternalCard title="Tooltip">
<Tooltip>
<TooltipTrigger>Hover</TooltipTrigger>
</Tooltip>
</InternalCard>
</div>
</div>
<div class="p-8">
<AccordionDemo class="max-w-[20rem]" />
</div>
</template>
<style>
body {
@apply bg-neutral-100;
}
</style>

View File

@ -1,14 +1,16 @@
<template>
<div class="flex items-center justify-center bg-white border border-neutral-200 shadow-sm rounded-xl h-[280px] overflow-hidden relative px-6">
<p class="text-xl font-semibold absolute left-4 top-3 rounded-lg bg-neutral-100 px-2 py-1 text-neutral-600">{{ props.title }}</p>
<div class="overflow-y-scroll flex-grow h-full max-h-full flex items-center justify-center pb-4 pt-12">
<slot />
</div>
</div>
</template>
<script setup>
const props = defineProps({
title: String
title: String,
})
</script>
</script>
<template>
<div class="flex items-center justify-center bg-white border border-neutral-200 shadow-sm rounded-xl h-[280px] overflow-hidden relative px-6">
<p class="text-xl font-semibold absolute left-4 top-3 rounded-lg bg-neutral-100 px-2 py-1 text-neutral-600">
{{ props.title }}
</p>
<div class="overflow-y-scroll flex-grow h-full max-h-full flex items-center justify-center pb-4 pt-12">
<slot />
</div>
</div>
</template>

View File

@ -1 +1 @@
export { default as Card } from "./Card.vue";
export { default as Card } from './Card.vue'

View File

@ -0,0 +1,44 @@
import { defineComponent, h } from 'vue'
import {
AccordionContent as AccordionContentPrimitive,
type AccordionContentProps, AccordionHeader as AccordionHeaderPrimitive,
type AccordionHeaderProps, AccordionItem as AccordionItemPrimitive,
type AccordionItemProps, AccordionRoot as AccordionRootPrimitive,
AccordionTrigger as AccordionTriggerPrimitive, type AccordionTriggerProps,
} from 'radix-vue'
import { cn } from '@/utils'
export const Accordion = AccordionRootPrimitive
export const AccordionContent = defineComponent<AccordionContentProps>(
(props, { attrs, slots }) => {
return () => h(AccordionContentPrimitive,
{ class: cn('overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down', attrs.class), ...props },
() => h('div', { class: 'pb-4 pt-0' }, slots),
)
},
)
export const AccordionTrigger = defineComponent<AccordionTriggerProps>(
(props, { attrs, slots }) => {
return () => h(AccordionHeaderPrimitive, { class: 'flex' },
() => h(AccordionTriggerPrimitive,
{ class: cn('flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180', attrs.class) },
slots,
),
)
},
)
export const AccordionItem = defineComponent<AccordionItemProps>(
(props, { attrs, slots }) => {
return () => h(AccordionItemPrimitive,
{ class: cn('border-b', attrs.class), ...props }, slots)
},
)
export const AccordionHeader = defineComponent<AccordionHeaderProps>(
(props, { attrs, slots }) => {
return () => h(AccordionHeaderPrimitive, { class: cn('flex', attrs.class), ...props }, slots)
},
)

View File

@ -1,8 +0,0 @@
<template>
<div>
<slot />
</div>
</template>
<script setup>
</script>

View File

@ -1,11 +0,0 @@
<template>
<DisclosurePanel class="overflow-hidden text-sm">
<div class="pb-4 pt-0"><slot /></div>
</DisclosurePanel>
</template>
<script setup>
import {
DisclosurePanel,
} from '@headlessui/vue'
</script>

View File

@ -1,11 +0,0 @@
<template>
<div class="border-b w-full">
<Disclosure>
<slot />
</Disclosure>
</div>
</template>
<script setup>
import { Disclosure } from "@headlessui/vue"
</script>

View File

@ -1,23 +0,0 @@
<template>
<div class="flex">
<DisclosureButton class="flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-headlessui-state=open]>svg]:rotate-180">
<slot />
<Icon icon="heroicons:chevron-down" class="text-[16px] transition-transform duration-200" />
</DisclosureButton>
</div>
</template>
<script setup lang="ts">
import { DisclosureButton } from "@headlessui/vue";
import { Icon } from "@iconify/vue";
/*
import MorphConfig from "@/morph.config.js";
import { computed } from "vue";
const triggerClass = computed(() => {
if (MorphConfig.ui.accordion.trigger) return MorphConfig.ui.accordion.trigger;
return "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-headlessui-state=open]>svg]:rotate-180";
});
*/
</script>

View File

@ -1,4 +0,0 @@
export { default as Accordion } from "./Accordion.vue";
export { default as AccordionContent } from "./AccordionContent.vue";
export { default as AccordionItem } from "./AccordionItem.vue";
export { default as AccordionTrigger } from "./AccordionTrigger.vue";

View File

@ -0,0 +1,34 @@
<script setup>
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './Accordion'
const defaultValue = 'item-1'
const accordionItems = [
{
value: 'item-1',
title: 'Is it accessible?',
content: 'Yes. It adheres to the WAI-ARIA design pattern.',
},
{
value: 'item-2',
title: 'Is it unstyled?',
content: 'Yes. It\'s unstyled by default, giving you freedom over the look and feel.',
},
{
value: 'item-3',
title: 'Can it be animated?',
content: 'Yes! You can use the transition prop to configure the animation.',
},
]
</script>
<template>
<Accordion type="single" collapsible :default-value="defaultValue">
<AccordionItem v-for="item in accordionItems" :key="item.value" :value="item.value">
<AccordionTrigger>{{ item.title }}</AccordionTrigger>
<AccordionContent>
{{ item.content }}
</AccordionContent>
</AccordionItem>
</Accordion>
</template>

View File

@ -1,11 +0,0 @@
<template>
<div class="relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11">
<slot />
</div>
</template>
<script setup>
const props = defineProps({
title: String
})
</script>

View File

@ -1,3 +0,0 @@
<template>
<div class="text-sm [&_p]:leading-relaxed"><slot/></div>
</template>

View File

@ -1,3 +0,0 @@
<template>
<h5 class="mb-1 font-medium leading-none tracking-tight"><slot/></h5>
</template>

View File

@ -1,3 +0,0 @@
export { default as Alert } from "./Alert.vue";
export { default as AlertTitle } from "./AlertTitle.vue";
export { default as AlertDescription } from "./AlertDescription.vue";

View File

@ -1,64 +0,0 @@
<template>
<TransitionRoot :appear="appear" :show="isOpen" as="template">
<Dialog class="relative z-50" @close="close">
<TransitionChild v-if="overlay" as="template" :appear="appear" enter='ease-out duration-300'
enterFrom='opacity-0' enterTo='opacity-100' leave='ease-in duration-200' leaveFrom='opacity-100'
leaveTo='opacity-0'>
<div class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in" />
</TransitionChild>
<div class="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
<TransitionChild as="template" :appear="appear" enter='ease-out duration-300'
enterFrom='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
enterTo='opacity-100 translate-y-0 sm:scale-100' leave='ease-in duration-200'
leaveFrom='opacity-100 translate-y-0 sm:scale-100'
leaveTo='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'>
<DialogPanel
class="fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-white p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full">
<slot />
</DialogPanel>
</TransitionChild>
</div>
</Dialog>
</TransitionRoot>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { Dialog, DialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
appear: {
type: Boolean,
default: false
},
overlay: {
type: Boolean,
default: true
},
transition: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
function close(value: boolean) {
isOpen.value = value
emit('close')
}
</script>

View File

@ -1,3 +0,0 @@
<template>
<p class="text-sm text-black/60"><slot/></p>
</template>

View File

@ -1,3 +0,0 @@
<template>
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2"><slot/></div>
</template>

View File

@ -1,3 +0,0 @@
<template>
<div class="flex flex-col space-y-2 text-center sm:text-left"><slot/></div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<h5 class="text-lg font-semibold">
<slot />
</h5>
</template>

View File

@ -1,12 +0,0 @@
<template>
<div class="flex items-center justify-center border-2 border-neutral-300 rounded-xl min-h-[300px] relative">
<p class="text-2xl font-semibold absolute left-5 top-3">{{ props.title }}</p>
<slot />
</div>
</template>
<script setup>
const props = defineProps({
title: String
})
</script>

View File

@ -1,5 +0,0 @@
export { default as AlertDialog } from "./AlertDialog.vue";
export { default as AlertDialogTitle } from "./AlertDialogTitle.vue";
export { default as AlertDialogHeader } from "./AlertDialogHeader.vue";
export { default as AlertDialogFooter } from "./AlertDialogFooter.vue";
export { default as AlertDialogDescription } from "./AlertDialogDescription.vue";

View File

@ -1,18 +0,0 @@
<template>
<div :class="`relative w-full`" :style="`padding-bottom: ${aspect}%`">
<div class="absolute inset-0">
<slot />
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
ratio: Number,
});
const aspect = computed(() => {
return 1/props.ratio*100;
});
</script>

View File

@ -1,3 +0,0 @@
<template>
<img style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;" />
</template>

View File

@ -1,2 +0,0 @@
export { default as AspectRatio } from "./AspectRatio.vue";
export { default as Image } from "./Image.vue";

View File

@ -1,4 +0,0 @@
<template>
<div class="relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full">
<slot />
</div></template>

View File

@ -1 +0,0 @@
<template><div class="flex h-full w-full items-center justify-center rounded-full bg-muted"><slot /></div></template>

View File

@ -1,10 +0,0 @@
<template>
<img :src="props.src" :alt="props.alt">
</template>
<script setup>
const props = defineProps({
src: String,
alt: String
})
</script>

View File

@ -1,3 +0,0 @@
export { default as Avatar } from "./Avatar.vue";
export { default as AvatarFallback } from "./AvatarFallback.vue";
export { default as AvatarImage } from "./AvatarImage.vue";

View File

@ -1,8 +0,0 @@
<template>
<div class="inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 bg-black hover:bg-black/80 border-transparent text-white">
<slot />
</div>
</template>
<script setup>
</script>

View File

@ -1 +0,0 @@
export { default as Badge } from "./Badge.vue";

View File

@ -1,14 +0,0 @@
<template>
<button
:class="`inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background h-10 py-2 px-4
${props.variant==='outline' ? 'border border-neutral-200 hover:bg-neutral-100' : 'bg-black text-white hover:bg-black/80' }`">
<slot />
</button>
</template>
<script setup>
const props = defineProps({
label: String,
variant: String,
});
</script>

View File

@ -1 +0,0 @@
export { default as Button } from "./Button.vue";

View File

@ -1,5 +0,0 @@
<template>
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
<slot />
</div>
</template>

View File

@ -1,7 +0,0 @@
<template>
<div class="p-6 pt-0"><slot/></div>
</template>
<script setup>
</script>

View File

@ -1,7 +0,0 @@
<template>
<p class="text-sm text-muted-foreground"><slot/></p>
</template>
<script setup>
</script>

View File

@ -1,7 +0,0 @@
<template>
<div class="flex items-center p-6 pt-0"><slot/></div>
</template>
<script setup>
</script>

View File

@ -1,7 +0,0 @@
<template>
<div class="flex flex-col space-y-1.5 p-6"><slot/></div>
</template>
<script setup>
</script>

View File

@ -1,7 +0,0 @@
<template>
<h3 class="text-lg font-semibold leading-none tracking-tight"><slot/></h3>
</template>
<script setup>
</script>

View File

@ -1,6 +0,0 @@
export { default as Card } from "./Card.vue";
export { default as CardContent } from "./CardContent.vue";
export { default as CardDescription } from "./CardDescription.vue";
export { default as CardFooter } from "./CardFooter.vue";
export { default as CardHeader } from "./CardHeader.vue";
export { default as CardTitle } from "./CardTitle.vue";

View File

@ -1,28 +0,0 @@
<template>
<Switch v-model="isOpen" :class="isOpen ? 'text-primary-foreground bg-primary' : ''"
class="peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground">
<span class="flex items-center justify-center text-current" v-if="isOpen"><Icon icon="lucide:check"/></span>
</Switch>
</template>
<script setup>
import { ref, computed } from 'vue'
import { Switch } from '@headlessui/vue'
import { Icon } from "@iconify/vue";
const props = defineProps({
modelValue: Boolean
})
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>

View File

@ -1 +0,0 @@
export { default as Checkbox } from "./Checkbox.vue";

View File

@ -1,9 +0,0 @@
<template>
<Disclosure as="div" v-slot="{ open }">
<slot :open="open"></slot>
</Disclosure>
</template>
<script setup>
import { Disclosure } from "@headlessui/vue"
</script>

View File

@ -1,11 +0,0 @@
<template>
<DisclosurePanel class="overflow-hidden text-sm">
<slot />
</DisclosurePanel>
</template>
<script setup>
import {
DisclosurePanel,
} from '@headlessui/vue'
</script>

View File

@ -1,9 +0,0 @@
<template>
<DisclosureButton as="template">
<slot />
</DisclosureButton>
</template>
<script setup lang="ts">
import { DisclosureButton } from "@headlessui/vue";
</script>

View File

@ -1,3 +0,0 @@
export { default as Collapsible } from "./Collapsible.vue";
export { default as CollapsibleContent } from "./CollapsibleContent.vue";
export { default as CollapsibleTrigger } from "./CollapsibleTrigger.vue";

View File

@ -1,44 +0,0 @@
<template>
<Listbox v-model="isOpen" v-slot="{ open }">
<Float
portal
placement="bottom"
flip
strategy="fixed"
adaptive-width
enter="transition duration-200 ease-out"
enter-from="scale-95 opacity-0"
enter-to="scale-100 opacity-100"
leave="transition duration-150 ease-in"
leave-from="scale-100 opacity-100"
leave-to="scale-95 opacity-0"
tailwindcss-origin-class
:offset="4"
as="div"
class="relative mt-1"
>
<slot :open="open"></slot>
</Float>
</Listbox>
</template>
<script setup>
import { ref, computed } from "vue";
import { Listbox } from "@headlessui/vue";
import { Float } from "@headlessui-float/vue";
const props = defineProps({
modelValue: Boolean,
});
const emit = defineEmits(["update:modelValue", "close"]);
const isOpen = computed({
get() {
return props.modelValue;
},
set(value) {
emit("update:modelValue", value);
},
});
</script>

View File

@ -1,16 +0,0 @@
<template>
<ListboxButton v-slot="{ open }"
class="flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50">
<span>
<slot />
</span>
<Icon icon="lucide:chevron-down" :class="`h-4 w-4 opacity-50 transition-all ${open ? 'rotate-180' : ''}`" />
</ListboxButton>
</template>
<script setup>
import {
ListboxButton,
} from '@headlessui/vue'
import { Icon } from "@iconify/vue";
</script>

View File

@ -1,24 +0,0 @@
<template>
<ListboxOption v-slot="{ active, selected, disabled }" as="template">
<li :class="[
active ? 'bg-accent text-accent-foreground' : 'text-gray-900',
disabled ? 'opacity-50 pointer-events-none' : 'text-gray-900',
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none',
]">
<span v-if="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<Icon icon="lucide:check" class="h-[14px] w-[14px]" aria-hidden="true" />
</span>
<span>
<slot />
</span>
</li>
</ListboxOption>
</template>
<script setup>
import {
ListboxOption,
} from '@headlessui/vue'
import { Icon } from "@iconify/vue";
</script>

View File

@ -1,12 +0,0 @@
<template>
<ListboxOptions
class="w-full min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md p-1 focus:outline-none">
<slot />
</ListboxOptions>
</template>
<script setup>
import {
ListboxOptions,
} from '@headlessui/vue'
</script>

View File

@ -1,4 +0,0 @@
export { default as Combobox } from "./Combobox.vue";
export { default as ComboboxInput } from "./ComboboxInput.vue";
export { default as ComboboxOptions } from "./ComboboxOptions.vue";
export { default as ComboboxOption } from "./ComboboxOption.vue";

View File

@ -1,68 +0,0 @@
<script setup>
import { Icon } from "@iconify/vue";
import { Button } from "@/src/components/ui/Button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/src/components/ui/Card"
const notifications = [
{
title: "Your call has been confirmed.",
description: "1 hour ago",
},
{
title: "You have a new message!",
description: "1 hour ago",
},
{
title: "Your subscription is expiring soon!",
description: "2 hours ago",
},
]
</script>
<template>
<Card class="w-[380px]">
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>You have 3 unread messages.</CardDescription>
</CardHeader>
<CardContent class="grid gap-4">
<div class=" flex items-center space-x-4 rounded-md border p-4">
<Icon icon="lucide:bell-ring" />
<div class="flex-1 space-y-1">
<p class="text-sm font-medium leading-none">
Push Notifications
</p>
<p class="text-sm text-muted-foreground">
Send notifications to device.
</p>
</div>
</div>
<div>
<div class="mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0"
v-for="notification, index in notifications" :key="index">
<span class="flex h-2 w-2 translate-y-1 rounded-full bg-sky-500" />
<div class="space-y-1">
<p class="text-sm font-medium leading-none">
{{ notification.title }}
</p>
<p class="text-sm text-muted-foreground">
{{ notification.description }}
</p>
</div>
</div>
</div>
</CardContent>
<CardFooter>
<Button class="w-full">
<Icon icon="lucide:check" class="mr-2 h-4 w-4" /> Mark all as read
</Button>
</CardFooter>
</Card>
</template>

View File

@ -1 +0,0 @@
export { default as Card } from "./Card.vue";

View File

@ -1,64 +0,0 @@
<template>
<TransitionRoot :appear="appear" :show="isOpen" as="template">
<Dialog class="relative z-50" @close="close">
<TransitionChild v-if="overlay" as="template" :appear="appear" enter='ease-out duration-300'
enterFrom='opacity-0' enterTo='opacity-100' leave='ease-in duration-200' leaveFrom='opacity-100'
leaveTo='opacity-0'>
<div class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in" />
</TransitionChild>
<div class="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
<TransitionChild as="template" :appear="appear" enter='ease-out duration-300'
enterFrom='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
enterTo='opacity-100 translate-y-0 sm:scale-100' leave='ease-in duration-200'
leaveFrom='opacity-100 translate-y-0 sm:scale-100'
leaveTo='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'>
<DialogPanel
class="fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-white p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full">
<slot />
</DialogPanel>
</TransitionChild>
</div>
</Dialog>
</TransitionRoot>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { Dialog, DialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
appear: {
type: Boolean,
default: false
},
overlay: {
type: Boolean,
default: true
},
transition: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
function close(value: boolean) {
isOpen.value = value
emit('close')
}
</script>

View File

@ -1,3 +0,0 @@
<template>
<p class="text-sm text-black/60"><slot/></p>
</template>

View File

@ -1,3 +0,0 @@
<template>
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2"><slot/></div>
</template>

View File

@ -1,3 +0,0 @@
<template>
<div class="flex flex-col space-y-2 text-center sm:text-left"><slot/></div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<h5 class="text-lg font-semibold">
<slot />
</h5>
</template>

View File

@ -1,12 +0,0 @@
<template>
<div class="flex items-center justify-center border-2 border-neutral-300 rounded-xl min-h-[300px] relative">
<p class="text-2xl font-semibold absolute left-5 top-3">{{ props.title }}</p>
<slot />
</div>
</template>
<script setup>
const props = defineProps({
title: String
})
</script>

View File

@ -1,5 +0,0 @@
export { default as Dialog } from "./Dialog.vue";
export { default as DialogTitle } from "./DialogTitle.vue";
export { default as DialogHeader } from "./DialogHeader.vue";
export { default as DialogFooter } from "./DialogFooter.vue";
export { default as DialogDescription } from "./DialogDescription.vue";

View File

@ -1,11 +0,0 @@
<template>
<input
:class="props.class"
class="flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" />
</template>
<script setup>
const props = defineProps({
class: String,
})
</script>

View File

@ -1 +0,0 @@
export { default as Input } from "./Input.vue";

View File

@ -1,11 +0,0 @@
<template>
<label :for="props.htmlFor" :class="`text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 ${props.class}`"><slot/></label>
</template>
<script setup>
const props = defineProps({
class: String,
id: String,
htmlFor: String
})
</script>

View File

@ -1 +0,0 @@
export { default as Label } from "./Label.vue";

View File

@ -1,15 +0,0 @@
<template>
<Popover v-slot="{ open }">
<Float portal composable placement="bottom" flip strategy="fixed" enter="transition duration-200 ease-out"
enter-from="scale-95 opacity-0" enter-to="scale-100 opacity-100" leave="transition duration-150 ease-in"
leave-from="scale-100 opacity-100" leave-to="scale-95 opacity-0" tailwindcss-origin-class :offset="6" as="div"
class="relative">
<slot :open="open"/>
</Float>
</Popover>
</template>
<script setup>
import { Popover } from "@headlessui/vue";
import { Float } from "@headlessui-float/vue";
</script>

View File

@ -1,13 +0,0 @@
<template>
<FloatContent>
<PopoverPanel
class="z-50 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-80">
<slot />
</PopoverPanel>
</FloatContent>
</template>
<script setup>
import { PopoverPanel } from "@headlessui/vue";
import { FloatContent } from "@headlessui-float/vue";
</script>

View File

@ -1,10 +0,0 @@
<template>
<FloatReference>
<PopoverButton><slot /></PopoverButton>
</FloatReference>
</template>
<script setup>
import { PopoverButton } from "@headlessui/vue";
import { FloatReference } from "@headlessui-float/vue";
</script>

View File

@ -1,3 +0,0 @@
export { default as Popover } from "./Popover.vue";
export { default as PopoverContent } from "./PopoverContent.vue";
export { default as PopoverTrigger } from "./PopoverTrigger.vue";

View File

@ -1,26 +0,0 @@
<template>
<RadioGroup v-model="isOpen" class="flex flex-col gap-2">
<slot />
</RadioGroup>
</template>
<script setup>
import { ref, computed } from 'vue'
import { RadioGroup } from '@headlessui/vue';
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>

View File

@ -1,13 +0,0 @@
<template>
<RadioGroupOption v-slot="{ active, checked }" class="list-none focus:outline-none flex items-center space-x-2 [&>[role='indicator']]:focus:outline-none [&>[role='indicator']]:focus-visible:ring-2 [&>[role='indicator']]:focus-visible:ring-ring [&>[role='indicator']]:focus-visible:ring-offset-2">
<button role="indicator"
class="aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 flex items-center justify-center">
<span v-show="checked" class="bg-black h-2.5 w-2.5 rounded-full"></span>
</button>
<slot />
</RadioGroupOption>
</template>
<script setup>
import { RadioGroupOption } from '@headlessui/vue';
</script>

View File

@ -1,2 +0,0 @@
export { default as RadioGroup } from "./RadioGroup.vue";
export { default as RadioGroupItem } from "./RadioGroupItem.vue";

View File

@ -1,44 +0,0 @@
<template>
<Listbox v-model="isOpen" v-slot="{ open }">
<Float
portal
placement="bottom"
flip
strategy="fixed"
adaptive-width
enter="transition duration-200 ease-out"
enter-from="scale-95 opacity-0"
enter-to="scale-100 opacity-100"
leave="transition duration-150 ease-in"
leave-from="scale-100 opacity-100"
leave-to="scale-95 opacity-0"
tailwindcss-origin-class
:offset="4"
as="div"
class="relative mt-1"
>
<slot :open="open"></slot>
</Float>
</Listbox>
</template>
<script setup>
import { ref, computed } from "vue";
import { Listbox } from "@headlessui/vue";
import { Float } from "@headlessui-float/vue";
const props = defineProps({
modelValue: Number,
});
const emit = defineEmits(["update:modelValue", "close"]);
const isOpen = computed({
get() {
return props.modelValue;
},
set(value) {
emit("update:modelValue", value);
},
});
</script>

View File

@ -1,12 +0,0 @@
<template>
<ListboxOptions
class="w-full min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md p-1 focus:outline-none">
<slot />
</ListboxOptions>
</template>
<script setup>
import {
ListboxOptions,
} from '@headlessui/vue'
</script>

View File

@ -1,24 +0,0 @@
<template>
<ListboxOption v-slot="{ active, selected, disabled }" as="template">
<li :class="[
active ? 'bg-accent text-accent-foreground' : 'text-gray-900',
disabled ? 'opacity-50 pointer-events-none' : 'text-gray-900',
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none',
]">
<span v-if="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<Icon icon="lucide:check" class="h-[14px] w-[14px]" aria-hidden="true" />
</span>
<span>
<slot />
</span>
</li>
</ListboxOption>
</template>
<script setup>
import {
ListboxOption,
} from '@headlessui/vue'
import { Icon } from "@iconify/vue";
</script>

View File

@ -1,15 +0,0 @@
<template>
<div class="py-1.5 pl-8 pr-2 text-sm font-semibold">
<ListboxLabel>
<slot />
</ListboxLabel>
</div>
</template>
<script setup>
import {
ListboxLabel,
} from '@headlessui/vue'
</script>

View File

@ -1,16 +0,0 @@
<template>
<ListboxButton v-slot="{ open }"
class="flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50">
<span>
<slot />
</span>
<Icon icon="lucide:chevron-down" :class="`h-4 w-4 opacity-50 transition-all ${open ? 'rotate-180' : ''}`" />
</ListboxButton>
</template>
<script setup>
import {
ListboxButton,
} from '@headlessui/vue'
import { Icon } from "@iconify/vue";
</script>

View File

@ -1,5 +0,0 @@
export { default as Select } from "./Select.vue";
export { default as SelectContent } from "./SelectContent.vue";
export { default as SelectTrigger } from "./SelectTrigger.vue";
export { default as SelectItem } from "./SelectItem.vue";
export { default as SelectLabel } from "./SelectLabel.vue";

View File

@ -1,13 +0,0 @@
<template>
<div :class="`shrink-0 bg-border ${props.orientation == 'vertical' ? 'w-[1px] h-full' : 'w-full h-[1px]'}`"></div>
</template>
<script setup>
const props = defineProps({
orientation: {
type: String,
required: false,
default: "horizontal"
}
})
</script>

View File

@ -1 +0,0 @@
export {default as Separator} from './Separator.vue'

View File

@ -1,28 +0,0 @@
<template>
<Switch v-model="isOpen" :class="isOpen ? 'bg-black' : 'bg-neutral-300'"
class="peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50">
<span :class="isOpen ? 'translate-x-5' : 'translate-x-0'"
class="pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform" />
</Switch>
</template>
<script setup>
import { ref, computed } from 'vue'
import { Switch } from '@headlessui/vue'
const props = defineProps({
modelValue: Boolean
})
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>

View File

@ -1 +0,0 @@
export { default as Switch } from "./Switch.vue";

View File

@ -1,9 +0,0 @@
<template>
<TabGroup>
<slot />
</TabGroup>
</template>
<script setup>
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
</script>

View File

@ -1,7 +0,0 @@
<template>
<div><slot/></div>
</template>
<script setup>
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
</script>

View File

@ -1,7 +0,0 @@
<template>
<div><slot/></div>
</template>
<script setup>
</script>

View File

@ -1,7 +0,0 @@
<template>
<div><slot/></div>
</template>
<script setup>
</script>

View File

@ -1,4 +0,0 @@
export { default as Tabs } from "./Tabs.vue";
export { default as TabsContent } from "./TabsContent.vue";
export { default as TabsList } from "./TabsList.vue";
export { default as TabsTrigger } from "./TabsTrigger.vue";

View File

@ -1,7 +0,0 @@
<template>
<textarea
class="flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" />
</template>
<script setup>
</script>

View File

@ -1 +0,0 @@
export { default as Textarea } from "./Textarea.vue";

View File

@ -1,29 +0,0 @@
<template>
<Switch v-model="isChecked" :class="isChecked ? 'text-accent-foreground bg-neutral-300' : 'bg-neutral-100'"
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background hover:bg-neutral-200/80 hover:text-black-40 h-10 px-3">
<span>
<slot />
</span>
</Switch>
</template>
<script setup>
import { ref, computed } from 'vue'
import { Switch } from '@headlessui/vue'
const props = defineProps({
modelValue: Boolean
})
const emit = defineEmits(['update:modelValue', 'close'])
const isChecked = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>

View File

@ -1 +0,0 @@
export { default as Toggle } from "./Toggle.vue";

View File

@ -1,21 +0,0 @@
<template>
<Float :show="show">
<slot :enter="handleMouseEnter" :leave="handleMouseLeave"/>
<div static>
Help me fund my Open Source work!
</div>
</Float>
</template>
<script setup>
import { ref } from 'vue'
import { Float } from '@headlessui-float/vue'
const show = ref(false)
const handleMouseEnter = () => {
show.value = true
}
const handleMouseLeave = () => {
show.value = false
}
</script>

View File

@ -1,12 +0,0 @@
<template>
<FloatContent static>
<div
class="z-50 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-80">
<slot />
</div>
</FloatContent>
</template>
<script setup>
import { FloatContent } from "@headlessui-float/vue";
</script>

View File

@ -1,15 +0,0 @@
<template>
<button @mouseenter="emit('enter')" @mouseleave="emit('leave')">Sponsor me</button>
</template>
<script setup>
import { ref } from 'vue'
import { Float } from '@headlessui-float/vue'
const show = ref(false)
const toggle = () => {
show.value = !show.value
}
const emit = defineEmits(['enter', 'leave'])
</script>

View File

@ -1,3 +0,0 @@
export { default as Tooltip } from "./Tooltip.vue";
export { default as TooltipContent } from "./TooltipContent.vue";
export { default as TooltipTrigger } from "./TooltipTrigger.vue";

View File

@ -0,0 +1,82 @@
import { type VariantProps, cva } from 'class-variance-authority'
export const AccordionClass = cva('', {
variants: {
variant: {
default: '',
},
component: {
root: '',
item: '',
header: '',
trigger: '',
content: '',
contentWrapper: '',
},
},
compoundVariants: [
{ variant: 'default', component: 'root', class: 'w-[300px]' },
{ variant: 'default', component: 'item', class: 'border-b w-full' },
{
variant: 'default',
component: 'trigger',
class:
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
},
{
variant: 'default',
component: 'content',
class:
'overflow-hidden text-sm data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up',
},
{
variant: 'default',
component: 'contentWrapper',
class: 'overflow-hidden text-sm',
},
{ variant: 'default', component: 'header', class: 'flex' },
],
defaultVariants: {
variant: 'default',
},
})
export type AccordionClassProps = VariantProps<typeof AccordionClass>
export const SwitchClass = cva('', {
variants: {
variant: {
default: '',
},
size: {
1: '',
2: '',
},
component: {
root: 'unset box-border select-none [&::before]:box-border [&::after]:box-border inline-flex items-center justify-center leading-none m-0 outline-none bg-[#e6e8eb] rounded-full relative focus-within:shadow-[0_0_0_2px_#c1c8cd] data-[state=checked]:bg-[#0091ff] data-[state=checked]:focus-within:shadow-[0_0_0_2px_#5eb0ef]',
thumb:
'absolute left-0 bg-white rounded-full shadow-switch transition-transform duration-100 ease-[cubic-bezier(0.22,_1,_0.36,_1)]',
},
},
compoundVariants: [
{ size: 1, component: 'root', class: 'w-[25px] h-[15px]' },
{ size: 2, component: 'root', class: 'w-[45px] h-[25px]' },
{
size: 1,
component: 'thumb',
class:
'w-[13px] h-[13px] translate-x-[1px] data-[state=checked]:translate-x-[11px]',
},
{
size: 2,
component: 'thumb',
class:
'w-[21px] h-[21px] translate-x-0.5 data-[state=checked]:translate-x-[22px]',
},
],
defaultVariants: {
variant: 'default',
size: 1,
},
})
export type SwitchClassProps = VariantProps<typeof SwitchClass>

View File

@ -1,61 +0,0 @@
<script setup lang="ts">
import {
AccordionContent,
AccordionHeader,
AccordionItem,
AccordionRoot,
AccordionTrigger,
} from "radix-vue";
import { Icon } from "@iconify/vue";
import { AccordionClass, type AccordionClassProps } from "./cva"
interface AccordionProps {
variant: AccordionClassProps["variant"];
data: {
value: string,
title: string,
content: string,
}[],
defaultValue: string,
type: string,
collapsible: boolean,
}
withDefaults(defineProps<AccordionProps>(), {
variant: 'default',
type: 'single',
collapsible: true,
});
</script>
<template>
<AccordionRoot
:class="AccordionClass({ variant, component: 'root' })"
:default-value="defaultValue"
:type="type"
:collapsible="collapsible"
>
<template v-for="item in data" :key="item.value">
<AccordionItem
:class="AccordionClass({ variant, component: 'item' })"
:value="item.value"
>
<AccordionHeader :class="AccordionClass({ variant, component: 'header' })">
<AccordionTrigger :class="AccordionClass({ variant, component: 'trigger' })"
><span>{{ item.title }}</span>
<Icon
icon="radix-icons:chevron-down"
class="text-green10 ease-[cubic-bezier(0.87,_0,_0.13,_1)] transition-transform duration-300 group-data-[state=open]:rotate-180"
aria-hidden
/>
</AccordionTrigger>
</AccordionHeader>
<AccordionContent :class="AccordionClass({ variant, component: 'content' })">
<div :class="AccordionClass({ variant, component: 'contentWrapper' })">
{{ item.content }}
</div>
</AccordionContent>
</AccordionItem>
</template>
</AccordionRoot>
</template>

Some files were not shown because too many files have changed in this diff Show More