@mpxjs/webpack-plugin 2.10.17-beta.6 → 2.10.17-beta.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/lib/index.js +1 -0
- package/lib/json-compiler/index.js +18 -1
- package/lib/platform/style/wx/index.js +1 -17
- package/lib/react/processJSON.js +20 -1
- package/lib/react/processScript.js +1 -0
- package/lib/react/script-helper.js +0 -1
- package/lib/runtime/components/ali/mpx-recycle-view.mpx +518 -0
- package/lib/runtime/components/ali/mpx-sticky-header.mpx +212 -0
- package/lib/runtime/components/ali/mpx-sticky-section.mpx +17 -0
- package/lib/runtime/components/react/animationHooks/useTransitionHooks.ts +34 -30
- package/lib/runtime/components/react/animationHooks/utils.ts +3 -2
- package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.js +38 -33
- package/lib/runtime/components/react/dist/animationHooks/utils.js +3 -2
- package/lib/runtime/components/react/dist/mpx-image.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-recycle-view.d.ts +45 -0
- package/lib/runtime/components/react/dist/mpx-recycle-view.jsx +272 -0
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +6 -5
- package/lib/runtime/components/react/dist/utils.d.ts +2 -1
- package/lib/runtime/components/react/dist/utils.jsx +15 -21
- package/lib/runtime/components/react/mpx-image.tsx +2 -2
- package/lib/runtime/components/react/mpx-recycle-view.tsx +398 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +1 -0
- package/lib/runtime/components/react/mpx-sticky-section.tsx +1 -1
- package/lib/runtime/components/react/mpx-swiper.tsx +6 -5
- package/lib/runtime/components/react/utils.tsx +18 -23
- package/lib/runtime/components/web/mpx-recycle-view.vue +508 -0
- package/lib/runtime/components/wx/mpx-list-header-default.mpx +21 -0
- package/lib/runtime/components/wx/mpx-recycle-item-default.mpx +21 -0
- package/lib/runtime/components/wx/mpx-recycle-view.mpx +193 -0
- package/lib/runtime/components/wx/mpx-section-header-default.mpx +21 -0
- package/lib/template-compiler/compiler.js +8 -3
- package/lib/utils/const.js +17 -0
- package/lib/utils/process-extend-components.js +43 -0
- package/lib/web/processJSON.js +20 -2
- package/package.json +1 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import React, { forwardRef, useRef, useState, useEffect, useMemo, createElement, useImperativeHandle } from 'react'
|
|
2
|
+
import { SectionList, FlatList, RefreshControl, NativeSyntheticEvent, NativeScrollEvent } from 'react-native'
|
|
3
|
+
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
4
|
+
import { extendObject, useLayout, useTransformStyle } from './utils'
|
|
5
|
+
interface ListItem {
|
|
6
|
+
isSectionHeader?: boolean;
|
|
7
|
+
_originalItemIndex?: number;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Section {
|
|
12
|
+
headerData: ListItem | null;
|
|
13
|
+
data: ListItem[];
|
|
14
|
+
hasSectionHeader?: boolean;
|
|
15
|
+
_originalItemIndex?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ItemHeightType {
|
|
19
|
+
value?: number;
|
|
20
|
+
getter?: (item: any, index: number) => number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface RecycleViewProps {
|
|
24
|
+
enhanced?: boolean;
|
|
25
|
+
bounces?: boolean;
|
|
26
|
+
scrollEventThrottle?: number;
|
|
27
|
+
height?: number | string;
|
|
28
|
+
width?: number | string;
|
|
29
|
+
listData?: ListItem[];
|
|
30
|
+
generichash?: string;
|
|
31
|
+
style?: Record<string, any>;
|
|
32
|
+
itemHeight?: ItemHeightType;
|
|
33
|
+
sectionHeaderHeight?: ItemHeightType;
|
|
34
|
+
listHeaderData?: any;
|
|
35
|
+
listHeaderHeight?: ItemHeightType;
|
|
36
|
+
useListHeader?: boolean;
|
|
37
|
+
'genericrecycle-item'?: string;
|
|
38
|
+
'genericsection-header'?: string;
|
|
39
|
+
'genericlist-header'?: string;
|
|
40
|
+
'enable-var'?: boolean;
|
|
41
|
+
'external-var-context'?: any;
|
|
42
|
+
'parent-font-size'?: number;
|
|
43
|
+
'parent-width'?: number;
|
|
44
|
+
'parent-height'?: number;
|
|
45
|
+
'enable-sticky'?: boolean;
|
|
46
|
+
'enable-back-to-top'?: boolean;
|
|
47
|
+
'end-reached-threshold'?: number;
|
|
48
|
+
'refresher-enabled'?: boolean;
|
|
49
|
+
'show-scrollbar'?: boolean;
|
|
50
|
+
'refresher-triggered'?: boolean;
|
|
51
|
+
bindrefresherrefresh?: (event: any) => void;
|
|
52
|
+
bindscrolltolower?: (event: any) => void;
|
|
53
|
+
bindscroll?: (event: any) => void;
|
|
54
|
+
[key: string]: any;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ScrollPositionParams {
|
|
58
|
+
index: number;
|
|
59
|
+
animated?: boolean;
|
|
60
|
+
viewOffset?: number;
|
|
61
|
+
viewPosition?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getGeneric = (generichash: string, generickey: string) => {
|
|
65
|
+
if (!generichash || !generickey) return null
|
|
66
|
+
const GenericComponent = global.__mpxGenericsMap?.[generichash]?.[generickey]?.()
|
|
67
|
+
if (!GenericComponent) return null
|
|
68
|
+
|
|
69
|
+
return forwardRef((props: any, ref: any) => {
|
|
70
|
+
return createElement(GenericComponent, extendObject({}, {
|
|
71
|
+
ref: ref
|
|
72
|
+
}, props))
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getListHeaderComponent = (generichash: string, generickey: string, data: any) => {
|
|
77
|
+
if (!generichash || !generickey) return undefined
|
|
78
|
+
const ListHeaderComponent = getGeneric(generichash, generickey)
|
|
79
|
+
return ListHeaderComponent ? createElement(ListHeaderComponent, { listHeaderData: data }) : null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const getSectionHeaderRenderer = (generichash: string, generickey: string) => {
|
|
83
|
+
if (!generichash || !generickey) return undefined
|
|
84
|
+
return (sectionData: { section: Section }) => {
|
|
85
|
+
if (!sectionData.section.hasSectionHeader) return null
|
|
86
|
+
const SectionHeaderComponent = getGeneric(generichash, generickey)
|
|
87
|
+
return SectionHeaderComponent ? createElement(SectionHeaderComponent, { itemData: sectionData.section.headerData }) : null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const getItemRenderer = (generichash: string, generickey: string) => {
|
|
92
|
+
if (!generichash || !generickey) return undefined
|
|
93
|
+
return ({ item }: { item: any }) => {
|
|
94
|
+
const ItemComponent = getGeneric(generichash, generickey)
|
|
95
|
+
return ItemComponent ? createElement(ItemComponent, { itemData: item }) : null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const RecycleView = forwardRef<any, RecycleViewProps>((props = {}, ref) => {
|
|
100
|
+
const {
|
|
101
|
+
enhanced = false,
|
|
102
|
+
bounces = true,
|
|
103
|
+
scrollEventThrottle = 0,
|
|
104
|
+
height,
|
|
105
|
+
width,
|
|
106
|
+
listData,
|
|
107
|
+
generichash,
|
|
108
|
+
style = {},
|
|
109
|
+
itemHeight = {},
|
|
110
|
+
sectionHeaderHeight = {},
|
|
111
|
+
listHeaderHeight = {},
|
|
112
|
+
listHeaderData = null,
|
|
113
|
+
useListHeader = false,
|
|
114
|
+
'genericrecycle-item': genericrecycleItem,
|
|
115
|
+
'genericsection-header': genericsectionHeader,
|
|
116
|
+
'genericlist-header': genericListHeader,
|
|
117
|
+
'enable-var': enableVar,
|
|
118
|
+
'external-var-context': externalVarContext,
|
|
119
|
+
'parent-font-size': parentFontSize,
|
|
120
|
+
'parent-width': parentWidth,
|
|
121
|
+
'parent-height': parentHeight,
|
|
122
|
+
'enable-sticky': enableSticky = false,
|
|
123
|
+
'enable-back-to-top': enableBackToTop = false,
|
|
124
|
+
'end-reached-threshold': onEndReachedThreshold = 0.1,
|
|
125
|
+
'refresher-enabled': refresherEnabled,
|
|
126
|
+
'show-scrollbar': showScrollbar = true,
|
|
127
|
+
'refresher-triggered': refresherTriggered
|
|
128
|
+
} = props
|
|
129
|
+
|
|
130
|
+
const [refreshing, setRefreshing] = useState(!!refresherTriggered)
|
|
131
|
+
|
|
132
|
+
const scrollViewRef = useRef<any>(null)
|
|
133
|
+
|
|
134
|
+
const indexMap = useRef<{ [key: string]: string | number }>({})
|
|
135
|
+
|
|
136
|
+
const reverseIndexMap = useRef<{ [key: string]: number }>({})
|
|
137
|
+
|
|
138
|
+
const {
|
|
139
|
+
hasSelfPercent,
|
|
140
|
+
setWidth,
|
|
141
|
+
setHeight
|
|
142
|
+
} = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
|
|
143
|
+
|
|
144
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef })
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (refreshing !== refresherTriggered) {
|
|
148
|
+
setRefreshing(!!refresherTriggered)
|
|
149
|
+
}
|
|
150
|
+
}, [refresherTriggered])
|
|
151
|
+
|
|
152
|
+
const onRefresh = () => {
|
|
153
|
+
const { bindrefresherrefresh } = props
|
|
154
|
+
bindrefresherrefresh &&
|
|
155
|
+
bindrefresherrefresh(
|
|
156
|
+
getCustomEvent('refresherrefresh', {}, { layoutRef }, props)
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const onEndReached = () => {
|
|
161
|
+
const { bindscrolltolower } = props
|
|
162
|
+
bindscrolltolower &&
|
|
163
|
+
bindscrolltolower(
|
|
164
|
+
getCustomEvent('scrolltolower', {}, { layoutRef }, props)
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
169
|
+
const { bindscroll } = props
|
|
170
|
+
bindscroll &&
|
|
171
|
+
bindscroll(
|
|
172
|
+
getCustomEvent('scroll', event.nativeEvent, { layoutRef }, props)
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 通过sectionIndex和rowIndex获取原始索引
|
|
177
|
+
const getOriginalIndex = (sectionIndex: number, rowIndex: number | 'header'): number => {
|
|
178
|
+
const key = `${sectionIndex}_${rowIndex}`
|
|
179
|
+
return reverseIndexMap.current[key] ?? -1 // 如果找不到,返回-1
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const scrollToIndex = ({ index, animated, viewOffset = 0, viewPosition = 0 }: ScrollPositionParams) => {
|
|
183
|
+
if (scrollViewRef.current) {
|
|
184
|
+
// 通过索引映射表快速定位位置
|
|
185
|
+
const position = indexMap.current[index]
|
|
186
|
+
const [sectionIndex, itemIndex] = (position as string).split('_')
|
|
187
|
+
scrollViewRef.current.scrollToLocation?.({
|
|
188
|
+
itemIndex: itemIndex === 'header' ? 0 : Number(itemIndex) + 1,
|
|
189
|
+
sectionIndex: Number(sectionIndex) || 0,
|
|
190
|
+
animated,
|
|
191
|
+
viewOffset,
|
|
192
|
+
viewPosition
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const getItemHeight = ({ sectionIndex, rowIndex }: { sectionIndex: number, rowIndex: number }) => {
|
|
198
|
+
if (!itemHeight) {
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
if ((itemHeight as ItemHeightType).getter) {
|
|
202
|
+
const item = convertedListData[sectionIndex].data[rowIndex]
|
|
203
|
+
// 使用getOriginalIndex获取原始索引
|
|
204
|
+
const originalIndex = getOriginalIndex(sectionIndex, rowIndex)
|
|
205
|
+
return (itemHeight as ItemHeightType).getter?.(item, originalIndex) || 0
|
|
206
|
+
} else {
|
|
207
|
+
return (itemHeight as ItemHeightType).value || 0
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const getSectionHeaderHeight = ({ sectionIndex }: { sectionIndex: number }) => {
|
|
212
|
+
const item = convertedListData[sectionIndex]
|
|
213
|
+
const { hasSectionHeader } = item
|
|
214
|
+
// 使用getOriginalIndex获取原始索引
|
|
215
|
+
const originalIndex = getOriginalIndex(sectionIndex, 'header')
|
|
216
|
+
if (!hasSectionHeader) return 0
|
|
217
|
+
if ((sectionHeaderHeight as ItemHeightType).getter) {
|
|
218
|
+
return (sectionHeaderHeight as ItemHeightType).getter?.(item, originalIndex) || 0
|
|
219
|
+
} else {
|
|
220
|
+
return (sectionHeaderHeight as ItemHeightType).value || 0
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const convertedListData = useMemo(() => {
|
|
225
|
+
const sections: Section[] = []
|
|
226
|
+
let currentSection: Section | null = null
|
|
227
|
+
// 清空之前的索引映射
|
|
228
|
+
indexMap.current = {}
|
|
229
|
+
// 清空反向索引映射
|
|
230
|
+
reverseIndexMap.current = {}
|
|
231
|
+
listData.forEach((item: ListItem, index: number) => {
|
|
232
|
+
if (item.isSectionHeader) {
|
|
233
|
+
// 如果已经存在一个 section,先把它添加到 sections 中
|
|
234
|
+
if (currentSection) {
|
|
235
|
+
sections.push(currentSection)
|
|
236
|
+
}
|
|
237
|
+
// 创建新的 section
|
|
238
|
+
currentSection = {
|
|
239
|
+
headerData: item,
|
|
240
|
+
data: [],
|
|
241
|
+
hasSectionHeader: true,
|
|
242
|
+
_originalItemIndex: index
|
|
243
|
+
}
|
|
244
|
+
// 为 section header 添加索引映射
|
|
245
|
+
const sectionIndex = sections.length
|
|
246
|
+
indexMap.current[index] = `${sectionIndex}_header`
|
|
247
|
+
// 添加反向索引映射
|
|
248
|
+
reverseIndexMap.current[`${sectionIndex}_header`] = index
|
|
249
|
+
} else {
|
|
250
|
+
// 如果没有当前 section,创建一个默认的
|
|
251
|
+
if (!currentSection) {
|
|
252
|
+
// 创建默认section (无header的section)
|
|
253
|
+
currentSection = {
|
|
254
|
+
headerData: null,
|
|
255
|
+
data: [],
|
|
256
|
+
hasSectionHeader: false,
|
|
257
|
+
_originalItemIndex: -1
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// 将 item 添加到当前 section 的 data 中
|
|
261
|
+
const itemIndex = currentSection.data.length
|
|
262
|
+
currentSection.data.push(extendObject({}, item, {
|
|
263
|
+
_originalItemIndex: index
|
|
264
|
+
}))
|
|
265
|
+
let sectionIndex
|
|
266
|
+
// 为 item 添加索引映射 - 存储格式为: "sectionIndex_itemIndex"
|
|
267
|
+
if (!currentSection.hasSectionHeader && sections.length === 0) {
|
|
268
|
+
// 在默认section中(第一个且无header)
|
|
269
|
+
sectionIndex = 0
|
|
270
|
+
indexMap.current[index] = `${sectionIndex}_${itemIndex}`
|
|
271
|
+
} else {
|
|
272
|
+
// 在普通section中
|
|
273
|
+
sectionIndex = sections.length
|
|
274
|
+
indexMap.current[index] = `${sectionIndex}_${itemIndex}`
|
|
275
|
+
}
|
|
276
|
+
// 添加反向索引映射
|
|
277
|
+
reverseIndexMap.current[`${sectionIndex}_${itemIndex}`] = index
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
// 添加最后一个 section
|
|
281
|
+
if (currentSection) {
|
|
282
|
+
sections.push(currentSection)
|
|
283
|
+
}
|
|
284
|
+
return sections
|
|
285
|
+
}, [listData])
|
|
286
|
+
|
|
287
|
+
const { getItemLayout } = useMemo(() => {
|
|
288
|
+
const layouts: Array<{ length: number, offset: number, index: number }> = []
|
|
289
|
+
let offset = 0
|
|
290
|
+
|
|
291
|
+
if (useListHeader) {
|
|
292
|
+
// 计算列表头部的高度
|
|
293
|
+
offset += listHeaderHeight.getter?.() || listHeaderHeight.value || 0
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 遍历所有 sections
|
|
297
|
+
convertedListData.forEach((section: Section, sectionIndex: number) => {
|
|
298
|
+
// 添加 section header 的位置信息
|
|
299
|
+
const headerHeight = getSectionHeaderHeight({ sectionIndex })
|
|
300
|
+
layouts.push({
|
|
301
|
+
length: headerHeight,
|
|
302
|
+
offset,
|
|
303
|
+
index: layouts.length
|
|
304
|
+
})
|
|
305
|
+
offset += headerHeight
|
|
306
|
+
|
|
307
|
+
// 添加该 section 中所有 items 的位置信息
|
|
308
|
+
section.data.forEach((item: ListItem, itemIndex: number) => {
|
|
309
|
+
const contenteight = getItemHeight({ sectionIndex, rowIndex: itemIndex })
|
|
310
|
+
layouts.push({
|
|
311
|
+
length: contenteight,
|
|
312
|
+
offset,
|
|
313
|
+
index: layouts.length
|
|
314
|
+
})
|
|
315
|
+
offset += contenteight
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// 添加该 section 尾部位置信息
|
|
319
|
+
// 因为即使 sectionList 没传 renderSectionFooter,getItemLayout 中的 index 的计算也会包含尾部节点
|
|
320
|
+
layouts.push({
|
|
321
|
+
length: 0,
|
|
322
|
+
offset,
|
|
323
|
+
index: layouts.length
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
return {
|
|
327
|
+
itemLayouts: layouts,
|
|
328
|
+
getItemLayout: (data: any, index: number) => layouts[index]
|
|
329
|
+
}
|
|
330
|
+
}, [convertedListData, useListHeader])
|
|
331
|
+
|
|
332
|
+
const scrollAdditionalProps = extendObject(
|
|
333
|
+
{
|
|
334
|
+
alwaysBounceVertical: false,
|
|
335
|
+
alwaysBounceHorizontal: false,
|
|
336
|
+
scrollEventThrottle: scrollEventThrottle,
|
|
337
|
+
scrollsToTop: enableBackToTop,
|
|
338
|
+
showsHorizontalScrollIndicator: showScrollbar,
|
|
339
|
+
onEndReachedThreshold,
|
|
340
|
+
ref: scrollViewRef,
|
|
341
|
+
bounces: false,
|
|
342
|
+
stickySectionHeadersEnabled: enableSticky,
|
|
343
|
+
onScroll: onScroll,
|
|
344
|
+
onEndReached: onEndReached
|
|
345
|
+
},
|
|
346
|
+
layoutProps
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
if (enhanced) {
|
|
350
|
+
Object.assign(scrollAdditionalProps, {
|
|
351
|
+
bounces
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
if (refresherEnabled) {
|
|
355
|
+
Object.assign(scrollAdditionalProps, {
|
|
356
|
+
refreshing: refreshing
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
useImperativeHandle(ref, () => {
|
|
361
|
+
return {
|
|
362
|
+
...props,
|
|
363
|
+
scrollToIndex
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
|
|
368
|
+
'id',
|
|
369
|
+
'show-scrollbar',
|
|
370
|
+
'lower-threshold',
|
|
371
|
+
'refresher-triggered',
|
|
372
|
+
'refresher-enabled',
|
|
373
|
+
'bindrefresherrefresh'
|
|
374
|
+
], { layoutRef })
|
|
375
|
+
|
|
376
|
+
return createElement(
|
|
377
|
+
SectionList,
|
|
378
|
+
extendObject(
|
|
379
|
+
{
|
|
380
|
+
style: [{ height, width }, style, layoutStyle],
|
|
381
|
+
sections: convertedListData,
|
|
382
|
+
renderItem: getItemRenderer(generichash, genericrecycleItem),
|
|
383
|
+
getItemLayout: getItemLayout,
|
|
384
|
+
ListHeaderComponent: useListHeader ? getListHeaderComponent(generichash, genericListHeader, listHeaderData) : null,
|
|
385
|
+
renderSectionHeader: getSectionHeaderRenderer(generichash, genericsectionHeader),
|
|
386
|
+
refreshControl: refresherEnabled
|
|
387
|
+
? React.createElement(RefreshControl, {
|
|
388
|
+
onRefresh: onRefresh,
|
|
389
|
+
refreshing: refreshing
|
|
390
|
+
})
|
|
391
|
+
: undefined
|
|
392
|
+
},
|
|
393
|
+
innerProps
|
|
394
|
+
)
|
|
395
|
+
)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
export default RecycleView
|
|
@@ -259,6 +259,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
259
259
|
|
|
260
260
|
// layout 完成前先隐藏,避免安卓闪烁问题
|
|
261
261
|
const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {} }, [hasRefresherLayoutRef.current])
|
|
262
|
+
|
|
262
263
|
const lastOffset = useRef(0)
|
|
263
264
|
|
|
264
265
|
if (scrollX && scrollY) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
import { useRef, forwardRef, createElement, ReactNode, useCallback, useMemo } from 'react'
|
|
3
2
|
import { View, ViewStyle } from 'react-native'
|
|
4
3
|
import useNodesRef, { HandlerRef } from './useNodesRef'
|
|
5
4
|
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils'
|
|
6
5
|
import { StickyContext } from './context'
|
|
6
|
+
|
|
7
7
|
import useInnerProps from './getInnerListeners'
|
|
8
8
|
|
|
9
9
|
interface StickySectionProps {
|
|
@@ -232,6 +232,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
232
232
|
const moveTranstion = useSharedValue(0)
|
|
233
233
|
const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
|
|
234
234
|
const intervalTimer = props.interval || 500
|
|
235
|
+
// 记录是否首次
|
|
236
|
+
const isFirstRef = useRef(true)
|
|
235
237
|
|
|
236
238
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
|
|
237
239
|
const waitForHandlers = flatGesture(waitFor)
|
|
@@ -477,10 +479,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
477
479
|
}, [])
|
|
478
480
|
|
|
479
481
|
function handleSwiperChange (current: number, pCurrent: number) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
bindchange && bindchange(eventData)
|
|
483
|
-
}
|
|
482
|
+
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
|
|
483
|
+
bindchange && bindchange(eventData)
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
const runOnJSCallbackRef = useRef({
|
|
@@ -529,9 +529,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
529
529
|
// 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
|
|
530
530
|
useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
|
|
531
531
|
// 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
|
|
532
|
-
if (newIndex !== preIndex && bindchange) {
|
|
532
|
+
if (newIndex !== preIndex && bindchange && !isFirstRef.current) {
|
|
533
533
|
runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent)
|
|
534
534
|
}
|
|
535
|
+
isFirstRef.current = false
|
|
535
536
|
})
|
|
536
537
|
|
|
537
538
|
useEffect(() => {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement, createElement, MutableRefObject } from 'react'
|
|
2
2
|
import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native'
|
|
3
|
-
import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn
|
|
3
|
+
import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
|
|
4
4
|
import { VarContext, ScrollViewContext, RouteContext } from './context'
|
|
5
5
|
import { ExpressionParser, parseFunc, ReplaceSource } from './parser'
|
|
6
6
|
import { initialWindowMetrics } from 'react-native-safe-area-context'
|
|
7
7
|
import FastImage, { FastImageProps } from '@d11/react-native-fast-image'
|
|
8
8
|
import type { AnyFunc, ExtendedFunctionComponent } from './types/common'
|
|
9
|
-
import { runOnJS } from 'react-native-reanimated'
|
|
10
9
|
import { Gesture } from 'react-native-gesture-handler'
|
|
11
10
|
|
|
12
11
|
export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/
|
|
@@ -143,16 +142,17 @@ export function splitStyle<T extends Record<string, any>> (styleObj: T): {
|
|
|
143
142
|
innerStyle: Partial<T>
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
|
-
|
|
147
|
-
const selfPercentRule: Record<string, 'height' | 'width'> = {
|
|
148
|
-
translateX: 'width',
|
|
149
|
-
translateY: 'height',
|
|
145
|
+
const radiusPercentRule: Record<string, 'height' | 'width'> = {
|
|
150
146
|
borderTopLeftRadius: 'width',
|
|
151
147
|
borderBottomLeftRadius: 'width',
|
|
152
148
|
borderBottomRightRadius: 'width',
|
|
153
149
|
borderTopRightRadius: 'width',
|
|
154
150
|
borderRadius: 'width'
|
|
155
151
|
}
|
|
152
|
+
const selfPercentRule: Record<string, 'height' | 'width'> = Object.assign({
|
|
153
|
+
translateX: 'width',
|
|
154
|
+
translateY: 'height'
|
|
155
|
+
}, radiusPercentRule)
|
|
156
156
|
|
|
157
157
|
const parentHeightPercentRule: Record<string, boolean> = {
|
|
158
158
|
height: true,
|
|
@@ -238,7 +238,7 @@ function transformVar (styleObj: Record<string, any>, varKeyPaths: Array<Array<s
|
|
|
238
238
|
const resolved = resolveVar(value, varContext)
|
|
239
239
|
if (resolved === undefined) {
|
|
240
240
|
delete target[key]
|
|
241
|
-
|
|
241
|
+
error(`Can not resolve css var at ${varKeyPath.join('.')}:${value}.`)
|
|
242
242
|
return
|
|
243
243
|
}
|
|
244
244
|
target[key] = resolved
|
|
@@ -390,23 +390,16 @@ function transformBoxShadow (styleObj: Record<string, any>) {
|
|
|
390
390
|
}, '')
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
function transformZIndex (styleObj: Record<string, any>) {
|
|
394
|
-
if (!styleObj.zIndex || typeof styleObj.zIndex === 'number') return
|
|
395
|
-
if (styleObj.zIndex === 'auto') {
|
|
396
|
-
error('Property [z-index] does not supported [auto], please check again!')
|
|
397
|
-
styleObj.zIndex = 0
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
393
|
interface TransformStyleConfig {
|
|
402
394
|
enableVar?: boolean
|
|
403
395
|
externalVarContext?: Record<string, any>
|
|
404
396
|
parentFontSize?: number
|
|
405
397
|
parentWidth?: number
|
|
406
398
|
parentHeight?: number
|
|
399
|
+
isTransformBorderRadiusPercent?: boolean
|
|
407
400
|
}
|
|
408
401
|
|
|
409
|
-
export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
|
|
402
|
+
export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, isTransformBorderRadiusPercent, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
|
|
410
403
|
const varStyle: Record<string, any> = {}
|
|
411
404
|
const unoVarStyle: Record<string, any> = {}
|
|
412
405
|
const normalStyle: Record<string, any> = {}
|
|
@@ -457,7 +450,7 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
|
|
|
457
450
|
function calcVisitor ({ key, value, keyPath }: VisitorArg) {
|
|
458
451
|
if (calcUseRegExp.test(value)) {
|
|
459
452
|
// calc translate & border-radius 的百分比计算
|
|
460
|
-
if (hasOwn(selfPercentRule, key) &&
|
|
453
|
+
if (hasOwn(selfPercentRule, key) && /calc\(\d+%/.test(value)) {
|
|
461
454
|
hasSelfPercent = true
|
|
462
455
|
percentKeyPaths.push(keyPath.slice())
|
|
463
456
|
}
|
|
@@ -467,7 +460,11 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
|
|
|
467
460
|
|
|
468
461
|
function percentVisitor ({ key, value, keyPath }: VisitorArg) {
|
|
469
462
|
// fixme 去掉 translate & border-radius 的百分比计算
|
|
470
|
-
|
|
463
|
+
// fixme Image 组件 borderRadius 仅支持 number
|
|
464
|
+
if (isTransformBorderRadiusPercent && hasOwn(radiusPercentRule, key) && PERCENT_REGEX.test(value)) {
|
|
465
|
+
hasSelfPercent = true
|
|
466
|
+
percentKeyPaths.push(keyPath.slice())
|
|
467
|
+
} else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
|
|
471
468
|
percentKeyPaths.push(keyPath.slice())
|
|
472
469
|
}
|
|
473
470
|
}
|
|
@@ -477,8 +474,10 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
|
|
|
477
474
|
[envVisitor, percentVisitor, calcVisitor].forEach(visitor => visitor({ target, key, value, keyPath }))
|
|
478
475
|
}
|
|
479
476
|
}
|
|
477
|
+
|
|
480
478
|
// transform 字符串格式转化数组格式(先转数组再处理css var)
|
|
481
479
|
transformTransform(styleObj)
|
|
480
|
+
|
|
482
481
|
// traverse var & generate normalStyle
|
|
483
482
|
traverseStyle(styleObj, [varVisitor])
|
|
484
483
|
hasVarDec = hasVarDec || !!externalVarContext
|
|
@@ -544,11 +543,7 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
|
|
|
544
543
|
transformStringify(normalStyle)
|
|
545
544
|
// transform rpx to px
|
|
546
545
|
transformBoxShadow(normalStyle)
|
|
547
|
-
|
|
548
|
-
transformZIndex(normalStyle)
|
|
549
|
-
if (Array.isArray(normalStyle.transform)) {
|
|
550
|
-
normalStyle.transform = normalStyle.transform.filter(item => !isEmptyObject(item))
|
|
551
|
-
}
|
|
546
|
+
|
|
552
547
|
return {
|
|
553
548
|
hasVarDec,
|
|
554
549
|
varContextRef,
|