@mpxjs/webpack-plugin 2.9.62 → 2.9.65

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 (95) hide show
  1. package/lib/config.js +38 -10
  2. package/lib/index.js +1 -3
  3. package/lib/platform/style/wx/index.js +115 -66
  4. package/lib/platform/template/wx/index.js +12 -8
  5. package/lib/react/processStyles.js +1 -0
  6. package/lib/react/processTemplate.js +2 -3
  7. package/lib/react/style-helper.js +9 -7
  8. package/lib/runtime/components/react/context.ts +9 -7
  9. package/lib/runtime/components/react/dist/context.js +1 -0
  10. package/lib/runtime/components/react/dist/getInnerListeners.js +12 -1
  11. package/lib/runtime/components/react/dist/mpx-button.jsx +53 -74
  12. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +20 -18
  13. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +30 -42
  14. package/lib/runtime/components/react/dist/mpx-form.jsx +18 -15
  15. package/lib/runtime/components/react/dist/mpx-icon.jsx +15 -17
  16. package/lib/runtime/components/react/dist/mpx-image/index.jsx +36 -34
  17. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +3 -1
  18. package/lib/runtime/components/react/dist/mpx-input.jsx +36 -31
  19. package/lib/runtime/components/react/dist/mpx-label.jsx +30 -37
  20. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +15 -19
  21. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +10 -9
  22. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +2 -1
  23. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -10
  24. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +9 -5
  25. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +13 -8
  26. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +3 -2
  27. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +22 -20
  28. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +103 -10
  29. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +149 -54
  30. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +20 -18
  31. package/lib/runtime/components/react/dist/mpx-radio.jsx +29 -43
  32. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +8 -4
  33. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +36 -27
  34. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +141 -75
  35. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +16 -7
  36. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +20 -11
  37. package/lib/runtime/components/react/dist/mpx-switch.jsx +18 -14
  38. package/lib/runtime/components/react/dist/mpx-text.jsx +20 -35
  39. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  40. package/lib/runtime/components/react/dist/mpx-view.jsx +296 -210
  41. package/lib/runtime/components/react/dist/mpx-web-view.jsx +11 -7
  42. package/lib/runtime/components/react/dist/parser.js +218 -0
  43. package/lib/runtime/components/react/dist/useNodesRef.js +1 -5
  44. package/lib/runtime/components/react/dist/utils.jsx +445 -0
  45. package/lib/runtime/components/react/getInnerListeners.ts +18 -8
  46. package/lib/runtime/components/react/mpx-button.tsx +83 -91
  47. package/lib/runtime/components/react/mpx-checkbox-group.tsx +50 -43
  48. package/lib/runtime/components/react/mpx-checkbox.tsx +56 -64
  49. package/lib/runtime/components/react/mpx-form.tsx +51 -22
  50. package/lib/runtime/components/react/mpx-icon.tsx +31 -27
  51. package/lib/runtime/components/react/mpx-image/index.tsx +54 -47
  52. package/lib/runtime/components/react/mpx-image/svg.tsx +5 -3
  53. package/lib/runtime/components/react/mpx-input.tsx +59 -38
  54. package/lib/runtime/components/react/mpx-label.tsx +55 -59
  55. package/lib/runtime/components/react/mpx-movable-area.tsx +40 -25
  56. package/lib/runtime/components/react/mpx-movable-view.tsx +29 -29
  57. package/lib/runtime/components/react/mpx-navigator.tsx +2 -2
  58. package/lib/runtime/components/react/mpx-picker/date.tsx +4 -4
  59. package/lib/runtime/components/react/mpx-picker/index.tsx +12 -11
  60. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +17 -13
  61. package/lib/runtime/components/react/mpx-picker/region.tsx +23 -19
  62. package/lib/runtime/components/react/mpx-picker/selector.tsx +7 -7
  63. package/lib/runtime/components/react/mpx-picker/time.tsx +29 -31
  64. package/lib/runtime/components/react/mpx-picker/type.ts +1 -1
  65. package/lib/runtime/components/react/mpx-picker-view-column.tsx +149 -20
  66. package/lib/runtime/components/react/mpx-picker-view.tsx +180 -63
  67. package/lib/runtime/components/react/mpx-radio-group.tsx +51 -47
  68. package/lib/runtime/components/react/mpx-radio.tsx +57 -72
  69. package/lib/runtime/components/react/mpx-root-portal.tsx +10 -8
  70. package/lib/runtime/components/react/mpx-scroll-view.tsx +136 -104
  71. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +175 -96
  72. package/lib/runtime/components/react/mpx-swiper/index.tsx +21 -9
  73. package/lib/runtime/components/react/mpx-swiper/type.ts +16 -5
  74. package/lib/runtime/components/react/mpx-swiper-item.tsx +48 -14
  75. package/lib/runtime/components/react/mpx-switch.tsx +46 -24
  76. package/lib/runtime/components/react/mpx-text.tsx +38 -45
  77. package/lib/runtime/components/react/mpx-textarea.tsx +1 -1
  78. package/lib/runtime/components/react/mpx-view.tsx +401 -241
  79. package/lib/runtime/components/react/mpx-web-view.tsx +22 -22
  80. package/lib/runtime/components/react/parser.ts +245 -0
  81. package/lib/runtime/components/react/types/common.ts +4 -4
  82. package/lib/runtime/components/react/types/global.d.ts +24 -2
  83. package/lib/runtime/components/react/useNodesRef.ts +1 -7
  84. package/lib/runtime/components/react/utils.tsx +524 -0
  85. package/lib/runtime/components/web/mpx-scroll-view.vue +25 -5
  86. package/lib/style-compiler/index.js +5 -4
  87. package/lib/template-compiler/compiler.js +133 -161
  88. package/lib/template-compiler/gen-node-react.js +1 -3
  89. package/lib/utils/const.js +2 -1
  90. package/lib/web/processStyles.js +2 -1
  91. package/lib/web/processTemplate.js +2 -3
  92. package/lib/wxml/loader.js +1 -1
  93. package/package.json +7 -4
  94. package/lib/runtime/components/react/dist/utils.js +0 -148
  95. package/lib/runtime/components/react/utils.ts +0 -170
