@mpxjs/webpack-plugin 2.10.24-beta.1 → 2.10.24-test.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.
Files changed (124) hide show
  1. package/lib/index.js +1 -4
  2. package/lib/platform/style/wx/index.js +395 -104
  3. package/lib/react/style-helper.js +6 -6
  4. package/lib/resolver/AddEnvPlugin.js +1 -1
  5. package/lib/resolver/AddModePlugin.js +1 -1
  6. package/lib/runtime/components/react/animationHooks/index.ts +7 -3
  7. package/lib/runtime/components/react/dist/animationHooks/index.d.ts +0 -2
  8. package/lib/runtime/components/react/dist/animationHooks/index.js +6 -1
  9. package/lib/runtime/components/react/dist/event.config.d.ts +2 -6
  10. package/lib/runtime/components/react/dist/event.config.js +28 -24
  11. package/lib/runtime/components/react/dist/getInnerListeners.d.ts +1 -1
  12. package/lib/runtime/components/react/dist/getInnerListeners.js +74 -56
  13. package/lib/runtime/components/react/dist/mpx-button.d.ts +1 -1
  14. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -9
  15. package/lib/runtime/components/react/dist/mpx-camera.d.ts +0 -1
  16. package/lib/runtime/components/react/dist/mpx-camera.jsx +1 -2
  17. package/lib/runtime/components/react/dist/mpx-canvas/index.d.ts +0 -1
  18. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +1 -2
  19. package/lib/runtime/components/react/dist/mpx-canvas/utils.d.ts +1 -3
  20. package/lib/runtime/components/react/dist/mpx-checkbox-group.d.ts +0 -1
  21. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +3 -4
  22. package/lib/runtime/components/react/dist/mpx-checkbox.d.ts +1 -1
  23. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +8 -9
  24. package/lib/runtime/components/react/dist/mpx-form.d.ts +1 -1
  25. package/lib/runtime/components/react/dist/mpx-form.jsx +5 -5
  26. package/lib/runtime/components/react/dist/mpx-icon/index.d.ts +0 -1
  27. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +2 -2
  28. package/lib/runtime/components/react/dist/mpx-image.d.ts +0 -1
  29. package/lib/runtime/components/react/dist/mpx-image.jsx +59 -43
  30. package/lib/runtime/components/react/dist/mpx-input.d.ts +0 -1
  31. package/lib/runtime/components/react/dist/mpx-input.jsx +2 -2
  32. package/lib/runtime/components/react/dist/mpx-label.d.ts +1 -1
  33. package/lib/runtime/components/react/dist/mpx-label.jsx +5 -6
  34. package/lib/runtime/components/react/dist/mpx-movable-area.d.ts +0 -1
  35. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +3 -3
  36. package/lib/runtime/components/react/dist/mpx-movable-view.d.ts +1 -1
  37. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +6 -6
  38. package/lib/runtime/components/react/dist/mpx-picker-view/index.d.ts +1 -1
  39. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +5 -7
  40. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.d.ts +0 -1
  41. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +2 -2
  42. package/lib/runtime/components/react/dist/mpx-progress.d.ts +0 -1
  43. package/lib/runtime/components/react/dist/mpx-progress.jsx +1 -2
  44. package/lib/runtime/components/react/dist/mpx-radio-group.d.ts +0 -1
  45. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +3 -4
  46. package/lib/runtime/components/react/dist/mpx-radio.d.ts +1 -1
  47. package/lib/runtime/components/react/dist/mpx-radio.jsx +8 -9
  48. package/lib/runtime/components/react/dist/mpx-rich-text/index.d.ts +0 -1
  49. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +1 -2
  50. package/lib/runtime/components/react/dist/mpx-scroll-view.d.ts +1 -1
  51. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -6
  52. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +24 -19
  53. package/lib/runtime/components/react/dist/mpx-simple-view.d.ts +4 -1
  54. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -4
  55. package/lib/runtime/components/react/dist/mpx-slider.d.ts +0 -1
  56. package/lib/runtime/components/react/dist/mpx-slider.jsx +6 -6
  57. package/lib/runtime/components/react/dist/mpx-sticky-header.d.ts +1 -1
  58. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +5 -5
  59. package/lib/runtime/components/react/dist/mpx-sticky-section.d.ts +1 -1
  60. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +6 -6
  61. package/lib/runtime/components/react/dist/mpx-swiper-item.d.ts +1 -1
  62. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +5 -5
  63. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +1 -1
  64. package/lib/runtime/components/react/dist/mpx-swiper.jsx +4 -7
  65. package/lib/runtime/components/react/dist/mpx-switch.d.ts +0 -1
  66. package/lib/runtime/components/react/dist/mpx-switch.jsx +1 -2
  67. package/lib/runtime/components/react/dist/mpx-text.d.ts +0 -1
  68. package/lib/runtime/components/react/dist/mpx-text.jsx +29 -25
  69. package/lib/runtime/components/react/dist/mpx-video.d.ts +0 -1
  70. package/lib/runtime/components/react/dist/mpx-video.jsx +4 -4
  71. package/lib/runtime/components/react/dist/mpx-view.d.ts +2 -1
  72. package/lib/runtime/components/react/dist/mpx-view.jsx +214 -113
  73. package/lib/runtime/components/react/dist/mpx-web-view.d.ts +1 -2
  74. package/lib/runtime/components/react/dist/mpx-web-view.jsx +5 -1
  75. package/lib/runtime/components/react/dist/namedColorSet.d.ts +2 -0
  76. package/lib/runtime/components/react/dist/namedColorSet.js +149 -0
  77. package/lib/runtime/components/react/dist/utils.d.ts +73 -70
  78. package/lib/runtime/components/react/dist/utils.jsx +1295 -445
  79. package/lib/runtime/components/react/event.config.ts +32 -25
  80. package/lib/runtime/components/react/getInnerListeners.ts +84 -60
  81. package/lib/runtime/components/react/mpx-button.tsx +18 -16
  82. package/lib/runtime/components/react/mpx-camera.tsx +0 -3
  83. package/lib/runtime/components/react/mpx-canvas/index.tsx +3 -7
  84. package/lib/runtime/components/react/mpx-canvas/utils.tsx +3 -3
  85. package/lib/runtime/components/react/mpx-checkbox-group.tsx +2 -6
  86. package/lib/runtime/components/react/mpx-checkbox.tsx +9 -11
  87. package/lib/runtime/components/react/mpx-form.tsx +6 -6
  88. package/lib/runtime/components/react/mpx-icon/index.tsx +1 -3
  89. package/lib/runtime/components/react/mpx-image.tsx +65 -54
  90. package/lib/runtime/components/react/mpx-input.tsx +1 -3
  91. package/lib/runtime/components/react/mpx-label.tsx +6 -8
  92. package/lib/runtime/components/react/mpx-movable-area.tsx +3 -4
  93. package/lib/runtime/components/react/mpx-movable-view.tsx +7 -7
  94. package/lib/runtime/components/react/mpx-picker-view/index.tsx +6 -8
  95. package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +2 -4
  96. package/lib/runtime/components/react/mpx-progress.tsx +0 -3
  97. package/lib/runtime/components/react/mpx-radio-group.tsx +2 -6
  98. package/lib/runtime/components/react/mpx-radio.tsx +9 -11
  99. package/lib/runtime/components/react/mpx-rich-text/index.tsx +0 -3
  100. package/lib/runtime/components/react/mpx-scroll-view.tsx +7 -7
  101. package/lib/runtime/components/react/mpx-simple-text.tsx +24 -26
  102. package/lib/runtime/components/react/mpx-simple-view.tsx +17 -5
  103. package/lib/runtime/components/react/mpx-slider.tsx +15 -10
  104. package/lib/runtime/components/react/mpx-sticky-header.tsx +6 -6
  105. package/lib/runtime/components/react/mpx-sticky-section.tsx +7 -7
  106. package/lib/runtime/components/react/mpx-swiper-item.tsx +6 -6
  107. package/lib/runtime/components/react/mpx-swiper.tsx +5 -8
  108. package/lib/runtime/components/react/mpx-switch.tsx +0 -3
  109. package/lib/runtime/components/react/mpx-text.tsx +31 -34
  110. package/lib/runtime/components/react/mpx-video.tsx +3 -5
  111. package/lib/runtime/components/react/mpx-view.tsx +235 -126
  112. package/lib/runtime/components/react/mpx-web-view.tsx +11 -7
  113. package/lib/runtime/components/react/namedColorSet.ts +150 -0
  114. package/lib/runtime/components/react/types/getInnerListeners.d.ts +21 -1
  115. package/lib/runtime/components/react/types/global.d.ts +1 -0
  116. package/lib/runtime/components/react/utils.tsx +1348 -544
  117. package/lib/runtime/i18n.wxs +15 -9
  118. package/lib/style-compiler/strip-conditional.js +19 -4
  119. package/package.json +2 -2
  120. package/lib/resolver/ExtendComponentsPlugin.js +0 -91
  121. package/lib/runtime/components/extends/section-list.mpx +0 -14
  122. package/lib/runtime/components/react/dist/mpx-section-list.d.ts +0 -56
  123. package/lib/runtime/components/react/dist/mpx-section-list.jsx +0 -484
  124. package/lib/runtime/components/react/mpx-section-list.tsx +0 -734
