dce-reactkit 4.0.0 → 4.0.2-beta-simple-date-chooser.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.
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * A very simple, lightweight date chooser
3
3
  * @author Gabe Abrams
4
+ * @author Gardenia Liu
4
5
  */
5
6
  import React from 'react';
6
7
  type Props = {
@@ -17,7 +18,8 @@ type Props = {
17
18
  */
18
19
  onChange: (month: number, day: number, year: number) => void;
19
20
  numMonthsToShow?: number;
20
- chooseFromPast?: boolean;
21
+ dontAllowPast?: boolean;
22
+ dontAllowFuture?: boolean;
21
23
  };
22
24
  declare const SimpleDateChooser: React.FC<Props>;
23
25
  export default SimpleDateChooser;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A very simple, lightweight time chooser
3
+ * @author Gardenia Liu
4
+ */
5
+ import React from 'react';
6
+ type Props = {
7
+ ariaLabel: string;
8
+ name: string;
9
+ hour: number;
10
+ minute: number;
11
+ /**
12
+ * Handler for when time changes
13
+ * @param hour new 24hr hour number
14
+ * @param minute new minute number
15
+ */
16
+ onChange: (hour: number, minute: number) => void;
17
+ intervalMin?: number;
18
+ };
19
+ declare const SimpleTimeChooser: React.FC<Props>;
20
+ export default SimpleTimeChooser;
@@ -7,6 +7,7 @@ import RadioButton from './components/RadioButton';
7
7
  import CheckboxButton from './components/CheckboxButton';
8
8
  import ButtonInputGroup from './components/ButtonInputGroup';
9
9
  import SimpleDateChooser from './components/SimpleDateChooser';
10
+ import SimpleTimeChooser from './components/SimpleTimeChooser';
10
11
  import Drawer from './components/Drawer';
11
12
  import PopSuccessMark from './components/PopSuccessMark';
12
13
  import PopFailureMark from './components/PopFailureMark';
@@ -100,4 +101,4 @@ import PickableItem from './components/ItemPicker/types/PickableItem';
100
101
  import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
101
102
  import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
102
103
  import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
103
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
104
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, SimpleTimeChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, alert, prompt, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, genCommaList, validateEmail, validatePhoneNumber, validateString, getLocalTimeInfo, idify, makeLinksClickable, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ParamType, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, IntelliTableColumn, DropdownItemType, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, };
@@ -6,6 +6,8 @@ declare enum ReactKitErrorCode {
6
6
  NoResponse = "DRK1",
7
7
  NoCode = "DRK2",
8
8
  SessionExpired = "DRK3",
9
- NoCACCLSendRequestFunction = "DRK7"
9
+ NoCACCLSendRequestFunction = "DRK7",
10
+ SimpleDateChooserInvalidDateRange = "DRK35",
11
+ SimpleDateChooserInvalidNumMonths = "DRK36"
10
12
  }
11
13
  export default ReactKitErrorCode;
package/dist/esm/index.js CHANGED
@@ -29,7 +29,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
29
29
  });
30
30
  }
31
31
 
