@mpxjs/webpack-plugin 2.8.25-alpha.21 → 2.8.25-alpha.22

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 (72) hide show
  1. package/lib/runtime/components/react/dist/KeyboardAvoidingView.jsx +89 -0
  2. package/lib/runtime/components/react/dist/context.js +14 -0
  3. package/lib/runtime/components/react/dist/event.config.js +27 -0
  4. package/lib/runtime/components/react/dist/getInnerListeners.js +262 -0
  5. package/lib/runtime/components/react/dist/mpx-button.jsx +271 -0
  6. package/lib/runtime/components/react/dist/mpx-canvas/Bus.js +60 -0
  7. package/lib/runtime/components/react/dist/mpx-canvas/CanvasGradient.js +15 -0
  8. package/lib/runtime/components/react/dist/mpx-canvas/CanvasRenderingContext2D.js +84 -0
  9. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +87 -0
  10. package/lib/runtime/components/react/dist/mpx-canvas/ImageData.js +15 -0
  11. package/lib/runtime/components/react/dist/mpx-canvas/constructorsRegistry.js +28 -0
  12. package/lib/runtime/components/react/dist/mpx-canvas/html.js +341 -0
  13. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +236 -0
  14. package/lib/runtime/components/react/dist/mpx-canvas/utils.jsx +89 -0
  15. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +90 -0
  16. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +131 -0
  17. package/lib/runtime/components/react/dist/mpx-form.jsx +68 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  21. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  22. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  23. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  24. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  25. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  26. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  27. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +50 -0
  28. package/lib/runtime/components/react/dist/mpx-image.jsx +292 -0
  29. package/lib/runtime/components/react/dist/mpx-input.jsx +292 -0
  30. package/lib/runtime/components/react/dist/mpx-label.jsx +52 -0
  31. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +32 -0
  32. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +468 -0
  33. package/lib/runtime/components/react/dist/mpx-navigator.jsx +33 -0
  34. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +74 -0
  35. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +141 -0
  36. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +147 -0
  37. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +99 -0
  38. package/lib/runtime/components/react/dist/mpx-picker/regionData.js +6099 -0
  39. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +81 -0
  40. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +242 -0
  41. package/lib/runtime/components/react/dist/mpx-picker/type.js +1 -0
  42. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
  43. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +193 -0
  44. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +125 -0
  45. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +30 -0
  46. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +112 -0
  47. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +41 -0
  48. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +86 -0
  49. package/lib/runtime/components/react/dist/mpx-radio.jsx +140 -0
  50. package/lib/runtime/components/react/dist/mpx-rich-text/html.js +39 -0
  51. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +62 -0
  52. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +17 -0
  53. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +372 -0
  54. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +11 -0
  55. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +59 -0
  56. package/lib/runtime/components/react/dist/mpx-swiper.jsx +671 -0
  57. package/lib/runtime/components/react/dist/mpx-switch.jsx +97 -0
  58. package/lib/runtime/components/react/dist/mpx-text.jsx +41 -0
  59. package/lib/runtime/components/react/dist/mpx-textarea.jsx +40 -0
  60. package/lib/runtime/components/react/dist/mpx-video.jsx +248 -0
  61. package/lib/runtime/components/react/dist/mpx-view.jsx +611 -0
  62. package/lib/runtime/components/react/dist/mpx-web-view.jsx +289 -0
  63. package/lib/runtime/components/react/dist/parser.js +218 -0
  64. package/lib/runtime/components/react/dist/pickerFaces.js +76 -0
  65. package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
  66. package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
  67. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  68. package/lib/runtime/components/react/dist/useAnimationHooks.js +346 -0
  69. package/lib/runtime/components/react/dist/useNodesRef.js +16 -0
  70. package/lib/runtime/components/react/dist/utils.jsx +599 -0
  71. package/package.json +6 -3
  72. package/LICENSE +0 -433
