-
- 蓝色
-
-
- 绿色
-
-
- 红色
-
-
- 紫色
-
+
+
+ {presetColors.map((color) => (
+
+ {color}
+
+ ))}
+
+
+ {customColors.map((color) => (
+
+ {color}
+
+ ))}
+
)
```
+### 禁用
+
+通过设置 `title={null}` 或者 `title=""` 可以禁用 Tooltip。
+
+```tsx
+import { Tooltip, Button } from 'RustUI';
+
+export default () => (
+
+ 鼠标移入无提示
+
+)
+```
+
## API
### Tooltip
diff --git a/src/components/display/tour/index.md b/src/components/display/tour/index.md
index c8966fd..51a2936 100644
--- a/src/components/display/tour/index.md
+++ b/src/components/display/tour/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据展示
title: Tour 漫游式引导
+order: 18
description: 漫游式引导组件,用于引导用户浏览页面功能,逐步介绍产品特性。
---
@@ -24,7 +25,7 @@ description: 漫游式引导组件,用于引导用户浏览页面功能,逐
```tsx
import { useState } from 'react';
-import { Tour, Button } from '@Pika/ui';
+import { Tour, Button } from 'RustUI';
export default () => {
const [open, setOpen] = useState(false);
@@ -52,7 +53,7 @@ export default () => {
```tsx
import { useState } from 'react';
-import { Tour, Button } from '@Pika/ui';
+import { Tour, Button } from 'RustUI';
export default () => {
const [open, setOpen] = useState(false);
@@ -80,7 +81,7 @@ export default () => {
```tsx
import { useState } from 'react';
-import { Tour, Button } from '@Pika/ui';
+import { Tour, Button } from 'RustUI';
export default () => {
const [open, setOpen] = useState(false);
diff --git a/src/components/display/tree/Tree.module.css b/src/components/display/tree/Tree.module.css
index d90484c..cd157e0 100644
--- a/src/components/display/tree/Tree.module.css
+++ b/src/components/display/tree/Tree.module.css
@@ -9,12 +9,13 @@
.node {
display: flex;
align-items: center;
- height: 24px;
+ height: 28px;
padding: 0 4px;
cursor: pointer;
user-select: none;
border-radius: var(--nv-radius-sm, 4px);
transition: background var(--nv-motion-fast, 0.15s) var(--nv-motion-ease-out, ease-out);
+ margin: 0;
}
.node:hover {
@@ -22,11 +23,11 @@
}
.node[data-selected] {
- background: #e6f4ff;
+ background: var(--nv-color-primary-bg, #f0f0ff);
}
.node[data-selected]:hover {
- background: #bae0ff;
+ background: var(--nv-color-primary-bg-hover, #e0e0ff);
}
.node[data-disabled] {
@@ -43,7 +44,7 @@
display: inline-flex;
flex-shrink: 0;
width: 24px;
- height: 24px;
+ height: 28px;
position: relative;
}
@@ -53,7 +54,7 @@
top: 0;
bottom: 0;
left: 11px;
- border-left: 1px solid var(--nv-border-color, #d9d9d9);
+ border-left: 1px solid var(--nv-color-border, #d9d9d9);
}
.indent[data-show-line][data-last]::before {
@@ -65,7 +66,7 @@
left: 11px;
top: 0;
bottom: 0;
- border-left: 1px solid var(--nv-border-color, #d9d9d9);
+ border-left: 1px solid var(--nv-color-border, #d9d9d9);
}
.switcher {
@@ -74,11 +75,16 @@
justify-content: center;
flex-shrink: 0;
width: 24px;
- height: 24px;
+ height: 28px;
cursor: pointer;
color: rgba(0, 0, 0, 0.45);
transition: transform var(--nv-motion-fast, 0.15s) var(--nv-motion-ease-out, ease-out);
transform: rotate(0deg);
+ border-radius: var(--nv-radius-sm, 4px);
+}
+
+.switcher:hover {
+ background: rgba(0, 0, 0, 0.04);
}
.switcher[data-expanded] {
@@ -90,6 +96,10 @@
color: rgba(0, 0, 0, 0.25);
}
+.switcher[data-leaf]:hover {
+ background: transparent;
+}
+
.switcher[data-leaf][data-show-leaf-icon] {
color: rgba(0, 0, 0, 0.45);
}
@@ -101,14 +111,18 @@
flex-shrink: 0;
width: 16px;
height: 16px;
- margin: 0 4px;
- border: 1px solid var(--nv-border-color, #d9d9d9);
+ margin: 0 6px 0 2px;
+ border: 1px solid var(--nv-color-border, #d9d9d9);
border-radius: var(--nv-radius-sm, 4px);
background: var(--nv-color-bg, #ffffff);
cursor: pointer;
transition: all var(--nv-motion-fast, 0.15s) ease;
}
+.checkbox:hover {
+ border-color: var(--nv-color-primary, #6c5ce7);
+}
+
.checkbox[data-checked] {
background: var(--nv-color-primary, #6c5ce7);
border-color: var(--nv-color-primary, #6c5ce7);
@@ -121,10 +135,14 @@
.checkbox[data-disabled] {
background: var(--nv-color-bg-secondary, #f5f5f5);
- border-color: var(--nv-border-color, #d9d9d9);
+ border-color: var(--nv-color-border, #d9d9d9);
cursor: not-allowed;
}
+.checkbox[data-disabled]:hover {
+ border-color: var(--nv-color-border, #d9d9d9);
+}
+
.checkboxCheck {
color: white;
font-size: 10px;
@@ -147,6 +165,8 @@
white-space: nowrap;
padding: 0 4px;
color: var(--nv-color-text, rgba(0, 0, 0, 0.88));
+ border-radius: var(--nv-radius-sm, 4px);
+ transition: background var(--nv-motion-fast, 0.15s) var(--nv-motion-ease-out, ease-out);
}
.title[data-disabled] {
@@ -158,7 +178,7 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
- margin-right: 4px;
+ margin-right: 6px;
color: rgba(0, 0, 0, 0.45);
}
@@ -168,8 +188,9 @@
justify-content: center;
flex-shrink: 0;
width: 24px;
- height: 24px;
+ height: 28px;
animation: nv-tree-spin 1s linear infinite;
+ color: var(--nv-color-primary, #6c5ce7);
}
@keyframes nv-tree-spin {
@@ -192,8 +213,7 @@
.directory .title[data-selected] {
background: var(--nv-color-primary, #6c5ce7);
color: #fff;
- border-radius: var(--nv-radius-sm, 4px);
- padding: 0 4px;
+ padding: 0 6px;
}
.directory .switcher[data-leaf] {
diff --git a/src/components/display/tree/Tree.tsx b/src/components/display/tree/Tree.tsx
index d16a10f..cd5f1d8 100644
--- a/src/components/display/tree/Tree.tsx
+++ b/src/components/display/tree/Tree.tsx
@@ -263,7 +263,7 @@ function flattenTree(
disabled: !!nodeDisabled,
selectable: nodeSelectable,
disableCheckbox: nodeDisableCheckbox ?? false,
- checkable: nodeCheckable ?? false,
+ checkable: nodeCheckable,
isLeaf,
level,
expanded: isExpanded,
@@ -1051,7 +1051,7 @@ const Tree = forwardRef
(
return visibleNodes.map(renderNode)
}
- const ITEM_HEIGHT = 24
+ const ITEM_HEIGHT = 28
const totalHeight = visibleNodes.length * ITEM_HEIGHT
const visibleCount = Math.ceil(height / ITEM_HEIGHT) + 2
const startIndex = Math.floor(scrollTop / ITEM_HEIGHT)
diff --git a/src/components/display/tree/index.md b/src/components/display/tree/index.md
index 1dae6a4..d8a4c61 100644
--- a/src/components/display/tree/index.md
+++ b/src/components/display/tree/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据展示
title: Tree 树形控件
+order: 19
description: 树形控件组件,用于展示层级数据,支持展开、选中、拖拽等功能。
---
@@ -20,32 +21,41 @@ description: 树形控件组件,用于展示层级数据,支持展开、选
### 基本用法
-最简单的用法,展示可展开、可选中、可勾选的树形结构。
+最简单的用法,展示可勾选、可选中、默认展开等功能。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: '0',
- title: '根节点',
+ key: '0-0',
+ title: 'parent 1',
children: [
- { key: '0-0', title: '子节点 0-0' },
- { key: '0-1', title: '子节点 0-1' },
{
- key: '0-2',
- title: '子节点 0-2',
+ key: '0-0-0',
+ title: 'parent 1-0',
children: [
- { key: '0-2-0', title: '叶子节点 0-2-0' },
- { key: '0-2-1', title: '叶子节点 0-2-1' },
+ { key: '0-0-0-0', title: 'leaf' },
+ { key: '0-0-0-1', title: 'leaf' },
],
},
+ {
+ key: '0-0-1',
+ title: 'parent 1-1',
+ children: [{ key: '0-0-1-0', title: 'sss' }],
+ },
],
},
];
export default () => (
-
+
)
```
@@ -54,7 +64,7 @@ export default () => (
通过 `checkable` 属性使树节点支持勾选,父子节点关联选中。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
@@ -120,57 +130,52 @@ export default () => (
```tsx
import { useState } from 'react';
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: '0',
- title: '前端技术',
+ key: '0-0',
+ title: 'parent 1',
children: [
{
- key: '0-0',
- title: 'React',
+ key: '0-0-0',
+ title: 'parent 1-0',
children: [
- { key: '0-0-0', title: 'React Router' },
- { key: '0-0-1', title: 'Redux' },
- { key: '0-0-2', title: 'Next.js' },
+ { key: '0-0-0-0', title: 'leaf' },
+ { key: '0-0-0-1', title: 'leaf' },
+ { key: '0-0-0-2', title: 'leaf' },
],
},
{
- key: '0-1',
- title: 'Vue',
+ key: '0-0-1',
+ title: 'parent 1-1',
children: [
- { key: '0-1-0', title: 'Vue Router' },
- { key: '0-1-1', title: 'Pinia' },
- { key: '0-1-2', title: 'Nuxt.js' },
+ { key: '0-0-1-0', title: 'leaf' },
+ { key: '0-0-1-1', title: 'leaf' },
],
},
- { key: '0-2', title: 'Angular' },
- ],
- },
- {
- key: '1',
- title: '后端技术',
- children: [
- { key: '1-0', title: 'Node.js' },
- { key: '1-1', title: 'Python' },
- { key: '1-2', title: 'Java' },
- { key: '1-3', title: 'Go' },
+ { key: '0-0-2', title: 'leaf' },
],
},
];
export default () => {
- const [expandedKeys, setExpandedKeys] = useState(['0']);
+ const [expandedKeys, setExpandedKeys] = useState(['0-0']);
const [selectedKeys, setSelectedKeys] = useState([]);
const [checkedKeys, setCheckedKeys] = useState([]);
return (
-
-
- 展开:{expandedKeys.join(', ') || '无'} |
- 选中:{selectedKeys.join(', ') || '无'} |
- 勾选:{checkedKeys.join(', ') || '无'}
+
+
+
+ 展开: {expandedKeys.join(', ') || '无'}
+
+
+ 选中: {selectedKeys.join(', ') || '无'}
+
+
+ 勾选: {checkedKeys.join(', ') || '无'}
+
{
```tsx
import { useState } from 'react';
-import { Tree } from '@Pika/ui';
+import { Tree, Input } from 'RustUI';
const treeData = [
{
@@ -232,20 +237,12 @@ export default () => {
const [searchValue, setSearchValue] = useState('');
return (
-
-
+
setSearchValue(e.target.value)}
- style={{
- marginBottom: 12,
- padding: '6px 12px',
- border: '1px solid #d9d9d9',
- borderRadius: 6,
- width: 220,
- fontSize: 14,
- outline: 'none',
- }}
+ style={{ width: 240 }}
/>
{
通过 `variant="directory"` 设置目录树模式,自动展示文件夹图标。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: 'src',
- title: 'src',
+ key: '0-0',
+ title: 'parent 1',
children: [
{
- key: 'components',
- title: 'components',
+ key: '0-0-0',
+ title: 'parent 1-0',
children: [
- { key: 'Button.tsx', title: 'Button.tsx', isLeaf: true },
- { key: 'Input.tsx', title: 'Input.tsx', isLeaf: true },
- { key: 'Modal.tsx', title: 'Modal.tsx', isLeaf: true },
+ { key: '0-0-0-0', title: 'leaf', isLeaf: true },
+ { key: '0-0-0-1', title: 'leaf', isLeaf: true },
+ { key: '0-0-0-2', title: 'leaf', isLeaf: true },
],
},
{
- key: 'pages',
- title: 'pages',
+ key: '0-0-1',
+ title: 'parent 1-1',
children: [
- { key: 'Home.tsx', title: 'Home.tsx', isLeaf: true },
- { key: 'About.tsx', title: 'About.tsx', isLeaf: true },
- { key: 'Dashboard.tsx', title: 'Dashboard.tsx', isLeaf: true },
+ { key: '0-0-1-0', title: 'leaf', isLeaf: true },
+ { key: '0-0-1-1', title: 'leaf', isLeaf: true },
],
},
{
- key: 'hooks',
- title: 'hooks',
- children: [
- { key: 'useAuth.ts', title: 'useAuth.ts', isLeaf: true },
- { key: 'useFetch.ts', title: 'useFetch.ts', isLeaf: true },
- ],
+ key: '0-0-2',
+ title: 'parent 1-2',
+ isLeaf: true,
},
- { key: 'App.tsx', title: 'App.tsx', isLeaf: true },
- { key: 'index.tsx', title: 'index.tsx', isLeaf: true },
],
},
{
- key: 'public',
- title: 'public',
+ key: '0-1',
+ title: 'parent 2',
children: [
- { key: 'index.html', title: 'index.html', isLeaf: true },
- { key: 'favicon.ico', title: 'favicon.ico', isLeaf: true },
- { key: 'logo.svg', title: 'logo.svg', isLeaf: true },
+ { key: '0-1-0', title: 'leaf', isLeaf: true },
+ { key: '0-1-1', title: 'leaf', isLeaf: true },
],
},
- { key: 'package.json', title: 'package.json', isLeaf: true },
- { key: 'tsconfig.json', title: 'tsconfig.json', isLeaf: true },
- { key: 'README.md', title: 'README.md', isLeaf: true },
];
export default () => (
@@ -318,6 +305,7 @@ export default () => (
data={treeData}
variant="directory"
defaultExpandAll
+ multiple
/>
)
```
@@ -327,23 +315,23 @@ export default () => (
通过 `draggable` 属性开启节点拖拽功能。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: '0',
- title: '节点一',
+ key: '0-0',
+ title: 'parent 1',
children: [
- { key: '0-0', title: '子节点 0-0' },
- { key: '0-1', title: '子节点 0-1' },
+ { key: '0-0-0', title: 'leaf 1' },
+ { key: '0-0-1', title: 'leaf 2' },
],
},
{
- key: '1',
- title: '节点二',
+ key: '0-1',
+ title: 'parent 2',
children: [
- { key: '1-0', title: '子节点 1-0' },
- { key: '1-1', title: '子节点 1-1' },
+ { key: '0-1-0', title: 'leaf 3' },
+ { key: '0-1-1', title: 'leaf 4' },
],
},
];
@@ -353,6 +341,7 @@ export default () => (
data={treeData}
draggable
defaultExpandAll
+ blockNode
onDrop={(info) => {
console.log('拖拽放置', info);
}}
@@ -365,33 +354,31 @@ export default () => (
通过 `showLine` 属性显示树节点之间的连接线。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: '0',
- title: '组织架构',
+ key: '0-0',
+ title: 'parent 1',
children: [
{
- key: '0-0',
- title: '技术部',
+ key: '0-0-0',
+ title: 'parent 1-0',
children: [
- { key: '0-0-0', title: '前端组' },
- { key: '0-0-1', title: '后端组' },
- { key: '0-0-2', title: '测试组' },
+ { key: '0-0-0-0', title: 'leaf' },
+ { key: '0-0-0-1', title: 'leaf' },
],
},
{
- key: '0-1',
- title: '产品部',
+ key: '0-0-1',
+ title: 'parent 1-1',
children: [
- { key: '0-1-0', title: '产品设计' },
- { key: '0-1-1', title: '用户研究' },
+ { key: '0-0-1-0', title: 'leaf' },
],
},
{
- key: '0-2',
- title: '运营部',
+ key: '0-0-2',
+ title: 'leaf',
},
],
},
@@ -400,7 +387,7 @@ const treeData = [
export default () => (
)
@@ -411,25 +398,25 @@ export default () => (
通过 `disabled` 属性禁用单个节点,使其不可选中和不可勾选。
```tsx
-import { Tree } from '@Pika/ui';
+import { Tree } from 'RustUI';
const treeData = [
{
- key: '0',
- title: '可用节点',
+ key: '0-0',
+ title: 'parent 1',
children: [
- { key: '0-0', title: '子节点 0-0' },
- { key: '0-1', title: '禁用节点', disabled: true },
- { key: '0-2', title: '子节点 0-2' },
+ { key: '0-0-0', title: 'leaf' },
+ { key: '0-0-1', title: 'disabled leaf', disabled: true },
+ { key: '0-0-2', title: 'leaf' },
],
},
{
- key: '1',
- title: '禁用分支',
+ key: '0-1',
+ title: 'disabled parent',
disabled: true,
children: [
- { key: '1-0', title: '子节点 1-0' },
- { key: '1-1', title: '子节点 1-1' },
+ { key: '0-1-0', title: 'leaf' },
+ { key: '0-1-1', title: 'leaf' },
],
},
];
@@ -443,6 +430,43 @@ export default () => (
)
```
+### 占据整行
+
+通过 `blockNode` 属性使节点占据整行宽度。
+
+```tsx
+import { Tree } from 'RustUI';
+
+const treeData = [
+ {
+ key: '0-0',
+ title: 'parent 1',
+ children: [
+ {
+ key: '0-0-0',
+ title: 'parent 1-0',
+ children: [
+ { key: '0-0-0-0', title: 'leaf' },
+ { key: '0-0-0-1', title: 'leaf' },
+ ],
+ },
+ {
+ key: '0-0-1',
+ title: 'parent 1-1',
+ },
+ ],
+ },
+];
+
+export default () => (
+
+)
+```
+
## API
| 属性 | 说明 | 类型 | 默认值 |
diff --git a/src/components/entry/auto-complete/index.md b/src/components/entry/auto-complete/index.md
index 5ae902a..b84d02b 100644
--- a/src/components/entry/auto-complete/index.md
+++ b/src/components/entry/auto-complete/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: AutoComplete 自动完成
+order: 0
description: 输入框自动完成组件,在用户输入时提供相关的输入建议,提升输入效率。
---
@@ -21,7 +22,7 @@ description: 输入框自动完成组件,在用户输入时提供相关的输
### 基本用法
```tsx
-import { AutoComplete } from '@Pika/ui';
+import { AutoComplete } from 'RustUI';
export default () => (
(
通过 `renderOption` 自定义选项的渲染内容。
```tsx
-import { AutoComplete } from '@Pika/ui';
+import { AutoComplete } from 'RustUI';
export default () => (
(
默认情况下,`filterOption` 为 `true`,会进行不区分大小写的过滤。设置为 `false` 可关闭过滤。
```tsx
-import { AutoComplete } from '@Pika/ui';
+import { AutoComplete } from 'RustUI';
export default () => (
(
```tsx
import { useState } from 'react';
-import { AutoComplete } from '@Pika/ui';
+import { AutoComplete } from 'RustUI';
export default () => {
const [options, setOptions] = useState([]);
diff --git a/src/components/entry/cascader/Cascader.tsx b/src/components/entry/cascader/Cascader.tsx
index dbe6b12..ad4a4c7 100644
--- a/src/components/entry/cascader/Cascader.tsx
+++ b/src/components/entry/cascader/Cascader.tsx
@@ -1,43 +1,28 @@
-import React, { useState, useCallback, useEffect, useRef, type FC, type ReactNode } from 'react'
+import React, { useState, useCallback, useRef, type FC, type ReactNode } from 'react'
import { inputTokens } from '../tokens'
import { useClickOutside } from '../../shared/hooks/useClickOutside'
import type { DataEntrySelectableProps } from '../common'
import styles from './Cascader.module.css'
-/**
- * 级联选择器选项接口
- */
export interface CascaderOption {
- /** 选项值 */
value: string | number
- /** 选项标签 */
label: string | ReactNode
- /** 是否禁用 */
disabled?: boolean
- /** 子选项 */
children?: CascaderOption[]
}
-/**
- * 级联选择器组件属性接口
- */
export interface CascaderProps extends DataEntrySelectableProps {
- /** 当前值(受控模式) */
value?: Array | null
- /** 默认值(非受控模式) */
defaultValue?: Array | null
- /** 选项数据 */
data?: CascaderOption[]
- /** 选项数据(别名) */
options?: CascaderOption[]
- /** 是否选择任意级即改变值 */
changeOnSelect?: boolean
- /** 次级菜单展开方式:点击或悬停 */
expandTrigger?: 'click' | 'hover'
- /** 选择完成后的回调 */
onChange?: (value: Array, selectedOptions: CascaderOption[]) => void
}
+const EMPTY_OPTIONS: CascaderOption[] = []
+
const Cascader: FC = ({
value,
defaultValue = [],
@@ -45,7 +30,7 @@ const Cascader: FC = ({
disabled = false,
size = 'middle',
status = 'default',
- data = [],
+ data = EMPTY_OPTIONS,
options,
showSearch = false,
showClear = false,
@@ -55,12 +40,20 @@ const Cascader: FC = ({
className,
style,
}) => {
+ const cascaderData = options ?? data
const [isOpen, setIsOpen] = useState(false)
const [internalValue, setInternalValue] = useState>(defaultValue ?? [])
- const cascaderData = options ?? data
- const [activeOptions, setActiveOptions] = useState([cascaderData])
+ const [expandedColumns, setExpandedColumns] = useState([])
const [searchValue, setSearchValue] = useState('')
const wrapperRef = useRef(null)
+ const prevDataRef = useRef(cascaderData)
+
+ if (prevDataRef.current !== cascaderData) {
+ prevDataRef.current = cascaderData
+ setExpandedColumns([])
+ }
+
+ const activeOptions = [cascaderData, ...expandedColumns]
useClickOutside(wrapperRef, () => setIsOpen(false), isOpen)
@@ -69,6 +62,7 @@ const Cascader: FC = ({
const height = inputTokens.height[size]
const fontSize = inputTokens.fontSize[size]
+ const paddingInline = inputTokens.paddingInline[size]
const radius = inputTokens.radius[size]
const findOption = (val: string | number, opts: CascaderOption[]): CascaderOption | null => {
@@ -109,8 +103,7 @@ const Cascader: FC = ({
onChange?.(newValue, selectedOpts)
if (option.children && option.children.length > 0) {
- const newActiveOptions = [...activeOptions.slice(0, level + 1), option.children]
- setActiveOptions(newActiveOptions)
+ setExpandedColumns((prev) => [...prev.slice(0, level), option.children!])
} else {
setIsOpen(false)
setSearchValue('')
@@ -121,7 +114,7 @@ const Cascader: FC = ({
setSearchValue('')
}
},
- [currentValue, activeOptions, isControlled, onChange, changeOnSelect]
+ [currentValue, isControlled, onChange, changeOnSelect, getSelectedOptions]
)
const handleClear = (e: React.MouseEvent) => {
@@ -134,18 +127,13 @@ const Cascader: FC = ({
const handlePanelMouseEnter = (option: CascaderOption, level: number) => {
if (expandTrigger === 'hover' && option.children) {
- const newActiveOptions = [...activeOptions.slice(0, level + 1), option.children]
- setActiveOptions(newActiveOptions)
+ setExpandedColumns((prev) => [...prev.slice(0, level), option.children!])
}
}
const selectedOptions = getSelectedOptions(currentValue)
const displayText = selectedOptions.map((opt) => opt.label).join(' / ')
- useEffect(() => {
- setActiveOptions([cascaderData])
- }, [cascaderData])
-
const renderOptions = (opts: CascaderOption[], level: number) => (
{opts.map((option) => {
@@ -180,7 +168,7 @@ const Cascader: FC
= ({
!disabled && setIsOpen(!isOpen)}
>
(
(
通过 `defaultValue` 设置默认选中的级联值。
```tsx
-import { Cascader } from '@Pika/ui';
+import { Cascader } from 'RustUI';
export default () => (
(
通过选项的 `disabled` 属性禁用特定选项。
```tsx
-import { Cascader } from '@Pika/ui';
+import { Cascader } from 'RustUI';
export default () => (
(
通过 `changeOnSelect` 实现选择任意级别即改变值。
```tsx
-import { Cascader } from '@Pika/ui';
+import { Cascader } from 'RustUI';
export default () => (
(
通过 `showSearch` 启用搜索功能。
```tsx
-import { Cascader } from '@Pika/ui';
+import { Cascader } from 'RustUI';
export default () => (
(
style={{
width: handleSize,
height: handleSize,
- transform: isChecked ? `translateX(${parseInt(width) - parseInt(handleSize) - 2}px)` : 'translateX(2px)',
+ transform: isChecked ? `translateX(${parseInt(width) - parseInt(handleSize) - 2}px) translateY(-50%)` : 'translateX(2px) translateY(-50%)',
}}
/>
{loading && }
diff --git a/src/components/entry/choice/index.md b/src/components/entry/choice/index.md
index a6e4411..cac093f 100644
--- a/src/components/entry/choice/index.md
+++ b/src/components/entry/choice/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Checkbox 多选框 / Radio 单选框 / Switch 开关
+order: 2
description: 多选框、单选框和开关组件,用于在一组选项中进行选择或切换状态的交互组件。
---
@@ -20,16 +21,14 @@ description: 多选框、单选框和开关组件,用于在一组选项中进
### 基本用法
```tsx
-import { Checkbox, Radio, Switch } from '@Pika/ui';
+import { Checkbox, Radio, Switch } from 'RustUI';
export default () => (
- <>
+
Checkbox
-
Radio
-
- >
+
)
```
@@ -38,7 +37,7 @@ export default () => (
使用 `Checkbox.Group` 进行多选组合。
```tsx
-import { Checkbox } from '@Pika/ui';
+import { Checkbox } from 'RustUI';
export default () => (
(
```tsx
import { useState } from 'react';
-import { Checkbox } from '@Pika/ui';
+import { Checkbox } from 'RustUI';
export default () => {
const [checked, setChecked] = useState(false);
@@ -75,22 +74,18 @@ export default () => {
通过 `disabled` 属性禁用多选框。
```tsx
-import { Checkbox, Radio, Switch } from '@Pika/ui';
+import { Checkbox, Radio, Switch } from 'RustUI';
export default () => (
- <>
+
禁用多选框
-
禁用且选中
-
禁用单选框
-
-
- >
+
)
```
@@ -100,7 +95,7 @@ export default () => (
```tsx
import { useState } from 'react';
-import { Checkbox } from '@Pika/ui';
+import { Checkbox } from 'RustUI';
const options = [
{ value: 'apple', label: '苹果' },
@@ -119,7 +114,7 @@ export default () => {
};
return (
- <>
+
{
>
全选
-
setCheckedList(val as string[])}
/>
- >
+
);
}
```
@@ -143,7 +137,7 @@ export default () => {
使用 `Radio.Group` 进行单选组合。
```tsx
-import { Radio } from '@Pika/ui';
+import { Radio } from 'RustUI';
export default () => (
(
通过 `layout` 属性设置竖向排列。
```tsx
-import { Radio } from '@Pika/ui';
+import { Radio } from 'RustUI';
export default () => (
(
通过 `checkedText` 和 `uncheckedText` 为 Switch 添加文字。
```tsx
-import { Switch } from '@Pika/ui';
+import { Switch } from 'RustUI';
export default () => (
- <>
+
-
- >
+
)
```
@@ -198,7 +191,7 @@ export default () => (
通过 `loading` 属性设置加载状态。
```tsx
-import { Switch } from '@Pika/ui';
+import { Switch } from 'RustUI';
export default () => (
diff --git a/src/components/entry/common.ts b/src/components/entry/common.ts
index ba525ff..b00c4a8 100644
--- a/src/components/entry/common.ts
+++ b/src/components/entry/common.ts
@@ -1,38 +1,27 @@
import type { CSSProperties } from 'react'
-import type { PikaSize, PikaStatus } from '../shared/types/common'
+import type { FoxSize, FoxStatus } from '../shared/types/common'
-export type DataEntrySize = PikaSize
+export type DataEntrySize = FoxSize
-export type DataEntryStatus = PikaStatus
+export type DataEntryStatus = FoxStatus
export interface DataEntryBaseProps {
- /** 控件尺寸 */
size?: DataEntrySize
- /** 是否禁用 */
disabled?: boolean
- /** 自定义类名 */
className?: string
- /** 自定义样式 */
style?: CSSProperties
}
export interface DataEntryInputProps extends DataEntryBaseProps {
- /** 控件状态 */
status?: DataEntryStatus
- /** 占位符文本 */
placeholder?: string
- /** 当前值 */
value?: unknown
- /** 默认值 */
defaultValue?: unknown
}
export interface DataEntryTextualProps extends DataEntryInputProps {
- /** 是否只读 */
readOnly?: boolean
- /** 获取焦点回调 */
onFocus?: () => void
- /** 失去焦点回调 */
onBlur?: () => void
}
diff --git a/src/components/entry/date-picker/index.md b/src/components/entry/date-picker/index.md
index b8aaf7d..8f2b5fb 100644
--- a/src/components/entry/date-picker/index.md
+++ b/src/components/entry/date-picker/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: DatePicker 日期选择器
+order: 3
description: 日期选择组件,用于选择日期、日期范围、月份、季度、年份等时间相关的选择。
---
@@ -21,7 +22,7 @@ description: 日期选择组件,用于选择日期、日期范围、月份、
### 基本用法
```tsx
-import { DatePicker } from '@Pika/ui';
+import { DatePicker } from 'RustUI';
export default () => (
@@ -33,7 +34,7 @@ export default () => (
通过 `showTime` 属性启用时间选择。
```tsx
-import { DatePicker } from '@Pika/ui';
+import { DatePicker } from 'RustUI';
export default () => (
@@ -45,7 +46,7 @@ export default () => (
通过 `disabledDate` 函数禁用特定日期。
```tsx
-import { DatePicker } from '@Pika/ui';
+import { DatePicker } from 'RustUI';
export default () => (
(
```tsx
import { useState } from 'react';
-import { DatePicker, Space, Button } from '@Pika/ui';
+import { DatePicker, Space, Button } from 'RustUI';
export default () => {
const [value, setValue] = useState(null);
diff --git a/src/components/entry/form/Form.module.css b/src/components/entry/form/Form.module.css
index 030dbe3..2ab30ba 100644
--- a/src/components/entry/form/Form.module.css
+++ b/src/components/entry/form/Form.module.css
@@ -5,9 +5,7 @@
}
.form[data-layout="horizontal"] {
- flex-direction: row;
- flex-wrap: wrap;
- align-items: flex-start;
+ flex-direction: column;
}
.form[data-layout="vertical"] {
@@ -18,15 +16,19 @@
flex-direction: row;
flex-wrap: wrap;
gap: var(--nv-space-md, 12px);
+ align-items: flex-start;
}
.form[data-layout="inline"] .formItem {
flex-direction: row;
+ align-items: center;
}
.form[data-layout="inline"] .label {
width: auto;
padding-right: var(--nv-space-sm, 8px);
+ padding-top: 0;
+ line-height: var(--nv-line-height-control, 32px);
}
.form[data-layout="inline"] .content {
@@ -44,6 +46,11 @@
align-items: flex-start;
}
+.formItem-inline {
+ flex-direction: row;
+ align-items: center;
+}
+
.label {
font-size: var(--nv-font-size-md, 14px);
font-weight: 500;
@@ -63,6 +70,7 @@
.label[data-layout="horizontal"] {
width: var(--nv-form-label-width, 120px);
padding-right: var(--nv-space-md, 12px);
+ padding-top: var(--nv-label-padding-top, 5px);
flex-shrink: 0;
}
@@ -74,7 +82,9 @@
.required {
color: var(--nv-color-error, #ff4d4f);
- margin-right: 2px;
+ margin-right: 4px;
+ font-family: SimSun, sans-serif;
+ line-height: 1;
}
.content {
@@ -82,12 +92,14 @@
display: flex;
flex-direction: column;
gap: var(--nv-space-xxs, 2px);
+ min-width: 0;
}
.content-horizontal {
flex: 1;
display: flex;
flex-direction: column;
+ min-width: 0;
}
.helpText {
@@ -95,6 +107,8 @@
color: var(--nv-color-text-tertiary, rgba(0, 0, 0, 0.45));
line-height: 1.5;
margin-top: var(--nv-space-xxs, 2px);
+ min-height: 0;
+ transition: color 0.3s;
}
.helpText[data-status="error"] {
diff --git a/src/components/entry/form/Form.tokens.css b/src/components/entry/form/Form.tokens.css
index f6e4632..65d74e9 100644
--- a/src/components/entry/form/Form.tokens.css
+++ b/src/components/entry/form/Form.tokens.css
@@ -1,13 +1,22 @@
.form {
--nv-form-label-width: 120px;
--nv-space-xxs: 2px;
+ --nv-space-xs: 4px;
--nv-space-sm: 8px;
--nv-space-md: 12px;
+ --nv-space-lg: 16px;
+ --nv-space-xl: 24px;
--nv-font-size-xs: 12px;
+ --nv-font-size-sm: 13px;
--nv-font-size-md: 14px;
+ --nv-line-height-control: 32px;
+ --nv-label-padding-top: 5px;
--nv-color-text: rgba(0, 0, 0, 0.88);
+ --nv-color-text-secondary: rgba(0, 0, 0, 0.65);
--nv-color-text-tertiary: rgba(0, 0, 0, 0.45);
--nv-color-error: #ff4d4f;
--nv-color-warning: #faad14;
--nv-color-primary: #6c5ce7;
+ --nv-color-success: #52c41a;
+ --nv-border-radius: 6px;
}
diff --git a/src/components/entry/form/Form.tsx b/src/components/entry/form/Form.tsx
index c263f7a..7defd67 100644
--- a/src/components/entry/form/Form.tsx
+++ b/src/components/entry/form/Form.tsx
@@ -463,8 +463,16 @@ const FormItem: FC = ({
const showError = showValidateMessage && touched && errors.length > 0
const showHelpText = helpText || showError
+ const formItemClassName = [
+ styles.formItem,
+ styles[`formItem-${layout}`],
+ className,
+ ]
+ .filter(Boolean)
+ .join(' ')
+
return (
-
+
{label && (
{required && * }
diff --git a/src/components/entry/form/index.md b/src/components/entry/form/index.md
index 74a0d9b..6b9c2bc 100644
--- a/src/components/entry/form/index.md
+++ b/src/components/entry/form/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Form 表单
+order: 5
description: 表单组件,用于数据收集、校验和提交,支持多种表单控件和复杂的校验规则。
---
@@ -11,8 +12,8 @@ description: 表单组件,用于数据收集、校验和提交,支持多种
## 何时使用
-- 需要收集用户数据时
-- 需要对输入进行校验时
+- 需要创建一个实体或收集信息时
+- 需要对输入的数据进行校验时
- 需要处理表单提交时
- 构建登录、注册、设置等表单页面时
@@ -20,28 +21,48 @@ description: 表单组件,用于数据收集、校验和提交,支持多种
### 基本用法
+最简单的用法,通过 `Form.Item` 包裹表单控件,实现数据收集。
+
```tsx
-import { Form, Input, Button } from '@Pika/ui';
+import { Form, Input, Button } from 'RustUI';
export default () => (
-
-
+
+
+
+
+
+
+
+ 提交
- Submit
)
```
### 表单验证
-通过 `rules` 属性设置校验规则,支持必填、长度、正则等多种校验方式。
+通过 `rules` 属性设置校验规则,支持必填、长度、正则、自定义校验等多种方式。
```tsx
-import { Form, Input, Button } from '@Pika/ui';
+import { Form, Input, Button } from 'RustUI';
export default () => (
+ {
+ if (value && value !== '待比较的密码值') {
+ throw new Error('两次输入的密码不一致');
+ }
+ },
+ },
+ ]}
+ >
+
+
- 提交
+ 注册
+
+
+)
+```
+
+### 表单布局
+
+通过 `layout` 属性设置表单布局方式,支持 `vertical`(垂直)、`horizontal`(水平)和 `inline`(行内)三种布局。
+
+```tsx
+import { Form, Input, Button, Select } from 'RustUI';
+
+export default () => (
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
+
+
+
+
+
+
+
+
+ 查询
+
+
+
+)
+```
+
+### 表单方法调用
+
+通过 `ref` 获取表单实例,可以调用 `getFieldsValue`、`setFieldsValue`、`resetFields`、`validateFields` 等方法。
+
+```tsx
+import { useRef } from 'react';
+import { Form, Input, Button, Space } from 'RustUI';
+import type { FormInstance } from 'RustUI';
+
+export default () => {
+ const formRef = useRef(null);
+
+ const handleFill = () => {
+ formRef.current?.setFieldsValue({
+ username: 'admin',
+ email: 'admin@example.com',
+ });
+ };
+
+ const handleReset = () => {
+ formRef.current?.resetFields();
+ };
+
+ const handleGetValues = () => {
+ const values = formRef.current?.getFieldsValue();
+ console.log('Current values:', values);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ 提交
+ 填充数据
+ 重置
+ 获取数据
+
+
+
+ );
+}
+```
+
+### 登录框
+
+常见的登录表单,结合水平布局和表单验证。
+
+```tsx
+import { Form, Input, Button, Checkbox } from 'RustUI';
+
+export default () => (
+
+
+
+
+
+
+
+ 记住我
+
+
+ 登录
)
@@ -84,17 +283,17 @@ export default () => (
### 动态增减表单项
-通过受控模式实现动态增减表单项。
+通过受控模式实现动态增减表单项,适用于需要动态添加/删除表单字段的场景。
```tsx
import { useState } from 'react';
-import { Form, Input, Button, Space } from '@Pika/ui';
+import { Form, Input, Button, Space } from 'RustUI';
export default () => {
const [fields, setFields] = useState([{ key: '0', name: '' }]);
const add = () => {
- const key = String(fields.length);
+ const key = String(Date.now());
setFields([...fields, { key, name: '' }]);
};
@@ -103,73 +302,66 @@ export default () => {
};
return (
-
+
-
+
{fields.length > 1 && (
- remove(index)}>删除
+ remove(index)}>删除
)}
))}
- 添加成员
+ + 添加成员
);
}
```
-### 表单布局
+### 表单禁用
-通过 `layout` 属性设置表单布局方式,支持 `vertical`、`horizontal` 和 `inline`。
+设置 `disabled` 属性后,表单内所有控件将变为禁用状态。
```tsx
-import { Form, Input, Button } from '@Pika/ui';
+import { Form, Input, Select, Button } from 'RustUI';
export default () => (
- <>
- 垂直布局(默认)
-
-
-
-
-
-
-
- 提交
-
-
-
- 水平布局
-
-
-
-
-
-
-
- 提交
-
-
-
- 行内布局
-
-
-
-
-
-
-
- 提交
-
-
- >
+
+
+
+
+
+
+
+
+
+
+ 提交
+
+
)
```
@@ -184,7 +376,7 @@ export default () => (
| initialValues | 表单初始值 | `Record` | `{}` |
| showValidateMessage | 是否显示校验提示 | `boolean` | `true` |
| validateTrigger | 校验触发方式 | `'onChange' \| 'onBlur' \| 'onSubmit'` | `'onChange'` |
-| disabled | 是否禁用 | `boolean` | `false` |
+| disabled | 是否禁用全部控件 | `boolean` | `false` |
| children | 表单内容 | `ReactNode` | - |
| onFinish | 表单提交成功回调 | `(values: Record) => void` | - |
| onFinishFailed | 表单提交失败回调 | `(errorInfo: FormErrorInfo) => void` | - |
@@ -219,3 +411,14 @@ export default () => (
| maxLength | 字符串最大长度 | `number` | - |
| pattern | 正则表达式校验 | `RegExp` | - |
| validator | 自定义校验函数 | `(value: unknown) => Promise` | - |
+
+### FormInstance
+
+| 方法 | 说明 | 类型 |
+| --- | --- | --- |
+| getFieldsValue | 获取所有字段值 | `() => Record` |
+| getFieldValue | 获取指定字段值 | `(name: string) => unknown` |
+| setFieldsValue | 设置字段值 | `(values: Record) => void` |
+| resetFields | 重置所有字段 | `() => void` |
+| validateFields | 校验所有字段 | `() => Promise>` |
+| submit | 提交表单 | `() => void` |
diff --git a/src/components/entry/input-number/index.md b/src/components/entry/input-number/index.md
index b1982ec..4a49b50 100644
--- a/src/components/entry/input-number/index.md
+++ b/src/components/entry/input-number/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: InputNumber 数字输入框
+order: 8
description: 数字输入框组件,仅允许输入数字,支持数值范围控制、步进调整等功能。
---
@@ -21,7 +22,7 @@ description: 数字输入框组件,仅允许输入数字,支持数值范围
### 基本用法
```tsx
-import { InputNumber } from '@Pika/ui';
+import { InputNumber } from 'RustUI';
export default () => (
@@ -33,14 +34,13 @@ export default () => (
通过 `precision` 设置小数精度,通过 `step` 设置步长。
```tsx
-import { InputNumber } from '@Pika/ui';
+import { InputNumber } from 'RustUI';
export default () => (
- <>
+
-
- >
+
)
```
@@ -49,16 +49,14 @@ export default () => (
通过 `size` 属性设置不同尺寸。
```tsx
-import { InputNumber } from '@Pika/ui';
+import { InputNumber } from 'RustUI';
export default () => (
- <>
+
-
-
- >
+
)
```
@@ -67,7 +65,7 @@ export default () => (
通过 `disabled` 属性禁用数字输入框。
```tsx
-import { InputNumber } from '@Pika/ui';
+import { InputNumber } from 'RustUI';
export default () => (
diff --git a/src/components/entry/input/index.md b/src/components/entry/input/index.md
index dc9b4cc..3688370 100644
--- a/src/components/entry/input/index.md
+++ b/src/components/entry/input/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Input 输入框
+order: 6
description: 基础文本输入框组件,支持前缀、后缀、图标、搜索等多种形态。
---
@@ -21,7 +22,7 @@ description: 基础文本输入框组件,支持前缀、后缀、图标、搜
### 基本用法
```tsx
-import { Input } from '@Pika/ui';
+import { Input } from 'RustUI';
export default () => (
@@ -33,16 +34,14 @@ export default () => (
通过 `prefix`、`suffix`、`prefixText`、`suffixText` 添加前后缀内容。
```tsx
-import { Input } from '@Pika/ui';
+import { Input } from 'RustUI';
export default () => (
- <>
+
-
-
- >
+
)
```
@@ -51,14 +50,13 @@ export default () => (
使用 `Input.Search` 创建搜索输入框,通过 `enterButton` 显示搜索按钮。
```tsx
-import { Input } from '@Pika/ui';
+import { Input } from 'RustUI';
export default () => (
- <>
+
console.log(value)} />
-
console.log(value)} />
- >
+
)
```
@@ -67,7 +65,7 @@ export default () => (
使用 `Input.Password` 创建密码输入框,支持切换密码可见性。
```tsx
-import { Input } from '@Pika/ui';
+import { Input } from 'RustUI';
export default () => (
@@ -79,7 +77,7 @@ export default () => (
使用 `TextArea` 组件实现多行文本输入。
```tsx
-import { TextArea } from '@Pika/ui';
+import { TextArea } from 'RustUI';
export default () => (
diff --git a/src/components/entry/mention/index.md b/src/components/entry/mention/index.md
index 5198a88..248b69c 100644
--- a/src/components/entry/mention/index.md
+++ b/src/components/entry/mention/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Mention 提及
+order: 9
description: 提及组件,在输入框中通过 @ 符号唤起用户列表,快速提及他人或标签。
---
@@ -21,7 +22,7 @@ description: 提及组件,在输入框中通过 @ 符号唤起用户列表,
### 基本用法
```tsx
-import { Mention } from '@Pika/ui';
+import { Mention } from 'RustUI';
export default () => (
(
输入 `@` 即可触发提及建议列表。
```tsx
-import { Mention } from '@Pika/ui';
+import { Mention } from 'RustUI';
export default () => (
(
```tsx
import { useState } from 'react';
-import { Mention } from '@Pika/ui';
+import { Mention } from 'RustUI';
const allUsers = [
{ value: 'zhangsan', label: '张三' },
diff --git a/src/components/entry/range-picker/index.md b/src/components/entry/range-picker/index.md
index 2d1000e..5da2ca4 100644
--- a/src/components/entry/range-picker/index.md
+++ b/src/components/entry/range-picker/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: RangePicker 范围选择器
+order: 4
description: 日期范围选择组件,用于选择一个日期范围,支持多种时间粒度的选择。
---
@@ -21,7 +22,7 @@ description: 日期范围选择组件,用于选择一个日期范围,支持
### 基本用法
```tsx
-import { RangePicker } from '@Pika/ui';
+import { RangePicker } from 'RustUI';
export default () => (
@@ -33,7 +34,7 @@ export default () => (
通过 `onChange` 回调获取选中的日期范围。
```tsx
-import { RangePicker } from '@Pika/ui';
+import { RangePicker } from 'RustUI';
export default () => (
(
```tsx
import { useState } from 'react';
-import { RangePicker, Space, Button } from '@Pika/ui';
+import { RangePicker, Space, Button } from 'RustUI';
export default () => {
const [value, setValue] = useState<[string | null, string | null] | null>(null);
diff --git a/src/components/entry/rate/index.md b/src/components/entry/rate/index.md
index 61263da..6aa1e49 100644
--- a/src/components/entry/rate/index.md
+++ b/src/components/entry/rate/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Rate 评分
+order: 10
description: 评分组件,用于对事物进行评级打分,支持自定义星星数量和半星选择。
---
@@ -21,7 +22,7 @@ description: 评分组件,用于对事物进行评级打分,支持自定义
### 基本用法
```tsx
-import { Rate } from '@Pika/ui';
+import { Rate } from 'RustUI';
export default () => (
@@ -33,7 +34,7 @@ export default () => (
通过 `showHalf` 属性支持半星选择。
```tsx
-import { Rate } from '@Pika/ui';
+import { Rate } from 'RustUI';
export default () => (
@@ -45,14 +46,13 @@ export default () => (
通过 `character` 属性自定义评分字符。
```tsx
-import { Rate } from '@Pika/ui';
+import { Rate } from 'RustUI';
export default () => (
- <>
+
-
- >
+
)
```
@@ -61,7 +61,7 @@ export default () => (
通过 `disabled` 属性设置只读模式,仅展示评分。
```tsx
-import { Rate } from '@Pika/ui';
+import { Rate } from 'RustUI';
export default () => (
diff --git a/src/components/entry/select/index.md b/src/components/entry/select/index.md
index 520f2c8..e709feb 100644
--- a/src/components/entry/select/index.md
+++ b/src/components/entry/select/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Select 选择器
+order: 11
description: 下拉选择器组件,用于从预设的选项中进行选择,支持单选、多选、搜索等功能。
---
@@ -20,71 +21,62 @@ description: 下拉选择器组件,用于从预设的选项中进行选择,
### 基本用法
+最简单的单选用法,通过 `data` 属性设置选项列表。
+
```tsx
-import { Select } from '@Pika/ui';
+import { Select } from 'RustUI';
export default () => (
console.log('Selected:', value, option)}
/>
)
```
### 多选
-通过 `mode="multiple"` 启用多选模式。
+通过 `mode="multiple"` 启用多选模式,选中值以标签形式展示。
```tsx
-import { Select } from '@Pika/ui';
+import { Select } from 'RustUI';
export default () => (
console.log('Selected:', value)}
/>
)
```
-### 搜索
+### 搜索筛选
-通过 `showSearch` 启用搜索功能。
+通过 `showSearch` 启用搜索功能,输入关键词可快速筛选选项。
```tsx
-import { Select } from '@Pika/ui';
+import { Select } from 'RustUI';
export default () => (
-)
-```
-
-### 分组
-
-通过选项数据实现分组选择。
-
-```tsx
-import { Select } from '@Pika/ui';
-
-export default () => (
- (
{ value: 'shenzhen', label: '深圳' },
{ value: 'chengdu', label: '成都' },
{ value: 'wuhan', label: '武汉' },
+ { value: 'hangzhou', label: '杭州' },
+ { value: 'nanjing', label: '南京' },
]}
+ onChange={(value) => console.log('Selected:', value)}
/>
)
```
-### 远程加载
+### 带清除按钮
-通过 `onSearch` 回调实现远程搜索加载选项。
+通过 `showClear` 属性显示清除按钮,方便用户快速清空已选值。
+
+```tsx
+import { Select } from 'RustUI';
+
+export default () => (
+ console.log('Cleared')}
+ />
+)
+```
+
+### 三种尺寸
+
+通过 `size` 属性设置选择器尺寸,支持 `large`、`middle`(默认)、`small` 三种尺寸。
+
+```tsx
+import { Select } from 'RustUI';
+
+const data = [
+ { value: '1', label: '选项一' },
+ { value: '2', label: '选项二' },
+ { value: '3', label: '选项三' },
+];
+
+export default () => (
+ <>
+
+
+
+
+
+ >
+)
+```
+
+### 禁用与禁用选项
+
+通过 `disabled` 属性禁用整个选择器,或通过选项的 `disabled` 属性禁用单个选项。
+
+```tsx
+import { Select } from 'RustUI';
+
+export default () => (
+ <>
+
+
+
+ >
+)
+```
+
+### 自定义状态
+
+通过 `status` 属性设置选择器的校验状态,适用于表单校验场景。
+
+```tsx
+import { Select } from 'RustUI';
+
+export default () => (
+ <>
+
+
+
+ >
+)
+```
+
+### 受控模式
+
+通过 `value` 和 `onChange` 实现受控模式,适用于需要外部控制选择器值的场景。
```tsx
import { useState } from 'react';
-import { Select } from '@Pika/ui';
+import { Select, Space } from 'RustUI';
export default () => {
- const [data, setData] = useState([
- { value: 'default', label: '输入关键词搜索' },
- ]);
+ const [value, setValue] = useState('lucy');
+
+ return (
+
+ setValue(val as string | number | null)}
+ data={[
+ { value: 'jack', label: 'Jack' },
+ { value: 'lucy', label: 'Lucy' },
+ { value: 'tom', label: 'Tom' },
+ ]}
+ />
+ 当前选中:{String(value ?? '无')}
+
+ );
+}
+```
+
+### 远程搜索
+
+通过 `onSearch` 回调配合 `showSearch` 实现远程搜索加载选项,适用于异步数据场景。
+
+```tsx
+import { useState } from 'react';
+import { Select } from 'RustUI';
+
+export default () => {
+ const [data, setData] = useState<{ value: string; label: string }[]>([]);
+ const [loading, setLoading] = useState(false);
const handleSearch = (value: string) => {
if (!value) {
- setData([{ value: 'default', label: '输入关键词搜索' }]);
+ setData([]);
return;
}
- setData([
- { value: `${value}-1`, label: `${value} 的结果 1` },
- { value: `${value}-2`, label: `${value} 的结果 2` },
- ]);
+ setLoading(true);
+ setTimeout(() => {
+ setData([
+ { value: `${value}-1`, label: `${value} 的结果 1` },
+ { value: `${value}-2`, label: `${value} 的结果 2` },
+ { value: `${value}-3`, label: `${value} 的结果 3` },
+ ]);
+ setLoading(false);
+ }, 300);
};
return (
@@ -126,33 +260,57 @@ export default () => {
showSearch
data={data}
onSearch={handleSearch}
- placeholder="请搜索"
+ placeholder="输入关键词搜索"
+ filterOption={false}
/>
);
}
```
+### 多选限制标签数
+
+通过 `maxTagCount` 属性限制多选模式下显示的标签数量,超出部分以 `+N` 形式展示。
+
+```tsx
+import { Select } from 'RustUI';
+
+export default () => (
+
+)
+```
+
## API
### Select
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
-| mode | 选择模式 | `'single' \| 'multiple'` | `'single'` |
-| value | 当前值(受控模式) | `string \| number \| Array` | - |
-| defaultValue | 默认值(非受控模式) | `string \| number \| Array` | - |
+| mode | 选择模式 | `'single' \| 'multiple' \| 'tags'` | `'single'` |
+| value | 当前值(受控模式) | `string \| number \| Array \| null` | - |
+| defaultValue | 默认值(非受控模式) | `string \| number \| Array \| null` | - |
| data | 选项数据 | `SelectOption[]` | `[]` |
-| placeholder | 占位符文本 | `string` | - |
+| placeholder | 占位符文本 | `string` | `'请选择'` |
| showSearch | 是否支持搜索 | `boolean` | `false` |
| showClear | 是否显示清空按钮 | `boolean` | `false` |
| autoClearSearchValue | 多选模式下选择时是否清空搜索 | `boolean` | `true` |
-| maxTagCount | 最多显示标签数 | `number` | - |
+| maxTagCount | 最多显示标签数 | `number \| { count: number; omit: string }` | - |
| tokenSeparators | 自动分词分隔符 | `string[]` | - |
-| filterOption | 过滤选项函数 | `boolean \| ((input: string, option: SelectOption) => boolean)` | - |
+| filterOption | 过滤选项函数 | `boolean \| ((input: string, option: SelectOption) => boolean)` | `true` |
| size | 控件尺寸 | `'small' \| 'middle' \| 'large'` | `'middle'` |
| status | 控件状态 | `'default' \| 'success' \| 'warning' \| 'error' \| 'info'` | `'default'` |
| disabled | 是否禁用 | `boolean` | `false` |
-| onChange | 值变化回调 | `(value: unknown, option: SelectOption \| SelectOption[]) => void` | - |
+| onChange | 值变化回调 | `(value: unknown, option: SelectOption \| SelectOption[] \| undefined) => void` | - |
| onSearch | 搜索回调 | `(value: string) => void` | - |
| onClear | 清空回调 | `() => void` | - |
| onFocus | 获取焦点回调 | `() => void` | - |
@@ -167,3 +325,4 @@ export default () => {
| value | 选项值 | `string \| number` | - |
| label | 选项标签 | `ReactNode` | - |
| disabled | 是否禁用 | `boolean` | `false` |
+| title | 选项标题 | `string` | - |
diff --git a/src/components/entry/slider/Slider.module.css b/src/components/entry/slider/Slider.module.css
index 3d22a9d..de84316 100644
--- a/src/components/entry/slider/Slider.module.css
+++ b/src/components/entry/slider/Slider.module.css
@@ -11,12 +11,25 @@
cursor: not-allowed;
}
+.vertical {
+ display: inline-flex;
+ align-items: stretch;
+ justify-content: center;
+ padding: 0 10px;
+ height: 300px;
+}
+
.track {
position: relative;
flex: 1;
cursor: pointer;
}
+.vertical .track {
+ flex: none;
+ height: 100%;
+}
+
.rail {
position: absolute;
top: 50%;
@@ -27,6 +40,14 @@
border-radius: var(--nv-radius-round, 9999px);
}
+.vertical .rail {
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ right: auto;
+ transform: translateX(-50%);
+}
+
.trackFill {
position: absolute;
top: 50%;
@@ -37,6 +58,14 @@
transition: width var(--nv-motion-fast, 0.1s) ease;
}
+.vertical .trackFill {
+ top: auto;
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ transition: height var(--nv-motion-fast, 0.1s) ease;
+}
+
.handle {
position: absolute;
top: 50%;
@@ -49,6 +78,12 @@
transition: box-shadow var(--nv-motion-fast, 0.15s) ease;
}
+.vertical .handle {
+ top: auto;
+ left: 50%;
+ transform: translate(-50%, 50%);
+}
+
.handle:hover {
box-shadow: 0 0 0 4px rgba(108, 92, 231, 0.2);
}
@@ -67,6 +102,14 @@
pointer-events: none;
}
+.vertical .ticks {
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ right: auto;
+ transform: translateX(-50%);
+}
+
.tick {
position: absolute;
width: 1px;
@@ -75,6 +118,12 @@
transform: translateX(-50%);
}
+.vertical .tick {
+ width: 6px;
+ height: 1px;
+ transform: translateY(50%);
+}
+
.mark {
position: absolute;
top: 100%;
@@ -82,6 +131,14 @@
margin-top: 4px;
}
+.vertical .mark {
+ top: auto;
+ left: 100%;
+ transform: translateY(50%);
+ margin-top: 0;
+ margin-left: 4px;
+}
+
.markLabel {
font-size: var(--nv-font-size-xs, 12px);
color: var(--nv-color-text-tertiary, rgba(0, 0, 0, 0.45));
@@ -105,6 +162,15 @@
transition: opacity var(--nv-motion-fast, 0.15s) ease;
}
+.vertical .tooltip {
+ bottom: auto;
+ left: 100%;
+ top: 50%;
+ transform: translateY(-50%);
+ margin-bottom: 0;
+ margin-left: 8px;
+}
+
.handle:hover .tooltip {
opacity: 1;
visibility: visible;
@@ -119,3 +185,12 @@
border: 4px solid transparent;
border-top-color: var(--nv-color-text, rgba(0, 0, 0, 0.88));
}
+
+.vertical .tooltip::after {
+ top: 50%;
+ left: auto;
+ right: 100%;
+ transform: translateY(-50%);
+ border: 4px solid transparent;
+ border-right-color: var(--nv-color-text, rgba(0, 0, 0, 0.88));
+}
diff --git a/src/components/entry/slider/Slider.tsx b/src/components/entry/slider/Slider.tsx
index 8c852e1..1b5ee92 100644
--- a/src/components/entry/slider/Slider.tsx
+++ b/src/components/entry/slider/Slider.tsx
@@ -2,29 +2,17 @@ import React, { useCallback, useMemo, useRef, useState, type FC, type MouseEvent
import type { DataEntryBaseProps } from '../common'
import styles from './Slider.module.css'
-/**
- * 滑块组件属性接口
- */
export interface SliderProps extends DataEntryBaseProps {
- /** 当前值(受控模式) */
value?: number
- /** 默认值(非受控模式) */
defaultValue?: number
- /** 最小值 */
min?: number
- /** 最大值 */
max?: number
- /** 步长 */
step?: number
- /** 是否显示刻度 */
showTicks?: boolean
- /** 刻度标记 */
marks?: Record
- /** 是否显示提示或自定义提示格式化函数 */
tooltip?: boolean | { formatter?: (value: number) => ReactNode }
- /** 值变化回调 */
+ vertical?: boolean
onChange?: (value: number) => void
- /** 值变化完成回调 */
onAfterChange?: (value: number) => void
}
@@ -39,6 +27,7 @@ const Slider: FC = ({
showTicks = false,
marks,
tooltip,
+ vertical = false,
onChange,
onAfterChange,
className,
@@ -59,15 +48,20 @@ const Slider: FC = ({
)
const getValueFromPosition = useCallback(
- (clientX: number) => {
+ (clientX: number, clientY: number) => {
if (!trackRef.current) return min
const rect = trackRef.current.getBoundingClientRect()
- const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
+ let percent: number
+ if (vertical) {
+ percent = Math.max(0, Math.min(1, 1 - (clientY - rect.top) / rect.height))
+ } else {
+ percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
+ }
const rawValue = min + percent * (max - min)
const steppedValue = Math.round(rawValue / step) * step
return Math.max(min, Math.min(max, steppedValue))
},
- [min, max, step]
+ [min, max, step, vertical]
)
const handleMouseDown = (e: MouseEvent) => {
@@ -75,14 +69,14 @@ const Slider: FC = ({
e.preventDefault()
setIsDragging(true)
- const newValue = getValueFromPosition(e.clientX)
+ const newValue = getValueFromPosition(e.clientX, e.clientY)
if (!isControlled) {
setInternalValue(newValue)
}
onChange?.(newValue)
const handleMouseMove = (moveEvent: globalThis.MouseEvent) => {
- const movedValue = getValueFromPosition(moveEvent.clientX)
+ const movedValue = getValueFromPosition(moveEvent.clientX, moveEvent.clientY)
if (!isControlled) {
setInternalValue(movedValue)
}
@@ -115,20 +109,30 @@ const Slider: FC = ({
const markElements = useMemo(() => {
if (!marks) return null
- return Object.entries(marks).map(([key, label]) => (
-
- {label}
-
- ))
- }, [marks, getPercent])
+ return Object.entries(marks).map(([key, label]) => {
+ const markPercent = getPercent(Number(key))
+ return (
+
+ {label}
+
+ )
+ })
+ }, [marks, getPercent, vertical])
return (
= ({
{showTicks && (
@@ -153,7 +166,10 @@ const Slider: FC
= ({
))}
@@ -161,12 +177,20 @@ const Slider: FC
= ({
{renderTooltip(currentValue as number)}
diff --git a/src/components/entry/slider/index.md b/src/components/entry/slider/index.md
index 7d227c0..af5a67a 100644
--- a/src/components/entry/slider/index.md
+++ b/src/components/entry/slider/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: Slider 滑动输入条
+order: 12
description: 滑动输入条组件,通过拖动滑块在一个固定区间内选择数值,支持单值和范围选择。
---
@@ -21,7 +22,7 @@ description: 滑动输入条组件,通过拖动滑块在一个固定区间内
### 基本用法
```tsx
-import { Slider } from '@Pika/ui';
+import { Slider } from 'RustUI';
export default () => (
@@ -34,7 +35,7 @@ export default () => (
```tsx
import { useState } from 'react';
-import { Slider } from '@Pika/ui';
+import { Slider } from 'RustUI';
export default () => {
const [value, setValue] = useState(30);
@@ -47,7 +48,7 @@ export default () => {
通过 `marks` 属性设置自定义标记。
```tsx
-import { Slider } from '@Pika/ui';
+import { Slider } from 'RustUI';
export default () => (
(
### 垂直滑块
-通过自定义样式实现垂直方向的滑块。
+通过 `vertical` 属性实现垂直方向的滑块。
```tsx
-import { Slider } from '@Pika/ui';
+import { Slider } from 'RustUI';
export default () => (
-
-
-
+
)
```
@@ -89,6 +88,7 @@ export default () => (
| showTicks | 是否显示刻度 | `boolean` | `false` |
| marks | 刻度标记 | `Record` | - |
| tooltip | 是否显示提示或自定义提示格式化函数 | `boolean \| { formatter?: (value: number) => ReactNode }` | - |
+| vertical | 是否垂直方向 | `boolean` | `false` |
| size | 控件尺寸 | `'small' \| 'middle' \| 'large'` | `'middle'` |
| disabled | 是否禁用 | `boolean` | `false` |
| onChange | 值变化回调 | `(value: number) => void` | - |
diff --git a/src/components/entry/textarea/index.md b/src/components/entry/textarea/index.md
index c471825..f7d6165 100644
--- a/src/components/entry/textarea/index.md
+++ b/src/components/entry/textarea/index.md
@@ -1,7 +1,8 @@
----
+---
nav: 组件
group: 数据录入
title: TextArea 文本域
+order: 7
description: 多行文本输入组件,支持自动调整高度、限制字数等功能。
---
@@ -21,7 +22,7 @@ description: 多行文本输入组件,支持自动调整高度、限制字数
### 基本用法
```tsx
-import { TextArea } from '@Pika/ui';
+import { TextArea } from 'RustUI';
export default () => (
@@ -33,14 +34,13 @@ export default () => (
通过 `rows` 属性设置文本域的行数。
```tsx
-import { TextArea } from '@Pika/ui';
+import { TextArea } from 'RustUI';
export default () => (
- <>
+
-
- >
+
)
```
@@ -49,7 +49,7 @@ export default () => (
通过 `showCount` 和 `maxLength` 显示字数统计和限制。
```tsx
-import { TextArea } from '@Pika/ui';
+import { TextArea } from 'RustUI';
export default () => (