@mpxjs/webpack-plugin 2.10.1 → 2.10.3

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 (41) hide show
  1. package/lib/dependencies/RecordPageConfigsMapDependency.js +45 -0
  2. package/lib/index.js +23 -1
  3. package/lib/platform/style/wx/index.js +6 -4
  4. package/lib/platform/template/wx/component-config/input.js +1 -1
  5. package/lib/platform/template/wx/component-config/textarea.js +1 -1
  6. package/lib/platform/template/wx/component-config/view.js +12 -2
  7. package/lib/react/index.js +0 -1
  8. package/lib/react/processJSON.js +13 -2
  9. package/lib/react/processScript.js +7 -4
  10. package/lib/react/processTemplate.js +18 -3
  11. package/lib/react/script-helper.js +18 -4
  12. package/lib/runtime/components/react/context.ts +3 -4
  13. package/lib/runtime/components/react/dist/mpx-image.jsx +2 -2
  14. package/lib/runtime/components/react/dist/mpx-input.jsx +54 -54
  15. package/lib/runtime/components/react/dist/{KeyboardAvoidingView.jsx → mpx-keyboard-avoiding-view.jsx} +23 -12
  16. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +1 -2
  17. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +16 -8
  18. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +22 -0
  19. package/lib/runtime/components/react/dist/mpx-textarea.jsx +6 -6
  20. package/lib/runtime/components/react/dist/mpx-view.jsx +10 -5
  21. package/lib/runtime/components/react/dist/mpx-web-view.jsx +6 -5
  22. package/lib/runtime/components/react/dist/useAnimationHooks.js +46 -48
  23. package/lib/runtime/components/react/dist/utils.jsx +17 -21
  24. package/lib/runtime/components/react/mpx-image.tsx +2 -2
  25. package/lib/runtime/components/react/mpx-input.tsx +66 -72
  26. package/lib/runtime/components/react/{KeyboardAvoidingView.tsx → mpx-keyboard-avoiding-view.tsx} +32 -18
  27. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +1 -2
  28. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -8
  29. package/lib/runtime/components/react/mpx-simple-view.tsx +32 -0
  30. package/lib/runtime/components/react/mpx-textarea.tsx +10 -6
  31. package/lib/runtime/components/react/mpx-view.tsx +17 -10
  32. package/lib/runtime/components/react/mpx-web-view.tsx +12 -10
  33. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  34. package/lib/runtime/components/react/useAnimationHooks.ts +46 -48
  35. package/lib/runtime/components/react/utils.tsx +21 -24
  36. package/lib/runtime/optionProcessor.js +3 -2
  37. package/lib/style-compiler/index.js +8 -6
  38. package/lib/template-compiler/compiler.js +22 -14
  39. package/lib/utils/match-condition.js +14 -8
  40. package/lib/web/processJSON.js +1 -3
  41. package/package.json +4 -4
@@ -0,0 +1,45 @@
1
+ const NullDependency = require('webpack/lib/dependencies/NullDependency')
2
+ const makeSerializable = require('webpack/lib/util/makeSerializable')
3
+ const isEmptyObject = require('../utils/is-empty-object')
4
+
5
+ class RecordPageConfigMapDependency extends NullDependency {
6
+ constructor (resourcePath, jsonObj) {
7
+ super()
8
+ this.resourcePath = resourcePath || ''
9
+ this.jsonObj = jsonObj || {}
10
+ }
11
+
12
+ get type () {
13
+ return 'mpx record pageConfigsMap'
14
+ }
15
+
16
+ mpxAction (module, compilation, callback) {
17
+ const mpx = compilation.__mpx__
18
+ const pagePath = mpx.pagesMap[this.resourcePath]
19
+ if (pagePath && !isEmptyObject(this.jsonObj)) mpx.pageConfigsMap[pagePath] = this.jsonObj
20
+ return callback()
21
+ }
22
+
23
+ serialize (context) {
24
+ const { write } = context
25
+ write(this.resourcePath)
26
+ write(this.jsonObj)
27
+ super.serialize(context)
28
+ }
29
+
30
+ deserialize (context) {
31
+ const { read } = context
32
+ this.pagePath = read()
33
+ this.jsonObj = read()
34
+ super.deserialize(context)
35
+ }
36
+ }
37
+
38
+ RecordPageConfigMapDependency.Template = class RecordPageConfigMapDependencyTemplate {
39
+ apply () {
40
+ }
41
+ }
42
+
43
+ makeSerializable(RecordPageConfigMapDependency, '@mpxjs/webpack-plugin/lib/dependencies/RecordPageConfigMapDependency')
44
+
45
+ module.exports = RecordPageConfigMapDependency
package/lib/index.js CHANGED
@@ -34,6 +34,7 @@ const FixDescriptionInfoPlugin = require('./resolver/FixDescriptionInfoPlugin')
34
34
  // const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
