@mpxjs/webpack-plugin 2.9.59 → 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 (115) hide show
  1. package/lib/index.js +1 -3
  2. package/lib/platform/style/wx/index.js +344 -270
  3. package/lib/platform/template/wx/component-config/checkbox-group.js +8 -0
  4. package/lib/platform/template/wx/component-config/checkbox.js +8 -0
  5. package/lib/platform/template/wx/component-config/cover-image.js +15 -0
  6. package/lib/platform/template/wx/component-config/cover-view.js +9 -0
  7. package/lib/platform/template/wx/component-config/form.js +13 -1
  8. package/lib/platform/template/wx/component-config/icon.js +8 -0
  9. package/lib/platform/template/wx/component-config/index.js +5 -1
  10. package/lib/platform/template/wx/component-config/label.js +15 -0
  11. package/lib/platform/template/wx/component-config/movable-area.js +18 -1
  12. package/lib/platform/template/wx/component-config/movable-view.js +18 -1
  13. package/lib/platform/template/wx/component-config/navigator.js +8 -0
  14. package/lib/platform/template/wx/component-config/picker-view-column.js +8 -0
  15. package/lib/platform/template/wx/component-config/picker-view.js +18 -2
  16. package/lib/platform/template/wx/component-config/picker.js +14 -1
  17. package/lib/platform/template/wx/component-config/radio-group.js +8 -0
  18. package/lib/platform/template/wx/component-config/radio.js +8 -0
  19. package/lib/platform/template/wx/component-config/root-portal.js +15 -0
  20. package/lib/platform/template/wx/component-config/switch.js +8 -0
  21. package/lib/platform/template/wx/component-config/unsupported.js +1 -3
  22. package/lib/react/processScript.js +2 -0
  23. package/lib/react/processStyles.js +1 -0
  24. package/lib/react/processTemplate.js +2 -3
  25. package/lib/react/style-helper.js +12 -7
  26. package/lib/runtime/components/react/context.ts +40 -0
  27. package/lib/runtime/components/react/dist/context.js +8 -0
  28. package/lib/runtime/components/react/dist/getInnerListeners.js +34 -12
  29. package/lib/runtime/components/react/dist/mpx-button.jsx +88 -88
  30. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +82 -0
  31. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +139 -0
  32. package/lib/runtime/components/react/dist/mpx-form.jsx +61 -0
  33. package/lib/runtime/components/react/dist/mpx-icon.jsx +48 -0
  34. package/lib/runtime/components/react/dist/mpx-image/index.jsx +39 -43
  35. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +3 -2
  36. package/lib/runtime/components/react/dist/mpx-input.jsx +63 -37
  37. package/lib/runtime/components/react/dist/mpx-label.jsx +55 -0
  38. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +41 -0
  39. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +346 -0
  40. package/lib/runtime/components/react/dist/mpx-navigator.jsx +35 -0
  41. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +69 -0
  42. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +138 -0
  43. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +142 -0
  44. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +94 -0
  45. package/lib/runtime/components/react/dist/mpx-picker/regionData.js +6099 -0
  46. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +76 -0
  47. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +244 -0
  48. package/lib/runtime/components/react/dist/mpx-picker/type.js +1 -0
  49. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +107 -0
  50. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +162 -0
  51. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +80 -0
  52. package/lib/runtime/components/react/dist/mpx-radio.jsx +154 -0
  53. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +15 -0
  54. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +93 -70
  55. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +281 -157
  56. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +21 -11
  57. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +19 -11
  58. package/lib/runtime/components/react/dist/mpx-switch.jsx +79 -0
  59. package/lib/runtime/components/react/dist/mpx-text.jsx +21 -49
  60. package/lib/runtime/components/react/dist/mpx-textarea.jsx +2 -2
  61. package/lib/runtime/components/react/dist/mpx-view.jsx +451 -146
  62. package/lib/runtime/components/react/dist/mpx-web-view.jsx +17 -20
  63. package/lib/runtime/components/react/dist/parser.js +218 -0
  64. package/lib/runtime/components/react/dist/types/common.js +1 -0
  65. package/lib/runtime/components/react/dist/useNodesRef.js +3 -8
  66. package/lib/runtime/components/react/dist/utils.jsx +433 -0
  67. package/lib/runtime/components/react/getInnerListeners.ts +43 -21
  68. package/lib/runtime/components/react/mpx-button.tsx +129 -119
  69. package/lib/runtime/components/react/mpx-checkbox-group.tsx +152 -0
  70. package/lib/runtime/components/react/mpx-checkbox.tsx +234 -0
  71. package/lib/runtime/components/react/mpx-form.tsx +117 -0
  72. package/lib/runtime/components/react/mpx-icon.tsx +106 -0
  73. package/lib/runtime/components/react/mpx-image/index.tsx +62 -68
  74. package/lib/runtime/components/react/mpx-image/svg.tsx +7 -5
  75. package/lib/runtime/components/react/mpx-input.tsx +90 -42
  76. package/lib/runtime/components/react/mpx-label.tsx +110 -0
  77. package/lib/runtime/components/react/mpx-movable-area.tsx +81 -0
  78. package/lib/runtime/components/react/mpx-movable-view.tsx +424 -0
  79. package/lib/runtime/components/react/mpx-navigator.tsx +67 -0
  80. package/lib/runtime/components/react/mpx-picker/date.tsx +82 -0
  81. package/lib/runtime/components/react/mpx-picker/index.tsx +155 -0
  82. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +156 -0
  83. package/lib/runtime/components/react/mpx-picker/region.tsx +107 -0
  84. package/lib/runtime/components/react/mpx-picker/regionData.ts +6101 -0
  85. package/lib/runtime/components/react/mpx-picker/selector.tsx +91 -0
  86. package/lib/runtime/components/react/mpx-picker/time.tsx +270 -0
  87. package/lib/runtime/components/react/mpx-picker/type.ts +107 -0
  88. package/lib/runtime/components/react/mpx-picker-view-column.tsx +156 -0
  89. package/lib/runtime/components/react/mpx-picker-view.tsx +220 -0
  90. package/lib/runtime/components/react/mpx-radio-group.tsx +150 -0
  91. package/lib/runtime/components/react/mpx-radio.tsx +230 -0
  92. package/lib/runtime/components/react/mpx-root-portal.tsx +27 -0
  93. package/lib/runtime/components/react/mpx-scroll-view.tsx +184 -130
  94. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +308 -183
  95. package/lib/runtime/components/react/mpx-swiper/index.tsx +27 -19
  96. package/lib/runtime/components/react/mpx-swiper/type.ts +23 -5
  97. package/lib/runtime/components/react/mpx-swiper-item.tsx +49 -14
  98. package/lib/runtime/components/react/mpx-switch.tsx +148 -0
  99. package/lib/runtime/components/react/mpx-text.tsx +53 -77
  100. package/lib/runtime/components/react/mpx-textarea.tsx +3 -3
  101. package/lib/runtime/components/react/mpx-view.tsx +576 -195
  102. package/lib/runtime/components/react/mpx-web-view.tsx +34 -39
  103. package/lib/runtime/components/react/parser.ts +245 -0
  104. package/lib/runtime/components/react/types/common.ts +12 -0
  105. package/lib/runtime/components/react/types/getInnerListeners.ts +2 -1
  106. package/lib/runtime/components/react/types/global.d.ts +17 -1
  107. package/lib/runtime/components/react/useNodesRef.ts +4 -10
  108. package/lib/runtime/components/react/utils.tsx +505 -0
  109. package/lib/runtime/optionProcessor.js +19 -17
  110. package/lib/template-compiler/compiler.js +84 -61
  111. package/lib/template-compiler/gen-node-react.js +7 -9
  112. package/lib/web/processStyles.js +2 -5
  113. package/package.json +8 -3
  114. package/lib/runtime/components/react/dist/utils.js +0 -80
  115. package/lib/runtime/components/react/utils.ts +0 -92