@@ -0,0 +1,524 @@
1
+ import { useEffect, useRef, ReactNode, ReactElement, FunctionComponent, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react'
2
+ import { LayoutChangeEvent, TextStyle } from 'react-native'
3
+ import { isObject, hasOwn, diffAndCloneA, error, warn, getFocusedNavigation } from '@mpxjs/utils'
4
+ import { VarContext } from './context'
5
+ import { ExpressionParser, parseFunc, ReplaceSource } from './parser'
6
+ import { initialWindowMetrics } from 'react-native-safe-area-context'
7
+
8
+ export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/
9
+ export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/
10
+ export const URL_REGEX = /^\s*url\(["']?(.*?)["']?\)\s*$/
11
+ export const BACKGROUND_REGEX = /^background(Image|Size|Repeat|Position)$/
12
+ export const TEXT_PROPS_REGEX = /ellipsizeMode|numberOfLines/
13
+ export const DEFAULT_FONT_SIZE = 16
14
+ export const DEFAULT_UNLAY_STYLE = {
15
+ opacity: 0
16
+ }
17
+
18
+ const varDecRegExp = /^--.*/
19
+ const varUseRegExp = /var\(/
20
+ const calcUseRegExp = /calc\(/
21
+ const envUseRegExp = /env\(/
22
+
23
+ const safeAreaInsetMap: Record<string, 'top' | 'right' | 'bottom' | 'left'> = {
24
+ 'safe-area-inset-top': 'top',
25
+ 'safe-area-inset-right': 'right',
26
+ 'safe-area-inset-bottom': 'bottom',
27
+ 'safe-area-inset-left': 'left'
28
+ }
29
+
30
+ function getSafeAreaInset (name: string) {
31
+ const navigation = getFocusedNavigation()
32
+ const insets = {
33
+ ...initialWindowMetrics?.insets,
34
+ ...navigation?.insets
35
+ }
36
+ return insets[safeAreaInsetMap[name]]
37
+ }
38
+
39
+ export function omit<T, K extends string> (obj: T, fields: K[]): Omit<T, K> {
40
+ const shallowCopy: any = Object.assign({}, obj)
41
+ for (let i = 0; i < fields.length; i += 1) {
42
+ const key = fields[i]
43
+ delete shallowCopy[key]
44
+ }
45
+ return shallowCopy
46
+ }
47
+
48
+ /**
49
+ * 用法等同于 useEffect,但是会忽略首次执行,只在依赖更新时执行
50
+ */
51
+ export const useUpdateEffect = (effect: any, deps: any) => {
52
+ const isMounted = useRef(false)
53
+
54
+ // for react-refresh
55
+ useEffect(() => {
56
+ return () => {
57
+ isMounted.current = false
58
+ }
59
+ }, [])
60
+
61
+ useEffect(() => {
62
+ if (!isMounted.current) {
63
+ isMounted.current = true
64
+ } else {
65
+ return effect()
66
+ }
67
+ }, deps)
68
+ }
69
+
70
+ /**
71
+ * 解析行内样式
72
+ * @param inlineStyle
73
+ * @returns
74
+ */
75
+ export const parseInlineStyle = (inlineStyle = ''): Record<string, string> => {
76
+ return inlineStyle.split(';').reduce((styleObj, style) => {
77
+ const [k, v, ...rest] = style.split(':')
78
+ if (rest.length || !v || !k) return styleObj
79
+ const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase())
80
+ return Object.assign(styleObj, { [key]: v.trim() })
81
+ }, {})
82
+ }
83
+
84
+ export const parseUrl = (cssUrl = '') => {
85
+ if (!cssUrl) return
86
+ const match = cssUrl.match(URL_REGEX)
87
+ return match?.[1]
88
+ }
89
+
90
+ export const getRestProps = (transferProps: any = {}, originProps: any = {}, deletePropsKey: any = []) => {
91
+ return {
92
+ ...transferProps,
93
+ ...omit(originProps, deletePropsKey)
94
+ }
95
+ }
96
+
97
+ export function isText (ele: ReactNode): ele is ReactElement {
98
+ if (isValidElement(ele)) {
99
+ const displayName = (ele.type as FunctionComponent)?.displayName
100
+ return displayName === 'mpx-text' || displayName === 'Text'
101
+ }
102
+ return false
103
+ }
104
+
105
+ export function isEmbedded (ele: ReactNode): ele is ReactElement {
106
+ if (isValidElement(ele)) {
107
+ const displayName = (ele.type as FunctionComponent)?.displayName || ''
108
+ return ['mpx-checkbox', 'mpx-radio', 'mpx-switch'].includes(displayName)
109
+ }
110
+ return false
111
+ }
112
+
113
+ export function every (children: ReactNode, callback: (children: ReactNode) => boolean) {
114
+ const childrenArray = Array.isArray(children) ? children : [children]
115
+ return childrenArray.every((child) => callback(child))
116
+ }
117
+
118
+ type GroupData<T> = Record<string, Partial<T>>
119
+ export function groupBy<T extends Record<string, any>> (
120
+ obj: T,
121
+ callback: (key: string, val: T[keyof T]) => string,
122
+ group: GroupData<T> = {}
123
+ ): GroupData<T> {
124
+ Object.entries(obj).forEach(([key, val]) => {
125
+ const groupKey = callback(key, val)
126
+ group[groupKey] = group[groupKey] || {}
127
+ group[groupKey][key as keyof T] = val
128
+ })
129
+ return group
130
+ }
131
+
132
+ export function splitStyle<T extends Record<string, any>> (styleObj: T): {
133
+ textStyle?: Partial<T>
134
+ backgroundStyle?: Partial<T>
135
+ innerStyle?: Partial<T>
136
+ } {
137
+ return groupBy(styleObj, (key) => {
138
+ if (TEXT_STYLE_REGEX.test(key)) {
139
+ return 'textStyle'
140
+ } else if (BACKGROUND_REGEX.test(key)) {
141
+ return 'backgroundStyle'
142
+ } else {
143
+ return 'innerStyle'
144
+ }
145
+ }) as {
146
+ textStyle: Partial<T>
147
+ backgroundStyle: Partial<T>
148
+ innerStyle: Partial<T>
149
+ }
150
+ }
151
+
152
+ const selfPercentRule: Record<string, 'height' | 'width'> = {
153
+ translateX: 'width',
154
+ translateY: 'height',
155
+ borderTopLeftRadius: 'width',
156
+ borderBottomLeftRadius: 'width',
157
+ borderBottomRightRadius: 'width',
158
+ borderTopRightRadius: 'width',
159
+ borderRadius: 'width'
160
+ }
161
+
162
+ const parentHeightPercentRule: Record<string, boolean> = {
163
+ height: true,
164
+ top: true,
165
+ bottom: true
166
+ }
167
+
168
+ interface PercentConfig {
169
+ fontSize?: number | string
170
+ width?: number
171
+ height?: number
172
+ parentFontSize?: number
173
+ parentWidth?: number
174
+ parentHeight?: number
175
+ }
176
+
177
+ function resolvePercent (value: string | number | undefined, key: string, percentConfig: PercentConfig): string | number | undefined {
178
+ if (!(typeof value === 'string' && PERCENT_REGEX.test(value))) return value
179
+ let base
180
+ let reason
181
+ if (key === 'fontSize') {
182
+ base = percentConfig.parentFontSize
183
+ reason = 'parent-font-size'
184
+ } else if (key === 'lineHeight') {
185
+ base = resolvePercent(percentConfig.fontSize, 'fontSize', percentConfig)
186
+ reason = 'font-size'
187
+ } else if (selfPercentRule[key]) {
188
+ base = percentConfig[selfPercentRule[key]]
189
+ reason = selfPercentRule[key]
190
+ } else if (parentHeightPercentRule[key]) {
191
+ base = percentConfig.parentHeight
192
+ reason = 'parent-height'
193
+ } else {
194
+ base = percentConfig.parentWidth
195
+ reason = 'parent-width'
196
+ }
197
+ if (typeof base !== 'number') {
198
+ error(`[${key}] can not contain % unit unless you set [${reason}] with a number for the percent calculation.`)
199
+ return value
200
+ } else {
201
+ return parseFloat(value) / 100 * base
202
+ }
203
+ }
204
+
205
+ function transformPercent (styleObj: Record<string, any>, percentKeyPaths: Array<Array<string>>, percentConfig: PercentConfig) {
206
+ percentKeyPaths.forEach((percentKeyPath) => {
207
+ setStyle(styleObj, percentKeyPath, ({ target, key, value }) => {
208
+ target[key] = resolvePercent(value, key, percentConfig)
209
+ })
210
+ })
211
+ }
212
+
213
+ function resolveVar (input: string, varContext: Record<string, any>) {
214
+ const parsed = parseFunc(input, 'var')
215
+ const replaced = new ReplaceSource(input)
216
+
217
+ parsed.forEach(({ start, end, args }) => {
218
+ const varName = args[0]
219
+ const fallback = args[1] || ''
220
+ let varValue = hasOwn(varContext, varName) ? varContext[varName] : fallback
221
+ if (varUseRegExp.test(varValue)) {
222
+ varValue = '' + resolveVar(varValue, varContext)
223
+ } else {
224
+ varValue = '' + global.__formatValue(varValue)
225
+ }
226
+ replaced.replace(start, end - 1, varValue)
227
+ })
228
+ return global.__formatValue(replaced.source())
229
+ }
230
+
231
+ function transformVar (styleObj: Record<string, any>, varKeyPaths: Array<Array<string>>, varContext: Record<string, any>) {
232
+ varKeyPaths.forEach((varKeyPath) => {
233
+ setStyle(styleObj, varKeyPath, ({ target, key, value }) => {
234
+ target[key] = resolveVar(value, varContext)
235
+ })
236
+ })
237
+ }
238
+
239
+ function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<string>>) {
240
+ envKeyPaths.forEach((envKeyPath) => {
241
+ setStyle(styleObj, envKeyPath, ({ target, key, value }) => {
242
+ const parsed = parseFunc(value, 'env')
243
+ const replaced = new ReplaceSource(value)
244
+ parsed.forEach(({ start, end, args }) => {
245
+ const name = args[0]
246
+ const fallback = args[1] || ''
247
+ const value = '' + (getSafeAreaInset(name) ?? global.__formatValue(fallback))
248
+ replaced.replace(start, end - 1, value)
249
+ })
250
+ target[key] = global.__formatValue(replaced.source())
251
+ })
252
+ })
253
+ }
254
+
255
+ function transformCalc (styleObj: Record<string, any>, calcKeyPaths: Array<Array<string>>, formatter: (value: string, key: string) => number) {
256
+ calcKeyPaths.forEach((calcKeyPath) => {
257
+ setStyle(styleObj, calcKeyPath, ({ target, key, value }) => {
258
+ const parsed = parseFunc(value, 'calc')
259
+ const replaced = new ReplaceSource(value)
260
+ parsed.forEach(({ start, end, args }) => {
261
+ const exp = args[0]
262
+ try {
263
+ const result = new ExpressionParser(exp, (value) => {
264
+ return formatter(value, key)
265
+ }).parse()
266
+ replaced.replace(start, end - 1, '' + result.value)
267
+ } catch (e) {
268
+ error(`calc(${exp}) parse error.`, undefined, e)
269
+ }
270
+ })
271
+ target[key] = global.__formatValue(replaced.source())
272
+ })
273
+ })
274
+ }
275
+
276
+ interface TransformStyleConfig {
277
+ enableVar?: boolean
278
+ externalVarContext?: Record<string, any>
279
+ parentFontSize?: number
280
+ parentWidth?: number
281
+ parentHeight?: number
282
+ }
283
+
284
+ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
285
+ const varStyle: Record<string, any> = {}
286
+ const normalStyle: Record<string, any> = {}
287
+ let hasVarDec = false
288
+ let hasVarUse = false
289
+ let hasSelfPercent = false
290
+ const varKeyPaths: Array<Array<string>> = []
291
+ const percentKeyPaths: Array<Array<string>> = []
292
+ const calcKeyPaths: Array<Array<string>> = []
293
+ const envKeyPaths: Array<Array<string>> = []
294
+ const [width, setWidth] = useState(0)
295
+ const [height, setHeight] = useState(0)
296
+
297
+ function varVisitor ({ key, value, keyPath }: VisitorArg) {
298
+ if (keyPath.length === 1) {
299
+ if (varDecRegExp.test(key)) {
300
+ hasVarDec = true
301
+ varStyle[key] = value
302
+ } else {
303
+ // clone对象避免set值时改写到props
304
+ normalStyle[key] = isObject(value) ? diffAndCloneA(value).clone : value
305
+ }
306
+ }
307
+ // 对于var定义中使用的var无需替换值,可以通过resolveVar递归解析出值
308
+ if (!varDecRegExp.test(key) && varUseRegExp.test(value)) {
309
+ hasVarUse = true
310
+ varKeyPaths.push(keyPath.slice())
311
+ }
312
+ }
313
+
314
+ // traverse var
315
+ traverseStyle(styleObj, [varVisitor])
316
+ hasVarDec = hasVarDec || !!externalVarContext
317
+ enableVar = enableVar || hasVarDec || hasVarUse
318
+ const enableVarRef = useRef(enableVar)
319
+ if (enableVarRef.current !== enableVar) {
320
+ error('css variable use/declare should be stable in the component lifecycle, or you can set [enable-var] with true.')
321
+ }
322
+ // apply var
323
+ const varContextRef = useRef({})
324
+ if (enableVarRef.current) {
325
+ const varContext = useContext(VarContext)
326
+ const newVarContext = Object.assign({}, varContext, externalVarContext, varStyle)
327
+ // 缓存比较newVarContext是否发生变化
328
+ if (diffAndCloneA(varContextRef.current, newVarContext).diff) {
329
+ varContextRef.current = newVarContext
330
+ }
331
+ transformVar(normalStyle, varKeyPaths, varContextRef.current)
332
+ }
333
+
334
+ function envVisitor ({ value, keyPath }: VisitorArg) {
335
+ if (envUseRegExp.test(value)) {
336
+ envKeyPaths.push(keyPath.slice())
337
+ }
338
+ }
339
+
340
+ function calcVisitor ({ value, keyPath }: VisitorArg) {
341
+ if (calcUseRegExp.test(value)) {
342
+ calcKeyPaths.push(keyPath.slice())
343
+ }
344
+ }
345
+
346
+ function percentVisitor ({ key, value, keyPath }: VisitorArg) {
347
+ if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
348
+ hasSelfPercent = true
349
+ percentKeyPaths.push(keyPath.slice())
350
+ } else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
351
+ percentKeyPaths.push(keyPath.slice())
352
+ }
353
+ }
354
+
355
+ // traverse env & calc & percent
356
+ traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor])
357
+
358
+ const percentConfig = {
359
+ width,
360
+ height,
361
+ fontSize: normalStyle.fontSize,
362
+ parentWidth,
363
+ parentHeight,
364
+ parentFontSize
365
+ }
366
+
367
+ // apply env
368
+ transformEnv(normalStyle, envKeyPaths)
369
+ // apply percent
370
+ transformPercent(normalStyle, percentKeyPaths, percentConfig)
371
+ // apply calc
372
+ transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
373
+ if (PERCENT_REGEX.test(value)) {
374
+ const resolved = resolvePercent(value, key, percentConfig)
375
+ return typeof resolved === 'number' ? resolved : 0
376
+ } else {
377
+ const formatted = global.__formatValue(value)
378
+ if (typeof formatted === 'number') {
379
+ return formatted
380
+ } else {
381
+ warn('calc() only support number, px, rpx, % temporarily.')
382
+ return 0
383
+ }
384
+ }
385
+ })
386
+
387
+ return {
388
+ normalStyle,
389
+ hasSelfPercent,
390
+ hasVarDec,
391
+ enableVarRef,
392
+ varContextRef,
393
+ setWidth,
394
+ setHeight
395
+ }
396
+ }
397
+
398
+ export interface VisitorArg {
399
+ target: Record<string, any>
400
+ key: string
401
+ value: any
402
+ keyPath: Array<string>
403
+ }
404
+
405
+ export function traverseStyle (styleObj: Record<string, any>, visitors: Array<(arg: VisitorArg) => void>) {
406
+ const keyPath: Array<string> = []
407
+ function traverse<T extends Record<string, any>> (target: T) {
408
+ if (Array.isArray(target)) {
409
+ target.forEach((value, index) => {
410
+ const key = String(index)
411
+ keyPath.push(key)
412
+ visitors.forEach(visitor => visitor({
413
+ target,
414
+ key,
415
+ value,
416
+ keyPath
417
+ }))
418
+ traverse(value)
419
+ keyPath.pop()
420
+ })
421
+ } else if (isObject(target)) {
422
+ Object.entries(target).forEach(([key, value]) => {
423
+ keyPath.push(key)
424
+ visitors.forEach(visitor => visitor({ target, key, value, keyPath }))
425
+ traverse(value)
426
+ keyPath.pop()
427
+ })
428
+ }
429
+ }
430
+ traverse(styleObj)
431
+ }
432
+
433
+ export function setStyle (styleObj: Record<string, any>, keyPath: Array<string>, setter: (arg: VisitorArg) => void) {
434
+ let target = styleObj
435
+ const lastKey = keyPath[keyPath.length - 1]
436
+ for (let i = 0; i < keyPath.length - 1; i++) {
437
+ target = target[keyPath[i]]
438
+ if (!target) return
439
+ }
440
+ setter({
441
+ target,
442
+ key: lastKey,
443
+ value: target[lastKey],
444
+ keyPath
445
+ })
446
+ }
447
+
448
+ export function splitProps<T extends Record<string, any>> (props: T): {
449
+ textProps?: Partial<T>
450
+ innerProps?: Partial<T>
451
+ } {
452
+ return groupBy(props, (key) => {
453
+ if (TEXT_PROPS_REGEX.test(key)) {
454
+ return 'textProps'
455
+ } else {
456
+ return 'innerProps'
457
+ }
458
+ }) as {
459
+ textProps: Partial<T>
460
+ innerProps: Partial<T>
461
+ }
462
+ }
463
+
464
+ interface LayoutConfig {
465
+ props: Record<string, any>
466
+ hasSelfPercent: boolean
467
+ setWidth: Dispatch<SetStateAction<number>>
468
+ setHeight: Dispatch<SetStateAction<number>>
469
+ onLayout?: (event?: LayoutChangeEvent) => void
470
+ nodeRef: React.RefObject<any>
471
+ }
472
+ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout, nodeRef }: LayoutConfig) => {
473
+ const layoutRef = useRef({})
474
+ const hasLayoutRef = useRef(false)
475
+ const layoutStyle: Record<string, any> = !hasLayoutRef.current && hasSelfPercent ? DEFAULT_UNLAY_STYLE : {}
476
+ const layoutProps: Record<string, any> = {}
477
+ const enableOffset = props['enable-offset']
478
+ if (hasSelfPercent || onLayout || enableOffset) {
479
+ layoutProps.onLayout = (e: LayoutChangeEvent) => {
480
+ hasLayoutRef.current = true
481
+ if (hasSelfPercent) {
482
+ const { width, height } = e?.nativeEvent?.layout || {}
483
+ setWidth(width || 0)
484
+ setHeight(height || 0)
485
+ }
486
+ if (enableOffset) {
487
+ nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
488
+ layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
489
+ })
490
+ }
491
+ onLayout && onLayout(e)
492
+ props.onLayout && props.onLayout(e)
493
+ }
494
+ }
495
+ return {
496
+ layoutRef,
497
+ layoutStyle,
498
+ layoutProps
499
+ }
500
+ }
501
+
502
+ export interface WrapChildrenConfig {
503
+ hasVarDec: boolean
504
+ varContext?: Record<string, any>
505
+ textStyle?: TextStyle
506
+ textProps?: Record<string, any>
507
+ }
508
+
509
+ export function wrapChildren (props: Record<string, any> = {}, { hasVarDec, varContext, textStyle, textProps }: WrapChildrenConfig) {
510
+ let { children } = props
511
+ if (textStyle || textProps) {
512
+ children = Children.map(children, (child) => {
513
+ if (isText(child)) {
514
+ const style = { ...textStyle, ...child.props.style }
515
+ return cloneElement(child, { ...textProps, style })
516
+ }
517
+ return child
518
+ })
519
+ }
520
+ if (hasVarDec && varContext) {
521
+ children = <VarContext.Provider value={varContext} key='varContextWrap'>{children}</VarContext.Provider>
522
+ }
523
+ return children
524
+ }
@@ -9,6 +9,7 @@
9
9
  BScroll.use(PullDown)
