@mpxjs/webpack-plugin 2.9.62 → 2.9.64

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 (86) hide show
  1. package/lib/index.js +1 -3
  2. package/lib/platform/style/wx/index.js +67 -53
  3. package/lib/react/processStyles.js +1 -0
  4. package/lib/react/processTemplate.js +2 -3
  5. package/lib/react/style-helper.js +12 -7
  6. package/lib/runtime/components/react/context.ts +9 -7
  7. package/lib/runtime/components/react/dist/context.js +1 -0
  8. package/lib/runtime/components/react/dist/getInnerListeners.js +12 -1
  9. package/lib/runtime/components/react/dist/mpx-button.jsx +52 -74
  10. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +19 -18
  11. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +28 -41
  12. package/lib/runtime/components/react/dist/mpx-form.jsx +16 -14
  13. package/lib/runtime/components/react/dist/mpx-icon.jsx +14 -17
  14. package/lib/runtime/components/react/dist/mpx-image/index.jsx +34 -33
  15. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +3 -1
  16. package/lib/runtime/components/react/dist/mpx-input.jsx +35 -31
  17. package/lib/runtime/components/react/dist/mpx-label.jsx +29 -37
  18. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +13 -18
  19. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +8 -8
  20. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +9 -9
  21. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +7 -4
  22. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +11 -7
  23. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +1 -1
  24. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +18 -18
  25. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +102 -10
  26. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +147 -53
  27. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +19 -18
  28. package/lib/runtime/components/react/dist/mpx-radio.jsx +28 -43
  29. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +8 -4
  30. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +33 -26
  31. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +139 -74
  32. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +14 -6
  33. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +19 -11
  34. package/lib/runtime/components/react/dist/mpx-switch.jsx +17 -14
  35. package/lib/runtime/components/react/dist/mpx-text.jsx +19 -35
  36. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  37. package/lib/runtime/components/react/dist/mpx-view.jsx +284 -209
  38. package/lib/runtime/components/react/dist/mpx-web-view.jsx +8 -5
  39. package/lib/runtime/components/react/dist/parser.js +218 -0
  40. package/lib/runtime/components/react/dist/utils.jsx +433 -0
  41. package/lib/runtime/components/react/getInnerListeners.ts +18 -8
  42. package/lib/runtime/components/react/mpx-button.tsx +81 -91
  43. package/lib/runtime/components/react/mpx-checkbox-group.tsx +48 -43
  44. package/lib/runtime/components/react/mpx-checkbox.tsx +52 -63
  45. package/lib/runtime/components/react/mpx-form.tsx +49 -21
  46. package/lib/runtime/components/react/mpx-icon.tsx +30 -27
  47. package/lib/runtime/components/react/mpx-image/index.tsx +52 -46
  48. package/lib/runtime/components/react/mpx-image/svg.tsx +5 -3
  49. package/lib/runtime/components/react/mpx-input.tsx +58 -38
  50. package/lib/runtime/components/react/mpx-label.tsx +54 -59
  51. package/lib/runtime/components/react/mpx-movable-area.tsx +38 -24
  52. package/lib/runtime/components/react/mpx-movable-view.tsx +27 -28
  53. package/lib/runtime/components/react/mpx-navigator.tsx +2 -2
  54. package/lib/runtime/components/react/mpx-picker/date.tsx +2 -3
  55. package/lib/runtime/components/react/mpx-picker/index.tsx +10 -10
  56. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +15 -12
  57. package/lib/runtime/components/react/mpx-picker/region.tsx +21 -18
  58. package/lib/runtime/components/react/mpx-picker/selector.tsx +5 -6
  59. package/lib/runtime/components/react/mpx-picker/time.tsx +25 -29
  60. package/lib/runtime/components/react/mpx-picker/type.ts +1 -1
  61. package/lib/runtime/components/react/mpx-picker-view-column.tsx +148 -20
  62. package/lib/runtime/components/react/mpx-picker-view.tsx +179 -63
  63. package/lib/runtime/components/react/mpx-radio-group.tsx +50 -47
  64. package/lib/runtime/components/react/mpx-radio.tsx +56 -72
  65. package/lib/runtime/components/react/mpx-root-portal.tsx +10 -8
  66. package/lib/runtime/components/react/mpx-scroll-view.tsx +133 -103
  67. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +174 -96
  68. package/lib/runtime/components/react/mpx-swiper/index.tsx +18 -9
  69. package/lib/runtime/components/react/mpx-swiper/type.ts +16 -5
  70. package/lib/runtime/components/react/mpx-swiper-item.tsx +46 -13
  71. package/lib/runtime/components/react/mpx-switch.tsx +44 -23
  72. package/lib/runtime/components/react/mpx-text.tsx +37 -45
  73. package/lib/runtime/components/react/mpx-textarea.tsx +1 -1
  74. package/lib/runtime/components/react/mpx-view.tsx +388 -240
  75. package/lib/runtime/components/react/mpx-web-view.tsx +19 -20
  76. package/lib/runtime/components/react/parser.ts +245 -0
  77. package/lib/runtime/components/react/types/common.ts +4 -4
  78. package/lib/runtime/components/react/types/global.d.ts +14 -2
  79. package/lib/runtime/components/react/useNodesRef.ts +1 -2
  80. package/lib/runtime/components/react/utils.tsx +505 -0
  81. package/lib/template-compiler/compiler.js +28 -20
  82. package/lib/template-compiler/gen-node-react.js +1 -3
  83. package/lib/web/processStyles.js +2 -5
  84. package/package.json +6 -4
  85. package/lib/runtime/components/react/dist/utils.js +0 -148
  86. package/lib/runtime/components/react/utils.ts +0 -170