@@ -4,48 +4,81 @@
4
4
  * ✔ hover-start-time
5
5
  * ✔ hover-stay-time
6
6
  */
7
- import { View, Text, StyleSheet, Image } from 'react-native';
7
+ import { View, StyleSheet, Image } from 'react-native';
8
8
  import { useRef, useState, useEffect, forwardRef } from 'react';
9
- // @ts-ignore
10
9
  import useInnerProps from './getInnerListeners';
11
- // @ts-ignore
12
- import useNodesRef from './useNodesRef'; // 引入辅助函数
13
- import { parseUrl, TEXT_STYLE_REGEX, PERCENT_REGEX, isText } from './utils';
14
- const IMAGE_STYLE_REGEX = /^background(Image|Size|Repeat|Position)$/;
15
- function groupBy(style, callback, group = {}) {
16
- let groupKey = '';
17
- for (let key in style) {
18
- if (style.hasOwnProperty(key)) { // 确保处理对象自身的属性
19
- let val = style[key];
20
- groupKey = callback(key, val);
21
- if (!group[groupKey]) {
22
- group[groupKey] = {};
23
- }
24
- group[groupKey][key] = val;
25
- }
26
- }
27
- return group;
10
+ import useNodesRef from './useNodesRef';
11
+ import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout } from './utils';
12
+ import LinearGradient from 'react-native-linear-gradient';
13
+ const linearMap = new Map([
14
+ ['top', 0],
15
+ ['bottom', 180],
16
+ ['left', 270],
17
+ ['right', 90]
18
+ ]);
19
+ // 对角线角度
20
+ const diagonalAngleMap = {
21
+ 'top right': (width, height) => {
22
+ return Math.acos((width / 2) /
23
+ (Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) / 2));
24
+ },
25
+ 'right top': (width, height) => { return diagonalAngleMap['top right'](width, height); },
26
+ 'bottom right': (width, height) => Math.PI - diagonalAngleMap['top right'](width, height),
27
+ 'right bottom': (width, height) => { return diagonalAngleMap['bottom right'](width, height); },
28
+ 'bottom left': (width, height) => Math.PI + diagonalAngleMap['top right'](width, height),
29
+ 'left bottom': (width, height) => { return diagonalAngleMap['bottom left'](width, height); },
30
+ 'top left': (width, height) => (2 * Math.PI) - diagonalAngleMap['top right'](width, height),
31
+ 'left top': (width, height) => { return diagonalAngleMap['top left'](width, height); }
32
+ };
33
+ // 弧度转化为角度的公式
34
+ function radToAngle(r) {
35
+ return r * 180 / Math.PI;
28
36
  }