10
10
 
11
11
  let mutationObserver = null
12
+ let resizeObserver = null
12
13
 
13
14
  export default {
14
15
  name: 'mpx-scroll-view',
@@ -357,11 +358,15 @@
357
358
  let minTop
358
359
  let maxBottom
359
360
  childrenArr.forEach(item => {
360
- const temp = item.getBoundingClientRect()
361
- minLeft = getMinLength(minLeft, temp.left)
362
- minTop = getMinLength(minTop, temp.top)
363
- maxRight = getMaxLength(maxRight, temp.right)
364
- maxBottom = getMaxLength(maxBottom, temp.bottom)
361
+ const left = item.offsetLeft
362
+ const top = item.offsetTop
363
+ const width = item.offsetWidth
364
+ const height = item.offsetHeight
365
+
366
+ minLeft = getMinLength(minLeft, left)
367
+ minTop = getMinLength(minTop, top)
368
+ maxRight = getMaxLength(maxRight, left + width)
369
+ maxBottom = getMaxLength(maxBottom, top + height)
365
370
  })
366
371
  const width = maxRight - minLeft || 0
367
372
  const height = maxBottom - minTop || 0
@@ -399,6 +404,17 @@
399
404
  const config = { attributes: true, childList: true, subtree: true }
400
405
  mutationObserver.observe(this.$refs.wrapper, config)
401
406
  }