@@ -0,0 +1,505 @@
1
+ import { useEffect, useRef, ReactNode, ReactElement, FunctionComponent, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react'
2
+ import { Dimensions, StyleSheet, LayoutChangeEvent, TextStyle } from 'react-native'
3
+ import { isObject, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
4
+ import { VarContext } from './context'
5
+ import { ExpressionParser, parseFunc, ReplaceSource } from './parser'
6
+
7
+ export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/
8
+ export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/
9
+ export const URL_REGEX = /^\s*url\(["']?(.*?)["']?\)\s*$/
10
+ export const BACKGROUND_REGEX = /^background(Image|Size|Repeat|Position)$/
11
+ export const TEXT_PROPS_REGEX = /ellipsizeMode|numberOfLines/
12
+ export const DEFAULT_FONT_SIZE = 16
13
+ export const DEFAULT_UNLAY_STYLE = {
14
+ opacity: 0
15
+ }
16
+
17
+ export function rpx (value: number) {
18
+ const { width } = Dimensions.get('screen')
19
+ // rn 单位 dp = 1(css)px = 1 物理像素 * pixelRatio(像素比)
20
+ // px = rpx * (750 / 屏幕宽度)
21
+ return value * width / 750
22
+ }
23
+
24
+ const rpxRegExp = /^\s*(-?\d+(\.\d+)?)rpx\s*$/
25
+ const pxRegExp = /^\s*(-?\d+(\.\d+)?)(px)?\s*$/
26
+ const hairlineRegExp = /^\s*hairlineWidth\s*$/
27
+ const varDecRegExp = /^--.*/
28
+ const varUseRegExp = /var\(/
29
+ const calcUseRegExp = /calc\(/
30
+
31
+ export function omit<T, K extends string> (obj: T, fields: K[]): Omit<T, K> {
32
+ const shallowCopy: any = Object.assign({}, obj)
33
+ for (let i = 0; i < fields.length; i += 1) {
34
+ const key = fields[i]
35
+ delete shallowCopy[key]
36
+ }
37
+ return shallowCopy
38
+ }
39
+
40
+ /**
41
+ * 用法等同于 useEffect,但是会忽略首次执行,只在依赖更新时执行
42
+ */
43
+ export const useUpdateEffect = (effect: any, deps: any) => {
44
+ const isMounted = useRef(false)
45
+
46
+ // for react-refresh
47
+ useEffect(() => {
48
+ return () => {
49
+ isMounted.current = false
50
+ }
51
+ }, [])
52
+
53
+ useEffect(() => {
54
+ if (!isMounted.current) {
55
+ isMounted.current = true
56
+ } else {
57
+ return effect()
58
+ }
59
+ }, deps)
60
+ }
61
+
62
+ /**
63
+ * 解析行内样式
64
+ * @param inlineStyle
65
+ * @returns
66
+ */
67
+ export const parseInlineStyle = (inlineStyle = ''): Record<string, string> => {
68
+ return inlineStyle.split(';').reduce((styleObj, style) => {
69
+ const [k, v, ...rest] = style.split(':')
70
+ if (rest.length || !v || !k) return styleObj
71
+ const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase())
72
+ return Object.assign(styleObj, { [key]: v.trim() })
73
+ }, {})
74
+ }
75
+
76
+ export const parseUrl = (cssUrl = '') => {
77
+ if (!cssUrl) return
78
+ const match = cssUrl.match(URL_REGEX)
79
+ return match?.[1]
80
+ }
81
+
82
+ export const getRestProps = (transferProps: any = {}, originProps: any = {}, deletePropsKey: any = []) => {
83
+ return {
84
+ ...transferProps,
85
+ ...omit(originProps, deletePropsKey)
86
+ }
87
+ }
88
+
89
+ export function isText (ele: ReactNode): ele is ReactElement {
90
+ if (isValidElement(ele)) {
91
+ const displayName = (ele.type as FunctionComponent)?.displayName
92
+ return displayName === 'mpx-text' || displayName === 'Text'
93
+ }
94
+ return false
95
+ }
96
+
97
+ export function isEmbedded (ele: ReactNode): ele is ReactElement {
98
+ if (isValidElement(ele)) {
99
+ const displayName = (ele.type as FunctionComponent)?.displayName || ''
100
+ return ['mpx-checkbox', 'mpx-radio', 'mpx-switch'].includes(displayName)
101
+ }
102
+ return false
103
+ }
104
+
105
+ export function every (children: ReactNode, callback: (children: ReactNode) => boolean) {
106
+ const childrenArray = Array.isArray(children) ? children : [children]
107
+ return childrenArray.every((child) => callback(child))
108
+ }
109
+
110
+ type GroupData<T> = Record<string, Partial<T>>
111
+ export function groupBy<T extends Record<string, any>> (
112
+ obj: T,
113
+ callback: (key: string, val: T[keyof T]) => string,
114
+ group: GroupData<T> = {}
115
+ ): GroupData<T> {
116
+ Object.entries(obj).forEach(([key, val]) => {
117
+ const groupKey = callback(key, val)
118
+ group[groupKey] = group[groupKey] || {}
119
+ group[groupKey][key as keyof T] = val
120
+ })
121
+ return group
122
+ }
123
+
124
+ export function splitStyle<T extends Record<string, any>> (styleObj: T): {
125
+ textStyle?: Partial<T>;
126
+ backgroundStyle?: Partial<T>;
127
+ innerStyle?: Partial<T>;
128
+ } {
129
+ return groupBy(styleObj, (key) => {
130
+ if (TEXT_STYLE_REGEX.test(key)) {
131
+ return 'textStyle'
132
+ } else if (BACKGROUND_REGEX.test(key)) {
133
+ return 'backgroundStyle'
134
+ } else {
135
+ return 'innerStyle'
136
+ }
137
+ }) as {
138
+ textStyle: Partial<T>;
139
+ backgroundStyle: Partial<T>;
140
+ innerStyle: Partial<T>;
141
+ }
142
+ }
143
+
144
+ const selfPercentRule: Record<string, 'height' | 'width'> = {
145
+ translateX: 'width',
146
+ translateY: 'height',
147
+ borderTopLeftRadius: 'width',
148
+ borderBottomLeftRadius: 'width',
149
+ borderBottomRightRadius: 'width',
150
+ borderTopRightRadius: 'width',
151
+ borderRadius: 'width'
152
+ }
153
+
154
+ const parentHeightPercentRule: Record<string, boolean> = {
155
+ height: true,
156
+ top: true,
157
+ bottom: true
158
+ }
159
+
160
+ // todo calc时处理角度和时间等单位
161
+ function formatValue (value: string) {
162
+ let matched
163
+ if ((matched = pxRegExp.exec(value))) {
164
+ return +matched[1]
165
+ } else if ((matched = rpxRegExp.exec(value))) {
166
+ return rpx(+matched[1])
167
+ } else if (hairlineRegExp.test(value)) {
168
+ return StyleSheet.hairlineWidth
169
+ }
170
+ return value
171
+ }
172
+
173
+ interface PercentConfig {
174
+ fontSize?: number | string
175
+ width?: number
176
+ height?: number
177
+ parentFontSize?: number
178
+ parentWidth?: number
179
+ parentHeight?: number
180
+ }
181
+
182
+ function resolvePercent (value: string | number | undefined, key: string, percentConfig: PercentConfig): string | number | undefined {
183
+ if (!(typeof value === 'string' && PERCENT_REGEX.test(value))) return value
184
+ let base
185
+ let reason
186
+ if (key === 'fontSize') {
187
+ base = percentConfig.parentFontSize
188
+ reason = 'parent-font-size'
189
+ } else if (key === 'lineHeight') {
190
+ base = resolvePercent(percentConfig.fontSize, 'fontSize', percentConfig)
191
+ reason = 'font-size'
192
+ } else if (selfPercentRule[key]) {
193
+ base = percentConfig[selfPercentRule[key]]
194
+ reason = selfPercentRule[key]
195
+ } else if (parentHeightPercentRule[key]) {
196
+ base = percentConfig.parentHeight
197
+ reason = 'parent-height'
198
+ } else {
199
+ base = percentConfig.parentWidth
200
+ reason = 'parent-width'
201
+ }
202
+ if (typeof base !== 'number') {
203
+ error(`[${key}] can not contain % unit unless you set [${reason}] with a number for the percent calculation.`)
204
+ return value
205
+ } else {
206
+ return parseFloat(value) / 100 * base
207
+ }
208
+ }
209
+
210
+ function transformPercent (styleObj: Record<string, any>, percentKeyPaths: Array<Array<string>>, percentConfig: PercentConfig) {
211
+ percentKeyPaths.forEach((percentKeyPath) => {
212
+ setStyle(styleObj, percentKeyPath, ({ target, key, value }) => {
213
+ target[key] = resolvePercent(value, key, percentConfig)
214
+ })
215
+ })
216
+ }
217
+
218
+ function resolveVar (input: string, varContext: Record<string, any>) {
219
+ const parsed = parseFunc(input, 'var')
220
+ const replaced = new ReplaceSource(input)
221
+
222
+ parsed.forEach(({ start, end, args }) => {
223
+ const varName = args[0]
224
+ const fallback = args[1] || ''
225
+ let varValue = hasOwn(varContext, varName) ? varContext[varName] : fallback
226
+ if (varUseRegExp.test(varValue)) {
227
+ varValue = '' + resolveVar(varValue, varContext)
228
+ } else {
229
+ varValue = '' + formatValue(varValue)
230
+ }
231
+ replaced.replace(start, end - 1, varValue)
232
+ })
233
+ return formatValue(replaced.source())
234
+ }
235
+
236
+ function transformVar (styleObj: Record<string, any>, varKeyPaths: Array<Array<string>>, varContext: Record<string, any>) {
237
+ varKeyPaths.forEach((varKeyPath) => {
238
+ setStyle(styleObj, varKeyPath, ({ target, key, value }) => {
239
+ target[key] = resolveVar(value, varContext)
240
+ })
241
+ })
242
+ }
243
+
244
+ function transformCalc (styleObj: Record<string, any>, calcKeyPaths: Array<Array<string>>, formatter: (value: string, key: string) => number) {
245
+ calcKeyPaths.forEach((calcKeyPath) => {
246
+ setStyle(styleObj, calcKeyPath, ({ target, key, value }) => {
247
+ const parsed = parseFunc(value, 'calc')
248
+ const replaced = new ReplaceSource(value)
249
+ parsed.forEach(({ start, end, args }) => {
250
+ const exp = args[0]
251
+ try {
252
+ const result = new ExpressionParser(exp, (value) => {
253
+ return formatter(value, key)
254
+ }).parse()
255
+ replaced.replace(start, end - 1, '' + result.value)
256
+ } catch (e) {
257
+ error(`calc(${exp}) parse error.`, undefined, e)
258
+ }
259
+ })
260
+ target[key] = formatValue(replaced.source())
261
+ })
262
+ })
263
+ }
264
+
265
+ interface TransformStyleConfig {
266
+ enableVar?: boolean
267
+ externalVarContext?: Record<string, any>
268
+ parentFontSize?: number
269
+ parentWidth?: number
270
+ parentHeight?: number
271
+ }
272
+
273
+ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
274
+ const varStyle: Record<string, any> = {}
275
+ const normalStyle: Record<string, any> = {}
276
+ let hasVarDec = false
277
+ let hasVarUse = false
278
+ let hasSelfPercent = false
279
+ const varKeyPaths: Array<Array<string>> = []
280
+ const percentKeyPaths: Array<Array<string>> = []
281
+ const calcKeyPaths: Array<Array<string>> = []
282
+ const [width, setWidth] = useState(0)
283
+ const [height, setHeight] = useState(0)
284
+
285
+ function varVisitor ({ key, value, keyPath }: VisitorArg) {
286
+ if (keyPath.length === 1) {
287
+ if (varDecRegExp.test(key)) {
288
+ hasVarDec = true
289
+ varStyle[key] = value
290
+ } else {
291
+ // clone对象避免set值时改写到props
292
+ normalStyle[key] = isObject(value) ? diffAndCloneA(value).clone : value
293
+ }
294
+ }
295
+ // 对于var定义中使用的var无需替换值,可以通过resolveVar递归解析出值
296
+ if (!varDecRegExp.test(key) && varUseRegExp.test(value)) {
297
+ hasVarUse = true
298
+ varKeyPaths.push(keyPath.slice())
299
+ }
300
+ }
301
+ // traverse var
302
+ traverseStyle(styleObj, [varVisitor])
303
+ hasVarDec = hasVarDec || !!externalVarContext
304
+ enableVar = enableVar || hasVarDec || hasVarUse
305
+ const enableVarRef = useRef(enableVar)
306
+ if (enableVarRef.current !== enableVar) {
307
+ error('css variable use/declare should be stable in the component lifecycle, or you can set [enable-var] with true.')
308
+ }
309
+ // apply var
310
+ const varContextRef = useRef({})
311
+ if (enableVarRef.current) {
312
+ const varContext = useContext(VarContext)
313
+ const newVarContext = Object.assign({}, varContext, externalVarContext, varStyle)
314
+ // 缓存比较newVarContext是否发生变化
315
+ if (diffAndCloneA(varContextRef.current, newVarContext).diff) {
316
+ varContextRef.current = newVarContext
317
+ }
318
+ transformVar(normalStyle, varKeyPaths, varContextRef.current)
319
+ }
320
+
321
+ function calcVisitor ({ value, keyPath }: VisitorArg) {
322
+ if (calcUseRegExp.test(value)) {
323
+ calcKeyPaths.push(keyPath.slice())
324
+ }
325
+ }
326
+
327
+ function percentVisitor ({ key, value, keyPath }: VisitorArg) {
328
+ if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
329
+ hasSelfPercent = true
330
+ percentKeyPaths.push(keyPath.slice())
331
+ } else if (key === 'fontSize' || key === 'lineHeight') {
332
+ percentKeyPaths.push(keyPath.slice())
333
+ }
334
+ }
335
+
336
+ // traverse calc & percent
337
+ traverseStyle(normalStyle, [percentVisitor, calcVisitor])
338
+
339
+ const percentConfig = {
340
+ width,
341
+ height,
342
+ fontSize: normalStyle.fontSize,
343
+ parentWidth,
344
+ parentHeight,
345
+ parentFontSize
346
+ }
347
+
348
+ // apply percent
349
+ transformPercent(normalStyle, percentKeyPaths, percentConfig)
350
+ // apply calc
351
+ transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
352
+ if (PERCENT_REGEX.test(value)) {
353
+ const resolved = resolvePercent(value, key, percentConfig)
354
+ return typeof resolved === 'number' ? resolved : 0
355
+ } else {
356
+ const formatted = formatValue(value)
357
+ if (typeof formatted === 'number') {
358
+ return formatted
359
+ } else {
360
+ warn('calc() only support number, px, rpx, % temporarily.')
361
+ return 0
362
+ }
363
+ }
364
+ })
365
+
366
+ return {
367
+ normalStyle,
368
+ hasSelfPercent,
369
+ hasVarDec,
370
+ enableVarRef,
371
+ varContextRef,
372
+ setWidth,
373
+ setHeight
374
+ }
375
+ }
376
+
377
+ export interface VisitorArg {
378
+ target: Record<string, any>
379
+ key: string
380
+ value: any
381
+ keyPath: Array<string>
382
+ }
383
+
384
+ export function traverseStyle (styleObj: Record<string, any>, visitors: Array<(arg: VisitorArg) => void>) {
385
+ const keyPath: Array<string> = []
386
+ function traverse<T extends Record<string, any>> (target: T) {
387
+ if (Array.isArray(target)) {
388
+ target.forEach((value, index) => {
389
+ const key = String(index)
390
+ keyPath.push(key)
391
+ visitors.forEach(visitor => visitor({
392
+ target,
393
+ key,
394
+ value,
395
+ keyPath
396
+ }))
397
+ traverse(value)
398
+ keyPath.pop()
399
+ })
400
+ } else if (isObject(target)) {
401
+ Object.entries(target).forEach(([key, value]) => {
402
+ keyPath.push(key)
403
+ visitors.forEach(visitor => visitor({ target, key, value, keyPath }))
404
+ traverse(value)
405
+ keyPath.pop()
406
+ })
407
+ }
408
+ }
409
+ traverse(styleObj)
410
+ }
411
+
412
+ export function setStyle (styleObj: Record<string, any>, keyPath: Array<string>, setter: (arg: VisitorArg) => void, needClone = false) {
413
+ let target = styleObj
414
+ const firstKey = keyPath[0]
415
+ const lastKey = keyPath[keyPath.length - 1]
416
+ if (needClone) target[firstKey] = diffAndCloneA(target[firstKey]).clone
417
+ for (let i = 0; i < keyPath.length - 1; i++) {
418
+ target = target[keyPath[i]]
419
+ if (!target) return
420
+ }
421
+ setter({
422
+ target,
423
+ key: lastKey,
424
+ value: target[lastKey],
425
+ keyPath
426
+ })
427
+ }
428
+
429
+ export function splitProps<T extends Record<string, any>> (props: T): {
430
+ textProps?: Partial<T>;
431
+ innerProps?: Partial<T>;
432
+ } {
433
+ return groupBy(props, (key) => {
434
+ if (TEXT_PROPS_REGEX.test(key)) {
435
+ return 'textProps'
436
+ } else {
437
+ return 'innerProps'
438
+ }
439
+ }) as {
440
+ textProps: Partial<T>;
441
+ innerProps: Partial<T>;
442
+ }
443
+ }
444
+
445
+ interface LayoutConfig {
446
+ props: Record<string, any>
447
+ hasSelfPercent: boolean
448
+ setWidth: Dispatch<SetStateAction<number>>
449
+ setHeight: Dispatch<SetStateAction<number>>
450
+ onLayout?: (event?: LayoutChangeEvent) => void
451
+ nodeRef: React.RefObject<any>
452
+ }
453
+ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout, nodeRef }:LayoutConfig) => {
454
+ const layoutRef = useRef({})
455
+ const hasLayoutRef = useRef(false)
456
+ const layoutStyle: Record<string, any> = !hasLayoutRef.current && hasSelfPercent ? DEFAULT_UNLAY_STYLE : {}
457
+ const layoutProps: Record<string, any> = {}
458
+ const enableOffset = props['enable-offset']
459
+ if (hasSelfPercent || onLayout || enableOffset) {
460
+ layoutProps.onLayout = (e: LayoutChangeEvent) => {
461
+ hasLayoutRef.current = true
462
+ if (hasSelfPercent) {
463
+ const { width, height } = e?.nativeEvent?.layout || {}
464
+ setWidth(width || 0)
465
+ setHeight(height || 0)
466
+ }
467
+ if (enableOffset) {
468
+ nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
469
+ layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
470
+ })
471
+ }
472
+ onLayout && onLayout(e)
473
+ props.onLayout && props.onLayout(e)
474
+ }
475
+ }
476
+ return {
477
+ layoutRef,
478
+ layoutStyle,
479
+ layoutProps
480
+ }
481
+ }
482
+
483
+ export interface WrapChildrenConfig {
484
+ hasVarDec: boolean
485
+ varContext?: Record<string, any>
486
+ textStyle?: TextStyle
487
+ textProps?: Record<string, any>
488
+ }
489
+
490
+ export function wrapChildren (props: Record<string, any> = {}, { hasVarDec, varContext, textStyle, textProps }: WrapChildrenConfig) {
491
+ let { children } = props
492
+ if (textStyle || textProps) {
493
+ children = Children.map(children, (child) => {
494
+ if (isText(child)) {
495
+ const style = { ...textStyle, ...child.props.style }
496
+ return cloneElement(child, { ...textProps, style })
497
+ }
498
+ return child
499
+ })
500
+ }
501
+ if (hasVarDec && varContext) {
502
+ children = <VarContext.Provider value={varContext} key='varContextWrap'>{children}</VarContext.Provider>
503
+ }
504
+ return children
505
+ }
@@ -1068,7 +1068,10 @@ function processStyleReact (el, options) {
1068
1068
  let staticStyle = getAndRemoveAttr(el, 'style').val || ''
1069
1069
  staticStyle = staticStyle.replace(/\s+/g, ' ')
1070
1070
 
1071
- const show = getAndRemoveAttr(el, config[mode].directive.show).val
1071
+ const { val: show, has } = getAndRemoveAttr(el, config[mode].directive.show)
1072
+ if (has && show === undefined) {
1073
+ error$1(`Attrs ${config[mode].directive.show} should have a value `)
1074
+ }
1072
1075
 
1073
1076
  if (dynamicClass || staticClass || dynamicStyle || staticStyle || show) {
1074
1077
  const staticClassExp = parseMustacheWithContext(staticClass).result
@@ -1080,14 +1083,14 @@ function processStyleReact (el, options) {
1080
1083
  addAttrs(el, [{
1081
1084
  name: 'style',
1082
1085
  // runtime helper
1083
- value: `{{this.__getStyle(${staticClassExp}, ${dynamicClassExp}, ${staticStyleExp}, ${dynamicStyleExp}, ${showExp})}}`
1086
+ value: `{{this.__getStyle(${staticClassExp}, ${dynamicClassExp}, ${staticStyleExp}, ${dynamicStyleExp}${show === undefined ? '' : `, !(${showExp})`})}}`
1084
1087
  }])
1085
1088
  }
1086
1089
 
1087
1090
  if (staticHoverClass && staticHoverClass !== 'none') {
1088
1091
  const staticClassExp = parseMustacheWithContext(staticHoverClass).result
1089
1092
  addAttrs(el, [{
1090
- name: 'hoverStyle',
1093
+ name: 'hover-style',
1091
1094
  value: `{{this.__getStyle(${staticClassExp})}}`
1092
1095
  }])
1093
1096
  }
@@ -1355,11 +1358,13 @@ function processEvent (el, options) {
1355
1358
  }
1356
1359
  }
1357
1360
 
1358
- function processSlotReact (el) {
1361
+ function processSlotReact (el, meta) {
1359
1362
  if (el.tag === 'slot') {
1360
1363
  el.slot = {
1361
1364
  name: getAndRemoveAttr(el, 'name').val
1362
1365
  }
1366
+ meta.options = meta.options || {}
1367
+ meta.options.disableMemo = true
1363
1368
  }
1364
1369
  }
1365
1370
 
@@ -1720,34 +1725,33 @@ function processRefReact (el, meta) {
1720
1725
  type
1721
1726
  }
1722
1727
 
1728
+ const selectors = []
1729
+
1730
+ /**
1731
+ * selectorsConf: [type, [[prefix, selector], [prefix, selector]]]
1732
+ */
1723
1733
  if (!val) {
1724
- refConf.key = `ref_rn_${++refId}`
1725
- refConf.sKeys = []
1726
1734
  const rawId = el.attrsMap.id
1727
1735
  const rawClass = el.attrsMap.class
1728
1736
  const rawDynamicClass = el.attrsMap[config[mode].directive.dynamicClass]
1729
1737
 
1730
- meta.computed = meta.computed || []
1731
1738
  if (rawId) {
1732
1739
  const staticId = parseMustacheWithContext(rawId).result
1733
- const computedIdKey = `_ri${refId}`
1734
- refConf.sKeys.push({ key: computedIdKey, prefix: '#' })
1735
- meta.computed.push(`${computedIdKey}() {\n return ${staticId}}`)
1740
+ selectors.push({ prefix: '#', selector: `${staticId}` })
1736
1741
  }
1737
1742
  if (rawClass || rawDynamicClass) {
1738
1743
  const staticClass = parseMustacheWithContext(rawClass).result
1739
1744
  const dynamicClass = parseMustacheWithContext(rawDynamicClass).result
1740
- const computedClassKey = `_rc${refId}`
1741
- refConf.sKeys.push({ key: computedClassKey, prefix: '.' })
1742
- meta.computed.push(`${computedClassKey}() {\n return this.__getClass(${staticClass}, ${dynamicClass})}`)
1745
+ selectors.push({ prefix: '.', selector: `this.__getClass(${staticClass}, ${dynamicClass})` })
1743
1746
  }
1747
+ } else {
1748
+ meta.refs.push(refConf)
1749
+ selectors.push({ prefix: '', selector: `"${refConf.key}"` })
1744
1750
  }
1745
-
1746
- meta.refs.push(refConf)
1747
-
1751
+ const selectorsConf = selectors.map(item => `["${item.prefix}", ${item.selector}]`)
1748
1752
  addAttrs(el, [{
1749
1753
  name: 'ref',
1750
- value: `{{ this.__getRefVal('${refConf.key}') }}`
1754
+ value: `{{ this.__getRefVal('${type}', [${selectorsConf}]) }}`
1751
1755
  }])
1752
1756
  }
1753
1757
  }