@@ -0,0 +1,289 @@
1
+ import { forwardRef, useRef, useContext, useMemo, useState, useEffect } from 'react';
2
+ import { warn, isFunction } from '@mpxjs/utils';
3
+ import Portal from './mpx-portal/index';
4
+ import { getCustomEvent } from './getInnerListeners';
5
+ import { promisify, redirectTo, navigateTo, navigateBack, reLaunch, switchTab } from '@mpxjs/api-proxy';
6
+ import { WebView } from 'react-native-webview';
7
+ import useNodesRef from './useNodesRef';
8
+ import { getCurrentPage, useNavigation } from './utils';
9
+ import { RouteContext } from './context';
10
+ import { StyleSheet, View, Text } from 'react-native';
11
+ const styles = StyleSheet.create({
12
+ loadErrorContext: {
13
+ display: 'flex',
14
+ alignItems: 'center'
15
+ },
16
+ loadErrorText: {
17
+ fontSize: 12,
18
+ color: '#666666',
19
+ paddingTop: '40%',
20
+ paddingBottom: 20,
21
+ paddingLeft: '10%',
22
+ paddingRight: '10%',
23
+ textAlign: 'center'
24
+ },
25
+ loadErrorButton: {
26
+ color: '#666666',
27
+ textAlign: 'center',
28
+ padding: 10,
29
+ borderColor: '#666666',
30
+ borderStyle: 'solid',
31
+ borderWidth: StyleSheet.hairlineWidth,
32
+ borderRadius: 10
33
+ }
34
+ });
35
+ const _WebView = forwardRef((props, ref) => {
36
+ const { src, bindmessage, bindload, binderror } = props;
37
+ const mpx = global.__mpx;
38
+ const errorText = {
39
+ 'zh-CN': {
40
+ text: '网络不可用,请检查网络设置',
41
+ button: '重新加载'
42
+ },
43
+ 'en-US': {
44
+ text: 'The network is not available. Please check the network settings',
45
+ button: 'Reload'
46
+ }
47
+ };
48
+ const currentErrorText = errorText[mpx.i18n.locale || 'zh-CN'];
49
+ if (props.style) {
50
+ warn('The web-view component does not support the style prop.');
51
+ }
52
+ const { pageId } = useContext(RouteContext) || {};
53
+ const [pageLoadErr, setPageLoadErr] = useState(false);
54
+ const currentPage = useMemo(() => getCurrentPage(pageId), [pageId]);
55
+ const webViewRef = useRef(null);
56
+ const fristLoaded = useRef(false);
57
+ const isLoadError = useRef(false);
58
+ const statusCode = useRef('');
59
+ const [isLoaded, setIsLoaded] = useState(true);
60
+ const defaultWebViewStyle = {
61
+ position: 'absolute',
62
+ left: 0,
63
+ right: 0,
64
+ top: 0,
65
+ bottom: 0
66
+ };
67
+ const canGoBack = useRef(false);
68
+ const isNavigateBack = useRef(false);
69
+ const beforeRemoveHandle = (e) => {
70
+ if (canGoBack.current && !isNavigateBack.current) {
71
+ webViewRef.current?.goBack();
72
+ e.preventDefault();
73
+ }
74
+ isNavigateBack.current = false;
75
+ };
76
+ const navigation = useNavigation();
77
+ useEffect(() => {
78
+ const beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle);
79
+ return () => {
80
+ if (isFunction(beforeRemoveSubscription)) {
81
+ beforeRemoveSubscription();
82
+ }
83
+ };
84
+ }, []);
85
+ useNodesRef(props, ref, webViewRef, {
86
+ style: defaultWebViewStyle
87
+ });
88
+ if (!src) {
89
+ return null;
90
+ }
91
+ const _reload = function () {
92
+ if (__mpx_mode__ === 'android') {
93
+ fristLoaded.current = false; // 安卓需要重新设置
94
+ }
95
+ setPageLoadErr(false);
96
+ };
97
+ const injectedJavaScript = `
98
+ if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
99
+ var _documentTitle = document.title;
100
+ window.ReactNativeWebView.postMessage(JSON.stringify({
101
+ type: 'setTitle',
102
+ payload: {
103
+ _documentTitle: _documentTitle
104
+ }
105
+ }))
106
+ Object.defineProperty(document, 'title', {
107
+ set (val) {
108
+ _documentTitle = val
109
+ window.ReactNativeWebView.postMessage(JSON.stringify({
110
+ type: 'setTitle',
111
+ payload: {
112
+ _documentTitle: _documentTitle
113
+ }
114
+ }))
115
+ },
116
+ get () {
117
+ return _documentTitle
118
+ }
119
+ });
120
+ }
121
+ true;
122
+ `;
123
+ const sendMessage = function (params) {
124
+ return `
125
+ window.mpxWebviewMessageCallback && window.mpxWebviewMessageCallback(${params})
126
+ true;
127
+ `;
128
+ };
129
+ const _changeUrl = function (navState) {
130
+ if (navState.navigationType) { // navigationType这个事件在页面开始加载时和页面加载完成时都会被触发所以判断这个避免其他无效触发执行该逻辑
131
+ canGoBack.current = navState.canGoBack;
132
+ currentPage.__webViewUrl = navState.url;
133
+ }
134
+ };
135
+ const _onLoadProgress = function (event) {
136
+ if (__mpx_mode__ === 'android') {
137
+ canGoBack.current = event.nativeEvent.canGoBack;
138
+ }
139
+ };
140
+ const _message = function (res) {
141
+ let data = {};
142
+ let asyncCallback;
143
+ const navObj = promisify({ redirectTo, navigateTo, navigateBack, reLaunch, switchTab });
144
+ try {
145
+ const nativeEventData = res.nativeEvent?.data;
146
+ if (typeof nativeEventData === 'string') {
147
+ data = JSON.parse(nativeEventData);
148
+ }
149
+ }
150
+ catch (e) { }
151
+ const args = data.args;
152
+ const postData = data.payload || {};
153
+ const params = Array.isArray(args) ? args : [postData];
154
+ const type = data.type;
155
+ switch (type) {
156
+ case 'setTitle':
157
+ { // case下不允许直接声明,包个块解决该问题
158
+ const title = postData._documentTitle;
159
+ if (title) {
160
+ navigation && navigation.setOptions({ title });
161
+ }
162
+ }
163
+ break;
164
+ case 'postMessage':
165
+ bindmessage && bindmessage(getCustomEvent('messsage', {}, {
166
+ detail: {
167
+ data: params[0]?.data
168
+ }
169
+ }));
170
+ asyncCallback = Promise.resolve({
171
+ errMsg: 'invokeWebappApi:ok'
172
+ });
173
+ break;
174
+ case 'navigateTo':
175
+ asyncCallback = navObj.navigateTo(...params);
176
+ break;
177
+ case 'navigateBack':
178
+ isNavigateBack.current = true;
179
+ asyncCallback = navObj.navigateBack(...params);
180
+ break;
181
+ case 'redirectTo':
182
+ asyncCallback = navObj.redirectTo(...params);
183
+ break;
184
+ case 'switchTab':
185
+ asyncCallback = navObj.switchTab(...params);
186
+ break;
187
+ case 'reLaunch':
188
+ asyncCallback = navObj.reLaunch(...params);
189
+ break;
190
+ default:
191
+ if (type) {
192
+ const implement = mpx.config.webviewConfig.apiImplementations && mpx.config.webviewConfig.apiImplementations[type];
193
+ if (isFunction(implement)) {
194
+ asyncCallback = Promise.resolve(implement(...params));
195
+ }
196
+ else {
197
+ /* eslint-disable prefer-promise-reject-errors */
198
+ asyncCallback = Promise.reject({
199
+ errMsg: `未在apiImplementations中配置${type}方法`
200
+ });
201
+ }
202
+ }
203
+ break;
204
+ }
205
+ asyncCallback && asyncCallback.then((res) => {
206
+ if (webViewRef.current?.postMessage) {
207
+ const result = JSON.stringify({
208
+ type,
209
+ callbackId: data.callbackId,
210
+ result: res
211
+ });
212
+ webViewRef.current.injectJavaScript(sendMessage(result));
213
+ }
214
+ }).catch((error) => {
215
+ if (webViewRef.current?.postMessage) {
216
+ const result = JSON.stringify({
217
+ type,
218
+ callbackId: data.callbackId,
219
+ error
220
+ });
221
+ webViewRef.current.injectJavaScript(sendMessage(result));
222
+ }
223
+ });
224
+ };
225
+ const onLoadEndHandle = function (res) {
226
+ fristLoaded.current = true;
227
+ setIsLoaded(true);
228
+ const src = res.nativeEvent?.url;
229
+ if (isLoadError.current) {
230
+ isLoadError.current = false;
231
+ isNavigateBack.current = false;
232
+ const result = {
233
+ type: 'error',
234
+ timeStamp: res.timeStamp,
235
+ detail: {
236
+ src,
237
+ statusCode: statusCode.current
238
+ }
239
+ };
240
+ binderror && binderror(result);
241
+ }
242
+ else {
243
+ const result = {
244
+ type: 'load',
245
+ timeStamp: res.timeStamp,
246
+ detail: {
247
+ src
248
+ }
249
+ };
250
+ bindload?.(result);
251
+ }
252
+ };
253
+ const onLoadEnd = function (res) {
254
+ if (__mpx_mode__ === 'android') {
255
+ setTimeout(() => {
256
+ onLoadEndHandle(res);
257
+ }, 0);
258
+ }
259
+ else {
260
+ onLoadEndHandle(res);
261
+ }
262
+ };
263
+ const onHttpError = function (res) {
264
+ isLoadError.current = true;
265
+ statusCode.current = res.nativeEvent?.statusCode;
266
+ };
267
+ const onError = function () {
268
+ statusCode.current = '';
269
+ isLoadError.current = true;
270
+ if (!fristLoaded.current) {
271
+ setPageLoadErr(true);
272
+ }
273
+ };
274
+ const onLoadStart = function () {
275
+ if (!fristLoaded.current) {
276
+ setIsLoaded(false);
277
+ }
278
+ };
279
+ return (<Portal key={pageLoadErr ? 'error' : 'webview'}>
280
+ {pageLoadErr
281
+ ? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
282
+ <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
283
+ <View style={styles.loadErrorButton} onTouchEnd={_reload}><Text style={{ fontSize: 12, color: '#666666' }}>{currentErrorText.button}</Text></View>
284
+ </View>)
285
+ : (<WebView style={defaultWebViewStyle} source={{ uri: src }} ref={webViewRef} javaScriptEnabled={true} onNavigationStateChange={_changeUrl} onMessage={_message} injectedJavaScript={injectedJavaScript} onLoadProgress={_onLoadProgress} onLoadEnd={onLoadEnd} onHttpError={onHttpError} onError={onError} onLoadStart={onLoadStart} allowsBackForwardNavigationGestures={isLoaded}></WebView>)}
286
+ </Portal>);
287
+ });
288
+ _WebView.displayName = 'MpxWebview';
289
+ export default _WebView;
@@ -0,0 +1,218 @@
1
+ export class ExpressionParser {
2
+ tokens;
3
+ formatter;
4
+ functions;
5
+ current;
6
+ constructor(input, formatter = val => parseFloat(val), functions = {}) {
7
+ this.tokens = this.tokenize(input);
8
+ this.formatter = formatter;
9
+ this.functions = functions;
10
+ this.current = 0;
11
+ }
12
+ tokenize(input) {
13
+ const tokens = [];
14
+ const regex = /(\d+\.?\d*(?:px|rpx|%|vw|vh)?|[+\-*/(),]|\b[a-zA-Z_][a-zA-Z0-9_]*\b)/g;
15
+ let match;
16
+ while ((match = regex.exec(input))) {
17
+ if (/^\d+\.?\d*(?:px|rpx|%|vw|vh)?$/.test(match[0])) {
18
+ const lastToken = tokens[tokens.length - 1];
19
+ const last2Token = tokens[tokens.length - 2];
20
+ if (lastToken?.type === '-' && (!last2Token || /^[+\-*/(,]$/.test(last2Token?.type))) {
21
+ tokens.pop();
22
+ tokens.push({
23
+ type: 'NUMBER',
24
+ value: '-' + match[0]
25
+ });
26
+ }
27
+ else {
28
+ tokens.push({
29
+ type: 'NUMBER',
30
+ value: match[0]
31
+ });
32
+ }
33
+ }
34
+ else {
35
+ tokens.push({
36
+ type: match[0],
37
+ value: match[0]
38
+ });
39
+ }
40
+ }
41
+ return tokens;
42
+ }
43
+ parse() {
44
+ return this.expression();
45
+ }
46
+ expression() {
47
+ let node = this.term();
48
+ while (this.current < this.tokens.length &&
49
+ (this.tokens[this.current].type === '+' || this.tokens[this.current].type === '-')) {
50
+ const operator = this.tokens[this.current].type;
51
+ this.current++;
52
+ const right = this.term();
53
+ node = this.applyOperator(operator, node, right);
54
+ }
55
+ return node;
56
+ }
57
+ term() {
58
+ let node = this.factor();
59
+ while (this.current < this.tokens.length &&
60
+ (this.tokens[this.current].type === '*' || this.tokens[this.current].type === '/')) {
61
+ const operator = this.tokens[this.current].type;
62
+ this.current++;
63
+ const right = this.factor();
64
+ node = this.applyOperator(operator, node, right);
65
+ }
66
+ return node;
67
+ }
68
+ factor() {
69
+ const token = this.tokens[this.current];
70
+ if (token.type === 'NUMBER') {
71
+ this.current++;
72
+ const numericValue = this.formatter(token.value);
73
+ return { type: 'NUMBER', value: numericValue };
74
+ }
75
+ else if (token.type === '(') {
76
+ this.current++;
77
+ const node = this.expression();
78
+ if (this.tokens[this.current].type !== ')') {
79
+ throw new Error('Expected closing parenthesis');
80
+ }
81
+ this.current++;
82
+ return node;
83
+ }
84
+ else if (this.functions[token.type]) {
85
+ this.current++;
86
+ if (this.tokens[this.current].type !== '(') {
87
+ throw new Error('Expected opening parenthesis after function');
88
+ }
89
+ this.current++;
90
+ const args = this.parseArguments();
91
+ if (this.tokens[this.current].type !== ')') {
92
+ throw new Error('Expected closing parenthesis');
93
+ }
94
+ this.current++;
95
+ return this.applyFunction(token.type, args);
96
+ }
97
+ throw new Error(`Unexpected token: ${token.type}`);
98
+ }
99
+ parseArguments() {
100
+ const args = [];
101
+ while (this.current < this.tokens.length && this.tokens[this.current].type !== ')') {
102
+ args.push(this.expression());
103
+ if (this.tokens[this.current].type === ',') {
104
+ this.current++;
105
+ }
106
+ }
107
+ return args;
108
+ }
109
+ applyOperator(operator, left, right) {
110
+ const leftVal = left.value;
111
+ const rightVal = right.value;
112
+ let result;
113
+ switch (operator) {
114
+ case '+':
115
+ result = leftVal + rightVal;
116
+ break;
117
+ case '-':
118
+ result = leftVal - rightVal;
119
+ break;
120
+ case '*':
121
+ result = leftVal * rightVal;
122
+ break;
123
+ case '/':
124
+ result = leftVal / rightVal;
125
+ break;
126
+ default: throw new Error(`Unknown operator: ${operator}`);
127
+ }
128
+ return { type: 'NUMBER', value: result };
129
+ }
130
+ applyFunction(func, args) {
131
+ if (args.some(arg => arg.type !== 'NUMBER')) {
132
+ throw new Error('Function arguments must be numbers');
133
+ }
134
+ const numericArgs = args.map(arg => arg.value);
135
+ if (this.functions[func]) {
136
+ return { type: 'NUMBER', value: this.functions[func].apply(null, numericArgs) };
137
+ }
138
+ else {
139
+ throw new Error(`Unknown function: ${func}`);
140
+ }
141
+ }
142
+ }
143
+ export function parseFunc(str, funcName) {
144
+ const regex = new RegExp(`${funcName}\\(`, 'g');
145
+ const result = [];
146
+ let match;
147
+ while ((match = regex.exec(str)) !== null) {
148
+ const start = match.index;
149
+ let i = start + funcName.length + 1;
150
+ let depth = 1;
151
+ const args = [];
152
+ let arg = '';
153
+ while (depth && i < str.length) {
154
+ if (depth === 1 && (str[i] === ',' || str[i] === ')')) {
155
+ args.push(arg.trim());
156
+ arg = '';
157
+ }
158
+ else {
159
+ arg += str[i];
160
+ }
161
+ switch (str[i]) {
162
+ case '(':
163
+ depth++;
164
+ break;
165
+ case ')':
166
+ depth--;
167
+ break;
168
+ default:
169
+ // Do nothing
170
+ }
171
+ i++;
172
+ }
173
+ const end = regex.lastIndex = i;
174
+ result.push({
175
+ start,
176
+ end,
177
+ args
178
+ });
179
+ }
180
+ return result;
181
+ }
182
+ export class ReplaceSource {
183
+ _source;
184
+ _replacements;
185
+ constructor(source) {
186
+ this._source = source;
187
+ this._replacements = [];
188
+ }
189
+ replace(start, end, content) {
190
+ this._replacements.push({ start, end, content });
191
+ }
192
+ source() {
193
+ if (this._replacements.length === 0) {
194
+ return this._source;
195
+ }
196
+ let current = this._source;
197
+ let pos = 0;
198
+ const result = [];
199
+ for (const replacement of this._replacements) {
200
+ const start = Math.floor(replacement.start);
201
+ const end = Math.floor(replacement.end) + 1;
202
+ if (pos < start) {
203
+ const offset = start - pos;
204
+ result.push(current.slice(0, offset));
205
+ current = current.slice(offset);
206
+ pos = start;
207
+ }
208
+ result.push(replacement.content);
209
+ if (pos < end) {
210
+ const offset = end - pos;
211
+ current = current.slice(offset);
212
+ pos = end;
213
+ }
214
+ }
215
+ result.push(current);
216
+ return result.join('');
217
+ }
218
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Borrowed from open-source code: https://github.com/quidone/react-native-wheel-picker
3
+ * Special thanks to the authors for their contribution to the open-source community.
4
+ */
5
+ export const degToRad = (deg) => (Math.PI * deg) / 180;
6
+ // Calculates the height of the element after rotating it relative to the user's screen.
7
+ const calcHeight = (degree, itemHeight) => itemHeight * Math.cos(degToRad(degree));
8
+ export const calcPickerHeight = (faces, itemHeight) => {
9
+ if (faces.length === 7) {
10
+ return itemHeight * 5;
11
+ }
12
+ return faces.reduce((r, v) => r + calcHeight(Math.abs(v.deg), itemHeight), 0);
13
+ };
14
+ export const createFaces = (itemHeight, visibleCount) => {
15
+ // e.g [30, 60, 90]
16
+ const getDegreesRelativeCenter = () => {
17
+ const maxStep = Math.trunc((visibleCount + 2) / 2); // + 2 because there are 2 more faces at 90 degrees
18
+ const stepDegree = 90 / maxStep;
19
+ const result = [];
20
+ for (let i = 1; i <= maxStep; i++) {
21
+ result.push(i * stepDegree);
22
+ }
23
+ return result;
24
+ };
25
+ const getScreenHeightsAndOffsets = (degrees) => {
26
+ const screenHeights = degrees.map((deg) => calcHeight(deg, itemHeight));
27
+ const freeSpaces = screenHeights.map((screenHeight) => itemHeight - screenHeight);
28
+ const offsets = freeSpaces.map((freeSpace, index) => {
29
+ let offset = freeSpace / 2;
30
+ for (let i = 0; i < index; i++) {
31
+ offset += freeSpaces[i];
32
+ }
33
+ return offset;
34
+ });
35
+ return [screenHeights, offsets];
36
+ };
37
+ const getOpacity = (index) => {
38
+ const map = {
39
+ 0: 0,
40
+ 1: 0.8,
41
+ 2: 0.9
42
+ };
43
+ return map[index] ?? Math.min(1, map[2] + index * 0.05);
44
+ };
45
+ const degrees = getDegreesRelativeCenter();
46
+ const [screenHeight, offsets] = getScreenHeightsAndOffsets(degrees);
47
+ const scales = [0.973, 0.9, 0.8];
48
+ return [
49
+ // top items
50
+ ...degrees
51
+ .map((degree, index) => {
52
+ return {
53
+ index: -1 * (index + 1),
54
+ deg: degree,
55
+ opacity: getOpacity(degrees.length - 1 - index),
56
+ offsetY: -1 * offsets[index],
57
+ scale: scales[index],
58
+ screenHeight: screenHeight[index]
59
+ };
60
+ })
61
+ .reverse(),
62
+ // center item
63
+ { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1, screenHeight: itemHeight },
64
+ // bottom items
65
+ ...degrees.map((degree, index) => {
66
+ return {
67
+ index: index + 1,
68
+ deg: -1 * degree,
69
+ opacity: getOpacity(degrees.length - 1 - index),
70
+ offsetY: offsets[index],
71
+ scale: scales[index],
72
+ screenHeight: screenHeight[index]
73
+ };
74
+ })
75
+ ];
76
+ };
@@ -0,0 +1,14 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const PickerViewColumnAnimationContext = createContext(undefined);
3
+ export const usePickerViewColumnAnimationContext = () => {
4
+ const value = useContext(PickerViewColumnAnimationContext);
5
+ if (value === undefined) {
6
+ throw new Error('usePickerViewColumnAnimationContext must be called from within PickerViewColumnAnimationContext.Provider!');
7
+ }
8
+ return value;
9
+ };
10
+ export const PickerViewStyleContext = createContext(undefined);
11
+ export const usePickerViewStyleContext = () => {
12
+ const value = useContext(PickerViewStyleContext);
13
+ return value;
14
+ };
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ const _PickerViewIndicator = ({ itemHeight, indicatorItemStyle, indicatorContainerStyle }) => {
4
+ return (<View style={[styles.indicatorContainer, indicatorContainerStyle]} pointerEvents={'none'}>
5
+ <View style={[styles.selection, { height: itemHeight }, indicatorItemStyle]}/>
6
+ </View>);
7
+ };
8
+ const styles = StyleSheet.create({
9
+ indicatorContainer: {
10
+ ...StyleSheet.absoluteFillObject,
11
+ justifyContent: 'center',
12
+ alignItems: 'center',
13
+ zIndex: 200
14
+ },
15
+ selection: {
16
+ borderTopWidth: 1,
17
+ borderBottomWidth: 1,
18
+ borderColor: 'rgba(0, 0, 0, 0.05)',
19
+ alignSelf: 'stretch'
20
+ }
21
+ });
22
+ _PickerViewIndicator.displayName = 'MpxPickerViewIndicator';
23
+ export default _PickerViewIndicator;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ import LinearGradient from 'react-native-linear-gradient';
4
+ const _PickerViewMask = ({ itemHeight, maskContainerStyle }) => {
5
+ return (<View style={[styles.maskContainer, maskContainerStyle]} pointerEvents={'none'}>
6
+ <LinearGradient colors={['rgba(255,255,255,1)', 'rgba(255,255,255,0.5)']} style={{ flex: 1 }}/>
7
+ <View style={{ height: itemHeight }}/>
8
+ <LinearGradient colors={['rgba(255,255,255,0.5)', 'rgba(255,255,255,1)']} style={{ flex: 1 }}/>
9
+ </View>);
10
+ };
11
+ const styles = StyleSheet.create({
12
+ maskContainer: {
13
+ ...StyleSheet.absoluteFillObject,
14
+ zIndex: 100
15
+ }
16
+ });
17
+ _PickerViewMask.displayName = 'MpxPickerViewMask';
18
+ export default _PickerViewMask;