@mpxjs/webpack-plugin 2.9.72 → 2.10.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 (80) hide show
  1. package/lib/file-loader.js +5 -0
  2. package/lib/index.js +21 -3
  3. package/lib/platform/template/wx/component-config/input.js +1 -1
  4. package/lib/platform/template/wx/component-config/textarea.js +1 -1
  5. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  6. package/lib/platform/template/wx/component-config/video.js +28 -1
  7. package/lib/platform/template/wx/index.js +2 -2
  8. package/lib/react/processScript.js +6 -4
  9. package/lib/react/processTemplate.js +5 -3
  10. package/lib/runtime/components/react/KeyboardAvoidingView.tsx +108 -0
  11. package/lib/runtime/components/react/context.ts +18 -2
  12. package/lib/runtime/components/react/dist/KeyboardAvoidingView.jsx +89 -0
  13. package/lib/runtime/components/react/dist/context.js +1 -0
  14. package/lib/runtime/components/react/dist/getInnerListeners.js +1 -2
  15. package/lib/runtime/components/react/dist/mpx-button.jsx +1 -1
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  21. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  22. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  23. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  24. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  25. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +50 -0
  26. package/lib/runtime/components/react/dist/mpx-image.jsx +19 -18
  27. package/lib/runtime/components/react/dist/mpx-input.jsx +48 -19
  28. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +16 -14
  29. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
  30. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +30 -0
  31. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +112 -0
  32. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +41 -0
  33. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  34. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +19 -7
  35. package/lib/runtime/components/react/dist/mpx-swiper.jsx +3 -2
  36. package/lib/runtime/components/react/dist/mpx-textarea.jsx +11 -3
  37. package/lib/runtime/components/react/dist/mpx-video.jsx +248 -0
  38. package/lib/runtime/components/react/dist/mpx-web-view.jsx +149 -50
  39. package/lib/runtime/components/react/dist/useAnimationHooks.js +4 -4
  40. package/lib/runtime/components/react/dist/utils.jsx +8 -3
  41. package/lib/runtime/components/react/getInnerListeners.ts +1 -2
  42. package/lib/runtime/components/react/mpx-button.tsx +1 -1
  43. package/lib/runtime/components/react/mpx-icon/icons/cancel.png +0 -0
  44. package/lib/runtime/components/react/mpx-icon/icons/clear.png +0 -0
  45. package/lib/runtime/components/react/mpx-icon/icons/download.png +0 -0
  46. package/lib/runtime/components/react/mpx-icon/icons/info.png +0 -0
  47. package/lib/runtime/components/react/mpx-icon/icons/search.png +0 -0
  48. package/lib/runtime/components/react/mpx-icon/icons/success.png +0 -0
  49. package/lib/runtime/components/react/mpx-icon/icons/success_no_circle.png +0 -0
  50. package/lib/runtime/components/react/mpx-icon/icons/waiting.png +0 -0
  51. package/lib/runtime/components/react/mpx-icon/icons/warn.png +0 -0
  52. package/lib/runtime/components/react/mpx-icon/index.tsx +111 -0
  53. package/lib/runtime/components/react/mpx-image.tsx +26 -14
  54. package/lib/runtime/components/react/mpx-input.tsx +52 -20
  55. package/lib/runtime/components/react/mpx-movable-view.tsx +19 -16
  56. package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
  57. package/lib/runtime/components/react/mpx-portal/index.tsx +39 -0
  58. package/lib/runtime/components/react/mpx-portal/portal-host.tsx +141 -0
  59. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
  60. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  61. package/lib/runtime/components/react/mpx-scroll-view.tsx +20 -8
  62. package/lib/runtime/components/react/mpx-swiper.tsx +3 -2
  63. package/lib/runtime/components/react/mpx-textarea.tsx +13 -3
  64. package/lib/runtime/components/react/mpx-video.tsx +388 -0
  65. package/lib/runtime/components/react/mpx-web-view.tsx +180 -49
  66. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  67. package/lib/runtime/components/react/types/global.d.ts +8 -0
  68. package/lib/runtime/components/react/useAnimationHooks.ts +4 -4
  69. package/lib/runtime/components/react/utils.tsx +12 -6
  70. package/lib/script-setup-compiler/index.js +6 -2
  71. package/lib/style-compiler/index.js +3 -4
  72. package/lib/style-compiler/strip-conditional-loader.js +127 -0
  73. package/lib/template-compiler/compiler.js +23 -9
  74. package/lib/template-compiler/index.js +4 -4
  75. package/lib/web/processTemplate.js +7 -5
  76. package/lib/wxs/loader.js +2 -2
  77. package/lib/wxs/pre-loader.js +2 -2
  78. package/package.json +7 -5
  79. package/lib/runtime/components/react/dist/mpx-icon.jsx +0 -41
  80. package/lib/runtime/components/react/mpx-icon.tsx +0 -102