@@ -2329,7 +2333,7 @@ function getVirtualHostRoot (options, meta) {
2329
2333
  if (ctorType === 'component') {
2330
2334
  if (mode === 'wx' && hasVirtualHost) {
2331
2335
  // wx组件注入virtualHost配置
2332
- !meta.options && (meta.options = {})
2336
+ meta.options = meta.options || {}
2333
2337
  meta.options.virtualHost = true
2334
2338
  }
2335
2339
  if (mode === 'web' && !hasVirtualHost) {
@@ -2353,8 +2357,12 @@ function getVirtualHostRoot (options, meta) {
2353
2357
  name: 'class',
2354
2358
  value: `${MPX_ROOT_VIEW} host-${moduleId}`
2355
2359
  }
2360
+ // todo 运行时通过root标识确定是否合并rootProps
2361
+ // {
2362
+ // name: 'is-root',
2363
+ // value: '{{true}}'
2364
+ // }
2356
2365
  ])
2357
- rootView.isRoot = true
2358
2366
  processElement(rootView, rootView, options, meta)
2359
2367
  return rootView
2360
2368
  }
@@ -2622,7 +2630,7 @@ function processElement (el, root, options, meta) {
2622
2630
  processStyleReact(el, options)
2623
2631
  processEventReact(el)
2624
2632
  processComponentIs(el, options)
2625
- processSlotReact(el)
2633
+ processSlotReact(el, meta)
2626
2634
  processAttrs(el, options)
2627
2635
  return
2628
2636
  }
