@yorkjs/hive-ui 0.0.1
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/package.json +32 -0
- package/src/components/NumberKeyboard/index.module.styl +74 -0
- package/src/components/NumberKeyboard/index.tsx +101 -0
- package/src/declarations.d.ts +9 -0
- package/src/index.ts +3 -0
- package/src/item/FlowItem/index.module.styl +94 -0
- package/src/item/FlowItem/index.tsx +123 -0
- package/src/styles/mixin.styl +289 -0
- package/src/styles/variable.styl +3 -0
- package/src/util/function.ts +56 -0
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yorkjs/hive-ui",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Hive UI - Taro React component library",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"module": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react": ">=18.0.0",
|
|
15
|
+
"@tarojs/taro": ">=4.0.0",
|
|
16
|
+
"@tarojs/components": ">=4.0.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependenciesMeta": {
|
|
19
|
+
"@tarojs/components": { "optional": false },
|
|
20
|
+
"@tarojs/taro": { "optional": false },
|
|
21
|
+
"react": { "optional": false }
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@tarojs/components": "^4.1.3",
|
|
25
|
+
"@tarojs/taro": "^4.1.3",
|
|
26
|
+
"@types/react": "^18.3.28",
|
|
27
|
+
"react": "^18.3.1",
|
|
28
|
+
"react-dom": "^18.3.1",
|
|
29
|
+
"typescript": "^6.0.2",
|
|
30
|
+
"stylus": "^0.64.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@import '../../styles/mixin.styl'
|
|
2
|
+
|
|
3
|
+
.number-keyboard-component
|
|
4
|
+
background-color var(--containerInner)
|
|
5
|
+
width 100%
|
|
6
|
+
user-select none
|
|
7
|
+
:global
|
|
8
|
+
.keyboard-header
|
|
9
|
+
height 50PX
|
|
10
|
+
display flex
|
|
11
|
+
flex-direction row
|
|
12
|
+
align-items center
|
|
13
|
+
padding 0 12PX
|
|
14
|
+
background-color var(--containerInner)
|
|
15
|
+
halfBorder(top, var(--lineBorder))
|
|
16
|
+
|
|
17
|
+
.header-placeholder
|
|
18
|
+
flex 1
|
|
19
|
+
|
|
20
|
+
.done-btn
|
|
21
|
+
height 100%
|
|
22
|
+
display flex
|
|
23
|
+
align-items center
|
|
24
|
+
justify-content center
|
|
25
|
+
padding 0 12PX
|
|
26
|
+
&:active
|
|
27
|
+
opacity 0.6
|
|
28
|
+
|
|
29
|
+
.done-text
|
|
30
|
+
font-size 18PX
|
|
31
|
+
color var(--primary)
|
|
32
|
+
font-weight 500
|
|
33
|
+
|
|
34
|
+
.keyboard-grid
|
|
35
|
+
display flex
|
|
36
|
+
flex-direction row
|
|
37
|
+
flex-wrap wrap
|
|
38
|
+
background-color var(--containerInner)
|
|
39
|
+
halfBorder(top, var(--lineBorder))
|
|
40
|
+
|
|
41
|
+
.key-item
|
|
42
|
+
width 33.333%
|
|
43
|
+
height 60PX
|
|
44
|
+
background-color var(--containerCard)
|
|
45
|
+
display flex
|
|
46
|
+
align-items center
|
|
47
|
+
justify-content center
|
|
48
|
+
box-sizing border-box
|
|
49
|
+
halfBorder(right bottom, var(--lineBorder))
|
|
50
|
+
|
|
51
|
+
// 点击反馈
|
|
52
|
+
&:active:not(.empty-key)
|
|
53
|
+
background-color var(--containerInner)
|
|
54
|
+
|
|
55
|
+
&.empty-key
|
|
56
|
+
background-color var(--containerInner)
|
|
57
|
+
cursor default
|
|
58
|
+
|
|
59
|
+
&.no-right-border
|
|
60
|
+
&:before
|
|
61
|
+
border-right none
|
|
62
|
+
|
|
63
|
+
&.last-row
|
|
64
|
+
height calc(60PX + env(safe-area-inset-bottom))
|
|
65
|
+
&:before
|
|
66
|
+
border-bottom none
|
|
67
|
+
|
|
68
|
+
.key-text
|
|
69
|
+
font-size 24PX
|
|
70
|
+
color var(--textTitle)
|
|
71
|
+
|
|
72
|
+
.delete-icon
|
|
73
|
+
width 22PX
|
|
74
|
+
height 22PX
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { View, Text, Image } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
import { formatClassNames } from '../../util/function'
|
|
5
|
+
|
|
6
|
+
import styles from './index.module.styl'
|
|
7
|
+
|
|
8
|
+
type KeyboardType = 'int' | 'number'
|
|
9
|
+
|
|
10
|
+
interface IProps {
|
|
11
|
+
/** 键盘类型:int(整数), number(带小数点) */
|
|
12
|
+
type?: KeyboardType
|
|
13
|
+
/** 插入字符回调 */
|
|
14
|
+
onInsert: (val: string) => void
|
|
15
|
+
/** 删除回调 */
|
|
16
|
+
onDelete: () => void
|
|
17
|
+
/** 是否显示头部状态栏 */
|
|
18
|
+
showHead?: boolean
|
|
19
|
+
/** 完成按钮文案 */
|
|
20
|
+
doneText?: string
|
|
21
|
+
/** 完成按钮点击 */
|
|
22
|
+
onDone?: () => void
|
|
23
|
+
/** 外部样式名 */
|
|
24
|
+
className?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const NumberKeyboard: React.FC<IProps> = (props) => {
|
|
28
|
+
const {
|
|
29
|
+
type = 'int',
|
|
30
|
+
onInsert,
|
|
31
|
+
onDelete,
|
|
32
|
+
showHead = false,
|
|
33
|
+
doneText = '完成',
|
|
34
|
+
onDone,
|
|
35
|
+
className
|
|
36
|
+
} = props
|
|
37
|
+
|
|
38
|
+
const keys = useMemo(() => {
|
|
39
|
+
const leftBottomKey = type === 'number' ? '.' : ''
|
|
40
|
+
return ['1', '2', '3', '4', '5', '6', '7', '8', '9', leftBottomKey, '0', 'delete']
|
|
41
|
+
}, [type])
|
|
42
|
+
|
|
43
|
+
const handleKeyPress = (key: string) => {
|
|
44
|
+
if (key === 'delete') {
|
|
45
|
+
onDelete()
|
|
46
|
+
}
|
|
47
|
+
else if (key !== '') {
|
|
48
|
+
onInsert(key)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<View className={formatClassNames(styles['number-keyboard-component'], className)}>
|
|
54
|
+
{
|
|
55
|
+
showHead
|
|
56
|
+
? (
|
|
57
|
+
<View className="keyboard-header">
|
|
58
|
+
<View className="header-placeholder" />
|
|
59
|
+
<View className="done-btn" onClick={onDone}>
|
|
60
|
+
<Text className="done-text">{doneText}</Text>
|
|
61
|
+
</View>
|
|
62
|
+
</View>
|
|
63
|
+
) : undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
<View className={"keyboard-grid"}>
|
|
67
|
+
{keys.map((key, index) => {
|
|
68
|
+
const isDelete = key === 'delete'
|
|
69
|
+
const isEmptyKey = key === '' && type === 'int'
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<View
|
|
73
|
+
key={index}
|
|
74
|
+
className={formatClassNames("key-item", {
|
|
75
|
+
'empty-key': isEmptyKey,
|
|
76
|
+
'no-right-border': (index + 1) % 3 === 0,
|
|
77
|
+
'last-row': index >= 9
|
|
78
|
+
})}
|
|
79
|
+
onClick={() => !isEmptyKey && handleKeyPress(key)}
|
|
80
|
+
>
|
|
81
|
+
{
|
|
82
|
+
isDelete
|
|
83
|
+
? (
|
|
84
|
+
<Image
|
|
85
|
+
className="delete-icon"
|
|
86
|
+
src="https://img.finstao.com/3ccf71a6e53be5a99c02b0dd7e9ed8c0.png"
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
: (
|
|
90
|
+
<Text className="key-text">{key}</Text>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
</View>
|
|
94
|
+
)
|
|
95
|
+
})}
|
|
96
|
+
</View>
|
|
97
|
+
</View>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default React.memo(NumberKeyboard)
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
@import '../../styles/mixin.styl'
|
|
2
|
+
@import '../../styles/variable.styl'
|
|
3
|
+
|
|
4
|
+
.flow-item
|
|
5
|
+
display flex
|
|
6
|
+
flex-direction row
|
|
7
|
+
justify-content space-between
|
|
8
|
+
align-items center
|
|
9
|
+
padding 0PX 10PX
|
|
10
|
+
margin 0 $WING_BLANK
|
|
11
|
+
background-color var(--containerCard)
|
|
12
|
+
|
|
13
|
+
:global
|
|
14
|
+
.flow-content
|
|
15
|
+
width 100%
|
|
16
|
+
display flex
|
|
17
|
+
flex-direction row
|
|
18
|
+
justify-content space-between
|
|
19
|
+
align-items center
|
|
20
|
+
halfBorder(bottom, var(--lineBorder))
|
|
21
|
+
padding-bottom 15PX
|
|
22
|
+
padding-top 15PX
|
|
23
|
+
|
|
24
|
+
.flow-content-no-border
|
|
25
|
+
width 100%
|
|
26
|
+
display flex
|
|
27
|
+
flex-direction row
|
|
28
|
+
justify-content space-between
|
|
29
|
+
align-items center
|
|
30
|
+
padding-bottom 15PX
|
|
31
|
+
padding-top 15PX
|
|
32
|
+
|
|
33
|
+
.flow-item-left
|
|
34
|
+
display flex
|
|
35
|
+
flex-direction row
|
|
36
|
+
justify-content flex-start
|
|
37
|
+
align-items center
|
|
38
|
+
margin-right 10PX
|
|
39
|
+
flex 1
|
|
40
|
+
overflow hidden
|
|
41
|
+
|
|
42
|
+
.flow-item-icon
|
|
43
|
+
flex-shrink 0
|
|
44
|
+
width 36PX
|
|
45
|
+
height 36PX
|
|
46
|
+
margin-right 10PX
|
|
47
|
+
|
|
48
|
+
border-radius 4PX
|
|
49
|
+
display flex
|
|
50
|
+
align-items center
|
|
51
|
+
justify-content center
|
|
52
|
+
padding 2PX
|
|
53
|
+
box-sizing border-box
|
|
54
|
+
|
|
55
|
+
.icon-img
|
|
56
|
+
width 100%
|
|
57
|
+
height 100%
|
|
58
|
+
|
|
59
|
+
.flow-item-content
|
|
60
|
+
flex 1
|
|
61
|
+
display flex
|
|
62
|
+
flex-direction column
|
|
63
|
+
justify-content center
|
|
64
|
+
align-items flex-start
|
|
65
|
+
overflow hidden
|
|
66
|
+
|
|
67
|
+
.flow-item-title
|
|
68
|
+
font-size 16PX
|
|
69
|
+
color var(--textTitle)
|
|
70
|
+
text-align left
|
|
71
|
+
width 100%
|
|
72
|
+
lineClamp(1)
|
|
73
|
+
|
|
74
|
+
.flow-item-time
|
|
75
|
+
font-size 12PX
|
|
76
|
+
color var(--textMuted)
|
|
77
|
+
margin-top 5PX
|
|
78
|
+
text-align left
|
|
79
|
+
|
|
80
|
+
.flow-item-right
|
|
81
|
+
display flex
|
|
82
|
+
flex-direction column
|
|
83
|
+
justify-content center
|
|
84
|
+
align-items flex-end
|
|
85
|
+
flex-shrink 0
|
|
86
|
+
|
|
87
|
+
.flow-item-amount
|
|
88
|
+
font-size 16PX
|
|
89
|
+
color var(--textTitle)
|
|
90
|
+
|
|
91
|
+
.flow-item-desc
|
|
92
|
+
margin-top 5PX
|
|
93
|
+
font-size 12PX
|
|
94
|
+
color var(--textMuted)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, Image } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
formatClassNames,
|
|
6
|
+
isEmpty
|
|
7
|
+
} from '../../util/function'
|
|
8
|
+
|
|
9
|
+
import styles from './index.module.styl'
|
|
10
|
+
|
|
11
|
+
interface IProps {
|
|
12
|
+
/** 左侧图标链接 */
|
|
13
|
+
icon?: string
|
|
14
|
+
/** 标题 */
|
|
15
|
+
title?: string
|
|
16
|
+
/** 时间或下方副标题 */
|
|
17
|
+
time?: string
|
|
18
|
+
/** 右侧金额 */
|
|
19
|
+
amount?: string | number
|
|
20
|
+
/** 金额颜色,不传默认 */
|
|
21
|
+
amountColor?: string
|
|
22
|
+
/** 右侧下方的描述或状态 */
|
|
23
|
+
desc?: string
|
|
24
|
+
/** 描述文字颜色 */
|
|
25
|
+
descColor?: string
|
|
26
|
+
/** 是否隐藏底部分割线 */
|
|
27
|
+
showSeparator?: boolean
|
|
28
|
+
/** 点击事件 */
|
|
29
|
+
onClick?: () => void
|
|
30
|
+
className?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const FlowItem: React.FC<IProps> = (props) => {
|
|
34
|
+
const {
|
|
35
|
+
icon,
|
|
36
|
+
title,
|
|
37
|
+
time,
|
|
38
|
+
amount,
|
|
39
|
+
amountColor,
|
|
40
|
+
desc,
|
|
41
|
+
descColor,
|
|
42
|
+
showSeparator = false,
|
|
43
|
+
onClick,
|
|
44
|
+
className
|
|
45
|
+
} = props
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View
|
|
49
|
+
className={formatClassNames(
|
|
50
|
+
styles['flow-item'],
|
|
51
|
+
className
|
|
52
|
+
)}
|
|
53
|
+
onClick={onClick}
|
|
54
|
+
>
|
|
55
|
+
<View className={showSeparator ? 'flow-content-no-border' : 'flow-content'}>
|
|
56
|
+
<View className="flow-item-left">
|
|
57
|
+
{
|
|
58
|
+
!isEmpty(icon)
|
|
59
|
+
? (
|
|
60
|
+
<View className="flow-item-icon">
|
|
61
|
+
<Image
|
|
62
|
+
src={icon}
|
|
63
|
+
className="icon-img"
|
|
64
|
+
mode="aspectFit"
|
|
65
|
+
/>
|
|
66
|
+
</View>
|
|
67
|
+
) : undefined
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
<View className="flow-item-content">
|
|
71
|
+
{
|
|
72
|
+
!isEmpty(title)
|
|
73
|
+
? (
|
|
74
|
+
<Text className="flow-item-title">
|
|
75
|
+
{title}
|
|
76
|
+
</Text>
|
|
77
|
+
)
|
|
78
|
+
: undefined
|
|
79
|
+
}
|
|
80
|
+
{
|
|
81
|
+
!isEmpty(time)
|
|
82
|
+
? (
|
|
83
|
+
<Text className="flow-item-time">
|
|
84
|
+
{time}
|
|
85
|
+
</Text>
|
|
86
|
+
)
|
|
87
|
+
: undefined
|
|
88
|
+
}
|
|
89
|
+
</View>
|
|
90
|
+
</View>
|
|
91
|
+
|
|
92
|
+
<View className="flow-item-right">
|
|
93
|
+
{
|
|
94
|
+
amount !== undefined
|
|
95
|
+
? (
|
|
96
|
+
<Text
|
|
97
|
+
className="flow-item-amount"
|
|
98
|
+
style={amountColor ? { color: amountColor } : {}}
|
|
99
|
+
>
|
|
100
|
+
{amount}
|
|
101
|
+
</Text>
|
|
102
|
+
)
|
|
103
|
+
: undefined
|
|
104
|
+
}
|
|
105
|
+
{
|
|
106
|
+
!isEmpty(desc)
|
|
107
|
+
? (
|
|
108
|
+
<Text
|
|
109
|
+
className="flow-item-desc"
|
|
110
|
+
style={descColor ? { color: descColor } : {}}
|
|
111
|
+
>
|
|
112
|
+
{desc}
|
|
113
|
+
</Text>
|
|
114
|
+
)
|
|
115
|
+
: undefined
|
|
116
|
+
}
|
|
117
|
+
</View>
|
|
118
|
+
</View>
|
|
119
|
+
</View>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default React.memo(FlowItem)
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
@import '~@src/env/variable.styl'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 清除浮动
|
|
6
|
+
*/
|
|
7
|
+
clearfix()
|
|
8
|
+
&:before,
|
|
9
|
+
&::after
|
|
10
|
+
content " "
|
|
11
|
+
display table
|
|
12
|
+
|
|
13
|
+
&::after
|
|
14
|
+
clear both
|
|
15
|
+
|
|
16
|
+
zoom 1
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 伪元素实现底部或顶部横线
|
|
20
|
+
*/
|
|
21
|
+
pseudoHLine($direction, $height, $color)
|
|
22
|
+
position relative
|
|
23
|
+
&:after
|
|
24
|
+
content ''
|
|
25
|
+
position absolute
|
|
26
|
+
{$direction} 0
|
|
27
|
+
left 0
|
|
28
|
+
right 0
|
|
29
|
+
height $height
|
|
30
|
+
background-color $color
|
|
31
|
+
transform scaleY(0.5)
|
|
32
|
+
transform-origin $direction center
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 伪元素实现左边或右边横线
|
|
36
|
+
*/
|
|
37
|
+
pseudoVLine($direction, $width, $color)
|
|
38
|
+
position relative
|
|
39
|
+
&:after
|
|
40
|
+
content ''
|
|
41
|
+
position absolute
|
|
42
|
+
{$direction} 0
|
|
43
|
+
top 0
|
|
44
|
+
bottom 0
|
|
45
|
+
width $width
|
|
46
|
+
background-color $color
|
|
47
|
+
transform scaleX(0.5)
|
|
48
|
+
transform-origin $direction center
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 文本结尾加省略号
|
|
52
|
+
*/
|
|
53
|
+
textOverflow()
|
|
54
|
+
overflow hidden
|
|
55
|
+
text-overflow ellipsis
|
|
56
|
+
white-space nowrap
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 文本自然换行
|
|
61
|
+
*/
|
|
62
|
+
wordBreak()
|
|
63
|
+
word-break break-all
|
|
64
|
+
word-wrap break-word
|
|
65
|
+
white-space pre-wrap
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 三角形
|
|
69
|
+
*/
|
|
70
|
+
triangle($direction, $width, $height, $color)
|
|
71
|
+
width 0
|
|
72
|
+
height 0
|
|
73
|
+
|
|
74
|
+
if $direction == 'top'
|
|
75
|
+
border-left ($width / 2) solid transparent
|
|
76
|
+
border-right ($width / 2) solid transparent
|
|
77
|
+
border-bottom $height solid $color
|
|
78
|
+
|
|
79
|
+
else if $direction == 'right'
|
|
80
|
+
border-top ($height / 2) solid transparent
|
|
81
|
+
border-bottom ($height / 2) solid transparent
|
|
82
|
+
border-left $width solid $color
|
|
83
|
+
|
|
84
|
+
else if $direction == 'bottom'
|
|
85
|
+
border-left ($width / 2) solid transparent
|
|
86
|
+
border-right ($width / 2) solid transparent
|
|
87
|
+
border-top $height solid $color
|
|
88
|
+
|
|
89
|
+
else // left
|
|
90
|
+
border-top ($height / 2) solid transparent
|
|
91
|
+
border-bottom ($height / 2) solid transparent
|
|
92
|
+
border-right $width solid $color
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 居中定位
|
|
96
|
+
*/
|
|
97
|
+
center($way, $vertical = 50%)
|
|
98
|
+
position $way
|
|
99
|
+
left 50%
|
|
100
|
+
top $vertical
|
|
101
|
+
transform translate(-50%, (-1 * $vertical))
|
|
102
|
+
|
|
103
|
+
// flex 水平垂直居中
|
|
104
|
+
flexHVCenter()
|
|
105
|
+
display flex
|
|
106
|
+
align-items center
|
|
107
|
+
justify-content center
|
|
108
|
+
|
|
109
|
+
placeholder($color)
|
|
110
|
+
|
|
111
|
+
&::-webkit-input-placeholder /* WebKit browsers */
|
|
112
|
+
color $color
|
|
113
|
+
|
|
114
|
+
&:-moz-placeholder /* Mozilla Firefox 4 to 18 */
|
|
115
|
+
color $color
|
|
116
|
+
|
|
117
|
+
&::-moz-placeholder /* Mozilla Firefox 19+ */
|
|
118
|
+
color $color
|
|
119
|
+
|
|
120
|
+
&:-ms-input-placeholder /* Internet Explorer 10+ */
|
|
121
|
+
color $color
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
lineClamp($count)
|
|
125
|
+
display -webkit-box
|
|
126
|
+
-webkit-line-clamp $count
|
|
127
|
+
-webkit-box-orient vertical
|
|
128
|
+
text-overflow ellipsis
|
|
129
|
+
overflow hidden
|
|
130
|
+
white-space normal
|
|
131
|
+
overflow-wrap break-word
|
|
132
|
+
word-break break-all
|
|
133
|
+
|
|
134
|
+
// 设置页面垂直滚动
|
|
135
|
+
scrollView()
|
|
136
|
+
height 100%
|
|
137
|
+
overflow-y auto
|
|
138
|
+
&::-webkit-scrollbar
|
|
139
|
+
display none
|
|
140
|
+
|
|
141
|
+
// 设置页面水平滚动
|
|
142
|
+
scrollXView()
|
|
143
|
+
width 100%
|
|
144
|
+
overflow-x auto
|
|
145
|
+
&::-webkit-scrollbar
|
|
146
|
+
display none
|
|
147
|
+
|
|
148
|
+
// 设置 padding 安全区域,取最大的一方的值
|
|
149
|
+
// https://juejin.cn/post/6865873665104773128
|
|
150
|
+
// stylus 格式化字符串语法
|
|
151
|
+
// https://www.stylus-lang.cn/docs/operators.html#sprintf
|
|
152
|
+
paddingBottomSafeArea($padding)
|
|
153
|
+
padding-bottom $padding
|
|
154
|
+
padding-bottom 'calc(%s + constant(safe-area-inset-bottom))' % $padding
|
|
155
|
+
padding-bottom 'max(%s, constant(safe-area-inset-bottom))' % $padding
|
|
156
|
+
padding-bottom 'calc(%s + env(safe-area-inset-bottom))' % $padding
|
|
157
|
+
padding-bottom 'max(%s, env(safe-area-inset-bottom))' % $padding
|
|
158
|
+
marginBottomSafeArea($margin)
|
|
159
|
+
margin-bottom $margin
|
|
160
|
+
margin-bottom 'calc(%s + constant(safe-area-inset-bottom))' % $margin
|
|
161
|
+
margin-bottom 'max(%s, constant(safe-area-inset-bottom))' % $margin
|
|
162
|
+
margin-bottom 'calc(%s + env(safe-area-inset-bottom))' % $margin
|
|
163
|
+
margin-bottom 'max(%s, env(safe-area-inset-bottom))' % $margin
|
|
164
|
+
bottomSafeArea($bottom)
|
|
165
|
+
bottom $bottom
|
|
166
|
+
bottom 'calc(%s + constant(safe-area-inset-bottom))' % $bottom
|
|
167
|
+
bottom 'max(%s, constant(safe-area-inset-bottom))' % $bottom
|
|
168
|
+
bottom 'calc(%s + env(safe-area-inset-bottom))' % $bottom
|
|
169
|
+
bottom 'max(%s, env(safe-area-inset-bottom))' % $bottom
|
|
170
|
+
|
|
171
|
+
// 设置 padding 包括安全区域
|
|
172
|
+
paddingBottomWithSafeArea($padding)
|
|
173
|
+
padding-bottom $padding
|
|
174
|
+
padding-bottom 'calc(%s + constant(safe-area-inset-bottom))' % $padding
|
|
175
|
+
padding-bottom 'calc(%s + env(safe-area-inset-bottom))' % $padding
|
|
176
|
+
marginBottomWithSafeArea($margin)
|
|
177
|
+
margin-bottom $margin
|
|
178
|
+
margin-bottom 'calc(%s + constant(safe-area-inset-bottom))' % $margin
|
|
179
|
+
margin-bottom 'calc(%s + env(safe-area-inset-bottom))' % $margin
|
|
180
|
+
bottomWithSafeArea($bottom)
|
|
181
|
+
bottom $bottom
|
|
182
|
+
bottom 'calc(%s + constant(safe-area-inset-bottom))' % $bottom
|
|
183
|
+
bottom 'calc(%s + env(safe-area-inset-bottom))' % $bottom
|
|
184
|
+
|
|
185
|
+
pageContainer()
|
|
186
|
+
background-color $brand-background
|
|
187
|
+
min-height 100%
|
|
188
|
+
box-sizing border-box
|
|
189
|
+
|
|
190
|
+
// 设置背景图片,按分辨率显示
|
|
191
|
+
// 写标准 css,插件会补上浏览器前缀 -webkit-image-set
|
|
192
|
+
setBackgroundImage($1x, $2x, $repeat=no-repeat, $postion=0 0)
|
|
193
|
+
background $1x $repeat $postion
|
|
194
|
+
background-image image-set(
|
|
195
|
+
$1x 1x,
|
|
196
|
+
$2x 2x
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
// 显示滚动条,兼容微信小程序滚动条样式设置
|
|
200
|
+
showScrollbar()
|
|
201
|
+
&::-webkit-scrollbar
|
|
202
|
+
display block
|
|
203
|
+
width 4PX
|
|
204
|
+
&::-webkit-scrollbar-track
|
|
205
|
+
background-color #F5F5F5
|
|
206
|
+
border-radius 2PX
|
|
207
|
+
&::-webkit-scrollbar-thumb
|
|
208
|
+
background-color #ccc
|
|
209
|
+
border-radius 2PX
|
|
210
|
+
// NOTE: 微信小程序滚动条样式
|
|
211
|
+
::-webkit-scrollbar
|
|
212
|
+
display block
|
|
213
|
+
width 4PX
|
|
214
|
+
::-webkit-scrollbar-track
|
|
215
|
+
background-color #F5F5F5
|
|
216
|
+
border-radius 2PX
|
|
217
|
+
::-webkit-scrollbar-thumb
|
|
218
|
+
background-color #ccc
|
|
219
|
+
border-radius 2PX
|
|
220
|
+
|
|
221
|
+
@keyframes spin
|
|
222
|
+
from
|
|
223
|
+
transform translateX(-50%) rotate(0deg)
|
|
224
|
+
to
|
|
225
|
+
transform translateX(-50%) rotate(360deg)
|
|
226
|
+
|
|
227
|
+
// 定义 halfBorder 函数
|
|
228
|
+
// $position: 边框位置(默认全部,支持 top/right/bottom/left 或组合)
|
|
229
|
+
// $color: 边框颜色(默认继承 currentColor)
|
|
230
|
+
// $radius: 圆角大小(默认 0)
|
|
231
|
+
halfBorder($position = top left right bottom, $color = currentColor, $radius = 0)
|
|
232
|
+
position relative
|
|
233
|
+
&::before
|
|
234
|
+
content ''
|
|
235
|
+
position absolute
|
|
236
|
+
top 0
|
|
237
|
+
left 0
|
|
238
|
+
width 200%
|
|
239
|
+
height 200%
|
|
240
|
+
box-sizing border-box
|
|
241
|
+
transform scale(0.5)
|
|
242
|
+
transform-origin left top
|
|
243
|
+
border-radius $radius // 圆角加倍补偿缩放
|
|
244
|
+
border-style solid
|
|
245
|
+
border-width 1PX
|
|
246
|
+
border-color transparent // 默认透明
|
|
247
|
+
pointer-events none
|
|
248
|
+
z-index 1
|
|
249
|
+
|
|
250
|
+
// 添加隔离样式
|
|
251
|
+
background-clip padding-box
|
|
252
|
+
-webkit-backface-visibility hidden
|
|
253
|
+
backface-visibility hidden
|
|
254
|
+
-webkit-perspective 1000
|
|
255
|
+
perspective 1000
|
|
256
|
+
|
|
257
|
+
// 根据 $position 设置边框颜色
|
|
258
|
+
if top in $position
|
|
259
|
+
border-top-color $color
|
|
260
|
+
if right in $position
|
|
261
|
+
border-right-color $color
|
|
262
|
+
if bottom in $position
|
|
263
|
+
border-bottom-color $color
|
|
264
|
+
if left in $position
|
|
265
|
+
border-left-color $color
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param $size 基础字号
|
|
269
|
+
*/
|
|
270
|
+
getPlatformFontSize($size)
|
|
271
|
+
font-size: unit($size, PX)
|
|
272
|
+
|
|
273
|
+
// iOS
|
|
274
|
+
:global(.is-ios) &
|
|
275
|
+
font-size: unit($size, PX)
|
|
276
|
+
|
|
277
|
+
// Android
|
|
278
|
+
:global(.is-android) &
|
|
279
|
+
font-size: unit($size, PX)
|
|
280
|
+
|
|
281
|
+
// 扩大点击区域
|
|
282
|
+
hitSlot($size)
|
|
283
|
+
&:after
|
|
284
|
+
content ''
|
|
285
|
+
position absolute
|
|
286
|
+
top ($size * -1)
|
|
287
|
+
left ($size * -1)
|
|
288
|
+
right ($size * -1)
|
|
289
|
+
bottom ($size * -1)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
interface ClassRecord {
|
|
2
|
+
[key: string]: boolean | string | number
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
type ClassValue = string | ClassRecord | undefined | null | false
|
|
6
|
+
|
|
7
|
+
export function formatClassNames(...args: ClassValue[]): string {
|
|
8
|
+
const classes: string[] = []
|
|
9
|
+
|
|
10
|
+
args.forEach(arg => {
|
|
11
|
+
if (!arg) return
|
|
12
|
+
|
|
13
|
+
if (typeof arg === 'string') {
|
|
14
|
+
classes.push(arg)
|
|
15
|
+
}
|
|
16
|
+
else if (typeof arg === 'object') {
|
|
17
|
+
Object.keys(arg).forEach(key => {
|
|
18
|
+
if (arg[key]) {
|
|
19
|
+
classes.push(key)
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return classes.join(' ')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type EmptyString = '' | ' '
|
|
29
|
+
|
|
30
|
+
// 定义一个包含所有"空"值的联合类型
|
|
31
|
+
type EmptyValue = null | undefined | EmptyString | [] | Record<string, never> | typeof NaN
|
|
32
|
+
|
|
33
|
+
// 空值: {}, [], '', ' ', null, undefined, NaN
|
|
34
|
+
export function isEmpty(value: any): value is EmptyValue {
|
|
35
|
+
if (value == null || Number.isNaN(value)) {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let isEmpty = false
|
|
40
|
+
const protoType = Object.prototype.toString.call(value)
|
|
41
|
+
switch (protoType) {
|
|
42
|
+
case '[object Object]':
|
|
43
|
+
isEmpty = Object.keys(value).length === 0
|
|
44
|
+
break
|
|
45
|
+
case '[object Array]':
|
|
46
|
+
isEmpty = value.length === 0
|
|
47
|
+
break
|
|
48
|
+
case '[object String]':
|
|
49
|
+
isEmpty = value.trim().length === 0
|
|
50
|
+
break
|
|
51
|
+
default:
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return isEmpty
|
|
56
|
+
}
|