@@ -1,13 +1,14 @@
1
- import { forwardRef, JSX, useRef, useContext, useMemo, createElement } from 'react'
2
- import { warn, getFocusedNavigation, isFunction } from '@mpxjs/utils'
3
- import { Portal } from '@ant-design/react-native'
1
+ import { forwardRef, useRef, useContext, useMemo, useState, useCallback, useEffect } from 'react'
2
+ import { warn, isFunction } from '@mpxjs/utils'
3
+ import Portal from './mpx-portal/index'
4
4
  import { getCustomEvent } from './getInnerListeners'
5
5
  import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy'
6
6
  import { WebView } from 'react-native-webview'
7
7
  import useNodesRef, { HandlerRef } from './useNodesRef'
8
- import { getCurrentPage, extendObject } from './utils'
9
- import { WebViewNavigationEvent, WebViewErrorEvent, WebViewMessageEvent, WebViewNavigation } from 'react-native-webview/lib/WebViewTypes'
8
+ import { getCurrentPage, useNavigation } from './utils'
9
+ import { WebViewHttpErrorEvent, WebViewEvent, WebViewMessageEvent, WebViewNavigation, WebViewProgressEvent } from 'react-native-webview/lib/WebViewTypes'
10
10
  import { RouteContext } from './context'
11
+ import { StyleSheet, View, Text } from 'react-native'
11
12
 
