@hero-design/rn 8.33.2 → 8.34.0
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/.turbo/turbo-build.log +1 -1
- package/assets/fonts/hero-icons-mobile.ttf +0 -0
- package/es/index.js +713 -479
- package/lib/assets/fonts/hero-icons-mobile.ttf +0 -0
- package/lib/index.js +716 -480
- package/package.json +6 -5
- package/rollup.config.js +1 -0
- package/src/components/Carousel/CardCarousel.tsx +2 -0
- package/src/components/Icon/HeroIcon/glyphMap.json +1 -1
- package/src/components/Icon/IconList.ts +2 -0
- package/src/components/Modal/ModalContentWrapper.tsx +112 -0
- package/src/components/Modal/ModalPresenter/ModalPresenter.tsx +135 -0
- package/src/components/Modal/ModalPresenter/index.tsx +9 -0
- package/src/components/Modal/ModalProvider.tsx +8 -0
- package/src/components/Modal/__tests__/ModalContentWrapper.spec.tsx +25 -0
- package/src/components/Modal/__tests__/ModalPresenter.spec.tsx +57 -0
- package/src/components/Modal/__tests__/__snapshots__/ModalContentWrapper.spec.tsx.snap +35 -0
- package/src/components/Modal/__tests__/__snapshots__/ModalPresenter.spec.tsx.snap +55 -0
- package/src/components/Modal/__tests__/index.spec.tsx +50 -0
- package/src/components/Modal/index.tsx +121 -0
- package/src/components/PinInput/index.tsx +2 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +5 -2
- package/src/components/Toast/ToastContainer.tsx +2 -0
- package/src/index.ts +2 -0
- package/testUtils/setup.tsx +24 -0
- package/types/components/Icon/IconList.d.ts +1 -1
- package/types/components/Icon/index.d.ts +1 -1
- package/types/components/Icon/utils.d.ts +1 -1
- package/types/components/Modal/ModalContentWrapper.d.ts +16 -0
- package/types/components/Modal/ModalPresenter/ModalPresenter.d.ts +34 -0
- package/types/components/Modal/ModalPresenter/index.d.ts +3 -0
- package/types/components/Modal/ModalProvider.d.ts +5 -0
- package/types/components/Modal/index.d.ts +33 -0
- package/types/components/RichTextEditor/RichTextEditor.d.ts +2 -2
- package/types/index.d.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hero-design/rn",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.34.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@emotion/native": "^11.9.3",
|
|
23
23
|
"@emotion/react": "^11.9.3",
|
|
24
|
-
"@hero-design/colors": "8.
|
|
24
|
+
"@hero-design/colors": "8.34.0",
|
|
25
25
|
"date-fns": "^2.16.1",
|
|
26
26
|
"events": "^3.2.0",
|
|
27
27
|
"hero-editor": "^1.9.21"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"react-native-gesture-handler": "^1.10.3 | ~2.5.0",
|
|
35
35
|
"react-native-linear-gradient": "^2.6.2",
|
|
36
36
|
"react-native-pager-view": "^5.4.25",
|
|
37
|
+
"react-native-root-siblings": "^4.1.1",
|
|
37
38
|
"react-native-safe-area-context": "^3.0.2",
|
|
38
39
|
"react-native-vector-icons": "^9.1.0",
|
|
39
40
|
"react-native-webview": "^11.2.5"
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"@babel/preset-typescript": "^7.17.12",
|
|
46
47
|
"@babel/runtime": "^7.18.9",
|
|
47
48
|
"@emotion/jest": "^11.9.3",
|
|
48
|
-
"@hero-design/eslint-plugin": "8.
|
|
49
|
+
"@hero-design/eslint-plugin": "8.34.0",
|
|
49
50
|
"@react-native-community/datetimepicker": "^3.5.2",
|
|
50
51
|
"@react-native-community/slider": "4.1.12",
|
|
51
52
|
"@rollup/plugin-babel": "^5.3.1",
|
|
@@ -61,10 +62,10 @@
|
|
|
61
62
|
"@types/react-native": "^0.67.7",
|
|
62
63
|
"@types/react-native-vector-icons": "^6.4.10",
|
|
63
64
|
"babel-plugin-inline-import": "^3.0.0",
|
|
64
|
-
"eslint-config-hd": "8.
|
|
65
|
+
"eslint-config-hd": "8.34.0",
|
|
65
66
|
"eslint-plugin-import": "^2.27.5",
|
|
66
67
|
"jest": "^27.3.1",
|
|
67
|
-
"prettier-config-hd": "8.
|
|
68
|
+
"prettier-config-hd": "8.34.0",
|
|
68
69
|
"react": "18.0.0",
|
|
69
70
|
"react-native": "0.69.7",
|
|
70
71
|
"react-native-gesture-handler": "~2.5.0",
|
package/rollup.config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"activate":59000,"add-emoji":59001,"add-person":59002,"adjustment":59003,"alignment":59004,"antenna":59005,"archive":59006,"assignment-warning":59007,"bank":59008,"bell":59009,"billing":59010,"bolt":59011,"bookmark-added":59012,"bookmark":59013,"box-check":59014,"box":59015,"bpay":59016,"buildings":59017,"cake":59018,"calendar-clock":59019,"calendar":59020,"candy-box-menu":59021,"caret-down-small":59022,"caret-down":59023,"caret-left-small":59024,"caret-left":59025,"caret-right-small":59026,"caret-right":59027,"caret-up-small":59028,"caret-up":59029,"check-radio":59030,"circle-add":59031,"circle-cancel":59032,"circle-check":59033,"circle-down":59034,"circle-info":59035,"circle-left":59036,"circle-ok":59037,"circle-pencil":59038,"circle-question":59039,"circle-remove":59040,"circle-right":59041,"circle-up":59042,"circle-warning":59043,"clock-3":59044,"clock":59045,"cloud-download":59046,"cloud-upload":59047,"cog":59048,"coin":59049,"contacts":59050,"credit-card":59051,"diamond":59052,"direction-arrows":59053,"directory":59054,"document":59055,"dollar-coin-shine":59056,"double-buildings":59057,"edit-template":59058,"envelope":59059,"exclude":59060,"expense":59061,"eye-circle":59062,"eye-invisible":59063,"eye":59064,"face-meh":59065,"face-sad":59066,"face-smiley":59067,"feed":59068,"feedbacks":59069,"file-certified":59070,"file-clone":59071,"file-copy":59072,"file-csv":59073,"file-dispose":59074,"file-doc":59075,"file-excel":59076,"file-export":59077,"file-lock":59078,"file-pdf":59079,"file-powerpoint":59080,"file-search":59081,"file-secured":59082,"file-sheets":59083,"file-slide":59084,"file-verified":59085,"file-word":59086,"file":59087,"filter":59088,"folder-user":59089,"folder":59090,"format-bold":59091,"format-heading1":59092,"format-heading2":59093,"format-italic":59094,"format-list-bulleted":59095,"format-list-numbered":59096,"format-underlined":59097,"funnel-filter":59098,"global-dollar":59099,"globe":59100,"graduation-cap":59101,"graph":59102,"happy-sun":59103,"health-bag":59104,"heart":59105,"home":59106,"image":59107,"import":59108,"incident-siren":59109,"instapay":59110,"list":59111,"loading-2":59112,"loading":59113,"location":59114,"lock":59115,"looks-one":59116,"looks-two":59117,"media-content":59118,"menu":59119,"money-notes":59120,"moneybag":59121,"moon":59122,"multiple-stars":59123,"multiple-users":59124,"node":59125,"open-folder":59126,"paperclip":59127,"payment-summary":59128,"pencil":59129,"phone":59130,"piggy-bank":59131,"plane-up":59132,"plane":59133,"play-circle":59134,"print":59135,"raising-hands":59136,"reply-arrow":59137,"reply":59138,"reschedule":59139,"rostering":59140,"save":59141,"schedule-send":59142,"schedule":59143,"search-person":59144,"send":59145,"speaker-active":59146,"speaker":59147,"star-award":59148,"star-badge":59149,"star-circle":59150,"star-medal":59151,"star":59152,"steps-circle":59153,"stopwatch":59154,"suitcase":59155,"surfing":59156,"survey":59157,"swag-pillar-benefit":59158,"swag-pillar-career":59159,"swag-pillar-money":59160,"swag-pillar-work":59161,"swag":59162,"switch":59163,"tag":59164,"target":59165,"teams":59166,"timesheet":59167,"touch-id":59168,"trash-bin":59169,"unlock":59170,"user":59171,"video-1":59172,"video-2":59173,"wallet":59174,"warning":59175,"activate-outlined":59176,"add-credit-card-outlined":59177,"add-person-outlined":59178,"add-section-outlined":59179,"add-time-outlined":59180,"add":59181,"adjustment-outlined":59182,"ai-outlined":59183,"alignment-2-outlined":59184,"alignment-outlined":59185,"all-caps":59186,"arrow-down":59187,"arrow-downwards":59188,"arrow-left":59189,"arrow-leftwards":59190,"arrow-right":59191,"arrow-rightwards":59192,"arrow-up":59193,"arrow-upwards":59194,"article-outlined":59195,"at-sign":59196,"auto-graph-outlined":59197,"beer-outlined":59198,"bell-active-outlined":59199,"bell-outlined":59200,"bell-slash-outlined":59201,"billing-outlined":59202,"body-outlined":59203,"bold":59204,"book-outlined":59205,"bookmark-added-outlined":59206,"bookmark-outlined":59207,"box-check-outlined":59208,"box-outlined":59209,"bullet-points":59210,"cake-outlined":59211,"calendar-dates-outlined":59212,"calendar-star-outlined":59213,"call-split-outlined":
|
|
1
|
+
{"activate":59000,"add-emoji":59001,"add-person":59002,"adjustment":59003,"alignment":59004,"antenna":59005,"archive":59006,"assignment-warning":59007,"bank":59008,"bell":59009,"billing":59010,"bolt":59011,"bookmark-added":59012,"bookmark":59013,"box-check":59014,"box":59015,"bpay":59016,"buildings":59017,"cake":59018,"calendar-clock":59019,"calendar":59020,"candy-box-menu":59021,"caret-down-small":59022,"caret-down":59023,"caret-left-small":59024,"caret-left":59025,"caret-right-small":59026,"caret-right":59027,"caret-up-small":59028,"caret-up":59029,"check-radio":59030,"circle-add":59031,"circle-cancel":59032,"circle-check":59033,"circle-down":59034,"circle-info":59035,"circle-left":59036,"circle-ok":59037,"circle-pencil":59038,"circle-question":59039,"circle-remove":59040,"circle-right":59041,"circle-up":59042,"circle-warning":59043,"clock-3":59044,"clock":59045,"cloud-download":59046,"cloud-upload":59047,"cog":59048,"coin":59049,"contacts":59050,"credit-card":59051,"diamond":59052,"direction-arrows":59053,"directory":59054,"document":59055,"dollar-coin-shine":59056,"double-buildings":59057,"edit-template":59058,"envelope":59059,"exclude":59060,"expense":59061,"eye-circle":59062,"eye-invisible":59063,"eye":59064,"face-meh":59065,"face-sad":59066,"face-smiley":59067,"feed":59068,"feedbacks":59069,"file-certified":59070,"file-clone":59071,"file-copy":59072,"file-csv":59073,"file-dispose":59074,"file-doc":59075,"file-excel":59076,"file-export":59077,"file-lock":59078,"file-pdf":59079,"file-powerpoint":59080,"file-search":59081,"file-secured":59082,"file-sheets":59083,"file-slide":59084,"file-verified":59085,"file-word":59086,"file":59087,"filter":59088,"folder-user":59089,"folder":59090,"format-bold":59091,"format-heading1":59092,"format-heading2":59093,"format-italic":59094,"format-list-bulleted":59095,"format-list-numbered":59096,"format-underlined":59097,"funnel-filter":59098,"global-dollar":59099,"globe":59100,"graduation-cap":59101,"graph":59102,"happy-sun":59103,"health-bag":59104,"heart":59105,"home":59106,"image":59107,"import":59108,"incident-siren":59109,"instapay":59110,"list":59111,"loading-2":59112,"loading":59113,"location":59114,"lock":59115,"looks-one":59116,"looks-two":59117,"media-content":59118,"menu":59119,"money-notes":59120,"moneybag":59121,"moon":59122,"multiple-stars":59123,"multiple-users":59124,"node":59125,"open-folder":59126,"paperclip":59127,"payment-summary":59128,"pencil":59129,"phone":59130,"piggy-bank":59131,"plane-up":59132,"plane":59133,"play-circle":59134,"print":59135,"raising-hands":59136,"reply-arrow":59137,"reply":59138,"reschedule":59139,"rostering":59140,"save":59141,"schedule-send":59142,"schedule":59143,"search-person":59144,"send":59145,"speaker-active":59146,"speaker":59147,"star-award":59148,"star-badge":59149,"star-circle":59150,"star-medal":59151,"star":59152,"steps-circle":59153,"stopwatch":59154,"suitcase":59155,"surfing":59156,"survey":59157,"swag-pillar-benefit":59158,"swag-pillar-career":59159,"swag-pillar-money":59160,"swag-pillar-work":59161,"swag":59162,"switch":59163,"tag":59164,"target":59165,"teams":59166,"timesheet":59167,"touch-id":59168,"trash-bin":59169,"unlock":59170,"user":59171,"video-1":59172,"video-2":59173,"wallet":59174,"warning":59175,"activate-outlined":59176,"add-credit-card-outlined":59177,"add-person-outlined":59178,"add-section-outlined":59179,"add-time-outlined":59180,"add":59181,"adjustment-outlined":59182,"ai-outlined":59183,"alignment-2-outlined":59184,"alignment-outlined":59185,"all-caps":59186,"arrow-down":59187,"arrow-downwards":59188,"arrow-left":59189,"arrow-leftwards":59190,"arrow-right":59191,"arrow-rightwards":59192,"arrow-up":59193,"arrow-upwards":59194,"article-outlined":59195,"at-sign":59196,"auto-graph-outlined":59197,"beer-outlined":59198,"bell-active-outlined":59199,"bell-outlined":59200,"bell-slash-outlined":59201,"billing-outlined":59202,"body-outlined":59203,"bold":59204,"book-outlined":59205,"bookmark-added-outlined":59206,"bookmark-outlined":59207,"box-check-outlined":59208,"box-outlined":59209,"bullet-points":59210,"cake-outlined":59211,"calendar-dates-outlined":59212,"calendar-star-outlined":59213,"call-outlined":59214,"call-split-outlined":59215,"camera-outlined":59216,"cancel":59217,"car-forward-outlined":59218,"charging-station-outlined":59219,"chat-bubble-outlined":59220,"chat-unread-outlined":59221,"checkmark":59222,"circle-add-outlined":59223,"circle-cancel-outlined":59224,"circle-down-outlined":59225,"circle-info-outlined":59226,"circle-left-outlined":59227,"circle-ok-outlined":59228,"circle-question-outlined":59229,"circle-remove-outlined":59230,"circle-right-outlined":59231,"circle-up-outlined":59232,"circle-warning-outlined":59233,"clock-2-outlined":59234,"clock-outlined":59235,"cog-outlined":59236,"coin-outlined":59237,"coin-super-outlined":59238,"comment-outlined":59239,"contacts-outlined":59240,"contacts-user-outlined":59241,"credit-card-outlined":59242,"cup-outlined":59243,"dentistry-outlined":59244,"direction-arrows-outlined":59245,"directory-outlined":59246,"document-outlined":59247,"dollar-box-outlined":59248,"dollar-card-outlined":59249,"dollar-coin-shine-outlined":59250,"dollar-credit-card-outlined":59251,"dollar-sign":59252,"double-buildings-outlined":59253,"double-left-arrows":59254,"double-right-arrows":59255,"download-box-outlined":59256,"download-outlined":59257,"edit-template-outlined":59258,"email-outlined":59259,"enter-arrow":59260,"envelope-outlined":59261,"expense-outlined":59262,"explore-outlined":59263,"extension-outlined":59264,"external-link":59265,"eye-invisible-outlined":59266,"eye-outlined":59267,"face-id":59268,"face-meh-outlined":59269,"face-open-smiley-outlined":59270,"face-sad-outlined":59271,"face-smiley-outlined":59272,"fastfood-outlined":59273,"feed-outlined":59274,"file-certified-outlined":59275,"file-clone-outlined":59276,"file-copy-outlined":59277,"file-dispose-outlined":59278,"file-dollar-certified-outlined":59279,"file-dollar-outlined":59280,"file-download-outlined":59281,"file-export-outlined":59282,"file-lock-outlined":59283,"file-outlined":59284,"file-search-outlined":59285,"file-secured-outlined":59286,"file-statutory-outlined":59287,"file-verified-outlined":59288,"filter-outlined":59289,"folder-outlined":59290,"folder-user-outlined":59291,"form-outlined":59292,"funnel-filter-outline":59293,"graph-outlined":59294,"hand-holding-user-outlined":59295,"happy-sun-outlined":59296,"health-bag-outlined":59297,"heart-outlined":59298,"home-active-outlined":59299,"home-outlined":59300,"id-card-outlined":59301,"image-outlined":59302,"import-outlined":59303,"instapay-outlined":59304,"italic":59305,"link-1":59306,"link-2":59307,"list-outlined":59308,"live-help-outlined":59309,"location-on-outlined":59310,"location-outlined":59311,"lock-outlined":59312,"locked-file-outlined":59313,"log-out":59314,"mail-outlined":59315,"media-content-outlined":59316,"menu-close":59317,"menu-expand":59318,"menu-fold-outlined":59319,"menu-unfold-outlined":59320,"moneybag-outlined":59321,"moon-outlined":59322,"more-horizontal":59323,"more-vertical":59324,"multiple-folders-outlined":59325,"multiple-users-outlined":59326,"near-me-outlined":59327,"node-outlined":59328,"number-points":59329,"number":59330,"overview-outlined":59331,"payment-summary-outlined":59332,"payslip-outlined":59333,"pencil-outlined":59334,"percentage":59335,"phone-outlined":59336,"piggy-bank-outlined":59337,"plane-outlined":59338,"play-circle-outlined":59339,"print-outlined":59340,"qr-code-outlined":59341,"qualification-outlined":59342,"re-assign":59343,"redeem":59344,"refresh":59345,"remove":59346,"reply-outlined":59347,"restart":59348,"return-arrow":59349,"rostering-outlined":59350,"save-outlined":59351,"schedule-outlined":59352,"search-outlined":59353,"search-secured-outlined":59354,"send-outlined":59355,"share-1":59356,"share-2":59357,"share-outlined":59358,"show-chart-outlined":59359,"single-down-arrow":59360,"single-left-arrow":59361,"single-right-arrow":59362,"single-up-arrow":59363,"speaker-active-outlined":59364,"speaker-outlined":59365,"star-circle-outlined":59366,"star-outlined":59367,"stopwatch-outlined":59368,"strikethrough":59369,"styler-outlined":59370,"suitcase-clock-outlined":59371,"suitcase-outlined":59372,"survey-outlined":59373,"switch-outlined":59374,"sync":59375,"tag-outlined":59376,"target-outlined":59377,"tennis-outlined":59378,"ticket-outlined":59379,"timesheet-outlined":59380,"today-outlined":59381,"transfer":59382,"trash-bin-outlined":59383,"umbrela-outlined":59384,"unavailable":59385,"underline":59386,"union-outlined":59387,"unlock-outlined":59388,"upload-outlined":59389,"user-circle-outlined":59390,"user-gear-outlined":59391,"user-outlined":59392,"user-rectangle-outlined":59393,"video-1-outlined":59394,"video-2-outlined":59395,"volunteer-outlined":59396,"wallet-outlined":59397}
|
|
@@ -214,6 +214,7 @@ const IconList = [
|
|
|
214
214
|
'cake-outlined',
|
|
215
215
|
'calendar-dates-outlined',
|
|
216
216
|
'calendar-star-outlined',
|
|
217
|
+
'call-outlined',
|
|
217
218
|
'call-split-outlined',
|
|
218
219
|
'camera-outlined',
|
|
219
220
|
'cancel',
|
|
@@ -314,6 +315,7 @@ const IconList = [
|
|
|
314
315
|
'lock-outlined',
|
|
315
316
|
'locked-file-outlined',
|
|
316
317
|
'log-out',
|
|
318
|
+
'mail-outlined',
|
|
317
319
|
'media-content-outlined',
|
|
318
320
|
'menu-close',
|
|
319
321
|
'menu-expand',
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
Dimensions,
|
|
5
|
+
Easing,
|
|
6
|
+
Platform,
|
|
7
|
+
StyleProp,
|
|
8
|
+
ViewStyle,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
|
|
11
|
+
type ModalContentWrapperProps = {
|
|
12
|
+
children: React.ReactElement;
|
|
13
|
+
visible?: boolean;
|
|
14
|
+
onShow?: () => void;
|
|
15
|
+
testID?: string;
|
|
16
|
+
animationType?: 'none' | 'slide' | 'fade';
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
animated?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type ModalContentWrapperHandler = {
|
|
22
|
+
hide: (callback?: () => void) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const windowHeight = Dimensions.get('window').height;
|
|
26
|
+
const defaultAnimationConfig = {
|
|
27
|
+
easing: Easing.inOut(Easing.cubic),
|
|
28
|
+
duration: 400,
|
|
29
|
+
useNativeDriver: Platform.OS !== 'web',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ModalContentWrapper = forwardRef<
|
|
33
|
+
ModalContentWrapperHandler,
|
|
34
|
+
ModalContentWrapperProps
|
|
35
|
+
>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
animationType,
|
|
39
|
+
children,
|
|
40
|
+
testID,
|
|
41
|
+
onShow,
|
|
42
|
+
style,
|
|
43
|
+
visible,
|
|
44
|
+
animated = true,
|
|
45
|
+
},
|
|
46
|
+
ref
|
|
47
|
+
) => {
|
|
48
|
+
const animatedValue = React.useRef(new Animated.Value(0)).current;
|
|
49
|
+
|
|
50
|
+
const modalAnimation = animatedValue.interpolate(
|
|
51
|
+
animationType === 'fade'
|
|
52
|
+
? {
|
|
53
|
+
inputRange: [0, 1],
|
|
54
|
+
outputRange: [0, 1],
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
inputRange: [0, 1],
|
|
58
|
+
outputRange: [windowHeight, 0],
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
React.useImperativeHandle(
|
|
63
|
+
ref,
|
|
64
|
+
() => ({
|
|
65
|
+
hide: (callback) => {
|
|
66
|
+
Animated.timing(animatedValue, {
|
|
67
|
+
toValue: 0,
|
|
68
|
+
...defaultAnimationConfig,
|
|
69
|
+
}).start(callback);
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
[animatedValue]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
// Hiding animation will be called from the modal component
|
|
77
|
+
if (visible) {
|
|
78
|
+
Animated.timing(animatedValue, {
|
|
79
|
+
toValue: 1,
|
|
80
|
+
...defaultAnimationConfig,
|
|
81
|
+
// Prevent animation when updating the modal content
|
|
82
|
+
duration: animated ? defaultAnimationConfig.duration : 0,
|
|
83
|
+
}).start(() => {
|
|
84
|
+
onShow?.();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}, [visible, animatedValue, onShow, animated]);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Animated.View
|
|
91
|
+
testID={testID}
|
|
92
|
+
style={[
|
|
93
|
+
style,
|
|
94
|
+
{
|
|
95
|
+
...(animationType === 'fade' ? { opacity: modalAnimation } : {}),
|
|
96
|
+
...(animationType === 'slide'
|
|
97
|
+
? {
|
|
98
|
+
transform: [{ translateY: modalAnimation }],
|
|
99
|
+
}
|
|
100
|
+
: {}),
|
|
101
|
+
},
|
|
102
|
+
]}
|
|
103
|
+
>
|
|
104
|
+
{children}
|
|
105
|
+
</Animated.View>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
ModalContentWrapper.displayName = 'ModalContentWrapper';
|
|
111
|
+
|
|
112
|
+
export default ModalContentWrapper;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { forwardRef, useRef } from 'react';
|
|
2
|
+
import { Animated, StyleSheet, ViewProps } from 'react-native';
|
|
3
|
+
import RootSiblings from 'react-native-root-siblings';
|
|
4
|
+
import { useTheme } from '../../../theme';
|
|
5
|
+
import Box from '../../Box';
|
|
6
|
+
|
|
7
|
+
export type ModalPresenterHandles = {
|
|
8
|
+
animatedOut: (completion?: () => void) => void;
|
|
9
|
+
};
|
|
10
|
+
export type ModalDismissFunc = (onDismiss?: () => void) => void;
|
|
11
|
+
export type ModalUpdateFunc = (content: React.ReactNode) => void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Modal handler is returned by `showModal` function.
|
|
15
|
+
*/
|
|
16
|
+
export type ModalHandler = {
|
|
17
|
+
/**
|
|
18
|
+
* Same `dismiss` function as in `ModalContentProps`.
|
|
19
|
+
*/
|
|
20
|
+
dismiss: ModalDismissFunc;
|
|
21
|
+
/**
|
|
22
|
+
* Same `update` function as in `ModalContentProps`.
|
|
23
|
+
*/
|
|
24
|
+
update: ModalUpdateFunc;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const ModalPresenter = forwardRef<ModalPresenterHandles, ViewProps>(
|
|
28
|
+
({ style, children, ...props }, ref) => {
|
|
29
|
+
const animatedOpacity = useRef(new Animated.Value(0));
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
Animated.spring(animatedOpacity.current, {
|
|
34
|
+
toValue: 1,
|
|
35
|
+
useNativeDriver: true,
|
|
36
|
+
}).start();
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
React.useImperativeHandle(ref, () => ({
|
|
40
|
+
animatedOut: (completion?: () => void) => {
|
|
41
|
+
Animated.spring(animatedOpacity.current, {
|
|
42
|
+
toValue: 0,
|
|
43
|
+
useNativeDriver: true,
|
|
44
|
+
}).start(() => {
|
|
45
|
+
completion?.();
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Box style={StyleSheet.absoluteFill}>
|
|
52
|
+
<Animated.View
|
|
53
|
+
style={[
|
|
54
|
+
{
|
|
55
|
+
width: '100%',
|
|
56
|
+
height: '100%',
|
|
57
|
+
backgroundColor: `${theme.colors.overlayGlobalSurface}66`, // 66 = 40% opacity as suggested by the mobile color guidelines
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
opacity: animatedOpacity.current,
|
|
61
|
+
},
|
|
62
|
+
style,
|
|
63
|
+
]}
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</Animated.View>
|
|
68
|
+
</Box>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Present a modal on screen immediately.
|
|
75
|
+
*
|
|
76
|
+
* The new presented modal will be on top of existing modals if there are any.
|
|
77
|
+
*
|
|
78
|
+
* @param Content A component to be presented as a modal on screen.
|
|
79
|
+
* This component will be centered horizontally and vertically on screen with
|
|
80
|
+
* a semitransparent black overlay underneath.
|
|
81
|
+
* @param contentProps Props for this modal component.
|
|
82
|
+
* @returns A `ModalHandler` you can use to dismiss the modal.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
export const showModal = (content: React.ReactNode): ModalHandler => {
|
|
86
|
+
let ref: ModalPresenterHandles | null = null;
|
|
87
|
+
let rootSiblings: RootSiblings | null = null;
|
|
88
|
+
|
|
89
|
+
const dismiss: ModalDismissFunc = (onDismiss) => {
|
|
90
|
+
if (rootSiblings) {
|
|
91
|
+
const cleanup = () => {
|
|
92
|
+
rootSiblings?.destroy();
|
|
93
|
+
rootSiblings = null;
|
|
94
|
+
ref = null;
|
|
95
|
+
onDismiss?.();
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (ref) {
|
|
99
|
+
ref.animatedOut(cleanup);
|
|
100
|
+
} else {
|
|
101
|
+
cleanup();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const update: ModalUpdateFunc = (newContent) => {
|
|
107
|
+
rootSiblings?.update(
|
|
108
|
+
<ModalPresenter
|
|
109
|
+
ref={(_ref) => {
|
|
110
|
+
ref = _ref;
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{newContent}
|
|
114
|
+
</ModalPresenter>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
rootSiblings = new RootSiblings(
|
|
119
|
+
(
|
|
120
|
+
<ModalPresenter
|
|
121
|
+
ref={(_ref) => {
|
|
122
|
+
ref = _ref;
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{content}
|
|
126
|
+
</ModalPresenter>
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return { dismiss, update };
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
ModalPresenter.displayName = 'ModalPresenter';
|
|
134
|
+
|
|
135
|
+
export default ModalPresenter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { RootSiblingParent } from 'react-native-root-siblings';
|
|
3
|
+
|
|
4
|
+
const ModalProvider = ({ children }: { children: ReactNode }) => {
|
|
5
|
+
return <RootSiblingParent>{children}</RootSiblingParent>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default ModalProvider;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
3
|
+
import ModalContentWrapper from '../ModalContentWrapper';
|
|
4
|
+
import Typography from '../../Typography';
|
|
5
|
+
|
|
6
|
+
describe('ModalContentWrapper', () => {
|
|
7
|
+
it('should render correctly', () => {
|
|
8
|
+
const wrapper = renderWithTheme(
|
|
9
|
+
<ModalContentWrapper
|
|
10
|
+
testID="modal-content-wrapper"
|
|
11
|
+
style={{ backgroundColor: 'blue' }}
|
|
12
|
+
visible
|
|
13
|
+
>
|
|
14
|
+
<Typography.Text>Modal content</Typography.Text>
|
|
15
|
+
</ModalContentWrapper>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
19
|
+
expect(wrapper.getByText('Modal content')).toBeTruthy();
|
|
20
|
+
expect(wrapper.getByTestId('modal-content-wrapper')).toBeTruthy();
|
|
21
|
+
expect(wrapper.getByTestId('modal-content-wrapper')).toHaveStyle({
|
|
22
|
+
backgroundColor: 'blue',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
3
|
+
import ModalPresenter, {
|
|
4
|
+
ModalPresenterHandles,
|
|
5
|
+
} from '../ModalPresenter/ModalPresenter';
|
|
6
|
+
import Typography from '../../Typography';
|
|
7
|
+
|
|
8
|
+
jest.mock('react-native', () => {
|
|
9
|
+
const RN = jest.requireActual('react-native');
|
|
10
|
+
|
|
11
|
+
const mockedAnimatedFunctions = {
|
|
12
|
+
start: (callback: () => void) => {
|
|
13
|
+
if (callback) {
|
|
14
|
+
callback();
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
_isUsingNativeDriver: () => jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
RN.Animated.spring = () => mockedAnimatedFunctions;
|
|
21
|
+
|
|
22
|
+
return RN;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('ModalPresenter', () => {
|
|
26
|
+
it('should render correctly', () => {
|
|
27
|
+
const wrapper = renderWithTheme(
|
|
28
|
+
<ModalPresenter
|
|
29
|
+
testID="modal-presenter"
|
|
30
|
+
style={{ backgroundColor: 'blue' }}
|
|
31
|
+
>
|
|
32
|
+
<Typography.Text>Modal content</Typography.Text>
|
|
33
|
+
</ModalPresenter>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
37
|
+
expect(wrapper.getByText('Modal content')).toBeTruthy();
|
|
38
|
+
expect(wrapper.getByTestId('modal-presenter')).toBeTruthy();
|
|
39
|
+
expect(wrapper.getByTestId('modal-presenter')).toHaveStyle({
|
|
40
|
+
backgroundColor: 'blue',
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('allow using ref', () => {
|
|
45
|
+
const ref = React.createRef<ModalPresenterHandles>();
|
|
46
|
+
const completion = jest.fn();
|
|
47
|
+
|
|
48
|
+
renderWithTheme(
|
|
49
|
+
<ModalPresenter ref={ref}>
|
|
50
|
+
<Typography.Text>Modal content</Typography.Text>
|
|
51
|
+
</ModalPresenter>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
ref.current?.animatedOut(completion);
|
|
55
|
+
expect(completion).toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`ModalContentWrapper should render correctly 1`] = `
|
|
4
|
+
<View
|
|
5
|
+
collapsable={false}
|
|
6
|
+
style={
|
|
7
|
+
Object {
|
|
8
|
+
"backgroundColor": "blue",
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
testID="modal-content-wrapper"
|
|
12
|
+
>
|
|
13
|
+
<Text
|
|
14
|
+
allowFontScaling={false}
|
|
15
|
+
style={
|
|
16
|
+
Array [
|
|
17
|
+
Object {
|
|
18
|
+
"color": "#001f23",
|
|
19
|
+
"fontFamily": "BeVietnamPro-Regular",
|
|
20
|
+
"fontSize": 14,
|
|
21
|
+
"letterSpacing": 0.42,
|
|
22
|
+
"lineHeight": 22,
|
|
23
|
+
},
|
|
24
|
+
undefined,
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
themeFontSize="medium"
|
|
28
|
+
themeFontWeight="regular"
|
|
29
|
+
themeIntent="body"
|
|
30
|
+
themeTypeface="neutral"
|
|
31
|
+
>
|
|
32
|
+
Modal content
|
|
33
|
+
</Text>
|
|
34
|
+
</View>
|
|
35
|
+
`;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`ModalPresenter should render correctly 1`] = `
|
|
4
|
+
<View
|
|
5
|
+
style={
|
|
6
|
+
Array [
|
|
7
|
+
Object {},
|
|
8
|
+
Object {
|
|
9
|
+
"bottom": 0,
|
|
10
|
+
"left": 0,
|
|
11
|
+
"position": "absolute",
|
|
12
|
+
"right": 0,
|
|
13
|
+
"top": 0,
|
|
14
|
+
},
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
>
|
|
18
|
+
<View
|
|
19
|
+
collapsable={false}
|
|
20
|
+
style={
|
|
21
|
+
Object {
|
|
22
|
+
"alignItems": "center",
|
|
23
|
+
"backgroundColor": "blue",
|
|
24
|
+
"height": "100%",
|
|
25
|
+
"justifyContent": "center",
|
|
26
|
+
"opacity": 0,
|
|
27
|
+
"width": "100%",
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
testID="modal-presenter"
|
|
31
|
+
>
|
|
32
|
+
<Text
|
|
33
|
+
allowFontScaling={false}
|
|
34
|
+
style={
|
|
35
|
+
Array [
|
|
36
|
+
Object {
|
|
37
|
+
"color": "#001f23",
|
|
38
|
+
"fontFamily": "BeVietnamPro-Regular",
|
|
39
|
+
"fontSize": 14,
|
|
40
|
+
"letterSpacing": 0.42,
|
|
41
|
+
"lineHeight": 22,
|
|
42
|
+
},
|
|
43
|
+
undefined,
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
themeFontSize="medium"
|
|
47
|
+
themeFontWeight="regular"
|
|
48
|
+
themeIntent="body"
|
|
49
|
+
themeTypeface="neutral"
|
|
50
|
+
>
|
|
51
|
+
Modal content
|
|
52
|
+
</Text>
|
|
53
|
+
</View>
|
|
54
|
+
</View>
|
|
55
|
+
`;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BackHandler } from 'react-native';
|
|
3
|
+
import Modal from '..';
|
|
4
|
+
import Typography from '../../Typography';
|
|
5
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
6
|
+
|
|
7
|
+
jest.mock('react-native', () => {
|
|
8
|
+
const RN = jest.requireActual('react-native');
|
|
9
|
+
|
|
10
|
+
const mockedAnimatedFunctions = {
|
|
11
|
+
start: (callback: () => void) => {
|
|
12
|
+
if (callback) {
|
|
13
|
+
callback();
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
_isUsingNativeDriver: () => jest.fn(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
RN.Animated.timing = () => mockedAnimatedFunctions;
|
|
20
|
+
|
|
21
|
+
return RN;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('Modal', () => {
|
|
25
|
+
it('onShow should be called correctly', () => {
|
|
26
|
+
const onShow = jest.fn();
|
|
27
|
+
|
|
28
|
+
renderWithTheme(
|
|
29
|
+
<Modal onShow={onShow} visible>
|
|
30
|
+
<Typography.Text>Modal content</Typography.Text>
|
|
31
|
+
</Modal>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(onShow).toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('onRequestClose should be called correctly', () => {
|
|
38
|
+
const onRequestClose = jest.fn();
|
|
39
|
+
|
|
40
|
+
renderWithTheme(
|
|
41
|
+
<Modal onRequestClose={onRequestClose} visible>
|
|
42
|
+
<Typography.Text>Modal content</Typography.Text>
|
|
43
|
+
</Modal>
|
|
44
|
+
);
|
|
45
|
+
// @ts-expect-error: BackHandler mock
|
|
46
|
+
BackHandler.mockPressBack();
|
|
47
|
+
|
|
48
|
+
expect(onRequestClose).toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
});
|