@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.
Files changed (67) hide show
  1. package/.gitlab-ci.yml +29 -0
  2. package/.husky/commit-msg +8 -0
  3. package/.husky/pre-push +1 -0
  4. package/README.md +13 -4
  5. package/dist/index.cjs +1545 -148
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.css +23630 -0
  8. package/dist/index.css.map +1 -1
  9. package/dist/index.modern.js +1318 -103
  10. package/dist/index.modern.js.map +1 -1
  11. package/dist/index.module.js +1534 -149
  12. package/dist/index.module.js.map +1 -1
  13. package/dist/index.umd.js +1544 -152
  14. package/dist/index.umd.js.map +1 -1
  15. package/package.json +16 -3
  16. package/src/assets/image/icons/arrow-tosca.svg +3 -0
  17. package/src/assets/image/icons/arrow-white.svg +14 -0
  18. package/src/assets/image/icons/failed-validation-icon.svg +15 -0
  19. package/src/assets/image/icons/successful-validation-icon.svg +10 -0
  20. package/src/common/amountUtils.js +4 -2
  21. package/src/common/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +14 -0
  22. package/src/common/utils/cache.js +4 -4
  23. package/src/components/atoms/BackgroundTitle/BackgroundTitle.jsx +44 -0
  24. package/src/components/atoms/BackgroundTitle/background-title.module.scss +52 -0
  25. package/src/components/atoms/Validation/Validation.jsx +130 -0
  26. package/src/components/atoms/Validation/validation.module.scss +15 -0
  27. package/src/components/atoms/buttons/Close/Close.jsx +64 -0
  28. package/src/components/atoms/buttons/Close/close.module.scss +75 -0
  29. package/src/components/atoms/buttons/LinkButton/LinkButton.jsx +121 -0
  30. package/src/components/atoms/buttons/LinkButton/link-button.module.scss +45 -0
  31. package/src/components/organisms/Dialog/Dialog.jsx +515 -0
  32. package/src/components/organisms/Dialog/DialogButtons/DialogButtons.jsx +122 -0
  33. package/src/components/organisms/Dialog/DialogButtons/dialog-buttons.module.scss +25 -0
  34. package/src/components/organisms/Dialog/DialogStep/DialogStep.jsx +664 -0
  35. package/src/components/organisms/Dialog/DialogStep/dialog-step.module.scss +362 -0
  36. package/src/components/organisms/Dialog/dialog.module.scss +223 -0
  37. package/src/components/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +139 -0
  38. package/src/components/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +71 -0
  39. package/src/components/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +144 -0
  40. package/src/components/utils/inputValueProviders.js +58 -0
  41. package/src/constants/organisms/dialog/DialogStep/dialogStep.js +1 -0
  42. package/src/constants/organisms/dialog/dialog.js +29 -0
  43. package/src/index.js +11 -0
  44. package/src/robustExteranlApiCallerService/robustExternalAPICallerService.js +3 -1
  45. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +787 -0
  46. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +745 -0
  47. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +31 -0
  48. package/src/swaps-lib/external-apis/swapProvider.js +17 -4
  49. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +91 -30
  50. package/src/swaps-lib/models/baseSwapCreationInfo.js +4 -1
  51. package/src/swaps-lib/models/existingSwap.js +3 -0
  52. package/src/swaps-lib/models/existingSwapWithFiatData.js +4 -0
  53. package/src/swaps-lib/services/publicSwapService.js +32 -4
  54. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +506 -0
  55. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1311 -0
  56. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getAllSupportedCurrencies.test.js +76 -0
  57. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getDepositCurrencies.test.js +82 -0
  58. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1892 -0
  59. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getWithdrawalCurrencies.test.js +111 -0
  60. package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +88 -0
  61. package/stories/stubs/exampleContent.jsx +20 -0
  62. package/styles/fonts/NunitoSans-Bold.ttf +0 -0
  63. package/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
  64. package/styles/fonts/NunitoSans-Light.ttf +0 -0
  65. package/styles/fonts/NunitoSans-Regular.ttf +0 -0
  66. package/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
  67. 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
+ };