dce-reactkit 5.0.8 → 5.0.10
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 +304 -16
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/helpers/useBackButton.d.ts +20 -0
- package/dist/cjs/types/index.d.ts +5 -2
- package/dist/cjs/types/types/BackButtonController.d.ts +40 -0
- package/dist/cjs/types/types/BackState.d.ts +11 -0
- package/dist/esm/index.js +299 -18
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/helpers/useBackButton.d.ts +20 -0
- package/dist/esm/types/index.d.ts +5 -2
- package/dist/esm/types/types/BackButtonController.d.ts +40 -0
- package/dist/esm/types/types/BackState.d.ts +11 -0
- package/dist/index.d.ts +71 -2
- package/package.json +2 -2
- package/src/components/ItemPicker/NestableItemList.tsx +44 -42
- package/src/helpers/useBackButton.tsx +352 -0
- package/src/index.ts +9 -0
- package/src/types/BackButtonController.ts +43 -0
- package/src/types/BackState.ts +15 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BackButtonController from '../types/BackButtonController';
|
|
2
|
+
/**
|
|
3
|
+
* Controller for driving back navigation from anywhere in the app. Requires
|
|
4
|
+
* useBackButton to have been called in the top-level app
|
|
5
|
+
* @author Yuen Ler Chow
|
|
6
|
+
*/
|
|
7
|
+
export declare const backButtonController: BackButtonController;
|
|
8
|
+
/**
|
|
9
|
+
* Hook that makes the browser's back button navigate within the app instead of
|
|
10
|
+
* leaving it. Call this once in your top-level app, then use
|
|
11
|
+
* backButtonController to enter subpanels and describe their state.
|
|
12
|
+
*
|
|
13
|
+
* Assumes a single level of navigation: one home screen plus subpanels that the
|
|
14
|
+
* user returns home from.
|
|
15
|
+
* @author Yuen Ler Chow
|
|
16
|
+
* @param handleGoHomeFunc handler that performs the app state changes required
|
|
17
|
+
* to return to the home screen
|
|
18
|
+
*/
|
|
19
|
+
declare const useBackButton: (handleGoHomeFunc: () => void) => void;
|
|
20
|
+
export default useBackButton;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, LOG_REVIEW_GET_LOGS_ROUTE, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, getMondayOfTimestamp, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, extractProp, compareArraysByProp, getLocalTimeInfo, genCommaList, validateEmail, validatePhoneNumber, validateString, idify, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, cloneDeep, getTimestampFromTimeInfoInET, spaceAtCapitals, ParamType, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, CommonKitErrorCode, LogReviewerFilterState } from 'dce-commonkit';
|
|
1
|
+
import { ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, LOG_REVIEW_GET_LOGS_ROUTE, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, ceilToNumDecimals, convertDateKeyToDateInfo, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, getMondayOfTimestamp, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, extractProp, compareArraysByProp, getLocalTimeInfo, genCommaList, validateEmail, validatePhoneNumber, validateString, idify, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, cloneDeep, getTimestampFromTimeInfoInET, spaceAtCapitals, ParamType, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, CommonKitErrorCode, LogReviewerFilterState } from 'dce-commonkit';
|
|
2
2
|
import AppWrapper, { alert, prompt, confirm, showFatalError, addFatalErrorHandler, leaveToURL } from './components/AppWrapper';
|
|
3
3
|
import LoadingSpinner from './components/LoadingSpinner';
|
|
4
4
|
import ErrorBox from './components/ErrorBox';
|
|
@@ -37,6 +37,7 @@ import isMobileOrTablet from './helpers/isMobileOrTablet';
|
|
|
37
37
|
import makeLinksClickable from './helpers/makeLinksClickable';
|
|
38
38
|
import combineClassNames from './helpers/combineClassNames';
|
|
39
39
|
import useForceRender from './helpers/useForceRender';
|
|
40
|
+
import useBackButton, { backButtonController } from './helpers/useBackButton';
|
|
40
41
|
import isSelectAdmin from './helpers/isSelectAdmin';
|
|
41
42
|
import ModalButtonType from './types/ModalButtonType';
|
|
42
43
|
import ModalSize from './types/ModalSize';
|
|
@@ -45,8 +46,10 @@ import Variant from './types/Variant';
|
|
|
45
46
|
import IntelliTableColumn from './types/IntelliTableColumn';
|
|
46
47
|
import DropdownItemType from './types/DropdownItemType';
|
|
47
48
|
import ProgressBarSize from './types/ProgressBarSize';
|
|
49
|
+
import BackState from './types/BackState';
|
|
50
|
+
import BackButtonController from './types/BackButtonController';
|
|
48
51
|
import PickableItem from './components/ItemPicker/types/PickableItem';
|
|
49
52
|
import DBEntry from './components/DBEntryManagerPanel/types/DBEntry';
|
|
50
53
|
import DBEntryField from './components/DBEntryManagerPanel/types/DBEntryField';
|
|
51
54
|
import DBEntryFieldType from './components/DBEntryManagerPanel/types/DBEntryFieldType';
|
|
52
|
-
export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, SimpleMonthChooser, SimpleTimeChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, ProgressBar, FakeProgressBar, alert, prompt, confirm, showFatalError, DynamicWord, stubServerEndpoint, canReviewLogs, isMobileOrTablet, makeLinksClickable, isSelectAdmin, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, setClientEventMetadataPopulator, ModalButtonType, ModalSize, ModalType, Variant, IntelliTableColumn, DropdownItemType, LogReviewerFilterState, ProgressBarSize, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, LOG_REVIEW_GET_LOGS_ROUTE, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, getMondayOfTimestamp, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, extractProp, compareArraysByProp, getLocalTimeInfo, genCommaList, validateEmail, validatePhoneNumber, validateString, idify, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, cloneDeep, getTimestampFromTimeInfoInET, spaceAtCapitals, ParamType, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, CommonKitErrorCode, };
|
|
55
|
+
export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, SimpleMonthChooser, SimpleTimeChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, DBEntryManagerPanel, Tooltip, ToggleSwitch, AutoscrollToBottomContainer, MultiSwitch, Dropdown, ProgressBar, FakeProgressBar, alert, prompt, confirm, showFatalError, DynamicWord, stubServerEndpoint, canReviewLogs, isMobileOrTablet, makeLinksClickable, isSelectAdmin, initClient, visitServerEndpoint, logClientEvent, addFatalErrorHandler, leaveToURL, combineClassNames, useForceRender, useBackButton, backButtonController, setClientEventMetadataPopulator, ModalButtonType, ModalSize, ModalType, Variant, IntelliTableColumn, DropdownItemType, LogReviewerFilterState, ProgressBarSize, BackState, BackButtonController, PickableItem, DBEntry, DBEntryField, DBEntryFieldType, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_ROUTE_PATH, LOG_REVIEW_STATUS_ROUTE, LOG_REVIEW_GET_LOGS_ROUTE, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, ceilToNumDecimals, convertDateKeyToDateInfo, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, getMondayOfTimestamp, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, extractProp, compareArraysByProp, getLocalTimeInfo, genCommaList, validateEmail, validatePhoneNumber, validateString, idify, prefixWithAOrAn, everyAsync, filterAsync, forEachAsync, mapAsync, someAsync, capitalize, shuffleArray, getWordCount, cloneDeep, getTimestampFromTimeInfoInET, spaceAtCapitals, ParamType, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, LogFunction, LogTypeSpecificInfo, LogMainInfo, LogSourceSpecificInfo, LogLevel, CommonKitErrorCode, };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import BackState from './BackState';
|
|
2
|
+
/**
|
|
3
|
+
* Controller for the back button, used to drive back navigation from anywhere
|
|
4
|
+
* in the app. Pass this to subpanels so they can describe their state and
|
|
5
|
+
* send the user home.
|
|
6
|
+
* @author Yuen Ler Chow
|
|
7
|
+
*/
|
|
8
|
+
type BackButtonController = {
|
|
9
|
+
/**
|
|
10
|
+
* Call this when the user navigates to a child of the home screen (something
|
|
11
|
+
* they can come back from)
|
|
12
|
+
*/
|
|
13
|
+
onSubpanelEntered: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Send the user back to the home screen
|
|
16
|
+
* @param [force] if true, go home immediately without checking the subpanel
|
|
17
|
+
* state (no confirmation, not blocked). If falsy, nothing happens when
|
|
18
|
+
* blocked and confirmation is required when there are unsaved changes
|
|
19
|
+
*/
|
|
20
|
+
goHome: (force?: boolean) => Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Set the state of the current subpanel, which determines what happens when
|
|
23
|
+
* the user tries to go back
|
|
24
|
+
* @param newSubpanelState the new state of the subpanel
|
|
25
|
+
*/
|
|
26
|
+
setSubpanelState: (newSubpanelState: BackState) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Set the confirmation message shown if the user tries to go back while there
|
|
29
|
+
* are unsaved changes (cleared upon returning to the home screen)
|
|
30
|
+
* @param message the message to show
|
|
31
|
+
*/
|
|
32
|
+
setCustomUnsavedChangesMessage: (message: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Set the message shown if the user tries to go back while blocked (cleared
|
|
35
|
+
* upon returning to the home screen)
|
|
36
|
+
* @param message the message to show
|
|
37
|
+
*/
|
|
38
|
+
setCustomBlockedMessage: (message: string) => void;
|
|
39
|
+
};
|
|
40
|
+
export default BackButtonController;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State of the current subpanel, determining what happens when the user tries
|
|
3
|
+
* to go back to the home screen
|
|
4
|
+
* @author Yuen Ler Chow
|
|
5
|
+
*/
|
|
6
|
+
declare enum BackState {
|
|
7
|
+
Normal = "Normal",
|
|
8
|
+
UnsavedChanges = "UnsavedChanges",
|
|
9
|
+
Blocked = "Blocked"
|
|
10
|
+
}
|
|
11
|
+
export default BackState;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CommonKitErrorCode, waitMs, ErrorWithCode, LOG_ROUTE_PATH, LogBuiltInMetadata, LogLevel, getOrdinal, getMonthName, getTimeInfoInET, padZerosLeft, ParamType, roundToNumDecimals, genCSV, LogAction, cloneDeep, LogType, LogSource, getHumanReadableDate, LOG_REVIEW_GET_LOGS_ROUTE, idify, padDecimalZeros, LOG_REVIEW_STATUS_ROUTE, SELECT_ADMIN_CHECK_ROUTE } from 'dce-commonkit';
|
|
2
|
-
export { CommonKitErrorCode, DAY_IN_MS, DayOfWeek, ErrorWithCode, HOUR_IN_MS, LOG_REVIEW_GET_LOGS_ROUTE, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LogAction, LogBuiltInMetadata, LogLevel, LogSource, LogType, MINUTE_IN_MS, ParamType, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, capitalize, ceilToNumDecimals, cloneDeep, compareArraysByProp, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMondayOfTimestamp, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getTimestampFromTimeInfoInET, getWordCount, idify, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, shuffleArray, someAsync, spaceAtCapitals, startMinWait, stringsToHumanReadableList, sum, validateEmail, validatePhoneNumber, validateString, waitMs } from 'dce-commonkit';
|
|
2
|
+
export { CommonKitErrorCode, DAY_IN_MS, DayOfWeek, ErrorWithCode, HOUR_IN_MS, LOG_REVIEW_GET_LOGS_ROUTE, LOG_REVIEW_ROUTE_PATH_PREFIX, LOG_REVIEW_STATUS_ROUTE, LOG_ROUTE_PATH, LogAction, LogBuiltInMetadata, LogLevel, LogSource, LogType, MINUTE_IN_MS, ParamType, SELECT_ADMIN_CHECK_ROUTE, abbreviate, avg, capitalize, ceilToNumDecimals, cloneDeep, compareArraysByProp, convertDateKeyToDateInfo, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, getHumanReadableDate, getLocalTimeInfo, getMondayOfTimestamp, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, getTimestampFromTimeInfoInET, getWordCount, idify, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, shuffleArray, someAsync, spaceAtCapitals, startMinWait, stringsToHumanReadableList, sum, validateEmail, validatePhoneNumber, validateString, waitMs } from 'dce-commonkit';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import React__default, { useState, useRef, useEffect, useReducer, forwardRef, useContext, useLayoutEffect, createContext, useMemo, useCallback, Component, Fragment } from 'react';
|
|
5
5
|
import { faExclamationTriangle, faCircle, faHourglassEnd, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faArrowLeft, faArrowRight, faCalendar, faTag, faHammer, faList, faTimes, faSearch, faSave, faTrash, faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
|
|
@@ -2860,22 +2860,23 @@ const NestableItemList = (props) => {
|
|
|
2860
2860
|
onChanged(changeChecked(item.id, checked, items));
|
|
2861
2861
|
}, ariaLabel: `Select ${accessibleName}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
|
|
2862
2862
|
return (React__default.createElement("div", { key: item.id },
|
|
2863
|
-
React__default.createElement("
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
React__default.createElement(
|
|
2878
|
-
|
|
2863
|
+
React__default.createElement("div", { className: "NestableItemList-item d-flex align-items-center" },
|
|
2864
|
+
React__default.createElement("span", { className: "NestableItemList-dropdown-button-container", style: {
|
|
2865
|
+
minWidth: '2rem',
|
|
2866
|
+
} }, item.isGroup && (React__default.createElement("button", { className: `NestableItemList-dropdown-button NestableItemList-dropdown-button-${item.id}`, style: {
|
|
2867
|
+
border: 0,
|
|
2868
|
+
backgroundColor: 'transparent',
|
|
2869
|
+
}, type: "button", onClick: () => {
|
|
2870
|
+
dispatch({
|
|
2871
|
+
type: ActionType$9.ToggleChild,
|
|
2872
|
+
id: item.id,
|
|
2873
|
+
});
|
|
2874
|
+
}, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${accessibleName}` },
|
|
2875
|
+
React__default.createElement(FontAwesomeIcon, { icon: childExpanded[item.id] ? faChevronDown : faChevronRight })))),
|
|
2876
|
+
item.tooltip
|
|
2877
|
+
? (React__default.createElement(Tooltip, { text: item.tooltip },
|
|
2878
|
+
React__default.createElement("span", { className: "d-inline-block" }, checkbox)))
|
|
2879
|
+
: checkbox),
|
|
2879
2880
|
(item.isGroup && childExpanded[item.id]) && (React__default.createElement("div", { className: "NestableItemList-children-container", style: {
|
|
2880
2881
|
paddingLeft: '2.2rem',
|
|
2881
2882
|
} },
|
|
@@ -14751,6 +14752,286 @@ const useForceRender = (useReducer) => {
|
|
|
14751
14752
|
};
|
|
14752
14753
|
};
|
|
14753
14754
|
|
|
14755
|
+
/**
|
|
14756
|
+
* State of the current subpanel, determining what happens when the user tries
|
|
14757
|
+
* to go back to the home screen
|
|
14758
|
+
* @author Yuen Ler Chow
|
|
14759
|
+
*/
|
|
14760
|
+
var BackState;
|
|
14761
|
+
(function (BackState) {
|
|
14762
|
+
// The user can go back immediately, no confirmation required
|
|
14763
|
+
BackState["Normal"] = "Normal";
|
|
14764
|
+
// The user must confirm before going back because progress will be lost
|
|
14765
|
+
BackState["UnsavedChanges"] = "UnsavedChanges";
|
|
14766
|
+
// The user cannot go back right now because work is in progress
|
|
14767
|
+
BackState["Blocked"] = "Blocked";
|
|
14768
|
+
})(BackState || (BackState = {}));
|
|
14769
|
+
var BackState$1 = BackState;
|
|
14770
|
+
|
|
14771
|
+
/*------------------------------------------------------------------------*/
|
|
14772
|
+
/* ------------------------------ Constants ----------------------------- */
|
|
14773
|
+
/*------------------------------------------------------------------------*/
|
|
14774
|
+
// Marker stored on history entries owned by this hook
|
|
14775
|
+
const HISTORY_STATE_MARKER = { dceReactKitBackButton: true };
|
|
14776
|
+
// Default message shown when the user tries to go back while blocked
|
|
14777
|
+
const BLOCKED_TITLE = 'Cannot Go Back';
|
|
14778
|
+
const BLOCKED_MESSAGE = 'A task is currently in progress. Please try again once it finishes.';
|
|
14779
|
+
// Default message shown when the user tries to go back with unsaved changes
|
|
14780
|
+
const UNSAVED_CHANGES_TITLE = 'Abandon Changes?';
|
|
14781
|
+
const UNSAVED_CHANGES_MESSAGE = 'Any unsaved changes may be lost.';
|
|
14782
|
+
/*------------------------------------------------------------------------*/
|
|
14783
|
+
/* --------------------------- Static State ----------------------------- */
|
|
14784
|
+
/*------------------------------------------------------------------------*/
|
|
14785
|
+
// Current state, stored statically so that any subpanel can drive the back
|
|
14786
|
+
// button without prop drilling or context
|
|
14787
|
+
let state = {
|
|
14788
|
+
promptVisible: false,
|
|
14789
|
+
bypassNextPop: false,
|
|
14790
|
+
};
|
|
14791
|
+
/*------------------------------------------------------------------------*/
|
|
14792
|
+
/* ------------------------- Helper Functions --------------------------- */
|
|
14793
|
+
/*------------------------------------------------------------------------*/
|
|
14794
|
+
/**
|
|
14795
|
+
* Get the app's go-home handler, throwing if the hook has not been set up yet
|
|
14796
|
+
* @author Yuen Ler Chow
|
|
14797
|
+
* @returns handler that returns the app to its home screen
|
|
14798
|
+
*/
|
|
14799
|
+
const getHandleGoHome = () => {
|
|
14800
|
+
if (!state.handleGoHome) {
|
|
14801
|
+
throw new Error('Cannot use the back button: call useBackButton in your top-level app before using backButtonController.');
|
|
14802
|
+
}
|
|
14803
|
+
return state.handleGoHome;
|
|
14804
|
+
};
|
|
14805
|
+
/**
|
|
14806
|
+
* Return to the home screen: clear the current subpanel and run the app's
|
|
14807
|
+
* go-home handler
|
|
14808
|
+
* @author Yuen Ler Chow
|
|
14809
|
+
* @param consumeHistoryEntry if true, also step back over the history entry
|
|
14810
|
+
* that was added when the subpanel was entered
|
|
14811
|
+
*/
|
|
14812
|
+
const returnHome = (consumeHistoryEntry) => {
|
|
14813
|
+
const handleGoHome = getHandleGoHome();
|
|
14814
|
+
const wasInSubpanel = !!state.subpanel;
|
|
14815
|
+
// Dropping the subpanel clears its back state and custom messages at once
|
|
14816
|
+
state.subpanel = undefined;
|
|
14817
|
+
// Update the app
|
|
14818
|
+
handleGoHome();
|
|
14819
|
+
// Entering a subpanel added a history entry. When the user leaves via an
|
|
14820
|
+
// in-app control, that entry is still on the stack, so we step over it to keep
|
|
14821
|
+
// the browser history in sync with the app. When the browser's back button is
|
|
14822
|
+
// what brought us here, that entry has already been consumed by the browser,
|
|
14823
|
+
// and stepping back again would take the user out of the app entirely
|
|
14824
|
+
if (consumeHistoryEntry && wasInSubpanel) {
|
|
14825
|
+
state.bypassNextPop = true;
|
|
14826
|
+
window.history.back();
|
|
14827
|
+
}
|
|
14828
|
+
};
|
|
14829
|
+
/**
|
|
14830
|
+
* Keep the user in place after a back navigation that should not be allowed.
|
|
14831
|
+
* The browser's popstate event is not cancelable, so the only way to stay put
|
|
14832
|
+
* is to immediately push a new entry to replace the one that was just popped.
|
|
14833
|
+
* This has to happen synchronously while handling the pop, before awaiting
|
|
14834
|
+
* anything, otherwise the navigation has already taken effect
|
|
14835
|
+
* @author Yuen Ler Chow
|
|
14836
|
+
*/
|
|
14837
|
+
const undoPop = () => {
|
|
14838
|
+
window.history.pushState(HISTORY_STATE_MARKER, '');
|
|
14839
|
+
};
|
|
14840
|
+
/**
|
|
14841
|
+
* Tell the user that they cannot go back right now
|
|
14842
|
+
* @author Yuen Ler Chow
|
|
14843
|
+
*/
|
|
14844
|
+
const showBlockedMessage = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
14845
|
+
var _a, _b;
|
|
14846
|
+
state.promptVisible = true;
|
|
14847
|
+
yield alert(BLOCKED_TITLE, (_b = (_a = state.subpanel) === null || _a === void 0 ? void 0 : _a.customBlockedMessage) !== null && _b !== void 0 ? _b : BLOCKED_MESSAGE);
|
|
14848
|
+
state.promptVisible = false;
|
|
14849
|
+
});
|
|
14850
|
+
/**
|
|
14851
|
+
* Ask the user whether they want to leave despite having unsaved changes
|
|
14852
|
+
* @author Yuen Ler Chow
|
|
14853
|
+
* @returns true if the user wants to leave
|
|
14854
|
+
*/
|
|
14855
|
+
const askToAbandonChanges = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
14856
|
+
var _c, _d;
|
|
14857
|
+
state.promptVisible = true;
|
|
14858
|
+
const confirmed = yield confirm(UNSAVED_CHANGES_TITLE, (_d = (_c = state.subpanel) === null || _c === void 0 ? void 0 : _c.customUnsavedChangesMessage) !== null && _d !== void 0 ? _d : UNSAVED_CHANGES_MESSAGE, {
|
|
14859
|
+
confirmButtonText: 'Abandon Changes',
|
|
14860
|
+
cancelButtonText: 'Stay Here',
|
|
14861
|
+
});
|
|
14862
|
+
state.promptVisible = false;
|
|
14863
|
+
return confirmed;
|
|
14864
|
+
});
|
|
14865
|
+
/**
|
|
14866
|
+
* Handle a back navigation performed by the browser
|
|
14867
|
+
* @author Yuen Ler Chow
|
|
14868
|
+
*/
|
|
14869
|
+
const handlePopState = () => {
|
|
14870
|
+
// Back navigation that we triggered ourselves: let it through
|
|
14871
|
+
if (state.bypassNextPop) {
|
|
14872
|
+
state.bypassNextPop = false;
|
|
14873
|
+
return;
|
|
14874
|
+
}
|
|
14875
|
+
// Already on the home screen: nothing for us to intercept
|
|
14876
|
+
if (!state.subpanel) {
|
|
14877
|
+
return;
|
|
14878
|
+
}
|
|
14879
|
+
// A prompt is already on screen: stay put instead of stacking another one
|
|
14880
|
+
if (state.promptVisible) {
|
|
14881
|
+
undoPop();
|
|
14882
|
+
return;
|
|
14883
|
+
}
|
|
14884
|
+
// Blocked: stay put and explain why
|
|
14885
|
+
if (state.subpanel.backState === BackState$1.Blocked) {
|
|
14886
|
+
undoPop();
|
|
14887
|
+
showBlockedMessage();
|
|
14888
|
+
return;
|
|
14889
|
+
}
|
|
14890
|
+
// Unsaved changes: stay put until the user confirms. The pop is undone
|
|
14891
|
+
// asynchronously here, before awaiting the confirmation, so that the user
|
|
14892
|
+
// remains in the subpanel while they decide
|
|
14893
|
+
if (state.subpanel.backState === BackState$1.UnsavedChanges) {
|
|
14894
|
+
undoPop();
|
|
14895
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
14896
|
+
if (yield askToAbandonChanges()) {
|
|
14897
|
+
returnHome(true);
|
|
14898
|
+
}
|
|
14899
|
+
}))();
|
|
14900
|
+
return;
|
|
14901
|
+
}
|
|
14902
|
+
// Normal: allow it. The browser already consumed the history entry, so there
|
|
14903
|
+
// is nothing left for us to step over
|
|
14904
|
+
returnHome(false);
|
|
14905
|
+
};
|
|
14906
|
+
/*------------------------------------------------------------------------*/
|
|
14907
|
+
/* ------------------------------ Controller ---------------------------- */
|
|
14908
|
+
/*------------------------------------------------------------------------*/
|
|
14909
|
+
/**
|
|
14910
|
+
* Controller for driving back navigation from anywhere in the app. Requires
|
|
14911
|
+
* useBackButton to have been called in the top-level app
|
|
14912
|
+
* @author Yuen Ler Chow
|
|
14913
|
+
*/
|
|
14914
|
+
const backButtonController = {
|
|
14915
|
+
/**
|
|
14916
|
+
* Call this when the user navigates to a child of the home screen (something
|
|
14917
|
+
* they can come back from)
|
|
14918
|
+
* @author Yuen Ler Chow
|
|
14919
|
+
*/
|
|
14920
|
+
onSubpanelEntered: () => {
|
|
14921
|
+
getHandleGoHome();
|
|
14922
|
+
state.subpanel = {
|
|
14923
|
+
backState: BackState$1.Normal,
|
|
14924
|
+
};
|
|
14925
|
+
// Add a history entry to come back to, so that the next back navigation is
|
|
14926
|
+
// intercepted instead of leaving the app
|
|
14927
|
+
window.history.pushState(HISTORY_STATE_MARKER, '');
|
|
14928
|
+
},
|
|
14929
|
+
/**
|
|
14930
|
+
* Send the user back to the home screen
|
|
14931
|
+
* @author Yuen Ler Chow
|
|
14932
|
+
* @param [force] if true, go home immediately without checking the subpanel
|
|
14933
|
+
* state. If falsy, nothing happens while blocked and confirmation is
|
|
14934
|
+
* required when there are unsaved changes
|
|
14935
|
+
*/
|
|
14936
|
+
goHome: (force) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14937
|
+
var _e, _f;
|
|
14938
|
+
getHandleGoHome();
|
|
14939
|
+
// Unless the caller is forcing the navigation, the subpanel's state decides
|
|
14940
|
+
// whether the user may leave: a blocked subpanel refuses and explains why,
|
|
14941
|
+
// and one with unsaved changes leaves only if the user confirms
|
|
14942
|
+
if (!force) {
|
|
14943
|
+
// A prompt is already asking the user this same question
|
|
14944
|
+
if (state.promptVisible) {
|
|
14945
|
+
return;
|
|
14946
|
+
}
|
|
14947
|
+
if (((_e = state.subpanel) === null || _e === void 0 ? void 0 : _e.backState) === BackState$1.Blocked) {
|
|
14948
|
+
yield showBlockedMessage();
|
|
14949
|
+
return;
|
|
14950
|
+
}
|
|
14951
|
+
if (((_f = state.subpanel) === null || _f === void 0 ? void 0 : _f.backState) === BackState$1.UnsavedChanges) {
|
|
14952
|
+
const confirmed = yield askToAbandonChanges();
|
|
14953
|
+
if (!confirmed) {
|
|
14954
|
+
return;
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
}
|
|
14958
|
+
returnHome(true);
|
|
14959
|
+
}),
|
|
14960
|
+
/**
|
|
14961
|
+
* Set the state of the current subpanel, which determines what happens when
|
|
14962
|
+
* the user tries to go back. Ignored while on the home screen, where there
|
|
14963
|
+
* is no back navigation to describe
|
|
14964
|
+
* @author Yuen Ler Chow
|
|
14965
|
+
* @param newSubpanelState the new state of the subpanel
|
|
14966
|
+
*/
|
|
14967
|
+
setSubpanelState: (newSubpanelState) => {
|
|
14968
|
+
if (!state.subpanel) {
|
|
14969
|
+
return;
|
|
14970
|
+
}
|
|
14971
|
+
state.subpanel.backState = newSubpanelState;
|
|
14972
|
+
},
|
|
14973
|
+
/**
|
|
14974
|
+
* Set the confirmation message shown if the user tries to go back while there
|
|
14975
|
+
* are unsaved changes. Cleared upon returning to the home screen
|
|
14976
|
+
* @author Yuen Ler Chow
|
|
14977
|
+
* @param message the message to show
|
|
14978
|
+
*/
|
|
14979
|
+
setCustomUnsavedChangesMessage: (message) => {
|
|
14980
|
+
if (!state.subpanel) {
|
|
14981
|
+
return;
|
|
14982
|
+
}
|
|
14983
|
+
state.subpanel.customUnsavedChangesMessage = message;
|
|
14984
|
+
},
|
|
14985
|
+
/**
|
|
14986
|
+
* Set the message shown if the user tries to go back while blocked. Cleared
|
|
14987
|
+
* upon returning to the home screen
|
|
14988
|
+
* @author Yuen Ler Chow
|
|
14989
|
+
* @param message the message to show
|
|
14990
|
+
*/
|
|
14991
|
+
setCustomBlockedMessage: (message) => {
|
|
14992
|
+
if (!state.subpanel) {
|
|
14993
|
+
return;
|
|
14994
|
+
}
|
|
14995
|
+
state.subpanel.customBlockedMessage = message;
|
|
14996
|
+
},
|
|
14997
|
+
};
|
|
14998
|
+
/*------------------------------------------------------------------------*/
|
|
14999
|
+
/* --------------------------------- Hook ------------------------------- */
|
|
15000
|
+
/*------------------------------------------------------------------------*/
|
|
15001
|
+
/**
|
|
15002
|
+
* Hook that makes the browser's back button navigate within the app instead of
|
|
15003
|
+
* leaving it. Call this once in your top-level app, then use
|
|
15004
|
+
* backButtonController to enter subpanels and describe their state.
|
|
15005
|
+
*
|
|
15006
|
+
* Assumes a single level of navigation: one home screen plus subpanels that the
|
|
15007
|
+
* user returns home from.
|
|
15008
|
+
* @author Yuen Ler Chow
|
|
15009
|
+
* @param handleGoHomeFunc handler that performs the app state changes required
|
|
15010
|
+
* to return to the home screen
|
|
15011
|
+
*/
|
|
15012
|
+
const useBackButton = (handleGoHomeFunc) => {
|
|
15013
|
+
// Store the handler on every render so that the listener below, which is only
|
|
15014
|
+
// registered once, always calls the app's current version of it
|
|
15015
|
+
state.handleGoHome = handleGoHomeFunc;
|
|
15016
|
+
// The popstate listener has to be attached to the window, which is outside of
|
|
15017
|
+
// React, so an effect is used to add it when the app mounts and remove it if
|
|
15018
|
+
// the app ever unmounts. The empty dependency array keeps this to a single
|
|
15019
|
+
// listener for the lifetime of the app instead of one per render
|
|
15020
|
+
useEffect(() => {
|
|
15021
|
+
// Mark the current entry as the home entry
|
|
15022
|
+
window.history.replaceState(HISTORY_STATE_MARKER, '');
|
|
15023
|
+
window.addEventListener('popstate', handlePopState);
|
|
15024
|
+
return () => {
|
|
15025
|
+
window.removeEventListener('popstate', handlePopState);
|
|
15026
|
+
// Clear the static state so that a remount starts from scratch
|
|
15027
|
+
state = {
|
|
15028
|
+
promptVisible: false,
|
|
15029
|
+
bypassNextPop: false,
|
|
15030
|
+
};
|
|
15031
|
+
};
|
|
15032
|
+
}, []);
|
|
15033
|
+
};
|
|
15034
|
+
|
|
14754
15035
|
/*------------------------------------------------------------------------*/
|
|
14755
15036
|
/* ------------------------------- Caching ------------------------------ */
|
|
14756
15037
|
/*------------------------------------------------------------------------*/
|
|
@@ -14787,5 +15068,5 @@ const isSelectAdmin = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
14787
15068
|
}
|
|
14788
15069
|
});
|
|
14789
15070
|
|
|
14790
|
-
export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, FakeProgressBar, IntelliTable, ItemPicker, LoadingSpinner, LogReviewer, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, PopFailureMark, PopPendingMark, PopSuccessMark, ProgressBar, ProgressBarSize$1 as ProgressBarSize, RadioButton, SimpleDateChooser, SimpleMonthChooser, SimpleTimeChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, addFatalErrorHandler, alert, canReviewLogs, combineClassNames, confirm, initClient, isMobileOrTablet, isSelectAdmin, leaveToURL, logClientEvent, makeLinksClickable, prompt, setClientEventMetadataPopulator, showFatalError, stubServerEndpoint, useForceRender, visitServerEndpoint };
|
|
15071
|
+
export { AppWrapper, AutoscrollToBottomContainer, BackState$1 as BackState, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, FakeProgressBar, IntelliTable, ItemPicker, LoadingSpinner, LogReviewer, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, PopFailureMark, PopPendingMark, PopSuccessMark, ProgressBar, ProgressBarSize$1 as ProgressBarSize, RadioButton, SimpleDateChooser, SimpleMonthChooser, SimpleTimeChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, addFatalErrorHandler, alert, backButtonController, canReviewLogs, combineClassNames, confirm, initClient, isMobileOrTablet, isSelectAdmin, leaveToURL, logClientEvent, makeLinksClickable, prompt, setClientEventMetadataPopulator, showFatalError, stubServerEndpoint, useBackButton, useForceRender, visitServerEndpoint };
|
|
14791
15072
|
//# sourceMappingURL=index.js.map
|