32
- // Highest error code = DRK34
32
+ // Highest error code = DRK36
33
33
  /**
34
34
  * List of error codes built into the react kit
35
35
  * @author Gabe Abrams
@@ -40,6 +40,8 @@ var ReactKitErrorCode;
40
40
  ReactKitErrorCode["NoCode"] = "DRK2";
41
41
  ReactKitErrorCode["SessionExpired"] = "DRK3";
42
42
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
43
+ ReactKitErrorCode["SimpleDateChooserInvalidDateRange"] = "DRK35";
44
+ ReactKitErrorCode["SimpleDateChooserInvalidNumMonths"] = "DRK36";
43
45
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
44
46
  var ReactKitErrorCode$1 = ReactKitErrorCode;
45
47
 
@@ -2521,6 +2523,19 @@ const ButtonInputGroup = (props) => {
2521
2523
  : children))));
2522
2524
  };
2523
2525
 
2526
+ // Constants
2527
+ const ORDINALS = ['th', 'st', 'nd', 'rd'];
2528
+ /**
2529
+ * Get a number's ordinal
2530
+ * @author Gabe Abrams
2531
+ * @param num the number being analyzed
2532
+ * @returns ordinal
2533
+ */
2534
+ const getOrdinal = (num) => {
2535
+ var _a, _b;
2536
+ return ((_b = (_a = ORDINALS[(num - 20) % 10]) !== null && _a !== void 0 ? _a : ORDINALS[num]) !== null && _b !== void 0 ? _b : ORDINALS[0]);
2537
+ };
2538
+
2524
2539
  const monthNames = [
2525
2540
  { short: 'Jan', full: 'January' },
2526
2541
  { short: 'Feb', full: 'February' },
@@ -2549,19 +2564,6 @@ const getMonthName = (month) => {
2549
2564
  return ((_a = monthNames[month - 1]) !== null && _a !== void 0 ? _a : monthNames[0]);
2550
2565
  };
2551
2566
 
2552
- // Constants
2553
- const ORDINALS = ['th', 'st', 'nd', 'rd'];
2554
- /**
2555
- * Get a number's ordinal
2556
- * @author Gabe Abrams
2557
- * @param num the number being analyzed
2558
- * @returns ordinal
2559
- */
2560
- const getOrdinal = (num) => {
2561
- var _a, _b;
2562
- return ((_b = (_a = ORDINALS[(num - 20) % 10]) !== null && _a !== void 0 ? _a : ORDINALS[num]) !== null && _b !== void 0 ? _b : ORDINALS[0]);
2563
- };
2564
-
2565
2567
  /**
2566
2568
  * Get current time info in US Boston Eastern Time, independent of machine
2567
2569
  * timezone
@@ -2625,35 +2627,86 @@ const getTimeInfoInET = (dateOrTimestamp) => {
2625
2627
  /**
2626
2628
  * A very simple, lightweight date chooser
2627
2629
  * @author Gabe Abrams
2630
+ * @author Gardenia Liu
2628
2631
  */
2629
2632
  /*------------------------------------------------------------------------*/
2630
- /* ------------------------------ Component ----------------------------- */
2633
+ /* -------------------------------- State ------------------------------- */
2631
2634
  /*------------------------------------------------------------------------*/
2632
- const SimpleDateChooser = (props) => {
2633
- /*------------------------------------------------------------------------*/
2634
- /* -------------------------------- Setup ------------------------------- */
2635
- /*------------------------------------------------------------------------*/
2636
- /* -------------- Props ------------- */
2637
- const { ariaLabel, name, onChange, chooseFromPast, numMonthsToShow = 6, } = props;
2638
- /*------------------------------------------------------------------------*/
2639
- /* ------------------------------- Render ------------------------------- */
2640
- /*------------------------------------------------------------------------*/
2641
- /*----------------------------------------*/
2642
- /* --------------- Main UI -------------- */
2643
- /*----------------------------------------*/
2635
+ /* -------------- Views ------------- */
2636
+ var View;
2637
+ (function (View) {
2638
+ // Date chooser
2639
+ View["DateChooser"] = "DateChooser";
2640
+ // Invalid date
2641
+ View["InvalidDate"] = "InvalidDate";
2642
+ })(View || (View = {}));
2643
+ /* ------------- Actions ------------ */
2644
+ // Types of actions
2645
+ var ActionType$a;
2646
+ (function (ActionType) {
2647
+ // Fix invalid date so it is now in range
2648
+ ActionType["FixInvalidDate"] = "FixInvalidDate";
2649
+ })(ActionType$a || (ActionType$a = {}));
2650
+ /**
2651
+ * Reducer that executes actions
2652
+ * @author Gardenia Liu
2653
+ * @param state current state
2654
+ * @param action action to execute
2655
+ * @returns updated state
2656
+ */
2657
+ const reducer$b = (state, action) => {
2658
+ switch (action.type) {
2659
+ case ActionType$a.FixInvalidDate: {
2660
+ return Object.assign(Object.assign({}, state), { view: View.DateChooser });
2661
+ }
2662
+ default: {
2663
+ return state;
2664
+ }
2665
+ }
2666
+ };
2667
+ /*------------------------------------------------------------------------*/
2668
+ /* --------------------------- Static Helpers --------------------------- */
2669
+ /*------------------------------------------------------------------------*/
2670
+ /**
2671
+ * Get the list of choices in the date chooser given props
2672
+ * @author Gardenia Liu
2673
+ * @author Gabe Abrams
2674
+ * @param opts object containing all arguments
2675
+ * @param opts.numMonthsToShow number of months to show
2676
+ * @param opts.dontAllowPast if true, the user isn't allowed to select dates in the past
2677
+ * @param opts.dontAllowFuture if true, the user isn't allowed to select dates in the future
2678
+ * @returns choices
2679
+ */
2680
+ const getChoices = (opts) => {
2681
+ // Destructure props
2682
+ const { dontAllowFuture, dontAllowPast, numMonthsToShow = 6, } = opts;
2644
2683
  // Determine the set of choices allowed
2645
2684
  const today = getTimeInfoInET();
2646
2685
  const choices = [];
2647
2686
  let startYear = today.year;
2648
2687
  let startMonth = today.month;
2649
- if (chooseFromPast) {
2688
+ // Don't allow past or future dates
2689
+ if (dontAllowPast && dontAllowFuture) {
2690
+ throw new ErrorWithCode('No past or future dates allowed', ReactKitErrorCode$1.SimpleDateChooserInvalidDateRange);
2691
+ }
2692
+ // Require numMonthsToShow to be positive
2693
+ if (numMonthsToShow <= 0) {
2694
+ throw new ErrorWithCode('numMonthsToShow must be positive', ReactKitErrorCode$1.SimpleDateChooserInvalidNumMonths);
2695
+ }
2696
+ // Recalculate startMonth and startYear when allowing past dates
2697
+ if (!dontAllowPast) {
2650
2698
  startMonth -= Math.max(0, numMonthsToShow - 1);
2651
2699
  while (startMonth <= 0) {
2652
2700
  startMonth += 12;
2653
2701
  startYear -= 1;
2654
2702
  }
2655
2703
  }
2656
- for (let i = 0; i < numMonthsToShow; i++) {
2704
+ // Calculate total number of months to show
2705
+ let totalMonthsToShow = numMonthsToShow;
2706
+ if (!dontAllowPast && !dontAllowFuture) {
2707
+ totalMonthsToShow = totalMonthsToShow * 2 - 1;
2708
+ }
2709
+ for (let i = 0; i < totalMonthsToShow; i++) {
2657
2710
  // Get month and year info
2658
2711
  const unmoddedMonth = (startMonth + i);
2659
2712
  let month = unmoddedMonth;
@@ -2672,23 +2725,25 @@ const SimpleDateChooser = (props) => {
2672
2725
  // Figure out which days are allowed
2673
2726
  const days = [];
2674
2727
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
2675
- if (chooseFromPast) {
2676
- // Past selection
2677
- const numDaysToAdd = ((month === today.month)
2678
- ? today.day // Current month, only add up to today
2679
- : numDaysInMonth // Past month, add all days
2680
- );
2681
- for (let day = 1; day <= numDaysToAdd; day++) {
2682
- days.push(day);
2728
+ // Current month
2729
+ if (month === today.month && year === today.year) {
2730
+ // Past selection: add all previous days of the month
2731
+ if (!dontAllowPast) {
2732
+ for (let day = 1; day < today.day; day++) {
2733
+ days.push(day);
2734
+ }
2683
2735
  }
2684
- }
2685
- else {
2736
+ days.push(today.day); // Add current day
2686
2737
  // Future selection: add all remaining days of the month
2687
- const firstDay = (month === today.month
2688
- ? today.day // Current month: start at current date
2689
- : 1 // Future month: start at beginning of month
2690
- );
2691
- for (let day = firstDay; day <= numDaysInMonth; day++) {
2738
+ if (!dontAllowFuture) {
2739
+ for (let day = today.day + 1; day <= numDaysInMonth; day++) {
2740
+ days.push(day);
2741
+ }
2742
+ }
2743
+ }
2744
+ else { // Past or future month
2745
+ // Include all days in the month
2746
+ for (let day = 1; day <= numDaysInMonth; day++) {
2692
2747
  days.push(day);
2693
2748
  }
2694
2749
  }
@@ -2699,36 +2754,262 @@ const SimpleDateChooser = (props) => {
2699
2754
  days,
2700
2755
  });
2701
2756
  }
2702
- // Create choice options
2703
- const { month, day, year, } = props;
2704
- const monthOptions = [];
2705
- const dayOptions = [];
2706
- choices.forEach((choice) => {
2707
- // Create month option
2708
- monthOptions.push(React__default.createElement("option", { key: `${choice.year}-${choice.month}`, value: `${choice.month}-${choice.year}`, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
2709
- onChange(choice.month, choice.days[0], choice.year);
2710
- } }, choice.choiceName));
2711
- if (month === choice.month) {
2712
- // This is the currently selected month
2713
- // Create day options
2714
- choice.days.forEach((dayChoice) => {
2715
- const ordinal = getOrdinal(dayChoice);
2716
- dayOptions.push(React__default.createElement("option", { key: `${choice.year}-${choice.month}-${dayChoice}`, value: dayChoice, "aria-label": `choose date ${dayChoice}` },
2717
- dayChoice,
2718
- ordinal));
2757
+ // Return choices
2758
+ return choices;
2759
+ };
2760
+ /**
2761
+ * Checks whether a given date is outside the valid range of allowed choices
2762
+ * @author Gardenia Liu
2763
+ * @param opts object containing all arguments
2764
+ * @param opts.month 1-indexed month
2765
+ * @param opts.day day of the month
2766
+ * @param opts.year full year
2767
+ * @param opts.choices valid date choices
2768
+ * @returns true if date is out of range
2769
+ */
2770
+ const isDateOutOfRange = (opts) => {
2771
+ const { month, day, year, choices, } = opts;
2772
+ return !choices.some((choice) => {
2773
+ return (choice.month === month
2774
+ && choice.year === year
2775
+ && choice.days.includes(day));
2776
+ });
2777
+ };
2778
+ /*------------------------------------------------------------------------*/
2779
+ /* ------------------------------ Component ----------------------------- */
2780
+ /*------------------------------------------------------------------------*/
2781
+ const SimpleDateChooser = (props) => {
2782
+ /*------------------------------------------------------------------------*/
2783
+ /* -------------------------------- Setup ------------------------------- */
2784
+ /*------------------------------------------------------------------------*/
2785
+ /* -------------- Props ------------- */
2786
+ const { ariaLabel, name, dontAllowPast, dontAllowFuture, numMonthsToShow, onChange, month, day, year, } = props;
2787
+ // Get choices
2788
+ const choices = getChoices({
2789
+ numMonthsToShow,
2790
+ dontAllowPast,
2791
+ dontAllowFuture,
2792
+ });
2793
+ /* -------------- State ------------- */
2794
+ // Check if the currently selected date is out of range
2795
+ const currentSelectedDateOutOfRange = isDateOutOfRange({
2796
+ month,
2797
+ day,
2798
+ year,
2799
+ choices,
2800
+ });
2801
+ // Initial state
2802
+ const initialState = {
2803
+ view: (currentSelectedDateOutOfRange
2804
+ ? View.InvalidDate
2805
+ : View.DateChooser),
2806
+ };
2807
+ // Initialize state
2808
+ const [state, dispatch] = useReducer(reducer$b, initialState);
2809
+ // Destructure common state
2810
+ const { view, } = state;
2811
+ /*------------------------------------------------------------------------*/
2812
+ /* ------------------------- Component Functions ------------------------ */
2813
+ /*------------------------------------------------------------------------*/
2814
+ /**
2815
+ * Ask the user if they want to edit an invalid date
2816
+ * @author Gardenia Liu
2817
+ * @author Gabe Abrams
2818
+ */
2819
+ const askToEditInvalidDate = () => __awaiter(void 0, void 0, void 0, function* () {
2820
+ // Ask the user if they want to edit the date
2821
+ const confirmed = yield confirm('Are you sure?', 'The current date is outside the normal range. If you edit it, you\'ll need to choose a new date in the normal range.', {
2822
+ confirmButtonText: 'Edit Date',
2823
+ });
2824
+ // Check if user confirmed
2825
+ if (confirmed) {
2826
+ // Update the date to today
2827
+ const today = getTimeInfoInET();
2828
+ onChange(today.month, today.day, today.year);
2829
+ // Update state
2830
+ dispatch({
2831
+ type: ActionType$a.FixInvalidDate,
2719
2832
  });
2720
2833
  }
2721
2834
  });
2722
- return (React__default.createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
2723
- React__default.createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: `${month}-${year}`, onChange: (e) => {
2724
- const choice = choices[e.target.selectedIndex];
2725
- // Change day, month, and year
2726
- onChange(choice.month, choice.days[0], choice.year);
2727
- } }, monthOptions),
2728
- React__default.createElement("select", { "aria-label": `day for ${ariaLabel}`, className: "custom-select d-inline-block", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-day`, value: day, onChange: (e) => {
2729
- // Only change the day
2730
- onChange(month, Number.parseInt(e.target.value, 10), year);
2731
- } }, dayOptions)));
2835
+ /*------------------------------------------------------------------------*/
2836
+ /* ------------------------------- Render ------------------------------- */
2837
+ /*------------------------------------------------------------------------*/
2838
+ /*----------------------------------------*/
2839
+ /* ---------------- Views --------------- */
2840
+ /*----------------------------------------*/
2841
+ // Body that will be filled with the current view
2842
+ let body;
2843
+ /* ---------- DateChooser ---------- */
2844
+ if (view === View.DateChooser) {
2845
+ // Create lists of options
2846
+ const monthOptions = [];
2847
+ const dayOptions = [];
2848
+ // Render each option and add it to the list
2849
+ choices.forEach((choice) => {
2850
+ // Create month option
2851
+ monthOptions.push(React__default.createElement("option", { key: `${choice.year}-${choice.month}`, value: `${choice.month}-${choice.year}`, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
2852
+ onChange(choice.month, choice.days[0], choice.year);
2853
+ } }, choice.choiceName));
2854
+ // This is the currently selected month
2855
+ if (month === choice.month) {
2856
+ // Create day options
2857
+ choice.days.forEach((dayChoice) => {
2858
+ const ordinal = getOrdinal(dayChoice);
2859
+ dayOptions.push(React__default.createElement("option", { key: `${choice.year}-${choice.month}-${dayChoice}`, value: dayChoice, "aria-label": `choose date ${dayChoice}` },
2860
+ dayChoice,
2861
+ ordinal));
2862
+ });
2863
+ }
2864
+ });
2865
+ // Create body
2866
+ body = (React__default.createElement("div", { className: "SimpleDateChooser-inner-container d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
2867
+ React__default.createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: `${month}-${year}`, onChange: (e) => {
2868
+ const choice = choices[e.target.selectedIndex];
2869
+ // Change day, month, and year
2870
+ onChange(choice.month, choice.days[0], choice.year);
2871
+ } }, monthOptions),
2872
+ React__default.createElement("select", { "aria-label": `day for ${ariaLabel}`, className: "custom-select d-inline-block", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-day`, value: day, onChange: (e) => {
2873
+ // Only change the day
2874
+ onChange(month, Number.parseInt(e.target.value, 10), year);
2875
+ } }, dayOptions)));
2876
+ }
2877
+ /* --------- DateOutOfRange --------- */
2878
+ if (view === View.InvalidDate) {
2879
+ body = (React__default.createElement("div", { className: "SimpleDateChooser-inner-container d-inline-block" },
2880
+ React__default.createElement("button", { type: "button", className: "btn btn-light", onClick: askToEditInvalidDate, "aria-label": `edit date for ${ariaLabel}` },
2881
+ getMonthName(month).full,
2882
+ ' ',
2883
+ day,
2884
+ getOrdinal(day),
2885
+ ",",
2886
+ ' ',
2887
+ year),
2888
+ React__default.createElement("button", { type: "button", className: "btn btn-secondary", onClick: askToEditInvalidDate, "aria-label": `edit date for ${ariaLabel}` }, "Edit")));
2889
+ }
2890
+ /*----------------------------------------*/
2891
+ /* --------------- Main UI -------------- */
2892
+ /*----------------------------------------*/
2893
+ return (React__default.createElement("span", { className: "SimpleDateChooser-outer-container" }, body));
2894
+ };
2895
+
2896
+ /**
2897
+ * Pad a number with zeros on the left (e.g. 5 becomes 05 with 2 digit padding)
2898
+ * @author Gabe Abrams
2899
+ * @param num the number to pad
2900
+ * @param numDigits the minimum number of digits before the decimal
2901
+ * @returns padded number
2902
+ */
2903
+ const padZerosLeft = (num, numDigits) => {
2904
+ // Convert to string
2905
+ let out = String(num);
2906
+ // Add zeros
2907
+ while (out.split('.')[0].length < numDigits) {
2908
+ out = `0${out}`;
2909
+ }
2910
+ // Return
2911
+ return out;
2912
+ };
2913
+
2914
+ /**
2915
+ * A very simple, lightweight time chooser
2916
+ * @author Gardenia Liu
2917
+ */
2918
+ /*------------------------------------------------------------------------*/
2919
+ /* ------------------------------ Constants ----------------------------- */
2920
+ /*------------------------------------------------------------------------*/
2921
+ // Allowed intervals between options
2922
+ const ALLOWED_INTERVALS = [15, 30, 60]; // min
2923
+ // Default interval to use if an unsupported interval is passed in
2924
+ const DEFAULT_INTERVAL = ALLOWED_INTERVALS[0]; // min
2925
+ /*------------------------------------------------------------------------*/
2926
+ /* ------------------------------ Component ----------------------------- */
2927
+ /*------------------------------------------------------------------------*/
2928
+ const SimpleTimeChooser = (props) => {
2929
+ /*------------------------------------------------------------------------*/
2930
+ /* -------------------------------- Setup ------------------------------- */
2931
+ /*------------------------------------------------------------------------*/
2932
+ /* -------------- Props ------------- */
2933
+ const { ariaLabel, name, hour, minute, onChange, } = props;
2934
+ let { intervalMin = DEFAULT_INTERVAL, } = props;
2935
+ // Use default interval if not supported
2936
+ if (!ALLOWED_INTERVALS.includes(intervalMin)) {
2937
+ intervalMin = DEFAULT_INTERVAL;
2938
+ }
2939
+ /*------------------------------------------------------------------------*/
2940
+ /* ------------------------- Component Functions ------------------------ */
2941
+ /*------------------------------------------------------------------------*/
2942
+ /**
2943
+ * Convert number of minutes since midnight into 24hour and minute format
2944
+ * @author Gabe Abrams
2945
+ * @param minSinceMidnight total minutes since midnight
2946
+ * @returns hours (24) and minutes
2947
+ */
2948
+ const convertMinSinceMidnightToHoursAndMin = (minSinceMidnight) => {
2949
+ return {
2950
+ hours: Math.floor(minSinceMidnight / 60),
2951
+ minutes: minSinceMidnight % 60,
2952
+ };
2953
+ };
2954
+ /**
2955
+ * Convert time in minutes into HH:MM format
2956
+ * @author Gardenia Liu
2957
+ * @param totalMinutes total minutes since midnight
2958
+ * @returns formatted time string
2959
+ */
2960
+ const formatTime = (totalMinutes) => {
2961
+ // Handle special cases
2962
+ if (totalMinutes === 0) {
2963
+ return '12:00 Midnight';
2964
+ }
2965
+ if (totalMinutes === 12 * 60) {
2966
+ return '12:00 Noon';
2967
+ }
2968
+ // All normal cases:
2969
+ const timeInfo = convertMinSinceMidnightToHoursAndMin(totalMinutes);
2970
+ let { hours } = timeInfo;
2971
+ const { minutes } = timeInfo;
2972
+ // Process 24hr -> 12hr
2973
+ const isAM = (hours < 12);
2974
+ if (hours === 0) {
2975
+ hours = 12;
2976
+ }
2977
+ else if (hours > 12) {
2978
+ hours %= 12;
2979
+ }
2980
+ // Pad with zeros
2981
+ const paddedMinutes = padZerosLeft(minutes, 2);
2982
+ // Assemble time string
2983
+ return `${hours}:${paddedMinutes} ${isAM ? 'AM' : 'PM'}`;
2984
+ };
2985
+ /*------------------------------------------------------------------------*/
2986
+ /* ------------------------------- Render ------------------------------- */
2987
+ /*------------------------------------------------------------------------*/
2988
+ /*----------------------------------------*/
2989
+ /* --------------- Main UI -------------- */
2990
+ /*----------------------------------------*/
2991
+ // Generate list of time options
2992
+ const times = [];
2993
+ for (let time = 0; time < 24 * 60; time += intervalMin) {
2994
+ times.push(formatTime(time));
2995
+ }
2996
+ // Currently selected time in minutes since midnight
2997
+ const selectedTimeMin = hour * 60 + minute;
2998
+ // Create choice options
2999
+ const timeOptions = times.map((timeString, timeIndex) => {
3000
+ const numMinutesForChoice = timeIndex * intervalMin;
3001
+ // Render the option
3002
+ return (React__default.createElement("option", { key: numMinutesForChoice, value: numMinutesForChoice, "aria-label": `choose ${timeString}` }, timeString));
3003
+ });
3004
+ return (React__default.createElement("div", { className: "SimpleTimeChooser-container", "aria-label": `time chooser with selected time: ${formatTime(selectedTimeMin)}` },
3005
+ React__default.createElement("select", { "aria-label": `time for ${ariaLabel}`, className: "custom-select d-inline-block", style: { width: 'auto' }, id: `SimpleTimeChooser-${name}-time`, value: selectedTimeMin, onChange: (e) => {
3006
+ // Parse selector value (string)
3007
+ const newTime = Number.parseInt(e.target.value, 10);
3008
+ // Convert minutes since midnight to hour and minute
3009
+ const timeInfo = convertMinSinceMidnightToHoursAndMin(newTime);
3010
+ // Notify parent
3011
+ onChange(timeInfo.hours, timeInfo.minutes);
3012
+ } }, timeOptions)));
2732
3013
  };
2733
3014
 
2734
3015
  /**
@@ -4825,14 +5106,14 @@ const LogReviewer = (props) => {
4825
5106
  if (expandedFilterDrawer) {
4826
5107
  if (expandedFilterDrawer === FilterDrawer.Date) {
4827
5108
  filterDrawer = (React__default.createElement(TabBox, { title: "Date" },
4828
- React__default.createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
5109
+ React__default.createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, dontAllowFuture: true, numMonthsToShow: 36, onChange: (month, day, year) => {
4829
5110
  dateFilterState.startDate = { month, day, year };
4830
5111
  handleDateRangeUpdated(dateFilterState);
4831
5112
  } }),
4832
5113
  ' ',
4833
5114
  "to",
4834
5115
  ' ',
4835
- React__default.createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
5116
+ React__default.createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, dontAllowFuture: true, numMonthsToShow: 12, onChange: (month, day, year) => {
4836
5117
  if (year < dateFilterState.startDate.year
4837
5118
  || (year === dateFilterState.startDate.year
4838
5119
  && month < dateFilterState.startDate.month)
@@ -15312,24 +15593,6 @@ const padDecimalZeros = (num, numDigits) => {
15312
15593
  return out;
15313
15594
  };
15314
15595
 
15315
- /**
15316
- * Pad a number with zeros on the left (e.g. 5 becomes 05 with 2 digit padding)
15317
- * @author Gabe Abrams
15318
- * @param num the number to pad
15319
- * @param numDigits the minimum number of digits before the decimal
15320
- * @returns padded number
15321
- */
15322
- const padZerosLeft = (num, numDigits) => {
15323
- // Convert to string
15324
- let out = String(num);
15325
- // Add zeros
15326
- while (out.split('.')[0].length < numDigits) {
15327
- out = `0${out}`;
15328
- }
15329
- // Return
15330
- return out;
15331
- };
15332
-
15333
15596
  /**
15334
15597
  * Stub a server endpoint response
15335
15598
  * @author Gabe Abrams
@@ -16200,5 +16463,5 @@ var DayOfWeek;
16200
16463
  })(DayOfWeek || (DayOfWeek = {}));
16201
16464
  var DayOfWeek$1 = DayOfWeek;
16202
16465
 
16203
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogLevel$1 as LogLevel, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getWordCount, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
16466
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogLevel$1 as LogLevel, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, SimpleTimeChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getWordCount, idify, initClient, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
16204
16467
  //# sourceMappingURL=index.js.map