@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 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)
@@ -0,0 +1,9 @@
1
+ declare module '*.styl' {
2
+ const content: Record<string, string>
3
+ export default content
4
+ }
5
+
6
+ declare module '*.css' {
7
+ const content: Record<string, string>
8
+ export default content
9
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default as NumberKeyboard } from './components/NumberKeyboard'
2
+
3
+ export { default as FlowItem } from './item/FlowItem'
@@ -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,3 @@
1
+ $WING_BLANK = 12PX
2
+ $CARD_BLANK = 10PX
3
+ $GUTTER_BLANK = 10PX
@@ -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
+ }