@mpxjs/webpack-plugin 2.10.24 → 2.11.0

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 (151) hide show
  1. package/lib/index.js +4 -1
  2. package/lib/platform/style/wx/index.js +417 -120
  3. package/lib/platform/template/wx/component-config/picker.js +0 -6
  4. package/lib/react/style-helper.js +6 -6
  5. package/lib/resolver/AddEnvPlugin.js +1 -1
  6. package/lib/resolver/AddModePlugin.js +1 -1
  7. package/lib/resolver/ExtendComponentsPlugin.js +68 -0
  8. package/lib/runtime/components/extends/section-list.mpx +14 -0
  9. package/lib/runtime/components/react/animationHooks/index.ts +7 -3
  10. package/lib/runtime/components/react/dist/animationHooks/index.d.ts +0 -2
  11. package/lib/runtime/components/react/dist/animationHooks/index.js +6 -1
  12. package/lib/runtime/components/react/dist/event.config.d.ts +2 -6
  13. package/lib/runtime/components/react/dist/event.config.js +28 -24
  14. package/lib/runtime/components/react/dist/getInnerListeners.d.ts +1 -1
  15. package/lib/runtime/components/react/dist/getInnerListeners.js +74 -56
  16. package/lib/runtime/components/react/dist/mpx-button.d.ts +1 -2
  17. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -9
  18. package/lib/runtime/components/react/dist/mpx-camera.d.ts +0 -2
  19. package/lib/runtime/components/react/dist/mpx-camera.jsx +36 -5
  20. package/lib/runtime/components/react/dist/mpx-canvas/index.d.ts +0 -2
  21. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +1 -3
  22. package/lib/runtime/components/react/dist/mpx-canvas/utils.d.ts +1 -1
  23. package/lib/runtime/components/react/dist/mpx-checkbox-group.d.ts +0 -2
  24. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +3 -4
  25. package/lib/runtime/components/react/dist/mpx-checkbox.d.ts +1 -2
  26. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +8 -9
  27. package/lib/runtime/components/react/dist/mpx-form.d.ts +1 -2
  28. package/lib/runtime/components/react/dist/mpx-form.jsx +5 -5
  29. package/lib/runtime/components/react/dist/mpx-icon/index.d.ts +0 -2
  30. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +2 -2
  31. package/lib/runtime/components/react/dist/mpx-image.d.ts +0 -2
  32. package/lib/runtime/components/react/dist/mpx-image.jsx +59 -43
  33. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +13 -4
  34. package/lib/runtime/components/react/dist/mpx-input.d.ts +0 -2
  35. package/lib/runtime/components/react/dist/mpx-input.jsx +16 -8
  36. package/lib/runtime/components/react/dist/mpx-label.d.ts +1 -2
  37. package/lib/runtime/components/react/dist/mpx-label.jsx +5 -6
  38. package/lib/runtime/components/react/dist/mpx-movable-area.d.ts +0 -2
  39. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +3 -3
  40. package/lib/runtime/components/react/dist/mpx-movable-view.d.ts +1 -2
  41. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +6 -6
  42. package/lib/runtime/components/react/dist/mpx-nav.jsx +1 -1
  43. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +72 -23
  44. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +2 -2
  45. package/lib/runtime/components/react/dist/mpx-picker/type.d.ts +4 -0
  46. package/lib/runtime/components/react/dist/mpx-picker-view/index.d.ts +1 -1
  47. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +10 -14
  48. package/lib/runtime/components/react/dist/mpx-picker-view/pickerVIewContext.d.ts +0 -2
  49. package/lib/runtime/components/react/dist/mpx-picker-view/pickerVIewContext.js +0 -5
  50. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.d.ts +1 -1
  51. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +34 -14
  52. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.d.ts +0 -2
  53. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.jsx +4 -5
  54. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.d.ts +0 -2
  55. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +3 -5
  56. package/lib/runtime/components/react/dist/mpx-popup/index.d.ts +5 -4
  57. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +2 -2
  58. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +3 -2
  59. package/lib/runtime/components/react/dist/mpx-progress.d.ts +0 -2
  60. package/lib/runtime/components/react/dist/mpx-progress.jsx +1 -3
  61. package/lib/runtime/components/react/dist/mpx-radio-group.d.ts +0 -2
  62. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +3 -4
  63. package/lib/runtime/components/react/dist/mpx-radio.d.ts +1 -2
  64. package/lib/runtime/components/react/dist/mpx-radio.jsx +8 -9
  65. package/lib/runtime/components/react/dist/mpx-rich-text/index.d.ts +0 -2
  66. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +1 -3
  67. package/lib/runtime/components/react/dist/mpx-scroll-view.d.ts +1 -2
  68. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -6
  69. package/lib/runtime/components/react/dist/mpx-section-list.d.ts +51 -0
  70. package/lib/runtime/components/react/dist/mpx-section-list.jsx +376 -0
  71. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +37 -19
  72. package/lib/runtime/components/react/dist/mpx-simple-view.d.ts +4 -1
  73. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -4
  74. package/lib/runtime/components/react/dist/mpx-slider.d.ts +0 -2
  75. package/lib/runtime/components/react/dist/mpx-slider.jsx +6 -7
  76. package/lib/runtime/components/react/dist/mpx-sticky-header.d.ts +1 -2
  77. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +5 -5
  78. package/lib/runtime/components/react/dist/mpx-sticky-section.d.ts +1 -2
  79. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +6 -6
  80. package/lib/runtime/components/react/dist/mpx-swiper-item.d.ts +1 -2
  81. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +5 -5
  82. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +1 -2
  83. package/lib/runtime/components/react/dist/mpx-swiper.jsx +4 -8
  84. package/lib/runtime/components/react/dist/mpx-switch.d.ts +0 -2
  85. package/lib/runtime/components/react/dist/mpx-switch.jsx +1 -3
  86. package/lib/runtime/components/react/dist/mpx-text.d.ts +0 -2
  87. package/lib/runtime/components/react/dist/mpx-text.jsx +43 -25
  88. package/lib/runtime/components/react/dist/mpx-video.d.ts +0 -2
  89. package/lib/runtime/components/react/dist/mpx-video.jsx +4 -5
  90. package/lib/runtime/components/react/dist/mpx-view.d.ts +2 -2
  91. package/lib/runtime/components/react/dist/mpx-view.jsx +214 -114
  92. package/lib/runtime/components/react/dist/mpx-web-view.d.ts +2 -1
  93. package/lib/runtime/components/react/dist/mpx-web-view.jsx +5 -1
  94. package/lib/runtime/components/react/dist/namedColorSet.d.ts +2 -0
  95. package/lib/runtime/components/react/dist/namedColorSet.js +149 -0
  96. package/lib/runtime/components/react/dist/utils.d.ts +71 -70
  97. package/lib/runtime/components/react/dist/utils.jsx +1295 -456
  98. package/lib/runtime/components/react/event.config.ts +32 -25
  99. package/lib/runtime/components/react/getInnerListeners.ts +84 -60
  100. package/lib/runtime/components/react/mpx-button.tsx +18 -18
  101. package/lib/runtime/components/react/mpx-camera.tsx +40 -8
  102. package/lib/runtime/components/react/mpx-canvas/index.tsx +5 -7
  103. package/lib/runtime/components/react/mpx-canvas/utils.tsx +3 -1
  104. package/lib/runtime/components/react/mpx-checkbox-group.tsx +2 -8
  105. package/lib/runtime/components/react/mpx-checkbox.tsx +9 -13
  106. package/lib/runtime/components/react/mpx-form.tsx +6 -8
  107. package/lib/runtime/components/react/mpx-icon/index.tsx +1 -5
  108. package/lib/runtime/components/react/mpx-image.tsx +66 -58
  109. package/lib/runtime/components/react/mpx-inline-text.tsx +13 -4
  110. package/lib/runtime/components/react/mpx-input.tsx +19 -12
  111. package/lib/runtime/components/react/mpx-label.tsx +6 -10
  112. package/lib/runtime/components/react/mpx-movable-area.tsx +3 -5
  113. package/lib/runtime/components/react/mpx-movable-view.tsx +7 -9
  114. package/lib/runtime/components/react/mpx-nav.tsx +1 -1
  115. package/lib/runtime/components/react/mpx-picker/index.tsx +100 -29
  116. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +3 -3
  117. package/lib/runtime/components/react/mpx-picker/selector.tsx +1 -1
  118. package/lib/runtime/components/react/mpx-picker/type.ts +4 -0
  119. package/lib/runtime/components/react/mpx-picker-view/index.tsx +17 -24
  120. package/lib/runtime/components/react/mpx-picker-view/pickerVIewContext.ts +0 -9
  121. package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +44 -23
  122. package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItem.tsx +1 -9
  123. package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItemLite.tsx +0 -9
  124. package/lib/runtime/components/react/mpx-popup/index.tsx +7 -3
  125. package/lib/runtime/components/react/mpx-popup/popupBase.tsx +3 -2
  126. package/lib/runtime/components/react/mpx-progress.tsx +0 -6
  127. package/lib/runtime/components/react/mpx-radio-group.tsx +2 -8
  128. package/lib/runtime/components/react/mpx-radio.tsx +9 -13
  129. package/lib/runtime/components/react/mpx-rich-text/index.tsx +0 -6
  130. package/lib/runtime/components/react/mpx-scroll-view.tsx +7 -9
  131. package/lib/runtime/components/react/mpx-section-list.tsx +557 -0
  132. package/lib/runtime/components/react/mpx-simple-text.tsx +36 -25
  133. package/lib/runtime/components/react/mpx-simple-view.tsx +17 -5
  134. package/lib/runtime/components/react/mpx-slider.tsx +15 -13
  135. package/lib/runtime/components/react/mpx-sticky-header.tsx +6 -8
  136. package/lib/runtime/components/react/mpx-sticky-section.tsx +7 -9
  137. package/lib/runtime/components/react/mpx-swiper-item.tsx +6 -7
  138. package/lib/runtime/components/react/mpx-swiper.tsx +5 -11
  139. package/lib/runtime/components/react/mpx-switch.tsx +0 -6
  140. package/lib/runtime/components/react/mpx-text.tsx +45 -36
  141. package/lib/runtime/components/react/mpx-video.tsx +3 -8
  142. package/lib/runtime/components/react/mpx-view.tsx +235 -129
  143. package/lib/runtime/components/react/mpx-web-view.tsx +12 -6
  144. package/lib/runtime/components/react/namedColorSet.ts +150 -0
  145. package/lib/runtime/components/react/types/getInnerListeners.d.ts +21 -1
  146. package/lib/runtime/components/react/types/global.d.ts +1 -0
  147. package/lib/runtime/components/react/utils.tsx +1352 -550
  148. package/lib/runtime/i18n.wxs +15 -9
  149. package/lib/style-compiler/strip-conditional.js +19 -4
  150. package/lib/template-compiler/compiler.js +6 -0
  151. package/package.json +6 -6