29
37
  const applyHandlers = (handlers, args) => {
30
- for (let handler of handlers) {
38
+ for (const handler of handlers) {
31
39
  handler(...args);
32
40
  }
33
41
  };
34
- const checkNeedLayout = (style) => {
35
- const [width, height] = style.sizeList;
36
- return (PERCENT_REGEX.test(`${height}`) && width === 'auto') || (PERCENT_REGEX.test(`${width}`) && height === 'auto');
42
+ const isPercent = (val) => typeof val === 'string' && PERCENT_REGEX.test(val);
43
+ const isBackgroundSizeKeyword = (val) => typeof val === 'string' && /^cover|contain$/.test(val);
44
+ const isNeedLayout = (preImageInfo) => {
45
+ const { sizeList, backgroundPosition, linearInfo } = preImageInfo;
46
+ const [width, height] = sizeList;
47
+ const bp = backgroundPosition;
48
+ // 含有百分号,center 需计算布局
49
+ return isBackgroundSizeKeyword(width) ||
50
+ (isPercent(height) && width === 'auto') ||
51
+ (isPercent(width) && height === 'auto') ||
52
+ isPercent(bp[1]) ||
53
+ isPercent(bp[3]) ||
54
+ isDiagonalAngle(linearInfo);
55
+ };
56
+ const checkNeedLayout = (preImageInfo) => {
57
+ const { sizeList } = preImageInfo;
58
+ const [width] = sizeList;
59
+ // 在渐变的时候,background-size的cover,contain, auto属性值,转化为100%, needLayout计算逻辑和原来保持一致,needImageSize始终为false
60
+ return {
61
+ // 是否开启layout的计算
62
+ needLayout: isNeedLayout(preImageInfo),
63
+ // 是否开启原始宽度的计算
64
+ needImageSize: isBackgroundSizeKeyword(width) || sizeList.includes('auto')
65
+ };
37
66
  };
38
67
  /**
39
- * h - 用户设置的高度
40
- * lh - 容器的高度
41
- * ratio - 原始图片的宽高比
42
- * **/
43
- function calculateSize(h, lh, ratio) {
44
- let height, width;
45
- if (PERCENT_REGEX.test(`${h}`)) { // auto px/rpx
68
+ * h - 用户设置的高度
69
+ * lh - 容器的高度
70
+ * ratio - 原始图片的宽高比
71
+ * **/
72
+ function calculateSize(h, ratio, lh, reverse = false) {
73
+ let height = 0;
74
+ let width = 0;
75
+ if (typeof lh === 'boolean') {
76
+ reverse = lh;
77
+ }
78
+ if (isPercent(h)) { // auto px/rpx
46
79
  if (!lh)
47
80
  return null;
48
- height = (parseFloat(`${h}`) / 100) * lh;
81
+ height = (parseFloat(h) / 100) * lh;
49
82
  width = height * ratio;
50
83
  }
51
84
  else { // 2. auto px/rpx - 根据比例计算
@@ -53,130 +86,389 @@ function calculateSize(h, lh, ratio) {
53
86
  width = height * ratio;
54
87
  }
55
88
  return {
56
- width,
57
- height
89
+ width: reverse ? height : width,
90
+ height: reverse ? width : height
91
+ };
92
+ }
93
+ /**
94
+ * 用户设置百分比后,转换为偏移量
95
+ * h - 用户设置图片的高度
96
+ * ch - 容器的高度
97
+ * val - 用户设置的百分比
98
+ * **/
99
+ function calculateSizePosition(h, ch, val) {
100
+ if (!h || !ch)
101
+ return 0;
102
+ // 百分比需要单独的计算
103
+ if (isPercent(h)) {
104
+ h = ch * parseFloat(h) / 100;
105
+ }
106
+ // (container width - image width) * (position x%) = (x offset value)
107
+ return (ch - h) * parseFloat(val) / 100;
108
+ }
109
+ /**
110
+ * 获取图片的展示宽高
111
+ * h - 用户设置的高度
112
+ * lh - 容器的高度
113
+ * **/
114
+ const calcPercent = (h, lh) => {
115
+ return isPercent(h) ? parseFloat(h) / 100 * lh : +h;
116
+ };
117
+ function backgroundPosition(imageProps, preImageInfo, imageSize, layoutInfo) {
118
+ const bps = preImageInfo.backgroundPosition;
119
+ if (bps.length === 0)
120
+ return;
121
+ const style = {};
122
+ const imageStyle = imageProps.style || {};
123
+ for (let i = 0; i < bps.length; i += 2) {
124
+ const key = bps[i];
125
+ const val = bps[i + 1];
126
+ // 需要获取 图片宽度 和 容器的宽度 进行计算
127
+ if (isPercent(val)) {
128
+ if (i === 0) {
129
+ style[key] = calculateSizePosition(imageStyle.width, layoutInfo?.width, val);
130
+ }
131
+ else {
132
+ style[key] = calculateSizePosition(imageStyle.height, layoutInfo?.height, val);
133
+ }
134
+ }
135
+ else {
136
+ style[key] = val;
137
+ }
138
+ }
139
+ imageProps.style = {
140
+ ...imageProps.style,
141
+ ...style
58
142
  };
59
143
  }
60
144
  // background-size 转换
61
145
  function backgroundSize(imageProps, preImageInfo, imageSize, layoutInfo) {
62
- let sizeList = preImageInfo.sizeList;
146
+ const sizeList = preImageInfo.sizeList;
63
147
  if (!sizeList)
64
148
  return;
149
+ const { width: layoutWidth, height: layoutHeight } = layoutInfo || {};
150
+ const { width: imageSizeWidth, height: imageSizeHeight } = imageSize || {};
151
+ const [width, height] = sizeList;
152
+ let dimensions = { width: 0, height: 0 };
65
153
  // 枚举值
66
- if (['cover', 'contain'].includes(`${sizeList[0]}`)) {
67
- imageProps.style.resizeMode = sizeList[0];
154
+ if (typeof width === 'string' && ['cover', 'contain'].includes(width)) {
155
+ if (layoutInfo && imageSize) {
156
+ const layoutRatio = layoutWidth / imageSizeWidth;
157
+ const eleRatio = imageSizeWidth / imageSizeHeight;
158
+ // 容器宽高比 大于 图片的宽高比,依据宽度作为基准,否则以高度为基准
159
+ if ((layoutRatio <= eleRatio && width === 'contain') || (layoutRatio >= eleRatio && width === 'cover')) {
160
+ dimensions = calculateSize(layoutWidth, imageSizeHeight / imageSizeWidth, true);
161
+ }
162
+ else if ((layoutRatio > eleRatio && width === 'contain') || (layoutRatio < eleRatio && width === 'cover')) {
163
+ dimensions = calculateSize(layoutHeight, imageSizeWidth / imageSizeHeight);
164
+ }
165
+ }
68
166
  }
69
167
  else {
70
- const [width, height] = sizeList;
71
- let newWidth = 0, newHeight = 0;
72
- const { width: imageSizeWidth, height: imageSizeHeight } = imageSize || {};
73
168
  if (width === 'auto' && height === 'auto') { // 均为auto
74
169
  if (!imageSize)
75
170
  return;
76
- newHeight = imageSizeHeight;
77
- newWidth = imageSizeWidth;
171
+ dimensions = {
172
+ width: imageSizeWidth,
173
+ height: imageSizeHeight
174
+ };
78
175
  }
79
176
  else if (width === 'auto') { // auto px/rpx/%
80
177
  if (!imageSize)
81
178
  return;
82
- const dimensions = calculateSize(height, layoutInfo?.height, imageSizeWidth / imageSizeHeight);
179
+ dimensions = calculateSize(height, imageSizeWidth / imageSizeHeight, layoutInfo?.height);
83
180
  if (!dimensions)
84
181
  return;
85
- newWidth = dimensions.width;
86
- newHeight = dimensions.height;
87
182
  }
88
183
  else if (height === 'auto') { // auto px/rpx/%
89
184
  if (!imageSize)
90
185
  return;
91
- const dimensions = calculateSize(width, layoutInfo?.width, imageSizeHeight / imageSizeWidth);
186
+ dimensions = calculateSize(width, imageSizeHeight / imageSizeWidth, layoutInfo?.width, true);
92
187
  if (!dimensions)
93
188
  return;
94
- newHeight = dimensions.width;
95
- newWidth = dimensions.height;
96
189
  }
97
190
  else { // 数值类型 ImageStyle
98
191
  // 数值类型设置为 stretch
99
192
  imageProps.style.resizeMode = 'stretch';
100
- newWidth = PERCENT_REGEX.test(`${width}`) ? width : +width;
101
- newHeight = PERCENT_REGEX.test(`${width}`) ? height : +height;
193
+ dimensions = {
194
+ width: isPercent(width) ? width : +width,
195
+ height: isPercent(height) ? height : +height
196
+ };
102
197
  }
103
- // 样式合并
104
- imageProps.style = {
105
- ...imageProps.style,
106
- width: newWidth,
107
- height: newHeight
108
- };
109
198
  }
199
+ // 样式合并
200
+ imageProps.style = {
201
+ ...imageProps.style,
202
+ ...dimensions
203
+ };
110
204
  }
111
205
  // background-image转换为source
112
206
  function backgroundImage(imageProps, preImageInfo) {
113
- imageProps.src = preImageInfo.src;
207
+ if (preImageInfo.src) {
208
+ imageProps.src = preImageInfo.src;
209
+ }
210
+ }
211
+ // 渐变的转换
212
+ function linearGradient(imageProps, preImageInfo, imageSize, layoutInfo) {
213
+ const { type, linearInfo } = preImageInfo;
214
+ const { colors = [], locations, direction = '' } = linearInfo || {};
215
+ const { width, height } = imageSize || {};
216
+ if (type !== 'linear')
217
+ return;
218
+ // 角度计算
219
+ let angle = +(linearMap.get(direction) || direction.match(/(-?\d+(\.\d+)?)deg/)?.[1] || 180) % 360;
220
+ // 对角线角度计算
221
+ if (layoutInfo && diagonalAngleMap[direction] && imageSize && linearInfo) {
222
+ angle = radToAngle(diagonalAngleMap[direction](width, height)) || 180;
223
+ }
224
+ // 赋值
225
+ imageProps.colors = colors;
226
+ imageProps.locations = locations;
227
+ imageProps.angle = angle;
114
228
  }
115
229
  const imageStyleToProps = (preImageInfo, imageSize, layoutInfo) => {
116
230
  // 初始化
117
231
  const imageProps = {
118
232
  style: {
119
233
  resizeMode: 'cover',
120
- ...StyleSheet.absoluteFillObject
121
- }
234
+ position: 'absolute'
235
+ // ...StyleSheet.absoluteFillObject
236
+ },
237
+ colors: []
122
238
  };
123
- applyHandlers([backgroundSize, backgroundImage], [imageProps, preImageInfo, imageSize, layoutInfo]);
124
- if (!imageProps?.src)
125
- return null;
239
+ applyHandlers([backgroundSize, backgroundImage, backgroundPosition, linearGradient], [imageProps, preImageInfo, imageSize, layoutInfo]);
126
240
  return imageProps;
127
241
  };
242
+ function isHorizontal(val) {
243
+ return typeof val === 'string' && /^(left|right)$/.test(val);
244
+ }
245
+ function isVertical(val) {
246
+ return typeof val === 'string' && /^(top|bottom)$/.test(val);
247
+ }
248
+ function normalizeBackgroundPosition(parts) {
249
+ if (parts.length === 0)
250
+ return [];
251
+ // 定义默认值
252
+ let hStart = 'left';
253
+ let hOffset = 0;
254
+ let vStart = 'top';
255
+ let vOffset = 0;
256
+ if (parts.length === 4)
257
+ return parts;
258
+ // 归一化
259
+ if (parts.length === 1) {
260
+ // 1. center
261
+ // 2. 2px - hOffset, vOffset(center) - center为50%
262
+ // 3. 10% - hOffset, vOffset(center) - center为50%
263
+ // 4. left - hStart, vOffset(center) - center为50%
264
+ // 5. top - hOffset(center), vStart - center为50%
265
+ if (isHorizontal(parts[0])) {
266
+ hStart = parts[0];
267
+ vOffset = '50%';
268
+ }
269
+ else if (isVertical(parts[0])) {
270
+ vStart = parts[0];
271
+ hOffset = '50%';
272
+ }
273
+ else {
274
+ hOffset = parts[0];
275
+ vOffset = '50%';
276
+ }
277
+ }
278
+ else if (parts.length === 2) {
279
+ // 1. center center - hOffset, vOffset
280
+ // 2. 10px center - hOffset, vStart
281
+ // 3. left center - hStart, vOffset
282
+ // 4. right center - hStart, vOffset
283
+ // 5. 第一位是 left right 覆盖的是 hStart
284
+ // center, 100% 正常的px 覆盖的是 hOffset
285
+ // 第二位是 top bottom 覆盖的是 vStart
286
+ // center, 100% 覆盖的是 vOffset
287
+ //
288
+ // 水平方向
289
+ if (isHorizontal(parts[0])) {
290
+ hStart = parts[0];
291
+ }
292
+ else { // center, 100% 正常的px 覆盖的是 hOffset
293
+ hOffset = parts[0];
294
+ }
295
+ // 垂直方向
296
+ if (isVertical(parts[1])) {
297
+ vStart = parts[1];
298
+ }
299
+ else { // center, 100% 正常的px 覆盖的是 hOffset
300
+ vOffset = parts[1];
301
+ }
302
+ }
303
+ else if (parts.length === 3) {
304
+ // 1. center top 10px / top 10px center 等价 - center为50%
305
+ // 2. right 10px center / center right 10px 等价 - center为50%
306
+ // 2. bottom 50px right
307
+ if (typeof parts[0] === 'string' && typeof parts[1] === 'string' && /^left|bottom|right|top$/.test(parts[0]) && /^left|bottom|right|top$/.test(parts[1])) {
308
+ [hStart, vStart, vOffset] = parts;
309
+ }
310
+ else {
311
+ [hStart, hOffset, vStart] = parts;
312
+ }
313
+ }
314
+ return [hStart, hOffset, vStart, vOffset];
315
+ }
316
+ /**
317
+ *
318
+ * calcSteps - 计算起始位置和终点位置之间的差值
319
+ * startVal - 起始位置距离
320
+ * endVal - 终点位置距离
321
+ * len - 数量
322
+ * **/
323
+ function calcSteps(startVal, endVal, len) {
324
+ const diffVal = endVal - startVal;
325
+ const step = diffVal / len;
326
+ const newArr = [];
327
+ for (let i = 1; i < len; i++) {
328
+ const val = startVal + step * i;
329
+ newArr.push(+val.toFixed(2));
330
+ }
331
+ return newArr;
332
+ }
333
+ function parseLinearGradient(text) {
334
+ let linearText = text.trim().match(/linear-gradient\((.*)\)/)?.[1];
335
+ if (!linearText)
336
+ return;
337
+ // 添加默认的角度
338
+ if (!/^to|^-?\d+deg/.test(linearText)) {
339
+ linearText = '180deg ,' + linearText;
340
+ }
341
+ else {
342
+ linearText = linearText.replace('to', '');
343
+ }
344
+ // 把 0deg, red 10%, blue 20% 解析为 ['0deg', 'red, 10%', 'blue, 20%']
345
+ const [direction, ...colorList] = linearText.split(/,(?![^(#]*\))/);
346
+ // 记录需要填充起点的起始位置
347
+ let startIdx = 0;
348
+ let startVal = 0;
349
+ // 把 ['red, 10%', 'blue, 20%']解析为 [[red, 10%], [blue, 20%]]
350
+ const linearInfo = colorList.map(item => item.trim().split(/(?<!,)\s+/))
351
+ .reduce((prev, cur, idx, self) => {
352
+ const { colors, locations } = prev;
353
+ const [color, val] = cur;
354
+ let numberVal = parseFloat(val) / 100;
355
+ // 处理渐变默认值
356
+ if (idx === 0) {
357
+ numberVal = numberVal || 0;
358
+ }
359
+ else if (self.length - 1 === idx) {
360
+ numberVal = numberVal || 1;
361
+ }
362
+ // 出现缺省值时进行填充
363
+ if (idx - startIdx > 1 && !isNaN(numberVal)) {
364
+ locations.push(...calcSteps(startVal, numberVal, idx - startIdx));
365
+ }
366
+ if (!isNaN(numberVal)) {
367
+ startIdx = idx;
368
+ startVal = numberVal;
369
+ }
370
+ // 添加color的数组
371
+ colors.push(color.trim());
372
+ !isNaN(numberVal) && locations.push(numberVal);
373
+ return prev;
374
+ }, { colors: [], locations: [] });
375
+ return {
376
+ ...linearInfo,
377
+ direction: direction.trim()
378
+ };
379
+ }
380
+ function parseBgImage(text) {
381
+ if (!text)
382
+ return {};
383
+ const src = parseUrl(text);
384
+ if (src)
385
+ return { src, type: 'image' };
386
+ const linearInfo = parseLinearGradient(text);
387
+ if (!linearInfo)
388
+ return {};
389
+ return {
390
+ linearInfo,
391
+ type: 'linear'
392
+ };
393
+ }
394
+ function normalizeBackgroundSize(backgroundSize, type) {
395
+ const sizeList = backgroundSize.slice();
396
+ if (sizeList.length === 1)
397
+ sizeList.push('auto');
398
+ if (type === 'linear') {
399
+ // 处理当使用渐变的时候,background-size出现cover, contain, auto,当作100%处理
400
+ for (const i in sizeList) {
401
+ const val = sizeList[i];
402
+ sizeList[i] = /^cover|contain|auto$/.test(val) ? '100%' : val;
403
+ }
404
+ }
405
+ return sizeList;
406
+ }
128
407
  function preParseImage(imageStyle) {
129
- const { backgroundImage, backgroundSize = ["auto"] } = imageStyle || {};
130
- const src = parseUrl(backgroundImage);
131
- let sizeList = backgroundSize.slice();
132
- sizeList.length === 1 && sizeList.push(sizeList[0]);
408
+ const { backgroundImage = '', backgroundSize = ['auto'], backgroundPosition = [0, 0] } = imageStyle || {};
409
+ const { type, src, linearInfo } = parseBgImage(backgroundImage);
133
410
  return {
134
411
  src,
135
- sizeList
412
+ linearInfo,
413
+ type,
414
+ sizeList: normalizeBackgroundSize(backgroundSize, type),
415
+ backgroundPosition: normalizeBackgroundPosition(backgroundPosition)
136
416
  };
137
417
  }
418
+ function isDiagonalAngle(linearInfo) {
419
+ return !!(linearInfo?.direction && diagonalAngleMap[linearInfo.direction]);
420
+ }
138
421
  function wrapImage(imageStyle) {
139
- const [show, setShow] = useState(false);
422
+ // 预处理数据
423
+ const preImageInfo = preParseImage(imageStyle);
424
+ // 预解析
425
+ const { src, sizeList, type } = preImageInfo;
426
+ // 判断是否可挂载onLayout
427
+ const { needLayout, needImageSize } = checkNeedLayout(preImageInfo);
428
+ const [show, setShow] = useState(((type === 'image' && !!src) || type === 'linear') && !needLayout && !needImageSize);
140
429
  const [, setImageSizeWidth] = useState(null);
141
430
  const [, setImageSizeHeight] = useState(null);
142
431
  const [, setLayoutInfoWidth] = useState(null);
143
432
  const [, setLayoutInfoHeight] = useState(null);
144
433
  const sizeInfo = useRef(null);
145
434
  const layoutInfo = useRef(null);
146
- // 预解析
147
- const preImageInfo = preParseImage(imageStyle);
148
- // 判断是否可挂载onLayout
149
- const needLayout = checkNeedLayout(preImageInfo);
150
- const { src, sizeList } = preImageInfo;
151
435
  useEffect(() => {
436
+ sizeInfo.current = null;
437
+ if (type === 'linear') {
438
+ if (!needLayout)
439
+ setShow(true);
440
+ return;
441
+ }
152
442
  if (!src) {
153
443
  setShow(false);
154
- sizeInfo.current = null;
155
- layoutInfo.current = null;
156
444
  return;
445
+ // 一开始未出现,数据改变时出现
157
446
  }
158
- if (!sizeList.includes('auto')) {
447
+ else if (!(needLayout || needImageSize)) {
159
448
  setShow(true);
160
449
  return;
161
450
  }
162
- Image.getSize(src, (width, height) => {
163
- sizeInfo.current = {
164
- width,
165
- height
166
- };
167
- //1. 当需要绑定onLayout 2. 获取到布局信息
168
- if (!needLayout || layoutInfo.current) {
169
- setImageSizeWidth(width);
170
- setImageSizeHeight(height);
171
- if (layoutInfo.current) {
172
- setLayoutInfoWidth(layoutInfo.current.width);
173
- setLayoutInfoHeight(layoutInfo.current.height);
451
+ if (needImageSize) {
452
+ Image.getSize(src, (width, height) => {
453
+ sizeInfo.current = {
454
+ width,
455
+ height
456
+ };
457
+ // 1. 当需要绑定onLayout 2. 获取到布局信息
458
+ if (!needLayout || layoutInfo.current) {
459
+ setImageSizeWidth(width);
460
+ setImageSizeHeight(height);
461
+ if (layoutInfo.current) {
462
+ setLayoutInfoWidth(layoutInfo.current.width);
463
+ setLayoutInfoHeight(layoutInfo.current.height);
464
+ }
465
+ setShow(true);
174
466
  }
175
- setShow(true);
176
- }
177
- });
178
- }, [preImageInfo?.src]);
179
- if (!preImageInfo?.src)
467
+ });
468
+ }
469
+ // type 添加type 处理无渐变 有渐变的场景
470
+ }, [src, type]);
471
+ if (!type)
180
472
  return null;
181
473
  const onLayout = (res) => {
182
474
  const { width, height } = res?.nativeEvent?.layout || {};
@@ -184,60 +476,77 @@ function wrapImage(imageStyle) {
184
476
  width,
185
477
  height
186
478
  };
187
- if (sizeInfo.current) {
188
- setImageSizeWidth(sizeInfo.current.width);
189
- setImageSizeHeight(sizeInfo.current.height);
479
+ if (!needImageSize) {
480
+ setLayoutInfoWidth(width);
481
+ setLayoutInfoHeight(height);
482
+ // 有渐变角度的时候,才触发渲染组件
483
+ if (type === 'linear') {
484
+ sizeInfo.current = {
485
+ width: calcPercent(sizeList[0], width),
486
+ height: calcPercent(sizeList[1], height)
487
+ };
488
+ setImageSizeWidth(sizeInfo.current.width);
489
+ setImageSizeHeight(sizeInfo.current.height);
490
+ setShow(true);
491
+ }
492
+ }
493
+ else if (sizeInfo.current) {
190
494
  setLayoutInfoWidth(width);
191
495
  setLayoutInfoHeight(height);
496
+ setImageSizeWidth(sizeInfo.current.width);
497
+ setImageSizeHeight(sizeInfo.current.height);
192
498
  setShow(true);
193
499
  }
194
500
  };
195
- return <View key='viewBgImg' {...needLayout ? { onLayout } : null} style={{ ...StyleSheet.absoluteFillObject, width: '100%', height: '100%', overflow: 'hidden' }}>
196
- {show && <Image {...imageStyleToProps(preImageInfo, sizeInfo.current, layoutInfo.current)}/>}
501
+ return <View key='backgroundImage' {...needLayout ? { onLayout } : null} style={{ ...StyleSheet.absoluteFillObject, width: '100%', height: '100%', overflow: 'hidden' }}>
502
+ {show && type === 'linear' && <LinearGradient useAngle={true} {...imageStyleToProps(preImageInfo, sizeInfo.current, layoutInfo.current)}/>}
503
+ {show && type === 'image' && <Image {...imageStyleToProps(preImageInfo, sizeInfo.current, layoutInfo.current)}/>}
197
504
  </View>;
198
505
  }
199
- function splitStyle(styles) {
200
- return groupBy(styles, (key) => {
201
- if (TEXT_STYLE_REGEX.test(key))
202
- return 'textStyle';
203
- else if (IMAGE_STYLE_REGEX.test(key))
204
- return 'imageStyle';
205
- return 'innerStyle';
206
- }, {});
207
- }
208
- function every(children, callback) {
209
- return children.every((child) => callback(child));
210
- }
211
- function wrapChildren(children, textStyle, imageStyle) {
212
- children = Array.isArray(children) ? children : [children];
213
- if (every(children, (child) => isText(child))) {
214
- children = [<Text key='viewTextWrap' style={textStyle}>{children}</Text>];
215
- }
216
- else {
217
- if (textStyle)
218
- console.warn('Text style will be ignored unless every child of the view is Text node!');
219
- }
506
+ function wrapWithChildren(props, { hasVarDec, enableBackground, textStyle, backgroundStyle, varContext, textProps }) {
507
+ const children = wrapChildren(props, {
508
+ hasVarDec,
509
+ varContext,
510
+ textStyle,
511
+ textProps
512
+ });
220
513
  return [
221
- wrapImage(imageStyle),
222
- ...children
514
+ enableBackground ? wrapImage(backgroundStyle) : null,
515
+ children
223
516
  ];
224
517
  }
225
- const _View = forwardRef((props, ref) => {
226
- const { style = [], children, hoverStyle, 'hover-start-time': hoverStartTime = 50, 'hover-stay-time': hoverStayTime = 400, 'enable-offset': enableOffset } = props;
518
+ const _View = forwardRef((viewProps, ref) => {
519
+ const { textProps, innerProps: props = {} } = splitProps(viewProps);
520
+ let { style = {}, 'hover-style': hoverStyle, 'hover-start-time': hoverStartTime = 50, 'hover-stay-time': hoverStayTime = 400, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'enable-background': enableBackground, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
227
521
  const [isHover, setIsHover] = useState(false);
228
- const layoutRef = useRef({});
229
- // 打平 style 数组
230
- const styleObj = StyleSheet.flatten(style);
231
522
  // 默认样式
232
523
  const defaultStyle = {
233
524
  // flex 布局相关的默认样式
234
- ...styleObj.display === 'flex' && {
525
+ ...style.display === 'flex' && {
235
526
  flexDirection: 'row',
236
527
  flexBasis: 'auto',
237
528
  flexShrink: 1,
238
529
  flexWrap: 'nowrap'
239
530
  }
240
531
  };
532
+ const styleObj = {
533
+ ...defaultStyle,
534
+ ...style,
535
+ ...(isHover ? hoverStyle : null)
536
+ };
537
+ const { normalStyle, hasSelfPercent, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
538
+ enableVar,
539
+ externalVarContext,
540
+ parentFontSize,
541
+ parentWidth,
542
+ parentHeight
543
+ });
544
+ const { textStyle, backgroundStyle, innerStyle } = splitStyle(normalStyle);
545
+ enableBackground = enableBackground || !!backgroundStyle;
546
+ const enableBackgroundRef = useRef(enableBackground);
547
+ if (enableBackgroundRef.current !== enableBackground) {
548
+ throw new Error('[Mpx runtime error]: background use should be stable in the component lifecycle, or you can set [enable-background] with true.');
549
+ }
241
550
  const { nodeRef } = useNodesRef(props, ref, {
242
551
  defaultStyle
243
552
  });
@@ -251,14 +560,14 @@ const _View = forwardRef((props, ref) => {
251
560
  const setStartTimer = () => {
252
561
  dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
253
562
  dataRef.current.startTimer = setTimeout(() => {
254
- setIsHover(() => true);
563
+ setIsHover(true);
255
564
  }, +hoverStartTime);
256
565
  };
257
566
  const setStayTimer = () => {
258
567
  dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer);
259
568
  dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
260
569
  dataRef.current.stayTimer = setTimeout(() => {
261
- setIsHover(() => false);
570
+ setIsHover(false);
262
571
  }, +hoverStayTime);
263
572
  };
264
573
  function onTouchStart(e) {
@@ -271,36 +580,32 @@ const _View = forwardRef((props, ref) => {
271
580
  bindtouchend && bindtouchend(e);
272
581
  setStayTimer();
273
582
  }
274
- const onLayout = () => {
275
- nodeRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
276
- layoutRef.current = { x, y, width, height, offsetLeft, offsetTop };
277
- });
278
- };
279
- const { textStyle, imageStyle, innerStyle } = splitStyle(StyleSheet.flatten([
280
- defaultStyle,
281
- styleObj,
282
- ...(isHover ? hoverStyle : [])
283
- ]));
583
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
284
584
  const innerProps = useInnerProps(props, {
285
585
  ref: nodeRef,
286
- ...enableOffset ? { onLayout } : {},
586
+ style: { ...innerStyle, ...layoutStyle },
587
+ ...layoutProps,
287
588
  ...(hoverStyle && {
288
589
  bindtouchstart: onTouchStart,
289
590
  bindtouchend: onTouchEnd
290
591
  })
291
592
  }, [
292
- 'style',
293
- 'children',
294
593
  'hover-start-time',
295
594
  'hover-stay-time',
296
- 'hoverStyle',
297
- 'hover-class',
298
- 'enable-offset'
595
+ 'hover-style',
596
+ 'hover-class'
299
597
  ], {
300
598
  layoutRef
301
599
  });
302
- return (<View {...innerProps} style={innerStyle}>
303
- {wrapChildren(children, textStyle, imageStyle)}
600
+ return (<View {...innerProps}>
601
+ {wrapWithChildren(props, {
602
+ hasVarDec,
603
+ enableBackground: enableBackgroundRef.current,
604
+ textStyle,
605
+ backgroundStyle,
606
+ varContext: varContextRef.current,
607
+ textProps
608
+ })}
304
609
  </View>);
305
610
  });
306
611
  _View.displayName = 'mpx-view';