@mpxjs/webpack-plugin 2.10.15 → 2.10.16-beta.2
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/dependencies/AppEntryDependency.js +2 -2
- package/lib/dependencies/DynamicEntryDependency.js +1 -1
- package/lib/dependencies/ImportDependency.js +102 -0
- package/lib/dependencies/RecordModuleIdMapDependency.js +49 -0
- package/lib/dependencies/ResolveDependency.js +1 -1
- package/lib/{retry-runtime-module.js → dependencies/RetryRuntimeModule.js} +1 -1
- package/lib/helpers.js +2 -0
- package/lib/index.js +51 -25
- package/lib/json-compiler/helper.js +72 -2
- package/lib/json-compiler/index.js +14 -54
- package/lib/json-compiler/plugin.js +2 -2
- package/lib/loader.js +6 -2
- package/lib/native-loader.js +6 -3
- package/lib/platform/json/wx/index.js +24 -29
- package/lib/platform/style/wx/index.js +8 -1
- package/lib/platform/template/wx/component-config/button.js +12 -3
- package/lib/platform/template/wx/component-config/camera.js +12 -0
- package/lib/platform/template/wx/component-config/component.js +31 -33
- package/lib/platform/template/wx/component-config/slider.js +12 -0
- package/lib/platform/template/wx/component-config/unsupported.js +1 -1
- package/lib/react/processJSON.js +39 -71
- package/lib/react/processStyles.js +3 -2
- package/lib/react/processTemplate.js +6 -6
- package/lib/react/script-helper.js +6 -16
- package/lib/react/style-helper.js +10 -2
- package/lib/resolver/AddEnvPlugin.js +13 -0
- package/lib/resolver/AddModePlugin.js +18 -0
- package/lib/runtime/components/react/context.ts +2 -0
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/mpx-camera.jsx +102 -0
- package/lib/runtime/components/react/dist/mpx-image.jsx +81 -37
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +19 -4
- package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +3 -2
- package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +9 -6
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.jsx +8 -11
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
- package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
- package/lib/runtime/components/react/dist/mpx-progress.jsx +26 -22
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -14
- package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
- package/lib/runtime/components/react/dist/mpx-text.jsx +33 -5
- package/lib/runtime/components/react/dist/mpx-view.jsx +8 -11
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +1 -1
- package/lib/runtime/components/react/dist/utils.jsx +16 -6
- package/lib/runtime/components/react/mpx-camera.tsx +167 -0
- package/lib/runtime/components/react/mpx-image.tsx +89 -42
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +31 -4
- package/lib/runtime/components/react/mpx-picker-view/index.tsx +4 -1
- package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +19 -8
- package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItem.tsx +8 -12
- package/lib/runtime/components/react/mpx-picker-view-column/pickerViewColumnItemLite.tsx +55 -0
- package/lib/runtime/components/react/mpx-portal/index.tsx +8 -2
- package/lib/runtime/components/react/mpx-progress.tsx +26 -24
- package/lib/runtime/components/react/mpx-scroll-view.tsx +6 -17
- package/lib/runtime/components/react/mpx-slider.tsx +444 -0
- package/lib/runtime/components/react/mpx-text.tsx +38 -5
- package/lib/runtime/components/react/mpx-view.tsx +8 -11
- package/lib/runtime/components/react/mpx-web-view.tsx +1 -1
- package/lib/runtime/components/react/utils.tsx +15 -6
- package/lib/runtime/components/web/mpx-input.vue +1 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +7 -1
- package/lib/runtime/components/web/mpx-video.vue +12 -1
- package/lib/runtime/optionProcessor.js +3 -1
- package/lib/runtime/optionProcessorReact.js +4 -2
- package/lib/script-setup-compiler/index.js +2 -2
- package/lib/style-compiler/index.js +3 -2
- package/lib/style-compiler/load-postcss-config.js +1 -1
- package/lib/style-compiler/plugins/trans-special.js +10 -2
- package/lib/style-compiler/strip-conditional-loader.js +155 -15
- package/lib/template-compiler/compiler.js +262 -61
- package/lib/template-compiler/gen-node-react.js +18 -6
- package/lib/template-compiler/index.js +6 -4
- package/lib/template-compiler/parse-exps.js +1 -1
- package/lib/utils/chain-assign.js +47 -0
- package/lib/utils/check-core-version-match.js +75 -15
- package/lib/utils/const.js +2 -1
- package/lib/utils/dom-tag-config.js +1 -1
- package/lib/utils/env.js +6 -1
- package/lib/utils/get-build-tag-component.js +35 -0
- package/lib/utils/pre-process-json.js +5 -0
- package/lib/web/processJSON.js +44 -16
- package/lib/web/processScript.js +1 -1
- package/lib/web/processTemplate.js +4 -4
- package/lib/web/script-helper.js +19 -9
- package/lib/wxs/pre-loader.js +5 -5
- package/lib/wxss/loader.js +1 -9
- package/package.json +14 -5
- package/LICENSE +0 -433
- package/lib/dependencies/ImportDependencyTemplate.js +0 -50
|
@@ -16,6 +16,8 @@ module.exports = class AddEnvPlugin {
|
|
|
16
16
|
apply (resolver) {
|
|
17
17
|
const target = resolver.ensureHook(this.target)
|
|
18
18
|
const env = this.env
|
|
19
|
+
const envPattern = new RegExp(`\\.${env}(\\.|$)`)
|
|
20
|
+
|
|
19
21
|
resolver.getHook(this.source).tapAsync('AddEnvPlugin', (request, resolveContext, callback) => {
|
|
20
22
|
if (request.env) {
|
|
21
23
|
return callback()
|
|
@@ -32,11 +34,22 @@ module.exports = class AddEnvPlugin {
|
|
|
32
34
|
}
|
|
33
35
|
// 当前资源没有后缀名或者路径不符合fileConditionRules规则时,直接返回
|
|
34
36
|
if (!extname || !matchCondition(resourcePath, this.fileConditionRules)) return callback()
|
|
37
|
+
|
|
35
38
|
const queryObj = parseQuery(request.query || '?')
|
|
36
39
|
queryObj.infix = `${queryObj.infix || ''}.${env}`
|
|
40
|
+
|
|
41
|
+
const resourceBasename = path.basename(resourcePath)
|
|
42
|
+
// 如果 infix 与 resourcePath 无法匹配,则认为未命中env
|
|
43
|
+
if (envPattern.test(resourceBasename) && resourceBasename.includes(queryObj.infix)) {
|
|
44
|
+
request.query = stringifyQuery(queryObj)
|
|
45
|
+
request.env = obj.env
|
|
46
|
+
return callback()
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
obj.query = stringifyQuery(queryObj)
|
|
38
50
|
obj.path = addInfix(resourcePath, env, extname)
|
|
39
51
|
obj.relativePath = request.relativePath && addInfix(request.relativePath, env, extname)
|
|
52
|
+
|
|
40
53
|
resolver.doResolve(target, Object.assign({}, request, obj), 'add env: ' + env, resolveContext, callback)
|
|
41
54
|
})
|
|
42
55
|
}
|
|
@@ -17,6 +17,9 @@ module.exports = class AddModePlugin {
|
|
|
17
17
|
const target = resolver.ensureHook(this.target)
|
|
18
18
|
const { options = {}, mode } = this
|
|
19
19
|
const { defaultMode, fileConditionRules, implicitMode } = options
|
|
20
|
+
const modePattern = new RegExp(`\\.${mode}(\\.|$)`)
|
|
21
|
+
const defaultModePattern = new RegExp(`\\.${defaultMode}(\\.|$)`)
|
|
22
|
+
|
|
20
23
|
resolver.getHook(this.source).tapAsync('AddModePlugin', (request, resolveContext, callback) => {
|
|
21
24
|
if (request.mode || request.env) {
|
|
22
25
|
return callback()
|
|
@@ -33,13 +36,28 @@ module.exports = class AddModePlugin {
|
|
|
33
36
|
}
|
|
34
37
|
// 当前资源没有后缀名或者路径不符合fileConditionRules规则时,直接返回
|
|
35
38
|
if (!extname || !matchCondition(resourcePath, fileConditionRules)) return callback()
|
|
39
|
+
|
|
36
40
|
const queryObj = parseQuery(request.query || '?')
|
|
37
41
|
const queryInfix = queryObj.infix
|
|
38
42
|
if (!implicitMode) queryObj.mode = mode
|
|
39
43
|
queryObj.infix = `${queryInfix || ''}.${mode}`
|
|
44
|
+
|
|
45
|
+
// 如果已经确认是mode后缀的文件,添加query与mode后直接返回
|
|
46
|
+
if (modePattern.test(path.basename(resourcePath))) {
|
|
47
|
+
request.query = stringifyQuery(queryObj)
|
|
48
|
+
request.mode = obj.mode
|
|
49
|
+
return callback()
|
|
50
|
+
} else if (defaultMode && defaultModePattern.test(path.basename(resourcePath))) {
|
|
51
|
+
queryObj.infix = `${queryInfix || ''}.${defaultMode}`
|
|
52
|
+
request.query = stringifyQuery(queryObj)
|
|
53
|
+
request.mode = obj.mode
|
|
54
|
+
return callback()
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
obj.query = stringifyQuery(queryObj)
|
|
41
58
|
obj.path = addInfix(resourcePath, mode, extname)
|
|
42
59
|
obj.relativePath = request.relativePath && addInfix(request.relativePath, mode, extname)
|
|
60
|
+
|
|
43
61
|
resolver.doResolve(target, Object.assign({}, request, obj), 'add mode: ' + mode, resolveContext, (err, result) => {
|
|
44
62
|
if (defaultMode && !result) {
|
|
45
63
|
queryObj.infix = `${queryInfix || ''}.${defaultMode}`
|
|
@@ -84,3 +84,5 @@ export const ScrollViewContext = createContext<ScrollViewContextValue>({ gesture
|
|
|
84
84
|
export const PortalContext = createContext<PortalContextValue>(null as any)
|
|
85
85
|
|
|
86
86
|
export const StickyContext = createContext<StickyContextValue>({ registerStickyHeader: noop, unregisterStickyHeader: noop })
|
|
87
|
+
|
|
88
|
+
export const ProviderContext = createContext(null)
|
|
@@ -15,3 +15,4 @@ export const KeyboardAvoidContext = createContext(null);
|
|
|
15
15
|
export const ScrollViewContext = createContext({ gestureRef: null, scrollOffset: new Animated.Value(0) });
|
|
16
16
|
export const PortalContext = createContext(null);
|
|
17
17
|
export const StickyContext = createContext({ registerStickyHeader: noop, unregisterStickyHeader: noop });
|
|
18
|
+
export const ProviderContext = createContext(null);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { forwardRef, useRef, useCallback, useContext, useState, useEffect } from 'react';
|
|
2
|
+
import { Camera, useCameraDevice, useCodeScanner, useCameraFormat } from 'react-native-vision-camera';
|
|
3
|
+
import { getCustomEvent } from './getInnerListeners';
|
|
4
|
+
import { RouteContext } from './context';
|
|
5
|
+
const _camera = forwardRef((props, ref) => {
|
|
6
|
+
const cameraRef = useRef(null);
|
|
7
|
+
const { mode = 'normal', resolution = 'medium', devicePosition = 'back', flash = 'auto', frameSize = 'medium', bindinitdone, bindstop, bindscancode } = props;
|
|
8
|
+
const isPhoto = mode === 'normal';
|
|
9
|
+
const device = useCameraDevice(devicePosition || 'back');
|
|
10
|
+
const { navigation } = useContext(RouteContext) || {};
|
|
11
|
+
const [zoomValue, setZoomValue] = useState(1);
|
|
12
|
+
const [hasPermission, setHasPermission] = useState(null);
|
|
13
|
+
// 先定义常量,避免在条件判断后使用
|
|
14
|
+
const maxZoom = device?.maxZoom || 1;
|
|
15
|
+
const RESOLUTION_MAPPING = {
|
|
16
|
+
low: { width: 640, height: 480 },
|
|
17
|
+
medium: { width: 1280, height: 720 },
|
|
18
|
+
high: { width: 1920, height: 1080 }
|
|
19
|
+
};
|
|
20
|
+
const FRAME_SIZE_MAPPING = {
|
|
21
|
+
small: { width: 480, height: 360 },
|
|
22
|
+
medium: { width: 720, height: 540 },
|
|
23
|
+
large: { width: 1080, height: 810 }
|
|
24
|
+
};
|
|
25
|
+
// 所有 Hooks 必须在条件判断之前调用
|
|
26
|
+
const format = useCameraFormat(device, [
|
|
27
|
+
{
|
|
28
|
+
photoResolution: RESOLUTION_MAPPING[resolution],
|
|
29
|
+
videoResolution: FRAME_SIZE_MAPPING[frameSize] || RESOLUTION_MAPPING[resolution]
|
|
30
|
+
}
|
|
31
|
+
]);
|
|
32
|
+
const codeScanner = useCodeScanner({
|
|
33
|
+
codeTypes: ['qr', 'ean-13'],
|
|
34
|
+
onCodeScanned: (codes) => {
|
|
35
|
+
const result = codes.map(code => code.value).join(',');
|
|
36
|
+
bindscancode && bindscancode(getCustomEvent('scancode', {}, {
|
|
37
|
+
detail: {
|
|
38
|
+
result: codes.map(code => code.value).join(',')
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const onInitialized = useCallback(() => {
|
|
44
|
+
bindinitdone && bindinitdone(getCustomEvent('initdone', {}, {
|
|
45
|
+
detail: {
|
|
46
|
+
maxZoom
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
}, [bindinitdone, maxZoom]);
|
|
50
|
+
const onStopped = useCallback(() => {
|
|
51
|
+
bindstop && bindstop();
|
|
52
|
+
}, [bindstop]);
|
|
53
|
+
// 检查相机权限
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const checkCameraPermission = async () => {
|
|
56
|
+
try {
|
|
57
|
+
const cameraPermission = global?.__mpx?.config?.rnConfig?.cameraPermission;
|
|
58
|
+
if (typeof cameraPermission === 'function') {
|
|
59
|
+
const permissionResult = await cameraPermission();
|
|
60
|
+
setHasPermission(permissionResult === true);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
setHasPermission(true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
setHasPermission(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
checkCameraPermission();
|
|
71
|
+
}, []);
|
|
72
|
+
const camera = {
|
|
73
|
+
setZoom: (zoom) => {
|
|
74
|
+
setZoomValue(zoom);
|
|
75
|
+
},
|
|
76
|
+
getTakePhoto: () => {
|
|
77
|
+
return cameraRef.current?.takePhoto;
|
|
78
|
+
},
|
|
79
|
+
getStartRecord: () => {
|
|
80
|
+
return cameraRef.current?.startRecording;
|
|
81
|
+
},
|
|
82
|
+
getStopRecord: () => {
|
|
83
|
+
return cameraRef.current?.stopRecording;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
if (navigation) {
|
|
87
|
+
navigation.camera = camera;
|
|
88
|
+
}
|
|
89
|
+
// 所有 Hooks 调用完成后再进行条件判断
|
|
90
|
+
if (hasPermission === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (!hasPermission) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (!device) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return (<Camera ref={cameraRef} isActive={true} photo={isPhoto} video={true} onInitialized={onInitialized} onStopped={onStopped} device={device} flash={flash} format={format} codeScanner={!isPhoto ? codeScanner : undefined} style={{ flex: 1 }} zoom={zoomValue} {...props}/>);
|
|
100
|
+
});
|
|
101
|
+
_camera.displayName = 'MpxCamera';
|
|
102
|
+
export default _camera;
|
|
@@ -40,7 +40,16 @@ const ModeMap = new Map([
|
|
|
40
40
|
...cropMode.map(mode => [mode, 'stretch'])
|
|
41
41
|
]);
|
|
42
42
|
const isNumber = (value) => typeof value === 'number';
|
|
43
|
-
const relativeCenteredSize = (viewSize, imageSize) =>
|
|
43
|
+
const relativeCenteredSize = (viewSize, imageSize) => {
|
|
44
|
+
return (viewSize - imageSize) / 2;
|
|
45
|
+
};
|
|
46
|
+
// 获取能完全显示图片的缩放比例:长宽方向的缩放比例最小值即为能完全展示的比例
|
|
47
|
+
function getFitScale(width1, height1, width2, height2) {
|
|
48
|
+
return Math.min(width2 / width1, height2 / height1);
|
|
49
|
+
}
|
|
50
|
+
function getFillScale(width1, height1, width2, height2) {
|
|
51
|
+
return Math.max(width2 / width1, height2 / height1);
|
|
52
|
+
}
|
|
44
53
|
function noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio) {
|
|
45
54
|
const isMeetSize = viewWidth && viewHeight && ratio;
|
|
46
55
|
if (isSvg && !isMeetSize)
|
|
@@ -49,6 +58,16 @@ function noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio) {
|
|
|
49
58
|
return true;
|
|
50
59
|
return false;
|
|
51
60
|
}
|
|
61
|
+
const getFixedWidth = (viewWidth, viewHeight, ratio) => {
|
|
62
|
+
if (!ratio)
|
|
63
|
+
return viewWidth;
|
|
64
|
+
const fixed = viewHeight / ratio;
|
|
65
|
+
return !fixed ? viewWidth : fixed;
|
|
66
|
+
};
|
|
67
|
+
const getFixedHeight = (viewWidth, viewHeight, ratio) => {
|
|
68
|
+
const fixed = viewWidth * ratio;
|
|
69
|
+
return !fixed ? viewHeight : fixed;
|
|
70
|
+
};
|
|
52
71
|
const Image = forwardRef((props, ref) => {
|
|
53
72
|
const { src = '', mode = 'scaleToFill', style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'enable-fast-image': enableFastImage, 'parent-width': parentWidth, 'parent-height': parentHeight, bindload, binderror } = props;
|
|
54
73
|
const defaultStyle = {
|
|
@@ -56,7 +75,6 @@ const Image = forwardRef((props, ref) => {
|
|
|
56
75
|
height: DEFAULT_IMAGE_HEIGHT
|
|
57
76
|
};
|
|
58
77
|
const styleObj = extendObject({}, defaultStyle, style, { overflow: 'hidden' });
|
|
59
|
-
const state = useRef({});
|
|
60
78
|
const nodeRef = useRef(null);
|
|
61
79
|
useNodesRef(props, ref, nodeRef, {
|
|
62
80
|
defaultStyle
|
|
@@ -70,13 +88,19 @@ const Image = forwardRef((props, ref) => {
|
|
|
70
88
|
const onLayout = ({ nativeEvent: { layout: { width, height } } }) => {
|
|
71
89
|
state.current.viewWidth = width;
|
|
72
90
|
state.current.viewHeight = height;
|
|
91
|
+
// 实际渲染尺寸可能会指定的值不一致,误差低于 0.5 则认为没有变化
|
|
92
|
+
if (Math.abs(viewHeight - height) < 0.5 && Math.abs(viewWidth - width) < 0.5) {
|
|
93
|
+
if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
|
|
94
|
+
if (!loaded)
|
|
95
|
+
setLoaded(true);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
73
99
|
if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
|
|
74
|
-
setViewWidth(width);
|
|
75
|
-
setViewHeight(height);
|
|
76
100
|
setRatio(state.current.ratio);
|
|
77
101
|
setImageWidth(state.current.imageWidth);
|
|
78
102
|
setImageHeight(state.current.imageHeight);
|
|
79
|
-
state.current
|
|
103
|
+
setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
|
|
80
104
|
setLoaded(true);
|
|
81
105
|
}
|
|
82
106
|
};
|
|
@@ -96,43 +120,56 @@ const Image = forwardRef((props, ref) => {
|
|
|
96
120
|
const [imageHeight, setImageHeight] = useState(0);
|
|
97
121
|
const [ratio, setRatio] = useState(0);
|
|
98
122
|
const [loaded, setLoaded] = useState(!isLayoutMode);
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
123
|
+
const state = useRef({
|
|
124
|
+
viewWidth,
|
|
125
|
+
viewHeight
|
|
126
|
+
});
|
|
127
|
+
function setViewSize(viewWidth, viewHeight, ratio) {
|
|
128
|
+
// 在特定模式下可预测 view 的变化,在onLayout触发时能以此避免重复render
|
|
129
|
+
switch (mode) {
|
|
130
|
+
case 'widthFix': {
|
|
131
|
+
setViewWidth(viewWidth);
|
|
132
|
+
const fixedHeight = getFixedHeight(viewWidth, viewHeight, ratio);
|
|
133
|
+
setViewHeight(fixedHeight);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'heightFix': {
|
|
137
|
+
setViewHeight(viewHeight);
|
|
138
|
+
const fixedWidth = getFixedWidth(viewWidth, viewHeight, ratio);
|
|
139
|
+
setViewWidth(fixedWidth);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
setViewHeight(viewHeight);
|
|
144
|
+
setViewWidth(viewWidth);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
109
148
|
const modeStyle = useMemo(() => {
|
|
110
149
|
if (noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio))
|
|
111
150
|
return {};
|
|
112
151
|
switch (mode) {
|
|
113
|
-
case 'scaleToFill':
|
|
152
|
+
case 'scaleToFill': // wx 中 svg 图片的 scaleToFill 模式效果与 aspectFit 一致,不会就行图片缩放,此处保持一致
|
|
114
153
|
case 'aspectFit':
|
|
115
154
|
if (isSvg) {
|
|
116
|
-
const scale =
|
|
117
|
-
? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
|
|
118
|
-
: imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight;
|
|
155
|
+
const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight);
|
|
119
156
|
return {
|
|
120
157
|
transform: [
|
|
121
|
-
{ scale },
|
|
122
|
-
|
|
158
|
+
{ translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
|
|
159
|
+
{ translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
|
|
160
|
+
{ scale }
|
|
123
161
|
]
|
|
124
162
|
};
|
|
125
163
|
}
|
|
126
164
|
return {};
|
|
127
165
|
case 'aspectFill':
|
|
128
166
|
if (isSvg) {
|
|
129
|
-
const scale =
|
|
130
|
-
? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
|
|
131
|
-
: imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight;
|
|
167
|
+
const scale = getFillScale(imageWidth, imageHeight, viewWidth, viewHeight);
|
|
132
168
|
return {
|
|
133
169
|
transform: [
|
|
134
|
-
{ scale },
|
|
135
|
-
|
|
170
|
+
{ translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
|
|
171
|
+
{ translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
|
|
172
|
+
{ scale }
|
|
136
173
|
]
|
|
137
174
|
};
|
|
138
175
|
}
|
|
@@ -140,9 +177,7 @@ const Image = forwardRef((props, ref) => {
|
|
|
140
177
|
case 'widthFix':
|
|
141
178
|
case 'heightFix':
|
|
142
179
|
if (isSvg) {
|
|
143
|
-
const scale =
|
|
144
|
-
? imageWidth >= fixedWidth ? fixedWidth / imageWidth : imageWidth / fixedWidth
|
|
145
|
-
: imageHeight >= fixedHeight ? fixedHeight / imageHeight : imageHeight / fixedHeight;
|
|
180
|
+
const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight);
|
|
146
181
|
return {
|
|
147
182
|
transform: [{ scale }]
|
|
148
183
|
};
|
|
@@ -205,12 +240,23 @@ const Image = forwardRef((props, ref) => {
|
|
|
205
240
|
default:
|
|
206
241
|
return {};
|
|
207
242
|
}
|
|
208
|
-
}, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio
|
|
243
|
+
}, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio]);
|
|
209
244
|
const onSvgLoad = (evt) => {
|
|
210
245
|
const { width, height } = evt.nativeEvent.layout;
|
|
211
|
-
|
|
212
|
-
setImageWidth(width);
|
|
246
|
+
state.current.imageHeight = height;
|
|
213
247
|
setImageHeight(height);
|
|
248
|
+
state.current.ratio = !width ? 0 : height / width;
|
|
249
|
+
if (isWidthFixMode
|
|
250
|
+
? state.current.viewWidth
|
|
251
|
+
: isHeightFixMode
|
|
252
|
+
? state.current.viewHeight
|
|
253
|
+
: state.current.viewWidth && state.current.viewHeight) {
|
|
254
|
+
setRatio(state.current.ratio);
|
|
255
|
+
setImageWidth(width);
|
|
256
|
+
setImageHeight(height);
|
|
257
|
+
setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
|
|
258
|
+
setLoaded(true);
|
|
259
|
+
}
|
|
214
260
|
bindload && bindload(getCustomEvent('load', evt, {
|
|
215
261
|
detail: { width, height },
|
|
216
262
|
layoutRef
|
|
@@ -248,12 +294,10 @@ const Image = forwardRef((props, ref) => {
|
|
|
248
294
|
: isHeightFixMode
|
|
249
295
|
? state.current.viewHeight
|
|
250
296
|
: state.current.viewWidth && state.current.viewHeight) {
|
|
251
|
-
|
|
252
|
-
state.current.viewHeight && setViewHeight(state.current.viewHeight);
|
|
253
|
-
setRatio(!width ? 0 : height / width);
|
|
297
|
+
setRatio(state.current.ratio);
|
|
254
298
|
setImageWidth(width);
|
|
255
299
|
setImageHeight(height);
|
|
256
|
-
state.current
|
|
300
|
+
setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
|
|
257
301
|
setLoaded(true);
|
|
258
302
|
}
|
|
259
303
|
}, () => {
|
|
@@ -263,7 +307,7 @@ const Image = forwardRef((props, ref) => {
|
|
|
263
307
|
}, [src, isSvg, isLayoutMode]);
|
|
264
308
|
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
265
309
|
ref: nodeRef,
|
|
266
|
-
style: extendObject({}, normalStyle, layoutStyle, isHeightFixMode ? { width:
|
|
310
|
+
style: extendObject({}, normalStyle, layoutStyle, isHeightFixMode ? { width: viewWidth } : {}, isWidthFixMode ? { height: viewHeight } : {})
|
|
267
311
|
}), [
|
|
268
312
|
'src',
|
|
269
313
|
'mode',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, { useContext, useEffect } from 'react';
|
|
1
|
+
import React, { useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import { Keyboard, View } from 'react-native';
|
|
3
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
3
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, cancelAnimation } from 'react-native-reanimated';
|
|
4
4
|
import { KeyboardAvoidContext } from './context';
|
|
5
5
|
import { isIOS } from './utils';
|
|
6
6
|
const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
@@ -9,14 +9,23 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
9
9
|
const offset = useSharedValue(0);
|
|
10
10
|
const basic = useSharedValue('auto');
|
|
11
11
|
const keyboardAvoid = useContext(KeyboardAvoidContext);
|
|
12
|
+
// fix: 某些特殊机型下隐藏键盘可能会先触发一次 keyboardWillShow,
|
|
13
|
+
// 比如机型 iPhone 11 Pro,可能会导致显隐动画冲突
|
|
14
|
+
// 因此增加状态标记 + cancelAnimation 来优化
|
|
15
|
+
const isShow = useRef(false);
|
|
12
16
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
13
17
|
transform: [{ translateY: -offset.value }],
|
|
14
18
|
flexBasis: basic.value
|
|
15
19
|
}));
|
|
16
20
|
const resetKeyboard = () => {
|
|
21
|
+
if (!isShow.current) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
isShow.current = false;
|
|
17
25
|
if (keyboardAvoid?.current) {
|
|
18
26
|
keyboardAvoid.current = null;
|
|
19
27
|
}
|
|
28
|
+
cancelAnimation(offset);
|
|
20
29
|
offset.value = withTiming(0, { duration, easing });
|
|
21
30
|
basic.value = 'auto';
|
|
22
31
|
};
|
|
@@ -30,8 +39,10 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
30
39
|
if (isIOS) {
|
|
31
40
|
subscriptions = [
|
|
32
41
|
Keyboard.addListener('keyboardWillShow', (evt) => {
|
|
33
|
-
if (!keyboardAvoid?.current)
|
|
42
|
+
if (!keyboardAvoid?.current || isShow.current) {
|
|
34
43
|
return;
|
|
44
|
+
}
|
|
45
|
+
isShow.current = true;
|
|
35
46
|
const { endCoordinates } = evt;
|
|
36
47
|
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
37
48
|
setTimeout(() => {
|
|
@@ -40,6 +51,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
40
51
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
41
52
|
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
|
|
42
53
|
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
54
|
+
cancelAnimation(offset);
|
|
43
55
|
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
44
56
|
if (finished) {
|
|
45
57
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
@@ -55,8 +67,10 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
55
67
|
else {
|
|
56
68
|
subscriptions = [
|
|
57
69
|
Keyboard.addListener('keyboardDidShow', (evt) => {
|
|
58
|
-
if (!keyboardAvoid?.current)
|
|
70
|
+
if (!keyboardAvoid?.current || isShow.current) {
|
|
59
71
|
return;
|
|
72
|
+
}
|
|
73
|
+
isShow.current = true;
|
|
60
74
|
const { endCoordinates } = evt;
|
|
61
75
|
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
62
76
|
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
@@ -65,6 +79,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
|
65
79
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
66
80
|
const belowValue = Math.min(belowOffset, cursorSpacing);
|
|
67
81
|
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
82
|
+
cancelAnimation(offset);
|
|
68
83
|
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
69
84
|
if (finished) {
|
|
70
85
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
@@ -17,7 +17,7 @@ const styles = {
|
|
|
17
17
|
};
|
|
18
18
|
const DefaultPickerItemH = 36;
|
|
19
19
|
const _PickerView = forwardRef((props, ref) => {
|
|
20
|
-
const { children, value = [], bindchange, style, 'indicator-style': indicatorStyle = {}, 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
|
|
20
|
+
const { children, value = [], bindchange, style, 'enable-wheel-animation': enableWheelAnimation = true, 'indicator-style': indicatorStyle = {}, 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
|
|
21
21
|
const { height: indicatorH, ...pickerIndicatorStyle } = indicatorStyle;
|
|
22
22
|
const nodeRef = useRef(null);
|
|
23
23
|
const cloneRef = useRef(null);
|
|
@@ -75,7 +75,8 @@ const _PickerView = forwardRef((props, ref) => {
|
|
|
75
75
|
onSelectChange: onSelectChange.bind(null, index),
|
|
76
76
|
initialIndex,
|
|
77
77
|
pickerIndicatorStyle,
|
|
78
|
-
pickerMaskStyle
|
|
78
|
+
pickerMaskStyle,
|
|
79
|
+
enableWheelAnimation
|
|
79
80
|
});
|
|
80
81
|
const realElement = React.cloneElement(child, wrappedProps);
|
|
81
82
|
return wrapChildren({
|
|
@@ -5,12 +5,13 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAn
|
|
|
5
5
|
import useNodesRef from '../useNodesRef';
|
|
6
6
|
import PickerIndicator from './pickerViewIndicator';
|
|
7
7
|
import PickerMask from './pickerViewMask';
|
|
8
|
-
import
|
|
8
|
+
import MpxPickerViewColumnItem from './pickerViewColumnItem';
|
|
9
|
+
import MpxPickerViewColumnItemLite from './pickerViewColumnItemLite';
|
|
9
10
|
import { PickerViewColumnAnimationContext } from '../mpx-picker-view/pickerVIewContext';
|
|
10
11
|
import { calcHeightOffsets } from './pickerViewFaces';
|
|
11
12
|
const visibleCount = 5;
|
|
12
13
|
const _PickerViewColumn = forwardRef((props, ref) => {
|
|
13
|
-
const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
|
|
14
|
+
const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, enableWheelAnimation = true, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
|
|
14
15
|
const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
|
|
15
16
|
const { textStyle = {} } = splitStyle(normalStyle);
|
|
16
17
|
const { textProps = {} } = splitProps(props);
|
|
@@ -217,7 +218,9 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
217
218
|
}
|
|
218
219
|
}, [itemRawH, maxIndex, calcOffset, onMomentumScrollEnd]);
|
|
219
220
|
const renderInnerchild = () => columnData.map((item, index) => {
|
|
220
|
-
return
|
|
221
|
+
return enableWheelAnimation
|
|
222
|
+
? (<MpxPickerViewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>)
|
|
223
|
+
: (<MpxPickerViewColumnItemLite key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} onItemLayout={onItemLayout}/>);
|
|
221
224
|
});
|
|
222
225
|
const renderScollView = () => {
|
|
223
226
|
const innerProps = extendObject({}, layoutProps, {
|
|
@@ -246,9 +249,9 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
246
249
|
const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
|
|
247
250
|
const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
|
|
248
251
|
return (<View style={[styles.wrapper, normalStyle]}>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
{renderScollView()}
|
|
253
|
+
{renderMask()}
|
|
254
|
+
{renderIndicator()}
|
|
252
255
|
</View>);
|
|
253
256
|
});
|
|
254
257
|
const styles = StyleSheet.create({
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import Reanimated, { Extrapolation, interpolate, useAnimatedStyle
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import Reanimated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated';
|
|
3
3
|
import { extendObject } from '../utils';
|
|
4
4
|
import { createFaces } from './pickerViewFaces';
|
|
5
5
|
import { usePickerViewColumnAnimationContext, usePickerViewStyleContext } from '../mpx-picker-view/pickerVIewContext';
|
|
6
6
|
const PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', textStyle, textProps, visibleCount, onItemLayout }) => {
|
|
7
7
|
const textStyleFromAncestor = usePickerViewStyleContext();
|
|
8
8
|
const offsetYShared = usePickerViewColumnAnimationContext();
|
|
9
|
-
const facesShared =
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
facesShared.value = createFaces(itemHeight, visibleCount);
|
|
12
|
-
}, [itemHeight]);
|
|
9
|
+
const facesShared = useMemo(() => createFaces(itemHeight, visibleCount), [itemHeight, visibleCount]);
|
|
13
10
|
const animatedStyles = useAnimatedStyle(() => {
|
|
14
|
-
const inputRange = facesShared.
|
|
11
|
+
const inputRange = facesShared.map((f) => itemHeight * (index + f.index));
|
|
15
12
|
return {
|
|
16
|
-
opacity: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
13
|
+
opacity: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.opacity), Extrapolation.CLAMP),
|
|
17
14
|
transform: [
|
|
18
|
-
{ translateY: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
19
|
-
{ rotateX: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
20
|
-
{ scale: interpolate(offsetYShared.value, inputRange, facesShared.
|
|
15
|
+
{ translateY: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.offsetY), Extrapolation.EXTEND) },
|
|
16
|
+
{ rotateX: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
|
|
17
|
+
{ scale: interpolate(offsetYShared.value, inputRange, facesShared.map((x) => x.scale), Extrapolation.EXTEND) }
|
|
21
18
|
]
|
|
22
19
|
};
|
|
23
20
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { extendObject } from '../utils';
|
|
4
|
+
import { usePickerViewStyleContext } from '../mpx-picker-view/pickerVIewContext';
|
|
5
|
+
const PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', textStyle, textProps, onItemLayout }) => {
|
|
6
|
+
const textStyleFromAncestor = usePickerViewStyleContext();
|
|
7
|
+
const strKey = `picker-column-item-${index}`;
|
|
8
|
+
const restProps = index === 0 ? { onLayout: onItemLayout } : {};
|
|
9
|
+
const itemProps = extendObject({
|
|
10
|
+
style: extendObject({ height: itemHeight, width: '100%' }, textStyleFromAncestor, textStyle, item.props.style)
|
|
11
|
+
}, textProps, restProps);
|
|
12
|
+
const realItem = React.cloneElement(item, itemProps);
|
|
13
|
+
return (<View key={strKey} style={[
|
|
14
|
+
{ height: itemHeight, width: itemWidth, pointerEvents: 'none' }
|
|
15
|
+
]}>
|
|
16
|
+
{realItem}
|
|
17
|
+
</View>);
|
|
18
|
+
};
|
|
19
|
+
PickerViewColumnItem.displayName = 'MpxPickerViewColumnItem';
|
|
20
|
+
export default PickerViewColumnItem;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { useContext, useEffect, useRef } from 'react';
|
|
2
|
-
import { PortalContext, RouteContext, VarContext } from '../context';
|
|
2
|
+
import { PortalContext, ProviderContext, RouteContext, VarContext } from '../context';
|
|
3
3
|
import PortalHost, { portal } from './portal-host';
|
|
4
4
|
const Portal = ({ children }) => {
|
|
5
5
|
const manager = useContext(PortalContext);
|
|
6
6
|
const keyRef = useRef(null);
|
|
7
7
|
const { pageId } = useContext(RouteContext) || {};
|
|
8
8
|
const varContext = useContext(VarContext);
|
|
9
|
+
const parentProvides = useContext(ProviderContext);
|
|
9
10
|
if (varContext) {
|
|
10
11
|
children = (<VarContext.Provider value={varContext} key='varContextWrap'>{children}</VarContext.Provider>);
|
|
11
12
|
}
|
|
13
|
+
if (parentProvides) {
|
|
14
|
+
children = (<ProviderContext.Provider value={parentProvides} key='providerContextWrap'>{children}</ProviderContext.Provider>);
|
|
15
|
+
}
|
|
12
16
|
useEffect(() => {
|
|
13
17
|
manager.update(keyRef.current, children);
|
|
14
18
|
}, [children]);
|