dce-reactkit 3.9.2 → 3.9.4-beta-prompt.1

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/dist/cjs/index.js CHANGED
@@ -59,7 +59,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
59
59
  });
60
60
  }
61
61
 
62
- // Highest error code = DRK16
62
+ // Highest error code = DRK18
63
63
  /**
64
64
  * List of error codes built into the react kit
65
65
  * @author Gabe Abrams
@@ -71,6 +71,8 @@ var ReactKitErrorCode;
71
71
  ReactKitErrorCode["SessionExpired"] = "DRK3";
72
72
  ReactKitErrorCode["MissingParameter"] = "DRK4";
73
73
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
74
+ ReactKitErrorCode["HostNotAllowed"] = "DRK17";
75
+ ReactKitErrorCode["HostBanned"] = "DRK18";
74
76
  ReactKitErrorCode["WrongCourse"] = "DRK6";
75
77
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
76
78
  ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
@@ -184,6 +186,7 @@ var ModalButtonType$1 = ModalButtonType;
184
186
  var ModalType;
185
187
  (function (ModalType) {
186
188
  ModalType["Okay"] = "okay";
189
+ ModalType["Cancel"] = "cancel";
187
190
  ModalType["OkayCancel"] = "okay-cancel";
188
191
  ModalType["YesNo"] = "yes-no";
189
192
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -338,6 +341,9 @@ const modalTypeToModalButtonTypes = {
338
341
  [ModalType$1.Okay]: [
339
342
  ModalButtonType$1.Okay,
340
343
  ],
344
+ [ModalType$1.Cancel]: [
345
+ ModalButtonType$1.Cancel,
346
+ ],
341
347
  [ModalType$1.OkayCancel]: [
342
348
  ModalButtonType$1.Okay,
343
349
  ModalButtonType$1.Cancel,
@@ -1078,6 +1084,73 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1078
1084
  });
1079
1085
  });
1080
1086
  /*----------------------------------------*/
1087
+ /* --------------- Prompt -------------- */
1088
+ /*----------------------------------------*/
1089
+ // Stored copies of setters
1090
+ let setPromptInfo;
1091
+ // Function to call when prompt is closed
1092
+ let onPromptClosed;
1093
+ /**
1094
+ * Show a prompt modal asking the user for input
1095
+ * @author Yuen Ler Chow
1096
+ * @param title the title text to display at the top of the prompt
1097
+ * @param text the text to display in the prompt
1098
+ * @param [opts={}] additional options for the prompt dialog
1099
+ * @param [opts.placeholder] the placeholder text for the input field
1100
+ * @param [opts.defaultText] the default text for the input field
1101
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
1102
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1103
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1104
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1105
+ * @returns Promise that resolves with the input string or null if canceled
1106
+ */
1107
+ const prompt = (title, text, currentInputFieldText, opts) => __awaiter(void 0, void 0, void 0, function* () {
1108
+ var _a;
1109
+ // Wait for helper to exist
1110
+ yield waitForHelper(() => {
1111
+ return !!setPromptInfo;
1112
+ });
1113
+ // Fallback if prompt is not available
1114
+ if (!setPromptInfo) {
1115
+ const resultPassesValidation = false;
1116
+ while (!resultPassesValidation) {
1117
+ // eslint-disable-next-line no-alert
1118
+ const result = window.prompt(`${title}\n\n${text}`, (_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : '');
1119
+ if (result === null) {
1120
+ return null;
1121
+ }
1122
+ // Validate min num chars
1123
+ const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1124
+ ? `Please enter at least ${opts.minNumChars} characters.`
1125
+ : undefined);
1126
+ // Run custom validation
1127
+ const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1128
+ && opts.findValidationError(result));
1129
+ // Show validation issue
1130
+ if (minNumCharsValidationError || customValidationError) {
1131
+ alert('Invalid Input', `${minNumCharsValidationError !== null && minNumCharsValidationError !== void 0 ? minNumCharsValidationError : ''} ${customValidationError !== null && customValidationError !== void 0 ? customValidationError : ''}`);
1132
+ }
1133
+ else {
1134
+ return result;
1135
+ }
1136
+ }
1137
+ }
1138
+ // Return promise that resolves with result of prompt
1139
+ return new Promise((resolve) => {
1140
+ // Setup handler
1141
+ onPromptClosed = (result) => {
1142
+ resolve(result);
1143
+ };
1144
+ // Show the prompt
1145
+ setPromptInfo({
1146
+ title,
1147
+ text,
1148
+ currentInputFieldText,
1149
+ opts: (opts !== null && opts !== void 0 ? opts : {}),
1150
+ });
1151
+ });
1152
+ });
1153
+ /*----------------------------------------*/
1081
1154
  /* ------------- Fatal Error ------------ */