@@ -60,9 +60,7 @@ function genNode (node) {
60
60
  exp += `__getSlot(${name ? s(name) : ''})`
61
61
  } else {
62
62
  exp += `createElement(${`getComponent(${node.is || s(node.tag)})`}`
63
- if (node.isRoot) {
64
- exp += `, Object.assign({}, rootProps, {style: Object.assign({}, ${attrExpMap.style}, rootProps.style)})`
65
- } else if (node.attrsList.length) {
63
+ if (node.attrsList.length) {
66
64
  const attrs = []
67
65
  node.attrsList && node.attrsList.forEach(({ name, value }) => {
68
66
  const attrExp = attrExpMap[name] ? attrExpMap[name] : s(value)
@@ -8,11 +8,8 @@ module.exports = function (styles, options, callback) {
8
8
  attrs (style) {
9
9
  const attrs = Object.assign({}, style.attrs)
10
10
  if (options.autoScope) attrs.scoped = true
11
- attrs.mpxStyleOptions = JSON.stringify({
12
- // scoped: !!options.autoScope,
13
- // query中包含module字符串会被新版vue-cli中的默认rules当做css-module处理
14
- mid: options.moduleId
15
- })
11
+ // query中包含module字符串会被新版vue-cli中的默认rules当做css-module处理
12
+ attrs.mid = options.moduleId
16
13
  return attrs
17
14
  }
18
15
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.9.62",
3
+ "version": "2.9.64",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"
@@ -28,7 +28,7 @@
28
28
  "@better-scroll/wheel": "^2.5.1",
29
29
  "@better-scroll/zoom": "^2.5.1",
30
30
  "@mpxjs/template-engine": "^2.8.7",
31
- "@mpxjs/utils": "^2.9.59",
31
+ "@mpxjs/utils": "^2.9.64",
32
32
  "acorn": "^8.11.3",
33
33
  "acorn-walk": "^7.2.0",
34
34
  "async": "^2.6.0",
@@ -82,16 +82,18 @@
82
82
  },
83
83
  "devDependencies": {
84
84
  "@ant-design/react-native": "^5.2.2",
85
- "@mpxjs/api-proxy": "^2.9.62",
85
+ "@mpxjs/api-proxy": "^2.9.64",
86
86
  "@types/babel-traverse": "^6.25.4",
87
87
  "@types/babel-types": "^7.0.4",
88
88
  "@types/react": "^18.2.79",
89
89
  "react-native": "^0.74.5",
90
90
  "react-native-gesture-handler": "^2.18.1",
91
+ "react-native-linear-gradient": "^2.8.3",
92
+ "react-native-webview": "^13.12.2",
91
93
  "rimraf": "^6.0.1"
92
94
  },
93
95
  "engines": {
94
96
  "node": ">=14.14.0"
95
97
  },
96
- "gitHead": "c31a343e7ef2c1c1585002752d8c50f016a858ae"
98
+ "gitHead": "803334dc0e600f219d514c27461aa7663b7a6653"
97
99
  }