@yorkjs/hive-ui 2.2.6 → 2.2.8
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
|
@@ -11,13 +11,12 @@
|
|
|
11
11
|
width 100%
|
|
12
12
|
.auto-height
|
|
13
13
|
height auto
|
|
14
|
-
min-height 80PX
|
|
15
14
|
background-color transparent
|
|
16
15
|
|
|
17
16
|
.block
|
|
18
17
|
position relative
|
|
19
18
|
border-radius 10PX
|
|
20
|
-
halfBorder(top right bottom left, var(--lineBorder),
|
|
19
|
+
halfBorder(top right bottom left, var(--lineBorder), 20PX)
|
|
21
20
|
|
|
22
21
|
.delete-btn
|
|
23
22
|
position absolute
|
|
@@ -39,14 +38,20 @@
|
|
|
39
38
|
display block
|
|
40
39
|
|
|
41
40
|
.text-block
|
|
41
|
+
background-color var(--containerCard)
|
|
42
|
+
border-radius 10PX
|
|
42
43
|
padding 15PX 15PX 6PX
|
|
43
44
|
|
|
45
|
+
.textarea-wrap
|
|
46
|
+
min-height 80PX
|
|
47
|
+
|
|
44
48
|
.editor-textarea
|
|
45
49
|
:global(.taro-textarea)
|
|
46
50
|
width 100%
|
|
47
51
|
font-size 14PX
|
|
48
52
|
line-height 1.6
|
|
49
53
|
color var(--textTitle)
|
|
54
|
+
overflow hidden
|
|
50
55
|
|
|
51
56
|
&.is-align-left
|
|
52
57
|
:global(.taro-textarea)
|
|
@@ -75,8 +80,13 @@
|
|
|
75
80
|
.image-block
|
|
76
81
|
overflow hidden
|
|
77
82
|
border-radius 10PX
|
|
83
|
+
background-color var(--containerCard)
|
|
84
|
+
user-select none
|
|
78
85
|
|
|
79
86
|
.image
|
|
80
87
|
display block
|
|
81
88
|
width 100%
|
|
82
89
|
height auto
|
|
90
|
+
pointer-events none
|
|
91
|
+
user-select none
|
|
92
|
+
-webkit-user-drag none
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useMemo } from 'react'
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
2
2
|
import { View, Textarea, Image, BaseEventOrig, ITouchEvent } from '@tarojs/components'
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../../config'
|
|
@@ -49,6 +49,42 @@ export interface RichTextEditorProps {
|
|
|
49
49
|
|
|
50
50
|
const ALIGN_OPTIONS: RichTextAlign[] = ['left', 'center', 'right']
|
|
51
51
|
|
|
52
|
+
const getNativeTextarea = (instance: any): HTMLTextAreaElement | null => {
|
|
53
|
+
if (!instance) {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (instance.tagName === 'TEXTAREA') {
|
|
58
|
+
return instance
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return instance.querySelector?.('textarea') || instance.textareaRef || null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const focusTextarea = (instance: any) => {
|
|
65
|
+
if (!instance) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof instance.focus === 'function') {
|
|
70
|
+
instance.focus()
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getNativeTextarea(instance)?.focus?.()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const syncTextareaAutoHeight = (instance: any) => {
|
|
78
|
+
const textarea = getNativeTextarea(instance)
|
|
79
|
+
if (!textarea) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
textarea.style.overflow = 'hidden'
|
|
84
|
+
textarea.style.height = 'auto'
|
|
85
|
+
textarea.style.height = `${textarea.scrollHeight}px`
|
|
86
|
+
}
|
|
87
|
+
|
|
52
88
|
const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
|
|
53
89
|
const {
|
|
54
90
|
value = [],
|
|
@@ -58,6 +94,8 @@ const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
|
|
|
58
94
|
} = props
|
|
59
95
|
|
|
60
96
|
const { themeSelect } = useTheme()
|
|
97
|
+
const textareaRefs = useRef<Record<number, any>>({})
|
|
98
|
+
const skipHeightSyncRef = useRef(false)
|
|
61
99
|
|
|
62
100
|
const deleteBtnStyle = useMemo(() => ({
|
|
63
101
|
backgroundColor: themeSelect('rgba(0, 0, 0, 0.5)', 'rgba(255, 255, 255, 0.5)'),
|
|
@@ -78,13 +116,56 @@ const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
|
|
|
78
116
|
index: number,
|
|
79
117
|
e: BaseEventOrig<{ value: string }>
|
|
80
118
|
) => {
|
|
119
|
+
skipHeightSyncRef.current = true
|
|
81
120
|
updateBlock(index, { content: e.detail.value })
|
|
121
|
+
|
|
122
|
+
const eventTarget = (e as any).target
|
|
123
|
+
const textarea = eventTarget?.tagName === 'TEXTAREA'
|
|
124
|
+
? eventTarget
|
|
125
|
+
: textareaRefs.current[index]
|
|
126
|
+
syncTextareaAutoHeight(textarea)
|
|
127
|
+
requestAnimationFrame(() => {
|
|
128
|
+
syncTextareaAutoHeight(textareaRefs.current[index])
|
|
129
|
+
})
|
|
82
130
|
}, [updateBlock])
|
|
83
131
|
|
|
84
132
|
const handleAlignChange = useCallback((index: number, align: RichTextAlign) => {
|
|
85
133
|
updateBlock(index, { align })
|
|
86
134
|
}, [updateBlock])
|
|
87
135
|
|
|
136
|
+
const handleTextBlockClick = useCallback((index: number) => {
|
|
137
|
+
focusTextarea(textareaRefs.current[index])
|
|
138
|
+
}, [])
|
|
139
|
+
|
|
140
|
+
const handleImageDragStart = useCallback((e: React.DragEvent<HTMLImageElement>) => {
|
|
141
|
+
e.preventDefault()
|
|
142
|
+
e.stopPropagation()
|
|
143
|
+
}, [])
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (skipHeightSyncRef.current) {
|
|
147
|
+
skipHeightSyncRef.current = false
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const syncAll = () => {
|
|
152
|
+
value.forEach((block, index) => {
|
|
153
|
+
if (block.type !== 'text') {
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
syncTextareaAutoHeight(textareaRefs.current[index])
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const rafId = requestAnimationFrame(syncAll)
|
|
161
|
+
const timer = setTimeout(syncAll, 0)
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
cancelAnimationFrame(rafId)
|
|
165
|
+
clearTimeout(timer)
|
|
166
|
+
}
|
|
167
|
+
}, [value])
|
|
168
|
+
|
|
88
169
|
return (
|
|
89
170
|
<View className={formatClassNames(styles['rich-text-editor'], className)}>
|
|
90
171
|
{value.map((block, index) => (
|
|
@@ -108,18 +189,27 @@ const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
|
|
|
108
189
|
{
|
|
109
190
|
block.type === 'text'
|
|
110
191
|
? (
|
|
111
|
-
<View
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
192
|
+
<View
|
|
193
|
+
className={styles['text-block']}
|
|
194
|
+
onClick={() => handleTextBlockClick(index)}
|
|
195
|
+
>
|
|
196
|
+
<View className={styles['textarea-wrap']}>
|
|
197
|
+
<Textarea
|
|
198
|
+
ref={(node) => {
|
|
199
|
+
textareaRefs.current[index] = node
|
|
200
|
+
}}
|
|
201
|
+
className={formatClassNames(
|
|
202
|
+
styles['editor-textarea'],
|
|
203
|
+
styles[`is-align-${block.align || 'left'}`]
|
|
204
|
+
)}
|
|
205
|
+
value={block.content}
|
|
206
|
+
placeholder={block?.placeholder || placeholder}
|
|
207
|
+
maxlength={-1}
|
|
208
|
+
autoHeight
|
|
209
|
+
disableDefaultPadding
|
|
210
|
+
onInput={(e) => handleTextChange(index, e)}
|
|
211
|
+
/>
|
|
212
|
+
</View>
|
|
123
213
|
<View className={styles['align-toolbar']}>
|
|
124
214
|
{ALIGN_OPTIONS.map((align) => {
|
|
125
215
|
const active = (block.align || 'left') === align
|
|
@@ -155,6 +245,10 @@ const RichTextEditor: React.FC<RichTextEditorProps> = (props) => {
|
|
|
155
245
|
className={styles['image']}
|
|
156
246
|
src={block.url}
|
|
157
247
|
mode='widthFix'
|
|
248
|
+
imgProps={{
|
|
249
|
+
draggable: false,
|
|
250
|
+
onDragStart: handleImageDragStart,
|
|
251
|
+
}}
|
|
158
252
|
/>
|
|
159
253
|
</View>
|
|
160
254
|
)
|