@mpxjs/webpack-plugin 2.9.69-beta.3 → 2.9.69-beta.4
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/index.js +17 -1
- package/lib/platform/style/wx/index.js +18 -18
- package/lib/platform/template/wx/component-config/movable-view.js +8 -1
- package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
- package/lib/react/processScript.js +1 -1
- package/lib/resolver/AddEnvPlugin.js +1 -0
- package/lib/resolver/AddModePlugin.js +1 -0
- package/lib/runtime/components/react/context.ts +25 -0
- package/lib/runtime/components/react/dist/context.js +2 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +2 -2
- package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +9 -37
- package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +12 -13
- package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
- package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
- package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -16
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +10 -5
- package/lib/runtime/components/react/dist/mpx-view.jsx +15 -21
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +78 -20
- package/lib/runtime/components/react/dist/pickerFaces.js +1 -1
- package/lib/runtime/components/react/dist/useAnimationHooks.js +14 -2
- package/lib/runtime/components/react/getInnerListeners.ts +5 -7
- package/lib/runtime/components/react/locale-provider.tsx +83 -0
- package/lib/runtime/components/react/mpx-button.tsx +13 -49
- package/lib/runtime/components/react/mpx-image.tsx +41 -25
- package/lib/runtime/components/react/mpx-input.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-view.tsx +1 -1
- package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
- package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +88 -0
- package/lib/runtime/components/react/mpx-picker-view-column.tsx +180 -163
- package/lib/runtime/components/react/mpx-picker-view.tsx +35 -37
- package/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +32 -0
- package/lib/runtime/components/react/mpx-portal/portal-host.tsx +158 -0
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
- package/lib/runtime/components/react/mpx-portal.tsx +29 -0
- package/lib/runtime/components/react/mpx-provider.tsx +51 -0
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
- package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
- package/lib/runtime/components/react/mpx-scroll-view.tsx +29 -18
- package/lib/runtime/components/react/mpx-swiper-item.tsx +38 -10
- package/lib/runtime/components/react/mpx-swiper.tsx +696 -0
- package/lib/runtime/components/react/mpx-view.tsx +25 -71
- package/lib/runtime/components/react/mpx-web-view.tsx +156 -23
- package/lib/runtime/components/react/pickerFaces.ts +15 -7
- package/lib/runtime/components/react/pickerVIewContext.ts +18 -0
- package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
- package/lib/runtime/components/react/{pickerOverlay.tsx → pickerViewOverlay.tsx} +5 -3
- package/lib/runtime/components/react/types/global.d.ts +6 -1
- package/lib/runtime/components/react/useAnimationHooks.ts +20 -4
- package/lib/runtime/components/react/utils.tsx +75 -5
- package/lib/style-compiler/index.js +3 -4
- package/lib/style-compiler/strip-conditional-loader.js +118 -0
- package/lib/template-compiler/compiler.js +9 -14
- package/lib/utils/hump-dash.js +1 -1
- package/lib/utils/pre-process-json.js +5 -9
- package/package.json +1 -1
- package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
- package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
- package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { View } from 'react-native'
|
|
2
|
-
import React, { forwardRef,
|
|
2
|
+
import React, { forwardRef, useRef } from 'react'
|
|
3
3
|
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
4
4
|
import useNodesRef, { HandlerRef } from './useNodesRef'
|
|
5
5
|
import {
|
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
wrapChildren,
|
|
10
10
|
parseInlineStyle,
|
|
11
11
|
useTransformStyle,
|
|
12
|
-
useDebounceCallback,
|
|
13
|
-
useStableCallback,
|
|
14
12
|
extendObject
|
|
15
13
|
} from './utils'
|
|
16
14
|
import type { AnyFunc } from './types/common'
|
|
@@ -21,21 +19,21 @@ import type { AnyFunc } from './types/common'
|
|
|
21
19
|
* ✘ bindpickend
|
|
22
20
|
* ✘ mask-class
|
|
23
21
|
* ✔ indicator-style: 优先级indicator-style.height > pick-view-column中的子元素设置的height
|
|
22
|
+
* WebView Only:
|
|
24
23
|
* ✘ indicator-class
|
|
25
|
-
*
|
|
24
|
+
* ✔ mask-style
|
|
26
25
|
* ✘ immediate-change
|
|
27
26
|
*/
|
|
28
27
|
|
|
29
28
|
interface PickerViewProps {
|
|
30
29
|
children: React.ReactNode
|
|
31
|
-
// 初始的defaultValue数组中的数字依次表示 picker-view 内的 picker-view-column 选择的第几项(下标从 0 开始),
|
|
32
|
-
// 数字大于 picker-view-column 可选项长度时,选择最后一项。
|
|
33
30
|
value?: Array<number>
|
|
34
31
|
bindchange?: AnyFunc
|
|
35
32
|
style: {
|
|
36
33
|
[key: string]: any
|
|
37
34
|
}
|
|
38
35
|
'indicator-style'?: string
|
|
36
|
+
'mask-style'?: string
|
|
39
37
|
'enable-var': boolean
|
|
40
38
|
'external-var-context'?: Record<string, any>,
|
|
41
39
|
'enable-offset': boolean
|
|
@@ -62,6 +60,8 @@ const styles: { [key: string]: Object } = {
|
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
|
|
63
|
+
const DefaultPickerItemH = 36
|
|
64
|
+
|
|
65
65
|
const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProps>((props: PickerViewProps, ref) => {
|
|
66
66
|
const {
|
|
67
67
|
children,
|
|
@@ -71,16 +71,16 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
71
71
|
'enable-var': enableVar,
|
|
72
72
|
'external-var-context': externalVarContext
|
|
73
73
|
} = props
|
|
74
|
-
|
|
75
|
-
// indicatorStyle 需要转换为rn的style
|
|
76
|
-
// 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传
|
|
77
74
|
const indicatorStyle = parseInlineStyle(props['indicator-style'])
|
|
75
|
+
const pickerMaskStyle = parseInlineStyle(props['mask-style'])
|
|
78
76
|
const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle
|
|
79
|
-
const [pickMaxH, setPickMaxH] = useState(0)
|
|
80
77
|
const nodeRef = useRef(null)
|
|
81
78
|
const cloneRef = useRef(null)
|
|
82
79
|
const activeValueRef = useRef(value)
|
|
83
80
|
activeValueRef.current = value.slice()
|
|
81
|
+
const snapActiveValueRef = useRef<number[] | null>(null)
|
|
82
|
+
|
|
83
|
+
console.log('[mpx-picker-view] value=', value, Date.now())
|
|
84
84
|
|
|
85
85
|
const {
|
|
86
86
|
normalStyle,
|
|
@@ -96,7 +96,6 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
96
96
|
})
|
|
97
97
|
|
|
98
98
|
const {
|
|
99
|
-
// 存储layout布局信息
|
|
100
99
|
layoutRef,
|
|
101
100
|
layoutProps,
|
|
102
101
|
layoutStyle
|
|
@@ -104,33 +103,34 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
104
103
|
const { textProps } = splitProps(props)
|
|
105
104
|
const { textStyle } = splitStyle(normalStyle)
|
|
106
105
|
|
|
107
|
-
const onColumnItemRawHChange = (height: number) => {
|
|
108
|
-
if (height > pickMaxH) {
|
|
109
|
-
setPickMaxH(height)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 300)
|
|
114
|
-
|
|
115
106
|
const onSelectChange = (columnIndex: number, selectedIndex: number) => {
|
|
116
|
-
bindchangeDebounce.clear()
|
|
117
107
|
const activeValue = activeValueRef.current
|
|
118
108
|
activeValue[columnIndex] = selectedIndex
|
|
109
|
+
console.log('[mpx-picker-view], onSelectChange ---> columnIndex=', columnIndex, 'selectedIndex=', selectedIndex, 'activeValue=', activeValue)
|
|
119
110
|
const eventData = getCustomEvent(
|
|
120
111
|
'change',
|
|
121
112
|
{},
|
|
122
113
|
{ detail: { value: activeValue, source: 'change' }, layoutRef }
|
|
123
114
|
)
|
|
124
|
-
|
|
115
|
+
bindchange?.(eventData)
|
|
116
|
+
snapActiveValueRef.current = activeValueRef.current
|
|
125
117
|
}
|
|
126
118
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
133
|
-
|
|
119
|
+
const hasDiff = (a: number[] = [], b: number[]) => {
|
|
120
|
+
return a.some((v, i) => v !== b[i])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const onInitialChange = (isInvalid: boolean, value: number[]) => {
|
|
124
|
+
if (isInvalid || !snapActiveValueRef.current || hasDiff(snapActiveValueRef.current, value)) {
|
|
125
|
+
console.log('[mpx-picker-view], onInitialChange ===> value=', value)
|
|
126
|
+
const eventData = getCustomEvent(
|
|
127
|
+
'change',
|
|
128
|
+
{},
|
|
129
|
+
{ detail: { value, source: 'change' }, layoutRef }
|
|
130
|
+
)
|
|
131
|
+
bindchange?.(eventData)
|
|
132
|
+
snapActiveValueRef.current = value.slice()
|
|
133
|
+
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const innerProps = useInnerProps(
|
|
@@ -153,7 +153,6 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
const renderColumn = (child: React.ReactElement, index: number, columnData: React.ReactNode[], initialIndex: number) => {
|
|
156
|
-
const extraProps = {}
|
|
157
156
|
const childProps = child?.props || {}
|
|
158
157
|
const wrappedProps = extendObject(
|
|
159
158
|
{},
|
|
@@ -164,15 +163,15 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
164
163
|
columnIndex: index,
|
|
165
164
|
key: `pick-view-${index}`,
|
|
166
165
|
wrapperStyle: {
|
|
167
|
-
height: normalStyle?.height ||
|
|
168
|
-
itemHeight: indicatorH ||
|
|
166
|
+
height: normalStyle?.height || DefaultPickerItemH,
|
|
167
|
+
itemHeight: indicatorH || DefaultPickerItemH
|
|
169
168
|
},
|
|
170
|
-
|
|
169
|
+
columnStyle: normalStyle,
|
|
171
170
|
onSelectChange: onSelectChange.bind(null, index),
|
|
172
171
|
initialIndex,
|
|
173
|
-
pickerOverlayStyle
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
pickerOverlayStyle,
|
|
173
|
+
pickerMaskStyle
|
|
174
|
+
}
|
|
176
175
|
)
|
|
177
176
|
const realElement = React.cloneElement(child, wrappedProps)
|
|
178
177
|
return wrapChildren(
|
|
@@ -215,7 +214,7 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
215
214
|
validValue.push(validIndex)
|
|
216
215
|
renderColumns.push(renderColumn(item, index, columnData, validIndex))
|
|
217
216
|
})
|
|
218
|
-
isInvalid
|
|
217
|
+
onInitialChange(isInvalid, validValue)
|
|
219
218
|
return renderColumns
|
|
220
219
|
}
|
|
221
220
|
|
|
@@ -227,5 +226,4 @@ const _PickerView = forwardRef<HandlerRef<View, PickerViewProps>, PickerViewProp
|
|
|
227
226
|
})
|
|
228
227
|
|
|
229
228
|
_PickerView.displayName = 'MpxPickerView'
|
|
230
|
-
|
|
231
229
|
export default _PickerView
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useRef, ReactNode } from 'react'
|
|
2
|
+
import { PortalContextValue } from '../context'
|
|
3
|
+
import { getFocusedNavigation } from '@mpxjs/utils'
|
|
4
|
+
|
|
5
|
+
export type PortalConsumerProps = {
|
|
6
|
+
manager: PortalContextValue
|
|
7
|
+
children?: ReactNode
|
|
8
|
+
}
|
|
9
|
+
const PortalConsumer = ({ manager, children } :PortalConsumerProps): JSX.Element | null => {
|
|
10
|
+
const keyRef = useRef<any>(null)
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const navigation = getFocusedNavigation()
|
|
13
|
+
const curPageId = navigation?.pageId
|
|
14
|
+
manager.update(keyRef.current, children, curPageId)
|
|
15
|
+
}, [children])
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!manager) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal`.\n\n'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
const navigation = getFocusedNavigation()
|
|
23
|
+
const curPageId = navigation?.pageId
|
|
24
|
+
keyRef.current = manager.mount(children, undefined, curPageId)
|
|
25
|
+
return () => {
|
|
26
|
+
manager.unmount(keyRef.current, curPageId)
|
|
27
|
+
}
|
|
28
|
+
}, [])
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default PortalConsumer
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { useEffect, useRef, ReactNode } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
DeviceEventEmitter,
|
|
5
|
+
EventSubscription,
|
|
6
|
+
NativeEventEmitter,
|
|
7
|
+
StyleSheet
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import PortalManager from './portal-manager'
|
|
10
|
+
import { getFocusedNavigation } from '@mpxjs/utils'
|
|
11
|
+
import { PortalManagerContextValue, PortalContext } from '../context'
|
|
12
|
+
|
|
13
|
+
export type PortalHostProps = {
|
|
14
|
+
children: ReactNode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type addIdsMapsType = {
|
|
18
|
+
[key: number]: number[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Operation =
|
|
22
|
+
| { type: 'mount'; key: number; children: ReactNode }
|
|
23
|
+
| { type: 'update'; key: number; children: ReactNode }
|
|
24
|
+
| { type: 'unmount'; key: number }
|
|
25
|
+
|
|
26
|
+
// events
|
|
27
|
+
const addType = 'MPX_RN_ADD_PORTAL'
|
|
28
|
+
const removeType = 'MPX_RN_REMOVE_PORTAL'
|
|
29
|
+
// fix react native web does not support DeviceEventEmitter
|
|
30
|
+
const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter()
|
|
31
|
+
|
|
32
|
+
const styles = StyleSheet.create({
|
|
33
|
+
container: {
|
|
34
|
+
flex: 1
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
class PortalGuard {
|
|
39
|
+
private nextKey = 10000
|
|
40
|
+
add = (e: ReactNode) => {
|
|
41
|
+
const key = this.nextKey++
|
|
42
|
+
TopViewEventEmitter.emit(addType, e, key)
|
|
43
|
+
return key
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
remove = (key: number) => {
|
|
47
|
+
TopViewEventEmitter.emit(removeType, key)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* portal
|
|
52
|
+
*/
|
|
53
|
+
export const portal = new PortalGuard()
|
|
54
|
+
|
|
55
|
+
const PortalHost = ({ children } :PortalHostProps): JSX.Element => {
|
|
56
|
+
const _nextKey = useRef(0)
|
|
57
|
+
const _queue = useRef<Operation[]>([])
|
|
58
|
+
const _addType = useRef<EventSubscription | null>(null)
|
|
59
|
+
const _removeType = useRef<EventSubscription | null>(null)
|
|
60
|
+
const manager = useRef<PortalManagerContextValue | null>(null)
|
|
61
|
+
let currentPageId: number | undefined
|
|
62
|
+
const _mount = (children: ReactNode, _key?: number, curPageId?: number) => {
|
|
63
|
+
const navigation = getFocusedNavigation()
|
|
64
|
+
const pageId = navigation?.pageId
|
|
65
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
const key = _key || _nextKey.current++
|
|
69
|
+
if (manager.current) {
|
|
70
|
+
manager.current.mount(key, children)
|
|
71
|
+
} else {
|
|
72
|
+
_queue.current.push({ type: 'mount', key, children })
|
|
73
|
+
}
|
|
74
|
+
return key
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const _unmount = (key: number, curPageId?: number) => {
|
|
78
|
+
const navigation = getFocusedNavigation()
|
|
79
|
+
const pageId = navigation?.pageId
|
|
80
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
if (manager.current) {
|
|
84
|
+
manager.current.unmount(key)
|
|
85
|
+
} else {
|
|
86
|
+
_queue.current.push({ type: 'unmount', key })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const _update = (key: number, children: ReactNode, curPageId?: number) => {
|
|
91
|
+
const navigation = getFocusedNavigation()
|
|
92
|
+
const pageId = navigation?.pageId
|
|
93
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
if (manager.current) {
|
|
97
|
+
manager.current.update(key, children)
|
|
98
|
+
} else {
|
|
99
|
+
const op: Operation = { type: 'mount', key, children }
|
|
100
|
+
const index = _queue.current.findIndex(
|
|
101
|
+
(o) => o.type === 'mount' || (o.type === 'update' && o.key === key)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if (index > -1) {
|
|
105
|
+
_queue.current[index] = op
|
|
106
|
+
} else {
|
|
107
|
+
_queue.current.push(op)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const navigation = getFocusedNavigation()
|
|
114
|
+
currentPageId = navigation?.pageId
|
|
115
|
+
_addType.current = TopViewEventEmitter.addListener(addType, _mount)
|
|
116
|
+
_removeType.current = TopViewEventEmitter.addListener(
|
|
117
|
+
removeType,
|
|
118
|
+
_unmount
|
|
119
|
+
)
|
|
120
|
+
return () => {
|
|
121
|
+
while (_queue.current.length && manager.current) {
|
|
122
|
+
const action = _queue.current.pop()
|
|
123
|
+
if (!action) {
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
// tslint:disable-next-line:switch-default
|
|
127
|
+
switch (action.type) {
|
|
128
|
+
case 'mount':
|
|
129
|
+
manager.current?.mount(action.key, action.children)
|
|
130
|
+
break
|
|
131
|
+
case 'update':
|
|
132
|
+
manager.current?.update(action.key, action.children)
|
|
133
|
+
break
|
|
134
|
+
case 'unmount':
|
|
135
|
+
manager.current?.unmount(action.key)
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}, [])
|
|
141
|
+
return (
|
|
142
|
+
<PortalContext.Provider
|
|
143
|
+
value={{
|
|
144
|
+
mount: _mount,
|
|
145
|
+
update: _update,
|
|
146
|
+
unmount: _unmount
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */}
|
|
150
|
+
<View style={styles.container} collapsable={false}>
|
|
151
|
+
{children}
|
|
152
|
+
</View>
|
|
153
|
+
<PortalManager ref={manager} />
|
|
154
|
+
</PortalContext.Provider>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default PortalHost
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react'
|
|
2
|
+
import { View, StyleSheet } from 'react-native'
|
|
3
|
+
|
|
4
|
+
export type State = {
|
|
5
|
+
portals: Array<{
|
|
6
|
+
key: number
|
|
7
|
+
children: ReactNode
|
|
8
|
+
}>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type PortalManagerProps = {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef<unknown>): ReactElement => {
|
|
15
|
+
const [state, setState] = useState<State>({
|
|
16
|
+
portals: []
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const mount = useCallback((key: number, children: ReactNode) => {
|
|
20
|
+
setState((prevState) => ({
|
|
21
|
+
portals: [...prevState.portals, { key, children }]
|
|
22
|
+
}))
|
|
23
|
+
}, [state])
|
|
24
|
+
|
|
25
|
+
const update = useCallback((key: number, children: ReactNode) => {
|
|
26
|
+
setState((prevState) => ({
|
|
27
|
+
portals: prevState.portals.map((item) => {
|
|
28
|
+
if (item.key === key) {
|
|
29
|
+
return { ...item, children }
|
|
30
|
+
}
|
|
31
|
+
return item
|
|
32
|
+
})
|
|
33
|
+
}))
|
|
34
|
+
}, [state])
|
|
35
|
+
|
|
36
|
+
const unmount = useCallback((key: number) => {
|
|
37
|
+
setState((prevState) => ({
|
|
38
|
+
portals: prevState.portals.filter((item) => item.key !== key)
|
|
39
|
+
}))
|
|
40
|
+
}, [])
|
|
41
|
+
|
|
42
|
+
useImperativeHandle(ref, () => ({
|
|
43
|
+
mount,
|
|
44
|
+
update,
|
|
45
|
+
unmount,
|
|
46
|
+
portals: state.portals
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
{state.portals.map(({ key, children }, i) => (
|
|
52
|
+
<View
|
|
53
|
+
key={key}
|
|
54
|
+
collapsable={false} // Need collapsable=false here to clip the elevations
|
|
55
|
+
pointerEvents="box-none"
|
|
56
|
+
style={[StyleSheet.absoluteFill, { zIndex: 1000 + i }]}>
|
|
57
|
+
{children}
|
|
58
|
+
</View>
|
|
59
|
+
))}
|
|
60
|
+
</>
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export default _PortalManager
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import { PortalContext, PortalContextValue } from './context'
|
|
3
|
+
import PortalConsumer from './mpx-portal/portal-consumer'
|
|
4
|
+
import PortalHost, { portal } from './mpx-portal/portal-host'
|
|
5
|
+
|
|
6
|
+
export type PortalProps = {
|
|
7
|
+
/**
|
|
8
|
+
* Content of the `Portal`.
|
|
9
|
+
*/
|
|
10
|
+
children?: ReactNode
|
|
11
|
+
key?: string
|
|
12
|
+
manager?: PortalContextValue
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Portal = ({ children }:PortalProps): JSX.Element => {
|
|
16
|
+
return (
|
|
17
|
+
<PortalContext.Consumer>
|
|
18
|
+
{(manager) => (
|
|
19
|
+
<PortalConsumer manager={manager}>{children}</PortalConsumer>
|
|
20
|
+
)}
|
|
21
|
+
</PortalContext.Consumer>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Portal.Host = PortalHost
|
|
26
|
+
Portal.add = portal.add
|
|
27
|
+
Portal.remove = portal.remove
|
|
28
|
+
|
|
29
|
+
export default Portal
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ReactNode, createContext, useMemo } from 'react'
|
|
2
|
+
import LocaleProvider, { LocaleContextProps } from './locale-provider'
|
|
3
|
+
import Portal from './mpx-portal'
|
|
4
|
+
import { extendObject } from './utils'
|
|
5
|
+
|
|
6
|
+
export type Theme = typeof defaultTheme & { [key: string]: any }
|
|
7
|
+
|
|
8
|
+
export interface ProviderProps {
|
|
9
|
+
locale?: LocaleContextProps
|
|
10
|
+
theme?: Partial<Theme>
|
|
11
|
+
children: ReactNode
|
|
12
|
+
}
|
|
13
|
+
const defaultTheme = {
|
|
14
|
+
color_text_base: '#000000', // 基本
|
|
15
|
+
color_text_base_inverse: '#ffffff', // 基本 _ 反色
|
|
16
|
+
color_text_secondary: '#a4a9b0', // 辅助色
|
|
17
|
+
color_text_placeholder: '#bbbbbb', // 文本框提示
|
|
18
|
+
color_text_disabled: '#bbbbbb', // 失效
|
|
19
|
+
color_text_caption: '#888888', // 辅助描述
|
|
20
|
+
color_text_paragraph: '#333333', // 段落
|
|
21
|
+
color_error: '#ff4d4f', // 错误(form validate)
|
|
22
|
+
color_warning: '#faad14', // 警告
|
|
23
|
+
color_success: '#52c41a',
|
|
24
|
+
color_primary: '#1677ff'
|
|
25
|
+
}
|
|
26
|
+
export const ThemeContext = createContext(defaultTheme)
|
|
27
|
+
|
|
28
|
+
export type PartialTheme = Partial<Theme>
|
|
29
|
+
|
|
30
|
+
export interface ThemeProviderProps {
|
|
31
|
+
value?: PartialTheme
|
|
32
|
+
children?: React.ReactNode
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ThemeProvider = (props: ThemeProviderProps) => {
|
|
36
|
+
const { value, children } = props
|
|
37
|
+
const theme = useMemo(() => (extendObject({}, defaultTheme, value)), [value])
|
|
38
|
+
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const Provider = ({ locale, theme, children }:ProviderProps): JSX.Element => {
|
|
42
|
+
return (
|
|
43
|
+
<LocaleProvider locale={locale}>
|
|
44
|
+
<ThemeProvider value={theme}>
|
|
45
|
+
<Portal.Host>{children}</Portal.Host>
|
|
46
|
+
</ThemeProvider>
|
|
47
|
+
</LocaleProvider>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default Provider
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* ✔ nodes
|
|
4
4
|
*/
|
|
5
5
|
import { View, ViewProps, ViewStyle } from 'react-native'
|
|
6
|
-
import { useRef, forwardRef, JSX, useState } from 'react'
|
|
6
|
+
import { useRef, forwardRef, JSX, useState, createElement } from 'react'
|
|
7
7
|
import useInnerProps from '../getInnerListeners'
|
|
8
8
|
import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数
|
|
9
|
-
import { useTransformStyle, useLayout } from '../utils'
|
|
9
|
+
import { useTransformStyle, useLayout, extendObject } from '../utils'
|
|
10
10
|
import { WebView, WebViewMessageEvent } from 'react-native-webview'
|
|
11
11
|
import { generateHTML } from './html'
|
|
12
12
|
|
|
@@ -91,28 +91,22 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _RichTextProps>((
|
|
|
91
91
|
layoutRef
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
const innerProps = useInnerProps(props, {
|
|
94
|
+
const innerProps = useInnerProps(props, extendObject({
|
|
95
95
|
ref: nodeRef,
|
|
96
|
-
style:
|
|
97
|
-
|
|
98
|
-
}, [], {
|
|
96
|
+
style: extendObject(normalStyle, layoutStyle)
|
|
97
|
+
}, layoutProps), [], {
|
|
99
98
|
layoutRef
|
|
100
99
|
})
|
|
101
100
|
|
|
102
101
|
const html: string = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes)
|
|
103
102
|
|
|
104
|
-
return (
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
setWebViewHeight(+event.nativeEvent.data)
|
|
112
|
-
}}
|
|
113
|
-
>
|
|
114
|
-
</WebView>
|
|
115
|
-
</View>
|
|
103
|
+
return createElement(View, innerProps,
|
|
104
|
+
createElement(WebView, {
|
|
105
|
+
source: { html: generateHTML(html) },
|
|
106
|
+
onMessage: (event: WebViewMessageEvent) => {
|
|
107
|
+
setWebViewHeight(+event.nativeEvent.data)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
116
110
|
)
|
|
117
111
|
})
|
|
118
112
|
|
|
@@ -32,15 +32,14 @@
|
|
|
32
32
|
* ✔ bindscroll
|
|
33
33
|
*/
|
|
34
34
|
import { ScrollView } from 'react-native-gesture-handler'
|
|
35
|
-
import { View, RefreshControl, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle } from 'react-native'
|
|
36
|
-
import { JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef, useContext, createElement } from 'react'
|
|
35
|
+
import { View, RefreshControl, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle, Platform } from 'react-native'
|
|
36
|
+
import { JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react'
|
|
37
37
|
import { useAnimatedRef } from 'react-native-reanimated'
|
|
38
38
|
import { warn } from '@mpxjs/utils'
|
|
39
39
|
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
40
40
|
import useNodesRef, { HandlerRef } from './useNodesRef'
|
|
41
41
|
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler } from './utils'
|
|
42
|
-
import { IntersectionObserverContext } from './context'
|
|
43
|
-
|
|
42
|
+
import { IntersectionObserverContext, ScrollViewContext } from './context'
|
|
44
43
|
interface ScrollViewProps {
|
|
45
44
|
children?: ReactNode;
|
|
46
45
|
enhanced?: boolean;
|
|
@@ -194,6 +193,12 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
194
193
|
gestureRef: scrollViewRef
|
|
195
194
|
})
|
|
196
195
|
|
|
196
|
+
const contextValue = useMemo(() => {
|
|
197
|
+
return {
|
|
198
|
+
gestureRef: scrollViewRef
|
|
199
|
+
}
|
|
200
|
+
}, [])
|
|
201
|
+
|
|
197
202
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
|
|
198
203
|
|
|
199
204
|
if (scrollX && scrollY) {
|
|
@@ -332,11 +337,6 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
332
337
|
}, props)
|
|
333
338
|
)
|
|
334
339
|
updateScrollOptions(e, { scrollLeft, scrollTop })
|
|
335
|
-
if (enableTriggerIntersectionObserver && intersectionObservers) {
|
|
336
|
-
for (const key in intersectionObservers) {
|
|
337
|
-
intersectionObservers[key].throttleMeasure()
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
function onScrollEnd (e: NativeSyntheticEvent<NativeScrollEvent>) {
|
|
@@ -358,8 +358,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
358
358
|
updateScrollOptions(e, { scrollLeft, scrollTop })
|
|
359
359
|
onStartReached(e)
|
|
360
360
|
onEndReached(e)
|
|
361
|
+
updateIntersection()
|
|
362
|
+
}
|
|
363
|
+
function updateIntersection () {
|
|
364
|
+
if (enableTriggerIntersectionObserver && intersectionObservers) {
|
|
365
|
+
for (const key in intersectionObservers) {
|
|
366
|
+
intersectionObservers[key].throttleMeasure();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
361
369
|
}
|
|
362
|
-
|
|
363
370
|
function scrollToOffset (x = 0, y = 0) {
|
|
364
371
|
if (scrollViewRef.current) {
|
|
365
372
|
scrollViewRef.current.scrollTo({ x, y, animated: !!scrollWithAnimation })
|
|
@@ -429,6 +436,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
429
436
|
function onScrollDrag (e: NativeSyntheticEvent<NativeScrollEvent>) {
|
|
430
437
|
const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset
|
|
431
438
|
updateScrollOptions(e, { scrollLeft, scrollTop })
|
|
439
|
+
updateIntersection()
|
|
432
440
|
}
|
|
433
441
|
|
|
434
442
|
const scrollAdditionalProps: ScrollAdditionalProps = extendObject(
|
|
@@ -507,14 +515,17 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
507
515
|
}, (refresherDefaultStyle && refresherDefaultStyle !== 'none' ? { colors: refreshColor[refresherDefaultStyle] } : null)))
|
|
508
516
|
: undefined
|
|
509
517
|
}),
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
+
createElement(ScrollViewContext.Provider,
|
|
519
|
+
{ value: contextValue },
|
|
520
|
+
wrapChildren(
|
|
521
|
+
props,
|
|
522
|
+
{
|
|
523
|
+
hasVarDec,
|
|
524
|
+
varContext: varContextRef.current,
|
|
525
|
+
textStyle,
|
|
526
|
+
textProps
|
|
527
|
+
}
|
|
528
|
+
)
|
|
518
529
|
)
|
|
519
530
|
)
|
|
520
531
|
})
|