@@ -0,0 +1,557 @@
1
+ import { forwardRef, useRef, useMemo, createElement, useImperativeHandle } from 'react'
2
+ import type { ComponentType, ReactElement } 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 } from '@mpxjs/utils'
7
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
8
+ import { extendObject, useLayout, useTransformStyle, GestureHandler, flatGesture, wrapChildren, splitStyle, useTextPassThrough, splitProps } from './utils'
9
+ import Portal from './mpx-portal'
10
+ interface ListItem {
11
+ isSectionHeader?: boolean;
12
+ isSectionFooter?: boolean;
13
+ [key: string]: any;
14
+ }
15
+
16
+ interface SectionExtra {
17
+ headerData: ListItem | null;
18
+ footerData: ListItem | null;
19
+ hasSectionHeader?: boolean;
20
+ hasSectionFooter?: boolean;
21
+ }
22
+
23
+ interface Section extends SectionExtra {
24
+ data: ListItem[];
25
+ }
26
+
27
+ type RNSection = SectionListData<ListItem, SectionExtra>
28
+
29
+ const TypedSectionList = SectionList as unknown as ComponentType<RNSectionListProps<ListItem, SectionExtra>>
30
+
31
+ interface ItemHeightType {
32
+ value?: number;
33
+ getter?: (item: any, index: number) => number;
34
+ }
35
+
36
+ interface MpxSectionListProps {
37
+ enhanced?: boolean;
38
+ bounces?: boolean;
39
+ height?: number | string;
40
+ width?: number | string;
41
+ generichash?: string;
42
+ style?: Record<string, any>;
43
+ 'scroll-event-throttle'?: number;
44
+ 'list-data'?: ListItem[];
45
+ 'item-height'?: ItemHeightType;
46
+ 'section-header-height'?: ItemHeightType;
47
+ 'section-footer-height'?: ItemHeightType;
48
+ 'list-header-data'?: any;
49
+ 'list-header-height'?: number;
50
+ 'use-list-header'?: boolean;
51
+ 'list-footer-data'?: any;
52
+ 'use-list-footer'?: boolean;
53
+ 'genericrecycle-item'?: string;
54
+ 'genericsection-header'?: string;
55
+ 'genericsection-footer'?: string;
56
+ 'genericlist-header'?: string;
57
+ 'genericlist-footer'?: string;
58
+ 'enable-var'?: boolean;
59
+ 'parent-width'?: number;
60
+ 'parent-height'?: number;
61
+ 'enable-sticky'?: boolean;
62
+ 'enable-back-to-top'?: boolean;
63
+ 'end-reached-threshold'?: number;
64
+ 'refresher-enabled'?: boolean;
65
+ 'show-scrollbar'?: boolean;
66
+ 'refresher-triggered'?: boolean;
67
+ 'wait-for'?: Array<GestureHandler>;
68
+ 'simultaneous-handlers'?: Array<GestureHandler>;
69
+ bindrefresherrefresh?: (event: any) => void;
70
+ bindscrolltolower?: (event: any) => void;
71
+ bindscroll?: (event: any) => void;
72
+ [key: string]: any;
73
+ }
74
+
75
+ interface ScrollPositionParams {
76
+ index: number;
77
+ animated?: boolean;
78
+ viewOffset?: number;
79
+ viewPosition?: number;
80
+ }
81
+
82
+ const getGeneric = (generichash: string, generickey: string) => {
83
+ if (!generichash || !generickey) return null
84
+ return global.__mpxGenericsMap?.[generichash]?.[generickey]?.() || null
85
+ }
86
+
87
+ const _SectionList = forwardRef<any, MpxSectionListProps>((sectionListProps = {}, ref) => {
88
+ const { textProps, innerProps: props = {} } = splitProps(sectionListProps)
89
+ const {
90
+ enhanced = false,
91
+ bounces = true,
92
+ height,
93
+ width,
94
+ generichash,
95
+ style = {},
96
+ 'list-data': listData,
97
+ 'scroll-event-throttle': scrollEventThrottle = 0,
98
+ 'item-height': itemHeight = {},
99
+ 'section-header-height': sectionHeaderHeight = {},
100
+ 'section-footer-height': sectionFooterHeight = {},
101
+ 'list-header-height': listHeaderHeight = 0,
102
+ 'list-header-data': listHeaderData = null,
103
+ 'use-list-header': useListHeader = false,
104
+ 'list-footer-data': listFooterData = null,
105
+ 'use-list-footer': useListFooter = false,
106
+ 'genericrecycle-item': genericrecycleItem,
107
+ 'genericsection-header': genericsectionHeader,
108
+ 'genericsection-footer': genericsectionFooter,
109
+ 'genericlist-header': genericListHeader,
110
+ 'genericlist-footer': genericListFooter,
111
+ 'enable-var': enableVar,
112
+ 'enable-text-pass-through': enableTextPassThrough,
113
+ 'parent-width': parentWidth,
114
+ 'parent-height': parentHeight,
115
+ 'enable-sticky': enableSticky = false,
116
+ 'enable-back-to-top': enableBackToTop = false,
117
+ 'end-reached-threshold': onEndReachedThreshold = 0.1,
118
+ 'refresher-enabled': refresherEnabled,
119
+ 'show-scrollbar': showScrollbar = true,
120
+ 'refresher-triggered': refresherTriggered,
121
+ 'simultaneous-handlers': originSimultaneousHandlers,
122
+ 'wait-for': waitFor
123
+ } = props
124
+
125
+ const refreshing = !!refresherTriggered
126
+
127
+ const scrollViewRef = useRef<any>(null)
128
+ const sectionListGestureRef = useRef<any>()
129
+
130
+ const indexMap = useRef<{ [key: string]: string }>({})
131
+
132
+ const reverseIndexMap = useRef<{ [key: string]: number }>({})
133
+
134
+ const {
135
+ normalStyle,
136
+ hasVarDec,
137
+ varContextRef,
138
+ hasSelfPercent,
139
+ hasPositionFixed,
140
+ setWidth,
141
+ setHeight
142
+ } = useTransformStyle(style, { enableVar, parentWidth, parentHeight })
143
+
144
+ const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
145
+ const textPassThrough = useTextPassThrough(textStyle, textProps, { enableTextPassThrough })
146
+
147
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef })
148
+
149
+ const onRefresh = () => {
150
+ const { bindrefresherrefresh } = props
151
+ bindrefresherrefresh &&
152
+ bindrefresherrefresh(
153
+ getCustomEvent('refresherrefresh', {}, { layoutRef }, props)
154
+ )
155
+ }
156
+
157
+ const onEndReached = () => {
158
+ const { bindscrolltolower } = props
159
+ bindscrolltolower &&
160
+ bindscrolltolower(
161
+ getCustomEvent('scrolltolower', {}, { layoutRef }, props)
162
+ )
163
+ }
164
+
165
+ const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
166
+ const { bindscroll } = props
167
+ if (bindscroll) {
168
+ const { nativeEvent } = event
169
+ bindscroll(
170
+ getCustomEvent('scroll', event, {
171
+ detail: {
172
+ scrollTop: nativeEvent.contentOffset.y
173
+ },
174
+ layoutRef
175
+ }, props)
176
+ )
177
+ }
178
+ }
179
+
180
+ // 通过sectionIndex和rowIndex获取原始索引
181
+ const getOriginalIndex = (sectionIndex: number, rowIndex: number | 'header' | 'footer'): number => {
182
+ const key = `${sectionIndex}_${rowIndex}`
183
+ return reverseIndexMap.current[key] ?? -1 // 如果找不到,返回-1
184
+ }
185
+
186
+ const scrollToIndex = ({ index, animated, viewOffset = 0, viewPosition = 0 }: ScrollPositionParams) => {
187
+ if (!scrollViewRef.current) return
188
+ // 通过索引映射表快速定位位置
189
+ const position = indexMap.current[index]
190
+ if (!position) return
191
+ const [sectionIndex, itemIndex] = position.split('_')
192
+ const targetSectionIndex = Number(sectionIndex) || 0
193
+ const targetItemIndex = itemIndex === 'header'
194
+ ? 0
195
+ : itemIndex === 'footer'
196
+ ? convertedListData[targetSectionIndex].data.length + 1
197
+ : Number(itemIndex) + 1
198
+ scrollViewRef.current.scrollToLocation?.({
199
+ itemIndex: targetItemIndex,
200
+ sectionIndex: targetSectionIndex,
201
+ animated,
202
+ viewOffset,
203
+ viewPosition
204
+ })
205
+ }
206
+
207
+ const getItemHeight = ({ sectionIndex, rowIndex }: { sectionIndex: number, rowIndex: number }) => {
208
+ if (!itemHeight) {
209
+ return 0
210
+ }
211
+ if ((itemHeight as ItemHeightType).getter) {
212
+ const item = convertedListData[sectionIndex].data[rowIndex]
213
+ // 使用getOriginalIndex获取原始索引
214
+ const originalIndex = getOriginalIndex(sectionIndex, rowIndex)
215
+ return (itemHeight as ItemHeightType).getter?.(item, originalIndex) || 0
216
+ } else {
217
+ return (itemHeight as ItemHeightType).value || 0
218
+ }
219
+ }
220
+
221
+ const getSectionExtraHeight = ({ sectionIndex, type }: { sectionIndex: number, type: 'header' | 'footer' }) => {
222
+ const item = convertedListData[sectionIndex]
223
+ const isHeader = type === 'header'
224
+ if (!(isHeader ? item.hasSectionHeader : item.hasSectionFooter)) return 0
225
+ const sectionExtraHeight = (isHeader ? sectionHeaderHeight : sectionFooterHeight) as ItemHeightType
226
+ if (sectionExtraHeight.getter) {
227
+ const sectionExtraData = isHeader ? item.headerData : item.footerData
228
+ return sectionExtraHeight.getter?.(sectionExtraData, getOriginalIndex(sectionIndex, type)) || 0
229
+ }
230
+ return sectionExtraHeight.value || 0
231
+ }
232
+
233
+ const convertedListData = useMemo(() => {
234
+ const sections: Section[] = []
235
+ let currentSection: Section | null = null
236
+ // 清空之前的索引映射
237
+ indexMap.current = {}
238
+ // 清空反向索引映射
239
+ reverseIndexMap.current = {}
240
+
241
+ // 处理 listData 为空的情况
242
+ if (!listData || !listData.length) {
243
+ return sections
244
+ }
245
+
246
+ listData.forEach((item: ListItem, index: number) => {
247
+ if (item.isSectionHeader) {
248
+ // 如果已经存在一个 section,先把它添加到 sections 中
249
+ if (currentSection) {
250
+ sections.push(currentSection)
251
+ }
252
+ // 创建新的 section
253
+ currentSection = {
254
+ headerData: item,
255
+ footerData: null,
256
+ data: [],
257
+ hasSectionHeader: true,
258
+ hasSectionFooter: false
259
+ }
260
+ // 为 section header 添加索引映射
261
+ const sectionIndex = sections.length
262
+ indexMap.current[index] = `${sectionIndex}_header`
263
+ // 添加反向索引映射
264
+ reverseIndexMap.current[`${sectionIndex}_header`] = index
265
+ } else if (item.isSectionFooter) {
266
+ // 如果没有当前 section,创建一个默认的
267
+ if (!currentSection) {
268
+ // 创建默认section (无header的section)
269
+ currentSection = {
270
+ headerData: null,
271
+ footerData: null,
272
+ data: [],
273
+ hasSectionHeader: false,
274
+ hasSectionFooter: false
275
+ }
276
+ }
277
+ const sectionIndex = sections.length
278
+ currentSection.footerData = item
279
+ currentSection.hasSectionFooter = true
280
+ indexMap.current[index] = `${sectionIndex}_footer`
281
+ // 添加反向索引映射
282
+ reverseIndexMap.current[`${sectionIndex}_footer`] = index
283
+ sections.push(currentSection)
284
+ currentSection = null
285
+ } else {
286
+ // 如果没有当前 section,创建一个默认的
287
+ if (!currentSection) {
288
+ // 创建默认section (无header的section)
289
+ currentSection = {
290
+ headerData: null,
291
+ footerData: null,
292
+ data: [],
293
+ hasSectionHeader: false,
294
+ hasSectionFooter: false
295
+ }
296
+ }
297
+ // 将 item 添加到当前 section 的 data 中
298
+ const itemIndex = currentSection.data.length
299
+ currentSection.data.push(item)
300
+ let sectionIndex
301
+ // 为 item 添加索引映射 - 存储格式为: "sectionIndex_itemIndex"
302
+ if (!currentSection.hasSectionHeader && sections.length === 0) {
303
+ // 在默认section中(第一个且无header)
304
+ sectionIndex = 0
305
+ indexMap.current[index] = `${sectionIndex}_${itemIndex}`
306
+ } else {
307
+ // 在普通section中
308
+ sectionIndex = sections.length
309
+ indexMap.current[index] = `${sectionIndex}_${itemIndex}`
310
+ }
311
+ // 添加反向索引映射
312
+ reverseIndexMap.current[`${sectionIndex}_${itemIndex}`] = index
313
+ }
314
+ })
315
+ // 添加最后一个 section
316
+ if (currentSection) {
317
+ sections.push(currentSection)
318
+ }
319
+ return sections
320
+ }, [listData, listData?.length])
321
+
322
+ const { getItemLayout } = useMemo(() => {
323
+ const layouts: Array<{ length: number, offset: number, index: number }> = []
324
+ let offset = 0
325
+
326
+ if (useListHeader) {
327
+ // 计算列表头部的高度
328
+ offset += listHeaderHeight
329
+ }
330
+
331
+ // 遍历所有 sections
332
+ convertedListData.forEach((section: Section, sectionIndex: number) => {
333
+ // 添加 section header 的位置信息
334
+ const headerHeight = getSectionExtraHeight({ sectionIndex, type: 'header' })
335
+ layouts.push({
336
+ length: headerHeight,
337
+ offset,
338
+ index: layouts.length
339
+ })
340
+ offset += headerHeight
341
+
342
+ // 添加该 section 中所有 items 的位置信息
343
+ section.data.forEach((item: ListItem, itemIndex: number) => {
344
+ const contentHeight = getItemHeight({ sectionIndex, rowIndex: itemIndex })
345
+ layouts.push({
346
+ length: contentHeight,
347
+ offset,
348
+ index: layouts.length
349
+ })
350
+ offset += contentHeight
351
+ })
352
+
353
+ // 添加该 section 尾部位置信息
354
+ // 因为即使 sectionList 没传 renderSectionFooter,getItemLayout 中的 index 的计算也会包含尾部节点
355
+ const footerHeight = getSectionExtraHeight({ sectionIndex, type: 'footer' })
356
+ layouts.push({
357
+ length: footerHeight,
358
+ offset,
359
+ index: layouts.length
360
+ })
361
+ offset += footerHeight
362
+ })
363
+ return {
364
+ itemLayouts: layouts,
365
+ getItemLayout: (data: any, index: number) => layouts[index]
366
+ }
367
+ }, [convertedListData, useListHeader, itemHeight.value, itemHeight.getter, sectionHeaderHeight.value, sectionHeaderHeight.getter, sectionFooterHeight.value, sectionFooterHeight.getter, listHeaderHeight])
368
+
369
+ const scrollAdditionalProps = extendObject(
370
+ {
371
+ style: [
372
+ hasOwn(innerStyle, 'flex') || hasOwn(innerStyle, 'flexGrow') ? null : { flexGrow: 0 },
373
+ { height, width },
374
+ innerStyle,
375
+ layoutStyle
376
+ ],
377
+ alwaysBounceVertical: false,
378
+ alwaysBounceHorizontal: false,
379
+ scrollEventThrottle: scrollEventThrottle,
380
+ scrollsToTop: enableBackToTop,
381
+ showsVerticalScrollIndicator: showScrollbar,
382
+ onEndReachedThreshold,
383
+ ref: scrollViewRef,
384
+ bounces: enhanced ? bounces : false,
385
+ stickySectionHeadersEnabled: enableSticky,
386
+ onScroll: onScroll,
387
+ onEndReached: onEndReached
388
+ },
389
+ refresherEnabled ? { refreshing } : null,
390
+ layoutProps
391
+ )
392
+
393
+ const nativeGesture = useMemo(() => {
394
+ const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
395
+ const waitForHandlers = flatGesture(waitFor)
396
+ const gesture = Gesture.Native().withRef(sectionListGestureRef as any)
397
+ if (simultaneousHandlers && simultaneousHandlers.length) {
398
+ gesture.simultaneousWithExternalGesture(...simultaneousHandlers)
399
+ }
400
+ if (waitForHandlers && waitForHandlers.length) {
401
+ gesture.requireExternalGestureToFail(...waitForHandlers)
402
+ }
403
+ return gesture
404
+ }, [originSimultaneousHandlers, waitFor])
405
+
406
+ useImperativeHandle(ref, () => {
407
+ return {
408
+ gestureRef: sectionListGestureRef,
409
+ scrollToIndex
410
+ }
411
+ })
412
+
413
+ const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
414
+ 'id',
415
+ 'enhanced',
416
+ 'height',
417
+ 'width',
418
+ 'list-data',
419
+ 'item-height',
420
+ 'section-header-height',
421
+ 'section-footer-height',
422
+ 'list-header-height',
423
+ 'list-header-data',
424
+ 'use-list-header',
425
+ 'list-footer-data',
426
+ 'use-list-footer',
427
+ 'genericrecycle-item',
428
+ 'genericsection-header',
429
+ 'genericsection-footer',
430
+ 'genericlist-header',
431
+ 'genericlist-footer',
432
+ 'enable-text-pass-through',
433
+ 'show-scrollbar',
434
+ 'lower-threshold',
435
+ 'scroll-event-throttle',
436
+ 'enable-sticky',
437
+ 'enable-back-to-top',
438
+ 'end-reached-threshold',
439
+ 'refresher-triggered',
440
+ 'refresher-enabled',
441
+ 'bindrefresherrefresh',
442
+ 'bindscrolltolower',
443
+ 'bindscroll',
444
+ 'simultaneous-handlers',
445
+ 'wait-for'
446
+ ], { layoutRef })
447
+
448
+ // 使用 useMemo 获取 GenericComponent 并创建渲染函数,避免每次组件更新都重新创建函数引用导致不必要的重新渲染
449
+ const renderItem = useMemo(
450
+ () => {
451
+ const ItemComponent = getGeneric(generichash, genericrecycleItem)
452
+ if (!ItemComponent) return undefined
453
+ return ({ item }: { item: ListItem }) => createElement(ItemComponent, { itemData: item })
454
+ },
455
+ [generichash, genericrecycleItem]
456
+ )
457
+
458
+ const renderSectionHeader = useMemo(
459
+ () => {
460
+ const SectionHeaderComponent = getGeneric(generichash, genericsectionHeader)
461
+ if (!SectionHeaderComponent) return undefined
462
+ return (sectionData: { section: RNSection }) => {
463
+ if (!sectionData.section.hasSectionHeader) return null
464
+ return createElement(SectionHeaderComponent, { itemData: sectionData.section.headerData })
465
+ }
466
+ },
467
+ [generichash, genericsectionHeader]
468
+ )
469
+
470
+ const renderSectionFooter = useMemo(
471
+ () => {
472
+ const SectionFooterComponent = getGeneric(generichash, genericsectionFooter)
473
+ if (!SectionFooterComponent) return undefined
474
+ return (sectionData: { section: RNSection }) => {
475
+ if (!sectionData.section.hasSectionFooter) return null
476
+ return createElement(SectionFooterComponent, { itemData: sectionData.section.footerData })
477
+ }
478
+ },
479
+ [generichash, genericsectionFooter]
480
+ )
481
+
482
+ const ListHeaderGenericComponent = useMemo(
483
+ () => {
484
+ if (!useListHeader) return null
485
+ return getGeneric(generichash, genericListHeader)
486
+ },
487
+ [useListHeader, generichash, genericListHeader]
488
+ )
489
+
490
+ const ListFooterGenericComponent = useMemo(
491
+ () => {
492
+ if (!useListFooter) return null
493
+ return getGeneric(generichash, genericListFooter)
494
+ },
495
+ [useListFooter, generichash, genericListFooter]
496
+ )
497
+
498
+ const ListHeaderComponent = useMemo(
499
+ () => {
500
+ if (!ListHeaderGenericComponent) return null
501
+ return createElement(ListHeaderGenericComponent, { listHeaderData })
502
+ },
503
+ [ListHeaderGenericComponent, listHeaderData]
504
+ )
505
+
506
+ const ListFooterComponent = useMemo(
507
+ () => {
508
+ if (!ListFooterGenericComponent) return null
509
+ return createElement(ListFooterGenericComponent, { listFooterData })
510
+ },
511
+ [ListFooterGenericComponent, listFooterData]
512
+ )
513
+
514
+ const rnSectionListProps: RNSectionListProps<ListItem, SectionExtra> = extendObject(
515
+ {
516
+ sections: convertedListData,
517
+ renderItem: renderItem,
518
+ getItemLayout: getItemLayout,
519
+ ListHeaderComponent: useListHeader ? ListHeaderComponent : null,
520
+ ListFooterComponent: useListFooter ? ListFooterComponent : null,
521
+ renderSectionHeader: renderSectionHeader,
522
+ renderSectionFooter: renderSectionFooter,
523
+ refreshControl: refresherEnabled
524
+ ? createElement(RefreshControl, {
525
+ onRefresh: onRefresh,
526
+ refreshing: refreshing
527
+ })
528
+ : undefined
529
+ },
530
+ innerProps
531
+ )
532
+
533
+ let sectionListComponent = wrapChildren(
534
+ createElement(
535
+ GestureDetector,
536
+ { gesture: nativeGesture },
537
+ createElement(
538
+ TypedSectionList,
539
+ rnSectionListProps
540
+ )
541
+ ),
542
+ {
543
+ hasVarDec,
544
+ varContext: varContextRef.current,
545
+ textPassThrough
546
+ }
547
+ ) as ReactElement
548
+
549
+ if (hasPositionFixed) {
550
+ sectionListComponent = createElement(Portal, null, sectionListComponent)
551
+ }
552
+ return sectionListComponent
553
+ })
554
+
555
+ _SectionList.displayName = 'MpxSectionList'
556
+
557
+ export default _SectionList
@@ -1,9 +1,10 @@
1
1
  import { Text, TextStyle, TextProps } from 'react-native'
