@yorkjs/hive-ui 2.2.4 → 2.2.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorkjs/hive-ui",
3
- "version": "2.2.4",
3
+ "version": "2.2.5",
4
4
  "description": "Hive UI - Taro React component library",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -0,0 +1,79 @@
1
+ @import '../../styles/mixin.styl'
2
+ @import '../../styles/variable.styl'
3
+
4
+ .rich-text-editor
5
+ display flex
6
+ flex-direction column
7
+ gap 15PX
8
+
9
+ :global
10
+ taro-textarea-core
11
+ width 100%
12
+ .auto-height
13
+ height auto
14
+ min-height 80PX
15
+ background-color transparent
16
+
17
+ .block
18
+ position relative
19
+ border-radius 10PX
20
+ halfBorder(top right bottom left, var(--lineBorder), 10PX)
21
+
22
+ .delete-btn
23
+ position absolute
24
+ top -6PX
25
+ right -6PX
26
+ width 16PX
27
+ height 16PX
28
+ line-height 16PX
29
+ text-align center
30
+ border-radius 50%
31
+ z-index 2
32
+
33
+ .delete-icon
34
+ position absolute
35
+ top 50%
36
+ left 50%
37
+ transform translate(-50%, -50%)
38
+ line-height 1
39
+ display block
40
+
41
+ .text-block
42
+ padding 15PX 15PX 6PX
43
+
44
+ .editor-textarea
45
+ :global(.taro-textarea)
46
+ width 100%
47
+ font-size 14PX
48
+ line-height 1.6
49
+ color var(--textTitle)
50
+
51
+ &.is-align-left
52
+ :global(.taro-textarea)
53
+ text-align left !important
54
+ &.is-align-center
55
+ :global(.taro-textarea)
56
+ text-align center !important
57
+ &.is-align-right
58
+ :global(.taro-textarea)
59
+ text-align right !important
60
+
61
+ .align-toolbar
62
+ display flex
63
+ justify-content flex-end
64
+ align-items center
65
+ gap 12PX
66
+ margin-top 4PX
67
+
68
+ .align-btn
69
+ padding 4PX
70
+ flexHVCenter()
71
+
72
+ .image-block
73
+ overflow hidden
74
+ border-radius 10PX
75
+
76
+ .image
77
+ display block
78
+ width 100%
79
+ height auto
@@ -0,0 +1,159 @@
1
+ import React, { useCallback, useMemo } from 'react'
2
+ import { View, Textarea, Image, BaseEventOrig, ITouchEvent } from '@tarojs/components'
3
+
4
+ import { useTheme } from '../../config'
5
+ import { formatClassNames } from '../../util/function'
6
+ import Icon from '../Icon'
7
+
8
+ import styles from './index.module.styl'
9
+
10
+ /** 文本对齐方式 */
11
+ export type RichTextAlign = 'left' | 'center' | 'right'
12
+
13
+ export type RichTextBlockType = 'text' | 'image'
14
+
15
+ interface RichTextBlockBase {
16
+ /** 可选唯一标识 ,便于后续排序等扩展;未传时用下标 */
17
+ id?: string
18
+ }
19
+
20
+ /** 文本块 */
21
+ export interface RichTextTextBlock extends RichTextBlockBase {
22
+ type: 'text'
23
+ /** 文本内容 */
24
+ content: string
25
+ /** 对齐方式,默认 left */
26
+ align?: RichTextAlign
27
+ placeholder?: string
28
+ }
29
+
30
+ /** 图片块 */
31
+ export interface RichTextImageBlock extends RichTextBlockBase {
32
+ type: 'image'
33
+ /** 图片地址 */
34
+ url: string
35
+ }
36
+
37
+ export type RichTextBlock = RichTextTextBlock | RichTextImageBlock
38
+
39
+ export interface RichTextEditorProps {
40
+ /** 块列表(受控) */
41
+ value?: RichTextBlock[]
42
+ /** 内容变更 */
43
+ onChange?: (value: RichTextBlock[]) => void
44
+ /** 文本占位符 */
45
+ placeholder?: string
46
+ /** 自定义类名 */
47
+ className?: string
48
+ }
49
+
50
+ const ALIGN_OPTIONS: RichTextAlign[] = ['left', 'center', 'right']
51
+
52
+ const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
53
+ const {
54
+ value = [],
55
+ onChange,
56
+ placeholder = '请输入内容',
57
+ className,
58
+ } = props
59
+
60
+ const { themeSelect } = useTheme()
61
+
62
+ const deleteBtnStyle = useMemo(() => ({
63
+ backgroundColor: themeSelect('rgba(0, 0, 0, 0.5)', 'rgba(255, 255, 255, 0.5)'),
64
+ }), [themeSelect])
65
+
66
+ const updateBlock = useCallback((index: number, patch: Partial<RichTextBlock>) => {
67
+ onChange?.(
68
+ value.map((block, i) => (i === index ? { ...block, ...patch } as RichTextBlock : block))
69
+ )
70
+ }, [value, onChange])
71
+
72
+ const handleDelete = useCallback((e: ITouchEvent, index: number) => {
73
+ e.stopPropagation()
74
+ onChange?.(value.filter((_, i) => i !== index))
75
+ }, [value, onChange])
76
+
77
+ const handleTextChange = useCallback((
78
+ index: number,
79
+ e: BaseEventOrig<{ value: string }>
80
+ ) => {
81
+ updateBlock(index, { content: e.detail.value })
82
+ }, [updateBlock])
83
+
84
+ const handleAlignChange = useCallback((index: number, align: RichTextAlign) => {
85
+ updateBlock(index, { align })
86
+ }, [updateBlock])
87
+
88
+ return (
89
+ <View className={formatClassNames(styles['rich-text-editor'], className)}>
90
+ {value.map((block, index) => (
91
+ <View
92
+ key={block.id ?? index}
93
+ className={styles['block']}
94
+ >
95
+ <View
96
+ className={styles['delete-btn']}
97
+ style={deleteBtnStyle}
98
+ onClick={(e) => handleDelete(e, index)}
99
+ >
100
+ <Icon
101
+ name='close'
102
+ size={10}
103
+ color="var(--containerCard)"
104
+ className={styles['delete-icon']}
105
+ />
106
+ </View>
107
+
108
+ {
109
+ block.type === 'text'
110
+ ? (
111
+ <View className={styles['text-block']}>
112
+ <Textarea
113
+ className={formatClassNames(
114
+ styles['editor-textarea'],
115
+ styles[`is-align-${block.align || 'left'}`]
116
+ )}
117
+ value={block.content}
118
+ placeholder={block?.placeholder || placeholder}
119
+ maxlength={-1}
120
+ autoHeight
121
+ onInput={(e) => handleTextChange(index, e)}
122
+ />
123
+ <View className={styles['align-toolbar']}>
124
+ {ALIGN_OPTIONS.map((align) => {
125
+ const active = (block.align || 'left') === align
126
+ return (
127
+ <View
128
+ key={align}
129
+ className={styles['align-btn']}
130
+ onClick={() => handleAlignChange(index, align)}
131
+ >
132
+ <Icon
133
+ name={`align-${align}`}
134
+ size={16}
135
+ color={active ? 'var(--textTitle)' : 'var(--textMuted)'}
136
+ />
137
+ </View>
138
+ )
139
+ })}
140
+ </View>
141
+ </View>
142
+ )
143
+ : (
144
+ <View className={styles['image-block']}>
145
+ <Image
146
+ className={styles['image']}
147
+ src={block.url}
148
+ mode='widthFix'
149
+ />
150
+ </View>
151
+ )
152
+ }
153
+ </View>
154
+ ))}
155
+ </View>
156
+ )
157
+ }
158
+
159
+ export default React.memo(RichTextEditor)
package/src/index.ts CHANGED
@@ -24,4 +24,14 @@ export {
24
24
  default as Table,
25
25
  type TableProps,
26
26
  type TableColumn,
27
- } from './components/Table'
27
+ } from './components/Table'
28
+
29
+ export {
30
+ default as RichTextEditor,
31
+ type RichTextEditorProps,
32
+ type RichTextBlock,
33
+ type RichTextTextBlock,
34
+ type RichTextImageBlock,
35
+ type RichTextAlign,
36
+ type RichTextBlockType,
37
+ } from './components/RichTextEditor'