12
13
  type OnMessageCallbackEvent = {
13
14
  detail: {
@@ -28,6 +29,7 @@ interface WebViewProps {
28
29
  binderror?: (event: CommonCallbackEvent) => void
29
30
  [x: string]: any
30
31
  }
32
+ type Listener = (type: string, callback: (e: Event) => void) => () => void
31
33
 
32
34
  interface PayloadData {
33
35
  [x: string]: any
@@ -40,15 +42,66 @@ type MessageData = {
40
42
  callbackId?: number
41
43
  }
42
44
 
45
+ type LanguageCode = 'zh-CN' | 'en-US'; // 支持的语言代码
46
+
47
+ interface ErrorText {
48
+ text: string;
49
+ button: string;
50
+ }
51
+
52
+ type ErrorTextMap = Record<LanguageCode, ErrorText>
53
+
54
+ const styles = StyleSheet.create({
55
+ loadErrorContext: {
56
+ display: 'flex',
57
+ alignItems: 'center'
58
+ },
59
+ loadErrorText: {
60
+ fontSize: 12,
61
+ color: '#666666',
62
+ paddingTop: '40%',
63
+ paddingBottom: 20,
64
+ paddingLeft: '10%',
65
+ paddingRight: '10%',
66
+ textAlign: 'center'
67
+ },
68
+ loadErrorButton: {
69
+ color: '#666666',
70
+ textAlign: 'center',
71
+ padding: 10,
72
+ borderColor: '#666666',
73
+ borderStyle: 'solid',
74
+ borderWidth: StyleSheet.hairlineWidth,
75
+ borderRadius: 10
76
+ }
77
+ })
78
+
43
79
  const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((props, ref): JSX.Element | null => {
44
80
  const { src, bindmessage, bindload, binderror } = props
45
81
  const mpx = global.__mpx
82
+ const errorText: ErrorTextMap = {
83
+ 'zh-CN': {
84
+ text: '网络不可用,请检查网络设置',
85
+ button: '重新加载'
86
+ },
87
+ 'en-US': {
88
+ text: 'The network is not available. Please check the network settings',
89
+ button: 'Reload'
90
+ }
91
+ }
92
+ const currentErrorText = errorText[(mpx.i18n.locale as LanguageCode) || 'zh-CN']
93
+
46
94
  if (props.style) {
47
95
  warn('The web-view component does not support the style prop.')
48
96
  }
49
- const pageId = useContext(RouteContext)
97
+ const { pageId } = useContext(RouteContext) || {}
98
+ const [pageLoadErr, setPageLoadErr] = useState<boolean>(false)
50
99
  const currentPage = useMemo(() => getCurrentPage(pageId), [pageId])
51
100
  const webViewRef = useRef<WebView>(null)
101
+ const fristLoaded = useRef<boolean>(false)
102
+ const isLoadError = useRef<boolean>(false)
103
+ const statusCode = useRef<string|number>('')
104
+ const [isLoaded, setIsLoaded] = useState<boolean>(true)
52
105
  const defaultWebViewStyle = {
53
106
  position: 'absolute' as 'absolute' | 'relative' | 'static',
54
107
  left: 0 as number,
@@ -56,6 +109,27 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
56
109
  top: 0 as number,
57
110
  bottom: 0 as number
58
111
  }
112
+ const canGoBack = useRef<boolean>(false)
113
+ const isNavigateBack = useRef<boolean>(false)
114
+
115
+ const beforeRemoveHandle = (e: Event) => {
116
+ if (canGoBack.current && !isNavigateBack.current) {
117
+ webViewRef.current?.goBack()
118
+ e.preventDefault()
119
+ }
120
+ isNavigateBack.current = false
121
+ }
122
+
123
+ const navigation = useNavigation()
124
+
125
+ useEffect(() => {
126
+ const beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle)
127
+ return () => {
128
+ if (isFunction(beforeRemoveSubscription)) {
129
+ beforeRemoveSubscription()
130
+ }
131
+ }
132
+ }, [])
59
133
 
60
134
  useNodesRef<WebView, WebViewProps>(props, ref, webViewRef, {
61
135
  style: defaultWebViewStyle
@@ -65,25 +139,11 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
65
139
  return null
66
140
  }
67
141
 
68
- const _load = function (res: WebViewNavigationEvent) {
69
- const result = {
70
- type: 'load',
71
- timeStamp: res.timeStamp,
72
- detail: {
73
- src: res.nativeEvent?.url
74
- }
75
- }
76
- bindload(result)
77
- }
78
- const _error = function (res: WebViewErrorEvent) {
79
- const result = {
80
- type: 'error',
81
- timeStamp: res.timeStamp,
82
- detail: {
83
- src: ''
84
- }
142
+ const _reload = function () {
143
+ if (__mpx_mode__ === 'android') {
144
+ fristLoaded.current = false // 安卓需要重新设置
85
145
  }
86
- binderror(result)
146
+ setPageLoadErr(false)
87
147
  }
88
148
  const injectedJavaScript = `
89
149
  if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
@@ -109,12 +169,27 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
109
169
  }
110
170
  });
111
171
  }
172
+ true;
112
173
  `
174
+
175
+ const sendMessage = function (params: string) {
176
+ return `
177
+ window.mpxWebviewMessageCallback && window.mpxWebviewMessageCallback(${params})
178
+ true;
179
+ `
180
+ }
113
181
  const _changeUrl = function (navState: WebViewNavigation) {
114
182
  if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑
183
+ canGoBack.current = navState.canGoBack
115
184
  currentPage.__webViewUrl = navState.url
116
185
  }
117
186
  }
187
+
188
+ const _onLoadProgress = function (event: WebViewProgressEvent) {
189
+ if (__mpx_mode__ === 'android') {
190
+ canGoBack.current = event.nativeEvent.canGoBack
191
+ }
192
+ }
118
193
  const _message = function (res: WebViewMessageEvent) {
119
194
  let data: MessageData = {}
120
195
  let asyncCallback
@@ -134,7 +209,6 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
134
209
  { // case下不允许直接声明,包个块解决该问题
135
210
  const title = postData._documentTitle
136
211
  if (title) {
137
- const navigation = getFocusedNavigation()
138
212
  navigation && navigation.setOptions({ title })
139
213
  }
140
214
  }
@@ -153,6 +227,7 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
153
227
  asyncCallback = navObj.navigateTo(...params)
154
228
  break
155
229
  case 'navigateBack':
230
+ isNavigateBack.current = true
156
231
  asyncCallback = navObj.navigateBack(...params)
157
232
  break
158
233
  case 'redirectTo':
@@ -181,47 +256,103 @@ const _WebView = forwardRef<HandlerRef<WebView, WebViewProps>, WebViewProps>((pr
181
256
 
182
257
  asyncCallback && asyncCallback.then((res: any) => {
183
258
  if (webViewRef.current?.postMessage) {
184
- const test = JSON.stringify({
259
+ const result = JSON.stringify({
185
260
  type,
186
261
  callbackId: data.callbackId,
187
262
  result: res
188
263
  })
189
- webViewRef.current.postMessage(test)
264
+ webViewRef.current.injectJavaScript(sendMessage(result))
190
265
  }
191
266
  }).catch((error: any) => {
192
267
  if (webViewRef.current?.postMessage) {
193
- const test = JSON.stringify({
268
+ const result = JSON.stringify({
194
269
  type,
195
270
  callbackId: data.callbackId,
196
271
  error
197
272
  })
198
- webViewRef.current.postMessage(test)
273
+ webViewRef.current.injectJavaScript(sendMessage(result))
199
274
  }
200
275
  })
201
276
  }
202
- const events = {}
203
-
204
- if (bindload) {
205
- extendObject(events, {
206
- onLoad: _load
207
- })
277
+ const onLoadEndHandle = function (res: WebViewEvent) {
278
+ fristLoaded.current = true
279
+ setIsLoaded(true)
280
+ const src = res.nativeEvent?.url
281
+ if (isLoadError.current) {
282
+ isLoadError.current = false
283
+ isNavigateBack.current = false
284
+ const result = {
285
+ type: 'error',
286
+ timeStamp: res.timeStamp,
287
+ detail: {
288
+ src,
289
+ statusCode: statusCode.current
290
+ }
291
+ }
292
+ binderror && binderror(result)
293
+ } else {
294
+ const result = {
295
+ type: 'load',
296
+ timeStamp: res.timeStamp,
297
+ detail: {
298
+ src
299
+ }
300
+ }
301
+ bindload?.(result)
302
+ }
208
303
  }
209
- if (binderror) {
210
- extendObject(events, {
211
- onError: _error
212
- })
304
+ const onLoadEnd = function (res: WebViewEvent) {
305
+ if (__mpx_mode__ === 'android') {
306
+ setTimeout(() => {
307
+ onLoadEndHandle(res)
308
+ }, 0)
309
+ } else {
310
+ onLoadEndHandle(res)
311
+ }
312
+ }
313
+ const onHttpError = function (res: WebViewHttpErrorEvent) {
314
+ isLoadError.current = true
315
+ statusCode.current = res.nativeEvent?.statusCode
316
+ }
317
+ const onError = function () {
318
+ statusCode.current = ''
319
+ isLoadError.current = true
320
+ if (!fristLoaded.current) {
321
+ setPageLoadErr(true)
322
+ }
323
+ }
324
+ const onLoadStart = function () {
325
+ if (!fristLoaded.current) {
326
+ setIsLoaded(false)
327
+ }
213
328
  }
214
- extendObject(events, {
215
- onMessage: _message
216
- })
217
329
 
218
- return createElement(Portal, null, createElement(WebView, extendObject({
219
- style: defaultWebViewStyle,
220
- source: { uri: src },
221
- ref: webViewRef,
222
- javaScriptEnabled: true,
223
- onNavigationStateChange: _changeUrl
224
- }, events)))
330
+ return (
331
+ <Portal key={pageLoadErr ? 'error' : 'webview'}>
332
+ {pageLoadErr
333
+ ? (
334
+ <View style={[styles.loadErrorContext, defaultWebViewStyle]}>
335
+ <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
336
+ <View style={styles.loadErrorButton} onTouchEnd={_reload}><Text style={{ fontSize: 12, color: '#666666' }}>{currentErrorText.button}</Text></View>
337
+ </View>
338
+ )
339
+ : (<WebView
340
+ style={ defaultWebViewStyle }
341
+ source={{ uri: src }}
342
+ ref={webViewRef}
343
+ javaScriptEnabled={true}
344
+ onNavigationStateChange={_changeUrl}
345
+ onMessage={_message}
346
+ injectedJavaScript={injectedJavaScript}
347
+ onLoadProgress={_onLoadProgress}
348
+ onLoadEnd={onLoadEnd}
349
+ onHttpError={onHttpError}
350
+ onError={onError}
351
+ onLoadStart={onLoadStart}
352
+ allowsBackForwardNavigationGestures={isLoaded}
353
+ ></WebView>)}
354
+ </Portal>
355
+ )
225
356
  })
226
357
 
227
358
  _WebView.displayName = 'MpxWebview'
@@ -13,7 +13,7 @@ type RemoveProps = string[];
13
13
 
14
14
  type NativeTouchEvent = NativeSyntheticEvent<NativeEvent>
15
15
 
16
- type Navigation = Record<string, any>
16
+ type Navigation = Record<string, any> | undefined
17
17
 
18
18
  interface NativeEvent {
19
19
  timestamp: number;
@@ -1,3 +1,4 @@
1
+ declare let __mpx_mode__: 'wx' | 'ali' | 'swan' | 'qq' | 'tt' | 'web' | 'dd' | 'qa' | 'jd' | 'android' | 'ios'
1
2
  declare module '@mpxjs/utils' {
2
3
  export function isEmptyObject (obj: Object): boolean
3
4
  export function isFunction (fn: unknown): boolean
@@ -20,6 +21,11 @@ declare module '@mpxjs/utils' {
20
21
  left: number
21
22
  right: number
22
23
  },
24
+ setOptions: (params: Record<string, any>) => void,
25
+ addListener: (eventName: string, callback: (e: Event) => void) => void
26
+ removeListener: (eventName: string, callback: (e: Event) => void) => void
27
+ dispatch: (eventName: string) => void
28
+ pageId: number
23
29
  layout: {
24
30
  x: number
25
31
  y: number
@@ -37,3 +43,5 @@ declare let global: {
37
43
  declare module '@react-navigation/native' {
38
44
  export function useNavigation (): Record<string, any>
39
45
  }
46
+
47
+ declare module '*.png'
@@ -40,10 +40,10 @@ export type AnimationProp = {
40
40
  // 微信 timingFunction 和 RN Easing 对应关系
41
41
  const EasingKey = {
42
42
  linear: Easing.linear,
43
- ease: Easing.ease,
44
- 'ease-in': Easing.in(Easing.ease),
45
- 'ease-in-out': Easing.inOut(Easing.ease),
46
- 'ease-out': Easing.out(Easing.ease)
43
+ ease: Easing.inOut(Easing.ease),
44
+ 'ease-in': Easing.in(Easing.poly(3)),
45
+ 'ease-in-out': Easing.inOut(Easing.poly(3)),
46
+ 'ease-out': Easing.out(Easing.poly(3))
47
47
  // 'step-start': '',
48
48
  // 'step-end': ''
49
49
  }
@@ -1,10 +1,9 @@
1
1
  import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react'
2
2
  import { LayoutChangeEvent, TextStyle, ImageProps, Image, Platform } from 'react-native'
3
3
  import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
4
- import { VarContext, ScrollViewContext } from './context'
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
- import { useNavigation } from '@react-navigation/native'
8
7
  import FastImage, { FastImageProps } from '@d11/react-native-fast-image'
9
8
  import type { AnyFunc, ExtendedFunctionComponent, ExtendedViewStyle } from './types/common'
10
9
  import { runOnJS } from 'react-native-reanimated'
@@ -40,11 +39,16 @@ const safeAreaInsetMap: Record<string, 'top' | 'right' | 'bottom' | 'left'> = {
40
39
  'safe-area-inset-left': 'left'
41
40
  }
42
41
 
43
- function getSafeAreaInset (name: string, navigation: Record<string, any>) {
42
+ function getSafeAreaInset (name: string, navigation: Record<string, any> | undefined) {
44
43
  const insets = extendObject({}, initialWindowMetrics?.insets, navigation?.insets)
45
44
  return insets[safeAreaInsetMap[name]]
46
45
  }
47
46
 
47
+ export function useNavigation (): Record<string, any> | undefined {
48
+ const { navigation } = useContext(RouteContext) || {}
49
+ return navigation
50
+ }
51
+
48
52
  export function omit<T, K extends string> (obj: T, fields: K[]): Omit<T, K> {
49
53
  const shallowCopy: any = extendObject({}, obj)
50
54
  for (let i = 0; i < fields.length; i += 1) {
@@ -239,7 +243,7 @@ function transformVar (styleObj: Record<string, any>, varKeyPaths: Array<Array<s
239
243
  })
240
244
  }
241
245
 
242
- function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<string>>, navigation: Record<string, any>) {
246
+ function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<string>>, navigation: Record<string, any> | undefined) {
243
247
  envKeyPaths.forEach((envKeyPath) => {
244
248
  setStyle(styleObj, envKeyPath, ({ target, key, value }) => {
245
249
  const parsed = parseFunc(value, 'env')
@@ -518,6 +522,7 @@ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout
518
522
  const hasLayoutRef = useRef(false)
519
523
  const layoutStyle = useMemo(() => { return !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {} }, [hasLayoutRef.current])
520
524
  const layoutProps: Record<string, any> = {}
525
+ const navigation = useNavigation()
521
526
  const enableOffset = props['enable-offset']
522
527
  if (hasSelfPercent || onLayout || enableOffset) {
523
528
  layoutProps.onLayout = (e: LayoutChangeEvent) => {
@@ -529,7 +534,8 @@ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout
529
534
  }
530
535
  if (enableOffset) {
531
536
  nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
532
- layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
537
+ const { y: navigationY = 0 } = navigation?.layout || {}
538
+ layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft, offsetTop: offsetTop - navigationY }
533
539
  })
534
540
  }
535
541
  onLayout && onLayout(e)
@@ -628,7 +634,7 @@ export function flatGesture (gestures: Array<GestureHandler> = []) {
628
634
 
629
635
  export const extendObject = Object.assign
630
636
 
631
- export function getCurrentPage (pageId: number | null) {
637
+ export function getCurrentPage (pageId: number | null | undefined) {
632
638
  if (!global.getCurrentPages) return
633
639
  const pages = global.getCurrentPages()
634
640
  return pages.find((page: any) => isFunction(page.getPageId) && page.getPageId() === pageId)
@@ -380,7 +380,9 @@ function compileScriptSetup (
380
380
 
381
381
  if (node.type === 'ImportDeclaration') {
382
382
  // import declarations are moved to top
383
- _s.move(start, end, 0)
383
+ if (start !== 0) {
384
+ _s.move(start, end, 0)
385
+ }
384
386
  // dedupe imports
385
387
  let removed = 0
386
388
  const removeSpecifier = (i) => {
@@ -533,7 +535,9 @@ function compileScriptSetup (
533
535
  (node.type === 'VariableDeclaration' && node.declare)
534
536
  ) {
535
537
  recordType(node, declaredTypes)
536
- _s.move(node.start, node.end + 1, 0)
538
+ if (node.start !== 0) {
539
+ _s.move(node.start, node.end + 1, 0)
540
+ }
537
541
  }
538
542
  }
539
543
 
@@ -4,7 +4,6 @@ const loadPostcssConfig = require('./load-postcss-config')
4
4
  const { MPX_ROOT_VIEW, MPX_DISABLE_EXTRACTOR_CACHE } = require('../utils/const')
5
5
  const rpx = require('./plugins/rpx')
6
6
  const vw = require('./plugins/vw')
7
- const pluginCondStrip = require('./plugins/conditional-strip')
8
7
  const scopeId = require('./plugins/scope-id')
9
8
  const transSpecial = require('./plugins/trans-special')
10
9
  const cssArrayList = require('./plugins/css-array-list')
@@ -58,9 +57,9 @@ module.exports = function (css, map) {
58
57
  plugins.push(transSpecial({ id }))
59
58
  }
60
59
 
61
- plugins.push(pluginCondStrip({
62
- defs
63
- }))
60
+ // plugins.push(pluginCondStrip({
61
+ // defs
62
+ // }))
64
63
 
65
64
  for (const item of transRpxRules) {
66
65
  const {
@@ -0,0 +1,127 @@
1
+ class Node {
2
+ constructor (type, condition = null) {
3
+ this.type = type // 'If', 'ElseIf', 'Else' 或 'Text'
4
+ this.condition = condition // If 或 Elif 的条件
5
+ this.children = []
6
+ this.value = ''
7
+ }
8
+ }
9
+
10
+ // 提取 css string 为 token
11
+ function tokenize (cssString) {
12
+ const regex = /\/\*\s*@mpx-(if|elif|else|endif)(?:\s*\((.*?)\))?\s*\*\//g
13
+ const tokens = []
14
+ let lastIndex = 0
15
+ let match
16
+
17
+ while ((match = regex.exec(cssString)) !== null) {
18
+ // 如果 token 前有普通文本,生成文本 token
19
+ if (match.index > lastIndex) {
20
+ const text = cssString.substring(lastIndex, match.index)
21
+ tokens.push({ type: 'text', content: text })
22
+ }
23
+ // match[1] 为关键字:if, elif, else, endif
24
+ // match[2] 为条件(如果存在)
25
+ tokens.push({
26
+ type: match[1], // 'if'、'elif'、'else' 或 'endif'
27
+ condition: match[2] ? match[2].trim() : null
28
+ })
29
+ lastIndex = regex.lastIndex
30
+ }
31
+ // 处理结尾剩余的文本
32
+ if (lastIndex < cssString.length) {
33
+ const text = cssString.substring(lastIndex)
34
+ tokens.push({ type: 'text', content: text })
35
+ }
36
+ return tokens
37
+ }
38
+
39
+ // parse:将生成的 token 数组构造成嵌套的 AST
40
+ function parse (cssString) {
41
+ const tokens = tokenize(cssString)
42
+ const ast = []
43
+ const nodeStack = []
44
+ let currentChildren = ast
45
+ tokens.forEach(token => {
46
+ if (token.type === 'text') {
47
+ const node = new Node('Text')
48
+ node.value = token.content
49
+ currentChildren.push(node)
50
+ } else if (token.type === 'if') {
51
+ const node = new Node('If', token.condition)
52
+ currentChildren.push(node)
53
+ nodeStack.push(currentChildren)
54
+ currentChildren = node.children
55
+ } else if (token.type === 'elif') {
56
+ if (nodeStack.length === 0) {
57
+ throw new Error('elif without a preceding if')
58
+ }
59
+ currentChildren = nodeStack[nodeStack.length - 1]
60
+ const node = new Node('ElseIf', token.condition)
61
+ currentChildren.push(node)
62
+ currentChildren = node.children
63
+ } else if (token.type === 'else') {
64
+ if (nodeStack.length === 0) {
65
+ throw new Error('else without a preceding if')
66
+ }
67
+ currentChildren = nodeStack[nodeStack.length - 1]
68
+ const node = new Node('Else')
69
+ currentChildren.push(node)
70
+ currentChildren = node.children
71
+ } else if (token.type === 'endif') {
72
+ if (nodeStack.length > 0) {
73
+ currentChildren = nodeStack.pop()
74
+ }
75
+ }
76
+ })
77
+ return ast
78
+ }
79
+
80
+ function evaluateCondition (condition, defs) {
81
+ try {
82
+ const keys = Object.keys(defs)
83
+ const values = keys.map(key => defs[key])
84
+ /* eslint-disable no-new-func */
85
+ const func = new Function(...keys, `return (${condition});`)
86
+ return func(...values)
87
+ } catch (e) {
88
+ console.error(`Error evaluating condition: ${condition}`, e)
89
+ return false
90
+ }
91
+ }
92
+
93
+ function traverseAndEvaluate (ast, defs) {
94
+ let output = ''
95
+ let batchedIf = false
96
+ function traverse (nodes) {
97
+ for (const node of nodes) {
98
+ if (node.type === 'Text') {
99
+ output += node.value
100
+ } else if (node.type === 'If') {
101
+ // 直接判断 If 节点
102
+ batchedIf = false
103
+ if (evaluateCondition(node.condition, defs)) {
104
+ traverse(node.children)
105
+ batchedIf = true
106
+ }
107
+ } else if (node.type === 'ElseIf' && !batchedIf) {
108
+ if (evaluateCondition(node.condition, defs)) {
109
+ traverse(node.children)
110
+ batchedIf = true
111
+ }
112
+ } else if (node.type === 'Else' && !batchedIf) {
113
+ traverse(node.children)
114
+ }
115
+ }
116
+ }
117
+ traverse(ast)
118
+ return output
119
+ }
120
+
121
+ module.exports = function (css) {
122
+ this.cacheable()
123
+ const mpx = this.getMpx()
124
+ const defs = mpx.defs
125
+ const ast = parse(css)
126
+ return traverseAndEvaluate(ast, defs)
127
+ }