refactor: 重构 components 子包组件目录和导出结构

This commit is contained in:
Hooray
2026-06-24 16:38:56 +08:00
parent de379dc904
commit 68539bee0d
89 changed files with 732 additions and 746 deletions
@@ -45,14 +45,6 @@ const routes: RouteRecordRaw = {
title: '按钮',
},
},
{
path: 'button_group',
name: 'componentExampleButtonGroup',
component: () => import('@/views/component_example/button_group.vue'),
meta: {
title: '按钮组',
},
},
{
path: 'card',
name: 'componentExampleCard',
@@ -69,14 +61,6 @@ const routes: RouteRecordRaw = {
title: '复选框',
},
},
{
path: 'checkbox_group',
name: 'componentExampleCheckboxGroup',
component: () => import('@/views/component_example/checkbox_group.vue'),
meta: {
title: '复选框组',
},
},
{
path: 'collapsible',
name: 'componentExampleCollapsible',
@@ -4,7 +4,7 @@ import { button } from '@fantastic-admin/components/examples'
<template>
<div>
<FaPageHeader title="按钮" description="FaButton" />
<FaPageHeader title="按钮" description="FaButton / FaButtonGroup" />
<FaPageMain
v-for="example in button"
:key="example.title"
@@ -1,17 +0,0 @@
<script setup lang="ts">
import { buttonGroup } from '@fantastic-admin/components/examples'
</script>
<template>
<div>
<FaPageHeader title="按钮组" description="FaButtonGroup" />
<FaPageMain
v-for="example in buttonGroup"
:key="example.title"
:code="example.componentRaw"
:title="example.title"
>
<component :is="example.component" />
</FaPageMain>
</div>
</template>
@@ -4,7 +4,7 @@ import { checkbox } from '@fantastic-admin/components/examples'
<template>
<div>
<FaPageHeader title="复选框" description="FaCheckbox" />
<FaPageHeader title="复选框" description="FaCheckbox / FaCheckboxGroup" />
<FaPageMain
v-for="example in checkbox"
:key="example.title"
@@ -1,17 +0,0 @@
<script setup lang="ts">
import { checkboxGroup } from '@fantastic-admin/components/examples'
</script>
<template>
<div>
<FaPageHeader title="复选框组" description="FaCheckboxGroup" />
<FaPageMain
v-for="example in checkboxGroup"
:key="example.title"
:code="example.componentRaw"
:title="example.title"
>
<component :is="example.component" />
</FaPageMain>
</div>
</template>
@@ -1,20 +1,12 @@
<script setup lang="ts">
import { kbd, kbdGroup } from '@fantastic-admin/components/examples'
const examples = [
...kbd,
...kbdGroup.map(example => ({
...example,
title: '组合',
})),
]
import { kbd } from '@fantastic-admin/components/examples'
</script>
<template>
<div>
<FaPageHeader title="键盘" description="FaKbd" />
<FaPageHeader title="键盘" description="FaKbd / FaKbdGroup" />
<FaPageMain
v-for="example in examples"
v-for="example in kbd"
:key="example.title"
:code="example.componentRaw"
:title="example.title"
@@ -0,0 +1 @@
export { default as Alert } from './index.vue'
@@ -0,0 +1 @@
export { default as Avatar } from './index.vue'
@@ -0,0 +1 @@
export { default as Badge } from './index.vue'
@@ -1,26 +0,0 @@
# FaButtonGroup 按钮组
将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
## 使用场景
- 将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
- 常见用法:垂直排列、带分割线、工具栏按钮组、垂直菜单。
## Props
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| orientation | 排列方向 | `'horizontal' \| 'vertical'` | `'horizontal'` |
| separator | 是否显示分割线 | `boolean` | `false` |
| class | 自定义类名 | `HTMLAttributes['class']` | `undefined` |
## Slots
| 名称 | 说明 |
|------|------|
| default | 默认插槽,放置 FaButton 组件 |
## 注意事项
- 无特殊限制,建议按示例中的受控方式接入,并结合业务容器尺寸验证最终展示效果。
@@ -1,38 +0,0 @@
import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
import Nested from './_nested.vue'
import NestedRaw from './_nested.vue?raw'
import Separator from './_separator.vue'
import SeparatorRaw from './_separator.vue?raw'
import Vertical from './_vertical.vue'
import VerticalRaw from './_vertical.vue?raw'
import WithComponents from './_with-components.vue'
import WithComponentsRaw from './_with-components.vue?raw'
export default [
{
title: '基础',
component: Basic,
componentRaw: BasicRaw,
},
{
title: '垂直',
component: Vertical,
componentRaw: VerticalRaw,
},
{
title: '分割线',
component: Separator,
componentRaw: SeparatorRaw,
},
{
title: '嵌套',
component: Nested,
componentRaw: NestedRaw,
},
{
title: '与其他组件组合',
component: WithComponents,
componentRaw: WithComponentsRaw,
},
]
@@ -41,3 +41,30 @@
- `ghost`:幽灵按钮,用于更次要的操作
- `link`:链接样式
- `expand`:展开动画效果按钮
## FaButtonGroup 按钮组
将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
## 使用场景
- 将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
- 常见用法:垂直排列、带分割线、工具栏按钮组、垂直菜单。
## Props
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| orientation | 排列方向 | `'horizontal' \| 'vertical'` | `'horizontal'` |
| separator | 是否显示分割线 | `boolean` | `false` |
| class | 自定义类名 | `HTMLAttributes['class']` | `undefined` |
## Slots
| 名称 | 说明 |
|------|------|
| default | 默认插槽,放置 FaButton 组件 |
## 注意事项
- 无特殊限制,建议按示例中的受控方式接入,并结合业务容器尺寸验证最终展示效果。
@@ -1,8 +1,8 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../../button/index.vue'
import FaIcon from '../../icon/index.vue'
import FaButtonGroup from '../index.vue'
import FaButtonGroup from '../ButtonGroup.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../index.vue'
</script>
<template>
@@ -1,8 +1,8 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../../button/index.vue'
import FaIcon from '../../icon/index.vue'
import FaButtonGroup from '../index.vue'
import FaButtonGroup from '../ButtonGroup.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../index.vue'
</script>
<template>
@@ -1,8 +1,8 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../../button/index.vue'
import FaIcon from '../../icon/index.vue'
import FaButtonGroup from '../index.vue'
import FaButtonGroup from '../ButtonGroup.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../index.vue'
</script>
<template>
@@ -1,8 +1,8 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../../button/index.vue'
import FaIcon from '../../icon/index.vue'
import FaButtonGroup from '../index.vue'
import FaButtonGroup from '../ButtonGroup.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../index.vue'
</script>
<template>
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../../button/index.vue'
import FaDropdown from '../../dropdown/index.vue'
import FaIcon from '../../icon/index.vue'
import FaInput from '../../input/index.vue'
import FaSelect from '../../select/index.vue'
import FaButtonGroup from '../index.vue'
import FaButtonGroup from '../ButtonGroup.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaButton from '../index.vue'
const currency = shallowRef('CNY')
</script>
@@ -2,6 +2,16 @@ import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
import Disabled from './_disabled.vue'
import DisabledRaw from './_disabled.vue?raw'
import GroupBasic from './_group-basic.vue'
import GroupBasicRaw from './_group-basic.vue?raw'
import GroupNested from './_group-nested.vue'
import GroupNestedRaw from './_group-nested.vue?raw'
import GroupSeparator from './_group-separator.vue'
import GroupSeparatorRaw from './_group-separator.vue?raw'
import GroupVertical from './_group-vertical.vue'
import GroupVerticalRaw from './_group-vertical.vue?raw'
import GroupWithComponents from './_group-with-components.vue'
import GroupWithComponentsRaw from './_group-with-components.vue?raw'
import Loading from './_loading.vue'
import LoadingRaw from './_loading.vue?raw'
import Size from './_size.vue'
@@ -35,4 +45,29 @@ export default [
component: Loading,
componentRaw: LoadingRaw,
},
{
title: '按钮组 - 基础',
component: GroupBasic,
componentRaw: GroupBasicRaw,
},
{
title: '按钮组 - 垂直',
component: GroupVertical,
componentRaw: GroupVerticalRaw,
},
{
title: '按钮组 - 分割线',
component: GroupSeparator,
componentRaw: GroupSeparatorRaw,
},
{
title: '按钮组 - 嵌套',
component: GroupNested,
componentRaw: GroupNestedRaw,
},
{
title: '按钮组 - 与其他组件组合',
component: GroupWithComponents,
componentRaw: GroupWithComponentsRaw,
},
]
@@ -0,0 +1,2 @@
export { default as ButtonGroup } from './ButtonGroup.vue'
export { default as Button } from './index.vue'
@@ -0,0 +1 @@
export { default as Card } from './index.vue'
@@ -1,61 +0,0 @@
# FaCheckboxGroup 复选框组
基于选项数组的复选框组组件,支持描述文案、自定义布局样式和选项插槽扩展。
## 使用场景
- 表单中的多选配置项
- 多标签偏好选择
- 权限点批量选择
- 带说明文案的多选列表
- 需要卡片式多选交互的场景
## Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `options` | `CheckboxGroupOption[]` | **必需** | 复选框组选项数据 |
| `disabled` | `boolean` | `false` | 是否整体禁用 |
| `min` | `number` | - | 最少选中数量,达到后已选项会被禁用 |
| `max` | `number` | - | 最多选中数量,达到后未选项会被禁用 |
| `dir` | `'ltr' \| 'rtl'` | - | 文本方向 |
| `class` | `HTMLAttributes['class']` | - | 组容器类名 |
| `optionClass` | `HTMLAttributes['class']` | - | 每个选项外层容器类名 |
| `itemClass` | `HTMLAttributes['class']` | - | 复选框本体类名 |
| `labelClass` | `HTMLAttributes['class']` | - | 标签内容类名 |
### CheckboxGroupOption 接口
```ts
export interface CheckboxGroupOption {
label: string
value: AcceptableValue
description?: string
disabled?: boolean
id?: string
}
```
## Slots
| 名称 | 说明 |
|------|------|
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }`,`option` 会保留 `options` 中的扩展字段类型 |
## Events
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| `change` | 选中值变化时触发 | `value: AcceptableValue[]` |
通过 `v-model` 实现双向绑定。
## 注意事项
1. **数据驱动**:组选项必须通过 `options` 传入,不再使用多个 `FaCheckbox` 手工拼接。
2. **v-model 类型**:组件通过 `v-model` 管理选中值数组。
3. **禁用优先级**:组件整体 `disabled` 为 `true` 时,会覆盖单项可用状态。
4. **数量限制**:当选中数量小于等于 `min` 时,已选项会被禁用;当选中数量大于等于 `max` 时,未选项会被禁用。
5. **布局控制**:通过 `class` 和 `optionClass` 自定义多列或卡片式排列,不再提供 `orientation`。
6. **描述文案布局**:存在 `description` 时,选项会自动切换为顶部对齐布局。
7. **自定义插槽**:使用 `option` 插槽后,默认复选框本体会隐藏,需要由插槽内容自行承担完整视觉表现。
@@ -1,38 +0,0 @@
import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
import CustomOption from './_custom-option.vue'
import CustomOptionRaw from './_custom-option.vue?raw'
import Description from './_description.vue'
import DescriptionRaw from './_description.vue?raw'
import Disabled from './_disabled.vue'
import DisabledRaw from './_disabled.vue?raw'
import Limit from './_limit.vue'
import LimitRaw from './_limit.vue?raw'
export default [
{
title: '基础',
component: Basic,
componentRaw: BasicRaw,
},
{
title: '描述',
component: Description,
componentRaw: DescriptionRaw,
},
{
title: '禁用',
component: Disabled,
componentRaw: DisabledRaw,
},
{
title: '数量限制',
component: Limit,
componentRaw: LimitRaw,
},
{
title: '自定义选项内容',
component: CustomOption,
componentRaw: CustomOptionRaw,
},
]
@@ -4,8 +4,8 @@ import type { HTMLAttributes } from 'vue'
import { useTextDirection } from '@vueuse/core'
import { computed, useId, watch } from 'vue'
import { cn } from '#utils'
import { Checkbox } from '../checkbox/checkbox'
import { Label } from '../label/label'
import { Checkbox } from './checkbox'
export interface CheckboxGroupOption {
label: string
@@ -40,3 +40,65 @@
3. **标签点击**:点击标签文字也能触发复选框状态切换。
4. **空标签隐藏**:当不提供默认插槽内容时,标签元素会自动隐藏。
5. **禁用状态**:禁用状态下复选框和标签都无法点击。
## FaCheckboxGroup 复选框组
基于选项数组的复选框组组件,支持描述文案、自定义布局样式和选项插槽扩展。
## 使用场景
- 表单中的多选配置项
- 多标签偏好选择
- 权限点批量选择
- 带说明文案的多选列表
- 需要卡片式多选交互的场景
## Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `options` | `CheckboxGroupOption[]` | **必需** | 复选框组选项数据 |
| `disabled` | `boolean` | `false` | 是否整体禁用 |
| `min` | `number` | - | 最少选中数量,达到后已选项会被禁用 |
| `max` | `number` | - | 最多选中数量,达到后未选项会被禁用 |
| `dir` | `'ltr' \| 'rtl'` | - | 文本方向 |
| `class` | `HTMLAttributes['class']` | - | 组容器类名 |
| `optionClass` | `HTMLAttributes['class']` | - | 每个选项外层容器类名 |
| `itemClass` | `HTMLAttributes['class']` | - | 复选框本体类名 |
| `labelClass` | `HTMLAttributes['class']` | - | 标签内容类名 |
### CheckboxGroupOption 接口
```ts
export interface CheckboxGroupOption {
label: string
value: AcceptableValue
description?: string
disabled?: boolean
id?: string
}
```
## Slots
| 名称 | 说明 |
|------|------|
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }`,`option` 会保留 `options` 中的扩展字段类型 |
## Events
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| `change` | 选中值变化时触发 | `value: AcceptableValue[]` |
通过 `v-model` 实现双向绑定。
## 注意事项
1. **数据驱动**:组选项必须通过 `options` 传入,不再使用多个 `FaCheckbox` 手工拼接。
2. **v-model 类型**:组件通过 `v-model` 管理选中值数组。
3. **禁用优先级**:组件整体 `disabled` 为 `true` 时,会覆盖单项可用状态。
4. **数量限制**:当选中数量小于等于 `min` 时,已选项会被禁用;当选中数量大于等于 `max` 时,未选项会被禁用。
5. **布局控制**:通过 `class` 和 `optionClass` 自定义多列或卡片式排列,不再提供 `orientation`。
6. **描述文案布局**:存在 `description` 时,选项会自动切换为顶部对齐布局。
7. **自定义插槽**:使用 `option` 插槽后,默认复选框本体会隐藏,需要由插槽内容自行承担完整视觉表现。
@@ -2,7 +2,7 @@
import { computed, shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaCheckboxGroup from '../index.vue'
import FaCheckboxGroup from '../CheckboxGroup.vue'
const value = shallowRef(['music'])
@@ -2,7 +2,7 @@
import { shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaCheckboxGroup from '../index.vue'
import FaCheckboxGroup from '../CheckboxGroup.vue'
const value = shallowRef(['focus', 'balanced'])
@@ -2,7 +2,7 @@
import { computed, shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaCheckboxGroup from '../index.vue'
import FaCheckboxGroup from '../CheckboxGroup.vue'
const value = shallowRef(['dashboard', 'report'])
@@ -2,7 +2,7 @@
import { computed, shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaCheckboxGroup from '../index.vue'
import FaCheckboxGroup from '../CheckboxGroup.vue'
const value = shallowRef(['basic'])
@@ -2,7 +2,7 @@
import { computed, shallowRef } from 'vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaCheckboxGroup from '../index.vue'
import FaCheckboxGroup from '../CheckboxGroup.vue'
const value = shallowRef(['dashboard'])
@@ -4,6 +4,16 @@ import CustomStyle from './_custom-style.vue'
import CustomStyleRaw from './_custom-style.vue?raw'
import Disabled from './_disabled.vue'
import DisabledRaw from './_disabled.vue?raw'
import GroupBasic from './_group-basic.vue'
import GroupBasicRaw from './_group-basic.vue?raw'
import GroupCustomOption from './_group-custom-option.vue'
import GroupCustomOptionRaw from './_group-custom-option.vue?raw'
import GroupDescription from './_group-description.vue'
import GroupDescriptionRaw from './_group-description.vue?raw'
import GroupDisabled from './_group-disabled.vue'
import GroupDisabledRaw from './_group-disabled.vue?raw'
import GroupLimit from './_group-limit.vue'
import GroupLimitRaw from './_group-limit.vue?raw'
import Indeterminate from './_indeterminate.vue'
import IndeterminateRaw from './_indeterminate.vue?raw'
@@ -13,19 +23,44 @@ export default [
component: Basic,
componentRaw: BasicRaw,
},
{
title: '半选状态',
component: Indeterminate,
componentRaw: IndeterminateRaw,
},
{
title: '禁用',
component: Disabled,
componentRaw: DisabledRaw,
},
{
title: '半选',
component: Indeterminate,
componentRaw: IndeterminateRaw,
},
{
title: '自定义样式',
component: CustomStyle,
componentRaw: CustomStyleRaw,
},
{
title: '复选框组 - 基础',
component: GroupBasic,
componentRaw: GroupBasicRaw,
},
{
title: '复选框组 - 描述',
component: GroupDescription,
componentRaw: GroupDescriptionRaw,
},
{
title: '复选框组 - 禁用',
component: GroupDisabled,
componentRaw: GroupDisabledRaw,
},
{
title: '复选框组 - 数量限制',
component: GroupLimit,
componentRaw: GroupLimitRaw,
},
{
title: '复选框组 - 自定义选项',
component: GroupCustomOption,
componentRaw: GroupCustomOptionRaw,
},
]
@@ -0,0 +1,2 @@
export { default as CheckboxGroup } from './CheckboxGroup.vue'
export { default as Checkbox } from './index.vue'
@@ -0,0 +1 @@
export { default as Collapsible } from './index.vue'
@@ -0,0 +1 @@
export { default as ContextMenu } from './index.vue'
@@ -0,0 +1,2 @@
export { default as Descriptions } from './index.vue'
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './index.vue'
@@ -0,0 +1 @@
export { default as Divider } from './index.vue'
+3 -135
View File
@@ -1,135 +1,3 @@
import type { Component, HTMLAttributes, VNode } from 'vue'
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
import Drawer from './index.vue'
export interface DrawerProps {
id?: string
modelValue?: boolean
zIndex?: number
side?: 'top' | 'bottom' | 'left' | 'right'
title?: string | (() => string)
description?: string | (() => string)
loading?: boolean
closable?: boolean
centered?: boolean
bordered?: boolean
overlay?: boolean
overlayBlur?: boolean
showConfirmButton?: boolean
showCancelButton?: boolean
confirmButtonText?: string | (() => string)
cancelButtonText?: string | (() => string)
confirmButtonDisabled?: boolean
confirmButtonLoading?: boolean
beforeClose?: (
action: 'confirm' | 'cancel' | 'close',
done: () => void,
) => void
header?: boolean
footer?: boolean
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
contentClass?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
footerClass?: HTMLAttributes['class']
}
export interface DrawerEmits {
'update:modelValue': [value: boolean]
'open': []
'opened': []
'close': []
'closed': []
'confirm': []
'cancel': []
}
type BaseOptions = Omit<DrawerProps, 'modelValue'> & {
content?: Component | VNode | string
onOpen?: () => void
onOpened?: () => void
onClose?: () => void
onClosed?: () => void
onConfirm?: () => void
onCancel?: () => void
}
export function useDrawer() {
function create(initialOptions: BaseOptions) {
const container = document.createElement('div')
const visible = ref(false)
const options = shallowRef<BaseOptions>({ ...initialOptions })
const instance = getCurrentInstance()
let vnode: VNode | null = null
const updateVNode = () => {
vnode = createVNode(Drawer, Object.assign({
'id': instance && instance.uid ? `FaDrawer-${instance.uid}` : undefined,
'modelValue': visible.value,
'onUpdate:modelValue': (val: boolean) => {
visible.value = val
},
...options.value,
}), {
default: () => {
if (typeof options.value.content === 'string') {
return options.value.content
}
else if (isVNode(options.value.content)) {
return options.value.content
}
else if (options.value.content) {
return h(options.value.content)
}
return null
},
})
// 继承主应用的上下文
if (instance && instance.appContext) {
vnode.appContext = instance.appContext
}
render(vnode, container)
}
// 监听 visible 和 options 变化,自动重新渲染
watchEffect(() => {
updateVNode()
})
// 挂载到当前实例
instance?.proxy?.$el?.appendChild(container)
// 监听组件卸载,自动清理
if (instance) {
onUnmounted(() => {
if (vnode) {
render(null, container)
vnode = null
}
if (container.parentNode) {
container.parentNode.removeChild(container)
}
})
}
const open = () => {
visible.value = true
}
const close = () => {
visible.value = false
}
const update = (newOptions: BaseOptions) => {
options.value = { ...options.value, ...newOptions }
}
return {
open,
close,
update,
}
}
return {
create,
}
}
export { default as Drawer } from './index.vue'
export { useDrawer } from './useDrawer'
export type { DrawerEmits, DrawerProps } from './useDrawer'
@@ -0,0 +1,135 @@
import type { Component, HTMLAttributes, VNode } from 'vue'
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
import Drawer from './index.vue'
export interface DrawerProps {
id?: string
modelValue?: boolean
zIndex?: number
side?: 'top' | 'bottom' | 'left' | 'right'
title?: string | (() => string)
description?: string | (() => string)
loading?: boolean
closable?: boolean
centered?: boolean
bordered?: boolean
overlay?: boolean
overlayBlur?: boolean
showConfirmButton?: boolean
showCancelButton?: boolean
confirmButtonText?: string | (() => string)
cancelButtonText?: string | (() => string)
confirmButtonDisabled?: boolean
confirmButtonLoading?: boolean
beforeClose?: (
action: 'confirm' | 'cancel' | 'close',
done: () => void,
) => void
header?: boolean
footer?: boolean
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
contentClass?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
footerClass?: HTMLAttributes['class']
}
export interface DrawerEmits {
'update:modelValue': [value: boolean]
'open': []
'opened': []
'close': []
'closed': []
'confirm': []
'cancel': []
}
type BaseOptions = Omit<DrawerProps, 'modelValue'> & {
content?: Component | VNode | string
onOpen?: () => void
onOpened?: () => void
onClose?: () => void
onClosed?: () => void
onConfirm?: () => void
onCancel?: () => void
}
export function useDrawer() {
function create(initialOptions: BaseOptions) {
const container = document.createElement('div')
const visible = ref(false)
const options = shallowRef<BaseOptions>({ ...initialOptions })
const instance = getCurrentInstance()
let vnode: VNode | null = null
const updateVNode = () => {
vnode = createVNode(Drawer, Object.assign({
'id': instance && instance.uid ? `FaDrawer-${instance.uid}` : undefined,
'modelValue': visible.value,
'onUpdate:modelValue': (val: boolean) => {
visible.value = val
},
...options.value,
}), {
default: () => {
if (typeof options.value.content === 'string') {
return options.value.content
}
else if (isVNode(options.value.content)) {
return options.value.content
}
else if (options.value.content) {
return h(options.value.content)
}
return null
},
})
// 继承主应用的上下文
if (instance && instance.appContext) {
vnode.appContext = instance.appContext
}
render(vnode, container)
}
// 监听 visible 和 options 变化,自动重新渲染
watchEffect(() => {
updateVNode()
})
// 挂载到当前实例
instance?.proxy?.$el?.appendChild(container)
// 监听组件卸载,自动清理
if (instance) {
onUnmounted(() => {
if (vnode) {
render(null, container)
vnode = null
}
if (container.parentNode) {
container.parentNode.removeChild(container)
}
})
}
const open = () => {
visible.value = true
}
const close = () => {
visible.value = false
}
const update = (newOptions: BaseOptions) => {
options.value = { ...options.value, ...newOptions }
}
return {
open,
close,
update,
}
}
return {
create,
}
}
@@ -0,0 +1 @@
export { default as Dropdown } from './index.vue'
@@ -0,0 +1,2 @@
export { default as FileUpload } from './index.vue'
export type { FileItem, FileUploadRequestOptions } from './index.vue'
@@ -2,7 +2,7 @@
import axios from 'axios'
import { filesize } from 'filesize'
import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
import ButtonGroup from '../button-group/index.vue'
import ButtonGroup from '../button/ButtonGroup.vue'
import Button from '../button/index.vue'
import Icon from '../icon/index.vue'
@@ -0,0 +1 @@
export { default as FixedBar } from './index.vue'
@@ -0,0 +1 @@
export { default as HoverCard } from './index.vue'
@@ -0,0 +1 @@
export { default as Icon } from './index.vue'
@@ -1,22 +1,2 @@
import { createApp, h } from 'vue'
import Preview from './preview.vue'
export function useImagePreview() {
function open(src: string | string[], index = 0) {
const container = document.createElement('div')
const app = createApp({
render() {
return h(Preview, {
modelValue: true,
src,
index,
})
},
})
app.mount(container)
}
return {
open,
}
}
export { default as ImagePreview } from './index.vue'
export { useImagePreview } from './useImagePreview'
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { VisuallyHidden } from 'reka-ui'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import ButtonGroup from '../button-group/index.vue'
import ButtonGroup from '../button/ButtonGroup.vue'
import Button from '../button/index.vue'
import Icon from '../icon/index.vue'
import { Dialog, DialogContent, DialogDescription, DialogTitle } from './dialog'
@@ -0,0 +1,22 @@
import { createApp, h } from 'vue'
import Preview from './preview.vue'
export function useImagePreview() {
function open(src: string | string[], index = 0) {
const container = document.createElement('div')
const app = createApp({
render() {
return h(Preview, {
modelValue: true,
src,
index,
})
},
})
app.mount(container)
}
return {
open,
}
}
@@ -0,0 +1,2 @@
export { default as ImageUpload } from './index.vue'
export type { UploadRequestOptions } from './index.vue'
@@ -0,0 +1 @@
export { default as InputOTP } from './index.vue'
@@ -0,0 +1 @@
export { default as Input } from './index.vue'
@@ -1,28 +0,0 @@
# FaKbdGroup 键盘组合
用于包裹多个 `FaKbd` 组件,展示组合按键的容器组件。
## 使用场景
- 快捷键组合展示
- 键盘操作说明
- 命令面板提示
- 帮助文档中的多键组合说明
## Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `class` | `HTMLAttributes['class']` | - | 自定义 CSS 类 |
## Slots
| 名称 | 说明 |
|------|------|
| `default` | 包含的 `FaKbd` 组件 |
## 注意事项
1. **仅用于组合**:此组件仅用于包裹多个 `FaKbd`,不建议单独使用
2. **间距自动**:内部的 `FaKbd` 会自动添加适当间距
3. **灵活内容**:除了 `FaKbd` 外,也可以放入其他内容(如分隔符)
@@ -1,10 +0,0 @@
import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
export default [
{
title: '基础',
component: Basic,
componentRaw: BasicRaw,
},
]
@@ -34,3 +34,32 @@
1. **配合使用**:组合按键时请使用 `FaKbdGroup` 包裹多个 `FaKbd`
2. **内容长度**:按键内容不宜过长,适合单个字符或简短单词
3. **图标支持**:可以使用 `FaIcon` 组件显示图标,图标会自动调整为合适尺寸
## FaKbdGroup 键盘组合
用于包裹多个 `FaKbd` 组件,展示组合按键的容器组件。
## 使用场景
- 快捷键组合展示
- 键盘操作说明
- 命令面板提示
- 帮助文档中的多键组合说明
## Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `class` | `HTMLAttributes['class']` | - | 自定义 CSS 类 |
## Slots
| 名称 | 说明 |
|------|------|
| `default` | 包含的 `FaKbd` 组件 |
## 注意事项
1. **仅用于组合**:此组件仅用于包裹多个 `FaKbd`,不建议单独使用
2. **间距自动**:内部的 `FaKbd` 会自动添加适当间距
3. **灵活内容**:除了 `FaKbd` 外,也可以放入其他内容(如分隔符)
@@ -1,7 +1,7 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaKbd from '../../kbd/index.vue'
import FaKbdGroup from '../index.vue'
import FaKbd from '../index.vue'
import FaKbdGroup from '../KbdGroup.vue'
</script>
<template>
@@ -1,5 +1,7 @@
import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
import GroupBasic from './_group-basic.vue'
import GroupBasicRaw from './_group-basic.vue?raw'
export default [
{
@@ -7,4 +9,9 @@ export default [
component: Basic,
componentRaw: BasicRaw,
},
{
title: '组合',
component: GroupBasic,
componentRaw: GroupBasicRaw,
},
]
@@ -0,0 +1,2 @@
export { default as Kbd } from './index.vue'
export { default as KbdGroup } from './KbdGroup.vue'
@@ -0,0 +1 @@
export { default as Label } from './index.vue'
+3 -231
View File
@@ -1,231 +1,3 @@
import type { Component, HTMLAttributes, VNode } from 'vue'
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
import Modal from './index.vue'
export interface ModalProps {
id?: string
modelValue?: boolean
zIndex?: number
title?: string | (() => string)
description?: string | (() => string)
icon?: 'info' | 'success' | 'warning' | 'error'
loading?: boolean
closable?: boolean
maximize?: boolean
maximizable?: boolean
draggable?: boolean
center?: boolean
border?: boolean
alignCenter?: boolean
overlay?: boolean
overlayBlur?: boolean
showConfirmButton?: boolean
showCancelButton?: boolean
confirmButtonText?: string | (() => string)
cancelButtonText?: string | (() => string)
confirmButtonDisabled?: boolean
confirmButtonLoading?: boolean
beforeClose?: (
action: 'confirm' | 'cancel' | 'close',
done: () => void,
) => void
header?: boolean
footer?: boolean
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
class?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
contentClass?: HTMLAttributes['class']
footerClass?: HTMLAttributes['class']
}
export interface ModalEmits {
'update:modelValue': [value: boolean]
'open': []
'opened': []
'close': []
'closed': []
'confirm': []
'cancel': []
}
type BaseOptions = Omit<ModalProps, 'modelValue'> & {
content?: Component | VNode | string
onOpen?: () => void
onOpened?: () => void
onClose?: () => void
onClosed?: () => void
onConfirm?: () => void
onCancel?: () => void
}
type alertOptions = Pick<BaseOptions, 'title' | 'description' | 'icon' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm'>
type confirmOptions = Pick<BaseOptions, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'beforeClose' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm' | 'onCancel'>
export function useModal() {
function create(initialOptions: BaseOptions) {
const container = document.createElement('div')
const visible = ref(false)
const options = shallowRef<BaseOptions>({ ...initialOptions })
const instance = getCurrentInstance()
let vnode: VNode | null = null
const updateVNode = () => {
vnode = createVNode(Modal, Object.assign({
'id': instance && instance.uid ? `FaModal-${instance.uid}` : undefined,
'modelValue': visible.value,
'onUpdate:modelValue': (val: boolean) => {
visible.value = val
},
...options.value,
}), {
default: () => {
if (typeof options.value.content === 'string') {
return options.value.content
}
else if (isVNode(options.value.content)) {
return options.value.content
}
else if (options.value.content) {
return h(options.value.content)
}
return null
},
})
// 继承主应用的上下文
if (instance && instance.appContext) {
vnode.appContext = instance.appContext
}
render(vnode, container)
}
// 监听 visible 和 options 变化,自动重新渲染
watchEffect(() => {
updateVNode()
})
// 挂载到当前实例
instance?.proxy?.$el?.appendChild(container)
// 监听组件卸载,自动清理
if (instance) {
onUnmounted(() => {
if (vnode) {
render(null, container)
vnode = null
}
if (container.parentNode) {
container.parentNode.removeChild(container)
}
})
}
const open = () => {
visible.value = true
}
const close = () => {
visible.value = false
}
const update = (newOptions: BaseOptions) => {
options.value = { ...options.value, ...newOptions }
}
return {
open,
close,
update,
}
}
function info(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'info',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function success(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'success',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function warning(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'warning',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function error(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'error',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function confirm(options: confirmOptions) {
const defaultOptions: BaseOptions = {
closable: false,
border: false,
alignCenter: true,
showCancelButton: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open, update } = create(Object.assign(defaultOptions, options))
open()
return {
update,
}
}
return {
create,
info,
success,
warning,
error,
confirm,
}
}
export { default as Modal } from './index.vue'
export { useModal } from './useModal'
export type { ModalEmits, ModalProps } from './useModal'
@@ -0,0 +1,231 @@
import type { Component, HTMLAttributes, VNode } from 'vue'
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
import Modal from './index.vue'
export interface ModalProps {
id?: string
modelValue?: boolean
zIndex?: number
title?: string | (() => string)
description?: string | (() => string)
icon?: 'info' | 'success' | 'warning' | 'error'
loading?: boolean
closable?: boolean
maximize?: boolean
maximizable?: boolean
draggable?: boolean
center?: boolean
border?: boolean
alignCenter?: boolean
overlay?: boolean
overlayBlur?: boolean
showConfirmButton?: boolean
showCancelButton?: boolean
confirmButtonText?: string | (() => string)
cancelButtonText?: string | (() => string)
confirmButtonDisabled?: boolean
confirmButtonLoading?: boolean
beforeClose?: (
action: 'confirm' | 'cancel' | 'close',
done: () => void,
) => void
header?: boolean
footer?: boolean
closeOnClickOverlay?: boolean
closeOnPressEscape?: boolean
destroyOnClose?: boolean
openAutoFocus?: boolean
class?: HTMLAttributes['class']
headerClass?: HTMLAttributes['class']
contentClass?: HTMLAttributes['class']
footerClass?: HTMLAttributes['class']
}
export interface ModalEmits {
'update:modelValue': [value: boolean]
'open': []
'opened': []
'close': []
'closed': []
'confirm': []
'cancel': []
}
type BaseOptions = Omit<ModalProps, 'modelValue'> & {
content?: Component | VNode | string
onOpen?: () => void
onOpened?: () => void
onClose?: () => void
onClosed?: () => void
onConfirm?: () => void
onCancel?: () => void
}
type alertOptions = Pick<BaseOptions, 'title' | 'description' | 'icon' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm'>
type confirmOptions = Pick<BaseOptions, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'beforeClose' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm' | 'onCancel'>
export function useModal() {
function create(initialOptions: BaseOptions) {
const container = document.createElement('div')
const visible = ref(false)
const options = shallowRef<BaseOptions>({ ...initialOptions })
const instance = getCurrentInstance()
let vnode: VNode | null = null
const updateVNode = () => {
vnode = createVNode(Modal, Object.assign({
'id': instance && instance.uid ? `FaModal-${instance.uid}` : undefined,
'modelValue': visible.value,
'onUpdate:modelValue': (val: boolean) => {
visible.value = val
},
...options.value,
}), {
default: () => {
if (typeof options.value.content === 'string') {
return options.value.content
}
else if (isVNode(options.value.content)) {
return options.value.content
}
else if (options.value.content) {
return h(options.value.content)
}
return null
},
})
// 继承主应用的上下文
if (instance && instance.appContext) {
vnode.appContext = instance.appContext
}
render(vnode, container)
}
// 监听 visible 和 options 变化,自动重新渲染
watchEffect(() => {
updateVNode()
})
// 挂载到当前实例
instance?.proxy?.$el?.appendChild(container)
// 监听组件卸载,自动清理
if (instance) {
onUnmounted(() => {
if (vnode) {
render(null, container)
vnode = null
}
if (container.parentNode) {
container.parentNode.removeChild(container)
}
})
}
const open = () => {
visible.value = true
}
const close = () => {
visible.value = false
}
const update = (newOptions: BaseOptions) => {
options.value = { ...options.value, ...newOptions }
}
return {
open,
close,
update,
}
}
function info(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'info',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function success(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'success',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function warning(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'warning',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function error(options: alertOptions) {
const defaultOptions: BaseOptions = {
icon: 'error',
closable: false,
border: false,
alignCenter: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open } = create(Object.assign(defaultOptions, options))
open()
}
function confirm(options: confirmOptions) {
const defaultOptions: BaseOptions = {
closable: false,
border: false,
alignCenter: true,
showCancelButton: true,
closeOnClickOverlay: false,
destroyOnClose: true,
openAutoFocus: true,
class: 'min-h-auto max-w-[calc(100%-2rem)]',
contentClass: 'py-0 min-h-auto text-center sm:text-start',
footerClass: 'p-4',
}
const { open, update } = create(Object.assign(defaultOptions, options))
open()
return {
update,
}
}
return {
create,
info,
success,
warning,
error,
confirm,
}
}
@@ -0,0 +1 @@
export { default as NumberField } from './index.vue'
@@ -0,0 +1 @@
export { default as PageHeader } from './index.vue'
@@ -0,0 +1 @@
export { default as PageMain } from './index.vue'
@@ -0,0 +1 @@
export { default as Pagination } from './index.vue'
@@ -0,0 +1 @@
export { default as PasswordStrength } from './index.vue'
@@ -0,0 +1 @@
export { default as Popover } from './index.vue'
@@ -0,0 +1 @@
export { default as Progress } from './index.vue'
@@ -0,0 +1 @@
export { default as RadioGroup } from './index.vue'
@@ -0,0 +1 @@
export { default as ScrollArea } from './index.vue'
@@ -0,0 +1 @@
export { default as SearchBar } from './index.vue'
@@ -0,0 +1 @@
export { default as Select } from './index.vue'
@@ -0,0 +1 @@
export { default as Slider } from './index.vue'
@@ -0,0 +1 @@
export { default as Switch } from './index.vue'
@@ -0,0 +1,2 @@
export { default as Table } from './index.vue'
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './index.vue'
@@ -0,0 +1 @@
export { default as Tabs } from './index.vue'
@@ -0,0 +1 @@
export { default as Tag } from './index.vue'
@@ -0,0 +1 @@
export { default as Textarea } from './index.vue'
+2 -5
View File
@@ -1,5 +1,2 @@
import { toast } from 'vue-sonner'
export function useToast() {
return toast
}
export { default as Toast } from './index.vue'
export { useToast } from './useToast'
@@ -0,0 +1,5 @@
import { toast } from 'vue-sonner'
export function useToast() {
return toast
}
@@ -0,0 +1 @@
export { default as Tooltip } from './index.vue'
@@ -0,0 +1 @@
export { default as Trend } from './index.vue'
-3
View File
@@ -1,10 +1,8 @@
export { default as alert } from './basic/alert/_examples'
export { default as avatar } from './basic/avatar/_examples'
export { default as badge } from './basic/badge/_examples'
export { default as buttonGroup } from './basic/button-group/_examples'
export { default as button } from './basic/button/_examples'
export { default as card } from './basic/card/_examples'
export { default as checkboxGroup } from './basic/checkbox-group/_examples'
export { default as checkbox } from './basic/checkbox/_examples'
export { default as collapsible } from './basic/collapsible/_examples'
export { default as contextMenu } from './basic/context-menu/_examples'
@@ -20,7 +18,6 @@ export { default as imagePreview } from './basic/image-preview/_examples'
export { default as imageUpload } from './basic/image-upload/_examples'
export { default as inputOtp } from './basic/input-otp/_examples'
export { default as input } from './basic/input/_examples'
export { default as kbdGroup } from './basic/kbd-group/_examples'
export { default as kbd } from './basic/kbd/_examples'
export { default as label } from './basic/label/_examples'
export { default as modal } from './basic/modal/_examples'
+53 -56
View File
@@ -1,57 +1,54 @@
// 基础版组件
export { default as FaAlert } from './basic/alert/index.vue'
export { default as FaAvatar } from './basic/avatar/index.vue'
export { default as FaBadge } from './basic/badge/index.vue'
export { default as FaButtonGroup } from './basic/button-group/index.vue'
export { default as FaButton } from './basic/button/index.vue'
export { default as FaCard } from './basic/card/index.vue'
export { default as FaCheckboxGroup } from './basic/checkbox-group/index.vue'
export { default as FaCheckbox } from './basic/checkbox/index.vue'
export { default as FaCollapsible } from './basic/collapsible/index.vue'
export { default as FaContextMenu } from './basic/context-menu/index.vue'
export { default as FaDescriptions } from './basic/descriptions/index.vue'
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './basic/descriptions/index.vue'
export { default as FaDivider } from './basic/divider/index.vue'
export type { DrawerEmits, DrawerProps } from './basic/drawer/index'
export { useDrawer as useFaDrawer } from './basic/drawer/index'
export { default as FaDrawer } from './basic/drawer/index.vue'
export { default as FaDropdown } from './basic/dropdown/index.vue'
export { default as FaFileUpload } from './basic/file-upload/index.vue'
export type { FileItem, FileUploadRequestOptions } from './basic/file-upload/index.vue'
export { default as FaFixedBar } from './basic/fixed-bar/index.vue'
export { default as FaHoverCard } from './basic/hover-card/index.vue'
export { default as FaIcon } from './basic/icon/index.vue'
export { useImagePreview as useFaImagePreview } from './basic/image-preview/index'
export { default as FaImagePreview } from './basic/image-preview/index.vue'
export { default as FaImageUpload } from './basic/image-upload/index.vue'
export type { UploadRequestOptions } from './basic/image-upload/index.vue'
export { default as FaInputOTP } from './basic/input-otp/index.vue'
export { default as FaInput } from './basic/input/index.vue'
export { default as FaKbdGroup } from './basic/kbd-group/index.vue'
export { default as FaKbd } from './basic/kbd/index.vue'
export { default as FaLabel } from './basic/label/index.vue'
export type { ModalEmits, ModalProps } from './basic/modal/index'
export { useModal as useFaModal } from './basic/modal/index'
export { default as FaModal } from './basic/modal/index.vue'
export { default as FaNumberField } from './basic/number-field/index.vue'
export { default as FaPageHeader } from './basic/page-header/index.vue'
export { default as FaPageMain } from './basic/page-main/index.vue'
export { default as FaPagination } from './basic/pagination/index.vue'
export { default as FaPasswordStrength } from './basic/password-strength/index.vue'
export { default as FaPopover } from './basic/popover/index.vue'
export { default as FaProgress } from './basic/progress/index.vue'
export { default as FaRadioGroup } from './basic/radio-group/index.vue'
export { default as FaScrollArea } from './basic/scroll-area/index.vue'
export { default as FaSearchBar } from './basic/search-bar/index.vue'
export { default as FaSelect } from './basic/select/index.vue'
export { default as FaSlider } from './basic/slider/index.vue'
export { default as FaSwitch } from './basic/switch/index.vue'
export { default as FaTable } from './basic/table/index.vue'
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './basic/table/index.vue'
export { default as FaTabs } from './basic/tabs/index.vue'
export { default as FaTag } from './basic/tag/index.vue'
export { default as FaTextarea } from './basic/textarea/index.vue'
export { useToast as useFaToast } from './basic/toast/index'
export { default as FaToast } from './basic/toast/index.vue'
export { default as FaTooltip } from './basic/tooltip/index.vue'
export { default as FaTrend } from './basic/trend/index.vue'
export { Alert as FaAlert } from './basic/alert'
export { Avatar as FaAvatar } from './basic/avatar'
export { Badge as FaBadge } from './basic/badge'
export { Button as FaButton, ButtonGroup as FaButtonGroup } from './basic/button'
export { Card as FaCard } from './basic/card'
export { Checkbox as FaCheckbox, CheckboxGroup as FaCheckboxGroup } from './basic/checkbox'
export { Collapsible as FaCollapsible } from './basic/collapsible'
export { ContextMenu as FaContextMenu } from './basic/context-menu'
export { Descriptions as FaDescriptions } from './basic/descriptions'
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './basic/descriptions'
export { Divider as FaDivider } from './basic/divider'
export { Drawer as FaDrawer } from './basic/drawer'
export type { DrawerEmits, DrawerProps } from './basic/drawer'
export { useDrawer as useFaDrawer } from './basic/drawer'
export { Dropdown as FaDropdown } from './basic/dropdown'
export { FileUpload as FaFileUpload } from './basic/file-upload'
export type { FileItem, FileUploadRequestOptions } from './basic/file-upload'
export { FixedBar as FaFixedBar } from './basic/fixed-bar'
export { HoverCard as FaHoverCard } from './basic/hover-card'
export { Icon as FaIcon } from './basic/icon'
export { ImagePreview as FaImagePreview } from './basic/image-preview'
export { useImagePreview as useFaImagePreview } from './basic/image-preview'
export { ImageUpload as FaImageUpload } from './basic/image-upload'
export type { UploadRequestOptions } from './basic/image-upload'
export { Input as FaInput } from './basic/input'
export { InputOTP as FaInputOTP } from './basic/input-otp'
export { Kbd as FaKbd, KbdGroup as FaKbdGroup } from './basic/kbd'
export { Label as FaLabel } from './basic/label'
export { Modal as FaModal } from './basic/modal'
export type { ModalEmits, ModalProps } from './basic/modal'
export { useModal as useFaModal } from './basic/modal'
export { NumberField as FaNumberField } from './basic/number-field'
export { PageHeader as FaPageHeader } from './basic/page-header'
export { PageMain as FaPageMain } from './basic/page-main'
export { Pagination as FaPagination } from './basic/pagination'
export { PasswordStrength as FaPasswordStrength } from './basic/password-strength'
export { Popover as FaPopover } from './basic/popover'
export { Progress as FaProgress } from './basic/progress'
export { RadioGroup as FaRadioGroup } from './basic/radio-group'
export { ScrollArea as FaScrollArea } from './basic/scroll-area'
export { SearchBar as FaSearchBar } from './basic/search-bar'
export { Select as FaSelect } from './basic/select'
export { Slider as FaSlider } from './basic/slider'
export { Switch as FaSwitch } from './basic/switch'
export { Table as FaTable } from './basic/table'
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './basic/table'
export { Tabs as FaTabs } from './basic/tabs'
export { Tag as FaTag } from './basic/tag'
export { Textarea as FaTextarea } from './basic/textarea'
export { Toast as FaToast } from './basic/toast'
export { useToast as useFaToast } from './basic/toast'
export { Tooltip as FaTooltip } from './basic/tooltip'
export { Trend as FaTrend } from './basic/trend'