diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js
index c34e50cc..2c85b44b 100644
--- a/docs/.vitepress/config.js
+++ b/docs/.vitepress/config.js
@@ -76,7 +76,7 @@ export default {
link: 'https://playground.view-shadcn-ui.devlive.org'
},
{
- text: '2025.1.4 最新',
+ text: '2025.1.5 最新',
items: [
{
text: '贡献指南',
@@ -120,8 +120,9 @@ export default {
'/changelog/': [
(() => {
const items = [
- {text: '2025.1.4 最新', link: 'latest', version: '2025-12-07'},
- {text: '2025.1.3', link: '2025-05-27', version: '2025-05-27'},
+ {text: '2025.1.5 最新', link: 'latest', version: '2025-12-30'},
+ {text: '2025.1.4', link: '2025.1.4', version: '2025-12-07'},
+ {text: '2025.1.3', link: '2025.1.3', version: '2025-05-27'},
{text: '2025.1.2', link: '2025.1.2', version: '2025-01-21'},
{text: '2025.1.1', link: '2025.1.1', version: '2025-01-15'},
{text: '2025.1.0', link: '2025.1.0', version: '2025-01-11'},
diff --git a/docs/changelog/2025.1.4.md b/docs/changelog/2025.1.4.md
new file mode 100644
index 00000000..6c215fbc
--- /dev/null
+++ b/docs/changelog/2025.1.4.md
@@ -0,0 +1,43 @@
+---
+title: 2025.1.3
+---
+
+# 📢 View Shadcn UI v2025.1.4 发布 - 全面拥抱暗黑模式
+
+我们很高兴地宣布,View Shadcn UI v2025.1.4 版本已于 2025 年 12 月 7 日正式发布!这是一个重要的里程碑版本,为整个组件库带来了全面的暗黑模式支持。
+
+## 📋 版本信息
+- **版本号**: 2025.1.4
+- **发布日期**: 2025 年 12 月 7 日
+- **项目地址**: https://github.com/devlive-community/view-shadcn-ui
+- **官方网站**: https://view-shadcn-ui.devlive.org/
+- **演练场**: https://playground.view-shadcn-ui.devlive.org/
+
+## 🌟 主要更新
+
+#### 🌙 全面暗黑模式支持
+本次更新的核心是为所有组件添加了完整的暗黑模式支持,让您的应用可以轻松切换明暗主题,提供更好的用户体验。
+
+**已支持暗黑模式的组件**:
+- **布局组件**:布局(Layout)
+- **表单组件**:输入标签(Input Tags)、切换(Switch)、复选框(Checkbox)、颜色选择器(Color Picker)
+- **反馈组件**:警告提示(Alert)、徽章(Badge)、提示(Tooltip)
+- **导航组件**:面包屑(Breadcrumb)、返回顶部(Back to Top)
+- **展示组件**:头像(Avatar)、卡片(Card)、折叠面板(Collapse)、轮播(Carousel)、抽屉(Drawer)
+- **操作组件**:按钮(Button)、右键菜单(Context Menu)
+- **高级组件**:数据表格(Data Table)、代码编辑器(Code Editor)、计划任务(Cron)
+
+#### 🔧 数据表格增强
+除了暗黑模式支持外,我们还对数据表格组件进行了多项功能增强:
+- ✅ 修复了悬停固定列的显示问题
+- ✅ 支持自定义表格样式
+- ✅ 支持表头自定义插槽
+- ✅ 支持数据列自定义插槽
+- ✅ 支持表格边框配置
+- ✅ 支持固定列功能
+
+## 💡 关于 View Shadcn UI
+View Shadcn UI 是一个基于 Tailwind CSS 构建的现代化 Vue3 组件库,致力于提供优雅、高效且易于使用的 UI 组件,帮助开发者快速构建美观的用户界面。
+
+## 🙏 致谢
+感谢社区所有贡献者和用户的支持与反馈,让 View Shadcn UI 变得更加完善!
\ No newline at end of file
diff --git a/docs/changelog/latest.md b/docs/changelog/latest.md
index 924c94d0..54bc30bd 100644
--- a/docs/changelog/latest.md
+++ b/docs/changelog/latest.md
@@ -1,43 +1,76 @@
---
-title: 2025.1.3
+title: 2025.1.5
---
-# 📢 View Shadcn UI v2025.1.4 发布 - 全面拥抱暗黑模式
+我们怀着激动的心情宣布,View Shadcn UI v2025.1.5 版本已于 2025 年 12 月 30 日正式发布!这是一个革命性的版本更新,为组件库带来了令人惊艳的液态玻璃(Glassmorphism)设计美学,让您的应用界面焕然一新。
-我们很高兴地宣布,View Shadcn UI v2025.1.4 版本已于 2025 年 12 月 7 日正式发布!这是一个重要的里程碑版本,为整个组件库带来了全面的暗黑模式支持。
-
-### 📋 版本信息
-- **版本号**: 2025.1.4
-- **发布日期**: 2025 年 12 月 7 日
+## 📋 版本信息
+- **版本号**: 2025.1.5
+- **发布日期**: 2025 年 12 月 30 日
- **项目地址**: https://github.com/devlive-community/view-shadcn-ui
- **官方网站**: https://view-shadcn-ui.devlive.org/
- **演练场**: https://playground.view-shadcn-ui.devlive.org/
-### 🌟 主要更新
-
-#### 🌙 全面暗黑模式支持
-本次更新的核心是为所有组件添加了完整的暗黑模式支持,让您的应用可以轻松切换明暗主题,提供更好的用户体验。
-
-**已支持暗黑模式的组件**:
-- **布局组件**:布局(Layout)
-- **表单组件**:输入标签(Input Tags)、切换(Switch)、复选框(Checkbox)、颜色选择器(Color Picker)
-- **反馈组件**:警告提示(Alert)、徽章(Badge)、提示(Tooltip)
-- **导航组件**:面包屑(Breadcrumb)、返回顶部(Back to Top)
-- **展示组件**:头像(Avatar)、卡片(Card)、折叠面板(Collapse)、轮播(Carousel)、抽屉(Drawer)
-- **操作组件**:按钮(Button)、右键菜单(Context Menu)
-- **高级组件**:数据表格(Data Table)、代码编辑器(Code Editor)、计划任务(Cron)
-
-#### 🔧 数据表格增强
-除了暗黑模式支持外,我们还对数据表格组件进行了多项功能增强:
-- ✅ 修复了悬停固定列的显示问题
-- ✅ 支持自定义表格样式
-- ✅ 支持表头自定义插槽
-- ✅ 支持数据列自定义插槽
-- ✅ 支持表格边框配置
-- ✅ 支持固定列功能
-
-### 💡 关于 View Shadcn UI
-View Shadcn UI 是一个基于 Tailwind CSS 构建的现代化 Vue3 组件库,致力于提供优雅、高效且易于使用的 UI 组件,帮助开发者快速构建美观的用户界面。
-
-### 🙏 致谢
-感谢社区所有贡献者和用户的支持与反馈,让 View Shadcn UI 变得更加完善!
\ No newline at end of file
+## ✨ 核心特性
+
+#### 🌊 液态玻璃效果全面覆盖
+本次更新的最大亮点是为 40+ 组件添加了精心设计的液态玻璃效果,带来通透、层次分明的现代化视觉体验。
+
+**已支持液态玻璃效果的组件**:
+
+**基础组件**
+- Alert(警告提示)、Avatar(头像)、Badge(徽章)、Button(按钮)
+- Text(文本)、Skeleton(骨架屏)
+
+**表单组件**
+- Form(表单)、Select(选择器)、Slider(滑块)、Switch(开关)
+- Toggle(切换)、DatePicker(日期选择器)、ColorPicker(颜色选择器)
+- Upload(上传)
+
+**导航组件**
+- Menu(菜单)、Breadcrumb(面包屑)、Pagination(分页)
+- Tab(标签页)、BackTop(返回顶部)
+
+**反馈组件**
+- Message(消息)、Modal(模态框)、Drawer(抽屉)
+- Tooltip(提示)、Progress(进度条)、Spin(加载)
+
+**数据展示**
+- Table(表格)、Collapse(折叠面板)、Carousel(轮播)
+
+**高级组件**
+- ContextMenu(右键菜单)、Dropdown(下拉菜单)
+- CountDown(倒计时)、Contribution(贡献图)
+
+**布局组件**
+- Global Footer(全局页脚)、Toolbar Footer(工具栏页脚)
+
+#### 🎯 功能增强
+
+**选择器组件优化**
+- ✨ 新增搜索功能,支持快速筛选选项
+
+**Markdown 组件**
+- 🆕 全新支持 Markdown 渲染组件
+
+#### 🐛 问题修复
+
+- ✅ 修复右键菜单多级菜单下父级隐藏问题
+- ✅ 修复下拉菜单位置在右侧时列表显示异常
+- ✅ 修复嵌套菜单异常问题
+- ✅ 修复菜单宽度不一致问题
+- ✅ 修复抽屉组件顶部空白问题
+
+#### 🛠️ 工程优化
+
+- 📦 优化 CI/CD 流程,拆分构建脚本
+- 📚 文档全面适配暗黑模式
+- 🔧 修复 VitePress 部署配置问题
+
+## 💡 关于 View Shadcn UI
+
+View Shadcn UI 是一个基于 Tailwind CSS 构建的现代化 Vue3 组件库,专注于提供优雅、高性能且易于使用的 UI 组件。我们致力于将最新的设计趋势与最佳的开发实践相结合,帮助开发者构建出色的用户界面。
+
+## 🙏 致谢
+
+感谢社区的每一位贡献者和用户!你们的支持和反馈是我们不断前进的动力。
\ No newline at end of file
diff --git a/docs/components/form/form.md b/docs/components/form/form.md
index b86c5f3a..6e63ffa6 100644
--- a/docs/components/form/form.md
+++ b/docs/components/form/form.md
@@ -254,6 +254,74 @@ const resetForm = () => {
:::
+## 液态玻璃效果 (Glass)
+
+::: raw
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+:::
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+```
+
+:::
+
## 动态表单 (dynamic)
::: raw
@@ -333,6 +401,8 @@ onMounted(() => onAdd())
:headers="['属性', '描述', '类型', '默认值', '依赖', '支持列表']"
:columns="[
['modelValue', '当前组件的值', 'any', '-', '-', '-'],
+ ['dark', '是否为暗黑模式', 'boolean', 'false', '-', '-'],
+ ['glass', '是否启用液态玻璃效果', 'boolean', 'false', '-', '-'],
]">
@@ -441,4 +511,14 @@ const onSubmit2 = () => proxy?.$Message.success({
})
onMounted(() => onAdd())
+
+const glassFormData = ref({
+ username: '',
+ email: '',
+ password: ''
+})
+
+const onGlassSubmit = (data: any) => {
+ console.log('提交数据:', data)
+}
\ No newline at end of file
diff --git a/docs/components/form/mention.md b/docs/components/form/mention.md
index 56768532..f5f6066d 100644
--- a/docs/components/form/mention.md
+++ b/docs/components/form/mention.md
@@ -202,6 +202,39 @@ title: 提及 (Mention)
:::
+## 液态玻璃效果 (glass)
+::: raw
+
+:::
+::: details 查看代码
+```vue
+
+
+
+
+```
+:::
## 表单 (form)
::: raw
@@ -244,6 +277,8 @@ title: 提及 (Mention)
['trigger', '触发字符', 'string', '@', '-'],
['max', '最大项目数', 'number', 'Infinity', '-'],
['loadData', '异步加载数据', 'function', '-', '-'],
+ ['glass', '是否启用液态玻璃效果', 'boolean', 'false', '-'],
+ ['dark', '是否启用暗黑模式', 'boolean', 'false', '-'],
]">
diff --git a/docs/components/form/select.md b/docs/components/form/select.md
index 7571d71e..740cd7b9 100644
--- a/docs/components/form/select.md
+++ b/docs/components/form/select.md
@@ -267,6 +267,81 @@ const defaultSelectOptions = [
:::
+## 搜索 (search)
+
+::: raw
+
+
+
+
+
+:::
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+```
+
+:::
+
## 液态玻璃效果 (glass)
::: raw
@@ -468,6 +543,8 @@ loadMoreData((children) => {
['loadData', '懒加载数据函数', 'function', '-', '-'],
['dark', '暗黑模式', 'boolean', 'false', '-'],
['glass', '液态玻璃效果', 'boolean', 'false', '-'],
+ ['search', '是否启用搜索功能', 'boolean', 'false', 'true | false'],
+ ['searchPlaceholder', '搜索框占位符', 'string', '搜索...', '-'],
]">
@@ -562,4 +639,21 @@ const onClick = () => {
const glassSelectValue = ref(null)
const glassMultiValue = ref([])
+
+const searchSelectValue = ref('')
+const searchMultiValue = ref([])
+const searchGlassValue = ref('')
+
+const citiesOptions = [
+ { value: 'beijing', label: '北京' },
+ { value: 'shanghai', label: '上海' },
+ { value: 'guangzhou', label: '广州' },
+ { value: 'shenzhen', label: '深圳' },
+ { value: 'hangzhou', label: '杭州' },
+ { value: 'nanjing', label: '南京' },
+ { value: 'chengdu', label: '成都' },
+ { value: 'chongqing', label: '重庆' },
+ { value: 'wuhan', label: '武汉' },
+ { value: 'xian', label: '西安' }
+]
\ No newline at end of file
diff --git a/docs/components/form/time-picker.md b/docs/components/form/time-picker.md
index 64cd80ed..79358569 100644
--- a/docs/components/form/time-picker.md
+++ b/docs/components/form/time-picker.md
@@ -138,6 +138,37 @@ title: 时间选择器 (Time Picker)
:::
+## 液态玻璃效果 (glass)
+::: raw
+
+:::
+::: details 查看代码
+```vue
+
+
+
+
+```
+:::
## 时间选择器 (Time Picker) 属性
@@ -183,4 +216,7 @@ const darkMode = computed(() => isDark.value)
import { ref , computed } from 'vue';
const value = ref('')
+const glassValue1 = ref('09:00')
+const glassValue2 = ref('12:30:45')
+const glassValue3 = ref('14:00')
\ No newline at end of file
diff --git a/docs/components/form/upload.md b/docs/components/form/upload.md
index 5d2f25bf..de35e859 100644
--- a/docs/components/form/upload.md
+++ b/docs/components/form/upload.md
@@ -68,6 +68,45 @@ title: 上传 (Upload)
:::
+## 液态玻璃 (glass)
+
+::: raw
+
+
+
+
+
+
+
+:::
+::: details 查看代码
+
+```vue
+
+
+
+```
+
+:::
+::: raw
+
+
+
+
+
+
+
+:::
+::: details 查看代码
+
+```vue
+
+
+
+```
+
+:::
+
## 上传 (Upload) 属性
diff --git a/docs/components/navigation/dropdown.md b/docs/components/navigation/dropdown.md
index 4ec99eb2..b318658d 100644
--- a/docs/components/navigation/dropdown.md
+++ b/docs/components/navigation/dropdown.md
@@ -186,6 +186,59 @@ title: 下拉菜单 (Dropdown)
:::
+## 液态玻璃效果 (glass)
+
+::: raw
+
+
+
+
+
+
+ 点击打开
+
+ 操作 1
+ 操作 2
+ 操作 3
+ 操作 4
+
+
+
+ 悬停打开
+
+ 操作 1
+ 操作 2
+ 操作 3
+ 操作 4 (禁用)
+
+
+
+
+
+:::
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+ 点击打开
+
+ 操作 1
+ 操作 2
+ 操作 3
+ 操作 4
+
+
+
+
+```
+
+:::
+
## 下拉菜单 (Dropdown) 属性
diff --git a/docs/components/navigation/menu.md b/docs/components/navigation/menu.md
index 213bda0e..c46d648b 100644
--- a/docs/components/navigation/menu.md
+++ b/docs/components/navigation/menu.md
@@ -159,6 +159,14 @@ title: 菜单 (Menu)
Settings
Change Username
Change Password
+
+ Security
+
+
+
+ Two-Factor Auth
+ Active Sessions
+
Change Email
@@ -206,14 +214,218 @@ title: 菜单 (Menu)
:::
+## 触发方式 (trigger)
+
+支持点击和悬停两种触发方式。
+
+
+
+
+
+
+
+ Home
+
+
+ Profile
+
+
+
+
+ Settings
+ Change Username
+ Change Password
+
+
+ Change Email
+
+
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+
+ Home
+
+
+ Profile
+
+
+
+
+ Settings
+ Change Username
+ Change Password
+
+
+ Change Email
+
+
+```
+
+:::
+
+## 液态玻璃效果 (glass)
+
+
+
+
+
+
+
+
+
+ 首页
+
+
+ 产品
+
+
+
+ 产品 1
+ 产品 2
+ 产品 3
+
+
+ 服务
+
+
+
+ 服务 1
+ 服务 2
+
+
+
+
+
+ 关于
+
+
+
+
+
+
+:::
+
+
+::: details 查看代码
+```vue
+
+
+
+
+
+
+
+
+ 首页
+
+
+ 产品
+
+
+
+ 产品 1
+ 产品 2
+ 产品 3
+
+
+ 服务
+
+
+
+ 服务 1
+ 服务 2
+
+
+
+
+
+ 关于
+
+
+
+
+
+
+```
+:::
+
+## 自定义插槽
+
+ShadcnMenuSub 支持 `header` 插槽,可以完全自定义子菜单的标题部分。
+
+
+
+
+
+
+
+ Home
+
+
+
+
+
+ Custom Profile
+ ▼
+
+
+ Change Username
+ Change Email
+
+
+
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+
+ Home
+
+
+
+
+
+ Custom Profile
+ ▼
+
+
+ Change Username
+ Change Email
+
+
+
+```
+
+:::
+
## 菜单 (Menu) 属性
@@ -271,11 +483,12 @@ title: 菜单 (Menu)
## 菜单子项 (Menu Sub) 插槽
diff --git a/docs/components/navigation/pagination.md b/docs/components/navigation/pagination.md
index 9f558dcf..a942dcd2 100644
--- a/docs/components/navigation/pagination.md
+++ b/docs/components/navigation/pagination.md
@@ -149,6 +149,42 @@ const defaultValue = ref(1)
:::
+## 液态玻璃效果 (glass)
+
+
+
+ 数据值: {{glassValue1}}
+
+ 数据值: {{glassValue2}}
+
+ 数据值: {{glassValue3}}
+
+
+
+
+
+:::
+
+::: details 查看代码
+```vue
+
+
+
+
+```
+:::
+
## 分页 (Pagination) 属性
@@ -196,4 +234,7 @@ const { isDark } = useData()
const darkMode = computed(() => isDark.value)
const defaultValue = ref(1)
+const glassValue1 = ref(1)
+const glassValue2 = ref(1)
+const glassValue3 = ref(1)
\ No newline at end of file
diff --git a/docs/components/navigation/tabs.md b/docs/components/navigation/tabs.md
index 55790a40..be423a76 100644
--- a/docs/components/navigation/tabs.md
+++ b/docs/components/navigation/tabs.md
@@ -348,8 +348,58 @@ title: 选项卡 (Tabs)
```
+:::
+## 液态玻璃效果 (glass)
+
+
+
+
+ Tab 1 content
+ Tab 2 content
+ Tab 3 content
+
+
+ Success content
+ Success content
+ Success content
+
+
+ Warning content
+ Warning content
+ Warning content
+
+
+
+
:::
+::: details 查看代码
+```vue
+
+
+
+
+ Tab 1 content
+ Tab 2 content
+ Tab 3 content
+
+
+ Success content
+ Success content
+ Success content
+
+
+ Warning content
+ Warning content
+ Warning content
+
+
+
+
+
+```
+:::
## 动态选项卡
@@ -414,6 +464,8 @@ const addTab = () => {
['direction', '选项卡的方向', 'string', 'horizontal', 'horizontal | vertical'],
['position', '选项卡的位置', 'string', 'top', 'left | right'],
['showScrollButtons', '是否显示滚动按钮', 'boolean', 'true', '-'],
+ ['glass', '是否启用液态玻璃效果', 'boolean', 'false', '-'],
+ ['dark', '是否启用暗黑模式', 'boolean', 'false', '-'],
]">
diff --git a/docs/components/view/spin.md b/docs/components/view/spin.md
index d5bc0a2f..6b9b8917 100644
--- a/docs/components/view/spin.md
+++ b/docs/components/view/spin.md
@@ -145,6 +145,42 @@ const darkMode = computed(() => isDark.value)
:::
+## 液态玻璃效果 (glass)
+
+::: raw
+
+
+
+
+
+:::
+
+::: details 查看代码
+
+```vue
+
+
+
+
+
+```
+
+:::
+
## 服务 ($Spin)
::: raw
@@ -188,6 +224,8 @@ export default {
['type', '组件的类型', 'enum', 'primary', 'primary | success | warning | error'],
['size', '组件的尺寸', 'enum', 'default', 'default | small | large'],
['fixed', '是否固定在父组件上,父组件必须使用 position: relative', 'boolean', 'false', '-'],
+ ['glass', '是否启用液态玻璃效果', 'boolean', 'false', '-'],
+ ['dark', '是否启用暗黑模式', 'boolean', 'false', '-'],
]">
diff --git a/playground/src/example/dropdown/glass.vue b/playground/src/example/dropdown/glass.vue
new file mode 100644
index 00000000..80d437ba
--- /dev/null
+++ b/playground/src/example/dropdown/glass.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+ 点击打开
+
+ 新建文件
+ 打开文件
+ 保存
+ 另存为
+ 退出
+
+
+
+
+ 悬停打开
+
+ 编辑
+ 复制
+ 粘贴
+ 删除
+ 禁用项
+
+
+
+
+
+
+
+
+
+
+
+ 上方菜单
+
+ 选项 1
+ 选项 2
+ 选项 3
+
+
+
+
+ 下方菜单
+
+ 更多操作
+ 设置
+ 帮助
+
+
+
+
+
+
+
+
diff --git a/playground/src/example/dropdown/usage.vue b/playground/src/example/dropdown/usage.vue
new file mode 100644
index 00000000..106ad28c
--- /dev/null
+++ b/playground/src/example/dropdown/usage.vue
@@ -0,0 +1,25 @@
+
+
+
+
+ 点击打开
+
+ 操作 1
+ 操作 2
+ 操作 3
+
+
+
+
+ 悬停打开
+
+ 操作 1
+ 操作 2
+ 操作 3
+ 操作 4
+
+
+
+
+
diff --git a/playground/src/example/form/glass.vue b/playground/src/example/form/glass.vue
new file mode 100644
index 00000000..c203bf77
--- /dev/null
+++ b/playground/src/example/form/glass.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
diff --git a/playground/src/example/form/usage.vue b/playground/src/example/form/usage.vue
new file mode 100644
index 00000000..6669951b
--- /dev/null
+++ b/playground/src/example/form/usage.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reset
+
+
+ Submit
+
+
+
+
+
+
+
diff --git a/playground/src/example/mention/glass.vue b/playground/src/example/mention/glass.vue
new file mode 100644
index 00000000..43fc1ac8
--- /dev/null
+++ b/playground/src/example/mention/glass.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
diff --git a/playground/src/example/menu/glass.vue b/playground/src/example/menu/glass.vue
new file mode 100644
index 00000000..00cbd9ae
--- /dev/null
+++ b/playground/src/example/menu/glass.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ 首页
+
+ 产品
+ 产品 1
+ 产品 2
+ 产品 3
+
+
+ 服务
+ 服务 1
+ 服务 2
+
+ 关于
+ 联系
+
+
+
+
+
+
+
+ 首页
+
+ 产品
+ 产品 1
+ 产品 2
+
+ 更多产品
+ 产品 3
+ 产品 4
+
+
+
+ 服务
+ 服务 1
+ 服务 2
+
+ 关于
+
+
+
+
+
+
+
diff --git a/playground/src/example/menu/usage.vue b/playground/src/example/menu/usage.vue
new file mode 100644
index 00000000..cd565e81
--- /dev/null
+++ b/playground/src/example/menu/usage.vue
@@ -0,0 +1,22 @@
+
+
+
+ 首页
+
+ 产品
+ 产品 1
+ 产品 2
+ 产品 3
+
+
+ 服务
+ 服务 1
+ 服务 2
+
+ 关于
+
+
+
+
+
diff --git a/playground/src/example/pagination/glass.vue b/playground/src/example/pagination/glass.vue
new file mode 100644
index 00000000..505ac0c7
--- /dev/null
+++ b/playground/src/example/pagination/glass.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
diff --git a/playground/src/example/pagination/usage.vue b/playground/src/example/pagination/usage.vue
new file mode 100644
index 00000000..d0729e04
--- /dev/null
+++ b/playground/src/example/pagination/usage.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/playground/src/example/select/search.vue b/playground/src/example/select/search.vue
new file mode 100644
index 00000000..477c171f
--- /dev/null
+++ b/playground/src/example/select/search.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
diff --git a/playground/src/example/spin/glass.vue b/playground/src/example/spin/glass.vue
new file mode 100644
index 00000000..a9a6f8a3
--- /dev/null
+++ b/playground/src/example/spin/glass.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/playground/src/example/spin/usage.vue b/playground/src/example/spin/usage.vue
new file mode 100644
index 00000000..88d2df7c
--- /dev/null
+++ b/playground/src/example/spin/usage.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/playground/src/example/tab/glass.vue b/playground/src/example/tab/glass.vue
new file mode 100644
index 00000000..3b7bf00d
--- /dev/null
+++ b/playground/src/example/tab/glass.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+ Tab 1 content
+ Tab 2 content
+ Tab 3 content
+
+
+ Success content
+ Success content
+ Success content
+
+
+
+
+
+
+
+
+
+ Warning content
+ Warning content
+ Warning content
+
+
+ Error content
+ Error content
+
+
+
+
+
+
+
+
diff --git a/playground/src/example/tab/usage.vue b/playground/src/example/tab/usage.vue
new file mode 100644
index 00000000..0f178978
--- /dev/null
+++ b/playground/src/example/tab/usage.vue
@@ -0,0 +1,18 @@
+
+
+
+
+ Tab 1 content
+
+
+ Tab 2 content
+
+
+ Tab 3 content
+
+
+
+
+
+
diff --git a/playground/src/example/time-picker/glass.vue b/playground/src/example/time-picker/glass.vue
new file mode 100644
index 00000000..1abd237e
--- /dev/null
+++ b/playground/src/example/time-picker/glass.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
diff --git a/playground/src/example/time-picker/usage.vue b/playground/src/example/time-picker/usage.vue
new file mode 100644
index 00000000..49c439bd
--- /dev/null
+++ b/playground/src/example/time-picker/usage.vue
@@ -0,0 +1,41 @@
+
+
+
+
基础用法
+
+
+
+
+
+
+
快捷时间
+
+
+
+
+
禁用状态
+
+
+
+
+
+
diff --git a/playground/src/example/upload/glass.vue b/playground/src/example/upload/glass.vue
new file mode 100644
index 00000000..ebad56c6
--- /dev/null
+++ b/playground/src/example/upload/glass.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
diff --git a/playground/src/example/upload/usage.vue b/playground/src/example/upload/usage.vue
new file mode 100644
index 00000000..80578be2
--- /dev/null
+++ b/playground/src/example/upload/usage.vue
@@ -0,0 +1,32 @@
+
+
+
+
基础用法
+
+
+
+
+
多选
+
+
+
+
+
禁用状态
+
+
+
+
+
自定义描述
+
+
+
+
+
限制文件类型
+
+
+
+
+
+
diff --git a/src/App.vue b/src/App.vue
index 699dc126..2868f484 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,51 +4,65 @@
切换暗黑模式
-
-
-
ShadcnTable - 液态玻璃效果
-
-
-
+
+
Select 搜索功能
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ui/contextmenu/ShadcnContextMenuSub.vue b/src/ui/contextmenu/ShadcnContextMenuSub.vue
index f66e0b78..f968011f 100644
--- a/src/ui/contextmenu/ShadcnContextMenuSub.vue
+++ b/src/ui/contextmenu/ShadcnContextMenuSub.vue
@@ -37,7 +37,7 @@
ref="subMenuRef"
@mouseenter="onSubMenuMouseEnter"
@mouseleave="onSubMenuMouseLeave"
- :class="['fixed min-w-[8rem] z-50 rounded-md border p-1',
+ :class="['fixed min-w-[8rem] z-[60] rounded-md border p-1',
glass ? 'backdrop-blur-xl backdrop-saturate-150' : '',
glass ? 'border-white/20' : (dark ? 'border-gray-700' : 'border-gray-200'),
glass ? (dark ? 'bg-white/10' : 'bg-white/60') : (dark ? 'bg-gray-800' : 'bg-white'),
@@ -52,7 +52,7 @@
diff --git a/src/ui/mention/ShadcnMention.vue b/src/ui/mention/ShadcnMention.vue
index dbbd5bfd..c82679e9 100644
--- a/src/ui/mention/ShadcnMention.vue
+++ b/src/ui/mention/ShadcnMention.vue
@@ -3,10 +3,13 @@
@@ -27,7 +30,8 @@
:class="[
{ 'cursor-not-allowed opacity-50': disabled },
disabled ? (dark ? 'bg-gray-800' : 'bg-gray-100') : '',
- dark ? 'text-gray-200 placeholder:text-gray-500' : ''
+ glass && (dark ? 'text-gray-100 placeholder:text-gray-300' : 'text-gray-800 placeholder:text-gray-500'),
+ !glass && (dark ? 'text-gray-200 placeholder:text-gray-500' : '')
]"
:disabled="disabled"
:placeholder="selectedTags.length ? '' : placeholder"
@@ -52,19 +56,30 @@
leave-to-class="opacity-0 translate-y-1">
handleItemClick(item, event)"
@mouseenter="() => handleItemHover(index, item)">
@@ -100,7 +115,8 @@ const props = withDefaults(defineProps
(), {
trigger: '@',
loadData: undefined,
max: Infinity,
- dark: false
+ dark: false,
+ glass: false
})
const emit = defineEmits()
diff --git a/src/ui/mention/types.ts b/src/ui/mention/types.ts
index d9ec1bf6..fc9b16bb 100644
--- a/src/ui/mention/types.ts
+++ b/src/ui/mention/types.ts
@@ -21,6 +21,7 @@ export interface MentionProps
max?: number
name?: string
dark?: boolean
+ glass?: boolean
}
export type MentionEmits = {
diff --git a/src/ui/menu/ShadcnMenu.vue b/src/ui/menu/ShadcnMenu.vue
index f7247da2..11244c09 100644
--- a/src/ui/menu/ShadcnMenu.vue
+++ b/src/ui/menu/ShadcnMenu.vue
@@ -1,6 +1,12 @@
-
@@ -9,7 +15,7 @@
+
\ No newline at end of file
diff --git a/src/ui/menu/ShadcnMenuGroup.vue b/src/ui/menu/ShadcnMenuGroup.vue
index 6452e517..85447cd5 100644
--- a/src/ui/menu/ShadcnMenuGroup.vue
+++ b/src/ui/menu/ShadcnMenuGroup.vue
@@ -13,9 +13,9 @@ import { computed, inject } from 'vue'
const menuContext = inject('menuContext') as {
direction: 'horizontal' | 'vertical'
- dark?: boolean
+ dark?: { value: boolean }
}
const isHorizontal = computed(() => menuContext.direction === 'horizontal')
-const dark = computed(() => menuContext.dark || false)
+const dark = computed(() => menuContext.dark?.value || false)
diff --git a/src/ui/menu/ShadcnMenuItem.vue b/src/ui/menu/ShadcnMenuItem.vue
index 07f88d99..d3f85843 100644
--- a/src/ui/menu/ShadcnMenuItem.vue
+++ b/src/ui/menu/ShadcnMenuItem.vue
@@ -1,11 +1,13 @@
void
direction: 'horizontal' | 'vertical'
- setExpandedKey: (key: string | null) => void
parentName?: string
- dark?: boolean
+ dark?: { value: boolean }
+ glass?: { value: boolean }
+ closeAllMenus?: () => void
}
const isActive = computed(() => props.active || menuContext.activeKey.value === props.name)
const isHorizontal = computed(() => menuContext.direction === 'horizontal')
const parentName = menuContext.parentName || null
-const dark = computed(() => menuContext.dark || false)
+const dark = computed(() => menuContext.dark?.value || false)
+const glass = computed(() => menuContext.glass?.value || false)
const onClick = (event: MouseEvent) => {
menuContext.setActiveKey(props.name)
- if (menuContext.direction === 'horizontal') {
- menuContext.setExpandedKey(null)
+ if (isHorizontal.value && menuContext.closeAllMenus) {
+ menuContext.closeAllMenus()
}
emit('on-active', !props.active)
diff --git a/src/ui/menu/ShadcnMenuSub.vue b/src/ui/menu/ShadcnMenuSub.vue
index 8232cb95..f6ae9a27 100644
--- a/src/ui/menu/ShadcnMenuSub.vue
+++ b/src/ui/menu/ShadcnMenuSub.vue
@@ -1,21 +1,29 @@
-
+
-
-
-
-
-
-
+ @click="trigger === 'click' && toggleExpand()">
+
+
+
+
+
+
+
+
-
+
@@ -51,10 +65,13 @@ const props = defineProps<{
const menuContext = inject('menuContext') as {
direction: 'horizontal' | 'vertical'
- expandedKey: { value: string | null }
activeKey: { value: string | null }
- setExpandedKey: (key: string | null) => void
- dark?: boolean
+ toggleExpandedKey: (key: string) => void
+ isExpanded: (key: string) => boolean
+ dark?: { value: boolean }
+ glass?: { value: boolean }
+ parentName?: string
+ trigger?: 'click' | 'hover'
}
provide('menuContext', {
@@ -63,13 +80,18 @@ provide('menuContext', {
})
const isHorizontal = computed(() => menuContext.direction === 'horizontal')
-const isExpanded = computed(() => menuContext.expandedKey.value === props.name)
-const dark = computed(() => menuContext.dark || false)
+const isNested = computed(() => !!menuContext.parentName && isHorizontal.value)
+const isExpanded = computed(() => menuContext.isExpanded(props.name))
+const dark = computed(() => menuContext.dark?.value || false)
+const glass = computed(() => menuContext.glass?.value || false)
+const trigger = computed(() => menuContext.trigger || 'click')
const hasActiveChild = ref(false)
+const menuContent = ref(null)
+let hoverTimer: NodeJS.Timeout | null = null
const checkActiveChild = () => {
- const slotElements = document.querySelectorAll(`[data-parent="${ props.name }"]`)
+ const slotElements = document.querySelectorAll(`[data-parent="${props.name}"]`)
hasActiveChild.value = Array.from(slotElements).some(
(element) => (element as HTMLElement).dataset.name === menuContext.activeKey.value
)
@@ -88,14 +110,94 @@ onMounted(() => {
})
const toggleExpand = () => {
- // If the current expanded item is this component, collapse it; otherwise expand it
- if (menuContext.expandedKey.value === props.name) {
- menuContext.setExpandedKey(null)
- // Keep the selected state when manually folding, if there is still a selected item in the subitem
+ menuContext.toggleExpandedKey(props.name)
+ if (!isExpanded.value) {
checkActiveChild()
}
+}
+
+const handleMouseEnter = () => {
+ if (hoverTimer) {
+ clearTimeout(hoverTimer)
+ hoverTimer = null
+ }
+ if (!isExpanded.value) {
+ menuContext.toggleExpandedKey(props.name)
+ }
+}
+
+const handleMouseLeave = () => {
+ hoverTimer = setTimeout(() => {
+ if (isExpanded.value) {
+ menuContext.toggleExpandedKey(props.name)
+ }
+ }, 200)
+}
+
+const onEnter = (el: Element) => {
+ const element = el as HTMLElement
+ if (isHorizontal.value || isNested.value) {
+ element.style.opacity = '0'
+ if (isNested.value) {
+ element.style.transform = 'translateX(-8px)'
+ } else {
+ element.style.transform = 'translateY(-8px)'
+ }
+ }
+ else {
+ element.style.height = '0'
+ element.style.opacity = '0'
+ }
+}
+
+const onAfterEnter = (el: Element) => {
+ const element = el as HTMLElement
+ if (isHorizontal.value || isNested.value) {
+ element.style.transition = 'opacity 0.2s ease, transform 0.2s ease'
+ element.style.opacity = '1'
+ element.style.transform = isNested.value ? 'translateX(0)' : 'translateY(0)'
+ }
+ else {
+ const height = element.scrollHeight
+ element.style.transition = 'height 0.3s ease, opacity 0.2s ease'
+ element.style.height = `${height}px`
+ element.style.opacity = '1'
+
+ setTimeout(() => {
+ element.style.height = 'auto'
+ }, 300)
+ }
+}
+
+const onLeave = (el: Element) => {
+ const element = el as HTMLElement
+ if (isHorizontal.value || isNested.value) {
+ element.style.transition = 'opacity 0.2s ease, transform 0.2s ease'
+ requestAnimationFrame(() => {
+ element.style.opacity = '0'
+ if (isNested.value) {
+ element.style.transform = 'translateX(-8px)'
+ } else {
+ element.style.transform = 'translateY(-8px)'
+ }
+ })
+ }
else {
- menuContext.setExpandedKey(props.name)
+ element.style.height = `${element.scrollHeight}px`
+ element.style.transition = 'height 0.3s ease, opacity 0.2s ease'
+
+ requestAnimationFrame(() => {
+ element.style.height = '0'
+ element.style.opacity = '0'
+ })
}
}
+
+const onAfterLeave = (el: Element) => {
+ const element = el as HTMLElement
+ element.style.height = ''
+ element.style.opacity = ''
+ element.style.transition = ''
+ element.style.transform = ''
+}
diff --git a/src/ui/pagination/ShadcnPagination.vue b/src/ui/pagination/ShadcnPagination.vue
index f671dbdf..881f1a46 100644
--- a/src/ui/pagination/ShadcnPagination.vue
+++ b/src/ui/pagination/ShadcnPagination.vue
@@ -1,6 +1,10 @@
-
-
+
+
{{ t('pagination.text.total', { total }) }}
@@ -8,8 +12,11 @@
- ...
+ ...
@@ -76,6 +90,7 @@ const props = withDefaults(defineProps<{
showSizer?: boolean
sizerOptions?: (number | string)[]
dark?: boolean
+ glass?: boolean
}>(), {
total: 100,
pageSize: 10,
@@ -85,7 +100,8 @@ const props = withDefaults(defineProps<{
showTotal: false,
showSizer: false,
sizerOptions: () => [10, 20, 50, 100],
- dark: false
+ dark: false,
+ glass: false
})
const currentPage = ref(Number(props.modelValue))
diff --git a/src/ui/select/ShadcnSelect.vue b/src/ui/select/ShadcnSelect.vue
index 04840fd0..bf1f7a90 100644
--- a/src/ui/select/ShadcnSelect.vue
+++ b/src/ui/select/ShadcnSelect.vue
@@ -4,7 +4,7 @@
border && (dark ? 'border border-gray-600' : 'border border-gray-200'),
MinSize[size]
]">
-
+
-
-
-
-
-
+ glass ? (dark ? 'bg-white/10' : 'bg-white/30') : (dark ? 'border-gray-600 bg-gray-800' : 'border-gray-200 bg-white')]">
+
+
+
+
+
+
+
+ 无匹配结果
+
+
+
+
+
+ 无匹配结果
+
+
+
@@ -105,6 +124,7 @@ import { HoverType } from '@/ui/common/type.ts'
import { SelectEmits, SelectOptionProps, SelectProps } from '@/ui/select/types.ts'
import { generateRandomId } from '@/utils/common.ts'
import { ShadcnSkeleton } from '@/ui/skeleton'
+import { ShadcnInput } from '@/ui/input'
const emit = defineEmits
()
@@ -118,7 +138,9 @@ const props = withDefaults(defineProps(), {
lazy: false,
loading: false,
dark: false,
- glass: false
+ glass: false,
+ search: false,
+ searchPlaceholder: '搜索...'
})
const isExpanded = ref(false)
@@ -128,6 +150,7 @@ const selectRef = ref(null)
const dropdownRef = ref(null)
const parentName = `shadcn-select-${generateRandomId()}`
const isLoading = ref(false)
+const searchQuery = ref('')
// Handle scroll loading
const handleScroll = () => {
@@ -217,14 +240,34 @@ const removeSelection = (index: number) => {
}
const internalOptions = computed(() => {
- return props.options || slotOptions.value
+ const options = props.options || slotOptions.value
+ if (props.search && searchQuery.value) {
+ return options.filter(option =>
+ option.label.toLowerCase().includes(searchQuery.value.toLowerCase())
+ )
+ }
+ return options
+})
+
+const visibleOptionsCount = computed(() => {
+ if (!props.search || !searchQuery.value) {
+ return slotOptions.value.length
+ }
+ return slotOptions.value.filter(option =>
+ option.label.toLowerCase().includes(searchQuery.value.toLowerCase())
+ ).length
})
const toggleDropdown = () => {
if (!props.disabled) {
isExpanded.value = !isExpanded.value
if (isExpanded.value) {
- nextTick(updateSelectedLabels)
+ nextTick(() => {
+ updateSelectedLabels()
+ })
+ }
+ else {
+ searchQuery.value = ''
}
}
}
@@ -281,7 +324,9 @@ provide('selectContext', {
multiple: props.multiple,
parentName,
dark: computed(() => props.dark),
- glass: computed(() => props.glass)
+ glass: computed(() => props.glass),
+ searchQuery: computed(() => searchQuery.value),
+ search: computed(() => props.search)
})
onMounted(() => {
diff --git a/src/ui/select/ShadcnSelectOption.vue b/src/ui/select/ShadcnSelectOption.vue
index fd0de98d..3c4482e6 100644
--- a/src/ui/select/ShadcnSelectOption.vue
+++ b/src/ui/select/ShadcnSelectOption.vue
@@ -1,5 +1,6 @@
-
-
+
@@ -15,7 +19,8 @@
@@ -36,12 +41,14 @@ const props = withDefaults(defineProps<{
size?: keyof typeof WrapperSize
fixed?: boolean
dark?: boolean
+ glass?: boolean
}>(), {
modelValue: true,
type: 'primary',
size: 'default',
fixed: false,
- dark: false
+ dark: false,
+ glass: false
})
watch(() => props.modelValue, (newValue) => {
diff --git a/src/ui/tab/ShadcnTab.vue b/src/ui/tab/ShadcnTab.vue
index bf8de475..b1bd4cf8 100644
--- a/src/ui/tab/ShadcnTab.vue
+++ b/src/ui/tab/ShadcnTab.vue
@@ -27,27 +27,33 @@
class="overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
@@ -12,19 +13,21 @@
class="flex items-center cursor-pointer hover:text-muted-foreground"
@click="onClear">
-
+
-
+
@@ -35,9 +38,10 @@
:formatter="zeroPadFormatter"
:parser="parseNumber"
:dark="dark"
+ :glass="glass"
@focus="isOpen = true"
@on-change="onTimeChange"/>
- :
+ :
- :
+ :
@@ -66,6 +72,7 @@
class="space-x-1"
type="single"
:dark="dark"
+ :glass="glass"
@on-change="handlePeriodChange as any">
{{ t('timePicker.text.am') }}
@@ -82,7 +89,10 @@
:key="time"
:class="[
'px-2 py-1 text-sm rounded-md select-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary',
- dark ? 'hover:bg-gray-700 active:bg-gray-600' : 'hover:bg-gray-100 active:bg-gray-200'
+ glass && !dark ? 'hover:bg-white/50 active:bg-white/70 text-gray-800' : '',
+ glass && dark ? 'hover:bg-white/20 active:bg-white/30 text-gray-100' : '',
+ !glass && dark ? 'hover:bg-gray-700 active:bg-gray-600' : '',
+ !glass && !dark ? 'hover:bg-gray-100 active:bg-gray-200' : ''
]"
@click="selectQuickTime(time)">
{{ time }}
@@ -109,7 +119,8 @@ const props = withDefaults(defineProps(), {
disabled: false,
clearable: true,
format: 'HH:mm',
- dark: false
+ dark: false,
+ glass: false
})
const emit = defineEmits()
diff --git a/src/ui/time-picker/types.ts b/src/ui/time-picker/types.ts
index b96708ff..7c234f28 100644
--- a/src/ui/time-picker/types.ts
+++ b/src/ui/time-picker/types.ts
@@ -9,6 +9,7 @@ export interface TimePickerProps
quickTimes?: string[] // see format
format?: 'HH:mm' | 'HH:mm:ss' | 'hh:mm A' | 'hh:mm:ss A'
dark?: boolean
+ glass?: boolean
}
export type TimePickerEmits = {
diff --git a/src/ui/tooltip/ShadcnTooltip.vue b/src/ui/tooltip/ShadcnTooltip.vue
index d1732c9d..b61d9d74 100644
--- a/src/ui/tooltip/ShadcnTooltip.vue
+++ b/src/ui/tooltip/ShadcnTooltip.vue
@@ -5,27 +5,35 @@
-
-
-
-
+ :style="[tooltipStyle, { maxWidth: computedMaxWidth }]">
+
+
+
+
+
diff --git a/src/ui/upload/ShadcnUpload.vue b/src/ui/upload/ShadcnUpload.vue
index 9401fb22..5b96a85f 100644
--- a/src/ui/upload/ShadcnUpload.vue
+++ b/src/ui/upload/ShadcnUpload.vue
@@ -3,9 +3,10 @@
-
-