@@ -1,734 +0,0 @@
1
- import { forwardRef, useRef, useMemo, createElement, useImperativeHandle, useEffect } from 'react'
2
- import type { ComponentType } from 'react'
3
- import { SectionList, RefreshControl, NativeSyntheticEvent, NativeScrollEvent } from 'react-native'
4
- import type { SectionListData, SectionListProps as RNSectionListProps } from 'react-native'
5
- import { Gesture, GestureDetector } from 'react-native-gesture-handler'
6
- import { hasOwn, error } from '@mpxjs/utils'
7
- import useInnerProps, { getCustomEvent } from './getInnerListeners'
8
- import { extendObject, useLayout, useTransformStyle, GestureHandler, flatGesture } from './utils'
9
- interface ListItem {
10
- isSectionHeader?: boolean;
11
- isSectionFooter?: boolean;
12
- [key: string]: any;
13
- }
14
-
15
- interface SectionExtra {
16
- headerData: ListItem | null;
17
- footerData: ListItem | null;
18
- hasSectionHeader?: boolean;
19
- hasSectionFooter?: boolean;
20
- }
21
-
22
- interface Section extends SectionExtra {
23
- data: ListItem[];
24
- }
25
-
26
- type RNSection = SectionListData<ListItem, SectionExtra>
27
-
28
- const TypedSectionList = SectionList as unknown as ComponentType<RNSectionListProps<ListItem, SectionExtra>>
29
-
30
- interface ItemHeightType {
31
- value?: number;
32
- getter?: (item: any, index: number) => number;
33
- }
34
-
35
- interface ItemLayoutInfo {
36
- length: number;
37
- offset: number;
38
- index: number;
39
- }
40
-
41
- interface ItemExposureLayout {
42
- offset: number;
43
- length: number;
44
- }
45
-
46
- interface ItemExposureDetail {
47
- index: number;
48
- itemData: ListItem | null;
49
- layout: ItemExposureLayout;
50
- threshold: number;
51
- }
52
-
53
- interface ItemExposureViewToken {
54
- item: Section | ListItem;
55
- key: string;
56
- index: number | null;
57
- isViewable: boolean;
58
- section?: any;
59
- }
60
-
61
- interface ItemExposureViewabilityConfig {
62
- itemVisiblePercentThreshold: number;
63
- }
64
-
65
- interface ItemExposureViewabilityPair {
66
- viewabilityConfig: ItemExposureViewabilityConfig;
67
- onViewableItemsChanged: (info: {
68
- viewableItems: ItemExposureViewToken[];
69
- changed: ItemExposureViewToken[];
70
- }) => void;
71
- }
72
-
73
- interface ItemExposureInfo {
74
- index: number;
75
- itemData: ListItem | null;
76
- layout: ItemExposureLayout;
77
- }
78
-
79
- type ItemExposureType = 'header' | 'footer' | 'item'
80
-
81
- interface ItemExposureIndexInfo {
82
- index: number;
83
- type: ItemExposureType;
84
- }
85
-
86
- interface MpxSectionListProps {
87
- enhanced?: boolean;
88
- bounces?: boolean;
89
- height?: number | string;
90
- width?: number | string;
91
- generichash?: string;
92
- style?: Record<string, any>;
93
- 'scroll-event-throttle'?: number;
94
- 'list-data'?: ListItem[];
95
- 'item-height'?: ItemHeightType;
96
- 'section-header-height'?: ItemHeightType;
97
- 'section-footer-height'?: ItemHeightType;
98
- 'list-header-data'?: any;
99
- 'list-header-height'?: number;
100
- 'use-list-header'?: boolean;
101
- 'list-footer-data'?: any;
102
- 'use-list-footer'?: boolean;
103
- 'genericrecycle-item'?: string;
104
- 'genericsection-header'?: string;
105
- 'genericsection-footer'?: string;
106
- 'genericlist-header'?: string;
107
- 'genericlist-footer'?: string;
108
- 'enable-var'?: boolean;
109
- 'external-var-context'?: any;
110
- 'parent-font-size'?: number;
111
- 'parent-width'?: number;
112
- 'parent-height'?: number;
113
- 'enable-sticky'?: boolean;
114
- 'enable-back-to-top'?: boolean;
115
- 'end-reached-threshold'?: number;
116
- 'refresher-enabled'?: boolean;
117
- 'show-scrollbar'?: boolean;
118
- 'refresher-triggered'?: boolean;
119
- 'enable-item-exposure'?: boolean;
120
- 'item-exposure-threshold'?: number;
121
- 'wait-for'?: Array<GestureHandler>;
122
- 'simultaneous-handlers'?: Array<GestureHandler>;
123
- bindrefresherrefresh?: (event: any) => void;
124
- bindscrolltolower?: (event: any) => void;
125
- bindscroll?: (event: any) => void;
126
- binditemexposure?: (event: any) => void;
127
- [key: string]: any;
128
- }
129
-
130
- interface ScrollPositionParams {
131
- index: number;
132
- animated?: boolean;
133
- viewOffset?: number;
134
- viewPosition?: number;
135
- }
136
-
137
- const getGeneric = (generichash: string, generickey: string) => {
138
- if (!generichash || !generickey) return null
139
- return global.__mpxGenericsMap?.[generichash]?.[generickey]?.() || null
140
- }
141
-
142
- const getExposurePercentThreshold = (threshold = 0) => {
143
- return Math.max(0, Math.min(100, threshold))
144
- }
145
-
146
- const _SectionList = forwardRef<any, MpxSectionListProps>((props = {}, ref) => {
147
- const {
148
- enhanced = false,
149
- bounces = true,
150
- height,
151
- width,
152
- generichash,
153
- style = {},
154
- 'list-data': listData,
155
- 'scroll-event-throttle': scrollEventThrottle = 0,
156
- 'item-height': itemHeight = {},
157
- 'section-header-height': sectionHeaderHeight = {},
158
- 'section-footer-height': sectionFooterHeight = {},
159
- 'list-header-height': listHeaderHeight = 0,
160
- 'list-header-data': listHeaderData = null,
161
- 'use-list-header': useListHeader = false,
162
- 'list-footer-data': listFooterData = null,
163
- 'use-list-footer': useListFooter = false,
164
- 'genericrecycle-item': genericrecycleItem,
165
- 'genericsection-header': genericsectionHeader,
166
- 'genericsection-footer': genericsectionFooter,
167
- 'genericlist-header': genericListHeader,
168
- 'genericlist-footer': genericListFooter,
169
- 'enable-var': enableVar,
170
- 'external-var-context': externalVarContext,
171
- 'parent-font-size': parentFontSize,
172
- 'parent-width': parentWidth,
173
- 'parent-height': parentHeight,
174
- 'enable-sticky': enableSticky = false,
175
- 'enable-back-to-top': enableBackToTop = false,
176
- 'end-reached-threshold': onEndReachedThreshold = 0.1,
177
- 'refresher-enabled': refresherEnabled,
178
- 'show-scrollbar': showScrollbar = true,
179
- 'refresher-triggered': refresherTriggered,
180
- 'enable-item-exposure': enableItemExposure = false,
181
- 'item-exposure-threshold': itemExposureThreshold = 0,
182
- 'simultaneous-handlers': originSimultaneousHandlers,
183
- 'wait-for': waitFor,
184
- binditemexposure
185
- } = props
186
-
187
- const refreshing = !!refresherTriggered
188
-
189
- const scrollViewRef = useRef<any>(null)
190
- const sectionListGestureRef = useRef<any>()
191
-
192
- const indexMap = useRef<{ [key: string]: string }>({})
193
-
194
- const reverseIndexMap = useRef<{ [key: string]: number }>({})
195
- const exposureIndexMap = useRef<{ [key: number]: ItemExposureIndexInfo }>({})
196
- const itemLayoutsRef = useRef<ItemLayoutInfo[]>([])
197
- const itemExposureState = useRef<{ [key: string]: boolean }>({})
198
- const enableStickyRef = useRef(enableSticky)
199
- const bindItemExposureRef = useRef<typeof binditemexposure>()
200
- const propsRef = useRef(props)
201
- const initialEnableItemExposureRef = useRef(enableItemExposure)
202
- const itemExposureViewabilityConfig = useRef<ItemExposureViewabilityConfig>()
203
- if (!itemExposureViewabilityConfig.current) {
204
- itemExposureViewabilityConfig.current = {
205
- itemVisiblePercentThreshold: getExposurePercentThreshold(itemExposureThreshold)
206
- }
207
- }
208
- const itemExposureViewabilityConfigValue = itemExposureViewabilityConfig.current as ItemExposureViewabilityConfig
209
-
210
- const {
211
- hasSelfPercent,
212
- setWidth,
213
- setHeight
214
- } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
215
-
216
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef })
217
-
218
- if (initialEnableItemExposureRef.current !== enableItemExposure) {
219
- error('[Mpx runtime error]: [enable-item-exposure] cannot be toggled at runtime. Set its value once at component initialization.')
220
- }
221
-
222
- enableStickyRef.current = enableSticky
223
- bindItemExposureRef.current = binditemexposure
224
- propsRef.current = props
225
-
226
- const onRefresh = () => {
227
- const { bindrefresherrefresh } = props
228
- bindrefresherrefresh &&
229
- bindrefresherrefresh(
230
- getCustomEvent('refresherrefresh', {}, { layoutRef }, props)
231
- )
232
- }
233
-
234
- const onEndReached = () => {
235
- const { bindscrolltolower } = props
236
- bindscrolltolower &&
237
- bindscrolltolower(
238
- getCustomEvent('scrolltolower', {}, { layoutRef }, props)
239
- )
240
- }
241
-
242
- const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
243
- const { bindscroll } = props
244
- bindscroll &&
245
- bindscroll(
246
- getCustomEvent('scroll', event.nativeEvent, { layoutRef }, props)
247
- )
248
- }
249
-
250
- const itemExposureViewabilityPairs = useRef<ItemExposureViewabilityPair[]>()
251
- if (!itemExposureViewabilityPairs.current) {
252
- itemExposureViewabilityPairs.current = [
253
- {
254
- viewabilityConfig: itemExposureViewabilityConfigValue,
255
- onViewableItemsChanged: ({ changed }) => {
256
- const bindItemExposure = bindItemExposureRef.current
257
- if (!bindItemExposure) return
258
-
259
- const exposedItems: ItemExposureDetail[] = []
260
- changed.forEach((viewToken) => {
261
- const exposureInfo = getItemExposureInfo(viewToken)
262
- if (!exposureInfo) return
263
-
264
- const { index, itemData, layout } = exposureInfo
265
- const key = `${index}`
266
-
267
- if (!viewToken.isViewable) {
268
- delete itemExposureState.current[key]
269
- return
270
- }
271
-
272
- if (!itemExposureState.current[key]) {
273
- itemExposureState.current[key] = true
274
- exposedItems.push({
275
- index,
276
- itemData,
277
- layout,
278
- threshold: itemExposureViewabilityConfigValue.itemVisiblePercentThreshold
279
- })
280
- }
281
- })
282
-
283
- if (exposedItems.length) {
284
- bindItemExposure(
285
- getCustomEvent('itemexposure', {}, {
286
- detail: {
287
- items: exposedItems,
288
- time: Date.now()
289
- },
290
- layoutRef
291
- }, propsRef.current)
292
- )
293
- }
294
- }
295
- }
296
- ]
297
- }
298
-
299
- // 通过sectionIndex和rowIndex获取原始索引
300
- const getOriginalIndex = (sectionIndex: number, rowIndex: number | 'header' | 'footer'): number => {
301
- const key = `${sectionIndex}_${rowIndex}`
302
- return reverseIndexMap.current[key] ?? -1 // 如果找不到,返回-1
303
- }
304
-
305
- const getItemExposureInfo = (viewToken: ItemExposureViewToken): ItemExposureInfo | null => {
306
- const item = viewToken.item
307
- if (!item) return null
308
-
309
- const viewIndex = viewToken.index
310
- if (viewIndex == null) return null
311
- const indexInfo = exposureIndexMap.current[viewIndex]
312
- if (!indexInfo) return null
313
- if (indexInfo.type === 'header' && enableStickyRef.current) return null
314
- const layoutInfo = itemLayoutsRef.current[viewIndex]
315
- const layout = {
316
- offset: layoutInfo ? layoutInfo.offset : 0,
317
- length: layoutInfo ? layoutInfo.length : 0
318
- }
319
-
320
- if (indexInfo.type === 'header') {
321
- const section = item as Section
322
- return {
323
- index: indexInfo.index,
324
- itemData: section.headerData || null,
325
- layout
326
- }
327
- }
328
-
329
- if (indexInfo.type === 'footer') {
330
- const section = item as Section
331
- return {
332
- index: indexInfo.index,
333
- itemData: section.footerData || null,
334
- layout
335
- }
336
- }
337
-
338
- return {
339
- index: indexInfo.index,
340
- itemData: item as ListItem,
341
- layout
342
- }
343
- }
344
-
345
- const scrollToIndex = ({ index, animated, viewOffset = 0, viewPosition = 0 }: ScrollPositionParams) => {
346
- if (!scrollViewRef.current) return
347
- // 通过索引映射表快速定位位置
348
- const position = indexMap.current[index]
349
- if (!position) return
350
- const [sectionIndex, itemIndex] = position.split('_')
351
- const targetSectionIndex = Number(sectionIndex) || 0
352
- const targetItemIndex = itemIndex === 'header'
353
- ? 0
354
- : itemIndex === 'footer'
355
- ? convertedListData[targetSectionIndex].data.length + 1
356
- : Number(itemIndex) + 1
357
- scrollViewRef.current.scrollToLocation?.({
358
- itemIndex: targetItemIndex,
359
- sectionIndex: targetSectionIndex,
360
- animated,
361
- viewOffset,
362
- viewPosition
363
- })
364
- }
365
-
366
- const getItemHeight = ({ sectionIndex, rowIndex }: { sectionIndex: number, rowIndex: number }) => {
367
- if (!itemHeight) {
368
- return 0
369
- }
370
- if ((itemHeight as ItemHeightType).getter) {
371
- const item = convertedListData[sectionIndex].data[rowIndex]
372
- return (itemHeight as ItemHeightType).getter?.(item, getOriginalIndex(sectionIndex, rowIndex)) || 0
373
- } else {
374
- return (itemHeight as ItemHeightType).value || 0
375
- }
376
- }
377
-
378
- const getSectionExtraHeight = ({ sectionIndex, type }: { sectionIndex: number, type: 'header' | 'footer' }) => {
379
- const item = convertedListData[sectionIndex]
380
- const isHeader = type === 'header'
381
- if (!(isHeader ? item.hasSectionHeader : item.hasSectionFooter)) return 0
382
- const sectionExtraHeight = (isHeader ? sectionHeaderHeight : sectionFooterHeight) as ItemHeightType
383
- if (sectionExtraHeight.getter) {
384
- const sectionExtraData = isHeader ? item.headerData : item.footerData
385
- return sectionExtraHeight.getter?.(sectionExtraData, getOriginalIndex(sectionIndex, type)) || 0
386
- }
387
- return sectionExtraHeight.value || 0
388
- }
389
-
390
- const convertedListData = useMemo(() => {
391
- const sections: Section[] = []
392
- let currentSection: Section | null = null
393
- // 清空之前的索引映射
394
- indexMap.current = {}
395
- // 清空反向索引映射
396
- reverseIndexMap.current = {}
397
- exposureIndexMap.current = {}
398
-
399
- // 处理 listData 为空的情况
400
- if (!listData || !listData.length) {
401
- return sections
402
- }
403
-
404
- let exposureIndex = 0
405
- // 需与 RN SectionList 和 getItemLayout 的扁平顺序保持一致:每个 section 都是 [header, ...items, footer]
406
- const createSection = (headerInfo?: { data: ListItem, index: number }): Section => {
407
- const sectionIndex = sections.length
408
- const section = {
409
- headerData: headerInfo?.data || null,
410
- footerData: null,
411
- data: [],
412
- hasSectionHeader: !!headerInfo,
413
- hasSectionFooter: false
414
- }
415
-
416
- if (headerInfo) {
417
- const { index } = headerInfo
418
- indexMap.current[index] = `${sectionIndex}_header`
419
- reverseIndexMap.current[`${sectionIndex}_header`] = index
420
- exposureIndexMap.current[exposureIndex] = {
421
- index,
422
- type: 'header'
423
- }
424
- }
425
- exposureIndex++
426
-
427
- return section
428
- }
429
-
430
- const closeSection = () => {
431
- const section = currentSection
432
- if (!section) return
433
-
434
- if (section.hasSectionFooter) {
435
- const sectionIndex = sections.length
436
- const index = getOriginalIndex(sectionIndex, 'footer')
437
- if (index > -1) {
438
- exposureIndexMap.current[exposureIndex] = {
439
- index,
440
- type: 'footer'
441
- }
442
- }
443
- }
444
- // RN SectionList 即使没有真实 footer,也会为每个 section 保留 footer slot
445
- exposureIndex++
446
-
447
- sections.push(section)
448
- currentSection = null
449
- }
450
-
451
- listData.forEach((item: ListItem, index: number) => {
452
- if (item.isSectionHeader) {
453
- // 如果已经存在一个 section,先把它添加到 sections 中
454
- if (currentSection) {
455
- closeSection()
456
- }
457
- currentSection = createSection({ data: item, index })
458
- } else if (item.isSectionFooter) {
459
- // 如果没有当前 section,创建一个默认的
460
- if (!currentSection) {
461
- // 创建默认section (无header的section)
462
- currentSection = createSection()
463
- }
464
- const sectionIndex = sections.length
465
- currentSection.footerData = item
466
- currentSection.hasSectionFooter = true
467
- indexMap.current[index] = `${sectionIndex}_footer`
468
- // 添加反向索引映射
469
- reverseIndexMap.current[`${sectionIndex}_footer`] = index
470
- closeSection()
471
- } else {
472
- // 如果没有当前 section,创建一个默认的
473
- if (!currentSection) {
474
- // 创建默认section (无header的section)
475
- currentSection = createSection()
476
- }
477
- // 将 item 添加到当前 section 的 data 中
478
- const itemIndex = currentSection.data.length
479
- currentSection.data.push(item)
480
- const sectionIndex = sections.length
481
- // 为 item 添加索引映射 - 存储格式为: "sectionIndex_itemIndex"
482
- indexMap.current[index] = `${sectionIndex}_${itemIndex}`
483
- // 添加反向索引映射
484
- reverseIndexMap.current[`${sectionIndex}_${itemIndex}`] = index
485
- exposureIndexMap.current[exposureIndex] = {
486
- index,
487
- type: 'item'
488
- }
489
- exposureIndex++
490
- }
491
- })
492
- // 添加最后一个 section
493
- if (currentSection) {
494
- closeSection()
495
- }
496
- return sections
497
- }, [listData])
498
-
499
- const { getItemLayout } = useMemo(() => {
500
- const layouts: ItemLayoutInfo[] = []
501
- let offset = 0
502
-
503
- if (useListHeader) {
504
- // 计算列表头部的高度
505
- offset += listHeaderHeight
506
- }
507
-
508
- // 遍历所有 sections
509
- convertedListData.forEach((section: Section, sectionIndex: number) => {
510
- // 添加 section header 的位置信息
511
- const headerHeight = getSectionExtraHeight({ sectionIndex, type: 'header' })
512
- layouts.push({
513
- length: headerHeight,
514
- offset,
515
- index: layouts.length
516
- })
517
- offset += headerHeight
518
-
519
- // 添加该 section 中所有 items 的位置信息
520
- section.data.forEach((item: ListItem, itemIndex: number) => {
521
- const contentHeight = getItemHeight({ sectionIndex, rowIndex: itemIndex })
522
- layouts.push({
523
- length: contentHeight,
524
- offset,
525
- index: layouts.length
526
- })
527
- offset += contentHeight
528
- })
529
-
530
- // 添加该 section 尾部位置信息
531
- // 因为即使 sectionList 没传 renderSectionFooter,getItemLayout 中的 index 的计算也会包含尾部节点
532
- const footerHeight = getSectionExtraHeight({ sectionIndex, type: 'footer' })
533
- layouts.push({
534
- length: footerHeight,
535
- offset,
536
- index: layouts.length
537
- })
538
- offset += footerHeight
539
- })
540
- itemLayoutsRef.current = layouts
541
- return {
542
- getItemLayout: (data: any, index: number) => layouts[index]
543
- }
544
- }, [convertedListData, useListHeader, itemHeight.value, itemHeight.getter, sectionHeaderHeight.value, sectionHeaderHeight.getter, sectionFooterHeight.value, sectionFooterHeight.getter, listHeaderHeight])
545
-
546
- useEffect(() => {
547
- itemExposureState.current = {}
548
- }, [convertedListData, enableSticky])
549
-
550
- const scrollAdditionalProps = extendObject(
551
- {
552
- style: [
553
- hasOwn(style, 'flex') || hasOwn(style, 'flexGrow') ? null : { flexGrow: 0 },
554
- { height, width },
555
- style,
556
- layoutStyle
557
- ],
558
- alwaysBounceVertical: false,
559
- alwaysBounceHorizontal: false,
560
- scrollEventThrottle: scrollEventThrottle,
561
- scrollsToTop: enableBackToTop,
562
- showsHorizontalScrollIndicator: showScrollbar,
563
- onEndReachedThreshold,
564
- ref: scrollViewRef,
565
- bounces: enhanced ? bounces : false,
566
- stickySectionHeadersEnabled: enableSticky,
567
- onScroll: onScroll,
568
- onEndReached: onEndReached
569
- },
570
- refresherEnabled ? { refreshing } : null,
571
- layoutProps
572
- )
573
-
574
- if (initialEnableItemExposureRef.current) {
575
- extendObject(scrollAdditionalProps, {
576
- viewabilityConfigCallbackPairs: itemExposureViewabilityPairs.current
577
- })
578
- }
579
-
580
- const nativeGesture = useMemo(() => {
581
- const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
582
- const waitForHandlers = flatGesture(waitFor)
583
- const gesture = Gesture.Native().withRef(sectionListGestureRef as any)
584
- if (simultaneousHandlers && simultaneousHandlers.length) {
585
- gesture.simultaneousWithExternalGesture(...simultaneousHandlers)
586
- }
587
- if (waitForHandlers && waitForHandlers.length) {
588
- gesture.requireExternalGestureToFail(...waitForHandlers)
589
- }
590
- return gesture
591
- }, [originSimultaneousHandlers, waitFor])
592
-
593
- useImperativeHandle(ref, () => {
594
- return {
595
- gestureRef: sectionListGestureRef,
596
- scrollToIndex
597
- }
598
- })
599
-
600
- const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
601
- 'id',
602
- 'enhanced',
603
- 'height',
604
- 'width',
605
- 'list-data',
606
- 'item-height',
607
- 'section-header-height',
608
- 'section-footer-height',
609
- 'list-header-height',
610
- 'list-header-data',
611
- 'use-list-header',
612
- 'list-footer-data',
613
- 'use-list-footer',
614
- 'genericrecycle-item',
615
- 'genericsection-header',
616
- 'genericsection-footer',
617
- 'genericlist-header',
618
- 'genericlist-footer',
619
- 'show-scrollbar',
620
- 'lower-threshold',
621
- 'scroll-event-throttle',
622
- 'enable-sticky',
623
- 'enable-back-to-top',
624
- 'end-reached-threshold',
625
- 'refresher-triggered',
626
- 'refresher-enabled',
627
- 'enable-item-exposure',
628
- 'item-exposure-threshold',
629
- 'bindrefresherrefresh',
630
- 'bindscrolltolower',
631
- 'bindscroll',
632
- 'binditemexposure',
633
- 'simultaneous-handlers',
634
- 'wait-for'
635
- ], { layoutRef })
636
-
637
- // 使用 useMemo 获取 GenericComponent 并创建渲染函数,避免每次组件更新都重新创建函数引用导致不必要的重新渲染
638
- const renderItem = useMemo(
639
- () => {
640
- const ItemComponent = getGeneric(generichash, genericrecycleItem)
641
- if (!ItemComponent) return undefined
642
- return ({ item }: { item: ListItem }) => createElement(ItemComponent, { itemData: item })
643
- },
644
- [generichash, genericrecycleItem]
645
- )
646
-
647
- const renderSectionHeader = useMemo(
648
- () => {
649
- const SectionHeaderComponent = getGeneric(generichash, genericsectionHeader)
650
- if (!SectionHeaderComponent) return undefined
651
- return (sectionData: { section: RNSection }) => {
652
- if (!sectionData.section.hasSectionHeader) return null
653
- return createElement(SectionHeaderComponent, { itemData: sectionData.section.headerData })
654
- }
655
- },
656
- [generichash, genericsectionHeader]
657
- )
658
-
659
- const renderSectionFooter = useMemo(
660
- () => {
661
- const SectionFooterComponent = getGeneric(generichash, genericsectionFooter)
662
- if (!SectionFooterComponent) return undefined
663
- return (sectionData: { section: RNSection }) => {
664
- if (!sectionData.section.hasSectionFooter) return null
665
- return createElement(SectionFooterComponent, { itemData: sectionData.section.footerData })
666
- }
667
- },
668
- [generichash, genericsectionFooter]
669
- )
670
-
671
- const ListHeaderGenericComponent = useMemo(
672
- () => {
673
- if (!useListHeader) return null
674
- return getGeneric(generichash, genericListHeader)
675
- },
676
- [useListHeader, generichash, genericListHeader]
677
- )
678
-
679
- const ListFooterGenericComponent = useMemo(
680
- () => {
681
- if (!useListFooter) return null
682
- return getGeneric(generichash, genericListFooter)
683
- },
684
- [useListFooter, generichash, genericListFooter]
685
- )
686
-
687
- const ListHeaderComponent = useMemo(
688
- () => {
689
- if (!ListHeaderGenericComponent) return null
690
- return createElement(ListHeaderGenericComponent, { listHeaderData })
691
- },
692
- [ListHeaderGenericComponent, listHeaderData]
693
- )
694
-
695
- const ListFooterComponent = useMemo(
696
- () => {
697
- if (!ListFooterGenericComponent) return null
698
- return createElement(ListFooterGenericComponent, { listFooterData })
699
- },
700
- [ListFooterGenericComponent, listFooterData]
701
- )
702
-
703
- const sectionListProps: RNSectionListProps<ListItem, SectionExtra> = extendObject(
704
- {
705
- sections: convertedListData,
706
- renderItem: renderItem,
707
- getItemLayout: getItemLayout,
708
- ListHeaderComponent: useListHeader ? ListHeaderComponent : null,
709
- ListFooterComponent: useListFooter ? ListFooterComponent : null,
710
- renderSectionHeader: renderSectionHeader,
711
- renderSectionFooter: renderSectionFooter,
712
- refreshControl: refresherEnabled
713
- ? createElement(RefreshControl, {
714
- onRefresh: onRefresh,
715
- refreshing: refreshing
716
- })
717
- : undefined
718
- },
719
- innerProps
720
- )
721
-
722
- return createElement(
723
- GestureDetector,
724
- { gesture: nativeGesture },
725
- createElement(
726
- TypedSectionList,
727
- sectionListProps
728
- )
729
- )
730
- })
731
-
732
- _SectionList.displayName = 'MpxSectionList'
733
-
734
- export default _SectionList