dce-reactkit 3.7.10 → 3.7.12

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.
@@ -1,520 +0,0 @@
1
- /* eslint-disable react/no-unused-prop-types */
2
- /* eslint-disable jsx-a11y/no-static-element-interactions */
3
- /* eslint-disable jsx-a11y/click-events-have-key-events */
4
- /* eslint-disable react/destructuring-assignment */
5
-
6
- /**
7
- * The displayable modal component (this is the modal that's added to the
8
- * wrapper, not the one that the programmer renders)
9
- * @author Gabe Abrams
10
- */
11
-
12
- // Import React
13
- import React, { useState, useEffect, useRef } from 'react';
14
-
15
- // Import other components
16
- import waitMs from '../../helpers/waitMs';
17
-
18
- // Import types
19
- import Variant from '../../types/Variant';
20
- import ModalButtonType from '../../types/ModalButtonType';
21
- import ModalSize from '../../types/ModalSize';
22
- import ModalType from '../../types/ModalType';
23
- import ModalProps from './ModalProps';
24
-
25
- // Import shared helpers
26
- // TODO: fix dependency cycle
27
- // eslint-disable-next-line import/no-cycle
28
- import { isDarkModeOn } from '../../client/initClient';
29
-
30
- /*------------------------------------------------------------------------*/
31
- /* ------------------------------ Constants ----------------------------- */
32
- /*------------------------------------------------------------------------*/
33
-
34
- // Base level of z-index
35
- const BASE_Z_INDEX = 1000000000;
36
- const BASE_Z_INDEX_ON_TOP = 2000000000;
37
-
38
- // Constants
39
- const MS_TO_ANIMATE = 200; // Animation duration
40
-
41
- // Modal type to list of buttons
42
- const modalTypeToModalButtonTypes: {
43
- [k: string]: ModalButtonType[]
44
- } = {
45
- [ModalType.Okay]: [
46
- ModalButtonType.Okay,
47
- ],
48
- [ModalType.OkayCancel]: [
49
- ModalButtonType.Okay,
50
- ModalButtonType.Cancel,
51
- ],
52
- [ModalType.YesNo]: [
53
- ModalButtonType.Yes,
54
- ModalButtonType.No,
55
- ],
56
- [ModalType.YesNoCancel]: [
57
- ModalButtonType.Yes,
58
- ModalButtonType.No,
59
- ModalButtonType.Cancel,
60
- ],
61
- [ModalType.AbandonGoBack]: [
62
- ModalButtonType.Abandon,
63
- ModalButtonType.GoBack,
64
- ],
65
- [ModalType.ImSureCancel]: [
66
- ModalButtonType.ImSure,
67
- ModalButtonType.Cancel,
68
- ],
69
- [ModalType.DeleteCancel]: [
70
- ModalButtonType.Delete,
71
- ModalButtonType.Cancel,
72
- ],
73
- [ModalType.ConfirmCancel]: [
74
- ModalButtonType.Confirm,
75
- ModalButtonType.Cancel,
76
- ],
77
- };
78
-
79
- /**
80
- * Get button type styling and labels
81
- * @author Gabe Abrams
82
- * @returns map of button type to label and variant
83
- */
84
- const getModalButtonTypeToLabelAndVariant = () => {
85
- const dark = isDarkModeOn();
86
- return {
87
- [ModalButtonType.Okay]: {
88
- label: 'Okay',
89
- variant: (
90
- dark
91
- ? Variant.Light
92
- : Variant.Dark
93
- ),
94
- },
95
- [ModalButtonType.Cancel]: {
96
- label: 'Cancel',
97
- variant: Variant.Secondary,
98
- },
99
- [ModalButtonType.Yes]: {
100
- label: 'Yes',
101
- variant: (
102
- dark
103
- ? Variant.Light
104
- : Variant.Dark
105
- ),
106
- },
107
- [ModalButtonType.No]: {
108
- label: 'No',
109
- variant: Variant.Secondary,
110
- },
111
- [ModalButtonType.Abandon]: {
112
- label: 'Abandon Changes',
113
- variant: Variant.Warning,
114
- },
115
- [ModalButtonType.GoBack]: {
116
- label: 'Go Back',
117
- variant: Variant.Secondary,
118
- },
119
- [ModalButtonType.Continue]: {
120
- label: 'Continue',
121
- variant: (
122
- dark
123
- ? Variant.Light
124
- : Variant.Dark
125
- ),
126
- },
127
- [ModalButtonType.ImSure]: {
128
- label: 'I am sure',
129
- variant: Variant.Warning,
130
- },
131
- [ModalButtonType.Delete]: {
132
- label: 'Yes, Delete',
133
- variant: Variant.Danger,
134
- },
135
- [ModalButtonType.Confirm]: {
136
- label: 'Confirm',
137
- variant: (
138
- dark
139
- ? Variant.Light
140
- : Variant.Dark
141
- ),
142
- },
143
- };
144
- };
145
-
146
- /*------------------------------------------------------------------------*/
147
- /* -------------------------------- Style ------------------------------- */
148
- /*------------------------------------------------------------------------*/
149
-
150
- const style = `
151
- .ModalForWrapper-backdrop {
152
- position: fixed;
153
- top: 0;
154
- left: 0;
155
- width: 100vw;
156
- height: 200vh;
157
- background-color: rgba(0, 0, 0, 0.7);
158
- }
159
- .ModalForWrapper-fading-in {
160
- animation-name: Modal-fading-in;
161
- animation-duration: ${Math.floor(MS_TO_ANIMATE * 2)}ms;
162
- animation-iteration-count: 1;
163
- animation-fill-mode: both;
164
- animation-timing-function: ease-out;
165
- }
166
- @keyframes Modal-fading-in {
167
- 0% {
168
- opacity: 0;
169
- }
170
- 100% {
171
- opacity: 1;
172
- }
173
- }
174
- .ModalForWrapper-animating-in {
175
- animation-name: Modal-animating-in;
176
- animation-duration: ${MS_TO_ANIMATE}ms;
177
- animation-iteration-count: 1;
178
- animation-fill-mode: both;
179
- animation-timing-function: ease-out;
180
- }
181
- @keyframes Modal-animating-in {
182
- 0% {
183
- transform: scale(1.05) translate(0, -1.5rem);
184
- opacity: 0;
185
- }
186
- 100% {
187
- transform: scale(1) translate(0, 0);
188
- opacity: 1;
189
- }
190
- }
191
- .ModalForWrapper-animating-pop {
192
- animation-name: ModalForWrapper-animating-pop;
193
- animation-duration: ${MS_TO_ANIMATE}ms;
194
- animation-iteration-count: 1;
195
- animation-fill-mode: both;
196
- animation-timing-function: ease-in-out;
197
- }
198
- @keyframes ModalForWrapper-animating-pop {
199
- 0% {
200
- transform: scale(1);
201
- opacity: 1;
202
- }
203
- 50% {
204
- transform: scale(1.05);
205
- opacity: 0.9;
206
- }
207
- 100% {
208
- transform: scale(1);
209
- opacity: 1;
210
- }
211
- }
212
- `;
213
-
214
- /*------------------------------------------------------------------------*/
215
- /* ------------------------------ Component ----------------------------- */
216
- /*------------------------------------------------------------------------*/
217
-
218
- const Modal: React.FC<ModalProps> = (props) => {
219
- /*------------------------------------------------------------------------*/
220
- /* -------------------------------- Setup ------------------------------- */
221
- /*------------------------------------------------------------------------*/
222
-
223
- /* -------------- Props ------------- */
224
-
225
- const {
226
- type = ModalType.NoButtons,
227
- size = ModalSize.Large,
228
- title,
229
- children,
230
- onClose,
231
- dontAllowBackdropExit,
232
- dontShowXButton,
233
- onTopOfOtherModals,
234
- } = props;
235
-
236
- /* -------------- State ------------- */
237
-
238
- // True if animation is in use
239
- const [animatingIn, setAnimatingIn] = useState(true);
240
- const [animatingPop, setAnimatingPop] = useState(false);
241
-
242
- // Keep track of whether modal is still mounted
243
- const mounted = useRef(false);
244
-
245
- /*------------------------------------------------------------------------*/
246
- /* ------------------------- Lifecycle Functions ------------------------ */
247
- /*------------------------------------------------------------------------*/
248
-
249
- /**
250
- * Mount
251
- * @author Gabe Abrams
252
- */
253
- useEffect(
254
- () => {
255
- (async () => {
256
- // Set defaults
257
- setAnimatingIn(true);
258
- setAnimatingPop(false);
259
- // Wait for animation
260
- await waitMs(MS_TO_ANIMATE);
261
- // Update to state after animated in
262
- if (mounted.current) {
263
- setAnimatingIn(false);
264
- }
265
- })();
266
-
267
- return () => {
268
- mounted.current = false;
269
- };
270
- },
271
- [],
272
- );
273
-
274
- /*------------------------------------------------------------------------*/
275
- /* ------------------------- Component Functions ------------------------ */
276
- /*------------------------------------------------------------------------*/
277
-
278
- /**
279
- * Handles the closing of the modal
280
- * @author Gabe Abrams
281
- * @param modalButtonType the button that was clicked when closing the
282
- * modal
283
- */
284
- const handleClose = async (modalButtonType: ModalButtonType) => {
285
- // Don't close if no handler
286
- if (!onClose) {
287
- return;
288
- }
289
-
290
- onClose(modalButtonType);
291
- };
292
-
293
- /*------------------------------------------------------------------------*/
294
- /* ------------------------------- Render ------------------------------- */
295
- /*------------------------------------------------------------------------*/
296
-
297
- // Calculate Z-index
298
- const baseZIndex = (
299
- onTopOfOtherModals
300
- ? BASE_Z_INDEX_ON_TOP
301
- : BASE_Z_INDEX
302
- );
303
-
304
- // Get list of buttons for this modal type
305
- const ModalButtonTypes: ModalButtonType[] = modalTypeToModalButtonTypes[type] ?? [];
306
-
307
- // Get map of button type to label and variant
308
- const ModalButtonTypeToLabelAndVariant = getModalButtonTypeToLabelAndVariant();
309
-
310
- // Create buttons
311
- const buttons = ModalButtonTypes.map((modalButtonType: ModalButtonType, i) => {
312
- // Get default style
313
- let {
314
- label,
315
- variant,
316
- } = ModalButtonTypeToLabelAndVariant[modalButtonType];
317
-
318
- // Override with customizations
319
- const newLabel = props[`${modalButtonType}Label`];
320
- if (newLabel) {
321
- label = newLabel;
322
- }
323
- const newVariant = props[`${modalButtonType}Variant`];
324
- if (newVariant) {
325
- variant = newVariant;
326
- }
327
-
328
- // Check if this button is last
329
- const last = (i === ModalButtonTypes.length - 1);
330
-
331
- // Create the button
332
- return (
333
- <button
334
- key={modalButtonType}
335
- type="button"
336
- className={`ModalForWrapper-${modalButtonType}-button btn btn-${variant} ${last ? '' : 'me-1'}`}
337
- onClick={() => {
338
- handleClose(modalButtonType);
339
- }}
340
- >
341
- {label}
342
- </button>
343
- );
344
- });
345
-
346
- // Put all buttons in a footer
347
- const footer = (
348
- (buttons && buttons.length)
349
- ? (
350
- <div>
351
- {buttons}
352
- </div>
353
- )
354
- : undefined
355
- );
356
-
357
- // Choose an animation
358
- let animationClass = '';
359
- let backdropAnimationClass = '';
360
- if (animatingIn) {
361
- animationClass = 'ModalForWrapper-animating-in';
362
- backdropAnimationClass = 'ModalForWrapper-fading-in';
363
- } else if (animatingPop) {
364
- animationClass = 'ModalForWrapper-animating-pop';
365
- }
366
-
367
- // Render the modal
368
- return (
369
- <div
370
- className="modal show"
371
- tabIndex={-1}
372
- style={{
373
- zIndex: baseZIndex,
374
- display: 'block',
375
- margin: 'auto',
376
- left: 0,
377
- right: 0,
378
- }}
379
- >
380
- <style>{style}</style>
381
- <div
382
- className={`ModalForWrapper-backdrop ${backdropAnimationClass}`}
383
- style={{
384
- zIndex: baseZIndex + 1,
385
- }}
386
- onClick={async () => {
387
- // Skip if exit via backdrop not allowed
388
- if (dontAllowBackdropExit || !onClose) {
389
- // Show pop animation
390
- if (!animatingPop) {
391
- setAnimatingPop(true);
392
-
393
- // Wait then stop pop animation
394
- await waitMs(MS_TO_ANIMATE);
395
- setAnimatingPop(false);
396
- }
397
- return;
398
- }
399
- // Handle close
400
- handleClose(ModalButtonType.Cancel);
401
- }}
402
- />
403
- <div
404
- className={`modal-dialog modal-${size} ${animationClass} modal-dialog-scrollable modal-dialog-centered`}
405
- style={{
406
- zIndex: baseZIndex + 2,
407
- }}
408
- >
409
- <div
410
- className="modal-content"
411
- style={{
412
- borderColor: (
413
- isDarkModeOn()
414
- ? 'gray'
415
- : undefined
416
- ),
417
- }}
418
- >
419
- <div
420
- className="modal-header"
421
- style={{
422
- color: (
423
- isDarkModeOn()
424
- ? 'white'
425
- : undefined
426
- ),
427
- backgroundColor: (
428
- isDarkModeOn()
429
- ? '#444'
430
- : undefined
431
- ),
432
- borderBottom: (
433
- isDarkModeOn()
434
- ? '0.1rem solid gray'
435
- : undefined
436
- ),
437
- }}
438
- >
439
- <h5
440
- className="modal-title"
441
- style={{
442
- fontWeight: 'bold',
443
- }}
444
- >
445
- {title}
446
- </h5>
447
-
448
- {(onClose && !dontShowXButton) && (
449
- <button
450
- type="button"
451
- className="ModalForWrapper-x-button btn-close"
452
- aria-label="Close"
453
- style={{
454
- backgroundColor: (
455
- isDarkModeOn()
456
- ? 'white'
457
- : undefined
458
- ),
459
- }}
460
- onClick={() => {
461
- // Handle close
462
- handleClose(ModalButtonType.Cancel);
463
- }}
464
- />
465
- )}
466
- </div>
467
- {children && (
468
- <div
469
- className="modal-body"
470
- style={{
471
- color: (
472
- isDarkModeOn()
473
- ? 'white'
474
- : undefined
475
- ),
476
- backgroundColor: (
477
- isDarkModeOn()
478
- ? '#444'
479
- : undefined
480
- ),
481
- }}
482
- >
483
- {children}
484
- </div>
485
- )}
486
- {footer && (
487
- <div
488
- className="modal-footer pt-1 pb-1"
489
- style={{
490
- color: (
491
- isDarkModeOn()
492
- ? 'white'
493
- : undefined
494
- ),
495
- backgroundColor: (
496
- isDarkModeOn()
497
- ? '#444'
498
- : undefined
499
- ),
500
- borderTop: (
501
- isDarkModeOn()
502
- ? '0.1rem solid gray'
503
- : undefined
504
- ),
505
- }}
506
- >
507
- {footer}
508
- </div>
509
- )}
510
- </div>
511
- </div>
512
- </div>
513
- );
514
- };
515
-
516
- /*------------------------------------------------------------------------*/
517
- /* ------------------------------- Wrap Up ------------------------------ */
518
- /*------------------------------------------------------------------------*/
519
-
520
- export default Modal;