feat: vitepress docs
This commit is contained in:
parent
90e2e2f7c0
commit
4017bc628c
|
|
@ -1,5 +1,4 @@
|
||||||
const process = require('node:process')
|
// const process = require('node:process')
|
||||||
|
|
||||||
// process.env.ESLINT_TSCONFIG = 'tsconfig.json'
|
// process.env.ESLINT_TSCONFIG = 'tsconfig.json'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -8,6 +7,7 @@ module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
'vue/one-component-per-file': 'off',
|
'vue/one-component-per-file': 'off',
|
||||||
'vue/no-reserved-component-names': 'off',
|
'vue/no-reserved-component-names': 'off',
|
||||||
|
'vue/no-useless-v-bind': 'off',
|
||||||
'symbol-description': 'off',
|
'symbol-description': 'off',
|
||||||
'no-console': 'warn',
|
'no-console': 'warn',
|
||||||
'no-tabs': 'off',
|
'no-tabs': 'off',
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,22 @@
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { defineConfig } from 'vitepress'
|
import { defineConfig } from 'vitepress'
|
||||||
|
import Icons from 'unplugin-icons/vite'
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
title: 'shadcn-vue',
|
title: 'shadcn-vue',
|
||||||
description: 'A VitePress Site',
|
description: 'A VitePress Site',
|
||||||
srcDir: path.resolve(__dirname, '../src/content'),
|
srcDir: path.resolve(__dirname, '../src/content'),
|
||||||
|
markdown: {
|
||||||
|
theme: 'css-variables',
|
||||||
|
},
|
||||||
vite: {
|
vite: {
|
||||||
|
plugins: [
|
||||||
|
Icons({ compiler: 'vue3', autoInstall: true }) as any,
|
||||||
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, '../src/lib'),
|
'@': path.resolve(__dirname, '../src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
54
apps/www/.vitepress/theme/components/ComponentPreview.vue
Normal file
54
apps/www/.vitepress/theme/components/ComponentPreview.vue
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import LucideSpinner from '~icons/lucide/loader-2'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
name: string
|
||||||
|
align?: 'center' | 'start' | 'end'
|
||||||
|
}>(), { align: 'center' })
|
||||||
|
|
||||||
|
const Component = defineAsyncComponent({
|
||||||
|
loadingComponent: LucideSpinner,
|
||||||
|
loader: () => import(`../../../src/lib/registry/default/examples/${props.name}.vue`),
|
||||||
|
timeout: 5000,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="group relative my-4 flex flex-col space-y-2">
|
||||||
|
<Tabs default-value="preview" class="relative mr-auto w-full">
|
||||||
|
<div class="flex items-center justify-between pb-3">
|
||||||
|
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||||
|
<TabsTrigger
|
||||||
|
value="preview"
|
||||||
|
class="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||||
|
>
|
||||||
|
Preview
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="code"
|
||||||
|
class="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
||||||
|
>
|
||||||
|
Code
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
<TabsContent value="preview" class="relative rounded-md border">
|
||||||
|
<div
|
||||||
|
:class="cn('preview flex min-h-[350px] w-full justify-center p-10', {
|
||||||
|
'items-center': align === 'center',
|
||||||
|
'items-start': align === 'start',
|
||||||
|
'items-end': align === 'end',
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<Component :is="Component" />
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="code">
|
||||||
|
<slot />
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
16
apps/www/.vitepress/theme/components/ManualInstall.vue
Normal file
16
apps/www/.vitepress/theme/components/ManualInstall.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Accordion type="single" :collapsible="true">
|
||||||
|
<AccordionItem value="manual-installation" class="border-none">
|
||||||
|
<AccordionTrigger>
|
||||||
|
Manual Installation
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<slot />
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
</template>
|
||||||
59
apps/www/.vitepress/theme/components/TableOfContent.vue
Normal file
59
apps/www/.vitepress/theme/components/TableOfContent.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onContentUpdated } from 'vitepress'
|
||||||
|
import { shallowRef } from 'vue'
|
||||||
|
import type { TableOfContents, TableOfContentsItem } from '../types/docs'
|
||||||
|
import TableOfContentTree from './TableOfContentTree.vue'
|
||||||
|
|
||||||
|
const headers = shallowRef<TableOfContents>()
|
||||||
|
|
||||||
|
function getHeadingsWithHierarchy(divId: string) {
|
||||||
|
const div = document.querySelector(divId)
|
||||||
|
if (!div)
|
||||||
|
return { items: [] }
|
||||||
|
|
||||||
|
const headings: HTMLHeadingElement[] = Array.from(
|
||||||
|
div.querySelectorAll('h2, h3'),
|
||||||
|
)
|
||||||
|
const hierarchy: TableOfContents = { items: [] }
|
||||||
|
let currentLevel: TableOfContentsItem | undefined
|
||||||
|
|
||||||
|
headings.forEach((heading: HTMLHeadingElement) => {
|
||||||
|
const level = Number.parseInt(heading.tagName.charAt(1))
|
||||||
|
if (!heading.id) {
|
||||||
|
const newId = heading.innerText
|
||||||
|
.replaceAll(/[^a-zA-Z0-9 ]/g, '')
|
||||||
|
.replaceAll(' ', '-')
|
||||||
|
.toLowerCase()
|
||||||
|
heading.id = `${newId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const item: TableOfContentsItem = {
|
||||||
|
title: heading.textContent || '',
|
||||||
|
url: `#${heading.id}`,
|
||||||
|
items: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level === 2) {
|
||||||
|
hierarchy.items.push(item)
|
||||||
|
currentLevel = item
|
||||||
|
}
|
||||||
|
else if (level === 3 && currentLevel?.items) {
|
||||||
|
currentLevel.items.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return hierarchy
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentUpdated(() => {
|
||||||
|
headers.value = getHeadingsWithHierarchy('.vp-doc')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
On This Page
|
||||||
|
</p>
|
||||||
|
<TableOfContentTree :tree="headers" :level="1" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
51
apps/www/.vitepress/theme/components/TableOfContentTree.vue
Normal file
51
apps/www/.vitepress/theme/components/TableOfContentTree.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vitepress'
|
||||||
|
import type { TableOfContentsItem } from '../types/docs'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
withDefaults(defineProps<{
|
||||||
|
level: number
|
||||||
|
tree: TableOfContentsItem
|
||||||
|
}>(), {
|
||||||
|
level: 1,
|
||||||
|
tree: () => ({
|
||||||
|
items: [],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const hash = ref('')
|
||||||
|
function setHash() {
|
||||||
|
hash.value = location.hash
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('hashchange', setHash)
|
||||||
|
setHash()
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('hashchange', setHash)
|
||||||
|
})
|
||||||
|
watch(() => route.path, () => {
|
||||||
|
setHash()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul :class="cn('m-0 list-none', { 'pl-4': level !== 1 })">
|
||||||
|
<template v-if="tree.items?.length">
|
||||||
|
<li v-for="item in tree.items" :key="item.title" class="mt-0 pt-2">
|
||||||
|
<a
|
||||||
|
:href="item.url"
|
||||||
|
:class="
|
||||||
|
cn('inline-block no-underline transition-colors hover:text-foreground',
|
||||||
|
item.url === hash
|
||||||
|
? 'font-medium text-foreground'
|
||||||
|
: 'text-muted-foreground')"
|
||||||
|
>{{ item.title }} </a>
|
||||||
|
|
||||||
|
<TableOfContentTree v-if="item.items?.length" :tree="item" :level="level + 1" />
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
2
apps/www/.vitepress/theme/components/index.ts
Normal file
2
apps/www/.vitepress/theme/components/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as ComponentPreview } from './ComponentPreview.vue'
|
||||||
|
export { default as ManualInstall } from './ManualInstall.vue'
|
||||||
365
apps/www/.vitepress/theme/config/docs.ts
Normal file
365
apps/www/.vitepress/theme/config/docs.ts
Normal file
|
|
@ -0,0 +1,365 @@
|
||||||
|
export interface NavItem {
|
||||||
|
title: string
|
||||||
|
href?: string
|
||||||
|
disabled?: boolean
|
||||||
|
external?: boolean
|
||||||
|
icon?: string
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SidebarNavItem = NavItem & {
|
||||||
|
items: SidebarNavItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NavItemWithChildren = NavItem & {
|
||||||
|
items: NavItemWithChildren[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DocsConfig {
|
||||||
|
mainNav: NavItem[]
|
||||||
|
sidebarNav: SidebarNavItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const docsConfig: DocsConfig = {
|
||||||
|
mainNav: [
|
||||||
|
{
|
||||||
|
title: 'Documentation',
|
||||||
|
href: '/docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Components',
|
||||||
|
href: '/docs/components/accordion',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Themes',
|
||||||
|
href: '/themes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Examples',
|
||||||
|
href: '/examples/dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GitHub',
|
||||||
|
href: 'https://github.com/huntabyte/shadcn-svelte',
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sidebarNav: [
|
||||||
|
{
|
||||||
|
title: 'Getting Started',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: 'Introduction',
|
||||||
|
href: '/docs',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Installation',
|
||||||
|
href: '/docs/installation',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'components.json',
|
||||||
|
href: '/docs/components-json',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Theming',
|
||||||
|
href: '/docs/theming',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'CLI',
|
||||||
|
href: '/docs/cli',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Typography',
|
||||||
|
href: '/docs/typography',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Figma',
|
||||||
|
href: '/docs/figma',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Changelog',
|
||||||
|
href: '/docs/changelog',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'About',
|
||||||
|
href: '/docs/about',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Components',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: 'Accordion',
|
||||||
|
href: '/docs/components/accordion',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Alert',
|
||||||
|
href: '/docs/components/alert',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Alert Dialog',
|
||||||
|
href: '/docs/components/alert-dialog',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Aspect Ratio',
|
||||||
|
href: '/docs/components/aspect-ratio',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Avatar',
|
||||||
|
href: '/docs/components/avatar',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Badge',
|
||||||
|
href: '/docs/components/badge',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Button',
|
||||||
|
href: '/docs/components/button',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Calendar",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Card',
|
||||||
|
href: '/docs/components/card',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Checkbox',
|
||||||
|
href: '/docs/components/checkbox',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Collapsible',
|
||||||
|
href: '/docs/components/collapsible',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Combobox',
|
||||||
|
disabled: true,
|
||||||
|
label: 'Soon',
|
||||||
|
href: '#',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Command",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Context Menu',
|
||||||
|
href: '/docs/components/context-menu',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Data Table',
|
||||||
|
href: '/docs/components/data-table',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Date Picker",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Dialog',
|
||||||
|
href: '/docs/components/dialog',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Dropdown Menu',
|
||||||
|
href: '/docs/components/dropdown-menu',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Form',
|
||||||
|
href: '#',
|
||||||
|
label: 'Soon',
|
||||||
|
disabled: true,
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Hover Card',
|
||||||
|
href: '/docs/components/hover-card',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Input',
|
||||||
|
href: '/docs/components/input',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Label',
|
||||||
|
href: '/docs/components/label',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Menubar',
|
||||||
|
href: '/docs/components/menubar',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Navigation Menu",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Popover',
|
||||||
|
href: '/docs/components/popover',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Progress',
|
||||||
|
href: '/docs/components/progress',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Radio Group',
|
||||||
|
href: '/docs/components/radio-group',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Scroll Area",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Select',
|
||||||
|
href: '/docs/components/select',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Separator',
|
||||||
|
href: '/docs/components/separator',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Sheet',
|
||||||
|
href: '/docs/components/sheet',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Skeleton',
|
||||||
|
href: '/docs/components/skeleton',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Slider',
|
||||||
|
href: '/docs/components/slider',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Switch',
|
||||||
|
href: '/docs/components/switch',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Table',
|
||||||
|
href: '/docs/components/table',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Tabs',
|
||||||
|
href: '/docs/components/tabs',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Textarea',
|
||||||
|
href: '/docs/components/textarea',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: "Toast",
|
||||||
|
// href: "#",
|
||||||
|
// label: "Soon",
|
||||||
|
// disabled: true,
|
||||||
|
// items: []
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: 'Toggle',
|
||||||
|
href: '/docs/components/toggle',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Tooltip',
|
||||||
|
href: '/docs/components/tooltip',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Example {
|
||||||
|
name: string
|
||||||
|
href: string
|
||||||
|
label?: string
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
export const examples: Example[] = [
|
||||||
|
{
|
||||||
|
name: 'Dashboard',
|
||||||
|
href: '/examples/dashboard',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/src/lib/components/docs/dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cards',
|
||||||
|
href: '/examples/cards',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/src/routes/examples/cards',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: "Tasks",
|
||||||
|
// href: "/examples/tasks",
|
||||||
|
// label: "New",
|
||||||
|
// code: "https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/apps/www/app/examples/tasks"
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'Playground',
|
||||||
|
href: '/examples/playground',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/apps/www/app/examples/playground',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Music',
|
||||||
|
href: '/examples/music',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/apps/www/app/examples/music',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Authentication',
|
||||||
|
href: '/examples/authentication',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/src/routes/examples/authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Forms',
|
||||||
|
href: '/examples/forms',
|
||||||
|
code: 'https://github.com/huntabyte/shadcn-svelte/tree/main/apps/www/src/routes/examples/forms',
|
||||||
|
},
|
||||||
|
]
|
||||||
14
apps/www/.vitepress/theme/config/site.ts
Normal file
14
apps/www/.vitepress/theme/config/site.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export const siteConfig = {
|
||||||
|
name: 'shadcn-vue',
|
||||||
|
url: 'https://shadcn-vue.com',
|
||||||
|
ogImage: 'https://shadcn-vue.com/og.png',
|
||||||
|
description:
|
||||||
|
'Beautifully designed components built with Radix Vue and Tailwind CSS.',
|
||||||
|
links: {
|
||||||
|
twitter: 'https://twitter.com/huntabyte',
|
||||||
|
github: 'https://github.com/huntabyte/shadcn-vue',
|
||||||
|
shadTwitter: 'https://twitter.com/shadcn',
|
||||||
|
shadGithub: 'https://github.com/shadcn/ui',
|
||||||
|
},
|
||||||
|
keywords: 'shadcn,Vue,Nuxt,Vue Components,TailwindCSS,Radix Vue',
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
|
/* eslint-disable vue/component-definition-name-casing */
|
||||||
// https://vitepress.dev/guide/custom-theme
|
// https://vitepress.dev/guide/custom-theme
|
||||||
import Layout from './Layout.vue'
|
import Layout from './layout/MainLayout.vue'
|
||||||
|
import DocsLayout from './layout/DocsLayout.vue'
|
||||||
|
import * as components from './components'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
import './styles/vp-doc.css'
|
||||||
|
import './styles/shiki.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Layout,
|
Layout,
|
||||||
enhanceApp({ app, router, siteData }) {
|
enhanceApp({ app }) {
|
||||||
// ...
|
// ...
|
||||||
|
app.component('docs', DocsLayout)
|
||||||
|
|
||||||
|
for (const component of Object.keys(components))
|
||||||
|
app.component(component, components[component])
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
apps/www/.vitepress/theme/layout/DocsLayout.vue
Normal file
81
apps/www/.vitepress/theme/layout/DocsLayout.vue
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useData, useRoute } from 'vitepress'
|
||||||
|
import { docsConfig } from '../config/docs'
|
||||||
|
import TableOfContentVue from '../components/TableOfContent.vue'
|
||||||
|
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
|
||||||
|
import RadixIconsCode from '~icons/radix-icons/code'
|
||||||
|
|
||||||
|
const $route = useRoute()
|
||||||
|
const { frontmatter } = useData()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
|
||||||
|
<aside
|
||||||
|
class="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto"
|
||||||
|
>
|
||||||
|
<ScrollArea orientation="vertical" class="h-full py-6 pl-8 pr-6 lg:py-8" :type="'auto'">
|
||||||
|
<div class="w-full">
|
||||||
|
<div v-for="docsGroup in docsConfig.sidebarNav" :key="docsGroup.title" class="pb-4">
|
||||||
|
<h4
|
||||||
|
class="mb-1 rounded-md px-2 py-1 text-sm font-semibold"
|
||||||
|
>
|
||||||
|
{{ docsGroup.title }}
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="doc in docsGroup.items "
|
||||||
|
:key="doc.title"
|
||||||
|
class="grid grid-flow-row auto-rows-max text-sm"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
v-if="doc.href"
|
||||||
|
:disabled="doc.disabled"
|
||||||
|
:href="doc.href"
|
||||||
|
class="group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline text-muted-foreground"
|
||||||
|
:class="{
|
||||||
|
'!font-semibold !text-foreground': $route.path === `${doc.href}.html`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ doc.title }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
|
||||||
|
<div class="mx-auto w-full min-w-0">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<h1 class="scroll-m-20 text-4xl font-bold tracking-tight">
|
||||||
|
{{ frontmatter.title }}
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg text-muted-foreground">
|
||||||
|
{{ frontmatter.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-2 pt-4">
|
||||||
|
<a v-if="frontmatter.source" :href="frontmatter.source" target="_blank" class="inline-flex items-center rounded-md border 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 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
|
||||||
|
<RadixIconsCode class="mr-1" />
|
||||||
|
Component Source
|
||||||
|
</a>
|
||||||
|
<a v-if="frontmatter.primitive" :href="frontmatter.primitive" target="_blank" class="inline-flex items-center rounded-md border 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 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
|
||||||
|
Primitive API Reference
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vp-doc">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden text-sm xl:block">
|
||||||
|
<div class="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] overflow-hidden pt-6">
|
||||||
|
<TableOfContentVue />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
182
apps/www/.vitepress/theme/layout/MainLayout.vue
Normal file
182
apps/www/.vitepress/theme/layout/MainLayout.vue
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
|
import { Content, useData, useRoute } from 'vitepress'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { Kbd } from '@/lib/registry/default/ui/kbd'
|
||||||
|
import RadixIconsGithubLogo from '~icons/radix-icons/github-logo'
|
||||||
|
import TablerBrandX from '~icons/tabler/brand-x'
|
||||||
|
import RadixIconsMoon from '~icons/radix-icons/moon'
|
||||||
|
import RadixIconsSun from '~icons/radix-icons/sun'
|
||||||
|
import LucidePanelRightOpen from '~icons/lucide/panel-right-open'
|
||||||
|
import LucideSearch from '~icons/lucide/search'
|
||||||
|
|
||||||
|
const { frontmatter } = useData()
|
||||||
|
|
||||||
|
const $route = useRoute()
|
||||||
|
const isDark = useDark()
|
||||||
|
|
||||||
|
const toggleDark = useToggle(isDark)
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
name: 'Documentation',
|
||||||
|
path: '/docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Components',
|
||||||
|
path: '/docs/components/accordion',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Themes',
|
||||||
|
path: '/themes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Examples',
|
||||||
|
path: '/examples/dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'GitHub',
|
||||||
|
path: 'https://github.com/radix-vue/shadcn-vue',
|
||||||
|
target: '_blank',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
name: 'GitHub',
|
||||||
|
href: 'https://github.com/radix-vue',
|
||||||
|
icon: RadixIconsGithubLogo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'X',
|
||||||
|
href: 'https://x.com',
|
||||||
|
icon: TablerBrandX,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex min-h-screen flex-col bg-background">
|
||||||
|
<header class="sticky z-40 top-0 bg-background border-b border-border">
|
||||||
|
<div
|
||||||
|
class="max-w-8xl flex h-[58px] items-center justify-between p-4 mx-auto"
|
||||||
|
>
|
||||||
|
<div class="flex gap-6 md:gap-8">
|
||||||
|
<a href="/" class="text-md font-bold"> shadcn-vue </a>
|
||||||
|
|
||||||
|
<nav
|
||||||
|
v-for="route in routes"
|
||||||
|
:key="route.name"
|
||||||
|
class="flex items-center space-x-6 text-sm font-medium"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
:href="route.path"
|
||||||
|
:target="route.target"
|
||||||
|
class="transition-colors hover:text-foreground/80 text-foreground/60"
|
||||||
|
:class="{
|
||||||
|
'font-semibold !text-foreground': $route.path === `${route.path}.html`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ route.name }}
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:flex flex-1 items-center justify-end space-x-4 hidden">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
class="w-72 h-8 px-3 hidden lg:flex lg:justify-between lg:items-center"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<LucideSearch class="w-4 h-4 mr-2 text-muted" />
|
||||||
|
<span class="text-muted"> Search for anything... </span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-x-1">
|
||||||
|
<Kbd>⌘</Kbd>
|
||||||
|
<Kbd>K</Kbd>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="link in links"
|
||||||
|
:key="link.name"
|
||||||
|
class="flex items-center space-x-1"
|
||||||
|
>
|
||||||
|
<a :href="link.href" target="_blank" class="text-foreground">
|
||||||
|
<component :is="link.icon" class="w-5 h-5" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="flex items-center justify-center"
|
||||||
|
aria-label="Toggle dark mode"
|
||||||
|
@click="toggleDark()"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="isDark ? RadixIconsSun : RadixIconsMoon"
|
||||||
|
class="w-5 h-5 text-foreground"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="flex md:hidden items-center justify-center">
|
||||||
|
<LucidePanelRightOpen class="w-5 h-5 text-foreground" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class="flex md:hidden items-center justify-center">
|
||||||
|
<LucidePanelRightOpen class="w-5 h-5 text-foreground" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<component :is="'docs'" v-if="$route.path.includes('docs')">
|
||||||
|
<Content />
|
||||||
|
</component>
|
||||||
|
<component :is="frontmatter.layout" v-else-if="frontmatter.layout">
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
<main v-else>
|
||||||
|
<Content />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="bg-background z-40 border-t border-border text-foreground">
|
||||||
|
<div class="max-w-8xl h-20 flex items-center justify-between p-4 mx-auto">
|
||||||
|
<div class="flex justify-center items-center">
|
||||||
|
<span class="text-sm">
|
||||||
|
Built and designed by {{ " " }}
|
||||||
|
<a
|
||||||
|
href="https://twitter.com/shadcn"
|
||||||
|
target="_blank"
|
||||||
|
class="underline underline-offset-4 font-bold decoration-foreground"
|
||||||
|
>
|
||||||
|
shadcn
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="text-sm ml-0.5"> . </span>
|
||||||
|
<span class="text-sm ml-2">
|
||||||
|
Ported to Vue by {{ " " }}
|
||||||
|
<a
|
||||||
|
href="https://twitter.com"
|
||||||
|
target="_blank"
|
||||||
|
class="underline underline-offset-4 font-bold decoration-foreground"
|
||||||
|
>
|
||||||
|
Radix Vue
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="text-sm ml-0.5"> . </span>
|
||||||
|
<span class="text-sm ml-2">
|
||||||
|
The code source is available on {{ " " }}
|
||||||
|
<a
|
||||||
|
href="https://github.com/radix-vue/shadcn-vue"
|
||||||
|
target="_blank"
|
||||||
|
class="underline underline-offset-4 font-bold decoration-foreground"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,5 +1,160 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
|
||||||
|
--primary: 240 5.9% 10%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 85.7% 97.3%;
|
||||||
|
|
||||||
|
--ring: 240 3.7% 15.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
font-variation-settings: normal;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground min-h-screen antialiased font-sans;
|
||||||
|
font-feature-settings: "rlig" 1, "calt" 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile tap highlight */
|
||||||
|
/* https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color */
|
||||||
|
html {
|
||||||
|
-webkit-tap-highlight-color: rgba(128, 128, 128, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Scrollbars === */
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
@apply w-2;
|
||||||
|
@apply h-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
@apply !bg-muted;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
@apply rounded-sm !bg-muted-foreground/30;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
|
||||||
|
html {
|
||||||
|
scrollbar-color: hsl(215.4 16.3% 46.9% / 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.antialised {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border !bg-zinc-950 py-4 dark:!bg-zinc-900
|
||||||
|
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
@apply relative rounded font-mono text-sm ;
|
||||||
|
}
|
||||||
|
pre code .line {
|
||||||
|
@apply px-4 min-h-[1.5rem] py-0.5 w-full inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: Arial, Helvetica;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.step {
|
||||||
|
counter-increment: step;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:before {
|
||||||
|
@apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background;
|
||||||
|
@apply ml-[-50px] mt-[-4px];
|
||||||
|
content: counter(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.container {
|
||||||
|
@apply px-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
13
apps/www/.vitepress/theme/styles/shiki.css
Normal file
13
apps/www/.vitepress/theme/styles/shiki.css
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
:root {
|
||||||
|
--shiki-color-text: #EEEEEE;
|
||||||
|
--shiki-color-background: #ffffff;
|
||||||
|
--shiki-token-constant: #ffffff;
|
||||||
|
--shiki-token-string: #ffffff88;
|
||||||
|
--shiki-token-comment: #880000;
|
||||||
|
--shiki-token-keyword: #ffffff88;
|
||||||
|
--shiki-token-parameter: #AA0000;
|
||||||
|
--shiki-token-function: #ffffff;
|
||||||
|
--shiki-token-string-expression: #ffffff88;
|
||||||
|
--shiki-token-punctuation: #ffffff;
|
||||||
|
--shiki-token-link: #EE0000;
|
||||||
|
}
|
||||||
570
apps/www/.vitepress/theme/styles/vp-doc.css
Normal file
570
apps/www/.vitepress/theme/styles/vp-doc.css
Normal file
|
|
@ -0,0 +1,570 @@
|
||||||
|
:root {
|
||||||
|
--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");
|
||||||
|
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
|
||||||
|
--vp-code-bg: hsl(240 3.7% 15.9%);
|
||||||
|
--vp-c-divider: hsl(240 3.7% 15.9%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Headings
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc h1,
|
||||||
|
.vp-doc h2,
|
||||||
|
.vp-doc h3,
|
||||||
|
.vp-doc h4,
|
||||||
|
.vp-doc h5,
|
||||||
|
.vp-doc h6 {
|
||||||
|
position: relative;
|
||||||
|
font-weight: 600;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 {
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 {
|
||||||
|
/* margin: 48px 0 16px; */
|
||||||
|
margin: 16px 0 16px;
|
||||||
|
border-top: 1px solid var(--vp-c-divider);
|
||||||
|
padding-top: 24px;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h3 {
|
||||||
|
margin: 32px 0 0;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 28px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .header-anchor {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -0.87em;
|
||||||
|
font-weight: 500;
|
||||||
|
user-select: none;
|
||||||
|
opacity: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .header-anchor:before {
|
||||||
|
content: var(--vp-header-anchor-symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1:hover .header-anchor,
|
||||||
|
.vp-doc h1 .header-anchor:focus,
|
||||||
|
.vp-doc h2:hover .header-anchor,
|
||||||
|
.vp-doc h2 .header-anchor:focus,
|
||||||
|
.vp-doc h3:hover .header-anchor,
|
||||||
|
.vp-doc h3 .header-anchor:focus,
|
||||||
|
.vp-doc h4:hover .header-anchor,
|
||||||
|
.vp-doc h4 .header-anchor:focus,
|
||||||
|
.vp-doc h5:hover .header-anchor,
|
||||||
|
.vp-doc h5 .header-anchor:focus,
|
||||||
|
.vp-doc h6:hover .header-anchor,
|
||||||
|
.vp-doc h6 .header-anchor:focus {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.vp-doc h1 {
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 .header-anchor {
|
||||||
|
top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paragraph and inline elements
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* .vp-doc p,
|
||||||
|
.vp-doc summary {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc p {
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc blockquote {
|
||||||
|
margin: 16px 0;
|
||||||
|
border-left: 2px solid var(--vp-c-divider);
|
||||||
|
padding-left: 16px;
|
||||||
|
transition: border-color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc blockquote > p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
transition: color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc a:hover {
|
||||||
|
color: var(--vp-c-brand-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc strong {
|
||||||
|
font-weight: 600;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc ul,
|
||||||
|
.vp-doc ol {
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc ul {
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc ol {
|
||||||
|
list-style: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc li + li {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc li > ol,
|
||||||
|
.vp-doc li > ul {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc table {
|
||||||
|
display: block;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc tr {
|
||||||
|
border-top: 1px solid var(--vp-c-divider);
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc tr:nth-child(2n) {
|
||||||
|
background-color: var(--vp-c-bg-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc th,
|
||||||
|
.vp-doc td {
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc th {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
background-color: var(--vp-c-bg-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc td {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorational elements
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc hr {
|
||||||
|
margin: 16px 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--vp-c-divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Block
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc .custom-block {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block p {
|
||||||
|
margin: 8px 0;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block p:first-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block div[class*='language-'] {
|
||||||
|
margin: 8px 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block div[class*='language-'] code {
|
||||||
|
font-weight: 400;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .custom-block .vp-code-group .tabs {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* inline code */
|
||||||
|
.vp-doc :not(pre, h1, h2, h3, h4, h5, h6) > code {
|
||||||
|
font-size: var(--vp-code-font-size);
|
||||||
|
color: var(--vp-code-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc :not(pre) > code {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
background-color: var(--vp-code-bg);
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
background-color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc a > code {
|
||||||
|
color: var(--vp-code-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc a:hover > code {
|
||||||
|
color: var(--vp-code-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 > code,
|
||||||
|
.vp-doc h2 > code,
|
||||||
|
.vp-doc h3 > code {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[class*='language-'],
|
||||||
|
.vp-block {
|
||||||
|
position: relative;
|
||||||
|
margin: 16px -24px;
|
||||||
|
background-color: var(--vp-code-block-bg);
|
||||||
|
overflow-x: auto;
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.vp-doc div[class*='language-'],
|
||||||
|
.vp-block {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 639px) {
|
||||||
|
.vp-doc li div[class*='language-'] {
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[class*='language-'] + div[class*='language-'],
|
||||||
|
.vp-doc div[class$='-api'] + div[class*='language-'],
|
||||||
|
.vp-doc div[class*='language-'] + div[class$='-api'] > div[class*='language-'] {
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] pre,
|
||||||
|
.vp-doc [class*='language-'] code {
|
||||||
|
/*rtl:ignore*/
|
||||||
|
direction: ltr;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] pre {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
margin: 0;
|
||||||
|
/* padding: 20px 0; */
|
||||||
|
background: transparent;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code {
|
||||||
|
display: block;
|
||||||
|
/* padding: 0 24px; */
|
||||||
|
width: fit-content;
|
||||||
|
min-width: 100%;
|
||||||
|
line-height: var(--vp-code-line-height);
|
||||||
|
/* font-size: var(--vp-code-font-size); */
|
||||||
|
color: var(--vp-code-block-color);
|
||||||
|
transition: color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .highlighted {
|
||||||
|
background-color: var(--vp-code-line-highlight-color);
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
margin: 0 -24px;
|
||||||
|
padding: 0 24px;
|
||||||
|
width: calc(100% + 2 * 24px);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .highlighted.error {
|
||||||
|
background-color: var(--vp-code-line-error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .highlighted.warning {
|
||||||
|
background-color: var(--vp-code-line-warning-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff {
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
margin: 0 -24px;
|
||||||
|
padding: 0 24px;
|
||||||
|
width: calc(100% + 2 * 24px);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff::before {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] .has-focused-lines .line:not(.has-focus) {
|
||||||
|
filter: blur(0.095rem);
|
||||||
|
opacity: 0.4;
|
||||||
|
transition:
|
||||||
|
filter 0.35s,
|
||||||
|
opacity 0.35s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] .has-focused-lines .line:not(.has-focus) {
|
||||||
|
opacity: 0.7;
|
||||||
|
transition:
|
||||||
|
filter 0.35s,
|
||||||
|
opacity 0.35s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-']:hover .has-focused-lines .line:not(.has-focus) {
|
||||||
|
filter: blur(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff.remove {
|
||||||
|
background-color: var(--vp-code-line-diff-remove-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff.remove::before {
|
||||||
|
content: '-';
|
||||||
|
color: var(--vp-code-line-diff-remove-symbol-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff.add {
|
||||||
|
background-color: var(--vp-code-line-diff-add-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] code .diff.add::before {
|
||||||
|
content: '+';
|
||||||
|
color: var(--vp-code-line-diff-add-symbol-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[class*='language-'].line-numbers-mode {
|
||||||
|
/*rtl:ignore*/
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .line-numbers-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
left: 0;
|
||||||
|
z-index: 3;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
border-right: 1px solid var(--vp-code-block-divider-color);
|
||||||
|
padding-top: 20px;
|
||||||
|
width: 32px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: var(--vp-font-family-mono);
|
||||||
|
line-height: var(--vp-code-line-height);
|
||||||
|
font-size: var(--vp-code-font-size);
|
||||||
|
color: var(--vp-code-line-number-color);
|
||||||
|
transition:
|
||||||
|
border-color 0.5s,
|
||||||
|
color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] > button.copy {
|
||||||
|
/*rtl:ignore*/
|
||||||
|
direction: ltr;
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
right: 12px;
|
||||||
|
z-index: 3;
|
||||||
|
border: 1px solid var(--vp-code-copy-code-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--vp-code-copy-code-bg);
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: var(--vp-icon-copy);
|
||||||
|
background-position: 50%;
|
||||||
|
background-size: 20px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
transition:
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s,
|
||||||
|
opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-']:hover > button.copy,
|
||||||
|
.vp-doc [class*='language-'] > button.copy:focus {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] > button.copy:hover,
|
||||||
|
.vp-doc [class*='language-'] > button.copy.copied {
|
||||||
|
border-color: var(--vp-code-copy-code-hover-border-color);
|
||||||
|
background-color: var(--vp-code-copy-code-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] > button.copy.copied,
|
||||||
|
.vp-doc [class*='language-'] > button.copy:hover.copied {
|
||||||
|
/*rtl:ignore*/
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
background-color: var(--vp-code-copy-code-hover-bg);
|
||||||
|
background-image: var(--vp-icon-copied);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] > button.copy.copied::before,
|
||||||
|
.vp-doc [class*='language-'] > button.copy:hover.copied::before {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
transform: translateX(calc(-100% - 1px));
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--vp-code-copy-code-hover-border-color);
|
||||||
|
/*rtl:ignore*/
|
||||||
|
border-right: 0;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
padding: 0 10px;
|
||||||
|
width: fit-content;
|
||||||
|
height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vp-code-copy-code-active-text);
|
||||||
|
background-color: var(--vp-code-copy-code-hover-bg);
|
||||||
|
white-space: nowrap;
|
||||||
|
content: var(--vp-code-copy-copied-text-content);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-'] > span.lang {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
/*rtl:ignore*/
|
||||||
|
right: 8px;
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vp-code-lang-color);
|
||||||
|
transition:
|
||||||
|
color 0.4s,
|
||||||
|
opacity 0.4s;
|
||||||
|
|
||||||
|
@apply text-gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc [class*='language-']:hover > button.copy + span.lang,
|
||||||
|
.vp-doc [class*='language-'] > button.copy:focus + span.lang {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component: Team
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.vp-doc .VPTeamMembers {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .VPTeamMembers.small.count-1 .container {
|
||||||
|
margin: 0 !important;
|
||||||
|
max-width: calc((100% - 24px) / 2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .VPTeamMembers.small.count-2 .container,
|
||||||
|
.vp-doc .VPTeamMembers.small.count-3 .container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc .VPTeamMembers.medium.count-1 .container {
|
||||||
|
margin: 0 !important;
|
||||||
|
max-width: calc((100% - 24px) / 2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
:is(.vp-external-link-icon, .vp-doc a[href*='://'], .vp-doc a[target='_blank']):not(.no-icon)::after {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: 4px;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
background: currentColor;
|
||||||
|
color: var(--vp-c-text-3);
|
||||||
|
flex-shrink: 0;
|
||||||
|
--icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");
|
||||||
|
-webkit-mask-image: var(--icon);
|
||||||
|
mask-image: var(--icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-external-link-icon::after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
9
apps/www/.vitepress/theme/types/docs.ts
Normal file
9
apps/www/.vitepress/theme/types/docs.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface TableOfContentsItem {
|
||||||
|
title?: string
|
||||||
|
url?: string
|
||||||
|
items?: TableOfContentsItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableOfContents {
|
||||||
|
items: TableOfContentsItem[]
|
||||||
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Vite + Vue + TS</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@iconify-json/radix-icons": "^1.1.11",
|
||||||
|
"@iconify-json/tabler": "^1.1.89",
|
||||||
"@iconify/vue": "^4.1.1",
|
"@iconify/vue": "^4.1.1",
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
|
@ -30,6 +32,7 @@
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^1.14.0",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
|
"unplugin-icons": "^0.16.6",
|
||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
"vue-tsc": "^1.4.2"
|
"vue-tsc": "^1.4.2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import AccordionDemo from '@/registry/default/examples/AccordionDemo.vue'
|
|
||||||
import PopoverDemo from '@/registry/default/examples/PopoverDemo.vue'
|
|
||||||
import DialogDemo from '@/registry/default/examples/DialogDemo.vue'
|
|
||||||
import AlertDialogDemo from '@/registry/default/examples/AlertDialogDemo.vue'
|
|
||||||
import SelectDemo from '@/registry/default/examples/SelectDemo.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="p-8 grid gap-12 grid-cols-3 place-items-center">
|
|
||||||
<AccordionDemo class="max-w-[20rem]" />
|
|
||||||
|
|
||||||
<PopoverDemo />
|
|
||||||
|
|
||||||
<DialogDemo />
|
|
||||||
|
|
||||||
<AlertDialogDemo />
|
|
||||||
|
|
||||||
<SelectDemo />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
title: String,
|
|
||||||
})
|
|
||||||
</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>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { default as Card } from './Card.vue'
|
|
||||||
5
apps/www/src/content/docs.md
Normal file
5
apps/www/src/content/docs.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
layout: docs
|
||||||
|
---
|
||||||
|
|
||||||
|
docs laytou please?
|
||||||
49
apps/www/src/content/docs/components/accordion.md
Normal file
49
apps/www/src/content/docs/components/accordion.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
title: Accordion
|
||||||
|
description: A vertically stacked set of interactive headings that each reveal a section of content.
|
||||||
|
source: https://github.com/radix-vue/shadcn-vue/tree/main/apps/www/src/lib/registry/default/ui/accordion
|
||||||
|
primitive: https://www.radix-vue.com/components/accordion.html
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
<ComponentPreview name="AccordionDemo" class="[&_.accordion]:sm:max-w-[70%]">
|
||||||
|
|
||||||
|
<<< ../../../lib/registry/default/examples/AccordionDemo.vue
|
||||||
|
|
||||||
|
</ComponentPreview>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx shadcn-vue@latest add accordion
|
||||||
|
```
|
||||||
|
|
||||||
|
<ManualInstall>
|
||||||
|
|
||||||
|
1. Install `radix-vue`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install radix-vue
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Copy and paste the component source files linked at the top of this page into your project.
|
||||||
|
</ManualInstall>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AccordionRoot>
|
||||||
|
<AccordionItem value="item-1">
|
||||||
|
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
Yes. It adheres to the WAI-ARIA design pattern.
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</AccordionRoot>
|
||||||
|
```
|
||||||
1
apps/www/src/content/example/dashboard.md
Normal file
1
apps/www/src/content/example/dashboard.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
hi
|
||||||
|
|
@ -2,3 +2,4 @@
|
||||||
home: true
|
home: true
|
||||||
---
|
---
|
||||||
|
|
||||||
|
this is main content
|
||||||
0
apps/www/src/content/themes.md
Normal file
0
apps/www/src/content/themes.md
Normal file
|
|
@ -1,24 +1,12 @@
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/registry/default/ui/accordion'
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
|
||||||
|
|
||||||
const defaultValue = 'item-1'
|
const defaultValue = 'item-1'
|
||||||
|
|
||||||
const accordionItems = [
|
const accordionItems = [
|
||||||
{
|
{ value: 'item-1', title: 'Is it accessible?', content: 'Yes. It adheres to the WAI-ARIA design pattern.' },
|
||||||
value: 'item-1',
|
{ value: 'item-2', title: 'Is it unstyled?', content: 'Yes. It\'s unstyled by default, giving you freedom over the look and feel.' },
|
||||||
title: 'Is it accessible?',
|
{ value: 'item-3', title: 'Can it be animated?', content: 'Yes! You can use the transition prop to configure the animation.' },
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
1
apps/www/src/lib/registry/default/examples/index.ts
Normal file
1
apps/www/src/lib/registry/default/examples/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as AccordionDemo } from './AccordionDemo.vue'
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
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 { ChevronDown } from 'lucide-vue-next'
|
|
||||||
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.default(), h(ChevronDown, { class: 'h-4 w-4 shrink-0 transition-transform duration-200' })],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
14
apps/www/src/lib/registry/default/ui/accordion/Accordion.vue
Normal file
14
apps/www/src/lib/registry/default/ui/accordion/Accordion.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
AccordionRoot,
|
||||||
|
type AccordionRootProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionRootProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionRoot v-bind="props" class="accordion">
|
||||||
|
<slot />
|
||||||
|
</AccordionRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AccordionContent, type AccordionContentProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionContentProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionContent
|
||||||
|
v-bind="props"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'overflow-hidden text-sm transition-all data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="pb-4 pt-0">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AccordionItem, type AccordionItemProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionItemProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionItem
|
||||||
|
v-bind="props"
|
||||||
|
:class="cn('border-b text-foreground border-border', props.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AccordionItem>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
AccordionHeader,
|
||||||
|
AccordionTrigger,
|
||||||
|
type AccordionTriggerProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { ChevronDown } from 'lucide-vue-next'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionTriggerProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionHeader class="flex" as="div">
|
||||||
|
<AccordionTrigger
|
||||||
|
v-bind="props"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex flex-1 group items-center justify-between py-4 font-medium transition-all hover:underline',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<ChevronDown
|
||||||
|
class="w-4 h-4 shrink-0 transition-transform duration-300 group-data-[state=open]:rotate-180"
|
||||||
|
/>
|
||||||
|
</AccordionTrigger>
|
||||||
|
</AccordionHeader>
|
||||||
|
</template>
|
||||||
4
apps/www/src/lib/registry/default/ui/accordion/index.ts
Normal file
4
apps/www/src/lib/registry/default/ui/accordion/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
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'
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
import { defineComponent, h } from 'vue'
|
|
||||||
import {
|
|
||||||
AlertDialogAction as AlertDialogActionPrimitive, type AlertDialogActionProps,
|
|
||||||
AlertDialogCancel as AlertDialogCancelPrimitive, type AlertDialogCancelProps,
|
|
||||||
type AlertDialogContentEmits, AlertDialogContent as AlertDialogContentPrimitive, type AlertDialogContentProps,
|
|
||||||
AlertDialogDescription as AlertDialogDescriptionPrimitive, type AlertDialogDescriptionProps,
|
|
||||||
AlertDialogOverlay as AlertDialogOverlayPrimitive, type AlertDialogOverlayProps,
|
|
||||||
AlertDialogPortal as AlertDialogPortalPrimitive,
|
|
||||||
AlertDialogRoot, AlertDialogTitle as AlertDialogTitlePrimitive,
|
|
||||||
type AlertDialogTitleProps, AlertDialogTrigger as AlertDialogTriggerPrimitive,
|
|
||||||
|
|
||||||
} from 'radix-vue'
|
|
||||||
import type { ParseEmits } from '@/utils'
|
|
||||||
import { cn, useEmitAsProps } from '@/utils'
|
|
||||||
import { buttonVariants } from '@/registry/default/ui/button'
|
|
||||||
|
|
||||||
export const AlertDialog = AlertDialogRoot
|
|
||||||
export const AlertDialogTrigger = AlertDialogTriggerPrimitive
|
|
||||||
export const AlertDialogPortal = AlertDialogPortalPrimitive
|
|
||||||
|
|
||||||
export const AlertDialogOverlay = defineComponent<AlertDialogOverlayProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(AlertDialogOverlayPrimitive,
|
|
||||||
{ class: cn('fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogOverlay' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogContent = defineComponent<AlertDialogContentProps, ParseEmits<AlertDialogContentEmits>>(
|
|
||||||
(props, { emit, attrs, slots }) => {
|
|
||||||
const emitsAsProps = useEmitAsProps(emit)
|
|
||||||
|
|
||||||
return () => h(AlertDialogPortal, () => [
|
|
||||||
h(AlertDialogOverlay),
|
|
||||||
h(AlertDialogContentPrimitive, {
|
|
||||||
class: cn('fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', attrs.class ?? ''),
|
|
||||||
...props,
|
|
||||||
...emitsAsProps,
|
|
||||||
}, slots),
|
|
||||||
])
|
|
||||||
}, { name: 'AlertDialogContent', emits: AlertDialogContentPrimitive.emits },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogHeader = defineComponent(
|
|
||||||
(_props, { attrs, slots }) => {
|
|
||||||
return () => h('div',
|
|
||||||
{ class: cn('flex flex-col space-y-2 text-center sm:text-left', attrs.class) },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogHeader' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogFooter = defineComponent(
|
|
||||||
(_props, { attrs, slots }) => {
|
|
||||||
return () => h('div',
|
|
||||||
{ class: cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', attrs.class) },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogFooter' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogTitle = defineComponent<AlertDialogTitleProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(AlertDialogTitlePrimitive,
|
|
||||||
{ class: cn('text-lg font-semibold', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogTitle' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogDescription = defineComponent<AlertDialogDescriptionProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(AlertDialogDescriptionPrimitive,
|
|
||||||
{ class: cn('text-sm text-muted-foreground', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogDescription' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogAction = defineComponent<AlertDialogActionProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(AlertDialogActionPrimitive,
|
|
||||||
{ class: cn(buttonVariants(), attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogAction' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const AlertDialogCancel = defineComponent<AlertDialogCancelProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(AlertDialogCancelPrimitive,
|
|
||||||
{ class: cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'AlertDialogCancel' },
|
|
||||||
)
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type AlertDialogProps, AlertDialogRoot } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogRoot :default-open="props.defaultOpen" :open="props.open">
|
||||||
|
<slot />
|
||||||
|
</AlertDialogRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogActionProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogAction v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</AlertDialogAction>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogCancelProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogCancel v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</AlertDialogCancel>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
AlertDialogContent,
|
||||||
|
type AlertDialogContentProps,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
AlertDialogPortal,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogContentProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogPortal>
|
||||||
|
<AlertDialogOverlay
|
||||||
|
class="fixed inset-0 z-50 bg-white/80 dark:bg-gray-950/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||||
|
/>
|
||||||
|
<AlertDialogContent
|
||||||
|
v-bind="props"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialogPortal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
AlertDialogDescription,
|
||||||
|
type AlertDialogDescriptionProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogDescriptionProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogDescription
|
||||||
|
:class="cn('text-muted text-sm', props.class)"
|
||||||
|
:as-child="props.asChild"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex flex-col space-y-2 sm:space-y-0 mt-3.5 sm:flex-row sm:justify-end sm:space-x-2',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="cn('flex flex-col space-y-2 text-center sm:text-left', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogTitleProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogTitle
|
||||||
|
:as-child="props.asChild"
|
||||||
|
:class="cn('text-lg text-foreground font-semibold', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AlertDialogTitle>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AlertDialogTrigger, type AlertDialogTriggerProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AlertDialogTriggerProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AlertDialogTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
export { default as AlertDialog } from './AlertDialog.vue'
|
||||||
|
export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'
|
||||||
|
export { default as AlertDialogContent } from './AlertDialogContent.vue'
|
||||||
|
export { default as AlertDialogHeader } from './AlertDialogHeader.vue'
|
||||||
|
export { default as AlertDialogTitle } from './AlertDialogTitle.vue'
|
||||||
|
export { default as AlertDialogDescription } from './AlertDialogDescription.vue'
|
||||||
|
export { default as AlertDialogFooter } from './AlertDialogFooter.vue'
|
||||||
|
export { default as AlertDialogAction } from './AlertDialogAction.vue'
|
||||||
|
export { default as AlertDialogCancel } from './AlertDialogCancel.vue'
|
||||||
78
apps/www/src/lib/registry/default/ui/alert/Alert.vue
Normal file
78
apps/www/src/lib/registry/default/ui/alert/Alert.vue
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
import { X } from 'lucide-vue-next'
|
||||||
|
import { Button } from '../button'
|
||||||
|
|
||||||
|
interface AlertProps {
|
||||||
|
variant?: 'primary' | 'success' | 'warning' | 'destructive' | 'info'
|
||||||
|
dismissible?: boolean
|
||||||
|
show?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<AlertProps>(), {
|
||||||
|
variant: 'primary',
|
||||||
|
dismissible: false,
|
||||||
|
show: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'update:show', value: boolean): void
|
||||||
|
(e: 'close'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const alertClass = computed(() => {
|
||||||
|
return cva(
|
||||||
|
'relative w-full rounded-lg p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
primary: 'border border-border text-foreground',
|
||||||
|
success:
|
||||||
|
'border dark:border-green-600 dark:text-green-600 text-green-500 border-green-500',
|
||||||
|
warning:
|
||||||
|
'border dark:border-yellow-600 dark:text-yellow-600 text-yellow-500 border-yellow-500',
|
||||||
|
destructive: 'border border-destructive text-destructive',
|
||||||
|
info: 'border dark:border-blue-600 dark:text-blue-600 text-blue-500 border-blue-500',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)({
|
||||||
|
variant: props.variant,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const show = computed({
|
||||||
|
get() {
|
||||||
|
return props.show
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emits('update:show', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Transition
|
||||||
|
enter-active-class="transition ease-out duration-300"
|
||||||
|
enter-from-class="opacity-0"
|
||||||
|
enter-to-class="opacity-100"
|
||||||
|
leave-active-class="transition ease-in duration-300"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<div v-if="show" :class="alertClass" role="alert">
|
||||||
|
<Button
|
||||||
|
v-if="props.dismissible"
|
||||||
|
variant="neutral"
|
||||||
|
class="absolute top-1.5 -right-0.5"
|
||||||
|
@click="emits('close')"
|
||||||
|
>
|
||||||
|
<X
|
||||||
|
class="w-4 h-4 text-muted transition-colors ease-in-out duration-300"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('text-sm text-left leading-relaxed', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<h5 class="mb-1 font-medium leading-none tracking-tight">
|
||||||
|
<slot />
|
||||||
|
</h5>
|
||||||
|
</template>
|
||||||
3
apps/www/src/lib/registry/default/ui/alert/index.ts
Normal file
3
apps/www/src/lib/registry/default/ui/alert/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Alert } from './Alert.vue'
|
||||||
|
export { default as AlertTitle } from './AlertTitle.vue'
|
||||||
|
export { default as AlertDescription } from './AlertDescription.vue'
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AspectRatio, type AspectRatioProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AspectRatioProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AspectRatio v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</AspectRatio>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as AspectRatio } from './AspectRatio.vue'
|
||||||
46
apps/www/src/lib/registry/default/ui/avatar/Avatar.vue
Normal file
46
apps/www/src/lib/registry/default/ui/avatar/Avatar.vue
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { AvatarRoot } from 'radix-vue'
|
||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
interface AvatarProps {
|
||||||
|
size?: 'sm' | 'base' | 'lg'
|
||||||
|
shape?: 'circle' | 'square'
|
||||||
|
class?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<AvatarProps>(), {
|
||||||
|
name: 'Anonymous',
|
||||||
|
size: 'base',
|
||||||
|
shape: 'circle',
|
||||||
|
})
|
||||||
|
|
||||||
|
const avatarClass = computed(() => {
|
||||||
|
return cva(
|
||||||
|
'inline-flex items-center justify-center font-normal text-foregorund select-none shrink-0 bg-secondary overflow-hidden',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
sm: 'h-10 w-10 text-xs',
|
||||||
|
base: 'h-16 w-16 text-2xl',
|
||||||
|
lg: 'h-32 w-32 text-5xl',
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
circle: 'rounded-full',
|
||||||
|
square: 'rounded-md',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)({
|
||||||
|
size: props.size,
|
||||||
|
shape: props.shape,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AvatarRoot :class="cn(avatarClass, props.class)">
|
||||||
|
<slot />
|
||||||
|
</AvatarRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AvatarFallback, type AvatarFallbackProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AvatarFallbackProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AvatarFallback v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</AvatarFallback>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { AvatarImage, type AvatarImageProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<AvatarImageProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AvatarImage v-bind="props" class="h-full w-full object-cover" />
|
||||||
|
</template>
|
||||||
3
apps/www/src/lib/registry/default/ui/avatar/index.ts
Normal file
3
apps/www/src/lib/registry/default/ui/avatar/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Avatar } from './Avatar.vue'
|
||||||
|
export { default as AvatarImage } from './AvatarImage.vue'
|
||||||
|
export { default as AvatarFallback } from './AvatarFallback.vue'
|
||||||
42
apps/www/src/lib/registry/default/ui/badge/Badge.vue
Normal file
42
apps/www/src/lib/registry/default/ui/badge/Badge.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
interface BadgeProps {
|
||||||
|
variant?: 'primary' | 'secondary' | 'outline' | 'destructive'
|
||||||
|
as?: string
|
||||||
|
class?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<BadgeProps>(), {
|
||||||
|
variant: 'primary',
|
||||||
|
as: 'span',
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeClass = computed(() => {
|
||||||
|
return cva(
|
||||||
|
'inline-flex items-center cursor-default text-xs font-semibold px-2.5 py-0.5 rounded-md transition-colors ease-in-out duration-300',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
primary: 'bg-primary text-primary-foreground hover:bg-primary-hover',
|
||||||
|
secondary: 'bg-secondary text-foreground hover:bg-secondary-hover',
|
||||||
|
outline:
|
||||||
|
'border border-border text-foreground shadow-sm hover:bg-outline-hover',
|
||||||
|
destructive:
|
||||||
|
'bg-destructive text-destructive-foreground hover:bg-destructive-hover',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)({
|
||||||
|
variant: props.variant,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="props.as" :class="cn(badgeClass, props.class)">
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
1
apps/www/src/lib/registry/default/ui/badge/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/badge/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Badge } from './Badge.vue'
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<ol class="flex items-center whitespace-nowrap min-w-0">
|
||||||
|
<slot />
|
||||||
|
</ol>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronRight } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
interface BreadCrumbItemProps {
|
||||||
|
path?: string
|
||||||
|
lastItem?: boolean
|
||||||
|
as?: string | object
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<BreadCrumbItemProps>(), {
|
||||||
|
as: 'span',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<li class="text-sm text-muted">
|
||||||
|
<component
|
||||||
|
:is="props.as"
|
||||||
|
:to="props.path"
|
||||||
|
class="flex items-center"
|
||||||
|
:class="{
|
||||||
|
'!font-semibold !text-foreground': $route.path === props.path,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<ChevronRight
|
||||||
|
v-if="!props.lastItem"
|
||||||
|
class="flex-shrink-0 h-3 w-3 text-muted mx-2"
|
||||||
|
/>
|
||||||
|
</component>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
2
apps/www/src/lib/registry/default/ui/breadcrumb/index.ts
Normal file
2
apps/www/src/lib/registry/default/ui/breadcrumb/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as BreadCrumb } from './BreadCrumb.vue'
|
||||||
|
export { default as BreadCrumbItem } from './BreadCrumbItem.vue'
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { defineComponent, h } from 'vue'
|
|
||||||
import { type VariantProps, cva } from 'class-variance-authority'
|
|
||||||
import { cn } from '@/utils'
|
|
||||||
|
|
||||||
export const buttonVariants = cva(
|
|
||||||
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background 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',
|
|
||||||
{
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
||||||
destructive:
|
|
||||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
||||||
outline:
|
|
||||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
||||||
secondary:
|
|
||||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
||||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
||||||
link: 'text-primary underline-offset-4 hover:underline',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
default: 'h-10 px-4 py-2',
|
|
||||||
sm: 'h-9 rounded-md px-3',
|
|
||||||
lg: 'h-11 rounded-md px-8',
|
|
||||||
icon: 'h-10 w-10',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: 'default',
|
|
||||||
size: 'default',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
interface ButtonProps extends VariantProps<typeof buttonVariants> {}
|
|
||||||
|
|
||||||
export const Button = defineComponent<ButtonProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h('button', { class: cn(buttonVariants(props), attrs.class ?? '') }, slots)
|
|
||||||
},
|
|
||||||
{ name: 'Button', props: ['size', 'variant'] },
|
|
||||||
)
|
|
||||||
64
apps/www/src/lib/registry/default/ui/button/Button.vue
Normal file
64
apps/www/src/lib/registry/default/ui/button/Button.vue
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
class?: string
|
||||||
|
variant?:
|
||||||
|
| 'primary'
|
||||||
|
| 'secondary'
|
||||||
|
| 'destructive'
|
||||||
|
| 'outline'
|
||||||
|
| 'ghost'
|
||||||
|
| 'link'
|
||||||
|
| 'neutral'
|
||||||
|
disabled?: boolean | undefined
|
||||||
|
as?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||||
|
variant: 'primary',
|
||||||
|
disabled: false,
|
||||||
|
as: 'button',
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonClass = computed(() => {
|
||||||
|
return cva(
|
||||||
|
'inline-flex items-center justify-center text-sm px-4 py-2.5 rounded-md font-medium transition-colors ease-in-out duration-300',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
primary:
|
||||||
|
'bg-primary text-primary-foreground enabled:hover:bg-primary-hover',
|
||||||
|
secondary:
|
||||||
|
'bg-secondary text-secondary-foreground enabled:hover:bg-secondary-hover',
|
||||||
|
destructive:
|
||||||
|
'bg-destructive text-destructive-foreground hover:bg-destructive-hover',
|
||||||
|
outline:
|
||||||
|
'border border-border text-foreground shadow-sm hover:bg-outline-hover',
|
||||||
|
ghost: 'text-foreground hover:bg-outline-hover',
|
||||||
|
link: 'text-foreground hover:underline hover:underline-offset-4',
|
||||||
|
neutral: '',
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
true: '!opacity-50 !cursor-not-allowed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)({
|
||||||
|
variant: props.variant,
|
||||||
|
disabled: props.disabled,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="props.as"
|
||||||
|
:class="cn(buttonClass, props.class)"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
1
apps/www/src/lib/registry/default/ui/button/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/button/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Button } from './Button.vue'
|
||||||
63
apps/www/src/lib/registry/default/ui/calendar/Calendar.vue
Normal file
63
apps/www/src/lib/registry/default/ui/calendar/Calendar.vue
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useDark } from '@vueuse/core'
|
||||||
|
import { Calendar } from 'v-calendar'
|
||||||
|
import 'v-calendar/style.css'
|
||||||
|
|
||||||
|
const isDark = useDark()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Calendar :is-dark="isDark" borderless trim-weeks expanded />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--vc-font-family: "Inter", sans-serif;
|
||||||
|
--vc-rounded-full: var(--radius);
|
||||||
|
--vc-font-bold: 500;
|
||||||
|
--vc-font-semibold: 600;
|
||||||
|
--vc-text-lg: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-light,
|
||||||
|
.vc-dark {
|
||||||
|
--vc-bg: var(--background);
|
||||||
|
--vc-border: var(--border);
|
||||||
|
--vc-focus-ring: 0 0 0 3px rgba(0, 0, 0, 0.2);
|
||||||
|
--vc-weekday-color: var(--muted);
|
||||||
|
--vc-popover-content-color: var(--muted);
|
||||||
|
--vc-popover-content-bg: var(--background);
|
||||||
|
--vc-popover-content-border: var(--border);
|
||||||
|
|
||||||
|
&.vc-attr,
|
||||||
|
& .vc-attr {
|
||||||
|
--vc-content-color: var(--primary);
|
||||||
|
--vc-highlight-outline-bg: var(--primary);
|
||||||
|
--vc-highlight-outline-border: var(--primary);
|
||||||
|
--vc-highlight-outline-content-color: var(--primary-foreground);
|
||||||
|
--vc-highlight-light-bg: var(
|
||||||
|
--vc-accent-200
|
||||||
|
); /* Highlighted color between two dates */
|
||||||
|
--vc-highlight-light-content-color: var(--secondary-foreground);
|
||||||
|
--vc-highlight-solid-bg: var(--primary);
|
||||||
|
--vc-highlight-solid-content-color: var(--primary-foreground);
|
||||||
|
--vc-dot-bg: var(--primary);
|
||||||
|
--vc-bar-bg: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-blue {
|
||||||
|
--vc-accent-200: var(--secondary);
|
||||||
|
--vc-accent-400: var(--primary);
|
||||||
|
--vc-accent-500: var(--primary);
|
||||||
|
--vc-accent-600: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.vc-blue {
|
||||||
|
--vc-accent-200: var(--secondary);
|
||||||
|
--vc-accent-400: var(--primary);
|
||||||
|
--vc-accent-500: var(--secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1
apps/www/src/lib/registry/default/ui/calendar/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/calendar/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Calendar } from './Calendar.vue'
|
||||||
23
apps/www/src/lib/registry/default/ui/card/Card.vue
Normal file
23
apps/www/src/lib/registry/default/ui/card/Card.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background text-foreground border border-border rounded-xl shadow-md',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
16
apps/www/src/lib/registry/default/ui/card/CardContent.vue
Normal file
16
apps/www/src/lib/registry/default/ui/card/CardContent.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('p-6 pt-0', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p :class="cn('text-muted text-sm', props.class)">
|
||||||
|
<slot />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
16
apps/www/src/lib/registry/default/ui/card/CardFooter.vue
Normal file
16
apps/www/src/lib/registry/default/ui/card/CardFooter.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('p-6 pt-0', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
16
apps/www/src/lib/registry/default/ui/card/CardHeader.vue
Normal file
16
apps/www/src/lib/registry/default/ui/card/CardHeader.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('flex flex-col space-y-1.5 p-6', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
20
apps/www/src/lib/registry/default/ui/card/CardTitle.vue
Normal file
20
apps/www/src/lib/registry/default/ui/card/CardTitle.vue
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h3
|
||||||
|
:class="
|
||||||
|
cn('text-2xl font-semibold leading-none tracking-tighter', props.class)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
6
apps/www/src/lib/registry/default/ui/card/index.ts
Normal file
6
apps/www/src/lib/registry/default/ui/card/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export { default as Card } from './Card.vue'
|
||||||
|
export { default as CardHeader } from './CardHeader.vue'
|
||||||
|
export { default as CardTitle } from './CardTitle.vue'
|
||||||
|
export { default as CardDescription } from './CardDescription.vue'
|
||||||
|
export { default as CardContent } from './CardContent.vue'
|
||||||
|
export { default as CardFooter } from './CardFooter.vue'
|
||||||
42
apps/www/src/lib/registry/default/ui/checkbox/Checkbox.vue
Normal file
42
apps/www/src/lib/registry/default/ui/checkbox/Checkbox.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import RadixIconsCheck from '~icons/radix-icons/check'
|
||||||
|
|
||||||
|
interface CheckBoxProps {
|
||||||
|
id?: string
|
||||||
|
modelValue?: boolean
|
||||||
|
required?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
invalid?: boolean
|
||||||
|
checked?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CheckBoxProps>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input
|
||||||
|
:id="props.id"
|
||||||
|
type="checkbox"
|
||||||
|
:value="props.modelValue"
|
||||||
|
:required="props.required"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
:class="{
|
||||||
|
'ring-destructive-light dark:ring-destructive': props.invalid,
|
||||||
|
'!cursor-not-allowed opacity-50': props.disabled,
|
||||||
|
}"
|
||||||
|
:checked="props.checked"
|
||||||
|
class="w-4 h-4 peer cursor-pointer shrink-0 relative checked:bg-primary appearance-none text-foreground border border-primary rounded"
|
||||||
|
@input="
|
||||||
|
($event) =>
|
||||||
|
emit('update:modelValue', ($event.target as HTMLInputElement).checked)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
|
||||||
|
<RadixIconsCheck
|
||||||
|
class="absolute pointer-events-none hidden peer-checked:block w-4 h-3 text-background"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
1
apps/www/src/lib/registry/default/ui/checkbox/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/checkbox/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Checkbox } from './Checkbox.vue'
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CollapsibleRoot, type CollapsibleRootProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<CollapsibleRootProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CollapsibleRoot v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</CollapsibleRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CollapsibleContent, type CollapsibleContentProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<CollapsibleContentProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CollapsibleContent v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</CollapsibleContent>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CollapsibleTrigger, type CollapsibleTriggerProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<CollapsibleTriggerProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CollapsibleTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Collapsible } from './Collapsible.vue'
|
||||||
|
export { default as CollapsibleTrigger } from './CollapsibleTrigger.vue'
|
||||||
|
export { default as CollapsibleContent } from './CollapsibleContent.vue'
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ContextMenuRoot, type ContextMenuRootProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuRootProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuRoot v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</ContextMenuRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuCheckboxItem,
|
||||||
|
type ContextMenuCheckboxItemEmits,
|
||||||
|
type ContextMenuCheckboxItemProps,
|
||||||
|
ContextMenuItemIndicator,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import RadixIconsCheck from '~icons/radix-icons/check'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuCheckboxItemProps & { class?: string }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuCheckboxItemEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuCheckboxItem
|
||||||
|
v-bind="props"
|
||||||
|
:class="[
|
||||||
|
cn(
|
||||||
|
'flex relative items-center rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[highlighted]:bg-outline-hover pl-7 py-1.5 text-sm outline-none select-none cursor-default',
|
||||||
|
props.class,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
@update:checked="emits('update:checked', $event)"
|
||||||
|
>
|
||||||
|
<ContextMenuItemIndicator
|
||||||
|
class="absolute left-1.5 inline-flex w-4 h-4 items-center justify-center"
|
||||||
|
>
|
||||||
|
<RadixIconsCheck />
|
||||||
|
</ContextMenuItemIndicator>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuCheckboxItem>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuContent,
|
||||||
|
type ContextMenuContentEmits,
|
||||||
|
type ContextMenuContentProps,
|
||||||
|
ContextMenuPortal,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuContentProps & { class?: string }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuContentEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuPortal force-mount>
|
||||||
|
<ContextMenuContent
|
||||||
|
:loop="props.loop"
|
||||||
|
:align-offset="props.alignOffset"
|
||||||
|
:as-child="props.asChild"
|
||||||
|
:class="[
|
||||||
|
cn(
|
||||||
|
'bg-background focus:outline-none outline-none border border-border p-1 z-50 min-w-[8rem] rounded-md shadow-md 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',
|
||||||
|
props.class,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenuPortal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ContextMenuGroup, type ContextMenuGroupProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuGroupProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuGroup v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</ContextMenuGroup>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuItem,
|
||||||
|
type ContextMenuItemEmits,
|
||||||
|
type ContextMenuItemProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuItemProps & { class?: string }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuItemEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuItem
|
||||||
|
v-bind="props"
|
||||||
|
:class="[
|
||||||
|
cn(
|
||||||
|
'flex items-center rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:pointer-events-none focus:bg-outline-hover px-2 py-1.5 text-sm outline-none select-none cursor-default',
|
||||||
|
props.class,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
@select="emits('select', $event)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuItem>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ContextMenuLabel, type ContextMenuLabelProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuLabelProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="px-2 py-1.5 text-sm font-semibold">
|
||||||
|
<ContextMenuLabel v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</ContextMenuLabel>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuRadioGroup,
|
||||||
|
type ContextMenuRadioGroupEmits,
|
||||||
|
type ContextMenuRadioGroupProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuRadioGroupProps>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuRadioGroupEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuRadioGroup
|
||||||
|
v-bind="props"
|
||||||
|
@update:model-value="emits('update:modelValue', $event)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuRadioGroup>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuItemIndicator,
|
||||||
|
ContextMenuRadioItem,
|
||||||
|
type ContextMenuRadioItemEmits,
|
||||||
|
type ContextMenuRadioItemProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import RiCheckboxBlankCircleFill from '~icons/ri/checkbox-blank-circle-fill'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuRadioItemProps & { class?: string }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuRadioItemEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuRadioItem
|
||||||
|
v-bind="props"
|
||||||
|
:class="[
|
||||||
|
cn(
|
||||||
|
'flex relative items-center rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[highlighted]:bg-outline-hover pl-7 py-1.5 text-sm outline-none select-none cursor-default',
|
||||||
|
props.class,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
@select="emits('select', $event)"
|
||||||
|
>
|
||||||
|
<ContextMenuItemIndicator
|
||||||
|
class="absolute left-2 inline-flex w-2 h-2 items-center justify-center"
|
||||||
|
>
|
||||||
|
<RiCheckboxBlankCircleFill class="text-foreground" />
|
||||||
|
</ContextMenuItemIndicator>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuRadioItem>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuSeparator,
|
||||||
|
type ContextMenuSeparatorProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuSeparatorProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuSeparator v-bind="props" class="-mx-1 my-1 h-px bg-secondary" />
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div class="text-xxs ml-auto tracking-widest opacity-50">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuSub,
|
||||||
|
type ContextMenuSubEmits,
|
||||||
|
type ContextMenuSubProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuSubProps>()
|
||||||
|
|
||||||
|
const emits = defineEmits<ContextMenuSubEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuSub v-bind="props" @update:open="emits('update:open', $event)">
|
||||||
|
<slot />
|
||||||
|
</ContextMenuSub>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuPortal,
|
||||||
|
ContextMenuSubContent,
|
||||||
|
type DropdownMenuSubContentEmits,
|
||||||
|
type DropdownMenuSubContentProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<DropdownMenuSubContentProps & { class?: string }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuPortal force-mount>
|
||||||
|
<ContextMenuSubContent
|
||||||
|
:loop="props.loop"
|
||||||
|
:align-offset="props.alignOffset"
|
||||||
|
:side="props.side"
|
||||||
|
:side-offset="props.sideOffset"
|
||||||
|
:as-child="props.asChild"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background focus:outline-none outline-none text-foreground border border-border p-1 z-50 min-w-[10rem] rounded-md shadow-md 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',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuSubContent>
|
||||||
|
</ContextMenuPortal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ContextMenuSubTrigger,
|
||||||
|
type ContextMenuSubTriggerProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuSubTriggerProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuSubTrigger
|
||||||
|
v-bind="props"
|
||||||
|
:class="[
|
||||||
|
cn(
|
||||||
|
'flex items-center rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[highlighted]:bg-outline-hover px-2 py-1.5 text-sm outline-none select-none cursor-default',
|
||||||
|
props.class,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ContextMenuSubTrigger>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ContextMenuTrigger, type ContextMenuTriggerProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<ContextMenuTriggerProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContextMenuTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
</template>
|
||||||
14
apps/www/src/lib/registry/default/ui/context-menu/index.ts
Normal file
14
apps/www/src/lib/registry/default/ui/context-menu/index.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export { default as ContextMenu } from './ContextMenu.vue'
|
||||||
|
export { default as ContextMenuTrigger } from './ContextMenuTrigger.vue'
|
||||||
|
export { default as ContextMenuContent } from './ContextMenuContent.vue'
|
||||||
|
export { default as ContextMenuGroup } from './ContextMenuGroup.vue'
|
||||||
|
export { default as ContextMenuRadioGroup } from './ContextMenuRadioGroup.vue'
|
||||||
|
export { default as ContextMenuItem } from './ContextMenuItem.vue'
|
||||||
|
export { default as ContextMenuCheckboxItem } from './ContextMenuCheckboxItem.vue'
|
||||||
|
export { default as ContextMenuRadioItem } from './ContextMenuRadioItem.vue'
|
||||||
|
export { default as ContextMenuShortcut } from './ContextMenuShortcut.vue'
|
||||||
|
export { default as ContextMenuSeparator } from './ContextMenuSeparator.vue'
|
||||||
|
export { default as ContextMenuLabel } from './ContextMenuLabel.vue'
|
||||||
|
export { default as ContextMenuSub } from './ContextMenuSub.vue'
|
||||||
|
export { default as ContextMenuSubTrigger } from './ContextMenuSubTrigger.vue'
|
||||||
|
export { default as ContextMenuSubContent } from './ContextMenuSubContent.vue'
|
||||||
327
apps/www/src/lib/registry/default/ui/data-table/DataTable.vue
Normal file
327
apps/www/src/lib/registry/default/ui/data-table/DataTable.vue
Normal file
|
|
@ -0,0 +1,327 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
type ColumnDef,
|
||||||
|
FlexRender,
|
||||||
|
type SortingState,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useVueTable,
|
||||||
|
} from '@tanstack/vue-table'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import {
|
||||||
|
ArrowDownNarrowWide,
|
||||||
|
ArrowUpDown,
|
||||||
|
ArrowUpNarrowWide,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
ChevronsLeft,
|
||||||
|
ChevronsRight,
|
||||||
|
X,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableEmpty,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '../table'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '../dropdown-menu'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
} from '../select'
|
||||||
|
import { Input } from '../input'
|
||||||
|
import { Button } from '../button'
|
||||||
|
import RadixIconsMixerHorizontal from '~icons/radix-icons/mixer-horizontal'
|
||||||
|
|
||||||
|
interface tableProps {
|
||||||
|
data: any[]
|
||||||
|
columns: ColumnDef<any>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<tableProps>()
|
||||||
|
|
||||||
|
const sorting = ref<SortingState>([])
|
||||||
|
const globalFilter = ref('')
|
||||||
|
const columnVisibility = ref({})
|
||||||
|
const rowSelection = ref({})
|
||||||
|
const pageSizes = computed(() => [10, 20, 30, 40, 50])
|
||||||
|
|
||||||
|
const table = useVueTable({
|
||||||
|
data: props.data,
|
||||||
|
columns: props.columns,
|
||||||
|
state: {
|
||||||
|
get sorting() {
|
||||||
|
return sorting.value
|
||||||
|
},
|
||||||
|
get globalFilter() {
|
||||||
|
return globalFilter.value
|
||||||
|
},
|
||||||
|
get columnVisibility() {
|
||||||
|
return columnVisibility.value
|
||||||
|
},
|
||||||
|
get rowSelection() {
|
||||||
|
return rowSelection.value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSortingChange: (updaterOrValue) => {
|
||||||
|
sorting.value
|
||||||
|
= typeof updaterOrValue === 'function'
|
||||||
|
? updaterOrValue(sorting.value)
|
||||||
|
: updaterOrValue
|
||||||
|
},
|
||||||
|
onGlobalFilterChange: (updaterOrValue) => {
|
||||||
|
globalFilter.value
|
||||||
|
= typeof updaterOrValue === 'function'
|
||||||
|
? updaterOrValue(globalFilter.value)
|
||||||
|
: updaterOrValue
|
||||||
|
},
|
||||||
|
onRowSelectionChange: (updaterOrValue) => {
|
||||||
|
rowSelection.value
|
||||||
|
= typeof updaterOrValue === 'function'
|
||||||
|
? updaterOrValue(rowSelection.value)
|
||||||
|
: updaterOrValue
|
||||||
|
},
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleColumnVisibility(column: any) {
|
||||||
|
columnVisibility.value = {
|
||||||
|
...columnVisibility.value,
|
||||||
|
[column.id]: !column.getIsVisible(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageSize = computed({
|
||||||
|
get() {
|
||||||
|
return table.getState().pagination.pageSize.toString()
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
table.setPageSize(Number(value))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex flex-1 items-center space-x-2">
|
||||||
|
<div class="w-full max-w-[18rem]">
|
||||||
|
<Input
|
||||||
|
v-model:value="globalFilter"
|
||||||
|
placeholder="Filter tasks..."
|
||||||
|
class="h-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
v-if="globalFilter"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-8"
|
||||||
|
@click="globalFilter = ''"
|
||||||
|
>
|
||||||
|
<span class="text-xs">Reset</span>
|
||||||
|
<X class="w-4 h-4 ml-1" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger>
|
||||||
|
<Button variant="outline" class="h-8">
|
||||||
|
<RadixIconsMixerHorizontal class="w-3.5 h-3.5 mr-2" />
|
||||||
|
<span class="text-xs">View</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" class="w-40">
|
||||||
|
<DropdownMenuLabel> Toggle Columns </DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
v-for="column in table
|
||||||
|
.getAllColumns()
|
||||||
|
.filter((column) => !column.getCanHide())"
|
||||||
|
:key="column.id"
|
||||||
|
:checked="column.getIsVisible()"
|
||||||
|
@update:checked="toggleColumnVisibility(column)"
|
||||||
|
>
|
||||||
|
<span class="text-sm">{{ column.columnDef.header }}</span>
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table class="mt-4 rounded-md border border-border">
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow
|
||||||
|
v-for="headerGroup in table.getHeaderGroups()"
|
||||||
|
:key="headerGroup.id"
|
||||||
|
>
|
||||||
|
<TableHead
|
||||||
|
v-for="header in headerGroup.headers"
|
||||||
|
:key="header.id"
|
||||||
|
class="h-10 px-2.5"
|
||||||
|
:col-span="header.colSpan"
|
||||||
|
>
|
||||||
|
<template v-if="!header.isPlaceholder">
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
v-if="header.column.getCanSort()"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-8 -translate-x-3"
|
||||||
|
@click="header.column.getToggleSortingHandler()?.($event)"
|
||||||
|
>
|
||||||
|
<FlexRender
|
||||||
|
:render="header.column.columnDef.header"
|
||||||
|
:props="header.getContext()"
|
||||||
|
/>
|
||||||
|
<ArrowUpDown
|
||||||
|
v-if="
|
||||||
|
header.column.getCanSort() && !header.column.getIsSorted()
|
||||||
|
"
|
||||||
|
class="w-3.5 h-3.5 ml-1.5 text-muted"
|
||||||
|
/>
|
||||||
|
<ArrowUpNarrowWide
|
||||||
|
v-if="header.column.getIsSorted() === 'asc'"
|
||||||
|
class="w-3.5 h-3.5 ml-1.5 text-muted"
|
||||||
|
/>
|
||||||
|
<ArrowDownNarrowWide
|
||||||
|
v-if="header.column.getIsSorted() === 'desc'"
|
||||||
|
class="w-3.5 h-3.5 ml-1.5 text-muted"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-center h-8 text-foreground"
|
||||||
|
>
|
||||||
|
<FlexRender
|
||||||
|
:render="header.column.columnDef.header"
|
||||||
|
:props="header.getContext()"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
<TableRow
|
||||||
|
v-for="row in table.getRowModel().rows"
|
||||||
|
:key="row.id"
|
||||||
|
:class="row.getIsSelected() ? 'bg-outline-hover' : ''"
|
||||||
|
>
|
||||||
|
<TableCell
|
||||||
|
v-for="cell in row.getVisibleCells()"
|
||||||
|
:key="cell.id"
|
||||||
|
class="p-2.5"
|
||||||
|
>
|
||||||
|
<FlexRender
|
||||||
|
:render="cell.column.columnDef.cell"
|
||||||
|
:props="cell.getContext()"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableEmpty
|
||||||
|
v-if="table.getRowModel().rows.length === 0"
|
||||||
|
:colspan="table.getAllLeafColumns().length"
|
||||||
|
>
|
||||||
|
No data available.
|
||||||
|
</TableEmpty>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end px-2 my-6">
|
||||||
|
<div class="flex-1 text-sm text-muted">
|
||||||
|
<span>
|
||||||
|
{{ table.getFilteredSelectedRowModel().rows.length }} of {{ " " }}
|
||||||
|
{{ table.getFilteredRowModel().rows.length }} row(s) selected
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-6 lg:space-x-8">
|
||||||
|
<div class="md:flex items-center space-x-2 hidden">
|
||||||
|
<p class="text-sm font-medium text-foreground">
|
||||||
|
Rows per page:
|
||||||
|
</p>
|
||||||
|
<Select v-model="pageSize">
|
||||||
|
<SelectTrigger class="h-8 w-[70px]">
|
||||||
|
{{ table.getState().pagination.pageSize }}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent side="top" align="start">
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem
|
||||||
|
v-for="pageSize in pageSizes"
|
||||||
|
:key="pageSize"
|
||||||
|
:value="`${pageSize}`"
|
||||||
|
>
|
||||||
|
{{ pageSize }}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex text-foreground items-center justify-center text-sm font-medium"
|
||||||
|
>
|
||||||
|
Page {{ table.getState().pagination.pageIndex + 1 }} of
|
||||||
|
{{ table.getPageCount() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
title="First page"
|
||||||
|
class="h-8 w-8 p-0"
|
||||||
|
:disabled="!table.getCanPreviousPage()"
|
||||||
|
@click="table.setPageIndex(0)"
|
||||||
|
>
|
||||||
|
<ChevronsLeft class="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
title="Previous page"
|
||||||
|
class="h-8 w-8 p-0"
|
||||||
|
:disabled="!table.getCanPreviousPage()"
|
||||||
|
@click="table.previousPage()"
|
||||||
|
>
|
||||||
|
<ChevronLeft class="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
title="Next page"
|
||||||
|
class="h-8 w-8 p-0"
|
||||||
|
:disabled="!table.getCanNextPage()"
|
||||||
|
@click="table.nextPage()"
|
||||||
|
>
|
||||||
|
<ChevronRight class="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
title="Last page"
|
||||||
|
class="h-8 w-8 p-0"
|
||||||
|
:disabled="!table.getCanNextPage()"
|
||||||
|
@click="table.setPageIndex(table.getPageCount() - 1)"
|
||||||
|
>
|
||||||
|
<ChevronsRight class="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
1
apps/www/src/lib/registry/default/ui/data-table/index.ts
Normal file
1
apps/www/src/lib/registry/default/ui/data-table/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as DataTable } from './DataTable.vue'
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useDark } from '@vueuse/core'
|
||||||
|
import { DatePicker } from 'v-calendar'
|
||||||
|
import 'v-calendar/style.css'
|
||||||
|
|
||||||
|
const isDark = useDark()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DatePicker :is-dark="isDark" borderless trim-weeks expanded />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--vc-font-family: "Inter", sans-serif;
|
||||||
|
--vc-rounded-full: var(--radius);
|
||||||
|
--vc-font-bold: 500;
|
||||||
|
--vc-font-semibold: 600;
|
||||||
|
--vc-text-lg: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-light,
|
||||||
|
.vc-dark {
|
||||||
|
--vc-bg: var(--background);
|
||||||
|
--vc-border: var(--border);
|
||||||
|
--vc-focus-ring: 0 0 0 3px rgba(0, 0, 0, 0.2);
|
||||||
|
--vc-weekday-color: var(--muted);
|
||||||
|
--vc-popover-content-color: var(--muted);
|
||||||
|
--vc-popover-content-bg: var(--background);
|
||||||
|
--vc-popover-content-border: var(--border);
|
||||||
|
|
||||||
|
&.vc-attr,
|
||||||
|
& .vc-attr {
|
||||||
|
--vc-content-color: var(--primary);
|
||||||
|
--vc-highlight-outline-bg: var(--primary);
|
||||||
|
--vc-highlight-outline-border: var(--primary);
|
||||||
|
--vc-highlight-outline-content-color: var(--primary-foreground);
|
||||||
|
--vc-highlight-light-bg: var(
|
||||||
|
--vc-accent-200
|
||||||
|
); /* Highlighted color between two dates */
|
||||||
|
--vc-highlight-light-content-color: var(--secondary-foreground);
|
||||||
|
--vc-highlight-solid-bg: var(--primary);
|
||||||
|
--vc-highlight-solid-content-color: var(--primary-foreground);
|
||||||
|
--vc-dot-bg: var(--primary);
|
||||||
|
--vc-bar-bg: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-blue {
|
||||||
|
--vc-accent-200: var(--secondary);
|
||||||
|
--vc-accent-400: var(--primary);
|
||||||
|
--vc-accent-500: var(--primary);
|
||||||
|
--vc-accent-600: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.vc-blue {
|
||||||
|
--vc-accent-200: var(--secondary);
|
||||||
|
--vc-accent-400: var(--primary);
|
||||||
|
--vc-accent-500: var(--secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as DatePicker } from './DatePicker.vue'
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
import { defineComponent, h } from 'vue'
|
|
||||||
import {
|
|
||||||
DialogClose, type DialogContentEmits, DialogContent as DialogContentPrimitive, type DialogContentProps,
|
|
||||||
DialogDescription as DialogDescriptionPrimitive, type DialogDescriptionProps,
|
|
||||||
DialogOverlay as DialogOverlayPrimitive, type DialogOverlayProps,
|
|
||||||
DialogPortal as DialogPortalPrimitive, DialogRoot,
|
|
||||||
DialogTitle as DialogTitlePrimitive, type DialogTitleProps,
|
|
||||||
DialogTrigger as DialogTriggerPrimitive,
|
|
||||||
} from 'radix-vue'
|
|
||||||
import { X } from 'lucide-vue-next'
|
|
||||||
import type { ParseEmits } from '@/utils'
|
|
||||||
import { cn, useEmitAsProps } from '@/utils'
|
|
||||||
|
|
||||||
export const Dialog = DialogRoot
|
|
||||||
export const DialogTrigger = DialogTriggerPrimitive
|
|
||||||
export const DialogPortal = DialogPortalPrimitive
|
|
||||||
|
|
||||||
export const DialogOverlay = defineComponent<DialogOverlayProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(DialogOverlayPrimitive,
|
|
||||||
{ class: cn('fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'DialogOverlay' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const DialogContent = defineComponent<DialogContentProps, ParseEmits<DialogContentEmits>>(
|
|
||||||
(props, { emit, attrs, slots }) => {
|
|
||||||
const emitsAsProps = useEmitAsProps(emit)
|
|
||||||
|
|
||||||
return () => h(DialogPortal, () => [
|
|
||||||
h(DialogOverlay),
|
|
||||||
h(DialogContentPrimitive, {
|
|
||||||
class: cn('fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', attrs.class ?? ''),
|
|
||||||
...props,
|
|
||||||
...emitsAsProps,
|
|
||||||
}, () => [
|
|
||||||
slots.default(),
|
|
||||||
h(DialogClose,
|
|
||||||
{ class: 'absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground' },
|
|
||||||
() => [h(X, { class: 'h-4 w-4' }), h('span', { class: 'sr-only' }, 'Close')],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}, { name: 'DialogContent', emits: DialogContentPrimitive.emits },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const DialogHeader = defineComponent(
|
|
||||||
(_props, { attrs, slots }) => {
|
|
||||||
return () => h('div',
|
|
||||||
{ class: cn('flex flex-col space-y-1.5 text-center sm:text-left', attrs.class) },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'DialogHeader' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const DialogFooter = defineComponent(
|
|
||||||
(_props, { attrs, slots }) => {
|
|
||||||
return () => h('div',
|
|
||||||
{ class: cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', attrs.class) },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'DialogFooter' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const DialogTitle = defineComponent<DialogTitleProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(DialogTitlePrimitive,
|
|
||||||
{ class: cn('text-lg font-semibold leading-none tracking-tight', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'DialogTitle' },
|
|
||||||
)
|
|
||||||
|
|
||||||
export const DialogDescription = defineComponent<DialogDescriptionProps>(
|
|
||||||
(props, { attrs, slots }) => {
|
|
||||||
return () => h(DialogDescriptionPrimitive,
|
|
||||||
{ class: cn('text-lg font-semibold leading-none tracking-tight', attrs.class), ...props },
|
|
||||||
slots,
|
|
||||||
)
|
|
||||||
}, { name: 'DialogDescription' },
|
|
||||||
)
|
|
||||||
9
apps/www/src/lib/registry/default/ui/dialog/Dialog.vue
Normal file
9
apps/www/src/lib/registry/default/ui/dialog/Dialog.vue
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { DialogRoot } from 'radix-vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogRoot>
|
||||||
|
<slot />
|
||||||
|
</DialogRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
type DialogContentProps,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { X } from 'lucide-vue-next'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<DialogContentProps & { class?: string }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay
|
||||||
|
class="fixed inset-0 z-50 bg-white/80 dark:bg-gray-950/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||||
|
/>
|
||||||
|
<DialogContent
|
||||||
|
:as-child="props.asChild"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<DialogClose
|
||||||
|
class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
||||||
|
>
|
||||||
|
<X class="w-4 h-4 text-muted" />
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogPortal>
|
||||||
|
</template>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user