@unif/react-native-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +103 -0
- package/lib/module/README.md +99 -0
- package/lib/module/action-sheet/index.js +63 -0
- package/lib/module/action-sheet/index.js.map +1 -0
- package/lib/module/action-sheet/index.md +69 -0
- package/lib/module/action-sheet/style/index.js +38 -0
- package/lib/module/action-sheet/style/index.js.map +1 -0
- package/lib/module/avatar/index.js +42 -0
- package/lib/module/avatar/index.js.map +1 -0
- package/lib/module/avatar/index.md +51 -0
- package/lib/module/avatar/style/index.js +17 -0
- package/lib/module/avatar/style/index.js.map +1 -0
- package/lib/module/button/index.js +52 -0
- package/lib/module/button/index.js.map +1 -0
- package/lib/module/button/index.md +90 -0
- package/lib/module/button/style/index.js +94 -0
- package/lib/module/button/style/index.js.map +1 -0
- package/lib/module/center/index.js +21 -0
- package/lib/module/center/index.js.map +1 -0
- package/lib/module/center/index.md +28 -0
- package/lib/module/center/style/index.js +10 -0
- package/lib/module/center/style/index.js.map +1 -0
- package/lib/module/chip/index.js +59 -0
- package/lib/module/chip/index.js.map +1 -0
- package/lib/module/chip/index.md +60 -0
- package/lib/module/chip/style/index.js +40 -0
- package/lib/module/chip/style/index.js.map +1 -0
- package/lib/module/column/index.js +30 -0
- package/lib/module/column/index.js.map +1 -0
- package/lib/module/column/index.md +34 -0
- package/lib/module/column/style/index.js +9 -0
- package/lib/module/column/style/index.js.map +1 -0
- package/lib/module/divider/index.js +21 -0
- package/lib/module/divider/index.js.map +1 -0
- package/lib/module/divider/index.md +44 -0
- package/lib/module/divider/style/index.js +17 -0
- package/lib/module/divider/style/index.js.map +1 -0
- package/lib/module/hooks/index.js +4 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/index.md +71 -0
- package/lib/module/hooks/useMergeStyles.js +27 -0
- package/lib/module/hooks/useMergeStyles.js.map +1 -0
- package/lib/module/index.js +29 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/input/index.js +69 -0
- package/lib/module/input/index.js.map +1 -0
- package/lib/module/input/index.md +73 -0
- package/lib/module/input/style/index.js +27 -0
- package/lib/module/input/style/index.js.map +1 -0
- package/lib/module/list-item/index.js +69 -0
- package/lib/module/list-item/index.js.map +1 -0
- package/lib/module/list-item/index.md +101 -0
- package/lib/module/list-item/style/index.js +39 -0
- package/lib/module/list-item/style/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/popover/index.js +60 -0
- package/lib/module/popover/index.js.map +1 -0
- package/lib/module/popover/index.md +57 -0
- package/lib/module/popover/style/index.js +22 -0
- package/lib/module/popover/style/index.js.map +1 -0
- package/lib/module/row/index.js +31 -0
- package/lib/module/row/index.js.map +1 -0
- package/lib/module/row/index.md +46 -0
- package/lib/module/row/style/index.js +15 -0
- package/lib/module/row/style/index.js.map +1 -0
- package/lib/module/space/index.js +20 -0
- package/lib/module/space/index.js.map +1 -0
- package/lib/module/space/index.md +31 -0
- package/lib/module/space/style/index.js +9 -0
- package/lib/module/space/style/index.js.map +1 -0
- package/lib/module/text/index.js +30 -0
- package/lib/module/text/index.js.map +1 -0
- package/lib/module/text/index.md +63 -0
- package/lib/module/text/style/index.js +51 -0
- package/lib/module/text/style/index.js.map +1 -0
- package/lib/module/theme/config.js +27 -0
- package/lib/module/theme/config.js.map +1 -0
- package/lib/module/theme/tokens.js +67 -0
- package/lib/module/theme/tokens.js.map +1 -0
- package/lib/module/touchable/index.js +35 -0
- package/lib/module/touchable/index.js.map +1 -0
- package/lib/module/touchable/index.md +42 -0
- package/lib/module/touchable/style/index.js +9 -0
- package/lib/module/touchable/style/index.js.map +1 -0
- package/lib/module/wave-animation/index.js +67 -0
- package/lib/module/wave-animation/index.js.map +1 -0
- package/lib/module/wave-animation/index.md +50 -0
- package/lib/typescript/jest.setup.d.ts +1 -0
- package/lib/typescript/jest.setup.d.ts.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/action-sheet/index.d.ts +22 -0
- package/lib/typescript/src/action-sheet/index.d.ts.map +1 -0
- package/lib/typescript/src/action-sheet/style/index.d.ts +16 -0
- package/lib/typescript/src/action-sheet/style/index.d.ts.map +1 -0
- package/lib/typescript/src/avatar/index.d.ts +19 -0
- package/lib/typescript/src/avatar/index.d.ts.map +1 -0
- package/lib/typescript/src/avatar/style/index.d.ts +7 -0
- package/lib/typescript/src/avatar/style/index.d.ts.map +1 -0
- package/lib/typescript/src/button/index.d.ts +24 -0
- package/lib/typescript/src/button/index.d.ts.map +1 -0
- package/lib/typescript/src/button/style/index.d.ts +16 -0
- package/lib/typescript/src/button/style/index.d.ts.map +1 -0
- package/lib/typescript/src/center/index.d.ts +11 -0
- package/lib/typescript/src/center/index.d.ts.map +1 -0
- package/lib/typescript/src/center/style/index.d.ts +5 -0
- package/lib/typescript/src/center/style/index.d.ts.map +1 -0
- package/lib/typescript/src/chip/index.d.ts +22 -0
- package/lib/typescript/src/chip/index.d.ts.map +1 -0
- package/lib/typescript/src/chip/style/index.d.ts +10 -0
- package/lib/typescript/src/chip/style/index.d.ts.map +1 -0
- package/lib/typescript/src/column/index.d.ts +20 -0
- package/lib/typescript/src/column/index.d.ts.map +1 -0
- package/lib/typescript/src/column/style/index.d.ts +4 -0
- package/lib/typescript/src/column/style/index.d.ts.map +1 -0
- package/lib/typescript/src/divider/index.d.ts +13 -0
- package/lib/typescript/src/divider/index.d.ts.map +1 -0
- package/lib/typescript/src/divider/style/index.d.ts +9 -0
- package/lib/typescript/src/divider/style/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +2 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useMergeStyles.d.ts +13 -0
- package/lib/typescript/src/hooks/useMergeStyles.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +39 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/input/index.d.ts +29 -0
- package/lib/typescript/src/input/index.d.ts.map +1 -0
- package/lib/typescript/src/input/style/index.d.ts +8 -0
- package/lib/typescript/src/input/style/index.d.ts.map +1 -0
- package/lib/typescript/src/list-item/index.d.ts +25 -0
- package/lib/typescript/src/list-item/index.d.ts.map +1 -0
- package/lib/typescript/src/list-item/style/index.d.ts +17 -0
- package/lib/typescript/src/list-item/style/index.d.ts.map +1 -0
- package/lib/typescript/src/popover/index.d.ts +27 -0
- package/lib/typescript/src/popover/index.d.ts.map +1 -0
- package/lib/typescript/src/popover/style/index.d.ts +7 -0
- package/lib/typescript/src/popover/style/index.d.ts.map +1 -0
- package/lib/typescript/src/row/index.d.ts +22 -0
- package/lib/typescript/src/row/index.d.ts.map +1 -0
- package/lib/typescript/src/row/style/index.d.ts +8 -0
- package/lib/typescript/src/row/style/index.d.ts.map +1 -0
- package/lib/typescript/src/space/index.d.ts +15 -0
- package/lib/typescript/src/space/index.d.ts.map +1 -0
- package/lib/typescript/src/space/style/index.d.ts +7 -0
- package/lib/typescript/src/space/style/index.d.ts.map +1 -0
- package/lib/typescript/src/text/index.d.ts +18 -0
- package/lib/typescript/src/text/index.d.ts.map +1 -0
- package/lib/typescript/src/text/style/index.d.ts +35 -0
- package/lib/typescript/src/text/style/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/config.d.ts +18 -0
- package/lib/typescript/src/theme/config.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens.d.ts +61 -0
- package/lib/typescript/src/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/src/touchable/index.d.ts +19 -0
- package/lib/typescript/src/touchable/index.d.ts.map +1 -0
- package/lib/typescript/src/touchable/style/index.d.ts +4 -0
- package/lib/typescript/src/touchable/style/index.d.ts.map +1 -0
- package/lib/typescript/src/wave-animation/index.d.ts +20 -0
- package/lib/typescript/src/wave-animation/index.d.ts.map +1 -0
- package/lib/typescript/tests/component/component.coverage.test.d.ts +2 -0
- package/lib/typescript/tests/component/component.coverage.test.d.ts.map +1 -0
- package/lib/typescript/tests/component/component.interaction.test.d.ts +2 -0
- package/lib/typescript/tests/component/component.interaction.test.d.ts.map +1 -0
- package/lib/typescript/tests/component/component.snapshot.test.d.ts +2 -0
- package/lib/typescript/tests/component/component.snapshot.test.d.ts.map +1 -0
- package/lib/typescript/tests/e2e/app.e2e.test.d.ts +2 -0
- package/lib/typescript/tests/e2e/app.e2e.test.d.ts.map +1 -0
- package/lib/typescript/tests/integration/ui.integration.test.d.ts +2 -0
- package/lib/typescript/tests/integration/ui.integration.test.d.ts.map +1 -0
- package/lib/typescript/tests/unit/mergeStyles.unit.test.d.ts +2 -0
- package/lib/typescript/tests/unit/mergeStyles.unit.test.d.ts.map +1 -0
- package/lib/typescript/tests/unit/public-api.unit.test.d.ts +2 -0
- package/lib/typescript/tests/unit/public-api.unit.test.d.ts.map +1 -0
- package/lib/typescript/tests/unit/theme-config.unit.test.d.ts +2 -0
- package/lib/typescript/tests/unit/theme-config.unit.test.d.ts.map +1 -0
- package/package.json +134 -0
- package/src/README.md +99 -0
- package/src/action-sheet/index.md +69 -0
- package/src/action-sheet/index.tsx +85 -0
- package/src/action-sheet/style/index.tsx +52 -0
- package/src/avatar/index.md +51 -0
- package/src/avatar/index.tsx +56 -0
- package/src/avatar/style/index.tsx +21 -0
- package/src/button/index.md +90 -0
- package/src/button/index.tsx +86 -0
- package/src/button/style/index.tsx +67 -0
- package/src/center/index.md +28 -0
- package/src/center/index.tsx +18 -0
- package/src/center/style/index.tsx +8 -0
- package/src/chip/index.md +60 -0
- package/src/chip/index.tsx +80 -0
- package/src/chip/style/index.tsx +47 -0
- package/src/column/index.md +34 -0
- package/src/column/index.tsx +43 -0
- package/src/column/style/index.tsx +7 -0
- package/src/divider/index.md +44 -0
- package/src/divider/index.tsx +30 -0
- package/src/divider/style/index.tsx +13 -0
- package/src/hooks/index.md +71 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useMergeStyles.ts +27 -0
- package/src/index.tsx +49 -0
- package/src/input/index.md +73 -0
- package/src/input/index.tsx +95 -0
- package/src/input/style/index.tsx +32 -0
- package/src/list-item/index.md +101 -0
- package/src/list-item/index.tsx +91 -0
- package/src/list-item/style/index.tsx +41 -0
- package/src/popover/index.md +57 -0
- package/src/popover/index.tsx +80 -0
- package/src/popover/style/index.tsx +23 -0
- package/src/row/index.md +46 -0
- package/src/row/index.tsx +47 -0
- package/src/row/style/index.tsx +14 -0
- package/src/space/index.md +31 -0
- package/src/space/index.tsx +28 -0
- package/src/space/style/index.tsx +3 -0
- package/src/text/index.md +63 -0
- package/src/text/index.tsx +45 -0
- package/src/text/style/index.tsx +32 -0
- package/src/theme/config.ts +26 -0
- package/src/theme/tokens.ts +66 -0
- package/src/touchable/index.md +42 -0
- package/src/touchable/index.tsx +45 -0
- package/src/touchable/style/index.tsx +5 -0
- package/src/wave-animation/index.md +50 -0
- package/src/wave-animation/index.tsx +93 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ViewStyle, ImageStyle } from 'react-native';
|
|
2
|
+
import { tokens } from '../../theme/tokens';
|
|
3
|
+
|
|
4
|
+
export type AvatarSemanticStyles = {
|
|
5
|
+
root?: ViewStyle;
|
|
6
|
+
image?: ImageStyle;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_STYLES: AvatarSemanticStyles = {
|
|
10
|
+
root: {
|
|
11
|
+
justifyContent: 'center',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
backgroundColor: tokens.colorBgElevated,
|
|
14
|
+
overflow: 'hidden',
|
|
15
|
+
...tokens.shadowSm,
|
|
16
|
+
},
|
|
17
|
+
image: {
|
|
18
|
+
width: '100%' as any,
|
|
19
|
+
height: '100%' as any,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Button 语义化按钮
|
|
2
|
+
|
|
3
|
+
支持 `type` / `size` 变体和 `styles` 语义插槽覆盖。使用 `Pressable` 实现按下态反馈。
|
|
4
|
+
|
|
5
|
+
## 何时使用
|
|
6
|
+
|
|
7
|
+
- 替代 `<TouchableOpacity>` + 手写按钮样式的组合
|
|
8
|
+
- 需要统一按钮风格(主按钮、描边按钮、幽灵按钮)时
|
|
9
|
+
|
|
10
|
+
## 代码演示
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
// 主按钮
|
|
14
|
+
<Button onPress={handleConfirm}>确认下单</Button>
|
|
15
|
+
|
|
16
|
+
// 描边按钮
|
|
17
|
+
<Button type="outline" onPress={handleModify}>修改</Button>
|
|
18
|
+
|
|
19
|
+
// 幽灵按钮
|
|
20
|
+
<Button type="ghost" onPress={handlePush}>推送经销商</Button>
|
|
21
|
+
|
|
22
|
+
// 并排等宽
|
|
23
|
+
<View style={{flexDirection: 'row', gap: 10}}>
|
|
24
|
+
<Button type="outline" block onPress={handleModify}>修改</Button>
|
|
25
|
+
<Button block onPress={handleConfirm}>确认</Button>
|
|
26
|
+
</View>
|
|
27
|
+
|
|
28
|
+
// 带图标
|
|
29
|
+
<Button icon={<Icon name="add" size={16} color="#FFF" />} onPress={handleAdd}>
|
|
30
|
+
添加
|
|
31
|
+
</Button>
|
|
32
|
+
|
|
33
|
+
// 语义插槽覆盖
|
|
34
|
+
<Button
|
|
35
|
+
type="outline"
|
|
36
|
+
onPress={handleAction}
|
|
37
|
+
styles={{root: {borderColor: 'red'}, content: {color: 'red'}}}>
|
|
38
|
+
危险操作
|
|
39
|
+
</Button>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
| 属性 | 说明 | 类型 | 默认值 |
|
|
45
|
+
|------|------|------|--------|
|
|
46
|
+
| type | 按钮类型 | `'primary'` \| `'outline'` \| `'ghost'` \| `'danger'` | `'primary'` |
|
|
47
|
+
| size | 按钮大小 | `'default'` \| `'small'` | `'default'` |
|
|
48
|
+
| icon | 左侧图标 | `ReactNode` | - |
|
|
49
|
+
| disabled | 禁用状态 | `boolean` | `false` |
|
|
50
|
+
| block | 撑满父容器(flex: 1) | `boolean` | `false` |
|
|
51
|
+
| loading | 加载状态 | `boolean` | `false` |
|
|
52
|
+
| onPress | 点击回调 | `() => void` | **必填** |
|
|
53
|
+
| style | 外层容器样式 | `ViewStyle` | - |
|
|
54
|
+
| styles | 语义插槽样式 | `Partial<ButtonSemanticStyles>` | - |
|
|
55
|
+
| testID | 测试标识 | `string` | - |
|
|
56
|
+
| children | 按钮内容 | `ReactNode` | **必填** |
|
|
57
|
+
|
|
58
|
+
## Semantic Styles
|
|
59
|
+
|
|
60
|
+
通过 `styles` prop 精确覆盖组件内部各 slot 的样式,不侵入组件结构。
|
|
61
|
+
|
|
62
|
+
| 插槽 | 类型 | 说明 |
|
|
63
|
+
|------|------|------|
|
|
64
|
+
| root | `ViewStyle` | 按钮容器(背景、边框、圆角等) |
|
|
65
|
+
| icon | `ViewStyle` | 图标区域(间距等) |
|
|
66
|
+
| content | `TextStyle` | 文字区域(字号、颜色、字重等) |
|
|
67
|
+
|
|
68
|
+
## Type 规格
|
|
69
|
+
|
|
70
|
+
| type | 背景 | 文字色 | 边框 |
|
|
71
|
+
|------|------|--------|------|
|
|
72
|
+
| primary | colorPrimary | #FFF | 无 |
|
|
73
|
+
| outline | 透明 | colorTextSecondary | colorBorder |
|
|
74
|
+
| ghost | 透明 | colorPrimary | colorPrimary |
|
|
75
|
+
| danger | colorError | #FFF | 无 |
|
|
76
|
+
|
|
77
|
+
## Size 规格
|
|
78
|
+
|
|
79
|
+
| size | paddingVertical | borderRadius | fontSize |
|
|
80
|
+
|------|----------------|--------------|----------|
|
|
81
|
+
| default | 10 | 8 | 14 |
|
|
82
|
+
| small | 6 | 6 | 12 |
|
|
83
|
+
|
|
84
|
+
## 样式合并顺序
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
DEFAULT_STYLES → SIZE_STYLES → TYPE_STYLES → DISABLED_STYLES → BLOCK_STYLES → semanticStyles
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
内部使用 `mergeStyles` + `useMemo` 按 slot 维度合并,仅在 props 变化时重算。
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按钮组件
|
|
3
|
+
*
|
|
4
|
+
* type + size + semantic styles。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useMemo } from 'react';
|
|
8
|
+
import { Pressable, Text, View, ActivityIndicator } from 'react-native';
|
|
9
|
+
import type { ViewStyle } from 'react-native';
|
|
10
|
+
import { mergeStyles } from '../hooks';
|
|
11
|
+
import type { ButtonSemanticStyles } from './style';
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_STYLES,
|
|
14
|
+
SIZE_STYLES,
|
|
15
|
+
TYPE_STYLES,
|
|
16
|
+
DISABLED_STYLES,
|
|
17
|
+
BLOCK_STYLES,
|
|
18
|
+
PRESSED_STYLE,
|
|
19
|
+
loadingStyle,
|
|
20
|
+
} from './style';
|
|
21
|
+
|
|
22
|
+
export interface ButtonProps {
|
|
23
|
+
type?: 'primary' | 'outline' | 'ghost' | 'danger';
|
|
24
|
+
size?: 'default' | 'small';
|
|
25
|
+
icon?: React.ReactNode;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
block?: boolean;
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
onPress: () => void;
|
|
30
|
+
style?: ViewStyle;
|
|
31
|
+
styles?: Partial<ButtonSemanticStyles>;
|
|
32
|
+
testID?: string;
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const Button: React.FC<ButtonProps> = ({
|
|
37
|
+
type = 'primary',
|
|
38
|
+
size = 'default',
|
|
39
|
+
icon,
|
|
40
|
+
disabled,
|
|
41
|
+
block,
|
|
42
|
+
loading,
|
|
43
|
+
onPress,
|
|
44
|
+
style,
|
|
45
|
+
styles: semanticStyles,
|
|
46
|
+
testID,
|
|
47
|
+
children,
|
|
48
|
+
}: ButtonProps) => {
|
|
49
|
+
const s = useMemo(
|
|
50
|
+
() =>
|
|
51
|
+
mergeStyles<ButtonSemanticStyles>(
|
|
52
|
+
DEFAULT_STYLES,
|
|
53
|
+
SIZE_STYLES[size],
|
|
54
|
+
TYPE_STYLES[type],
|
|
55
|
+
disabled ? DISABLED_STYLES : undefined,
|
|
56
|
+
block ? BLOCK_STYLES : undefined,
|
|
57
|
+
semanticStyles,
|
|
58
|
+
),
|
|
59
|
+
[type, size, disabled, block, semanticStyles],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Pressable
|
|
64
|
+
style={({ pressed }) => [s.root, pressed && PRESSED_STYLE, style]}
|
|
65
|
+
onPress={onPress}
|
|
66
|
+
disabled={disabled || loading}
|
|
67
|
+
testID={testID}
|
|
68
|
+
>
|
|
69
|
+
{icon && <View style={s.icon}>{icon}</View>}
|
|
70
|
+
{loading && (
|
|
71
|
+
<ActivityIndicator
|
|
72
|
+
color={s.content?.color as string}
|
|
73
|
+
size="small"
|
|
74
|
+
style={loadingStyle}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
{typeof children === 'string' ? (
|
|
78
|
+
<Text style={s.content}>{children}</Text>
|
|
79
|
+
) : (
|
|
80
|
+
<View style={s.content as ViewStyle}>{children}</View>
|
|
81
|
+
)}
|
|
82
|
+
</Pressable>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default Button;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import type { ViewStyle, TextStyle } from 'react-native';
|
|
3
|
+
import { tokens } from '../../theme/tokens';
|
|
4
|
+
|
|
5
|
+
export type ButtonSemanticStyles = {
|
|
6
|
+
root?: ViewStyle;
|
|
7
|
+
icon?: ViewStyle;
|
|
8
|
+
content?: TextStyle;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_STYLES: ButtonSemanticStyles = {
|
|
12
|
+
root: {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
justifyContent: 'center',
|
|
16
|
+
},
|
|
17
|
+
icon: {
|
|
18
|
+
marginRight: 6,
|
|
19
|
+
},
|
|
20
|
+
content: {
|
|
21
|
+
fontSize: 14,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const SIZE_STYLES: Record<string, Partial<ButtonSemanticStyles>> = {
|
|
26
|
+
default: {
|
|
27
|
+
root: { paddingVertical: 10, borderRadius: 8 },
|
|
28
|
+
content: { fontSize: 14 },
|
|
29
|
+
},
|
|
30
|
+
small: {
|
|
31
|
+
root: { paddingVertical: 6, borderRadius: 6 },
|
|
32
|
+
content: { fontSize: 12 },
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const TYPE_STYLES: Record<string, Partial<ButtonSemanticStyles>> = {
|
|
37
|
+
primary: {
|
|
38
|
+
root: { backgroundColor: tokens.colorPrimary },
|
|
39
|
+
content: { color: '#FFF', fontWeight: '600' },
|
|
40
|
+
},
|
|
41
|
+
outline: {
|
|
42
|
+
root: { borderWidth: 1, borderColor: tokens.colorBorder },
|
|
43
|
+
content: { color: tokens.colorTextSecondary },
|
|
44
|
+
},
|
|
45
|
+
ghost: {
|
|
46
|
+
root: { borderWidth: 1, borderColor: tokens.colorPrimary },
|
|
47
|
+
content: { color: tokens.colorPrimary },
|
|
48
|
+
},
|
|
49
|
+
danger: {
|
|
50
|
+
root: { backgroundColor: tokens.colorError },
|
|
51
|
+
content: { color: '#FFF', fontWeight: '600' },
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const DISABLED_STYLES: Partial<ButtonSemanticStyles> = {
|
|
56
|
+
root: { opacity: 0.5 },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const BLOCK_STYLES: Partial<ButtonSemanticStyles> = {
|
|
60
|
+
root: { flex: 1 },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const PRESSED_STYLE: ViewStyle = { opacity: 0.7 };
|
|
64
|
+
|
|
65
|
+
export const loadingStyle = StyleSheet.create({
|
|
66
|
+
s: { marginRight: 6 },
|
|
67
|
+
}).s;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Center 居中布局
|
|
2
|
+
|
|
3
|
+
内容水平 + 垂直居中。替代 `justifyContent: 'center' + alignItems: 'center'`。
|
|
4
|
+
|
|
5
|
+
## 何时使用
|
|
6
|
+
|
|
7
|
+
- 圆形图标按钮、头像容器
|
|
8
|
+
- 空状态页面居中
|
|
9
|
+
- 任何需要内容完全居中的场景
|
|
10
|
+
|
|
11
|
+
## 代码演示
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
// 圆形按钮
|
|
15
|
+
<Center style={{width: 32, height: 32, borderRadius: 16, backgroundColor: '#EB6E00'}}>
|
|
16
|
+
<Icon name="add" size={18} color="#FFF" />
|
|
17
|
+
</Center>
|
|
18
|
+
|
|
19
|
+
// 空状态
|
|
20
|
+
<Center style={{flex: 1}}>
|
|
21
|
+
<Icon name="inbox" size={48} />
|
|
22
|
+
<Text secondary>暂无数据</Text>
|
|
23
|
+
</Center>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
> 继承所有 RN `ViewProps`,无额外属性。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 居中布局容器
|
|
3
|
+
*
|
|
4
|
+
* justifyContent + alignItems 均为 'center'。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { View } from 'react-native';
|
|
9
|
+
import type { ViewProps } from 'react-native';
|
|
10
|
+
import { baseStyle } from './style';
|
|
11
|
+
|
|
12
|
+
export type CenterProps = ViewProps;
|
|
13
|
+
|
|
14
|
+
const Center: React.FC<CenterProps> = ({ style, ...rest }) => (
|
|
15
|
+
<View style={[baseStyle, style]} {...rest} />
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export default Center;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Chip
|
|
3
|
+
nav:
|
|
4
|
+
title: 组件
|
|
5
|
+
order: 1
|
|
6
|
+
group:
|
|
7
|
+
title: 基础
|
|
8
|
+
order: 2
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Chip 胶囊标签
|
|
12
|
+
|
|
13
|
+
用于展示标签或可选项的胶囊形组件。
|
|
14
|
+
|
|
15
|
+
## 何时使用
|
|
16
|
+
|
|
17
|
+
- 展示快捷建议
|
|
18
|
+
- 标签选择
|
|
19
|
+
- 筛选项
|
|
20
|
+
|
|
21
|
+
## 代码演示
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { Chip, Icon } from '@unif/react-native-ui';
|
|
25
|
+
|
|
26
|
+
// 基本用法
|
|
27
|
+
<Chip onPress={() => {}}>标签文本</Chip>
|
|
28
|
+
|
|
29
|
+
// 带图标
|
|
30
|
+
<Chip icon={<Icon name="search-outline" size={14} />} onPress={() => {}}>
|
|
31
|
+
搜索
|
|
32
|
+
</Chip>
|
|
33
|
+
|
|
34
|
+
// 选中态
|
|
35
|
+
<Chip selected onPress={() => {}}>已选中</Chip>
|
|
36
|
+
|
|
37
|
+
// 小尺寸
|
|
38
|
+
<Chip size="small">小标签</Chip>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
| 属性 | 说明 | 类型 | 默认值 |
|
|
44
|
+
| -------- | ------------ | ------------------------- | ----------- |
|
|
45
|
+
| children | 内容 | `ReactNode` | - |
|
|
46
|
+
| onPress | 点击回调 | `() => void` | - |
|
|
47
|
+
| selected | 选中状态 | `boolean` | - |
|
|
48
|
+
| disabled | 禁用状态 | `boolean` | - |
|
|
49
|
+
| size | 尺寸 | `'default'` \| `'small'` | `'default'` |
|
|
50
|
+
| icon | 左侧图标 | `ReactNode` | - |
|
|
51
|
+
| style | 容器样式 | `ViewStyle` | - |
|
|
52
|
+
| styles | 语义样式 | `ChipSemanticStyles` | - |
|
|
53
|
+
| testID | 测试标识 | `string` | - |
|
|
54
|
+
|
|
55
|
+
## Semantic Styles
|
|
56
|
+
|
|
57
|
+
| Slot | 说明 |
|
|
58
|
+
| ------- | ---------- |
|
|
59
|
+
| root | 外层容器 |
|
|
60
|
+
| content | 文本样式 |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chip 基础组件
|
|
3
|
+
*
|
|
4
|
+
* 胶囊标签,支持选中态、禁用态和图标。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useMemo } from 'react';
|
|
8
|
+
import { View, Text, Pressable } from 'react-native';
|
|
9
|
+
import type { ViewStyle } from 'react-native';
|
|
10
|
+
import { mergeStyles } from '../hooks';
|
|
11
|
+
import type { ChipSemanticStyles } from './style';
|
|
12
|
+
import { DEFAULT_STYLES, SELECTED_STYLES, DISABLED_STYLES, SMALL_STYLES } from './style';
|
|
13
|
+
|
|
14
|
+
export interface ChipProps {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
onPress?: () => void;
|
|
17
|
+
selected?: boolean;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
size?: 'default' | 'small';
|
|
20
|
+
icon?: React.ReactNode;
|
|
21
|
+
style?: ViewStyle;
|
|
22
|
+
styles?: Partial<ChipSemanticStyles>;
|
|
23
|
+
testID?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PRESSED_STYLE: ViewStyle = { opacity: 0.7 };
|
|
27
|
+
|
|
28
|
+
const Chip: React.FC<ChipProps> = ({
|
|
29
|
+
children,
|
|
30
|
+
onPress,
|
|
31
|
+
selected,
|
|
32
|
+
disabled,
|
|
33
|
+
size = 'default',
|
|
34
|
+
icon,
|
|
35
|
+
style,
|
|
36
|
+
styles: semanticStyles,
|
|
37
|
+
testID,
|
|
38
|
+
}) => {
|
|
39
|
+
const s = useMemo(
|
|
40
|
+
() =>
|
|
41
|
+
mergeStyles<ChipSemanticStyles>(
|
|
42
|
+
DEFAULT_STYLES,
|
|
43
|
+
size === 'small' ? SMALL_STYLES : undefined,
|
|
44
|
+
selected ? SELECTED_STYLES : undefined,
|
|
45
|
+
disabled ? DISABLED_STYLES : undefined,
|
|
46
|
+
semanticStyles,
|
|
47
|
+
),
|
|
48
|
+
[size, selected, disabled, semanticStyles],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const content = (
|
|
52
|
+
<View style={[s.root, { flexDirection: 'row', alignItems: 'center' }, style]}>
|
|
53
|
+
{icon && <View style={{ marginRight: 4 }}>{icon}</View>}
|
|
54
|
+
{typeof children === 'string' ? (
|
|
55
|
+
<Text style={s.content} numberOfLines={1}>
|
|
56
|
+
{children}
|
|
57
|
+
</Text>
|
|
58
|
+
) : (
|
|
59
|
+
children
|
|
60
|
+
)}
|
|
61
|
+
</View>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (onPress) {
|
|
65
|
+
return (
|
|
66
|
+
<Pressable
|
|
67
|
+
onPress={onPress}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
style={({ pressed }) => pressed && PRESSED_STYLE}
|
|
70
|
+
testID={testID}
|
|
71
|
+
>
|
|
72
|
+
{content}
|
|
73
|
+
</Pressable>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return content;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default Chip;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ViewStyle, TextStyle } from 'react-native';
|
|
2
|
+
import { tokens } from '../../theme/tokens';
|
|
3
|
+
|
|
4
|
+
export type ChipSemanticStyles = {
|
|
5
|
+
root?: ViewStyle;
|
|
6
|
+
content?: TextStyle;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_STYLES: ChipSemanticStyles = {
|
|
10
|
+
root: {
|
|
11
|
+
backgroundColor: '#F0F0F5',
|
|
12
|
+
borderRadius: tokens.radiusFull,
|
|
13
|
+
paddingHorizontal: 14,
|
|
14
|
+
paddingVertical: 7,
|
|
15
|
+
},
|
|
16
|
+
content: {
|
|
17
|
+
fontSize: tokens.fontSizeSm,
|
|
18
|
+
color: tokens.colorText,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const SELECTED_STYLES: Partial<ChipSemanticStyles> = {
|
|
23
|
+
root: {
|
|
24
|
+
borderWidth: 1,
|
|
25
|
+
borderColor: tokens.colorPrimary,
|
|
26
|
+
backgroundColor: '#FFF5EB',
|
|
27
|
+
},
|
|
28
|
+
content: {
|
|
29
|
+
color: tokens.colorPrimary,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const DISABLED_STYLES: Partial<ChipSemanticStyles> = {
|
|
34
|
+
root: {
|
|
35
|
+
opacity: 0.5,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const SMALL_STYLES: Partial<ChipSemanticStyles> = {
|
|
40
|
+
root: {
|
|
41
|
+
paddingHorizontal: 10,
|
|
42
|
+
paddingVertical: 4,
|
|
43
|
+
},
|
|
44
|
+
content: {
|
|
45
|
+
fontSize: tokens.fontSizeXs,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Column 垂直布局
|
|
2
|
+
|
|
3
|
+
垂直排列子元素。语义化替代裸 `<View>`,显式声明布局方向。
|
|
4
|
+
|
|
5
|
+
## 何时使用
|
|
6
|
+
|
|
7
|
+
- 需要显式声明垂直布局方向时
|
|
8
|
+
- 需要 `gap`、`align`、`justify` 等布局属性时
|
|
9
|
+
|
|
10
|
+
## 代码演示
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
// 基础用法
|
|
14
|
+
<Column gap={8}>
|
|
15
|
+
<Text variant="heading">标题</Text>
|
|
16
|
+
<Text variant="caption" secondary>描述文字</Text>
|
|
17
|
+
</Column>
|
|
18
|
+
|
|
19
|
+
// 居中对齐
|
|
20
|
+
<Column align="center" justify="center" style={{flex: 1}}>
|
|
21
|
+
<Icon name="empty" />
|
|
22
|
+
<Text>暂无数据</Text>
|
|
23
|
+
</Column>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
| 属性 | 说明 | 类型 | 默认值 |
|
|
29
|
+
|------|------|------|--------|
|
|
30
|
+
| align | alignItems | `ViewStyle['alignItems']` | - |
|
|
31
|
+
| justify | justifyContent | `ViewStyle['justifyContent']` | - |
|
|
32
|
+
| gap | 子元素间距 | `number` | - |
|
|
33
|
+
|
|
34
|
+
> 继承所有 RN `ViewProps`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 垂直布局容器
|
|
3
|
+
*
|
|
4
|
+
* 语义化的 View,显式 flexDirection: 'column'。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { View } from 'react-native';
|
|
9
|
+
import type { ViewProps, ViewStyle } from 'react-native';
|
|
10
|
+
import { baseStyle } from './style';
|
|
11
|
+
|
|
12
|
+
type AlignItems = ViewStyle['alignItems'];
|
|
13
|
+
type JustifyContent = ViewStyle['justifyContent'];
|
|
14
|
+
|
|
15
|
+
export interface ColumnProps extends ViewProps {
|
|
16
|
+
/** alignItems */
|
|
17
|
+
align?: AlignItems;
|
|
18
|
+
/** justifyContent */
|
|
19
|
+
justify?: JustifyContent;
|
|
20
|
+
/** 子元素间距 */
|
|
21
|
+
gap?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const Column: React.FC<ColumnProps> = ({
|
|
25
|
+
align,
|
|
26
|
+
justify,
|
|
27
|
+
gap,
|
|
28
|
+
style,
|
|
29
|
+
...rest
|
|
30
|
+
}: ColumnProps) => (
|
|
31
|
+
<View
|
|
32
|
+
style={[
|
|
33
|
+
baseStyle,
|
|
34
|
+
align != null && { alignItems: align },
|
|
35
|
+
justify != null && { justifyContent: justify },
|
|
36
|
+
gap != null && { gap },
|
|
37
|
+
style,
|
|
38
|
+
]}
|
|
39
|
+
{...rest}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export default Column;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Divider 分割线
|
|
2
|
+
|
|
3
|
+
用于独立的视觉分隔元素。
|
|
4
|
+
|
|
5
|
+
## 何时使用
|
|
6
|
+
|
|
7
|
+
- 替代独立的 `<View style={{height: 1, backgroundColor: border}} />`
|
|
8
|
+
- 列表项之间的分隔线(如 `FlatList` 的 `ItemSeparatorComponent`)
|
|
9
|
+
|
|
10
|
+
## 何时不使用
|
|
11
|
+
|
|
12
|
+
- **不替换**容器自带的 `borderBottomWidth`(内嵌 border 保留原样)
|
|
13
|
+
- 容器 header / footer 的 `borderTopWidth` / `borderBottomWidth` 属于布局边界,改为独立 View 会影响 flex 布局
|
|
14
|
+
|
|
15
|
+
| 场景 | 处理 | 原因 |
|
|
16
|
+
|------|------|------|
|
|
17
|
+
| `<View style={styles.separator} />` | ✅ 用 `<Divider />` | 独立分割元素 |
|
|
18
|
+
| `styles.itemRow { borderBottomWidth }` | ❌ 保留原样 | 内嵌于容器 |
|
|
19
|
+
| `styles.header { borderBottomWidth }` | ❌ 保留原样 | 布局边界 |
|
|
20
|
+
|
|
21
|
+
## 代码演示
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// 默认 hairline
|
|
25
|
+
<Divider />
|
|
26
|
+
|
|
27
|
+
// 1px 带水平边距(列表分隔)
|
|
28
|
+
<Divider hairline={false} marginHorizontal={12} />
|
|
29
|
+
|
|
30
|
+
// 作为 FlatList 分隔
|
|
31
|
+
const Separator = () => <Divider hairline={false} marginHorizontal={12} />;
|
|
32
|
+
<FlatList ItemSeparatorComponent={Separator} ... />
|
|
33
|
+
|
|
34
|
+
// 自定义样式
|
|
35
|
+
<Divider style={{backgroundColor: 'red', marginVertical: 8}} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
| 属性 | 说明 | 类型 | 默认值 |
|
|
41
|
+
|------|------|------|--------|
|
|
42
|
+
| hairline | 使用 `StyleSheet.hairlineWidth` | `boolean` | `true` |
|
|
43
|
+
| marginHorizontal | 水平边距 | `number` | - |
|
|
44
|
+
| style | 自定义样式 | `ViewStyle` | - |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 分割线组件
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { View } from 'react-native';
|
|
7
|
+
import type { ViewStyle } from 'react-native';
|
|
8
|
+
import { hairlineStyle, standardStyle } from './style';
|
|
9
|
+
|
|
10
|
+
interface DividerProps {
|
|
11
|
+
hairline?: boolean;
|
|
12
|
+
marginHorizontal?: number;
|
|
13
|
+
style?: ViewStyle;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Divider: React.FC<DividerProps> = ({
|
|
17
|
+
hairline = true,
|
|
18
|
+
marginHorizontal,
|
|
19
|
+
style,
|
|
20
|
+
}: DividerProps) => (
|
|
21
|
+
<View
|
|
22
|
+
style={[
|
|
23
|
+
hairline ? hairlineStyle : standardStyle,
|
|
24
|
+
marginHorizontal != null && { marginHorizontal },
|
|
25
|
+
style,
|
|
26
|
+
]}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export default Divider;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { tokens } from '../../theme/tokens';
|
|
3
|
+
|
|
4
|
+
export const hairlineStyle = StyleSheet.create({
|
|
5
|
+
line: {
|
|
6
|
+
height: StyleSheet.hairlineWidth,
|
|
7
|
+
backgroundColor: tokens.colorBorder,
|
|
8
|
+
},
|
|
9
|
+
}).line;
|
|
10
|
+
|
|
11
|
+
export const standardStyle = StyleSheet.create({
|
|
12
|
+
line: { height: 1, backgroundColor: tokens.colorBorder },
|
|
13
|
+
}).line;
|