@momo-kits/foundation 0.167.2-beta.1 → 0.168.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Application/BottomSheet.tsx +113 -14
- package/Application/BottomTab/BottomTabBar.tsx +6 -3
- package/Application/BottomTab/CustomBottomTabItem.tsx +3 -5
- package/Application/BottomTab/DuoBarCompression.tsx +135 -0
- package/Application/BottomTab/DuoTabBar.tsx +537 -0
- package/Application/BottomTab/index.tsx +145 -134
- package/Application/Components/DuoGlassBackground.tsx +158 -0
- package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
- package/Application/Components/HeaderAnimated.tsx +9 -3
- package/Application/Components/HeaderExtendHeader.tsx +70 -28
- package/Application/Components/HeaderLeft.tsx +38 -0
- package/Application/Components/HeaderRight.tsx +144 -9
- package/Application/Components/HeaderTitle.tsx +43 -7
- package/Application/Components/SearchHeader.tsx +20 -15
- package/Application/Components/SideBarButton.tsx +134 -0
- package/Application/ModalScreen.tsx +16 -2
- package/Application/NavigationContainer.tsx +22 -38
- package/Application/Navigator.ts +1 -3
- package/Application/StackScreen.tsx +2 -5
- package/Application/device.ts +29 -0
- package/Application/types.ts +10 -1
- package/Application/utils.tsx +10 -7
- package/Badge/Badge.tsx +2 -3
- package/Badge/BadgeDotAnimation.tsx +3 -3
- package/Button/index.tsx +11 -11
- package/Consts/theme.ts +3 -4
- package/Input/Input.tsx +3 -4
- package/Input/InputMoney.tsx +22 -45
- package/Input/InputOTP.tsx +9 -12
- package/Input/InputPhoneNumber.tsx +3 -4
- package/Input/InputSearch.tsx +17 -14
- package/Input/InputTextArea.tsx +3 -4
- package/Input/SystemTextInput.tsx +2 -3
- package/Input/TextTyping.tsx +11 -6
- package/Input/utils.ts +2 -3
- package/Layout/Card.tsx +8 -26
- package/Layout/FloatingButton.tsx +3 -3
- package/Layout/Item.tsx +1 -10
- package/Layout/ReservedRegions.tsx +78 -0
- package/Layout/Screen.tsx +242 -48
- package/Layout/Section.tsx +5 -18
- package/Layout/index.ts +4 -4
- package/Layout/styles.ts +5 -0
- package/Layout/utils.ts +477 -5
- package/Pagination/Dot.tsx +3 -6
- package/Popup/PopupNotify.tsx +33 -22
- package/Popup/PopupPromotion.tsx +21 -8
- package/Popup/utils.ts +32 -0
- package/Radio/index.tsx +2 -3
- package/Text/utils.ts +1 -2
- package/Title/index.tsx +8 -11
- package/package.json +5 -2
- package/Layout/GridSystem.tsx +0 -137
package/Input/InputMoney.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
1
|
import React, {
|
|
3
2
|
forwardRef,
|
|
4
3
|
useContext,
|
|
@@ -9,10 +8,9 @@ import React, {
|
|
|
9
8
|
useState,
|
|
10
9
|
} from 'react';
|
|
11
10
|
import {
|
|
12
|
-
NativeSyntheticEvent,
|
|
13
11
|
TextInput,
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
TextInputFocusEvent,
|
|
13
|
+
TextInputSelectionChangeEvent,
|
|
16
14
|
TouchableOpacity,
|
|
17
15
|
View,
|
|
18
16
|
} from 'react-native';
|
|
@@ -44,7 +42,6 @@ const countDigits = (text: string) => text.replace(/\D/g, '').length;
|
|
|
44
42
|
const groupDigits = (digits: string) =>
|
|
45
43
|
digits.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
|
46
44
|
|
|
47
|
-
// Caret position right after the `digitIndex`-th digit of `display`.
|
|
48
45
|
const caretFromDigitIndex = (display: string, digitIndex: number) => {
|
|
49
46
|
let count = 0;
|
|
50
47
|
for (let i = 0; i < display.length && count < digitIndex; i++) {
|
|
@@ -98,9 +95,6 @@ const detectEdit = (old: string, next: string, selection: Selection): Edit => {
|
|
|
98
95
|
};
|
|
99
96
|
};
|
|
100
97
|
|
|
101
|
-
// Applies `edit` to the digits of `display`. Separators and currency are never
|
|
102
|
-
// deleted: removing a separator deletes the digit before it instead, removing
|
|
103
|
-
// only the currency leaves the digits untouched.
|
|
104
98
|
const applyEdit = (display: string, edit: Edit, digitLimit: number) => {
|
|
105
99
|
const digits = display.replace(/\D/g, '');
|
|
106
100
|
let left = countDigits(display.slice(0, edit.start));
|
|
@@ -166,11 +160,10 @@ const InputMoney = forwardRef(
|
|
|
166
160
|
const inputFontFamily = exportFontFamily('bold');
|
|
167
161
|
const [focused, setFocused] = useState(false);
|
|
168
162
|
const [displayValue, setDisplayValue] = useState('');
|
|
169
|
-
const [numericValue, setNumericValue] = useState('');
|
|
170
163
|
const [commitCount, setCommitCount] = useState(0);
|
|
171
164
|
const displayRef = useRef('');
|
|
165
|
+
const digitsRef = useRef('');
|
|
172
166
|
const selectionRef = useRef<Selection>({ start: 0, end: 0 });
|
|
173
|
-
// Caret to push to native once the committed text is rendered.
|
|
174
167
|
const pendingCaretRef = useRef<number | null>(null);
|
|
175
168
|
const pendingSelectionRef = useRef<Selection | null>(null);
|
|
176
169
|
const editingRef = useRef(false);
|
|
@@ -184,55 +177,39 @@ const InputMoney = forwardRef(
|
|
|
184
177
|
props.accessibilityLabel,
|
|
185
178
|
);
|
|
186
179
|
|
|
187
|
-
const digitLimit =
|
|
188
|
-
typeof (props as any)?.maxLength === 'number'
|
|
189
|
-
? (props as any).maxLength
|
|
190
|
-
: Number.POSITIVE_INFINITY;
|
|
180
|
+
const digitLimit = props.maxLength ?? Number.POSITIVE_INFINITY;
|
|
191
181
|
|
|
192
182
|
useEffect(() => {
|
|
193
183
|
if (value !== undefined && typeof value === 'string') {
|
|
194
184
|
const cleanValue = value.replace(/\D/g, '').replace(/^0+(?=\d)/, '');
|
|
195
|
-
if (cleanValue !==
|
|
185
|
+
if (cleanValue !== digitsRef.current) {
|
|
196
186
|
commit(cleanValue, cleanValue.length);
|
|
197
187
|
}
|
|
198
188
|
}
|
|
189
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
199
190
|
}, [value]);
|
|
200
191
|
|
|
201
|
-
// Always set the caret imperatively: a controlled `selection` prop is only
|
|
202
|
-
// sent to native when it differs from the last native selection, so it is
|
|
203
|
-
// skipped whenever a stale selection event already reported the same value.
|
|
204
192
|
useLayoutEffect(() => {
|
|
205
193
|
const caret = pendingCaretRef.current;
|
|
206
|
-
if (caret === null) return;
|
|
194
|
+
if (caret === null || !inputRef.current?.isFocused()) return;
|
|
207
195
|
pendingCaretRef.current = null;
|
|
208
|
-
|
|
209
|
-
inputRef.current.setSelection(caret, caret);
|
|
210
|
-
}
|
|
196
|
+
inputRef.current.setSelection(caret, caret);
|
|
211
197
|
}, [commitCount]);
|
|
212
198
|
|
|
213
199
|
const commit = (digits: string, caretDigit: number) => {
|
|
214
200
|
const display = digits ? `${groupDigits(digits)}${currency}` : '';
|
|
215
201
|
const caret = caretFromDigitIndex(display, caretDigit);
|
|
216
|
-
const changed = digits !==
|
|
202
|
+
const changed = digits !== digitsRef.current;
|
|
217
203
|
displayRef.current = display;
|
|
204
|
+
digitsRef.current = digits;
|
|
218
205
|
selectionRef.current = { start: caret, end: caret };
|
|
219
206
|
pendingCaretRef.current = caret;
|
|
220
207
|
setCommitCount(count => count + 1);
|
|
221
208
|
setDisplayValue(display);
|
|
222
|
-
setNumericValue(digits);
|
|
223
209
|
return changed;
|
|
224
210
|
};
|
|
225
211
|
|
|
226
|
-
const parseInputText = (text: string): string => {
|
|
227
|
-
if (!text) return '';
|
|
228
|
-
const currencyRegex = new RegExp(` ?${currency}$`, 'g');
|
|
229
|
-
const textWithoutCurrency = text.replace(currencyRegex, '').trim();
|
|
230
|
-
return textWithoutCurrency.replace(/[^\d]/g, '');
|
|
231
|
-
};
|
|
232
|
-
|
|
233
212
|
const onClearText = () => {
|
|
234
|
-
inputRef?.current?.clear();
|
|
235
|
-
inputRef.current?.setNativeProps({ text: '' });
|
|
236
213
|
commit('', 0);
|
|
237
214
|
onChangeText?.('');
|
|
238
215
|
};
|
|
@@ -258,10 +235,7 @@ const InputMoney = forwardRef(
|
|
|
258
235
|
}
|
|
259
236
|
};
|
|
260
237
|
|
|
261
|
-
|
|
262
|
-
const _onSelectionChange = (
|
|
263
|
-
e: NativeSyntheticEvent<TextInputSelectionChangeEventData>,
|
|
264
|
-
) => {
|
|
238
|
+
const _onSelectionChange = (e: TextInputSelectionChangeEvent) => {
|
|
265
239
|
props.onSelectionChange?.(e);
|
|
266
240
|
if (editingRef.current) return;
|
|
267
241
|
const selection = e.nativeEvent.selection;
|
|
@@ -283,23 +257,25 @@ const InputMoney = forwardRef(
|
|
|
283
257
|
});
|
|
284
258
|
};
|
|
285
259
|
|
|
286
|
-
const _onFocus = (e:
|
|
260
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
287
261
|
setFocused(true);
|
|
262
|
+
const caret = pendingCaretRef.current;
|
|
263
|
+
pendingCaretRef.current = null;
|
|
264
|
+
if (caret !== null && !props.selectTextOnFocus) {
|
|
265
|
+
selectionRef.current = { start: caret, end: caret };
|
|
266
|
+
inputRef.current?.setSelection(caret, caret);
|
|
267
|
+
}
|
|
288
268
|
onFocus?.(e);
|
|
289
269
|
};
|
|
290
270
|
|
|
291
|
-
const _onBlur = (e:
|
|
271
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
292
272
|
setFocused(false);
|
|
293
273
|
onBlur?.(e);
|
|
294
274
|
};
|
|
295
275
|
|
|
296
276
|
const _setText = (text: string) => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
onChangeText?.('');
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
const digits = parseInputText(text)
|
|
277
|
+
const digits = text
|
|
278
|
+
.replace(/\D/g, '')
|
|
303
279
|
.replace(/^0+(?=\d)/, '')
|
|
304
280
|
.slice(0, digitLimit);
|
|
305
281
|
if (commit(digits, digits.length)) {
|
|
@@ -350,6 +326,7 @@ const InputMoney = forwardRef(
|
|
|
350
326
|
accessibilityLabel={`${componentId}`}
|
|
351
327
|
textAlignVertical="center"
|
|
352
328
|
ref={inputRef}
|
|
329
|
+
editable={props.editable !== false && !disabled}
|
|
353
330
|
accessibilityState={{
|
|
354
331
|
disabled: disabled,
|
|
355
332
|
...props.accessibilityState,
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -64,8 +64,8 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
64
64
|
opacity: opacity.value,
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
|
|
68
|
+
const spacingStyle = hasNextInput && { marginRight: Spacing.L };
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
71
|
<View style={[Styles.rowCenter, spacingStyle]}>
|
|
@@ -162,6 +162,7 @@ const InputOTP = forwardRef(
|
|
|
162
162
|
|
|
163
163
|
const renderInputs = (inputLength: number) => {
|
|
164
164
|
const TextInputs: React.ReactNode[] = [];
|
|
165
|
+
const textColors = theme.colors.text;
|
|
165
166
|
for (let i = 0; i < inputLength; i++) {
|
|
166
167
|
TextInputs.push(
|
|
167
168
|
<View key={`otp-input-${i}`}>
|
|
@@ -170,9 +171,7 @@ const InputOTP = forwardRef(
|
|
|
170
171
|
) : (
|
|
171
172
|
<Text
|
|
172
173
|
style={i !== inputLength - 1 && { marginRight: Spacing.L }}
|
|
173
|
-
color={
|
|
174
|
-
value[i] ? theme.colors.text.default : theme.colors.text.hint
|
|
175
|
-
}
|
|
174
|
+
color={value[i] ? textColors.default : textColors.hint}
|
|
176
175
|
typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
|
|
177
176
|
>
|
|
178
177
|
{value[i] || '-'}
|
|
@@ -201,16 +200,14 @@ const InputOTP = forwardRef(
|
|
|
201
200
|
return (
|
|
202
201
|
<View style={Styles.row}>
|
|
203
202
|
{splitValue.map((letter, index) => {
|
|
203
|
+
const isLast = index === value.length - 1;
|
|
204
|
+
const isTyping = focused && value.length !== MAX_LENGTH;
|
|
205
|
+
const isCompact = fontScale > 1 && value.length > 6;
|
|
206
|
+
const marginRight = isCompact ? Spacing.M : Spacing.L;
|
|
204
207
|
return (
|
|
205
208
|
<Text
|
|
206
209
|
key={`otp-letter-${index}`}
|
|
207
|
-
style={
|
|
208
|
-
(index !== value.length - 1 ||
|
|
209
|
-
(focused && value.length !== MAX_LENGTH)) && {
|
|
210
|
-
marginRight:
|
|
211
|
-
fontScale > 1 && value.length > 6 ? Spacing.M : Spacing.L,
|
|
212
|
-
}
|
|
213
|
-
}
|
|
210
|
+
style={(!isLast || isTyping) && { marginRight }}
|
|
214
211
|
typography={'header_m_bold'}
|
|
215
212
|
>
|
|
216
213
|
{letter}
|
|
@@ -6,10 +6,9 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import {
|
|
9
|
-
NativeSyntheticEvent,
|
|
10
9
|
StyleSheet,
|
|
11
10
|
TextInput,
|
|
12
|
-
|
|
11
|
+
TextInputFocusEvent,
|
|
13
12
|
TouchableOpacity,
|
|
14
13
|
View,
|
|
15
14
|
} from 'react-native';
|
|
@@ -93,12 +92,12 @@ const InputPhoneNumber = forwardRef(
|
|
|
93
92
|
onChangeText?.(text);
|
|
94
93
|
};
|
|
95
94
|
|
|
96
|
-
const _onFocus = (e:
|
|
95
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
97
96
|
setFocused(true);
|
|
98
97
|
onFocus?.(e);
|
|
99
98
|
};
|
|
100
99
|
|
|
101
|
-
const _onBlur = (e:
|
|
100
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
102
101
|
setFocused(false);
|
|
103
102
|
onBlur?.(e);
|
|
104
103
|
};
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -10,9 +10,8 @@ import React, {
|
|
|
10
10
|
useState,
|
|
11
11
|
} from 'react';
|
|
12
12
|
import {
|
|
13
|
-
NativeSyntheticEvent,
|
|
14
13
|
TextInput,
|
|
15
|
-
|
|
14
|
+
TextInputFocusEvent,
|
|
16
15
|
TouchableOpacity,
|
|
17
16
|
View,
|
|
18
17
|
} from 'react-native';
|
|
@@ -59,9 +58,11 @@ const TextTyping: FC<any> = ({
|
|
|
59
58
|
listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
currentIndex.current >= data?.length
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
if (currentIndex.current >= data?.length) {
|
|
62
|
+
handleResetList();
|
|
63
|
+
} else {
|
|
64
|
+
handleAnimation();
|
|
65
|
+
}
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
const showChar = (char?: string) => {
|
|
@@ -82,9 +83,12 @@ const TextTyping: FC<any> = ({
|
|
|
82
83
|
}, timeDelayNextChar);
|
|
83
84
|
};
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const textLength = data?.[currentIndex.current]?.length || 0;
|
|
87
|
+
if (currentCharIndex.current >= textLength) {
|
|
88
|
+
handleResetString();
|
|
89
|
+
} else {
|
|
90
|
+
handleShowChar();
|
|
91
|
+
}
|
|
88
92
|
};
|
|
89
93
|
|
|
90
94
|
const reset = () => {
|
|
@@ -166,12 +170,12 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
166
170
|
props.accessibilityLabel,
|
|
167
171
|
);
|
|
168
172
|
|
|
169
|
-
const _onFocus = (e:
|
|
173
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
170
174
|
setFocused(true);
|
|
171
175
|
onFocus?.(e);
|
|
172
176
|
};
|
|
173
177
|
|
|
174
|
-
const _onBlur = (e:
|
|
178
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
175
179
|
setFocused(false);
|
|
176
180
|
onBlur?.(e);
|
|
177
181
|
};
|
|
@@ -303,6 +307,8 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
303
307
|
inputState = 'filled';
|
|
304
308
|
}
|
|
305
309
|
|
|
310
|
+
const inputBackground = backgroundColor ?? theme.colors.background.surface;
|
|
311
|
+
|
|
306
312
|
return (
|
|
307
313
|
<ComponentContext.Provider
|
|
308
314
|
value={{
|
|
@@ -322,10 +328,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
322
328
|
<View
|
|
323
329
|
style={[
|
|
324
330
|
styles.searchInputWrapper,
|
|
325
|
-
{
|
|
326
|
-
backgroundColor:
|
|
327
|
-
backgroundColor ?? theme.colors.background.surface,
|
|
328
|
-
},
|
|
331
|
+
{ backgroundColor: inputBackground },
|
|
329
332
|
]}
|
|
330
333
|
>
|
|
331
334
|
<Icon
|
package/Input/InputTextArea.tsx
CHANGED
|
@@ -7,9 +7,8 @@ import React, {
|
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
9
|
import {
|
|
10
|
-
NativeSyntheticEvent,
|
|
11
10
|
TextInput,
|
|
12
|
-
|
|
11
|
+
TextInputFocusEvent,
|
|
13
12
|
TouchableOpacity,
|
|
14
13
|
View,
|
|
15
14
|
} from 'react-native';
|
|
@@ -79,12 +78,12 @@ const InputTextArea = forwardRef(
|
|
|
79
78
|
setInputValue(text);
|
|
80
79
|
};
|
|
81
80
|
|
|
82
|
-
const _onFocus = (e:
|
|
81
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
83
82
|
setFocused(true);
|
|
84
83
|
onFocus?.(e);
|
|
85
84
|
};
|
|
86
85
|
|
|
87
|
-
const _onBlur = (e:
|
|
86
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
88
87
|
setFocused(false);
|
|
89
88
|
onBlur?.(e);
|
|
90
89
|
};
|
|
@@ -17,11 +17,10 @@ const SystemTextInput: FC<SystemTextInputProps> = props => {
|
|
|
17
17
|
const needsAndroidWrapper =
|
|
18
18
|
Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
|
|
19
19
|
|
|
20
|
+
const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
|
|
20
21
|
const inputProps: TextInputProps = {
|
|
21
22
|
...props,
|
|
22
|
-
...(needsAndroidWrapper
|
|
23
|
-
? { onPressIn: undefined, onPressOut: undefined }
|
|
24
|
-
: null),
|
|
23
|
+
...(needsAndroidWrapper ? noPressHandlers : null),
|
|
25
24
|
...(editable === false ? { accessibilityState: undefined } : null),
|
|
26
25
|
};
|
|
27
26
|
|
package/Input/TextTyping.tsx
CHANGED
|
@@ -36,9 +36,11 @@ const TextTyping: FC<TextTypingProps> = ({
|
|
|
36
36
|
listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
currentIndex.current >= data?.length
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
if (currentIndex.current >= data?.length) {
|
|
40
|
+
handleResetList();
|
|
41
|
+
} else {
|
|
42
|
+
handleAnimation();
|
|
43
|
+
}
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
const showChar = (char?: string) => {
|
|
@@ -59,9 +61,12 @@ const TextTyping: FC<TextTypingProps> = ({
|
|
|
59
61
|
}, timeDelayNextChar);
|
|
60
62
|
};
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const textLength = data?.[currentIndex.current]?.length || 0;
|
|
65
|
+
if (currentCharIndex.current >= textLength) {
|
|
66
|
+
handleResetString();
|
|
67
|
+
} else {
|
|
68
|
+
handleShowChar();
|
|
69
|
+
}
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
const reset = () => {
|
package/Input/utils.ts
CHANGED
|
@@ -15,9 +15,8 @@ const formatNumberToMoney = (number: number, currency = '') => {
|
|
|
15
15
|
|
|
16
16
|
const numberString = Math.abs(number).toString();
|
|
17
17
|
if (numberString.length < 3) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
: `${numberString}${currency}`;
|
|
18
|
+
const sign = isNegative ? '-' : '';
|
|
19
|
+
return `${sign}${numberString}${currency}`;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
let count = 0;
|
package/Layout/Card.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { useGridSystem } from './utils';
|
|
2
|
+
import { View, ViewProps } from 'react-native';
|
|
3
|
+
import { useGridSystem, useScreenLayout } from './utils';
|
|
4
4
|
import { GridContextProps } from './types';
|
|
5
|
-
import { GridContext
|
|
5
|
+
import { GridContext } from './index';
|
|
6
6
|
import { Image } from '../Image';
|
|
7
|
-
import { ApplicationContext
|
|
7
|
+
import { ApplicationContext } from '../Context';
|
|
8
8
|
import styles from './styles';
|
|
9
|
-
import {
|
|
9
|
+
import { Radius, Shadow, Spacing } from '../Consts';
|
|
10
10
|
|
|
11
11
|
export interface CardProps extends ViewProps {
|
|
12
12
|
/**
|
|
@@ -27,12 +27,13 @@ const Card: React.FC<CardProps> = ({
|
|
|
27
27
|
useShadow = false,
|
|
28
28
|
...props
|
|
29
29
|
}) => {
|
|
30
|
-
const {
|
|
30
|
+
const { theme } = useContext(ApplicationContext);
|
|
31
31
|
const { numberOfColumns } = useGridSystem();
|
|
32
|
+
const { contentWidth } = useScreenLayout();
|
|
32
33
|
|
|
33
34
|
const gutterSize = 8;
|
|
34
35
|
const margin = 12;
|
|
35
|
-
const widthSection =
|
|
36
|
+
const widthSection = contentWidth - margin * 2;
|
|
36
37
|
let sizePerSpan =
|
|
37
38
|
(widthSection - margin * 2 - gutterSize * (numberOfColumns - 1)) /
|
|
38
39
|
numberOfColumns -
|
|
@@ -47,23 +48,6 @@ const Card: React.FC<CardProps> = ({
|
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
* render overlay only dev mode
|
|
52
|
-
*/
|
|
53
|
-
const renderOverlay = () => {
|
|
54
|
-
return (
|
|
55
|
-
<>
|
|
56
|
-
<GridSystem
|
|
57
|
-
width={widthSection}
|
|
58
|
-
gutterSize={Spacing.S}
|
|
59
|
-
screenGrid={false}
|
|
60
|
-
gridColor={Colors.red_01}
|
|
61
|
-
/>
|
|
62
|
-
<View pointerEvents={'none'} style={styles.boxGridCard} />
|
|
63
|
-
</>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
51
|
return (
|
|
68
52
|
<GridContext.Provider value={gridContext}>
|
|
69
53
|
<View
|
|
@@ -95,9 +79,7 @@ const Card: React.FC<CardProps> = ({
|
|
|
95
79
|
]}
|
|
96
80
|
/>
|
|
97
81
|
)}
|
|
98
|
-
{/* Render only allowed children as valid React elements */}
|
|
99
82
|
{children}
|
|
100
|
-
{showGrid && renderOverlay()}
|
|
101
83
|
</View>
|
|
102
84
|
</GridContext.Provider>
|
|
103
85
|
);
|
|
@@ -10,8 +10,8 @@ import Animated, {
|
|
|
10
10
|
useAnimatedStyle,
|
|
11
11
|
useSharedValue,
|
|
12
12
|
withTiming,
|
|
13
|
-
runOnJS,
|
|
14
13
|
} from 'react-native-reanimated';
|
|
14
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
15
15
|
import { ApplicationContext } from '../Context';
|
|
16
16
|
import { Icon } from '../Icon';
|
|
17
17
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
@@ -78,7 +78,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
78
78
|
minWidth,
|
|
79
79
|
{ duration: 100 },
|
|
80
80
|
finished => {
|
|
81
|
-
if (finished)
|
|
81
|
+
if (finished) scheduleOnRN(setShowText, false);
|
|
82
82
|
},
|
|
83
83
|
);
|
|
84
84
|
} else {
|
|
@@ -87,7 +87,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
87
87
|
maxWidth.value,
|
|
88
88
|
{ duration: 100 },
|
|
89
89
|
finished => {
|
|
90
|
-
if (finished)
|
|
90
|
+
if (finished) scheduleOnRN(setShowText, true);
|
|
91
91
|
},
|
|
92
92
|
);
|
|
93
93
|
}
|
package/Layout/Item.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import { View, ViewProps } from 'react-native';
|
|
3
3
|
import { GridContext } from './index';
|
|
4
|
-
import {
|
|
4
|
+
import { MiniAppContext } from '../Context';
|
|
5
5
|
import styles from './styles';
|
|
6
6
|
import { SpanNumber } from './types';
|
|
7
7
|
|
|
@@ -23,19 +23,11 @@ const Item: React.FC<ItemProps> = ({
|
|
|
23
23
|
children,
|
|
24
24
|
style,
|
|
25
25
|
}) => {
|
|
26
|
-
const { showGrid } = useContext(ApplicationContext);
|
|
27
26
|
const context = useContext<any>(MiniAppContext);
|
|
28
27
|
const grid = useContext(GridContext);
|
|
29
28
|
|
|
30
29
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
31
30
|
|
|
32
|
-
/**
|
|
33
|
-
* render overlay view only dev mode
|
|
34
|
-
*/
|
|
35
|
-
const renderOverlay = () => {
|
|
36
|
-
return <View pointerEvents={'none'} style={styles.boxGrid} />;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
31
|
return (
|
|
40
32
|
<View
|
|
41
33
|
style={[
|
|
@@ -49,7 +41,6 @@ const Item: React.FC<ItemProps> = ({
|
|
|
49
41
|
]}
|
|
50
42
|
>
|
|
51
43
|
{children}
|
|
52
|
-
{showGrid && renderOverlay()}
|
|
53
44
|
</View>
|
|
54
45
|
);
|
|
55
46
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { createContext, ReactNode, useMemo } from 'react';
|
|
2
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export type ReservedRegion = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type NativeReservedRegions = typeof import('react-native-reserved-regions');
|
|
12
|
+
|
|
13
|
+
// native lib is optional so apps without react-native-reserved-regions still bundle
|
|
14
|
+
let nativeLib: NativeReservedRegions | undefined;
|
|
15
|
+
try {
|
|
16
|
+
nativeLib = require('react-native-reserved-regions');
|
|
17
|
+
} catch {
|
|
18
|
+
nativeLib = undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* screen regions the system currently occludes, relative to the provider, which fills
|
|
23
|
+
* the root view so they line up with window coordinates
|
|
24
|
+
*/
|
|
25
|
+
const ReservedRegionsContext = createContext<ReservedRegion[]>([]);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* keeps the occlusion regions reported by the native provider, the only kind layout reads
|
|
29
|
+
*/
|
|
30
|
+
const OcclusionRegions: React.FC<{
|
|
31
|
+
useRegions: NativeReservedRegions['useReservedRegions'];
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}> = ({ useRegions, children }) => {
|
|
34
|
+
const regions = useRegions();
|
|
35
|
+
const occlusion = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
regions
|
|
38
|
+
.filter(region => region.kind === 'occlusion')
|
|
39
|
+
.map(region => region.frame),
|
|
40
|
+
[regions],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<ReservedRegionsContext.Provider value={occlusion}>
|
|
45
|
+
{children}
|
|
46
|
+
</ReservedRegionsContext.Provider>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* tracks the system occlusion regions (iOS 27.1+), e.g. the iPhone Duo status bar
|
|
52
|
+
* cluster growing for a call or a Live Activity
|
|
53
|
+
*/
|
|
54
|
+
const ReservedRegionsProvider: React.FC<{ children: ReactNode }> = ({
|
|
55
|
+
children,
|
|
56
|
+
}) => {
|
|
57
|
+
if (Platform.OS !== 'ios' || !nativeLib) {
|
|
58
|
+
return <>{children}</>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { ReservedRegionsProvider: NativeProvider, useReservedRegions } =
|
|
62
|
+
nativeLib;
|
|
63
|
+
return (
|
|
64
|
+
<NativeProvider style={styles.container}>
|
|
65
|
+
<OcclusionRegions useRegions={useReservedRegions}>
|
|
66
|
+
{children}
|
|
67
|
+
</OcclusionRegions>
|
|
68
|
+
</NativeProvider>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const styles = StyleSheet.create({
|
|
73
|
+
container: {
|
|
74
|
+
flex: 1,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export { ReservedRegionsContext, ReservedRegionsProvider };
|