@mpxjs/webpack-plugin 2.9.69 → 2.9.70-alpha.1

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 (144) hide show
  1. package/README.md +1 -1
  2. package/lib/config.js +14 -0
  3. package/lib/dependencies/AddEntryDependency.js +24 -0
  4. package/lib/dependencies/ResolveDependency.js +5 -0
  5. package/lib/index.js +38 -7
  6. package/lib/json-compiler/helper.js +3 -3
  7. package/lib/loader.js +52 -0
  8. package/lib/platform/template/wx/component-config/button.js +14 -2
  9. package/lib/platform/template/wx/component-config/image.js +4 -0
  10. package/lib/platform/template/wx/component-config/input.js +5 -1
  11. package/lib/platform/template/wx/component-config/rich-text.js +4 -0
  12. package/lib/platform/template/wx/component-config/scroll-view.js +4 -0
  13. package/lib/platform/template/wx/component-config/swiper.js +1 -1
  14. package/lib/platform/template/wx/component-config/switch.js +4 -0
  15. package/lib/platform/template/wx/component-config/text.js +4 -0
  16. package/lib/platform/template/wx/component-config/textarea.js +6 -1
  17. package/lib/platform/template/wx/component-config/view.js +4 -0
  18. package/lib/platform/template/wx/index.js +127 -1
  19. package/lib/react/processTemplate.js +3 -0
  20. package/lib/resolve-loader.js +4 -1
  21. package/lib/runtime/components/react/context.ts +4 -0
  22. package/lib/runtime/components/react/dist/context.js +5 -0
  23. package/lib/runtime/components/react/dist/event.config.js +24 -24
  24. package/lib/runtime/components/react/dist/getInnerListeners.js +183 -166
  25. package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
  26. package/lib/runtime/components/react/dist/mpx-button.jsx +39 -74
  27. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +30 -12
  28. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +13 -19
  29. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +29 -38
  30. package/lib/runtime/components/react/dist/mpx-form.jsx +16 -19
  31. package/lib/runtime/components/react/dist/mpx-icon.jsx +8 -16
  32. package/lib/runtime/components/react/dist/mpx-image.jsx +295 -0
  33. package/lib/runtime/components/react/dist/mpx-input.jsx +54 -27
  34. package/lib/runtime/components/react/dist/mpx-label.jsx +15 -22
  35. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +13 -16
  36. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +14 -14
  37. package/lib/runtime/components/react/dist/mpx-navigator.jsx +2 -4
  38. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +6 -2
  39. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +5 -3
  40. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +6 -2
  41. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +6 -2
  42. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +6 -2
  43. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +10 -14
  44. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +39 -0
  45. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +126 -112
  46. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +32 -29
  47. package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
  48. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
  49. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
  50. package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
  51. package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
  52. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +11 -19
  53. package/lib/runtime/components/react/dist/mpx-radio.jsx +27 -42
  54. package/lib/runtime/components/react/dist/mpx-rich-text/html.js +39 -0
  55. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +62 -0
  56. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +7 -5
  57. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +62 -47
  58. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +11 -0
  59. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +28 -9
  60. package/lib/runtime/components/react/dist/mpx-swiper.jsx +613 -0
  61. package/lib/runtime/components/react/dist/mpx-switch.jsx +20 -10
  62. package/lib/runtime/components/react/dist/mpx-text.jsx +11 -10
  63. package/lib/runtime/components/react/dist/mpx-textarea.jsx +8 -3
  64. package/lib/runtime/components/react/dist/mpx-view.jsx +37 -89
  65. package/lib/runtime/components/react/dist/mpx-web-view.jsx +205 -46
  66. package/lib/runtime/components/react/dist/pickerFaces.js +12 -6
  67. package/lib/runtime/components/react/dist/pickerVIewContext.js +9 -0
  68. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  69. package/lib/runtime/components/react/dist/{pickerOverlay.jsx → pickerViewOverlay.jsx} +5 -3
  70. package/lib/runtime/components/react/dist/useAnimationHooks.js +50 -12
  71. package/lib/runtime/components/react/dist/utils.jsx +83 -28
  72. package/lib/runtime/components/react/getInnerListeners.ts +35 -28
  73. package/lib/runtime/components/react/mpx-button.tsx +55 -36
  74. package/lib/runtime/components/react/mpx-canvas/index.tsx +2 -2
  75. package/lib/runtime/components/react/mpx-checkbox-group.tsx +13 -12
  76. package/lib/runtime/components/react/mpx-checkbox.tsx +28 -28
  77. package/lib/runtime/components/react/mpx-form.tsx +10 -8
  78. package/lib/runtime/components/react/mpx-icon.tsx +10 -15
  79. package/lib/runtime/components/react/mpx-image.tsx +396 -0
  80. package/lib/runtime/components/react/mpx-input.tsx +61 -33
  81. package/lib/runtime/components/react/mpx-label.tsx +14 -13
  82. package/lib/runtime/components/react/mpx-movable-area.tsx +8 -7
  83. package/lib/runtime/components/react/mpx-movable-view.tsx +1 -1
  84. package/lib/runtime/components/react/mpx-picker/date.tsx +5 -2
  85. package/lib/runtime/components/react/mpx-picker/index.tsx +3 -2
  86. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +5 -2
  87. package/lib/runtime/components/react/mpx-picker/region.tsx +5 -2
  88. package/lib/runtime/components/react/mpx-picker/selector.tsx +5 -2
  89. package/lib/runtime/components/react/mpx-picker/time.tsx +10 -15
  90. package/lib/runtime/components/react/mpx-picker/type.ts +48 -43
  91. package/lib/runtime/components/react/mpx-picker-view-column.tsx +4 -1
  92. package/lib/runtime/components/react/mpx-picker-view.tsx +7 -1
  93. package/lib/runtime/components/react/mpx-radio-group.tsx +11 -12
  94. package/lib/runtime/components/react/mpx-radio.tsx +26 -29
  95. package/lib/runtime/components/react/mpx-scroll-view.tsx +32 -30
  96. package/lib/runtime/components/react/mpx-simple-text.tsx +18 -0
  97. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +4 -2
  98. package/lib/runtime/components/react/mpx-swiper-item.tsx +3 -2
  99. package/lib/runtime/components/react/mpx-switch.tsx +10 -8
  100. package/lib/runtime/components/react/mpx-text.tsx +6 -2
  101. package/lib/runtime/components/react/mpx-view.tsx +37 -45
  102. package/lib/runtime/components/react/mpx-web-view.tsx +25 -15
  103. package/lib/runtime/components/react/types/global.d.ts +1 -16
  104. package/lib/runtime/components/react/utils.tsx +24 -24
  105. package/lib/runtime/components/tenon/getInnerListeners.js +334 -0
  106. package/lib/runtime/components/tenon/tenon-button.vue +309 -0
  107. package/lib/runtime/components/tenon/tenon-image.vue +66 -0
  108. package/lib/runtime/components/tenon/tenon-input.vue +171 -0
  109. package/lib/runtime/components/tenon/tenon-rich-text.vue +26 -0
  110. package/lib/runtime/components/tenon/tenon-scroll-view.vue +127 -0
  111. package/lib/runtime/components/tenon/tenon-switch.vue +96 -0
  112. package/lib/runtime/components/tenon/tenon-text.vue +70 -0
  113. package/lib/runtime/components/tenon/tenon-textarea.vue +86 -0
  114. package/lib/runtime/components/tenon/tenon-view.vue +93 -0
  115. package/lib/runtime/components/web/getInnerListeners.js +6 -6
  116. package/lib/runtime/components/web/mpx-movable-view.vue +334 -344
  117. package/lib/runtime/components/web/mpx-picker-view-column.vue +75 -75
  118. package/lib/runtime/components/web/mpx-picker.vue +382 -385
  119. package/lib/runtime/components/web/mpx-web-view.vue +162 -162
  120. package/lib/runtime/optionProcessor.js +7 -16
  121. package/lib/runtime/optionProcessor.tenon.js +84 -0
  122. package/lib/runtime/utils.js +2 -0
  123. package/lib/style-compiler/index.js +1 -1
  124. package/lib/style-compiler/plugins/hm.js +20 -0
  125. package/lib/template-compiler/bind-this.js +7 -2
  126. package/lib/template-compiler/compiler.js +67 -40
  127. package/lib/template-compiler/gen-node-react.js +2 -2
  128. package/lib/tenon/index.js +112 -0
  129. package/lib/tenon/processJSON.js +352 -0
  130. package/lib/tenon/processScript.js +198 -0
  131. package/lib/tenon/processStyles.js +21 -0
  132. package/lib/tenon/processTemplate.js +125 -0
  133. package/lib/tenon/script-helper.js +223 -0
  134. package/lib/utils/env.js +6 -1
  135. package/lib/utils/get-relative-path.js +25 -0
  136. package/package.json +7 -3
  137. package/LICENSE +0 -433
  138. package/lib/runtime/components/react/dist/mpx-image/index.jsx +0 -226
  139. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +0 -7
  140. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -478
  141. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
  142. package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
  143. package/lib/runtime/components/react/mpx-image/index.tsx +0 -345
  144. package/lib/runtime/components/react/mpx-image/svg.tsx +0 -22