1082
1155
  /*----------------------------------------*/
1083
1156
  // Stored copies of setters
@@ -1093,14 +1166,14 @@ const fatalErrorHandlers = [];
1093
1166
  * @param [errorTitle] title of the error box
1094
1167
  */
1095
1168
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1096
- var _a, _b;
1169
+ var _b, _c;
1097
1170
  // Determine message and code
1098
1171
  const message = (typeof error === 'string'
1099
1172
  ? error.trim()
1100
- : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1173
+ : String((_b = error.message) !== null && _b !== void 0 ? _b : 'An unknown error occurred.'));
1101
1174
  const code = (typeof error === 'string'
1102
1175
  ? ReactKitErrorCode$1.NoCode
1103
- : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1176
+ : String((_c = error.code) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode));
1104
1177
  // Call all fatal error listeners
1105
1178
  try {
1106
1179
  fatalErrorHandlers.forEach((handler) => {
@@ -1227,6 +1300,9 @@ const AppWrapper = (props) => {
1227
1300
  // Confirm
1228
1301
  const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
1229
1302
  setConfirmInfo = setConfirmInfoInner;
1303
+ // Prompt
1304
+ const [promptInfo, setPromptInfoInner] = React.useState(undefined);
1305
+ setPromptInfo = setPromptInfoInner;
1230
1306
  // Session expired
1231
1307
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
1232
1308
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1257,6 +1333,37 @@ const AppWrapper = (props) => {
1257
1333
  }
1258
1334
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1259
1335
  }
1336
+ /* ------------- Prompt ------------ */
1337
+ if (promptInfo) {
1338
+ // Run min char validation
1339
+ const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1340
+ && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1341
+ ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1342
+ : undefined);
1343
+ // Run custom validation
1344
+ const customValidationError = (promptInfo.opts.findValidationError
1345
+ && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1346
+ modal = (React__default["default"].createElement(Modal, { key: `prompt-${promptInfo.title}-${promptInfo.text}`, title: promptInfo.title,
1347
+ // Don't show ok button if there is a validation error
1348
+ type: ((customValidationError || minNumCharsValidationError)
1349
+ ? ModalType$1.Cancel
1350
+ : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1351
+ const result = (buttonType === ModalButtonType$1.Okay
1352
+ ? promptInfo.currentInputFieldText
1353
+ : null);
1354
+ setPromptInfo(undefined);
1355
+ if (onPromptClosed) {
1356
+ onPromptClosed(result);
1357
+ }
1358
+ }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1359
+ React__default["default"].createElement("div", { className: "d-flex flex-column align-items-center" },
1360
+ React__default["default"].createElement("p", null, promptInfo.text),
1361
+ React__default["default"].createElement("input", { type: "text", placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1362
+ return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1363
+ } }),
1364
+ minNumCharsValidationError && (React__default["default"].createElement("div", { className: "text-danger bg-danger bg-opacity-25 p-2 m-1 rounded" }, minNumCharsValidationError)),
1365
+ customValidationError && (React__default["default"].createElement("div", { className: "text-danger bg-danger bg-opacity-25 p-2 m-1 rounded" }, customValidationError)))));
1366
+ }
1260
1367
  /* ------ Custom Modal Portals ------ */
1261
1368
  // Custom modal portals
1262
1369
  const customModalPortals = [];
@@ -14469,6 +14576,12 @@ const parseUserAgent = (userAgent) => {
14469
14576
  * @param opts.handler function that processes the request
14470
14577
  * @param [opts.skipSessionCheck] if true, skip the session check (allow users
14471
14578
  * to not be logged in and launched via LTI)
14579
+ * @param [opts.allowedHosts] if included, only allow requests from these hosts
14580
+ * (start a hostname with a "*" to only check the end of the hostname)
14581
+ * you can include just one string instead of an array
14582
+ * @param [opts.bannedHosts] if included, do not allow requests from these hosts
14583
+ * (start a hostname with a "*" to only check the end of the hostname)
14584
+ * you can include just one string instead of an array
14472
14585
  * @param [opts.unhandledErrorMessagePrefix] if included, when an error that
14473
14586
  * is not of type ErrorWithCode is thrown, the client will receive an error
14474
14587
  * where the error message is prefixed with this string. For example,
@@ -14500,6 +14613,72 @@ const genRouteHandler = (opts) => {
14500
14613
  // Output params
14501
14614
  const output = {};
14502
14615
  /*----------------------------------------*/
14616
+ /* ----------- Hostname Check ----------- */
14617
+ /*----------------------------------------*/
14618
+ // Get hostnames
14619
+ const originURL = String(req.get('origin')
14620
+ || req.headers.origin
14621
+ || req.headers.referer);
14622
+ const originHostname = (originURL
14623
+ // Remove protocol
14624
+ .replace(/(^\w+:|^)\/\//, '')
14625
+ // Remove port
14626
+ .replace(/:\d+$/, ''));
14627
+ const serverHostname = String(req.hostname);
14628
+ // Check allowed
14629
+ if (opts.allowedHosts) {
14630
+ // Only accept requests from allowed hosts
14631
+ const allowedArray = (Array.isArray(opts.allowedHosts)
14632
+ ? opts.allowedHosts
14633
+ : [opts.allowedHosts]);
14634
+ // Check if server is localhost
14635
+ if (serverHostname === 'localhost') {
14636
+ // Allow localhost
14637
+ allowedArray.push('localhost');
14638
+ }
14639
+ // Check if current host is allowed
14640
+ const allowed = allowedArray.some((allowedHost) => {
14641
+ if (allowedHost.startsWith('*')) {
14642
+ // Check end of hostname
14643
+ return originHostname.endsWith(allowedHost.substring(1));
14644
+ }
14645
+ // Check full hostname
14646
+ return originHostname.toLowerCase() === allowedHost.toLowerCase();
14647
+ });
14648
+ // If not allowed, return error
14649
+ if (!allowed) {
14650
+ return handleError(res, {
14651
+ message: 'You are not allowed to access this endpoint.',
14652
+ code: ReactKitErrorCode$1.HostNotAllowed,
14653
+ status: 403,
14654
+ });
14655
+ }
14656
+ }
14657
+ // Check banned
14658
+ if (opts.bannedHosts) {
14659
+ // Do not allow requests from banned hosts
14660
+ const bannedArray = (Array.isArray(opts.bannedHosts)
14661
+ ? opts.bannedHosts
14662
+ : [opts.bannedHosts]);
14663
+ // Check if current host is banned
14664
+ const banned = bannedArray.some((bannedHost) => {
14665
+ if (bannedHost.startsWith('*')) {
14666
+ // Check end of hostname
14667
+ return originHostname.endsWith(bannedHost.substring(1));
14668
+ }
14669
+ // Check full hostname
14670
+ return originHostname.toLowerCase() === bannedHost.toLowerCase();
14671
+ });
14672
+ // If banned, return error
14673
+ if (banned) {
14674
+ return handleError(res, {
14675
+ message: 'You are not allowed to access this endpoint.',
14676
+ code: ReactKitErrorCode$1.HostBanned,
14677
+ status: 403,
14678
+ });
14679
+ }
14680
+ }
14681
+ /*----------------------------------------*/
14503
14682
  /* ------------ Parse Params ------------ */
14504
14683
  /*----------------------------------------*/
14505
14684
  // Process items one by one
@@ -16247,6 +16426,7 @@ exports.padDecimalZeros = padDecimalZeros;
16247
16426
  exports.padZerosLeft = padZerosLeft;
16248
16427
  exports.parallelLimit = parallelLimit;
16249
16428
  exports.prefixWithAOrAn = prefixWithAOrAn;
16429
+ exports.prompt = prompt;
16250
16430
  exports.roundToNumDecimals = roundToNumDecimals;
16251
16431
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
16252
16432
  exports.showFatalError = showFatalError;