@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorkjs/hive-ui",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "description": "Hive UI - Taro React component library",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -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), 10PX)
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 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
- />
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
  )