@@ -25,7 +25,7 @@ import { warn } from '@mpxjs/utils'
25
25
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
26
26
  import useNodesRef, { HandlerRef } from './useNodesRef'
27
27
  import Icon from './mpx-icon'
28
- import { splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren } from './utils'
28
+ import { splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject } from './utils'
29
29
  import { CheckboxGroupContext, LabelContext } from './context'
30
30
 
31
31
  interface Selection {
@@ -46,7 +46,7 @@ export interface CheckboxProps extends Selection {
46
46
  'parent-height'?: number;
47
47
  children?: ReactNode
48
48
  bindtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
49
- catchtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
49
+ _onChange?: (evt: NativeSyntheticEvent<TouchEvent> | unknown, { checked }: { checked: boolean }) => void
50
50
  }
51
51
 
52
52
  const styles = StyleSheet.create({
@@ -92,7 +92,7 @@ const Checkbox = forwardRef<HandlerRef<View, CheckboxProps>, CheckboxProps>(
92
92
  'parent-width': parentWidth,
93
93
  'parent-height': parentHeight,
94
94
  bindtap,
95
- catchtap
95
+ _onChange
96
96
  } = props
97
97
 
98
98
  const [isChecked, setIsChecked] = useState<boolean>(!!checked)
@@ -101,15 +101,13 @@ const Checkbox = forwardRef<HandlerRef<View, CheckboxProps>, CheckboxProps>(
101
101
  let groupValue: { [key: string]: { checked: boolean; setValue: Dispatch<SetStateAction<boolean>>; } } | undefined
102
102
  let notifyChange: (evt: NativeSyntheticEvent<TouchEvent>) => void | undefined
103
103
 
104
- const defaultStyle = {
105
- ...styles.wrapper,
106
- ...(disabled && styles.wrapperDisabled)
107
- }
104
+ const defaultStyle = extendObject(
105
+ {},
106
+ styles.wrapper,
107
+ disabled ? styles.wrapperDisabled : null
108
+ )
108
109
 
109
- const styleObj = {
110
- ...styles.container,
111
- ...style
112
- }
110
+ const styleObj = extendObject({}, styles.container, style)
113
111
 
114
112
  const onChange = (evt: NativeSyntheticEvent<TouchEvent>) => {
115
113
  if (disabled) return
@@ -119,20 +117,15 @@ const Checkbox = forwardRef<HandlerRef<View, CheckboxProps>, CheckboxProps>(
119
117
  groupValue[value].checked = checked
120
118
  }
121
119
  notifyChange && notifyChange(evt)
120
+ // Called when the switch type attribute is checkbox
121
+ _onChange && _onChange(evt, { checked })
122
122
  }
123
123
 
124
124
  const onTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
125
- if (disabled) return
126
125
  bindtap && bindtap(getCustomEvent('tap', evt, { layoutRef }, props))
127
126
  onChange(evt)
128
127
  }
129
128
 
130
- const catchTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
131
- if (disabled) return
132
- catchtap && catchtap(getCustomEvent('tap', evt, { layoutRef }, props))
133
- onChange(evt)
134
- }
135
-
136
129
  const {
137
130
  hasSelfPercent,
138
131
  normalStyle,
@@ -145,13 +138,13 @@ const Checkbox = forwardRef<HandlerRef<View, CheckboxProps>, CheckboxProps>(
145
138
  const nodeRef = useRef(null)
146
139
 
147
140
  useNodesRef(props, ref, nodeRef, {
148
- defaultStyle,
141
+ style: extendObject({}, defaultStyle, normalStyle),
149
142
  change: onChange
150
143
  })
151
144
 
152
145
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef })
153
146
 
154
- const { textStyle, backgroundStyle, innerStyle } = splitStyle(normalStyle)
147
+ const { textStyle, backgroundStyle, innerStyle = {} } = splitStyle(normalStyle)
155
148
 
156
149
  if (backgroundStyle) {
157
150
  warn('Checkbox does not support background image-related styles!')
@@ -170,14 +163,21 @@ const Checkbox = forwardRef<HandlerRef<View, CheckboxProps>, CheckboxProps>(
170
163
 
171
164
  const innerProps = useInnerProps(
172
165
  props,
173
- {
174
- ref: nodeRef,
175
- style: { ...innerStyle, ...layoutStyle },
176
- ...layoutProps,
177
- bindtap: onTap,
178
- catchtap: catchTap
179
- },
180
- [],
166
+ extendObject(
167
+ {
168
+ ref: nodeRef,
169
+ style: extendObject({}, innerStyle, layoutStyle)
170
+ },
171
+ layoutProps,
172
+ {
173
+ bindtap: !disabled && onTap
174
+ }
175
+ ),
176
+ [
177
+ 'value',
178
+ 'disabled',
179
+ 'checked'
180
+ ],
181
181
  {
182
182
  layoutRef
183
183
  }
@@ -9,7 +9,7 @@ import { JSX, useRef, forwardRef, ReactNode, useMemo, useCallback } from 'react'
9
9
  import useNodesRef, { HandlerRef } from './useNodesRef'
10
10
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
11
11
  import { FormContext } from './context'
12
- import { useTransformStyle, splitProps, splitStyle, useLayout, wrapChildren } from './utils'
12
+ import { useTransformStyle, splitProps, splitStyle, useLayout, wrapChildren, extendObject } from './utils'
13
13
  interface FormProps {
14
14
  style?: Record<string, any>;
15
15
  children?: ReactNode;
@@ -47,21 +47,23 @@ const _Form = forwardRef<HandlerRef<View, FormProps>, FormProps>((fromProps: For
47
47
  setHeight
48
48
  } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
49
49
 
50
- const { textStyle, innerStyle } = splitStyle(normalStyle)
50
+ const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
51
51
 
52
52
  const formRef = useRef(null)
53
- useNodesRef(props, ref, formRef)
53
+ useNodesRef(props, ref, formRef, {
54
+ style: normalStyle
55
+ })
54
56
 
55
57
  const propsRef = useRef<FormProps>({})
56
58
  propsRef.current = props
57
59
 
58
60
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: formRef })
59
61
 
60
- const innerProps = useInnerProps(props, {
61
- style: { ...innerStyle, ...layoutStyle },
62
- ref: formRef,
63
- ...layoutProps
64
- }, [
62
+ const innerProps = useInnerProps(props, extendObject({
63
+ style: extendObject({}, innerStyle, layoutStyle),
64
+ ref: formRef
65
+ }, layoutProps)
66
+ , [
65
67
  'bindsubmit',
66
68
  'bindreset'
67
69
  ], { layoutRef })
@@ -7,7 +7,7 @@ import { JSX, forwardRef, useRef } from 'react'
7
7
  import { Text, TextStyle, Image } from 'react-native'
8
8
  import useInnerProps from './getInnerListeners'
9
9
  import useNodesRef, { HandlerRef } from './useNodesRef'
10
- import { useLayout, useTransformStyle } from './utils'
10
+ import { useLayout, useTransformStyle, extendObject } from './utils'
11
11
 
12
12
  export type IconType =
13
13
  | 'success'
@@ -63,10 +63,7 @@ const Icon = forwardRef<HandlerRef<Text, IconProps>, IconProps>(
63
63
 
64
64
  const defaultStyle = { width: ~~size, height: ~~size }
65
65
 
66
- const styleObj = {
67
- ...defaultStyle,
68
- ...style
69
- }
66
+ const styleObj = extendObject({}, defaultStyle, style)
70
67
 
71
68
  const {
72
69
  hasSelfPercent,
@@ -76,22 +73,20 @@ const Icon = forwardRef<HandlerRef<Text, IconProps>, IconProps>(
76
73
  } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
77
74
 
78
75
  const nodeRef = useRef(null)
79
- useNodesRef(props, ref, nodeRef, { defaultStyle })
76
+ useNodesRef(props, ref, nodeRef, { style: normalStyle })
80
77
 
81
78
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef })
82
79
 
83
80
  const innerProps = useInnerProps(
84
81
  props,
85
- {
86
- ref: nodeRef,
87
- style: {
88
- ...normalStyle,
89
- ...layoutStyle,
90
- tintColor: color
82
+ extendObject(
83
+ {
84
+ ref: nodeRef,
85
+ source: { uri },
86
+ style: extendObject({}, normalStyle, layoutStyle, { tintColor: color })
91
87
  },
92
- source: { uri },
93
- ...layoutProps
94
- },
88
+ layoutProps
89
+ ),
95
90
  [],
96
91
  {
97
92
  layoutRef
@@ -0,0 +1,396 @@
1
+ /**
2
+ * ✔ src
3
+ * ✔ mode
4
+ * ✘ show-menu-by-longpress
5
+ * ✔ binderror
6
+ * ✔ bindload
7
+ * ✘ fade-in
8
+ * ✔ webp
9
+ * ✘ lazy-load
10
+ * ✔ bindtap
11
+ * ✔ DEFAULT_SIZE
12
+ */
13
+ import { useEffect, useMemo, useState, useRef, forwardRef } from 'react'
14
+ import {
15
+ Image as RNImage,
16
+ View,
17
+ ImageStyle,
18
+ ImageResizeMode,
19
+ NativeSyntheticEvent,
20
+ ImageErrorEventData,
21
+ LayoutChangeEvent,
22
+ DimensionValue,
23
+ ImageLoadEventData
24
+ } from 'react-native'
25
+ import { SvgCssUri } from 'react-native-svg/css'
26
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
27
+ import useNodesRef, { HandlerRef } from './useNodesRef'
28
+ import { SVG_REGEXP, useLayout, useTransformStyle, extendObject } from './utils'
29
+
30
+ export type Mode =
31
+ | 'scaleToFill'
32
+ | 'aspectFit'
33
+ | 'aspectFill'
34
+ | 'widthFix'
35
+ | 'heightFix'
36
+ | 'top'
37
+ | 'bottom'
38
+ | 'center'
39
+ | 'left'
40
+ | 'right'
41
+ | 'top left'
42
+ | 'top right'
43
+ | 'bottom left'
44
+ | 'bottom right'
45
+
46
+ export interface ImageProps {
47
+ src?: string
48
+ mode?: Mode
49
+ svg?: boolean
50
+ style?: ImageStyle & Record<string, any>
51
+ 'enable-offset'?: boolean;
52
+ 'enable-var'?: boolean
53
+ 'external-var-context'?: Record<string, any>
54
+ 'parent-font-size'?: number
55
+ 'parent-width'?: number
56
+ 'parent-height'?: number
57
+ bindload?: (evt: NativeSyntheticEvent<ImageLoadEventData> | unknown) => void
58
+ binderror?: (evt: NativeSyntheticEvent<ImageErrorEventData> | unknown) => void
59
+ }
60
+
61
+ const DEFAULT_IMAGE_WIDTH = 320
62
+ const DEFAULT_IMAGE_HEIGHT = 240
63
+
64
+ const cropMode: Mode[] = [
65
+ 'top',
66
+ 'bottom',
67
+ 'center',
68
+ 'right',
69
+ 'left',
70
+ 'top left',
71
+ 'top right',
72
+ 'bottom left',
73
+ 'bottom right'
74
+ ]
75
+
76
+ const ModeMap = new Map<Mode, ImageResizeMode | undefined>([
77
+ ['scaleToFill', 'stretch'],
78
+ ['aspectFit', 'contain'],
79
+ ['aspectFill', 'cover'],
80
+ ['widthFix', 'stretch'],
81
+ ['heightFix', 'stretch'],
82
+ ...cropMode.map<[Mode, ImageResizeMode]>(mode => [mode, 'stretch'])
83
+ ])
84
+
85
+ const isNumber = (value: DimensionValue) => typeof value === 'number'
86
+
87
+ const relativeCenteredSize = (viewSize: number, imageSize: number) => (viewSize - imageSize) / 2
88
+
89
+ function noMeetCalcRule (isSvg: boolean, mode: Mode, viewWidth: number, viewHeight: number, ratio: number) {
90
+ const isMeetSize = viewWidth && viewHeight && ratio
91
+ if (isSvg && !isMeetSize) return true
92
+ if (!isSvg && !['scaleToFill', 'aspectFit', 'aspectFill'].includes(mode) && !isMeetSize) return true
93
+ return false
94
+ }
95
+
96
+ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, ref): JSX.Element => {
97
+ const {
98
+ src = '',
99
+ mode = 'scaleToFill',
100
+ style = {},
101
+ 'enable-var': enableVar,
102
+ 'external-var-context': externalVarContext,
103
+ 'parent-font-size': parentFontSize,
104
+ 'parent-width': parentWidth,
105
+ 'parent-height': parentHeight,
106
+ bindload,
107
+ binderror
108
+ } = props
109
+
110
+ const defaultStyle = {
111
+ width: DEFAULT_IMAGE_WIDTH,
112
+ height: DEFAULT_IMAGE_HEIGHT
113
+ }
114
+
115
+ const styleObj = extendObject(
116
+ {},
117
+ defaultStyle,
118
+ style,
119
+ { overflow: 'hidden' }
120
+ )
121
+
122
+ const nodeRef = useRef(null)
123
+
124
+ const onLayout = ({ nativeEvent: { layout: { width, height } } }: LayoutChangeEvent) => {
125
+ setViewWidth(width)
126
+ setViewHeight(height)
127
+ }
128
+
129
+ const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
130
+
131
+ useNodesRef(props, ref, nodeRef, {
132
+ style: normalStyle
133
+ })
134
+
135
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout })
136
+
137
+ const { width, height } = normalStyle
138
+
139
+ const isSvg = SVG_REGEXP.test(src)
140
+ const isWidthFixMode = mode === 'widthFix'
141
+ const isHeightFixMode = mode === 'heightFix'
142
+ const isCropMode = cropMode.includes(mode)
143
+ const isLayoutMode = isWidthFixMode || isHeightFixMode || isCropMode
144
+ const resizeMode: ImageResizeMode = ModeMap.get(mode) || 'stretch'
145
+
146
+ const [viewWidth, setViewWidth] = useState(isNumber(width) ? width : 0)
147
+ const [viewHeight, setViewHeight] = useState(isNumber(height) ? height : 0)
148
+ const [imageWidth, setImageWidth] = useState(0)
149
+ const [imageHeight, setImageHeight] = useState(0)
150
+ const [ratio, setRatio] = useState(0)
151
+
152
+ const fixedHeight = useMemo(() => {
153
+ const fixed = viewWidth * ratio
154
+ return !fixed ? viewHeight : fixed
155
+ }, [ratio, viewWidth, viewHeight])
156
+
157
+ const fixedWidth = useMemo(() => {
158
+ if (!ratio) return viewWidth
159
+ const fixed = viewHeight / ratio
160
+ return !fixed ? viewWidth : fixed
161
+ }, [ratio, viewWidth, viewHeight])
162
+
163
+ const modeStyle: ImageStyle = useMemo(() => {
164
+ if (noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio)) return {}
165
+ switch (mode) {
166
+ case 'scaleToFill':
167
+ case 'aspectFit':
168
+ if (isSvg) {
169
+ const scale = ratio <= 1
170
+ ? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
171
+ : imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight
172
+ return {
173
+ transform: [
174
+ { scale },
175
+ ratio <= 1 ? { translateY: -(imageHeight * scale - viewHeight) / 2 / scale } : { translateX: -(imageWidth * scale - viewWidth) / 2 / scale }
176
+ ]
177
+ }
178
+ }
179
+ return {}
180
+ case 'aspectFill':
181
+ if (isSvg) {
182
+ const scale = ratio >= 1
183
+ ? imageWidth >= viewWidth ? viewWidth / imageWidth : imageWidth / viewWidth
184
+ : imageHeight >= viewHeight ? viewHeight / imageHeight : imageHeight / viewHeight
185
+ return {
186
+ transform: [
187
+ { scale },
188
+ ratio >= 1 ? { translateY: -(imageHeight * scale - viewHeight) / 2 / scale } : { translateX: -(imageWidth * scale - viewWidth) / 2 / scale }
189
+ ]
190
+ }
191
+ }
192
+ return {}
193
+ case 'widthFix':
194
+ case 'heightFix':
195
+ if (isSvg) {
196
+ const scale = ratio >= 1
197
+ ? imageWidth >= fixedWidth ? fixedWidth / imageWidth : imageWidth / fixedWidth
198
+ : imageHeight >= fixedHeight ? fixedHeight / imageHeight : imageHeight / fixedHeight
199
+ return {
200
+ transform: [{ scale }]
201
+ }
202
+ }
203
+ return {}
204
+ case 'top':
205
+ return {
206
+ transform: [
207
+ { translateX: relativeCenteredSize(viewWidth, imageWidth) }
208
+ ]
209
+ }
210
+ case 'bottom':
211
+ return {
212
+ transform: [
213
+ { translateY: viewHeight - imageHeight },
214
+ { translateX: relativeCenteredSize(viewWidth, imageWidth) }
215
+ ]
216
+ }
217
+ case 'center':
218
+ return {
219
+ transform: [
220
+ { translateY: relativeCenteredSize(viewHeight, imageHeight) },
221
+ { translateX: relativeCenteredSize(viewWidth, imageWidth) }
222
+ ]
223
+ }
224
+ case 'left':
225
+ return {
226
+ transform: [
227
+ { translateY: relativeCenteredSize(viewHeight, imageHeight) }
228
+ ]
229
+ }
230
+ case 'right':
231
+ return {
232
+ transform: [
233
+ { translateY: relativeCenteredSize(viewHeight, imageHeight) },
234
+ { translateX: viewWidth - imageWidth }
235
+ ]
236
+ }
237
+ case 'top left':
238
+ return {}
239
+ case 'top right':
240
+ return {
241
+ transform: [
242
+ { translateX: viewWidth - imageWidth }
243
+ ]
244
+ }
245
+ case 'bottom left':
246
+ return {
247
+ transform: [
248
+ { translateY: viewHeight - imageHeight }
249
+ ]
250
+ }
251
+ case 'bottom right':
252
+ return {
253
+ transform: [
254
+ { translateY: viewHeight - imageHeight },
255
+ { translateX: viewWidth - imageWidth }
256
+ ]
257
+ }
258
+ default:
259
+ return {}
260
+ }
261
+ }, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio, fixedWidth, fixedHeight])
262
+
263
+ const onSvgLoad = (evt: LayoutChangeEvent) => {
264
+ const { width, height } = evt.nativeEvent.layout
265
+ setRatio(!width ? 0 : height / width)
266
+ setImageWidth(width)
267
+ setImageHeight(height)
268
+
269
+ bindload && bindload(
270
+ getCustomEvent(
271
+ 'load',
272
+ evt,
273
+ {
274
+ detail: { width, height },
275
+ layoutRef
276
+ },
277
+ props
278
+ )
279
+ )
280
+ }
281
+
282
+ const onSvgError = (evt: Error) => {
283
+ binderror!(
284
+ getCustomEvent(
285
+ 'error',
286
+ evt,
287
+ {
288
+ detail: { errMsg: evt?.message },
289
+ layoutRef
290
+ },
291
+ props
292
+ )
293
+ )
294
+ }
295
+
296
+ const onImageLoad = (evt: NativeSyntheticEvent<ImageLoadEventData>) => {
297
+ evt.persist()
298
+ RNImage.getSize(src, (width: number, height: number) => {
299
+ bindload!(
300
+ getCustomEvent(
301
+ 'load',
302
+ evt,
303
+ {
304
+ detail: { width, height },
305
+ layoutRef
306
+ },
307
+ props
308
+ )
309
+ )
310
+ })
311
+ }
312
+
313
+ const onImageError = (evt: NativeSyntheticEvent<ImageErrorEventData>) => {
314
+ binderror!(
315
+ getCustomEvent(
316
+ 'error',
317
+ evt,
318
+ {
319
+ detail: { errMsg: evt.nativeEvent.error },
320
+ layoutRef
321
+ },
322
+ props
323
+ )
324
+ )
325
+ }
326
+
327
+ useEffect(() => {
328
+ if (!isSvg && isLayoutMode) {
329
+ RNImage.getSize(src, (width: number, height: number) => {
330
+ setRatio(!width ? 0 : height / width)
331
+ setImageWidth(width)
332
+ setImageHeight(height)
333
+ })
334
+ }
335
+ }, [src, isSvg, isLayoutMode])
336
+
337
+ const innerProps = useInnerProps(
338
+ props,
339
+ extendObject(
340
+ {
341
+ ref: nodeRef,
342
+ style: extendObject(
343
+ {},
344
+ normalStyle,
345
+ layoutStyle,
346
+ isHeightFixMode ? { width: fixedWidth } : {},
347
+ isWidthFixMode ? { height: fixedHeight } : {}
348
+ )
349
+ },
350
+ layoutProps
351
+ ),
352
+ [
353
+ 'src',
354
+ 'mode',
355
+ 'svg'
356
+ ],
357
+ {
358
+ layoutRef
359
+ }
360
+ )
361
+
362
+ return (
363
+ <View {...innerProps}>
364
+ {
365
+ isSvg
366
+ ? <SvgCssUri
367
+ uri={src}
368
+ onLayout={onSvgLoad}
369
+ onError={binderror && onSvgError}
370
+ style={extendObject(
371
+ { transformOrigin: 'top left' },
372
+ modeStyle
373
+ )}
374
+ />
375
+ : <RNImage
376
+ source={{ uri: src }}
377
+ resizeMode={resizeMode}
378
+ onLoad={bindload && onImageLoad}
379
+ onError={binderror && onImageError}
380
+ style={extendObject(
381
+ {
382
+ transformOrigin: 'top left',
383
+ width: isCropMode ? imageWidth : '100%',
384
+ height: isCropMode ? imageHeight : '100%'
385
+ },
386
+ isCropMode ? modeStyle : {}
387
+ )}
388
+ />
389
+ }
390
+ </View>
391
+ )
392
+ })
393
+
394
+ Image.displayName = 'mpx-image'
395
+
396
+ export default Image