407
+ if (typeof ResizeObserver !== 'undefined') {
408
+ let isFirstResize = true
409
+ resizeObserver = new ResizeObserver(() => {
410
+ if (isFirstResize) {
411
+ isFirstResize = false
412
+ return
413
+ }
414
+ this.debounceRefresh()
415
+ })
416
+ resizeObserver.observe(this.$refs.wrapper)
417
+ }
402
418
  },
403
419
  mutationObserverHandler (mutations) {
404
420
  let needRefresh = false
@@ -428,6 +444,10 @@
428
444
  mutationObserver.disconnect()
429
445
  mutationObserver = null
430
446
  }
447
+ if (resizeObserver) {
448
+ resizeObserver.disconnect()
449
+ resizeObserver = null
450
+ }
431
451
  }
432
452
  },
433
453
  render (createElement) {
@@ -18,7 +18,8 @@ module.exports = function (css, map) {
18
18
  const cb = this.async()
19
19
  const { resourcePath, queryObj } = parseRequest(this.resource)
20
20
  const mpx = this.getMpx()
21
- const id = queryObj.moduleId || queryObj.mid || '_' + mpx.pathHash(resourcePath)
21
+ const mpxStyleOptions = (queryObj.mpxStyleOptions && JSON.parse(queryObj.mpxStyleOptions)) || {}
22
+ const id = queryObj.moduleId || mpxStyleOptions.mid || '_' + mpx.pathHash(resourcePath)
22
23
  const appInfo = mpx.appInfo
23
24
  const defs = mpx.defs
24
25
  const mode = mpx.mode
@@ -46,14 +47,14 @@ module.exports = function (css, map) {
46
47
  config.options
47
48
  )
48
49
  // ali平台下处理scoped和host选择器
49
- if (mode === 'ali') {
50
- if (queryObj.scoped) {
50
+ if (mode === 'ali' || mode === 'web') {
51
+ if (queryObj.scoped || mpxStyleOptions.scoped) {
51
52
  plugins.push(scopeId({ id }))
52
53
  }
53
54
  plugins.push(transSpecial({ id }))
54
55
  }
55
56
 
56
- if (mode === 'web' || isReact(mode)) {
57
+ if (isReact(mode)) {
57
58
  plugins.push(transSpecial({ id }))
58
59
  }
59
60