贡献
了解如何为 shadcn/vue 贡献代码。
简介
感谢您对 shadcn-vue.com 的贡献。我们很高兴您能来这里。
在提交您的第一个拉取请求之前,请花点时间查看这份文档。我们强烈建议您检查开放的问题和拉取请求,看看是否有其他人正在做类似的事情。
如果您需要任何帮助,请随时在 Discord 上联系核心团队。
本指南提供了详细的信息,帮助新贡献者。
关于这个仓库
这个仓库是一个单仓库。
- 我们在开发中使用 pnpm 和
workspaces
。
项目结构
GitHub 仓库由几个文件夹组成。这里有一个快速视图。
packages - 包含支持
nuxt
作为模块和cli
添加新组件的源代码。apps/www - 主文件夹,包含网站和所有
shadcn/vue
组件的源代码。此文件夹包含重要的子文件夹,并且是一个带有自己的package.json
的子项目。.vitepress - 包含
vitepress
和shadcn/vue
网站的配置和源代码。src - 托管所有
shadcn/vue
组件或演示以及它们在网站上的文档的源代码。__registry__ - 保存由
scripts/build-registry.ts
生成的注册文件,以用于为cli
提供组件。此文件夹的内容是自动生成的,不应手动编辑。scripts - 包含各种辅助脚本,如
build-registry.ts
,它会自动生成__registry__
文件夹。content - 此文件夹包含
/docs
路由的所有文档。每个组件都有一个.md
文件,记录组件的安装和使用。examples - 包含所有不在
/docs
中的示例,例如 主页。lib/registry - 主文件夹包含每个组件不同样式的源代码。这可能是您要更改的主要文件夹。
我们支持 shadcn/vue
中每个组件的两种不同样式
- 默认
- 纽约
添加到仓库的每个组件都必须支持这两个版本,包括主要源代码和相关的演示。
添加或修改组件时,请确保
- 您对每种样式都进行了更改。
- 您更新了文档。
- 您运行了
pnpm build:registry
以更新注册表。
开发
首先克隆仓库
git clone [email protected]:radix-vue/shadcn-vue.git
安装依赖项
pnpm install
运行工作区
您可以使用 pnpm --filter=[WORKSPACE]
命令来启动工作区或我们设置的一些快捷命令的开发过程。
示例
- 要运行
shadcn-vue.com
网站
pnpm dev
- 要运行
shadcn-vue
cli 包
pnpm dev:cli
文档
此项目的文档位于 www
工作区中。您可以通过运行以下命令在本地运行文档
pnpm dev
文档使用 md 编写。您可以在 apps/www/src/content
目录中找到文档文件。
CLI
shadcn-vue
包是一个用于将组件添加到项目的 CLI。您可以在 此处 找到 CLI 的文档。
对 CLI 的任何更改都应在 packages/cli
目录中进行。如果您能做到,最好为您的更改添加测试。
测试
测试使用 Vitest 编写。您可以从仓库根目录运行所有测试。
pnpm test
提交拉取请求时,请确保测试通过。如果您添加了新功能,请包含测试。
提交约定
在创建拉取请求之前,请检查您的提交是否符合此仓库中使用的提交约定。
创建提交时,我们恳请您在提交消息中遵循约定 category(scope or module): message
,同时使用以下类别之一
feat / feature
:所有引入全新代码或新功能的更改fix
:修复错误的更改(理想情况下,您还会参考现有的问题)refactor
:任何与代码相关的更改,既不是修复也不是功能docs
:更改现有文档或创建新文档(例如 README、库使用文档或 cli 使用文档)build
:所有与软件构建相关的更改,依赖项更改或添加新依赖项test
:所有与测试相关的更改(添加新测试或更改现有测试)ci
:所有与持续集成配置相关的更改(例如 github actions、ci 系统)chore
:所有不属于上述任何类别的仓库更改例如
feat(components): add new prop to the avatar component
如果您有兴趣了解详细规范,您可以访问 Conventional Commits。
SFC - 单文件组件
多个组件集成到 shadcn/ui
(shadcn
的 React 版本)中的一个文件中,而 Vue 每个文件只支持一个组件,因此称为单文件组件 (SFC)。在这种情况下,您需要为每个组件部分创建单独的文件,然后在 index.ts
文件中导出它们。
以 Accordion
源代码为例。
包装 Radix-Vue 组件
Radix-Vue 托管许多用于构建可重用组件的低级 UI 组件。在许多情况下,您需要包装 Radix-Vue
组件。
属性和事件
所有 Radix-Vue
组件都公开其属性和发射类型。我们需要转发来自外部的任何属性/事件到 Radix-Vue
组件。
为此,我们有一个名为 useForwardPropsEmits
的辅助函数,它组合必须绑定到子 radix 组件的属性和事件。
更明确地说,函数 useForwardPropsEmits
接收属性和可选的发射函数,并返回一个计算对象,该对象将解析后的属性和发射组合为属性。
以下是 Accordion
根组件的示例。
<script setup lang="ts">
import {
AccordionRoot,
type AccordionRootEmits,
type AccordionRootProps,
useForwardPropsEmits,
} from 'radix-vue'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot />
</AccordionRoot>
</template>
如您所见,AccordionRootEmits
和 AccordionRootProps
类型从 radix 导入,与 useForwardPropsEmits
组合,然后使用 v-bind
语法绑定。
CSS 类
在某些情况下,我们希望在 shadcn/vue
组件中接受 class
作为属性,然后通过 cn
实用程序函数将其与 radix-vue
组件上的默认 tailwind 类组合起来。
在这种情况下,我们不能使用 v-bind
,因为这会导致 双重类绑定。
看一下 DrawerDescription.vue
。
<script lang="ts" setup>
import type { DrawerDescriptionProps } from 'vaul-vue'
import { DrawerDescription } from 'vaul-vue'
import { type HtmlHTMLAttributes, computed } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<DrawerDescriptionProps & { class?: HtmlHTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<DrawerDescription v-bind="delegatedProps" :class="cn('text-sm text-muted-foreground', props.class)">
<slot />
</DrawerDescription>
</template>
如您所见,我们创建了一个名为 delegatedProps
的计算属性,从属性中删除 class
,然后将返回值绑定到我们的 radix 组件(在本例中为 DrawerDescription
)。
至于我们的类,我们首先将其声明为 HtmlHTMLAttributes['class']
类型,并使用 cn
合并来自 class
属性的 tailwind 类和我们自己的类。
此模式仅在需要 cn
实用程序时才需要应用。在不需要将默认的 Tailwind 类与用户提供的类合并的情况下,此模式是不必要的。SelectValue.vue
组件就是一个很好的例子。
<script setup lang="ts">
import { SelectValue, type SelectValueProps } from 'radix-vue'
const props = defineProps<SelectValueProps>()
</script>
<template>
<SelectValue v-bind="props">
<slot />
</SelectValue>
</template>
布尔属性
当您为组件构建包装器时,在某些情况下您可能希望忽略 Vue Props 布尔值转换。您可以将所有布尔字段的默认值设置为 undefined,或者使用 useForwardProps
可组合函数。
查看 AccordionItem.vue
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<AccordionItem
v-bind="forwardedProps"
:class="cn('border-b', props.class)"
>
<slot />
</AccordionItem>
</template>
由于 AccordionItemProps
类型至少有一个布尔属性,因此我们需要在整个 props 对象上使用 useForwardProps
。
请注意,useForwardPropsEmits
在幕后使用了 useForwardProps
。
组件作为根节点
当您的根组件是来自 vue 的 Component
原生组件时,使用 Primitive
会更方便。
让我们看一下 Button.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { type ButtonVariants, buttonVariants } from '.'
import { cn } from '@/lib/utils'
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
class?: HTMLAttributes['class']
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</Primitive>
</template>
您需要在您的 props 中扩展 PrimitiveProps
以支持 Primitive
组件。在大多数情况下,您还需要为 as
属性设置默认值。
使用 shadcn/ui
更新
shadcn/vue
是 shadcn/ui
的非官方社区主导的 Vue 端口,随着时间的推移,它们可能会失去同步。
截至今天,我们与 shadcn/ui
的这个 提交 保持同步。
点击以下链接检查是否有我们应该同步的较新提交。
- 没有变化 - 如果您看到“没有要比较的内容”,则无需执行任何操作,因为我们已与最新版本同步。
- 如果有变化,您应该查看这些变化并尝试将它们应用到
shadcn/vue
代码库中,并创建一个 PR,请记住在 此文件 中更新latestSyncCommitTag
。
调试
以下是一些工具和技术,可以帮助您在为 shadcn/vue
做贡献或开发自己的项目时更有效地进行调试。
安装 Vue Dev Tools
为了轻松检查组件 props、属性、事件等等,您可以利用浏览器上的 Vue DevTools
扩展。此扩展为调试 Vue 组件提供了一个用户友好的界面,可以改善您的开发体验。
启用自定义格式化程序
Vue 以一种方式包装存储在 ref
中的值,当记录时,会导致嵌套对象,并且需要手动检查才能访问存储在 ref 中的值。
您可以在浏览器中启用自定义格式化程序来自动执行此过程。