dce-reactkit 3.0.0-beta.4 → 3.0.0-beta.42
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/.eslintrc.js +95 -0
- package/README.md +1 -546
- package/dist/cjs/index.js +944 -85
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +25 -2
- package/dist/cjs/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/cjs/types/components/CheckboxButton.d.ts +20 -0
- package/dist/cjs/types/components/RadioButton.d.ts +20 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +22 -0
- package/dist/cjs/types/components/TabBox.d.ts +1 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/cjs/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/cjs/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/cjs/types/helpers/handleError.d.ts +18 -0
- package/dist/cjs/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/cjs/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/cjs/types/index.d.ts +13 -1
- package/dist/cjs/types/server/initServer.d.ts +21 -0
- package/dist/cjs/types/types/ParamType.d.ts +17 -0
- package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/esm/index.js +933 -86
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +25 -2
- package/dist/esm/types/components/ButtonInputGroup.d.ts +12 -0
- package/dist/esm/types/components/CheckboxButton.d.ts +20 -0
- package/dist/esm/types/components/RadioButton.d.ts +20 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +22 -0
- package/dist/esm/types/components/TabBox.d.ts +1 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +30 -0
- package/dist/esm/types/helpers/getOrdinal.d.ts +8 -0
- package/dist/esm/types/helpers/getTimeInfoInET.d.ts +17 -0
- package/dist/esm/types/helpers/handleError.d.ts +18 -0
- package/dist/esm/types/helpers/handleSuccess.d.ts +8 -0
- package/dist/esm/types/helpers/visitServerEndpoint.d.ts +23 -0
- package/dist/esm/types/index.d.ts +13 -1
- package/dist/esm/types/server/initServer.d.ts +21 -0
- package/dist/esm/types/types/ParamType.d.ts +17 -0
- package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/index.d.ts +229 -10
- package/package.json +13 -1
- package/rollup.config.js +0 -1
- package/src/components/AppWrapper.tsx +73 -15
- package/src/components/ButtonInputGroup.tsx +89 -0
- package/src/components/CheckboxButton.tsx +100 -0
- package/src/components/ErrorBox.tsx +4 -4
- package/src/components/LoadingSpinner.tsx +3 -3
- package/src/components/Modal.tsx +185 -71
- package/src/components/RadioButton.tsx +102 -0
- package/src/components/SimpleDateChooser.tsx +222 -0
- package/src/components/TabBox.tsx +65 -58
- package/src/helpers/genRouteHandler.ts +326 -0
- package/src/helpers/getOrdinal.tsx +13 -0
- package/src/helpers/getTimeInfoInET.tsx +56 -0
- package/src/helpers/handleError.ts +65 -0
- package/src/helpers/handleSuccess.ts +18 -0
- package/src/helpers/visitServerEndpoint.tsx +110 -0
- package/src/index.ts +27 -0
- package/src/server/initServer.ts +53 -0
- package/src/types/ParamType.ts +18 -0
- package/src/types/ReactKitErrorCode.tsx +3 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A checkbox button
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Import FontAwesome Icons
|
|
10
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
11
|
+
import { faCheckSquare } from '@fortawesome/free-solid-svg-icons';
|
|
12
|
+
import { faSquare } from '@fortawesome/free-regular-svg-icons';
|
|
13
|
+
|
|
14
|
+
// Import shared types
|
|
15
|
+
import Variant from '../types/Variant';
|
|
16
|
+
|
|
17
|
+
/*------------------------------------------------------------------------*/
|
|
18
|
+
/* Types */
|
|
19
|
+
/*------------------------------------------------------------------------*/
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
// Text of the button
|
|
23
|
+
text: string,
|
|
24
|
+
// Handler for when this item is toggled
|
|
25
|
+
onChanged: (checked: boolean) => void,
|
|
26
|
+
// Aria label
|
|
27
|
+
ariaLabel: string,
|
|
28
|
+
// Button title
|
|
29
|
+
title?: string,
|
|
30
|
+
// True if checked
|
|
31
|
+
checked?: boolean,
|
|
32
|
+
// The id of the button
|
|
33
|
+
id?: string,
|
|
34
|
+
// If true, no margin on right
|
|
35
|
+
noMarginOnRight?: boolean,
|
|
36
|
+
// Variant for when checkbox is checked
|
|
37
|
+
checkedVariant?: Variant,
|
|
38
|
+
// Variant for when checkbox is not checked
|
|
39
|
+
uncheckedVariant?: Variant,
|
|
40
|
+
// True if using a small button
|
|
41
|
+
small?: boolean,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/*------------------------------------------------------------------------*/
|
|
45
|
+
/* Component */
|
|
46
|
+
/*------------------------------------------------------------------------*/
|
|
47
|
+
|
|
48
|
+
const CheckboxButton: React.FC<Props> = (props) => {
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
/* Setup */
|
|
51
|
+
/*------------------------------------------------------------------------*/
|
|
52
|
+
|
|
53
|
+
/* -------------- Props ------------- */
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
text,
|
|
57
|
+
onChanged,
|
|
58
|
+
ariaLabel,
|
|
59
|
+
title,
|
|
60
|
+
checked,
|
|
61
|
+
id,
|
|
62
|
+
noMarginOnRight,
|
|
63
|
+
checkedVariant = Variant.Secondary,
|
|
64
|
+
uncheckedVariant = Variant.Light,
|
|
65
|
+
small,
|
|
66
|
+
} = props;
|
|
67
|
+
|
|
68
|
+
/*------------------------------------------------------------------------*/
|
|
69
|
+
/* Render */
|
|
70
|
+
/*------------------------------------------------------------------------*/
|
|
71
|
+
|
|
72
|
+
/*----------------------------------------*/
|
|
73
|
+
/* Main UI */
|
|
74
|
+
/*----------------------------------------*/
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
id={id}
|
|
80
|
+
title={title}
|
|
81
|
+
className={`btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`}
|
|
82
|
+
aria-label={`${ariaLabel}${checked ? ': currently checked' : ''}`}
|
|
83
|
+
onClick={() => {
|
|
84
|
+
onChanged(!checked);
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<FontAwesomeIcon
|
|
88
|
+
icon={checked ? faCheckSquare : faSquare}
|
|
89
|
+
className="me-1"
|
|
90
|
+
/>
|
|
91
|
+
{text}
|
|
92
|
+
</button>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/*------------------------------------------------------------------------*/
|
|
97
|
+
/* Wrap up */
|
|
98
|
+
/*------------------------------------------------------------------------*/
|
|
99
|
+
|
|
100
|
+
export default CheckboxButton;
|
|
@@ -63,9 +63,9 @@ const ErrorBox: React.FC<Props> = (props) => {
|
|
|
63
63
|
<span
|
|
64
64
|
style={{
|
|
65
65
|
backgroundColor: 'white',
|
|
66
|
-
borderRadius: '
|
|
67
|
-
paddingLeft: '
|
|
68
|
-
paddingRight: '
|
|
66
|
+
borderRadius: '0.3rem',
|
|
67
|
+
paddingLeft: '0.2rem',
|
|
68
|
+
paddingRight: '0.2rem',
|
|
69
69
|
color: '#DC4150',
|
|
70
70
|
fontVariant: 'small-caps',
|
|
71
71
|
fontSize: '80%',
|
|
@@ -85,7 +85,7 @@ const ErrorBox: React.FC<Props> = (props) => {
|
|
|
85
85
|
<div
|
|
86
86
|
className="alert alert-danger text-center"
|
|
87
87
|
style={{
|
|
88
|
-
maxWidth: '
|
|
88
|
+
maxWidth: '40rem',
|
|
89
89
|
margin: 'auto',
|
|
90
90
|
}}
|
|
91
91
|
>
|
|
@@ -20,10 +20,10 @@ const style = `
|
|
|
20
20
|
.LoadingSpinner-blip-2,
|
|
21
21
|
.LoadingSpinner-blip-3,
|
|
22
22
|
.LoadingSpinner-blip-4 {
|
|
23
|
-
font-size:
|
|
23
|
+
font-size: 1.8rem;
|
|
24
24
|
opacity: 0.6;
|
|
25
|
-
margin-top:
|
|
26
|
-
margin-bottom:
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
margin-bottom: 1rem;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/* First Blip */
|
package/src/components/Modal.tsx
CHANGED
|
@@ -15,69 +15,12 @@ import ModalButtonType from '../types/ModalButtonType';
|
|
|
15
15
|
import ModalSize from '../types/ModalSize';
|
|
16
16
|
import ModalType from '../types/ModalType';
|
|
17
17
|
|
|
18
|
-
/*------------------------------------------------------------------------*/
|
|
19
|
-
/* Style */
|
|
20
|
-
/*------------------------------------------------------------------------*/
|
|
21
|
-
|
|
22
|
-
const style = `
|
|
23
|
-
/* More opaque backdrop */
|
|
24
|
-
.Modal-backdrop {
|
|
25
|
-
background-color: black !important;
|
|
26
|
-
opacity: 0.8 !important;
|
|
27
|
-
z-index: 10000000 !important;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/* Customize the modal header and body */
|
|
31
|
-
.modal-header {
|
|
32
|
-
/* Dark theme */
|
|
33
|
-
background-color: #222 !important;
|
|
34
|
-
color: white !important;
|
|
35
|
-
|
|
36
|
-
/* Border */
|
|
37
|
-
border: 1px solid #545454 !important;
|
|
38
|
-
}
|
|
39
|
-
.modal-body {
|
|
40
|
-
/* Dark theme */
|
|
41
|
-
background-color: #222 !important;
|
|
42
|
-
color: white !important;
|
|
43
|
-
|
|
44
|
-
/* Border */
|
|
45
|
-
border-top: 0 !important;
|
|
46
|
-
border-left: 1px solid #545454 !important;
|
|
47
|
-
border-bottom: 1px solid #545454 !important;
|
|
48
|
-
border-right: 1px solid #545454 !important;
|
|
49
|
-
border-bottom-left-radius: 3px;
|
|
50
|
-
border-bottom-right-radius: 3px;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/* Create our own animation */
|
|
54
|
-
.modal-content {
|
|
55
|
-
animation-name: Modal-drop-in;
|
|
56
|
-
animation-duration: 0.4s;
|
|
57
|
-
animation-iteration-count: 1;
|
|
58
|
-
animation-fill-mode: forwards;
|
|
59
|
-
animation-timing-function: ease-out;
|
|
60
|
-
}
|
|
61
|
-
@keyframes Modal-drop-in {
|
|
62
|
-
0% {
|
|
63
|
-
transform: scale(0.8);
|
|
64
|
-
opacity: 0.5;
|
|
65
|
-
}
|
|
66
|
-
100% {
|
|
67
|
-
transform: scale(1);
|
|
68
|
-
opacity: 1;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
`;
|
|
72
|
-
|
|
73
18
|
/*------------------------------------------------------------------------*/
|
|
74
19
|
/* Constants */
|
|
75
20
|
/*------------------------------------------------------------------------*/
|
|
76
21
|
|
|
77
22
|
// Constants
|
|
78
|
-
const MS_TO_ANIMATE =
|
|
79
|
-
const MS_ANIMATE_IN_DELAY = 10;
|
|
80
|
-
// Time to wait before animating in (must be >0 or animation won't trigger)
|
|
23
|
+
const MS_TO_ANIMATE = 200; // Animation duration
|
|
81
24
|
|
|
82
25
|
// Modal type to list of buttons
|
|
83
26
|
const modalTypeToModalButtonTypes: {
|
|
@@ -161,6 +104,116 @@ const ModalButtonTypeToLabelAndVariant = {
|
|
|
161
104
|
},
|
|
162
105
|
};
|
|
163
106
|
|
|
107
|
+
/*------------------------------------------------------------------------*/
|
|
108
|
+
/* Style */
|
|
109
|
+
/*------------------------------------------------------------------------*/
|
|
110
|
+
|
|
111
|
+
const style = `
|
|
112
|
+
.Modal-backdrop {
|
|
113
|
+
position: fixed;
|
|
114
|
+
top: 0;
|
|
115
|
+
left: 0;
|
|
116
|
+
width: 100vw;
|
|
117
|
+
height: 100vw;
|
|
118
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.Modal-fading-in {
|
|
122
|
+
animation-name: Modal-fading-in;
|
|
123
|
+
animation-duration: ${Math.floor(MS_TO_ANIMATE * 2)}ms;
|
|
124
|
+
animation-iteration-count: 1;
|
|
125
|
+
animation-fill-mode: both;
|
|
126
|
+
animation-timing-function: ease-out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@keyframes Modal-fading-in {
|
|
130
|
+
0% {
|
|
131
|
+
opacity: 0;
|
|
132
|
+
}
|
|
133
|
+
100% {
|
|
134
|
+
opacity: 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.Modal-fading-out {
|
|
139
|
+
animation-name: Modal-fading-out;
|
|
140
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
141
|
+
animation-iteration-count: 1;
|
|
142
|
+
animation-fill-mode: both;
|
|
143
|
+
animation-timing-function: ease-in;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@keyframes Modal-fading-out {
|
|
147
|
+
0% {
|
|
148
|
+
opacity: 1;
|
|
149
|
+
}
|
|
150
|
+
100% {
|
|
151
|
+
opacity: 0;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.Modal-animating-in {
|
|
156
|
+
animation-name: Modal-animating-in;
|
|
157
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
158
|
+
animation-iteration-count: 1;
|
|
159
|
+
animation-fill-mode: both;
|
|
160
|
+
animation-timing-function: ease-out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@keyframes Modal-animating-in {
|
|
164
|
+
0% {
|
|
165
|
+
transform: scale(1.05) translate(0, -1.5rem);
|
|
166
|
+
opacity: 0;
|
|
167
|
+
}
|
|
168
|
+
100% {
|
|
169
|
+
transform: scale(1) translate(0, 0);
|
|
170
|
+
opacity: 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.Modal-animating-pop {
|
|
175
|
+
animation-name: Modal-animating-pop;
|
|
176
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
177
|
+
animation-iteration-count: 1;
|
|
178
|
+
animation-fill-mode: both;
|
|
179
|
+
animation-timing-function: ease-in-out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@keyframes Modal-animating-pop {
|
|
183
|
+
0% {
|
|
184
|
+
transform: scale(1);
|
|
185
|
+
opacity: 1;
|
|
186
|
+
}
|
|
187
|
+
50% {
|
|
188
|
+
transform: scale(1.05);
|
|
189
|
+
opacity: 0.9;
|
|
190
|
+
}
|
|
191
|
+
100% {
|
|
192
|
+
transform: scale(1);
|
|
193
|
+
opacity: 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.Modal-animating-out {
|
|
198
|
+
animation-name: Modal-animating-out;
|
|
199
|
+
animation-duration: ${MS_TO_ANIMATE}ms;
|
|
200
|
+
animation-iteration-count: 1;
|
|
201
|
+
animation-fill-mode: both;
|
|
202
|
+
animation-timing-function: ease-in;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@keyframes Modal-animating-out {
|
|
206
|
+
0% {
|
|
207
|
+
transform: scale(1) translate(0, 0);
|
|
208
|
+
opacity: 1;
|
|
209
|
+
}
|
|
210
|
+
100% {
|
|
211
|
+
transform: scale(1.05) translate(0, -1.5rem);
|
|
212
|
+
opacity: 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
216
|
+
|
|
164
217
|
/*------------------------------------------------------------------------*/
|
|
165
218
|
/* Types */
|
|
166
219
|
/*------------------------------------------------------------------------*/
|
|
@@ -245,11 +298,17 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
245
298
|
|
|
246
299
|
/* -------------- State ------------- */
|
|
247
300
|
|
|
301
|
+
// If true, the modal is completely gone
|
|
302
|
+
// (not just invisible, also removed from the dom)
|
|
303
|
+
const [gone, setGone] = useState(false);
|
|
304
|
+
|
|
248
305
|
// If true, the modal is shown
|
|
249
306
|
const [visible, setVisible] = useState(false);
|
|
250
307
|
|
|
251
|
-
// True if
|
|
252
|
-
const [animatingIn, setAnimatingIn] = useState(
|
|
308
|
+
// True if animation is in use
|
|
309
|
+
const [animatingIn, setAnimatingIn] = useState(true);
|
|
310
|
+
const [animatingPop, setAnimatingPop] = useState(false);
|
|
311
|
+
const [animatingOut, setAnimatingOut] = useState(false);
|
|
253
312
|
|
|
254
313
|
/*------------------------------------------------------------------------*/
|
|
255
314
|
/* Lifecycle Functions */
|
|
@@ -262,13 +321,10 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
262
321
|
useEffect(
|
|
263
322
|
() => {
|
|
264
323
|
(async () => {
|
|
265
|
-
// Start the component animating in
|
|
266
|
-
await waitMs(MS_ANIMATE_IN_DELAY);
|
|
267
|
-
setAnimatingIn(true);
|
|
268
|
-
|
|
269
324
|
// Wait and then set visible to true
|
|
270
325
|
await waitMs(MS_TO_ANIMATE);
|
|
271
326
|
setVisible(true);
|
|
327
|
+
setAnimatingIn(false);
|
|
272
328
|
})();
|
|
273
329
|
},
|
|
274
330
|
[],
|
|
@@ -302,16 +358,23 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
302
358
|
|
|
303
359
|
// Update the state
|
|
304
360
|
setVisible(false);
|
|
361
|
+
setAnimatingOut(true);
|
|
305
362
|
|
|
306
363
|
// Call the handler after the modal has animated out
|
|
307
364
|
await waitMs(MS_TO_ANIMATE);
|
|
308
365
|
onClose(ModalButtonType);
|
|
366
|
+
setGone(true);
|
|
309
367
|
};
|
|
310
368
|
|
|
311
369
|
/*------------------------------------------------------------------------*/
|
|
312
370
|
/* Render */
|
|
313
371
|
/*------------------------------------------------------------------------*/
|
|
314
372
|
|
|
373
|
+
// Return nothing if gone
|
|
374
|
+
if (gone) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
|
|
315
378
|
/*----------------------------------------*/
|
|
316
379
|
/* Footer */
|
|
317
380
|
/*----------------------------------------*/
|
|
@@ -343,8 +406,9 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
343
406
|
// Create the button
|
|
344
407
|
return (
|
|
345
408
|
<button
|
|
409
|
+
key={ModalButtonType}
|
|
346
410
|
type="button"
|
|
347
|
-
className={`Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : '
|
|
411
|
+
className={`Modal-${ModalButtonType}-button btn btn-${variant} ${last ? '' : 'me-1'}`}
|
|
348
412
|
onClick={() => {
|
|
349
413
|
handleClose(ModalButtonType);
|
|
350
414
|
}}
|
|
@@ -365,23 +429,73 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
365
429
|
: undefined
|
|
366
430
|
);
|
|
367
431
|
|
|
432
|
+
// Choose an animation
|
|
433
|
+
let animationClass = '';
|
|
434
|
+
let backdropAnimationClass = '';
|
|
435
|
+
if (animatingIn) {
|
|
436
|
+
animationClass = 'Modal-animating-in';
|
|
437
|
+
backdropAnimationClass = 'Modal-fading-in';
|
|
438
|
+
} else if (animatingOut) {
|
|
439
|
+
animationClass = 'Modal-animating-out';
|
|
440
|
+
backdropAnimationClass = 'Modal-fading-out';
|
|
441
|
+
} else if (animatingPop) {
|
|
442
|
+
animationClass = 'Modal-animating-pop';
|
|
443
|
+
}
|
|
444
|
+
|
|
368
445
|
// Render the modal
|
|
369
446
|
return (
|
|
370
447
|
<div
|
|
371
|
-
className={`modal
|
|
448
|
+
className={`modal show modal-dialog-scrollable modal-dialog-centered modal-${size}`}
|
|
372
449
|
tabIndex={-1}
|
|
373
450
|
style={{
|
|
374
451
|
zIndex: (
|
|
375
452
|
onTopOfOtherModals
|
|
376
|
-
?
|
|
453
|
+
? 5000000001
|
|
377
454
|
: 5000000000
|
|
378
455
|
),
|
|
456
|
+
display: 'block',
|
|
457
|
+
margin: 'auto',
|
|
458
|
+
left: 0,
|
|
459
|
+
right: 0,
|
|
379
460
|
}}
|
|
380
461
|
>
|
|
381
|
-
<
|
|
462
|
+
<style>{style}</style>
|
|
463
|
+
<div
|
|
464
|
+
className={`Modal-backdrop ${backdropAnimationClass}`}
|
|
465
|
+
style={{
|
|
466
|
+
zIndex: 5000000003,
|
|
467
|
+
}}
|
|
468
|
+
onClick={async () => {
|
|
469
|
+
// Skip if exit via backdrop not allowed
|
|
470
|
+
if (dontAllowBackdropExit || !onClose) {
|
|
471
|
+
// Show pop animation
|
|
472
|
+
if (!animatingPop) {
|
|
473
|
+
setAnimatingPop(true);
|
|
474
|
+
|
|
475
|
+
// Wait then stop pop animation
|
|
476
|
+
await waitMs(MS_TO_ANIMATE);
|
|
477
|
+
setAnimatingPop(false);
|
|
478
|
+
}
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
// Handle close
|
|
482
|
+
handleClose(ModalButtonType.Cancel);
|
|
483
|
+
}}
|
|
484
|
+
/>
|
|
485
|
+
<div
|
|
486
|
+
className={`modal-dialog ${animationClass}`}
|
|
487
|
+
style={{
|
|
488
|
+
zIndex: 5000000002,
|
|
489
|
+
}}
|
|
490
|
+
>
|
|
382
491
|
<div className="modal-content">
|
|
383
492
|
<div className="modal-header">
|
|
384
|
-
<h5
|
|
493
|
+
<h5
|
|
494
|
+
className="modal-title"
|
|
495
|
+
style={{
|
|
496
|
+
fontWeight: 'bold',
|
|
497
|
+
}}
|
|
498
|
+
>
|
|
385
499
|
{title}
|
|
386
500
|
</h5>
|
|
387
501
|
|
|
@@ -389,9 +503,9 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
389
503
|
<button
|
|
390
504
|
type="button"
|
|
391
505
|
className="btn-close"
|
|
392
|
-
data-bs-dismiss="modal"
|
|
393
506
|
aria-label="Close"
|
|
394
507
|
onClick={() => {
|
|
508
|
+
// Handle close
|
|
395
509
|
handleClose(ModalButtonType.Cancel);
|
|
396
510
|
}}
|
|
397
511
|
/>
|
|
@@ -403,7 +517,7 @@ const Modal: React.FC<Props> = (props) => {
|
|
|
403
517
|
</div>
|
|
404
518
|
)}
|
|
405
519
|
{footer && (
|
|
406
|
-
<div className="modal-footer">
|
|
520
|
+
<div className="modal-footer pt-1 pb-1">
|
|
407
521
|
{footer}
|
|
408
522
|
</div>
|
|
409
523
|
)}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A radio selection button
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Import FontAwesome Icons
|
|
10
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
11
|
+
import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
|
|
12
|
+
import { faCircle } from '@fortawesome/free-regular-svg-icons';
|
|
13
|
+
|
|
14
|
+
// Import shared types
|
|
15
|
+
import Variant from '../types/Variant';
|
|
16
|
+
|
|
17
|
+
/*------------------------------------------------------------------------*/
|
|
18
|
+
/* Types */
|
|
19
|
+
/*------------------------------------------------------------------------*/
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
// Text of the button
|
|
23
|
+
text: string,
|
|
24
|
+
// Handler for when this item is selected (not called if already selected)
|
|
25
|
+
onSelected: () => void,
|
|
26
|
+
// Aria label
|
|
27
|
+
ariaLabel: string,
|
|
28
|
+
// Button title
|
|
29
|
+
title?: string,
|
|
30
|
+
// True if selected
|
|
31
|
+
selected?: boolean,
|
|
32
|
+
// The id of the button
|
|
33
|
+
id?: string,
|
|
34
|
+
// If true, no margin on right
|
|
35
|
+
noMarginOnRight?: boolean,
|
|
36
|
+
// Variant for when radio is selected
|
|
37
|
+
selectedVariant?: Variant,
|
|
38
|
+
// Variant for when radio is not selected
|
|
39
|
+
unselectedVariant?: Variant,
|
|
40
|
+
// True if using a small button
|
|
41
|
+
small?: boolean,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/*------------------------------------------------------------------------*/
|
|
45
|
+
/* Component */
|
|
46
|
+
/*------------------------------------------------------------------------*/
|
|
47
|
+
|
|
48
|
+
const RadioButton: React.FC<Props> = (props) => {
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
/* Setup */
|
|
51
|
+
/*------------------------------------------------------------------------*/
|
|
52
|
+
|
|
53
|
+
/* -------------- Props ------------- */
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
text,
|
|
57
|
+
onSelected,
|
|
58
|
+
ariaLabel,
|
|
59
|
+
title,
|
|
60
|
+
selected,
|
|
61
|
+
id,
|
|
62
|
+
noMarginOnRight,
|
|
63
|
+
selectedVariant = Variant.Secondary,
|
|
64
|
+
unselectedVariant = Variant.Light,
|
|
65
|
+
small,
|
|
66
|
+
} = props;
|
|
67
|
+
|
|
68
|
+
/*------------------------------------------------------------------------*/
|
|
69
|
+
/* Render */
|
|
70
|
+
/*------------------------------------------------------------------------*/
|
|
71
|
+
|
|
72
|
+
/*----------------------------------------*/
|
|
73
|
+
/* Main UI */
|
|
74
|
+
/*----------------------------------------*/
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
id={id}
|
|
80
|
+
title={title}
|
|
81
|
+
className={`btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`}
|
|
82
|
+
aria-label={`${ariaLabel}${selected ? ': currently selected' : ''}`}
|
|
83
|
+
onClick={() => {
|
|
84
|
+
if (!selected) {
|
|
85
|
+
onSelected();
|
|
86
|
+
}
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<FontAwesomeIcon
|
|
90
|
+
icon={selected ? faDotCircle : faCircle}
|
|
91
|
+
className="me-1"
|
|
92
|
+
/>
|
|
93
|
+
{text}
|
|
94
|
+
</button>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/*------------------------------------------------------------------------*/
|
|
99
|
+
/* Wrap up */
|
|
100
|
+
/*------------------------------------------------------------------------*/
|
|
101
|
+
|
|
102
|
+
export default RadioButton;
|