@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.
- package/lib/config.js +38 -10
- package/lib/index.js +1 -3
- package/lib/platform/style/wx/index.js +115 -66
- package/lib/platform/template/wx/index.js +12 -8
- package/lib/react/processStyles.js +1 -0
- package/lib/react/processTemplate.js +2 -3
- package/lib/react/style-helper.js +9 -7
- package/lib/runtime/components/react/context.ts +9 -7
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +12 -1
- package/lib/runtime/components/react/dist/mpx-button.jsx +53 -74
- package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +20 -18
- package/lib/runtime/components/react/dist/mpx-checkbox.jsx +30 -42
- package/lib/runtime/components/react/dist/mpx-form.jsx +18 -15
- package/lib/runtime/components/react/dist/mpx-icon.jsx +15 -17
- package/lib/runtime/components/react/dist/mpx-image/index.jsx +36 -34
- package/lib/runtime/components/react/dist/mpx-image/svg.jsx +3 -1
- package/lib/runtime/components/react/dist/mpx-input.jsx +36 -31
- package/lib/runtime/components/react/dist/mpx-label.jsx +30 -37
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +15 -19
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +10 -9
- package/lib/runtime/components/react/dist/mpx-picker/date.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -10
- package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-picker/region.jsx +13 -8
- package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +3 -2
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +22 -20
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +103 -10
- package/lib/runtime/components/react/dist/mpx-picker-view.jsx +149 -54
- package/lib/runtime/components/react/dist/mpx-radio-group.jsx +20 -18
- package/lib/runtime/components/react/dist/mpx-radio.jsx +29 -43
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +8 -4
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +36 -27
- package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +141 -75
- package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +16 -7
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +20 -11
- package/lib/runtime/components/react/dist/mpx-switch.jsx +18 -14
- package/lib/runtime/components/react/dist/mpx-text.jsx +20 -35
- package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-view.jsx +296 -210
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +11 -7
- package/lib/runtime/components/react/dist/parser.js +218 -0
- package/lib/runtime/components/react/dist/useNodesRef.js +1 -5
- package/lib/runtime/components/react/dist/utils.jsx +445 -0
- package/lib/runtime/components/react/getInnerListeners.ts +18 -8
- package/lib/runtime/components/react/mpx-button.tsx +83 -91
- package/lib/runtime/components/react/mpx-checkbox-group.tsx +50 -43
- package/lib/runtime/components/react/mpx-checkbox.tsx +56 -64
- package/lib/runtime/components/react/mpx-form.tsx +51 -22
- package/lib/runtime/components/react/mpx-icon.tsx +31 -27
- package/lib/runtime/components/react/mpx-image/index.tsx +54 -47
- package/lib/runtime/components/react/mpx-image/svg.tsx +5 -3
- package/lib/runtime/components/react/mpx-input.tsx +59 -38
- package/lib/runtime/components/react/mpx-label.tsx +55 -59
- package/lib/runtime/components/react/mpx-movable-area.tsx +40 -25
- package/lib/runtime/components/react/mpx-movable-view.tsx +29 -29
- package/lib/runtime/components/react/mpx-navigator.tsx +2 -2
- package/lib/runtime/components/react/mpx-picker/date.tsx +4 -4
- package/lib/runtime/components/react/mpx-picker/index.tsx +12 -11
- package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +17 -13
- package/lib/runtime/components/react/mpx-picker/region.tsx +23 -19
- package/lib/runtime/components/react/mpx-picker/selector.tsx +7 -7
- package/lib/runtime/components/react/mpx-picker/time.tsx +29 -31
- package/lib/runtime/components/react/mpx-picker/type.ts +1 -1
- package/lib/runtime/components/react/mpx-picker-view-column.tsx +149 -20
- package/lib/runtime/components/react/mpx-picker-view.tsx +180 -63
- package/lib/runtime/components/react/mpx-radio-group.tsx +51 -47
- package/lib/runtime/components/react/mpx-radio.tsx +57 -72
- package/lib/runtime/components/react/mpx-root-portal.tsx +10 -8
- package/lib/runtime/components/react/mpx-scroll-view.tsx +136 -104
- package/lib/runtime/components/react/mpx-swiper/carouse.tsx +175 -96
- package/lib/runtime/components/react/mpx-swiper/index.tsx +21 -9
- package/lib/runtime/components/react/mpx-swiper/type.ts +16 -5
- package/lib/runtime/components/react/mpx-swiper-item.tsx +48 -14
- package/lib/runtime/components/react/mpx-switch.tsx +46 -24
- package/lib/runtime/components/react/mpx-text.tsx +38 -45
- package/lib/runtime/components/react/mpx-textarea.tsx +1 -1
- package/lib/runtime/components/react/mpx-view.tsx +401 -241
- package/lib/runtime/components/react/mpx-web-view.tsx +22 -22
- package/lib/runtime/components/react/parser.ts +245 -0
- package/lib/runtime/components/react/types/common.ts +4 -4
- package/lib/runtime/components/react/types/global.d.ts +24 -2
- package/lib/runtime/components/react/useNodesRef.ts +1 -7
- package/lib/runtime/components/react/utils.tsx +524 -0
- package/lib/runtime/components/web/mpx-scroll-view.vue +25 -5
- package/lib/style-compiler/index.js +5 -4
- package/lib/template-compiler/compiler.js +133 -161
- package/lib/template-compiler/gen-node-react.js +1 -3
- package/lib/utils/const.js +2 -1
- package/lib/web/processStyles.js +2 -1
- package/lib/web/processTemplate.js +2 -3
- package/lib/wxml/loader.js +1 -1
- package/package.json +7 -4
- package/lib/runtime/components/react/dist/utils.js +0 -148
- 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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
|
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 (
|
|
57
|
+
if (isReact(mode)) {
|
|
57
58
|
plugins.push(transSpecial({ id }))
|
|
58
59
|
}
|
|
59
60
|
|