@rabbitio/ui-kit 1.0.0-beta.42 → 1.0.0-beta.45
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/.gitlab-ci.yml +29 -0
- package/.husky/commit-msg +8 -0
- package/.husky/pre-push +1 -0
- package/README.md +13 -4
- package/dist/index.cjs +1545 -148
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +23630 -0
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +1318 -103
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +1534 -149
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1544 -152
- package/dist/index.umd.js.map +1 -1
- package/package.json +16 -3
- package/src/assets/image/icons/arrow-tosca.svg +3 -0
- package/src/assets/image/icons/arrow-white.svg +14 -0
- package/src/assets/image/icons/failed-validation-icon.svg +15 -0
- package/src/assets/image/icons/successful-validation-icon.svg +10 -0
- package/src/common/amountUtils.js +4 -2
- package/src/common/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +14 -0
- package/src/common/utils/cache.js +4 -4
- package/src/components/atoms/BackgroundTitle/BackgroundTitle.jsx +44 -0
- package/src/components/atoms/BackgroundTitle/background-title.module.scss +52 -0
- package/src/components/atoms/Validation/Validation.jsx +130 -0
- package/src/components/atoms/Validation/validation.module.scss +15 -0
- package/src/components/atoms/buttons/Close/Close.jsx +64 -0
- package/src/components/atoms/buttons/Close/close.module.scss +75 -0
- package/src/components/atoms/buttons/LinkButton/LinkButton.jsx +121 -0
- package/src/components/atoms/buttons/LinkButton/link-button.module.scss +45 -0
- package/src/components/organisms/Dialog/Dialog.jsx +515 -0
- package/src/components/organisms/Dialog/DialogButtons/DialogButtons.jsx +122 -0
- package/src/components/organisms/Dialog/DialogButtons/dialog-buttons.module.scss +25 -0
- package/src/components/organisms/Dialog/DialogStep/DialogStep.jsx +664 -0
- package/src/components/organisms/Dialog/DialogStep/dialog-step.module.scss +362 -0
- package/src/components/organisms/Dialog/dialog.module.scss +223 -0
- package/src/components/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +139 -0
- package/src/components/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +71 -0
- package/src/components/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +144 -0
- package/src/components/utils/inputValueProviders.js +58 -0
- package/src/constants/organisms/dialog/DialogStep/dialogStep.js +1 -0
- package/src/constants/organisms/dialog/dialog.js +29 -0
- package/src/index.js +11 -0
- package/src/robustExteranlApiCallerService/robustExternalAPICallerService.js +3 -1
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +787 -0
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +745 -0
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +31 -0
- package/src/swaps-lib/external-apis/swapProvider.js +17 -4
- package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +91 -30
- package/src/swaps-lib/models/baseSwapCreationInfo.js +4 -1
- package/src/swaps-lib/models/existingSwap.js +3 -0
- package/src/swaps-lib/models/existingSwapWithFiatData.js +4 -0
- package/src/swaps-lib/services/publicSwapService.js +32 -4
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +506 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1311 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getAllSupportedCurrencies.test.js +76 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getDepositCurrencies.test.js +82 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1892 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getWithdrawalCurrencies.test.js +111 -0
- package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +88 -0
- package/stories/stubs/exampleContent.jsx +20 -0
- package/styles/fonts/NunitoSans-Bold.ttf +0 -0
- package/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
- package/styles/fonts/NunitoSans-Light.ttf +0 -0
- package/styles/fonts/NunitoSans-Regular.ttf +0 -0
- package/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
- package/styles/index.scss +14 -13
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@import "../../../../../styles/index";
|
|
2
|
+
|
|
3
|
+
.link-button {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
|
|
7
|
+
&:not(.disabled) {
|
|
8
|
+
@extend %hover-state;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&.icon-rotate-90deg img {
|
|
12
|
+
transform: rotate(90deg);
|
|
13
|
+
}
|
|
14
|
+
&.icon-rotate-180deg img {
|
|
15
|
+
transform: rotate(180deg);
|
|
16
|
+
}
|
|
17
|
+
&.icon-rotate-270deg img {
|
|
18
|
+
transform: rotate(270deg);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
img {
|
|
22
|
+
margin-left: -3px;
|
|
23
|
+
margin-right: Margin("1");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&-text {
|
|
27
|
+
@extend %text-bold;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
color: $white;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
text-align: center;
|
|
32
|
+
line-height: 14px;
|
|
33
|
+
// padding-bottom: 1px;
|
|
34
|
+
margin: 0;
|
|
35
|
+
|
|
36
|
+
&.colored {
|
|
37
|
+
color: SolidColor("tosca");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.disabled {
|
|
41
|
+
color: SolidColor("grey");
|
|
42
|
+
cursor: default;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { CSSTransition } from "react-transition-group";
|
|
4
|
+
import AnimateHeight from "react-animate-height";
|
|
5
|
+
import ResizeObserver from "resize-observer-polyfill";
|
|
6
|
+
import {
|
|
7
|
+
disableBodyScroll,
|
|
8
|
+
enableBodyScroll,
|
|
9
|
+
clearAllBodyScrollLocks,
|
|
10
|
+
} from "body-scroll-lock";
|
|
11
|
+
import animateScrollTo from "animated-scroll-to";
|
|
12
|
+
|
|
13
|
+
import { useReferredState } from "../../hooks/useReferredState";
|
|
14
|
+
import { logErrorOrOutputToConsole } from "../../../common/errorUtils";
|
|
15
|
+
import {
|
|
16
|
+
DIALOG_TRANSITION_STEP_DURATION,
|
|
17
|
+
DIALOG_SIZES,
|
|
18
|
+
} from "../../../constants/organisms/dialog/dialog";
|
|
19
|
+
|
|
20
|
+
import s from "./dialog.module.scss";
|
|
21
|
+
import { Close } from "../../atoms/buttons/Close/Close";
|
|
22
|
+
import { DialogButtons } from "./DialogButtons/DialogButtons";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Flexible modal window component with support for multiple steps. Can be used as an inline component and inserted in regular layouts. Only <DialogStep> components can be passed as children.
|
|
26
|
+
*
|
|
27
|
+
* @component
|
|
28
|
+
* @param {Object} props - Component props.
|
|
29
|
+
* @param {boolean} props.showDialog - Step name, specified on the root dialog level, used as an ID for dialog navigation.
|
|
30
|
+
* @param {string} props.onClose - Handler for when the dialog is closed.
|
|
31
|
+
* @param {string} props.initControls - State-setting function to initialize the dialog-to-step connection.
|
|
32
|
+
* @param {string} props.children - Content of the dialog window, supports only dialog step components.
|
|
33
|
+
* @param {Coin} props.width - Width in pixels, formatted as "1000px" and passed a string. Values should be used from constants.
|
|
34
|
+
* @param {string} props.dispatchDialogOpened - Dispatch upon the dialog closure.
|
|
35
|
+
* @param {string} props.dispatchDialogClosed - Dispatch upon the dialog opening.
|
|
36
|
+
* @param {function} props.inline - If false or not set, dialog will be opened as a modal window above the UI. If true, inline mode will be used, making the dialog a regular block, that can be placed inside a regular layout. Default: false.
|
|
37
|
+
* @returns {JSX.Element} React component
|
|
38
|
+
*/
|
|
39
|
+
export const Dialog = ({
|
|
40
|
+
showDialog = false,
|
|
41
|
+
onClose = () => {},
|
|
42
|
+
initControls = null,
|
|
43
|
+
children = [],
|
|
44
|
+
width = "",
|
|
45
|
+
dispatchDialogOpened = () => {},
|
|
46
|
+
dispatchDialogClosed = () => {},
|
|
47
|
+
inline = false,
|
|
48
|
+
}) => {
|
|
49
|
+
const buttonsConfigurationDefault = {
|
|
50
|
+
primaryButtonTitle: null,
|
|
51
|
+
primaryButtonOnClick: null,
|
|
52
|
+
primaryButtonLoader: false,
|
|
53
|
+
primaryButtonTo: null,
|
|
54
|
+
primaryButtonSetClickTrigger: () => {},
|
|
55
|
+
secondaryButtonTitle: null,
|
|
56
|
+
secondaryButtonOnClick: null,
|
|
57
|
+
secondaryButtonTo: null,
|
|
58
|
+
fixedButtonsEnabled: false,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const [showDialogProcessed, setShowDialogProcessed] = useState(false);
|
|
62
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
63
|
+
const [steps, setSteps] = useState(null);
|
|
64
|
+
const [dialogConfiguration, updateDialogConfiguration] = useState({
|
|
65
|
+
cornerBackButtonTitle: "",
|
|
66
|
+
customWidth: width !== "" ? width : DIALOG_SIZES.small.width,
|
|
67
|
+
});
|
|
68
|
+
const [animationConfiguration, updateAnimationConfiguration] = useState({
|
|
69
|
+
animateHeightDelay: DIALOG_TRANSITION_STEP_DURATION * 2,
|
|
70
|
+
animateHeightTransitionSpeed: DIALOG_TRANSITION_STEP_DURATION,
|
|
71
|
+
animateHeightTransitionEnabled: false,
|
|
72
|
+
wrapperWidthAnimationEnabled: false,
|
|
73
|
+
currentStepAnimationEnabled: false,
|
|
74
|
+
cornerBackButtonAnimationEnabled: false,
|
|
75
|
+
triggerAnimationFinishedDispatcher: false,
|
|
76
|
+
dialogContainerOverflowDisabled: false,
|
|
77
|
+
hideMobileBottomButtonSection: false,
|
|
78
|
+
});
|
|
79
|
+
const [stepAnimationFinishedTrigger, setStepAnimationFinishedTrigger] =
|
|
80
|
+
useState(0);
|
|
81
|
+
const [dialogVerticalHeight, setDialogVerticalHeight] = useState(300);
|
|
82
|
+
const [currentStepRef, setCurrentStepRef] = useState({});
|
|
83
|
+
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
84
|
+
const [buttonsConfiguration, updateButtonsConfigurationState] = useState(
|
|
85
|
+
buttonsConfigurationDefault
|
|
86
|
+
);
|
|
87
|
+
const [buttonControllingStep, updateButtonControllingStep] =
|
|
88
|
+
useReferredState("");
|
|
89
|
+
const [isMultiStep, setIsMultiStep] = useState(false);
|
|
90
|
+
|
|
91
|
+
const dialogWrapperRef = useRef();
|
|
92
|
+
const dialogRef = useRef();
|
|
93
|
+
const scrollableRef = useRef();
|
|
94
|
+
const resizeObserverRef = useRef(null);
|
|
95
|
+
|
|
96
|
+
const updateButtonsConfiguration = (newConfiguration, stepName) => {
|
|
97
|
+
if (
|
|
98
|
+
buttonControllingStep.current === stepName ||
|
|
99
|
+
buttonControllingStep.current === ""
|
|
100
|
+
)
|
|
101
|
+
updateButtonsConfigurationState(newConfiguration);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (showDialog) disableAnimation();
|
|
106
|
+
setShowDialogProcessed(showDialog);
|
|
107
|
+
showDialog ? dispatchDialogOpened() : dispatchDialogClosed();
|
|
108
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
|
+
}, [showDialog]);
|
|
110
|
+
|
|
111
|
+
const showCornerButtonForStep = (stepName, stepButtonShowParam) => {
|
|
112
|
+
if (stepName !== "") {
|
|
113
|
+
let stepNameId = steps.findIndex((step) => step.name === stepName);
|
|
114
|
+
return stepButtonShowParam !== null &&
|
|
115
|
+
typeof stepButtonShowParam === "boolean"
|
|
116
|
+
? stepButtonShowParam
|
|
117
|
+
: stepNameId > steps.findIndex((step) => step.hidden === false);
|
|
118
|
+
} else {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const nextStep = (animationControlEnabled = true) => {
|
|
124
|
+
const nextStep = steps.find(
|
|
125
|
+
(step, index) => index > currentStep && step.hidden === false
|
|
126
|
+
);
|
|
127
|
+
if (nextStep) {
|
|
128
|
+
goToStep(nextStep.name, animationControlEnabled);
|
|
129
|
+
return true;
|
|
130
|
+
} else {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const previousStep = (animationControlEnabled = true) => {
|
|
136
|
+
const prevSteps = steps
|
|
137
|
+
.map((step, index) => {
|
|
138
|
+
return index < currentStep && step.hidden === false
|
|
139
|
+
? step.name
|
|
140
|
+
: undefined;
|
|
141
|
+
})
|
|
142
|
+
.filter((item) => typeof item !== "undefined");
|
|
143
|
+
|
|
144
|
+
if (prevSteps.length > 0) {
|
|
145
|
+
goToStep(prevSteps[prevSteps.length - 1], animationControlEnabled);
|
|
146
|
+
return true;
|
|
147
|
+
} else {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const getStepClass = (stepName) => {
|
|
153
|
+
if (steps.length > 1) {
|
|
154
|
+
let stepNameId = steps.findIndex((step) => step.name === stepName);
|
|
155
|
+
let stepClass =
|
|
156
|
+
currentStep > stepNameId
|
|
157
|
+
? "prev"
|
|
158
|
+
: currentStep === stepNameId
|
|
159
|
+
? "current"
|
|
160
|
+
: "next";
|
|
161
|
+
return stepClass;
|
|
162
|
+
} else {
|
|
163
|
+
return "current";
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const isCurrentStep = (stepName) => {
|
|
168
|
+
return steps[currentStep].name === stepName;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const toFirstStep = (animationControlEnabled = true) => {
|
|
172
|
+
if (animationControlEnabled) enableAnimation();
|
|
173
|
+
setCurrentStep(steps.findIndex((step) => step.hidden === false));
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const goToStep = (stepName, animationControlEnabled = true) => {
|
|
177
|
+
if (animationControlEnabled) enableAnimation();
|
|
178
|
+
try {
|
|
179
|
+
let stepNameId = steps.findIndex((step) => step.name === stepName);
|
|
180
|
+
setCurrentStep(stepNameId);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
logErrorOrOutputToConsole(e);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const provideCurrentStepRef = (ref) => {
|
|
187
|
+
setCurrentStepRef(ref);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const disableAnimation = (dispatch = false) => {
|
|
191
|
+
updateAnimationConfiguration((prev) => {
|
|
192
|
+
return {
|
|
193
|
+
...prev,
|
|
194
|
+
animateHeightTransitionEnabled: false,
|
|
195
|
+
wrapperWidthAnimationEnabled: false,
|
|
196
|
+
currentStepAnimationEnabled: false,
|
|
197
|
+
cornerBackButtonAnimationEnabled: false,
|
|
198
|
+
dialogContainerOverflowDisabled: false,
|
|
199
|
+
hideMobileBottomButtonSection: false,
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
if (dispatch) {
|
|
203
|
+
updateAnimationConfiguration((prev) => {
|
|
204
|
+
return {
|
|
205
|
+
...prev,
|
|
206
|
+
triggerAnimationFinishedDispatcher: true,
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (animationConfiguration.triggerAnimationFinishedDispatcher) {
|
|
214
|
+
setStepAnimationFinishedTrigger((prev) => prev + 1);
|
|
215
|
+
updateAnimationConfiguration((prev) => {
|
|
216
|
+
return {
|
|
217
|
+
...prev,
|
|
218
|
+
triggerAnimationFinishedDispatcher: false,
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
223
|
+
}, [animationConfiguration]);
|
|
224
|
+
|
|
225
|
+
const enableAnimation = () => {
|
|
226
|
+
updateAnimationConfiguration((prev) => {
|
|
227
|
+
return {
|
|
228
|
+
...prev,
|
|
229
|
+
animateHeightTransitionEnabled: true,
|
|
230
|
+
wrapperWidthAnimationEnabled: true,
|
|
231
|
+
currentStepAnimationEnabled: true,
|
|
232
|
+
cornerBackButtonAnimationEnabled: true,
|
|
233
|
+
dialogContainerOverflowDisabled: true,
|
|
234
|
+
hideMobileBottomButtonSection: true,
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const scrollDialogToTop = () => {
|
|
240
|
+
animateScrollTo(0, {
|
|
241
|
+
elementToScroll: dialogRef?.current,
|
|
242
|
+
minDuration: DIALOG_TRANSITION_STEP_DURATION,
|
|
243
|
+
maxDuration: DIALOG_TRANSITION_STEP_DURATION,
|
|
244
|
+
});
|
|
245
|
+
animateScrollTo(0, {
|
|
246
|
+
elementToScroll: scrollableRef?.current,
|
|
247
|
+
minDuration: DIALOG_TRANSITION_STEP_DURATION,
|
|
248
|
+
maxDuration: DIALOG_TRANSITION_STEP_DURATION,
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const clearButtonsConfiguration = () => {
|
|
253
|
+
updateButtonsConfigurationState(buttonsConfigurationDefault);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
initControls &&
|
|
258
|
+
initControls((prev) => {
|
|
259
|
+
return {
|
|
260
|
+
...prev,
|
|
261
|
+
stepAnimationFinishedTrigger: stepAnimationFinishedTrigger,
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
265
|
+
}, [stepAnimationFinishedTrigger]);
|
|
266
|
+
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
initControls &&
|
|
269
|
+
initControls((prev) => {
|
|
270
|
+
return {
|
|
271
|
+
...prev,
|
|
272
|
+
animationConfiguration: animationConfiguration,
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
276
|
+
}, [animationConfiguration]);
|
|
277
|
+
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (currentStepRef.current) {
|
|
280
|
+
resizeObserverRef.current = new ResizeObserver((entries = []) => {
|
|
281
|
+
entries.forEach((entry) => {
|
|
282
|
+
const { width, height } = entry.contentRect;
|
|
283
|
+
setDimensions({ width, height });
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
if (currentStepRef.current)
|
|
287
|
+
resizeObserverRef.current.observe(currentStepRef.current);
|
|
288
|
+
return () => {
|
|
289
|
+
if (resizeObserverRef.current)
|
|
290
|
+
resizeObserverRef.current.disconnect();
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
294
|
+
}, [currentStepRef]);
|
|
295
|
+
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
setDialogVerticalHeight((prev) =>
|
|
298
|
+
prev !== 0 && dimensions.height === 0 ? prev : dimensions.height
|
|
299
|
+
);
|
|
300
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
301
|
+
}, [dimensions]);
|
|
302
|
+
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
const childrenFormatted = children.length > 1 ? children : [children];
|
|
305
|
+
|
|
306
|
+
let stepsArr = childrenFormatted.map((child) => {
|
|
307
|
+
return {
|
|
308
|
+
name: child.props.stepName,
|
|
309
|
+
hidden: !!child.props.hidden,
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
setSteps(stepsArr);
|
|
313
|
+
|
|
314
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
315
|
+
}, []);
|
|
316
|
+
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
setIsMultiStep(typeof initControls === "function");
|
|
319
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
320
|
+
}, [initControls]);
|
|
321
|
+
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
if (steps)
|
|
324
|
+
initControls &&
|
|
325
|
+
initControls({
|
|
326
|
+
nextStep: nextStep,
|
|
327
|
+
previousStep: previousStep,
|
|
328
|
+
currentStep: currentStep,
|
|
329
|
+
isCurrentStep: isCurrentStep,
|
|
330
|
+
getStepClass: getStepClass,
|
|
331
|
+
updateDialogConfiguration: updateDialogConfiguration,
|
|
332
|
+
toFirstStep: toFirstStep,
|
|
333
|
+
goToStep: goToStep,
|
|
334
|
+
provideCurrentStepRef: provideCurrentStepRef,
|
|
335
|
+
animationConfiguration: animationConfiguration,
|
|
336
|
+
disableAnimation: disableAnimation,
|
|
337
|
+
enableAnimation: enableAnimation,
|
|
338
|
+
showCornerButtonForStep: showCornerButtonForStep,
|
|
339
|
+
stepAnimationFinishedTrigger: stepAnimationFinishedTrigger,
|
|
340
|
+
scrollDialogToTop: scrollDialogToTop,
|
|
341
|
+
updateButtonsConfiguration: updateButtonsConfiguration,
|
|
342
|
+
clearButtonsConfiguration: clearButtonsConfiguration,
|
|
343
|
+
buttonControllingStep: buttonControllingStep.current,
|
|
344
|
+
updateButtonControllingStep: updateButtonControllingStep,
|
|
345
|
+
updateAnimationConfiguration: updateAnimationConfiguration,
|
|
346
|
+
inline: inline,
|
|
347
|
+
});
|
|
348
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
349
|
+
}, [currentStep, steps]);
|
|
350
|
+
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
return () => {
|
|
353
|
+
clearAllBodyScrollLocks();
|
|
354
|
+
updateButtonControllingStep("");
|
|
355
|
+
clearButtonsConfiguration();
|
|
356
|
+
};
|
|
357
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
358
|
+
}, []);
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<CSSTransition
|
|
362
|
+
in={showDialogProcessed}
|
|
363
|
+
timeout={DIALOG_TRANSITION_STEP_DURATION}
|
|
364
|
+
classNames={
|
|
365
|
+
inline
|
|
366
|
+
? undefined
|
|
367
|
+
: {
|
|
368
|
+
enter: s["dialog-transition-enter"],
|
|
369
|
+
enterActive: s["dialog-transition-enter-active"],
|
|
370
|
+
exit: s["dialog-transition-exit"],
|
|
371
|
+
exitActive: s["dialog-transition-exit-active"],
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
unmountOnExit
|
|
375
|
+
onExited={() => {
|
|
376
|
+
disableAnimation(false);
|
|
377
|
+
clearButtonsConfiguration();
|
|
378
|
+
updateButtonControllingStep("");
|
|
379
|
+
}}
|
|
380
|
+
onExiting={() => {
|
|
381
|
+
if (inline) return;
|
|
382
|
+
enableBodyScroll(scrollableRef.current);
|
|
383
|
+
enableBodyScroll(dialogRef.current);
|
|
384
|
+
}}
|
|
385
|
+
onEntered={() => {
|
|
386
|
+
if (inline) return;
|
|
387
|
+
disableBodyScroll(scrollableRef.current);
|
|
388
|
+
disableBodyScroll(dialogRef.current);
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
<div
|
|
392
|
+
className={
|
|
393
|
+
s["dialog"] +
|
|
394
|
+
" " +
|
|
395
|
+
(dialogVerticalHeight > 1 || !isMultiStep
|
|
396
|
+
? ""
|
|
397
|
+
: " " + s["hidden"]) +
|
|
398
|
+
(animationConfiguration.dialogContainerOverflowDisabled
|
|
399
|
+
? " " + s["vertical-overflow-disabled"]
|
|
400
|
+
: "") +
|
|
401
|
+
(inline ? " " + s["inline"] : "")
|
|
402
|
+
}
|
|
403
|
+
ref={dialogRef}
|
|
404
|
+
>
|
|
405
|
+
<div
|
|
406
|
+
className={
|
|
407
|
+
s["scrollable"] + (inline ? " " + s["inline"] : "")
|
|
408
|
+
}
|
|
409
|
+
ref={scrollableRef}
|
|
410
|
+
>
|
|
411
|
+
<div
|
|
412
|
+
className={
|
|
413
|
+
s["dialog-wrapper"] +
|
|
414
|
+
(animationConfiguration.wrapperWidthAnimationEnabled
|
|
415
|
+
? ""
|
|
416
|
+
: " " + s["animation-disabled"]) +
|
|
417
|
+
(inline ? " " + s["inline"] : "")
|
|
418
|
+
}
|
|
419
|
+
ref={dialogWrapperRef}
|
|
420
|
+
style={
|
|
421
|
+
dialogConfiguration.customWidth !== ""
|
|
422
|
+
? { maxWidth: dialogConfiguration.customWidth }
|
|
423
|
+
: width !== ""
|
|
424
|
+
? { maxWidth: width }
|
|
425
|
+
: null
|
|
426
|
+
}
|
|
427
|
+
>
|
|
428
|
+
<AnimateHeight
|
|
429
|
+
duration={
|
|
430
|
+
animationConfiguration.animateHeightTransitionEnabled
|
|
431
|
+
? animationConfiguration.animateHeightTransitionSpeed
|
|
432
|
+
: 0
|
|
433
|
+
}
|
|
434
|
+
delay={
|
|
435
|
+
animationConfiguration.animateHeightTransitionEnabled
|
|
436
|
+
? animationConfiguration.animateHeightDelay
|
|
437
|
+
: 0
|
|
438
|
+
}
|
|
439
|
+
height={isMultiStep ? dialogVerticalHeight : "auto"}
|
|
440
|
+
contentClassName={s["dialog-wrapper-rah-content"]}
|
|
441
|
+
>
|
|
442
|
+
{inline ? (
|
|
443
|
+
""
|
|
444
|
+
) : (
|
|
445
|
+
<div className={s["dialog-wrapper-close"]}>
|
|
446
|
+
<Close
|
|
447
|
+
color="dark"
|
|
448
|
+
onClick={(e) => onClose()}
|
|
449
|
+
large
|
|
450
|
+
/>
|
|
451
|
+
</div>
|
|
452
|
+
)}
|
|
453
|
+
{children}
|
|
454
|
+
</AnimateHeight>
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
{
|
|
458
|
+
<CSSTransition
|
|
459
|
+
in={
|
|
460
|
+
(!!buttonsConfiguration.primaryButtonTitle ||
|
|
461
|
+
!!buttonsConfiguration.secondaryButtonTitle) &&
|
|
462
|
+
!animationConfiguration.hideMobileBottomButtonSection &&
|
|
463
|
+
buttonsConfiguration.fixedButtonsEnabled
|
|
464
|
+
}
|
|
465
|
+
timeout={DIALOG_TRANSITION_STEP_DURATION}
|
|
466
|
+
// classNames={s["fixed-buttons-container"]}
|
|
467
|
+
classNames={{
|
|
468
|
+
enter: s["fixed-buttons-container-enter"],
|
|
469
|
+
enterActive:
|
|
470
|
+
s["fixed-buttons-container-enter-active"],
|
|
471
|
+
enterDone: s["fixed-buttons-container-enter-done"],
|
|
472
|
+
exit: s["fixed-buttons-container-exit"],
|
|
473
|
+
exitActive:
|
|
474
|
+
s["fixed-buttons-container-exit-active"],
|
|
475
|
+
exitDone: s["fixed-buttons-container-exit-done"],
|
|
476
|
+
}}
|
|
477
|
+
unmountOnExit
|
|
478
|
+
>
|
|
479
|
+
<div className={s["fixed-buttons-container"]}>
|
|
480
|
+
<DialogButtons {...buttonsConfiguration} />
|
|
481
|
+
</div>
|
|
482
|
+
</CSSTransition>
|
|
483
|
+
}
|
|
484
|
+
</div>
|
|
485
|
+
</CSSTransition>
|
|
486
|
+
);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
Dialog.propTypes = {
|
|
490
|
+
showDialog: PropTypes.bool,
|
|
491
|
+
onClose: PropTypes.func.isRequired,
|
|
492
|
+
initControls: PropTypes.func,
|
|
493
|
+
children: PropTypes.node,
|
|
494
|
+
width: PropTypes.oneOf([
|
|
495
|
+
"550px",
|
|
496
|
+
"600px",
|
|
497
|
+
"700px",
|
|
498
|
+
"750px",
|
|
499
|
+
"800px",
|
|
500
|
+
"1000px",
|
|
501
|
+
]),
|
|
502
|
+
dispatchDialogOpened: PropTypes.func,
|
|
503
|
+
dispatchDialogClosed: PropTypes.func,
|
|
504
|
+
inline: PropTypes.bool,
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
Dialog.defaultProps = {
|
|
508
|
+
showDialog: false,
|
|
509
|
+
onClose: () => {},
|
|
510
|
+
initControls: null,
|
|
511
|
+
children: [],
|
|
512
|
+
dispatchDialogOpened: () => {},
|
|
513
|
+
dispatchDialogClosed: () => {},
|
|
514
|
+
inline: false,
|
|
515
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
|
|
4
|
+
import { useCallHandlingErrors } from "../../../hooks/useCallHandlingErrors";
|
|
5
|
+
|
|
6
|
+
import s from "./dialog-buttons.module.scss";
|
|
7
|
+
import { LinkButton } from "../../../atoms/buttons/LinkButton/LinkButton";
|
|
8
|
+
import { Button } from "../../../atoms/buttons/Button/Button";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renders a dialog component with configurable primary and secondary buttons.
|
|
12
|
+
* This component allows for a variety of configurations including different button modes,
|
|
13
|
+
* sizes, and actions. It's designed to handle user interactions effectively with error management.
|
|
14
|
+
*
|
|
15
|
+
* @component
|
|
16
|
+
* @param {Object} props - Props for configuring the DialogButtons.
|
|
17
|
+
* @param {string} [props.primaryButtonTitle] - The text to display on the primary button.
|
|
18
|
+
* @param {Function} props.primaryButtonOnClick - Function to call when the primary button is clicked.
|
|
19
|
+
* @param {boolean} [props.primaryButtonLoader=false] - Whether to show a loader on the primary button.
|
|
20
|
+
* @param {string} [props.primaryButtonTo] - The URL or route the primary button should navigate to.
|
|
21
|
+
* @param {Function} [props.primaryButtonSetClickTrigger] - Additional click handling logic for the primary button.
|
|
22
|
+
* @param {string} [props.secondaryButtonTitle] - The text to display on the secondary button.
|
|
23
|
+
* @param {Function} props.secondaryButtonOnClick - Function to call when the secondary button is clicked.
|
|
24
|
+
* @param {string} [props.secondaryButtonTo] - The URL or route the secondary button should navigate to.
|
|
25
|
+
* @param {boolean} [props.secondaryButtonBig=false] - Makes the secondary button larger for visual emphasis.
|
|
26
|
+
* @param {boolean} [props.withBackgroundImage=false] - Indicates if the button should adjust its styling for background images.
|
|
27
|
+
* @param {boolean} [props.hideOnMobiles=false] - Whether to hide the buttons on mobile devices for responsive designs.
|
|
28
|
+
* @returns {React.Element} The rendered component with two buttons based on provided props.
|
|
29
|
+
*/
|
|
30
|
+
export const DialogButtons = ({
|
|
31
|
+
primaryButtonTitle,
|
|
32
|
+
primaryButtonOnClick,
|
|
33
|
+
primaryButtonLoader = false,
|
|
34
|
+
primaryButtonTo,
|
|
35
|
+
primaryButtonSetClickTrigger,
|
|
36
|
+
secondaryButtonTitle,
|
|
37
|
+
secondaryButtonOnClick,
|
|
38
|
+
secondaryButtonTo,
|
|
39
|
+
secondaryButtonBig = false,
|
|
40
|
+
withBackgroundImage = false,
|
|
41
|
+
hideOnMobiles = false,
|
|
42
|
+
}) => {
|
|
43
|
+
const callHandlingErrors = useCallHandlingErrors();
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<div
|
|
48
|
+
className={
|
|
49
|
+
s["dialog-buttons"] +
|
|
50
|
+
(hideOnMobiles ? " " + s["hide-on-mobiles"] : "") +
|
|
51
|
+
(secondaryButtonBig ? " " + s["space-between-layout"] : "")
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
{secondaryButtonTitle ? (
|
|
55
|
+
secondaryButtonTo ? (
|
|
56
|
+
<Button
|
|
57
|
+
mode="primary-transparent"
|
|
58
|
+
to={secondaryButtonTo}
|
|
59
|
+
size="lg"
|
|
60
|
+
loader={false}
|
|
61
|
+
fullWidthOnMobiles
|
|
62
|
+
content={secondaryButtonTitle}
|
|
63
|
+
onClick={secondaryButtonOnClick}
|
|
64
|
+
handleError={callHandlingErrors}
|
|
65
|
+
/>
|
|
66
|
+
) : secondaryButtonBig ? (
|
|
67
|
+
<Button
|
|
68
|
+
mode="transparent"
|
|
69
|
+
size="lg"
|
|
70
|
+
loader={false}
|
|
71
|
+
fullWidthOnMobiles
|
|
72
|
+
content={secondaryButtonTitle}
|
|
73
|
+
onClick={secondaryButtonOnClick}
|
|
74
|
+
handleError={callHandlingErrors}
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
<LinkButton
|
|
78
|
+
onClick={secondaryButtonOnClick}
|
|
79
|
+
content={secondaryButtonTitle}
|
|
80
|
+
isColored={!withBackgroundImage}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
) : null}
|
|
84
|
+
|
|
85
|
+
{primaryButtonTitle ? (
|
|
86
|
+
<Button
|
|
87
|
+
mode="primary"
|
|
88
|
+
size="lg"
|
|
89
|
+
onClick={primaryButtonOnClick}
|
|
90
|
+
loader={primaryButtonLoader}
|
|
91
|
+
to={primaryButtonTo}
|
|
92
|
+
fullWidthOnMobiles
|
|
93
|
+
content={primaryButtonTitle}
|
|
94
|
+
setClickTrigger={primaryButtonSetClickTrigger}
|
|
95
|
+
handleError={callHandlingErrors}
|
|
96
|
+
/>
|
|
97
|
+
) : null}
|
|
98
|
+
</div>
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
DialogButtons.propTypes = {
|
|
104
|
+
primaryButtonTitle: PropTypes.string,
|
|
105
|
+
primaryButtonOnClick: PropTypes.func,
|
|
106
|
+
primaryButtonLoader: PropTypes.bool,
|
|
107
|
+
primaryButtonTo: PropTypes.string,
|
|
108
|
+
primaryButtonSetClickTrigger: PropTypes.func,
|
|
109
|
+
secondaryButtonTitle: PropTypes.string,
|
|
110
|
+
secondaryButtonOnClick: PropTypes.func,
|
|
111
|
+
secondaryButtonTo: PropTypes.string,
|
|
112
|
+
secondaryButtonBig: PropTypes.bool,
|
|
113
|
+
withBackgroundImage: PropTypes.bool,
|
|
114
|
+
hideOnMobiles: PropTypes.bool,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
DialogButtons.defaultProps = {
|
|
118
|
+
primaryButtonLoader: false,
|
|
119
|
+
secondaryButtonBig: false,
|
|
120
|
+
withBackgroundImage: false,
|
|
121
|
+
hideOnMobiles: false,
|
|
122
|
+
};
|