35
35
  // const RemovedModuleDependency = require('./dependencies/RemovedModuleDependency')
36
36
  const AppEntryDependency = require('./dependencies/AppEntryDependency')
37
+ const RecordPageConfigMapDependency = require('./dependencies/RecordPageConfigsMapDependency')
37
38
  const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
38
39
  const RecordGlobalComponentsDependency = require('./dependencies/RecordGlobalComponentsDependency')
39
40
  const RecordIndependentDependency = require('./dependencies/RecordIndependentDependency')
@@ -617,6 +618,9 @@ class MpxWebpackPlugin {
617
618
  compilation.dependencyFactories.set(RemoveEntryDependency, new NullFactory())
618
619
  compilation.dependencyTemplates.set(RemoveEntryDependency, new RemoveEntryDependency.Template())
619
620
 
621
+ compilation.dependencyFactories.set(RecordPageConfigMapDependency, new NullFactory())
622
+ compilation.dependencyTemplates.set(RecordPageConfigMapDependency, new RecordPageConfigMapDependency.Template())
623
+
620
624
  compilation.dependencyFactories.set(RecordResourceMapDependency, new NullFactory())
621
625
  compilation.dependencyTemplates.set(RecordResourceMapDependency, new RecordResourceMapDependency.Template())
622
626
 
@@ -656,6 +660,8 @@ class MpxWebpackPlugin {
656
660
  __vfs,
657
661
  // app信息,便于获取appName
658
662
  appInfo: {},
663
+ // pageConfig信息
664
+ pageConfigsMap: {},
659
665
  // pages全局记录,无需区分主包分包
660
666
  pagesMap: {},
661
667
  // 组件资源记录,依照所属包进行记录
@@ -1598,7 +1604,7 @@ class MpxWebpackPlugin {
1598
1604
  name: 'MpxWebpackPlugin',
1599
1605
  stage: compilation.PROCESS_ASSETS_STAGE_ADDITIONS
1600
1606
  }, () => {
1601
- if (isWeb(mpx.mode) || isReact(mpx.mode)) return
1607
+ if (isWeb(mpx.mode)) return
1602
1608
 
1603
1609
  if (this.options.generateBuildMap) {
1604
1610
  const pagesMap = compilation.__mpx__.pagesMap
@@ -1637,6 +1643,22 @@ class MpxWebpackPlugin {
1637
1643
 
1638
1644
  const originalSource = compilation.assets[chunkFile]
1639
1645
  const source = new ConcatSource()
1646
+
1647
+ if (isReact(mpx.mode)) {
1648
+ // 添加 @refresh reset 注释用于在 React HMR 时刷新组件
1649
+ source.add('/* @refresh reset */\n')
1650
+ // 注入页面的配置,供screen前置设置导航情况
1651
+ if (isRuntime) {
1652
+ source.add('// inject pageconfigmap for screen\n' +
1653
+ 'var context = (function() { return this })() || Function("return this")();\n')
1654
+ source.add(`context.__mpxPageConfigsMap = ${JSON.stringify(mpx.pageConfigsMap)};\n`)
1655
+ }
1656
+ source.add(originalSource)
1657
+ compilation.assets[chunkFile] = source
1658
+ processedChunk.add(chunk)
1659
+ return
1660
+ }
1661
+
1640
1662
  source.add(`\nvar ${globalObject} = {};\n`)
1641
1663
 
1642
1664
  relativeChunks.forEach((relativeChunk, index) => {
@@ -47,7 +47,7 @@ module.exports = function getSpec ({ warn, error }) {
47
47
  'flex-wrap': ['wrap', 'nowrap', 'wrap-reverse'],
48
48
  'pointer-events': ['auto', 'box-none', 'box-only', 'none'],
49
49
  'vertical-align': ['auto', 'top', 'bottom', 'center'],
50
- position: ['relative', 'absolute'],
50
+ position: ['relative', 'absolute', 'fixed'],
51
51
  'font-variant': ['small-caps', 'oldstyle-nums', 'lining-nums', 'tabular-nums', 'proportional-nums'],
52
52
  'text-align': ['left', 'right', 'center', 'justify'],
53
53
  'font-style': ['normal', 'italic'],
@@ -395,22 +395,22 @@ module.exports = function getSpec ({ warn, error }) {
395
395
  case 'skewY':
396
396
  case 'perspective':
397
397
  // 单个值处理
398
+ // rotate 处理成 rotateZ
399
+ key = key === 'rotate' ? 'rotateZ' : key
398
400
  transform.push({ [key]: val })
399
401
  break
400
402
  case 'matrix':
401
- case 'matrix3d':
402
403
  transform.push({ [key]: parseValues(val, ',').map(val => +val) })
403
404
  break
404
405
  case 'translate':
405
406
  case 'scale':
406
407
  case 'skew':
407
- case 'rotate3d': // x y z angle
408
408
  case 'translate3d': // x y 支持 z不支持
409
409
  case 'scale3d': // x y 支持 z不支持
410
410
  {
411
411
  // 2 个以上的值处理
412
412
  key = key.replace('3d', '')
413
- const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
413
+ const vals = parseValues(val, ',').splice(0, 3)
414
414
  // scale(.5) === scaleX(.5) scaleY(.5)
415
415
  if (vals.length === 1 && key === 'scale') {
416
416
  vals.push(vals[0])
@@ -426,6 +426,8 @@ module.exports = function getSpec ({ warn, error }) {
426
426
  }
427
427
  case 'translateZ':
428
428
  case 'scaleZ':
429
+ case 'rotate3d': // x y z angle
430
+ case 'matrix3d':
429
431
  default:
430
432
  // 不支持的属性处理
431
433
  unsupportedPropError({ prop, value, selector }, { mode })
@@ -92,7 +92,7 @@ module.exports = function ({ print }) {
92
92
  qa: qaPropLog
93
93
  },
94
94
  {
95
- test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|safe-password-.+)$/,
95
+ test: /^(always-embed|hold-keyboard|safe-password-.+)$/,
96
96
  ios: iosPropLog,
97
97
  android: androidPropLog
98
98
  }
@@ -78,7 +78,7 @@ module.exports = function ({ print }) {
78
78
  }
79
79
  },
80
80
  {
81
- test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|disable-default-padding|adjust-keyboard-to|fixed|show-confirm-bar)$/,
81
+ test: /^(always-embed|hold-keyboard|disable-default-padding|adjust-keyboard-to|fixed|show-confirm-bar)$/,
82
82
  ios: iosPropLog,
83
83
  android: androidPropLog
84
84
  }
@@ -25,11 +25,11 @@ module.exports = function ({ print }) {
25
25
  },
26
26
  ios (tag, { el }) {
27
27
  el.isBuiltIn = true
28
- return 'mpx-view'
28
+ return el.isSimple ? 'mpx-simple-view' : 'mpx-view'
29
29
  },
30
30
  android (tag, { el }) {
31
31
  el.isBuiltIn = true
32
- return 'mpx-view'
32
+ return el.isSimple ? 'mpx-simple-view' : 'mpx-view'
33
33
  },
34
34
  qa (tag) {
35
35
  return 'div'
@@ -47,6 +47,16 @@ module.exports = function ({ print }) {
47
47
  test: /^(hover-stop-propagation)$/,
48
48
  android: androidPropLog,
49
49
  ios: iosPropLog
50
+ }, {
51
+ test: /^(is-simple)$/,
52
+ android (prop, { el }) {
53
+ el.isSimple = true
54
+ return false
55
+ },
56
+ ios (prop, { el }) {
57
+ el.isSimple = true
58
+ return false
59
+ }
50
60
  }
51
61
  ],
52
62
  // 组件事件中的差异部分
@@ -85,7 +85,6 @@ module.exports = function ({
85
85
  srcMode,
86
86
  moduleId,
87
87
  isProduction,
88
- componentGenerics,
89
88
  jsonConfig: jsonRes.jsonObj,
90
89
  outputPath: queryObj.outputPath || '',
91
90
  builtInComponentsMap: templateRes.builtInComponentsMap,
@@ -12,6 +12,7 @@ const createJSONHelper = require('../json-compiler/helper')
12
12
  const getRulesRunner = require('../platform/index')
13
13
  const { RESOLVE_IGNORED_ERR } = require('../utils/const')
14
14
  const RecordResourceMapDependency = require('../dependencies/RecordResourceMapDependency')
15
+ const RecordPageConfigsMapDependency = require('../dependencies/RecordPageConfigsMapDependency')
15
16
 
16
17
  module.exports = function (jsonContent, {
17
18
  loaderContext,
@@ -79,7 +80,6 @@ module.exports = function (jsonContent, {
79
80
  })
80
81
  }
81
82
 
82
- const isApp = ctorType === 'app'
83
83
  if (!jsonContent) {
84
84
  return callback()
85
85
  }
@@ -99,7 +99,7 @@ module.exports = function (jsonContent, {
99
99
  }
100
100
  }
101
101
 
102
- if (!isApp) {
102
+ if (ctorType !== 'app') {
103
103
  rulesRunnerOptions.mainKey = ctorType
104
104
  }
105
105
 
@@ -112,6 +112,17 @@ module.exports = function (jsonContent, {
112
112
  return callback(e)
113
113
  }
114
114
 
115
+ if (ctorType === 'page') {
116
+ const keysToExtract = ['navigationStyle']
117
+ const configObj = {}
118
+ keysToExtract.forEach(key => {
119
+ if (jsonObj[key]) {
120
+ configObj[key] = jsonObj[key]
121
+ }
122
+ })
123
+ loaderContext._module.addPresentationalDependency(new RecordPageConfigsMapDependency(parseRequest(loaderContext.resource).resourcePath, configObj))
124
+ }
125
+
115
126
  const fs = loaderContext._compiler.inputFileSystem
116
127
 
117
128
  const defaultTabbar = {
@@ -12,9 +12,12 @@ module.exports = function (script, {
12
12
  outputPath,
13
13
  builtInComponentsMap,
14
14
  localComponentsMap,
15
- localPagesMap
15
+ localPagesMap,
16
+ componentGenerics,
17
+ genericsInfo
16
18
  }, callback) {
17
19
  let scriptSrcMode = srcMode
20
+ const mode = loaderContext.getMpx().mode
18
21
  if (script) {
19
22
  scriptSrcMode = script.mode || scriptSrcMode
20
23
  } else {
@@ -26,7 +29,7 @@ module.exports = function (script, {
26
29
  output += `
27
30
  import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
28
31
  import { NavigationContainer, StackActions } from '@react-navigation/native'
29
- import { createNativeStackNavigator } from '@react-navigation/native-stack'
32
+ ${mode === 'ios' ? "import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack'" : "import { createStackNavigator } from '@react-navigation/stack'"}
30
33
  import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
31
34
  import { useHeaderHeight } from '@react-navigation/elements';
32
35
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
@@ -34,7 +37,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler'
34
37
 
35
38
  global.__navigationHelper = {
36
39
  NavigationContainer: NavigationContainer,
37
- createStackNavigator: createNativeStackNavigator,
40
+ createStackNavigator: createStackNavigator,
38
41
  useHeaderHeight: useHeaderHeight,
39
42
  StackActions: StackActions,
40
43
  GestureHandlerRootView: GestureHandlerRootView,
@@ -67,7 +70,7 @@ global.__navigationHelper = {
67
70
  jsonConfig
68
71
  })
69
72
 
70
- output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath })
73
+ output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath, genericsInfo, componentGenerics })
71
74
  output += getRequireScript({ ctorType, script, loaderContext })
72
75
  output += `export default global.__mpxOptionsMap[${JSON.stringify(moduleId)}]\n`
73
76
  }
@@ -5,6 +5,8 @@ const loaderUtils = require('loader-utils')
5
5
  const templateCompiler = require('../template-compiler/compiler')
6
6
  const genNode = require('../template-compiler/gen-node-react')
7
7
  const bindThis = require('../template-compiler/bind-this')
8
+ const isEmptyObject = require('../utils/is-empty-object')
9
+ const dash2hump = require('../utils/hump-dash').dash2hump
8
10
 
9
11
  module.exports = function (template, {
10
12
  loaderContext,
@@ -75,16 +77,15 @@ module.exports = function (template, {
75
77
  defs,
76
78
  decodeHTMLText,
77
79
  externalClasses,
78
- // todo 后续输出web也采用mpx的scoped处理
79
80
  hasScoped: false,
80
81
  moduleId,
81
82
  filePath: rawResourcePath,
82
83
  // react中模版i18n不需要特殊处理
83
84
  i18n: null,
84
85
  checkUsingComponents,
85
- // web模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
+ // rn模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
87
  globalComponents: [],
87
- // web模式下实现抽象组件
88
+ // rn模式下实现抽象组件
88
89
  componentGenerics,
89
90
  hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
90
91
  forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
@@ -148,6 +149,20 @@ ${e.stack}`)
148
149
  if (meta.options) {
149
150
  output += `global.currentInject.injectOptions = ${JSON.stringify(meta.options)};\n`
150
151
  }
152
+ if (!isEmptyObject(componentGenerics)) {
153
+ output += 'global.currentInject.injectProperties = {\n'
154
+ output += ' generichash: String,\n'
155
+
156
+ Object.keys(componentGenerics).forEach(genericName => {
157
+ const defaultValue = componentGenerics[genericName].default
158
+ if (defaultValue) {
159
+ output += ` generic${dash2hump(genericName)}: { type: String, value: '${genericName}default' },\n`
160
+ } else {
161
+ output += ` generic${dash2hump(genericName)}: String,\n`
162
+ }
163
+ })
164
+ output += '}\n'
165
+ }
151
166
  }
152
167
  }
153
168
 
@@ -89,7 +89,8 @@ function buildGlobalParams ({
89
89
  componentsMap,
90
90
  pagesMap,
91
91
  firstPage,
92
- outputPath
92
+ outputPath,
93
+ genericsInfo
93
94
  }) {
94
95
  let content = ''
95
96
  if (ctorType === 'app') {
@@ -115,9 +116,22 @@ global.currentInject.firstPage = ${JSON.stringify(firstPage)}\n`
115
116
  delete pageConfig.usingComponents
116
117
  content += `global.currentInject.pageConfig = ${JSON.stringify(pageConfig)}\n`
117
118
  }
118
- content += `global.currentInject.getComponents = function () {
119
- return ${shallowStringify(componentsMap)}
120
- }\n`
119
+
120
+ content += `
121
+
122
+ function getComponents() {
123
+ return ${shallowStringify(componentsMap)}
124
+ }
125
+
126
+ global.currentInject.getComponents = getComponents\n`
127
+ if (genericsInfo) {
128
+ content += `
129
+ const genericHash = ${JSON.stringify(genericsInfo.hash)}\n
130
+ global.__mpxGenericsMap[genericHash] = function (name) {
131
+ return getComponents()[name]
132
+ }
133
+ \n`
134
+ }
121
135
  if (ctorType === 'component') {
122
136
  content += `global.currentInject.componentPath = '/' + ${JSON.stringify(outputPath)}\n`
123
137
  }
@@ -5,10 +5,9 @@ export type LabelContextValue = MutableRefObject<{
5
5
  triggerChange: (evt: NativeSyntheticEvent<TouchEvent>) => void
6
6
  }>
7
7
 
8
- export type KeyboardAvoidContextValue = MutableRefObject<{
9
- cursorSpacing: number
10
- ref: MutableRefObject<any>
11
- }>
8
+ export type KeyboardAvoidContextValue = MutableRefObject<
9
+ { cursorSpacing: number, ref: MutableRefObject<any> } | null
10
+ >
12
11
 
13
12
  export interface GroupValue {
14
13
  [key: string]: { checked: boolean; setValue: Dispatch<SetStateAction<boolean>> }
@@ -272,7 +272,7 @@ const Image = forwardRef((props, ref) => {
272
272
  uri: src,
273
273
  onLayout: onSvgLoad,
274
274
  onError: binderror && onSvgError,
275
- style: extendObject({ transformOrigin: 'top left' }, modeStyle)
275
+ style: extendObject({ transformOrigin: 'left top' }, modeStyle)
276
276
  }));
277
277
  const BaseImage = renderImage(extendObject({
278
278
  source: { uri: src },
@@ -280,7 +280,7 @@ const Image = forwardRef((props, ref) => {
280
280
  onLoad: bindload && onImageLoad,
281
281
  onError: binderror && onImageError,
282
282
  style: extendObject({
283
- transformOrigin: 'top left',
283
+ transformOrigin: 'left top',
284
284
  width: isCropMode ? imageWidth : '100%',
285
285
  height: isCropMode ? imageHeight : '100%'
286
286
  }, isCropMode ? modeStyle : {})
@@ -4,7 +4,7 @@
4
4
  * ✔ password
5
5
  * ✔ placeholder
6
6
  * - placeholder-style: Only support color.
7
- * placeholder-class
7
+ * - placeholder-class: Only support color.
8
8
  * ✔ disabled
9
9
  * ✔ maxlength
10
10
  * ✔ cursor-spacing
@@ -40,7 +40,7 @@
40
40
  import { forwardRef, useRef, useState, useContext, useEffect, createElement } from 'react';
41
41
  import { Platform, TextInput } from 'react-native';
42
42
  import { warn } from '@mpxjs/utils';
43
- import { parseInlineStyle, useUpdateEffect, useTransformStyle, useLayout, extendObject } from './utils';
43
+ import { useUpdateEffect, useTransformStyle, useLayout, extendObject } from './utils';
44
44
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
45
45
  import useNodesRef from './useNodesRef';
46
46
  import { FormContext, KeyboardAvoidContext } from './context';
@@ -54,7 +54,7 @@ const keyboardTypeMap = {
54
54
  }) || ''
55
55
  };
56
56
  const Input = forwardRef((props, ref) => {
57
- const { style = {}, allowFontScaling = false, type = 'text', value, password, 'placeholder-style': placeholderStyle, disabled, maxlength = 140, 'cursor-spacing': cursorSpacing = 0, 'auto-focus': autoFocus, focus, 'confirm-type': confirmType = 'done', 'confirm-hold': confirmHold = false, cursor, 'cursor-color': cursorColor, 'selection-start': selectionStart = -1, 'selection-end': selectionEnd = -1, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'adjust-position': adjustPosition = true, bindinput, bindfocus, bindblur, bindconfirm, bindselectionchange,
57
+ const { style = {}, allowFontScaling = false, type = 'text', value, password, 'placeholder-style': placeholderStyle = {}, disabled, maxlength = 140, 'cursor-spacing': cursorSpacing = 0, 'auto-focus': autoFocus, focus, 'confirm-type': confirmType = 'done', 'confirm-hold': confirmHold = false, cursor, 'cursor-color': cursorColor, 'selection-start': selectionStart = -1, 'selection-end': selectionEnd = -1, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'adjust-position': adjustPosition = true, bindinput, bindfocus, bindblur, bindconfirm, bindselectionchange,
58
58
  // private
59
59
  multiline, 'auto-height': autoHeight, bindlinechange } = props;
60
60
  const formContext = useContext(FormContext);
@@ -76,7 +76,6 @@ const Input = forwardRef((props, ref) => {
76
76
  };
77
77
  const keyboardType = keyboardTypeMap[type];
78
78
  const defaultValue = parseValue(value);
79
- const placeholderTextColor = parseInlineStyle(placeholderStyle)?.color;
80
79
  const textAlignVertical = multiline ? 'top' : 'auto';
81
80
  const tmpValue = useRef(defaultValue);
82
81
  const cursorIndex = useRef(0);
@@ -85,7 +84,7 @@ const Input = forwardRef((props, ref) => {
85
84
  const [contentHeight, setContentHeight] = useState(0);
86
85
  const [selection, setSelection] = useState({ start: -1, end: -1 });
87
86
  const styleObj = extendObject({ padding: 0, backgroundColor: '#fff' }, style, multiline && autoHeight
88
- ? { minHeight: Math.max(style?.minHeight || 35, contentHeight) }
87
+ ? { height: 'auto', minHeight: Math.max(style?.minHeight || 35, contentHeight) }
89
88
  : {});
90
89
  const { hasSelfPercent, normalStyle, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
91
90
  const nodeRef = useRef(null);
@@ -106,44 +105,54 @@ const Input = forwardRef((props, ref) => {
106
105
  setSelection({ start: selectionStart, end: selectionEnd });
107
106
  }
108
107
  }, [cursor, selectionStart, selectionEnd]);
109
- const onTextInput = ({ nativeEvent }) => {
110
- if (!bindinput && !bindblur)
111
- return;
112
- const { range: { start, end }, text } = nativeEvent;
113
- cursorIndex.current = start < end ? start : start + text.length;
108
+ // have not selection on the Android platformg
109
+ const getCursorIndex = (changedSelection, prevValue, curValue) => {
110
+ if (changedSelection)
111
+ return changedSelection.end;
112
+ if (!prevValue || !curValue || prevValue.length === curValue.length)
113
+ return curValue.length;
114
+ const prevStr = prevValue.substring(cursorIndex.current);
115
+ const curStr = curValue.substring(cursorIndex.current);
116
+ return cursorIndex.current + curStr.length - prevStr.length;
114
117
  };
115
118
  const onChange = (evt) => {
116
- tmpValue.current = evt.nativeEvent.text;
117
- if (!bindinput)
119
+ const { text, selection } = evt.nativeEvent;
120
+ // will trigger twice on the Android platformg, prevent the second trigger
121
+ if (tmpValue.current === text)
118
122
  return;
119
- const result = bindinput(getCustomEvent('input', evt, {
120
- detail: {
121
- value: evt.nativeEvent.text,
122
- cursor: cursorIndex.current
123
- },
124
- layoutRef
125
- }, props));
126
- if (typeof result === 'string') {
127
- tmpValue.current = result;
128
- setInputValue(result);
123
+ const index = getCursorIndex(selection, tmpValue.current, text);
124
+ tmpValue.current = text;
125
+ cursorIndex.current = index;
126
+ if (bindinput) {
127
+ const result = bindinput(getCustomEvent('input', evt, {
128
+ detail: {
129
+ value: tmpValue.current,
130
+ cursor: cursorIndex.current
131
+ },
132
+ layoutRef
133
+ }, props));
134
+ if (typeof result === 'string') {
135
+ tmpValue.current = result;
136
+ setInputValue(result);
137
+ }
138
+ else {
139
+ setInputValue(tmpValue.current);
140
+ }
129
141
  }
130
142
  else {
131
143
  setInputValue(tmpValue.current);
132
144
  }
133
145
  };
134
146
  const setKeyboardAvoidContext = () => {
135
- if (adjustPosition && keyboardAvoid?.current) {
136
- extendObject(keyboardAvoid.current, {
137
- cursorSpacing,
138
- ref: nodeRef
139
- });
147
+ if (adjustPosition && keyboardAvoid) {
148
+ keyboardAvoid.current = { cursorSpacing, ref: nodeRef };
140
149
  }
141
150
  };
142
- const onInputTouchStart = () => {
151
+ const onTouchStart = () => {
143
152
  // sometimes the focus event occurs later than the keyboardWillShow event
144
153
  setKeyboardAvoidContext();
145
154
  };
146
- const onInputFocus = (evt) => {
155
+ const onFocus = (evt) => {
147
156
  setKeyboardAvoidContext();
148
157
  bindfocus && bindfocus(getCustomEvent('focus', evt, {
149
158
  detail: {
@@ -152,7 +161,7 @@ const Input = forwardRef((props, ref) => {
152
161
  layoutRef
153
162
  }, props));
154
163
  };
155
- const onInputBlur = (evt) => {
164
+ const onBlur = (evt) => {
156
165
  bindblur && bindblur(getCustomEvent('blur', evt, {
157
166
  detail: {
158
167
  value: tmpValue.current || '',
@@ -161,15 +170,6 @@ const Input = forwardRef((props, ref) => {
161
170
  layoutRef
162
171
  }, props));
163
172
  };
164
- const onKeyPress = (evt) => {
165
- evt.nativeEvent.key === 'Enter' &&
166
- bindconfirm(getCustomEvent('confirm', evt, {
167
- detail: {
168
- value: tmpValue.current || ''
169
- },
170
- layoutRef
171
- }, props));
172
- };
173
173
  const onSubmitEditing = (evt) => {
174
174
  bindconfirm(getCustomEvent('confirm', evt, {
175
175
  detail: {
@@ -179,11 +179,14 @@ const Input = forwardRef((props, ref) => {
179
179
  }, props));
180
180
  };
181
181
  const onSelectionChange = (evt) => {
182
- setSelection(evt.nativeEvent.selection);
182
+ const { selection } = evt.nativeEvent;
183
+ const { start, end } = selection;
184
+ cursorIndex.current = start;
185
+ setSelection(selection);
183
186
  bindselectionchange && bindselectionchange(getCustomEvent('selectionchange', evt, {
184
187
  detail: {
185
- selectionStart: evt.nativeEvent.selection.start,
186
- selectionEnd: evt.nativeEvent.selection.end
188
+ selectionStart: start,
189
+ selectionEnd: end
187
190
  },
188
191
  layoutRef
189
192
  }, props));
@@ -252,24 +255,21 @@ const Input = forwardRef((props, ref) => {
252
255
  maxLength: maxlength === -1 ? undefined : maxlength,
253
256
  editable: !disabled,
254
257
  autoFocus: !!autoFocus || !!focus,
255
- returnKeyType: confirmType,
256
258
  selection: selection,
257
259
  selectionColor: cursorColor,
258
260
  blurOnSubmit: !multiline && !confirmHold,
259
261
  underlineColorAndroid: 'rgba(0,0,0,0)',
260
262
  textAlignVertical: textAlignVertical,
261
- placeholderTextColor: placeholderTextColor,
263
+ placeholderTextColor: placeholderStyle?.color,
262
264
  multiline: !!multiline
263
- }, layoutProps, {
264
- onTouchStart: onInputTouchStart,
265
- onFocus: onInputFocus,
266
- onBlur: onInputBlur,
267
- onKeyPress: bindconfirm && onKeyPress,
268
- onSubmitEditing: bindconfirm && multiline && onSubmitEditing,
269
- onSelectionChange: onSelectionChange,
270
- onTextInput: onTextInput,
271
- onChange: onChange,
272
- onContentSizeChange: onContentSizeChange
265
+ }, !!multiline && confirmType === 'return' ? {} : { enterKeyHint: confirmType }, layoutProps, {
266
+ onTouchStart,
267
+ onFocus,
268
+ onBlur,
269
+ onChange,
270
+ onSelectionChange,
271
+ onContentSizeChange,
272
+ onSubmitEditing: bindconfirm && !multiline && onSubmitEditing
273
273
  }), [
274
274
  'type',
275
275
  'password',