dce-reactkit 3.0.0-beta.9 → 3.0.2
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/README.md +1 -546
- package/dist/cjs/index.js +945 -83
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +25 -0
- 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 +32 -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 +934 -84
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +25 -0
- 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 +32 -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 +231 -10
- package/package.json +3 -1
- package/rollup.config.js +0 -1
- package/src/components/AppWrapper.tsx +73 -11
- 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 +184 -23
- 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 +329 -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
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,29 @@ import React from 'react';
|
|
|
7
7
|
* @author Gabe Abrams
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
declare type Props$
|
|
10
|
+
declare type Props$7 = {
|
|
11
11
|
children: React.ReactNode;
|
|
12
|
+
sendRequest: SendRequestFunction;
|
|
12
13
|
dark?: boolean;
|
|
13
14
|
sessionExpiredMessage?: string;
|
|
14
15
|
};
|
|
16
|
+
declare type SendRequestFunction = (opts: {
|
|
17
|
+
path: string;
|
|
18
|
+
method: ('GET' | 'POST' | 'DELETE' | 'PUT');
|
|
19
|
+
params?: {
|
|
20
|
+
[x: string]: any;
|
|
21
|
+
} | undefined;
|
|
22
|
+
headers?: {
|
|
23
|
+
[x: string]: any;
|
|
24
|
+
} | undefined;
|
|
25
|
+
numRetries?: number | undefined;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
body: any;
|
|
28
|
+
status: number;
|
|
29
|
+
headers: {
|
|
30
|
+
[x: string]: any;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
15
33
|
/**
|
|
16
34
|
* Show an alert modal with an "Okay" button
|
|
17
35
|
* @author Gabe Abrams
|
|
@@ -34,7 +52,7 @@ declare const confirm: (title: string, text: string) => Promise<boolean>;
|
|
|
34
52
|
* @param [errorTitle] title of the error box
|
|
35
53
|
*/
|
|
36
54
|
declare const showFatalError: (error: any, errorTitle?: string) => undefined;
|
|
37
|
-
declare const AppWrapper: React.FC<Props$
|
|
55
|
+
declare const AppWrapper: React.FC<Props$7>;
|
|
38
56
|
|
|
39
57
|
/**
|
|
40
58
|
* Loading spinner/indicator
|
|
@@ -47,12 +65,12 @@ declare const LoadingSpinner: () => JSX.Element;
|
|
|
47
65
|
* @author Gabe Abrams
|
|
48
66
|
*/
|
|
49
67
|
|
|
50
|
-
declare type Props$
|
|
68
|
+
declare type Props$6 = {
|
|
51
69
|
error: any;
|
|
52
70
|
title?: string;
|
|
53
71
|
onClose?: () => void;
|
|
54
72
|
};
|
|
55
|
-
declare const ErrorBox: React.FC<Props$
|
|
73
|
+
declare const ErrorBox: React.FC<Props$6>;
|
|
56
74
|
|
|
57
75
|
/**
|
|
58
76
|
* Bootstrap variants
|
|
@@ -118,7 +136,7 @@ declare enum ModalType {
|
|
|
118
136
|
* @author Gabe Abrams
|
|
119
137
|
*/
|
|
120
138
|
|
|
121
|
-
declare type Props$
|
|
139
|
+
declare type Props$5 = {
|
|
122
140
|
type?: ModalType;
|
|
123
141
|
size?: ModalSize;
|
|
124
142
|
title?: React.ReactNode;
|
|
@@ -147,18 +165,91 @@ declare type Props$1 = {
|
|
|
147
165
|
confirmVariant?: Variant;
|
|
148
166
|
onTopOfOtherModals?: boolean;
|
|
149
167
|
};
|
|
150
|
-
declare const Modal: React.FC<Props$
|
|
168
|
+
declare const Modal: React.FC<Props$5>;
|
|
151
169
|
|
|
152
170
|
/**
|
|
153
171
|
* A box with a tab on the top that holds buttons and other content
|
|
154
172
|
* @author Gabe Abrams
|
|
155
173
|
*/
|
|
156
174
|
|
|
157
|
-
declare type Props = {
|
|
175
|
+
declare type Props$4 = {
|
|
158
176
|
title: React.ReactNode;
|
|
159
177
|
children: React.ReactNode;
|
|
178
|
+
noBottomPadding?: boolean;
|
|
179
|
+
};
|
|
180
|
+
declare const TabBox: React.FC<Props$4>;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A radio selection button
|
|
184
|
+
* @author Gabe Abrams
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
declare type Props$3 = {
|
|
188
|
+
text: string;
|
|
189
|
+
onSelected: () => void;
|
|
190
|
+
ariaLabel: string;
|
|
191
|
+
title?: string;
|
|
192
|
+
selected?: boolean;
|
|
193
|
+
id?: string;
|
|
194
|
+
noMarginOnRight?: boolean;
|
|
195
|
+
selectedVariant?: Variant;
|
|
196
|
+
unselectedVariant?: Variant;
|
|
197
|
+
small?: boolean;
|
|
198
|
+
};
|
|
199
|
+
declare const RadioButton: React.FC<Props$3>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A checkbox button
|
|
203
|
+
* @author Gabe Abrams
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
declare type Props$2 = {
|
|
207
|
+
text: string;
|
|
208
|
+
onChanged: (checked: boolean) => void;
|
|
209
|
+
ariaLabel: string;
|
|
210
|
+
title?: string;
|
|
211
|
+
checked?: boolean;
|
|
212
|
+
id?: string;
|
|
213
|
+
noMarginOnRight?: boolean;
|
|
214
|
+
checkedVariant?: Variant;
|
|
215
|
+
uncheckedVariant?: Variant;
|
|
216
|
+
small?: boolean;
|
|
217
|
+
};
|
|
218
|
+
declare const CheckboxButton: React.FC<Props$2>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Input group with a title and space for buttons
|
|
222
|
+
* @author Gabe Abrams
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
declare type Props$1 = {
|
|
226
|
+
label: string;
|
|
227
|
+
minLabelWidth?: string;
|
|
228
|
+
children: React.ReactNode;
|
|
229
|
+
};
|
|
230
|
+
declare const ButtonInputGroup: React.FC<Props$1>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* A very simple, lightweight date chooser
|
|
234
|
+
* @author Gabe Abrams
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
declare type Props = {
|
|
238
|
+
ariaLabel: string;
|
|
239
|
+
name: string;
|
|
240
|
+
month: number;
|
|
241
|
+
day: number;
|
|
242
|
+
year: number;
|
|
243
|
+
/**
|
|
244
|
+
* Handler for when date changes
|
|
245
|
+
* @param month new 1-indexed month number
|
|
246
|
+
* @param day new 1-indexed day number
|
|
247
|
+
* @param year new full year number
|
|
248
|
+
*/
|
|
249
|
+
onChange: (month: number, day: number, year: number) => void;
|
|
250
|
+
numMonthsToShow?: number;
|
|
160
251
|
};
|
|
161
|
-
declare const
|
|
252
|
+
declare const SimpleDateChooser: React.FC<Props>;
|
|
162
253
|
|
|
163
254
|
/**
|
|
164
255
|
* An error with a code
|
|
@@ -258,6 +349,134 @@ declare const sum: (nums: number[]) => number;
|
|
|
258
349
|
*/
|
|
259
350
|
declare const waitMs: (ms?: number) => Promise<unknown>;
|
|
260
351
|
|
|
352
|
+
/**
|
|
353
|
+
* Visit an endpoint on the server [for client only]
|
|
354
|
+
* @author Gabe Abrams
|
|
355
|
+
* @param opts object containing all arguments
|
|
356
|
+
* @param opts.path - the path of the server endpoint
|
|
357
|
+
* @param [opts.method=GET] - the method of the endpoint
|
|
358
|
+
* @param [opts.params] - query/body parameters to include
|
|
359
|
+
* @returns response from server
|
|
360
|
+
*/
|
|
361
|
+
declare const visitServerEndpoint: (opts: {
|
|
362
|
+
path: string;
|
|
363
|
+
method?: "GET" | "POST" | "DELETE" | "PUT" | undefined;
|
|
364
|
+
params?: {
|
|
365
|
+
[x: string]: any;
|
|
366
|
+
} | undefined;
|
|
367
|
+
}) => Promise<any>;
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Server-side API param types
|
|
371
|
+
* @author Gabe Abrams
|
|
372
|
+
*/
|
|
373
|
+
declare enum ParamType {
|
|
374
|
+
Boolean = "boolean",
|
|
375
|
+
BooleanOptional = "boolean-optional",
|
|
376
|
+
Float = "float",
|
|
377
|
+
FloatOptional = "float-optional",
|
|
378
|
+
Int = "int",
|
|
379
|
+
IntOptional = "int-optional",
|
|
380
|
+
JSON = "json",
|
|
381
|
+
JSONOptional = "json-optional",
|
|
382
|
+
String = "string",
|
|
383
|
+
StringOptional = "string-optional"
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Handle an error and respond to the client
|
|
388
|
+
* @author Gabe Abrams
|
|
389
|
+
* @param res express response
|
|
390
|
+
* @param error error info
|
|
391
|
+
* @param opts.err the error to send to the client
|
|
392
|
+
* or the error message
|
|
393
|
+
* @param [opts.code] an error code (only used if err.code is not
|
|
394
|
+
* included)
|
|
395
|
+
* @param [opts.status=500] the https status code to use
|
|
396
|
+
* defined)
|
|
397
|
+
*/
|
|
398
|
+
declare const handleError: (res: any, error: ({
|
|
399
|
+
message: any;
|
|
400
|
+
code?: string;
|
|
401
|
+
status?: number;
|
|
402
|
+
} | Error | string | any)) => undefined;
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Send successful API response
|
|
406
|
+
* @author Gabe Abrams
|
|
407
|
+
* @param res express response
|
|
408
|
+
* @param body the body of the response to send to the client
|
|
409
|
+
*/
|
|
410
|
+
declare const handleSuccess: (res: any, body: any) => undefined;
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Generate an express API route handler
|
|
414
|
+
* @author Gabe Abrams
|
|
415
|
+
* @param opts object containing all arguments
|
|
416
|
+
* @param opts.paramTypes map containing the types for each parameter that is
|
|
417
|
+
* included in the request (map: param name => type)
|
|
418
|
+
* @param opts.handler function that processes the request
|
|
419
|
+
* @returns express route handler that takes the following arguments:
|
|
420
|
+
* params (map: param name => value), handleSuccess (function for handling
|
|
421
|
+
* successful requests), handleError (function for handling failed requests),
|
|
422
|
+
* req (express request object), res (express response object),
|
|
423
|
+
* next (express next function)
|
|
424
|
+
*/
|
|
425
|
+
declare const genRouteHandler: (opts: {
|
|
426
|
+
paramTypes?: {
|
|
427
|
+
[k: string]: ParamType;
|
|
428
|
+
} | undefined;
|
|
429
|
+
handler: (opts: {
|
|
430
|
+
params: {
|
|
431
|
+
[k: string]: any;
|
|
432
|
+
};
|
|
433
|
+
handleSuccess: (body: any) => void;
|
|
434
|
+
handleError: (error: any) => void;
|
|
435
|
+
req: any;
|
|
436
|
+
res: any;
|
|
437
|
+
next: () => void;
|
|
438
|
+
}) => void;
|
|
439
|
+
}) => (req: any, res: any, next: () => void) => Promise<undefined>;
|
|
440
|
+
|
|
441
|
+
declare type GetLaunchInfoFunction = (req: any) => {
|
|
442
|
+
launched: boolean;
|
|
443
|
+
launchInfo?: any;
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* Prepare dce-reactkit to run on the server
|
|
447
|
+
* @author Gabe Abrams
|
|
448
|
+
* @param opts object containing all arguments
|
|
449
|
+
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
450
|
+
*/
|
|
451
|
+
declare const initServer: (opts: {
|
|
452
|
+
getLaunchInfo: GetLaunchInfoFunction;
|
|
453
|
+
}) => void;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Get a number's ordinal
|
|
457
|
+
* @author Gabe Abrams
|
|
458
|
+
* @param num the number being analyzed
|
|
459
|
+
* @returns ordinal
|
|
460
|
+
*/
|
|
461
|
+
declare const getOrdinal: (num: number) => string;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Get current time info in US Boston Eastern Time, independent of machine
|
|
465
|
+
* timezone
|
|
466
|
+
* @author Gabe Abrams
|
|
467
|
+
* @param {Date} [date=now] the date to get info on
|
|
468
|
+
* @returns object with timestamp (ms since epoch) and numbers
|
|
469
|
+
* corresponding to ET time values for year, month, day, hour, minute
|
|
470
|
+
*/
|
|
471
|
+
declare const getTimeInfoInET: (date?: Date | undefined) => {
|
|
472
|
+
timestamp: number;
|
|
473
|
+
year: number;
|
|
474
|
+
month: number;
|
|
475
|
+
day: number;
|
|
476
|
+
hour: number;
|
|
477
|
+
minute: number;
|
|
478
|
+
};
|
|
479
|
+
|
|
261
480
|
/**
|
|
262
481
|
* List of error codes built into the react kit
|
|
263
482
|
* @author Gabe Abrams
|
|
@@ -268,7 +487,9 @@ declare enum ReactKitErrorCode {
|
|
|
268
487
|
SessionExpired = "DRK3",
|
|
269
488
|
MissingParameter = "DRK4",
|
|
270
489
|
InvalidParameter = "DRK5",
|
|
271
|
-
WrongCourse = "DRK6"
|
|
490
|
+
WrongCourse = "DRK6",
|
|
491
|
+
NoCACCLSendRequestFunction = "DRK7",
|
|
492
|
+
NoCACCLGetLaunchInfoFunction = "DRK8"
|
|
272
493
|
}
|
|
273
494
|
|
|
274
|
-
export { AppWrapper, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, waitMs };
|
|
495
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, ErrorBox, ErrorWithCode, LoadingSpinner, Modal, ModalButtonType, ModalSize, ModalType, ParamType, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getOrdinal, getTimeInfoInET, handleError, handleSuccess, initServer, padDecimalZeros, padZerosLeft, roundToNumDecimals, showFatalError, sum, visitServerEndpoint, waitMs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dce-reactkit",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Shared components for Harvard DCE apps",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/harvard-edtech/dce-reactkit#readme",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
+
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
|
22
23
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
|
23
24
|
"@fortawesome/react-fontawesome": "^0.1.18",
|
|
24
25
|
"bootstrap": "^5.1.3",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@rollup/plugin-commonjs": "^22.0.0",
|
|
30
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
29
31
|
"@rollup/plugin-node-resolve": "^13.2.1",
|
|
30
32
|
"@rollup/plugin-typescript": "^8.3.2",
|
|
31
33
|
"@types/react": "^18.0.7",
|
package/rollup.config.js
CHANGED
|
@@ -28,22 +28,70 @@ import ErrorWithCode from '../errors/ErrorWithCode';
|
|
|
28
28
|
type Props = {
|
|
29
29
|
// The entire app
|
|
30
30
|
children: React.ReactNode,
|
|
31
|
+
// Copy of CACCL's send request function
|
|
32
|
+
sendRequest: SendRequestFunction,
|
|
31
33
|
// True if this app is a dark-themed app
|
|
32
34
|
dark?: boolean,
|
|
33
35
|
// Custom session expired message
|
|
34
36
|
sessionExpiredMessage?: string,
|
|
35
37
|
};
|
|
36
38
|
|
|
39
|
+
// Type of CACCL's send request function
|
|
40
|
+
type SendRequestFunction = (
|
|
41
|
+
opts: {
|
|
42
|
+
path: string;
|
|
43
|
+
method: ('GET' | 'POST' | 'DELETE' | 'PUT');
|
|
44
|
+
params?: {
|
|
45
|
+
[x: string]: any;
|
|
46
|
+
} | undefined;
|
|
47
|
+
headers?: {
|
|
48
|
+
[x: string]: any;
|
|
49
|
+
} | undefined;
|
|
50
|
+
numRetries?: number | undefined;
|
|
51
|
+
},
|
|
52
|
+
) => Promise<{
|
|
53
|
+
body: any;
|
|
54
|
+
status: number;
|
|
55
|
+
headers: {
|
|
56
|
+
[x: string]: any;
|
|
57
|
+
};
|
|
58
|
+
}>;
|
|
59
|
+
|
|
37
60
|
/*------------------------------------------------------------------------*/
|
|
38
61
|
/* Static Helpers */
|
|
39
62
|
/*------------------------------------------------------------------------*/
|
|
40
63
|
|
|
64
|
+
/*----------------------------------------*/
|
|
65
|
+
/* Send Request */
|
|
66
|
+
/*----------------------------------------*/
|
|
67
|
+
|
|
68
|
+
// Store copy of caccl send request
|
|
69
|
+
let _cacclSendRequest: SendRequestFunction;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Send a request using caccl's send request feature
|
|
73
|
+
* @author Gabe Abrams
|
|
74
|
+
* @param opts send request options
|
|
75
|
+
* @returns send request response
|
|
76
|
+
*/
|
|
77
|
+
export const cacclSendRequest: SendRequestFunction = async (opts) => {
|
|
78
|
+
// Make sure send request has been passed in
|
|
79
|
+
if (!_cacclSendRequest) {
|
|
80
|
+
throw new ErrorWithCode(
|
|
81
|
+
'The request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.',
|
|
82
|
+
ReactKitErrorCode.NoCACCLSendRequestFunction,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return _cacclSendRequest(opts);
|
|
87
|
+
};
|
|
88
|
+
|
|
41
89
|
/*----------------------------------------*/
|
|
42
90
|
/* Alert */
|
|
43
91
|
/*----------------------------------------*/
|
|
44
92
|
|
|
45
93
|
// Stored copies of setters
|
|
46
|
-
let setAlertInfo: (info: { title: string, text: string }) => void;
|
|
94
|
+
let setAlertInfo: (info: undefined | { title: string, text: string }) => void;
|
|
47
95
|
let onAlertClosed: () => void;
|
|
48
96
|
|
|
49
97
|
/**
|
|
@@ -79,7 +127,7 @@ export const alert = async (title: string, text: string): Promise<undefined> =>
|
|
|
79
127
|
/*----------------------------------------*/
|
|
80
128
|
|
|
81
129
|
// Stored copies of setters
|
|
82
|
-
let setConfirmInfo: (info: { title: string, text: string }) => void;
|
|
130
|
+
let setConfirmInfo: (info: undefined | { title: string, text: string }) => void;
|
|
83
131
|
let onConfirmClosed: (confirmed: boolean) => void;
|
|
84
132
|
|
|
85
133
|
/**
|
|
@@ -190,10 +238,14 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
190
238
|
|
|
191
239
|
const {
|
|
192
240
|
children,
|
|
241
|
+
sendRequest,
|
|
193
242
|
dark,
|
|
194
243
|
sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.',
|
|
195
244
|
} = props;
|
|
196
245
|
|
|
246
|
+
// Store copy of send request
|
|
247
|
+
_cacclSendRequest = sendRequest;
|
|
248
|
+
|
|
197
249
|
/* -------------- State ------------- */
|
|
198
250
|
|
|
199
251
|
// Fatal error
|
|
@@ -217,20 +269,26 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
217
269
|
const [
|
|
218
270
|
alertInfo,
|
|
219
271
|
setAlertInfoInner,
|
|
220
|
-
] = useState<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
272
|
+
] = useState<
|
|
273
|
+
undefined
|
|
274
|
+
| {
|
|
275
|
+
title: string,
|
|
276
|
+
text: string
|
|
277
|
+
}
|
|
278
|
+
>(undefined);
|
|
224
279
|
setAlertInfo = setAlertInfoInner;
|
|
225
280
|
|
|
226
281
|
// Confirm
|
|
227
282
|
const [
|
|
228
283
|
confirmInfo,
|
|
229
284
|
setConfirmInfoInner,
|
|
230
|
-
] = useState<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
285
|
+
] = useState<
|
|
286
|
+
undefined
|
|
287
|
+
| {
|
|
288
|
+
title: string,
|
|
289
|
+
text: string
|
|
290
|
+
}
|
|
291
|
+
>(undefined);
|
|
234
292
|
setConfirmInfo = setConfirmInfoInner;
|
|
235
293
|
|
|
236
294
|
// Session expired
|
|
@@ -256,10 +314,12 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
256
314
|
if (alertInfo) {
|
|
257
315
|
modal = (
|
|
258
316
|
<Modal
|
|
317
|
+
key={`alert-${alertInfo.title}-${alertInfo.text}`}
|
|
259
318
|
title={alertInfo.title}
|
|
260
319
|
type={ModalType.Okay}
|
|
261
320
|
onClose={() => {
|
|
262
321
|
// Alert closed
|
|
322
|
+
setAlertInfo(undefined);
|
|
263
323
|
if (onAlertClosed) {
|
|
264
324
|
onAlertClosed();
|
|
265
325
|
}
|
|
@@ -276,16 +336,18 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
276
336
|
if (confirmInfo) {
|
|
277
337
|
modal = (
|
|
278
338
|
<Modal
|
|
339
|
+
key={`confirm-${confirmInfo.title}-${confirmInfo.text}`}
|
|
279
340
|
title={confirmInfo.title}
|
|
280
341
|
type={ModalType.OkayCancel}
|
|
281
342
|
onClose={(buttonType) => {
|
|
343
|
+
setConfirmInfo(undefined);
|
|
282
344
|
if (onConfirmClosed) {
|
|
283
345
|
onConfirmClosed(buttonType === ModalButtonType.Okay);
|
|
284
346
|
}
|
|
285
347
|
}}
|
|
286
348
|
dontAllowBackdropExit
|
|
287
349
|
>
|
|
288
|
-
|
|
350
|
+
{confirmInfo.text}
|
|
289
351
|
</Modal>
|
|
290
352
|
);
|
|
291
353
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input group with a title and space for buttons
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
/*------------------------------------------------------------------------*/
|
|
10
|
+
/* Types */
|
|
11
|
+
/*------------------------------------------------------------------------*/
|
|
12
|
+
|
|
13
|
+
// Props definition
|
|
14
|
+
type Props = {
|
|
15
|
+
// Label text
|
|
16
|
+
label: string,
|
|
17
|
+
// Minimum width of the label (css units)
|
|
18
|
+
minLabelWidth?: string,
|
|
19
|
+
// Buttons
|
|
20
|
+
children: React.ReactNode,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/*------------------------------------------------------------------------*/
|
|
24
|
+
/* Component */
|
|
25
|
+
/*------------------------------------------------------------------------*/
|
|
26
|
+
|
|
27
|
+
const ButtonInputGroup: React.FC<Props> = (props) => {
|
|
28
|
+
/*------------------------------------------------------------------------*/
|
|
29
|
+
/* Setup */
|
|
30
|
+
/*------------------------------------------------------------------------*/
|
|
31
|
+
|
|
32
|
+
/* -------------- Props ------------- */
|
|
33
|
+
|
|
34
|
+
// Destructure all props
|
|
35
|
+
const {
|
|
36
|
+
label,
|
|
37
|
+
minLabelWidth,
|
|
38
|
+
children,
|
|
39
|
+
} = props;
|
|
40
|
+
|
|
41
|
+
/*------------------------------------------------------------------------*/
|
|
42
|
+
/* Render */
|
|
43
|
+
/*------------------------------------------------------------------------*/
|
|
44
|
+
|
|
45
|
+
/*----------------------------------------*/
|
|
46
|
+
/* Main UI */
|
|
47
|
+
/*----------------------------------------*/
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="input-group">
|
|
51
|
+
<div className="input-group-prepend d-flex w-100">
|
|
52
|
+
{/* Label */}
|
|
53
|
+
<span
|
|
54
|
+
className="input-group-text"
|
|
55
|
+
style={{
|
|
56
|
+
minWidth: (
|
|
57
|
+
minLabelWidth
|
|
58
|
+
?? undefined
|
|
59
|
+
),
|
|
60
|
+
borderTopRightRadius: 0,
|
|
61
|
+
borderBottomRightRadius: 0,
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
{label}
|
|
65
|
+
</span>
|
|
66
|
+
|
|
67
|
+
{/* Contents */}
|
|
68
|
+
<span
|
|
69
|
+
className="input-group-text flex-grow-1 rounded-right"
|
|
70
|
+
style={{
|
|
71
|
+
backgroundColor: 'white',
|
|
72
|
+
borderTopLeftRadius: 0,
|
|
73
|
+
borderBottomLeftRadius: 0,
|
|
74
|
+
borderLeftWidth: 0,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/*------------------------------------------------------------------------*/
|
|
85
|
+
/* Wrap Up */
|
|
86
|
+
/*------------------------------------------------------------------------*/
|
|
87
|
+
|
|
88
|
+
// Export component
|
|
89
|
+
export default ButtonInputGroup;
|
|
@@ -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;
|