2
- import { JSX, createElement, useContext } from 'react'
2
+ import { JSX, createElement, useContext, useRef } from 'react'
3
3
  import useInnerProps from './getInnerListeners'
4
- import { extendObject, getDefaultAllowFontScaling, useTextPassThroughValue, wrapChildren, isStringChildren, transformBoxSizing, splitStyle, isBoxSizingAffectingStyle } from './utils'
5
- import { TextPassThroughContext } from './context'
4
+ import { extendObject, getDefaultAllowFontScaling, wrapChildren, isStringChildren, transformBoxSizing, splitStyle, isBoxSizingAffectingStyle, resolveTextFontSizePercentStyle, resolveTextLineHeightPercentStyle } from './utils'
6
5
  import * as perf from '@mpxjs/perf'
6
+ import { diffAndCloneA } from '@mpxjs/utils'
7
+ import { TextPassThroughContext, TextPassThroughContextValue } from './context'
7
8
 
8
9
  const SimpleText = (props: TextProps): JSX.Element => {
9
10
  let idTotal = -1
@@ -12,30 +13,40 @@ const SimpleText = (props: TextProps): JSX.Element => {
12
13
  // ───── style 阶段 ─────
13
14
  let idStyle = -1
14
15
  if (__mpx_perf_framework__) idStyle = perf.scopeStart('simple-text:render:style')
15
- const inheritedText = useContext(TextPassThroughContext)
16
- const mergedStyle = extendObject({}, inheritedText?.textStyle, props.style)
17
16
  let hasBoxSizingAffectingStyle = false
18
- const { textStyle = {} } = splitStyle(mergedStyle, (key) => {
17
+ const { textStyle } = splitStyle(props.style as TextStyle | undefined, (key) => {
19
18
  if (!hasBoxSizingAffectingStyle && isBoxSizingAffectingStyle(key)) {
20
19
  hasBoxSizingAffectingStyle = true
21
20
  }
22
21
  })
23
- const finalStyle = transformBoxSizing(mergedStyle, hasBoxSizingAffectingStyle)
24
- const mergedProps = extendObject({}, inheritedText?.pendingTextProps, props)
25
- const {
26
- allowFontScaling,
27
- children
28
- } = mergedProps
29
- const isStringOnly = isStringChildren(children)
30
- const childTextStyle: TextStyle | undefined = !isStringOnly && Object.keys(textStyle).length ? textStyle : undefined
31
- const childTextPassThrough = useTextPassThroughValue(
32
- childTextStyle,
33
- undefined,
34
- {
35
- inheritTextProps: false,
36
- disabled: isStringOnly
22
+ const inheritedText = useContext(TextPassThroughContext)
23
+ const resolvedTextStyle = resolveTextFontSizePercentStyle(textStyle, inheritedText?.textStyle)
24
+ // textStyle 仅在子节点非纯字符串时才需要透传给子级;按需计算 isStringChildren
25
+ const childTextStyle = resolvedTextStyle && !isStringChildren(props.children)
26
+ ? resolvedTextStyle
27
+ : undefined
28
+ const textPassThroughRef = useRef<TextPassThroughContextValue | null>(null)
29
+ let textPassThrough: TextPassThroughContextValue | null = null
30
+ if (childTextStyle) {
31
+ const nextTextPassThrough = {
32
+ textStyle: extendObject({}, inheritedText?.textStyle, childTextStyle)
37
33
  }
38
- )
34
+ if (diffAndCloneA(textPassThroughRef.current, nextTextPassThrough).diff) {
35
+ textPassThroughRef.current = nextTextPassThrough
36
+ }
37
+ textPassThrough = textPassThroughRef.current
38
+ }
39
+
40
+ const mergedProps = inheritedText?.pendingTextProps
41
+ ? extendObject({}, inheritedText.pendingTextProps, props)
42
+ : props
43
+ const mergedStyle = inheritedText?.textStyle || props.style
44
+ ? extendObject({}, inheritedText?.textStyle, props.style, resolvedTextStyle) as TextStyle
45
+ : undefined
46
+ if (mergedStyle) {
47
+ resolveTextLineHeightPercentStyle(mergedStyle, inheritedText?.textStyle)
48
+ if (hasBoxSizingAffectingStyle) transformBoxSizing(mergedStyle as Record<string, any>)
49
+ }
39
50
  if (__mpx_perf_framework__) perf.scopeEnd(idStyle)
40
51
 
41
52
  // ───── innerProps 阶段 ─────
@@ -46,8 +57,8 @@ const SimpleText = (props: TextProps): JSX.Element => {
46
57
  {},
47
58
  mergedProps,
48
59
  {
49
- allowFontScaling: allowFontScaling ?? getDefaultAllowFontScaling(),
50
- style: finalStyle
60
+ allowFontScaling: mergedProps.allowFontScaling ?? getDefaultAllowFontScaling(),
61
+ style: mergedStyle
51
62
  }
52
63
  )
53
64
  )
@@ -57,10 +68,10 @@ const SimpleText = (props: TextProps): JSX.Element => {
57
68
  let idCreate = -1
58
69
  if (__mpx_perf_framework__) idCreate = perf.scopeStart('simple-text:render:createElement')
59
70
  const result = createElement(Text, innerProps, wrapChildren(
60
- { children },
71
+ mergedProps.children,
61
72
  {
62
73
  hasVarDec: false,
63
- textPassThrough: childTextPassThrough
74
+ textPassThrough
64
75
  }
65
76
  ))
66
77
  if (__mpx_perf_framework__) perf.scopeEnd(idCreate)