dce-reactkit 3.4.0 → 3.5.1-beta.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/.eslintrc.js +2 -0
- package/.vscode/settings.json +1 -0
- package/dist/cjs/index.js +36372 -191
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +10 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +34 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +9 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/index.d.ts +25 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +8 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +49 -0
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +12 -0
- package/dist/cjs/types/helpers/addDBEditorEndpoints.d.ts +40 -0
- package/dist/cjs/types/helpers/canReviewLogs.d.ts +0 -5
- package/dist/cjs/types/index.d.ts +6 -1
- package/dist/esm/index.js +36587 -427
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +10 -0
- package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +34 -0
- package/dist/esm/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +9 -0
- package/dist/esm/types/components/DBEntryManagerPanel/index.d.ts +25 -0
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +8 -0
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +49 -0
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +12 -0
- package/dist/esm/types/helpers/addDBEditorEndpoints.d.ts +40 -0
- package/dist/esm/types/helpers/canReviewLogs.d.ts +0 -5
- package/dist/esm/types/index.d.ts +6 -1
- package/dist/index.d.ts +166 -35
- package/package.json +16 -12
- package/src/client/initClient.tsx +23 -31
- package/src/components/AppWrapper.tsx +1 -1
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.tsx +303 -0
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/index.tsx +737 -0
- package/src/components/DBEntryManagerPanel/helpers/generateEndpointPath.ts +16 -0
- package/src/components/DBEntryManagerPanel/index.tsx +522 -0
- package/src/components/DBEntryManagerPanel/types/DBEntry.ts +7 -0
- package/src/components/DBEntryManagerPanel/types/DBEntryField.ts +94 -0
- package/src/components/DBEntryManagerPanel/types/DBEntryFieldType.ts +18 -0
- package/src/components/ItemPicker/index.test.tsx +10 -10
- package/src/helpers/addDBEditorEndpoints.ts +128 -0
- package/src/helpers/canReviewLogs.ts +0 -20
- package/src/helpers/genCSV.ts +2 -5
- package/src/helpers/{getOrdinal.ts → getOrdinal.tsx} +0 -1
- package/src/index.ts +11 -9
- package/tsconfig.json +2 -6
- package/dist/cjs/types/helpers/abbreviate.test.d.ts +0 -5
- package/dist/cjs/types/helpers/avg.test.d.ts +0 -5
- package/dist/cjs/types/helpers/canReviewLogs.test.d.ts +0 -5
- package/dist/cjs/types/helpers/ceilToNumDecimals.test.d.ts +0 -5
- package/dist/cjs/types/helpers/compareArraysByProp.test.d.ts +0 -5
- package/dist/cjs/types/helpers/extractProp.test.d.ts +0 -5
- package/dist/cjs/types/helpers/floorToNumDecimals.test.d.ts +0 -5
- package/dist/cjs/types/helpers/forceNumIntoBounds.test.d.ts +0 -5
- package/dist/cjs/types/helpers/genCSV.test.d.ts +0 -5
- package/dist/cjs/types/helpers/genCommaList.d.ts +0 -12
- package/dist/cjs/types/helpers/genCommaList.test.d.ts +0 -1
- package/dist/cjs/types/helpers/getHumanReadableDate.test.d.ts +0 -5
- package/dist/cjs/types/helpers/getLocalTimeInfo.test.d.ts +0 -6
- package/dist/cjs/types/helpers/getMonthName.test.d.ts +0 -5
- package/dist/cjs/types/helpers/getOrdinal.test.d.ts +0 -5
- package/dist/cjs/types/helpers/getPartOfDay.test.d.ts +0 -5
- package/dist/cjs/types/helpers/getTimeInfoInET.test.d.ts +0 -5
- package/dist/cjs/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +0 -10
- package/dist/cjs/types/helpers/validators/shared/helpers/validateRegex.d.ts +0 -19
- package/dist/cjs/types/helpers/validators/shared/types/ValidationResult.d.ts +0 -12
- package/dist/cjs/types/helpers/validators/validateEmail.d.ts +0 -11
- package/dist/cjs/types/helpers/validators/validateEmail.test.d.ts +0 -1
- package/dist/cjs/types/helpers/validators/validatePhoneNumber.d.ts +0 -11
- package/dist/cjs/types/helpers/validators/validatePhoneNumber.test.d.ts +0 -1
- package/dist/cjs/types/helpers/validators/validateString.d.ts +0 -22
- package/dist/cjs/types/helpers/validators/validateString.test.d.ts +0 -1
- package/dist/esm/types/helpers/abbreviate.test.d.ts +0 -5
- package/dist/esm/types/helpers/avg.test.d.ts +0 -5
- package/dist/esm/types/helpers/canReviewLogs.test.d.ts +0 -5
- package/dist/esm/types/helpers/ceilToNumDecimals.test.d.ts +0 -5
- package/dist/esm/types/helpers/compareArraysByProp.test.d.ts +0 -5
- package/dist/esm/types/helpers/extractProp.test.d.ts +0 -5
- package/dist/esm/types/helpers/floorToNumDecimals.test.d.ts +0 -5
- package/dist/esm/types/helpers/forceNumIntoBounds.test.d.ts +0 -5
- package/dist/esm/types/helpers/genCSV.test.d.ts +0 -5
- package/dist/esm/types/helpers/genCommaList.d.ts +0 -12
- package/dist/esm/types/helpers/genCommaList.test.d.ts +0 -1
- package/dist/esm/types/helpers/getHumanReadableDate.test.d.ts +0 -5
- package/dist/esm/types/helpers/getLocalTimeInfo.test.d.ts +0 -6
- package/dist/esm/types/helpers/getMonthName.test.d.ts +0 -5
- package/dist/esm/types/helpers/getOrdinal.test.d.ts +0 -5
- package/dist/esm/types/helpers/getPartOfDay.test.d.ts +0 -5
- package/dist/esm/types/helpers/getTimeInfoInET.test.d.ts +0 -5
- package/dist/esm/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +0 -10
- package/dist/esm/types/helpers/validators/shared/helpers/validateRegex.d.ts +0 -19
- package/dist/esm/types/helpers/validators/shared/types/ValidationResult.d.ts +0 -12
- package/dist/esm/types/helpers/validators/validateEmail.d.ts +0 -11
- package/dist/esm/types/helpers/validators/validateEmail.test.d.ts +0 -1
- package/dist/esm/types/helpers/validators/validatePhoneNumber.d.ts +0 -11
- package/dist/esm/types/helpers/validators/validatePhoneNumber.test.d.ts +0 -1
- package/dist/esm/types/helpers/validators/validateString.d.ts +0 -22
- package/dist/esm/types/helpers/validators/validateString.test.d.ts +0 -1
- package/src/helpers/abbreviate.test.ts +0 -26
- package/src/helpers/avg.test.ts +0 -24
- package/src/helpers/canReviewLogs.test.ts +0 -67
- package/src/helpers/ceilToNumDecimals.test.ts +0 -39
- package/src/helpers/compareArraysByProp.test.ts +0 -102
- package/src/helpers/extractProp.test.ts +0 -34
- package/src/helpers/floorToNumDecimals.test.ts +0 -39
- package/src/helpers/forceNumIntoBounds.test.ts +0 -34
- package/src/helpers/genCSV.test.ts +0 -61
- package/src/helpers/genCommaList.test.ts +0 -57
- package/src/helpers/genCommaList.ts +0 -21
- package/src/helpers/getHumanReadableDate.test.ts +0 -29
- package/src/helpers/getLocalTimeInfo.test.ts +0 -48
- package/src/helpers/getMonthName.test.ts +0 -79
- package/src/helpers/getOrdinal.test.ts +0 -84
- package/src/helpers/getPartOfDay.test.ts +0 -56
- package/src/helpers/getTimeInfoInET.test.ts +0 -72
- package/src/helpers/validators/shared/constants/ERROR_MESSAGES.ts +0 -18
- package/src/helpers/validators/shared/helpers/validateRegex.ts +0 -47
- package/src/helpers/validators/shared/types/ValidationResult.ts +0 -20
- package/src/helpers/validators/validateEmail.test.ts +0 -110
- package/src/helpers/validators/validateEmail.ts +0 -47
- package/src/helpers/validators/validatePhoneNumber.test.ts +0 -94
- package/src/helpers/validators/validatePhoneNumber.ts +0 -49
- package/src/helpers/validators/validateString.test.ts +0 -175
- package/src/helpers/validators/validateString.ts +0 -171
- /package/src/helpers/{abbreviate.ts → abbreviate.tsx} +0 -0
- /package/src/helpers/{avg.ts → avg.tsx} +0 -0
- /package/src/helpers/{ceilToNumDecimals.ts → ceilToNumDecimals.tsx} +0 -0
- /package/src/helpers/{floorToNumDecimals.ts → floorToNumDecimals.tsx} +0 -0
- /package/src/helpers/{forceNumIntoBounds.ts → forceNumIntoBounds.tsx} +0 -0
- /package/src/helpers/{getLocalTimeInfo.ts → getLocalTimeInfo.tsx} +0 -0
- /package/src/helpers/{getTimeInfoInET.ts → getTimeInfoInET.tsx} +0 -0
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dce-reactkit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1-beta.1",
|
|
4
4
|
"description": "Shared components for Harvard DCE apps",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "rm -rf dist && rollup -c",
|
|
10
|
-
"test": "jest
|
|
10
|
+
"test": "jest"
|
|
11
11
|
},
|
|
12
12
|
"jest": {
|
|
13
13
|
"preset": "ts-jest",
|
|
@@ -28,25 +28,26 @@
|
|
|
28
28
|
"@fortawesome/free-solid-svg-icons": "^x.x.x",
|
|
29
29
|
"@fortawesome/react-fontawesome": "^x.x.x",
|
|
30
30
|
"bootstrap": "^5.x.x",
|
|
31
|
-
"react": "^x.x.x"
|
|
31
|
+
"react": "^x.x.x",
|
|
32
|
+
"express": "^x.x.x"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@rollup/plugin-commonjs": "^22.0.0",
|
|
35
36
|
"@rollup/plugin-json": "^4.1.0",
|
|
36
37
|
"@rollup/plugin-node-resolve": "^13.2.1",
|
|
37
38
|
"@rollup/plugin-typescript": "^8.3.2",
|
|
39
|
+
"@types/express": "^4.17.17",
|
|
38
40
|
"@types/jest": "^28.1.7",
|
|
39
|
-
"@types/node": "^20.2.5",
|
|
40
41
|
"@types/react": "^17.0.7",
|
|
41
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
42
|
-
"@typescript-eslint/parser": "^5.
|
|
43
|
-
"eslint": "^8.
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
|
43
|
+
"@typescript-eslint/parser": "^5.21.0",
|
|
44
|
+
"eslint": "^8.14.0",
|
|
44
45
|
"eslint-config-airbnb": "^19.0.4",
|
|
45
46
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
|
46
|
-
"eslint-plugin-import": "^2.
|
|
47
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
48
|
-
"eslint-plugin-react": "^7.
|
|
49
|
-
"eslint-plugin-react-hooks": "^4.
|
|
47
|
+
"eslint-plugin-import": "^2.26.0",
|
|
48
|
+
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
49
|
+
"eslint-plugin-react": "^7.29.4",
|
|
50
|
+
"eslint-plugin-react-hooks": "^4.4.0",
|
|
50
51
|
"jest": "^28.1.3",
|
|
51
52
|
"jest-environment-jsdom": "^28.1.3",
|
|
52
53
|
"raixa": "^1.0.19",
|
|
@@ -54,6 +55,9 @@
|
|
|
54
55
|
"rollup-plugin-dts": "^4.2.1",
|
|
55
56
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
56
57
|
"ts-jest": "^28.0.8",
|
|
57
|
-
"typescript": "^4.
|
|
58
|
+
"typescript": "^4.6.3"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"react-select": "^5.7.3"
|
|
58
62
|
}
|
|
59
63
|
}
|
|
@@ -17,22 +17,22 @@ import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
|
17
17
|
*/
|
|
18
18
|
type SendRequestFunction = (
|
|
19
19
|
opts: {
|
|
20
|
-
path: string
|
|
21
|
-
method: ('GET' | 'POST' | 'DELETE' | 'PUT')
|
|
20
|
+
path: string,
|
|
21
|
+
method: ('GET' | 'POST' | 'DELETE' | 'PUT'),
|
|
22
22
|
params?: {
|
|
23
|
-
[x: string]: any
|
|
24
|
-
} | undefined
|
|
23
|
+
[x: string]: any
|
|
24
|
+
} | undefined,
|
|
25
25
|
headers?: {
|
|
26
|
-
[x: string]: any
|
|
27
|
-
} | undefined
|
|
28
|
-
numRetries?: number | undefined
|
|
26
|
+
[x: string]: any,
|
|
27
|
+
} | undefined,
|
|
28
|
+
numRetries?: number | undefined,
|
|
29
29
|
},
|
|
30
30
|
) => Promise<{
|
|
31
|
-
body: any
|
|
32
|
-
status: number
|
|
31
|
+
body: any,
|
|
32
|
+
status: number,
|
|
33
33
|
headers: {
|
|
34
|
-
[x: string]: any
|
|
35
|
-
}
|
|
34
|
+
[x: string]: any
|
|
35
|
+
},
|
|
36
36
|
}>;
|
|
37
37
|
|
|
38
38
|
/*----------------------------------------*/
|
|
@@ -56,32 +56,24 @@ let storedSendRequest: SendRequestFunction;
|
|
|
56
56
|
* @returns sendRequest function
|
|
57
57
|
*/
|
|
58
58
|
export const getSendRequest = async () => {
|
|
59
|
-
//
|
|
60
|
-
let
|
|
59
|
+
// Show timeout error if too much time passes
|
|
60
|
+
let successful = false;
|
|
61
61
|
(async () => {
|
|
62
62
|
await waitMs(5000);
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
if (!successful) {
|
|
65
|
+
showFatalError(
|
|
66
|
+
new ErrorWithCode(
|
|
67
|
+
'Could not send a request because the request needed to be sent before dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.',
|
|
68
|
+
ReactKitErrorCode.NoCACCLSendRequestFunction,
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
64
72
|
})(),
|
|
65
73
|
|
|
66
74
|
// Wait for initialization
|
|
67
75
|
await initialized;
|
|
68
|
-
|
|
69
|
-
// Error if no send request function
|
|
70
|
-
if (timedOut) {
|
|
71
|
-
showFatalError(
|
|
72
|
-
new ErrorWithCode(
|
|
73
|
-
'Could not send a request because the request needed to be sent before dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.',
|
|
74
|
-
ReactKitErrorCode.NoCACCLSendRequestFunction,
|
|
75
|
-
),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// Return dummy function that never resolves
|
|
79
|
-
return (
|
|
80
|
-
() => {
|
|
81
|
-
return new Promise(() => {});
|
|
82
|
-
}
|
|
83
|
-
) as SendRequestFunction;
|
|
84
|
-
}
|
|
76
|
+
successful = true;
|
|
85
77
|
|
|
86
78
|
// Return
|
|
87
79
|
return storedSendRequest;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiselect component that allows the user to type in values and add them to
|
|
3
|
+
* the multiselect component
|
|
4
|
+
* @author Yuen Ler Chow
|
|
5
|
+
* @author Gabe Abrams
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Import React
|
|
9
|
+
import React, { KeyboardEventHandler, useReducer } from 'react';
|
|
10
|
+
|
|
11
|
+
// Import React Select
|
|
12
|
+
import CreatableSelect from 'react-select/creatable';
|
|
13
|
+
import { MultiValue } from 'react-select';
|
|
14
|
+
|
|
15
|
+
// Import other types
|
|
16
|
+
import DBEntryFieldType from '../types/DBEntryFieldType';
|
|
17
|
+
|
|
18
|
+
/*------------------------------------------------------------------------*/
|
|
19
|
+
/* -------------------------------- Types ------------------------------- */
|
|
20
|
+
/*------------------------------------------------------------------------*/
|
|
21
|
+
|
|
22
|
+
// Props definition
|
|
23
|
+
type Props = {
|
|
24
|
+
// Type of the field
|
|
25
|
+
type: DBEntryFieldType.StringArray | DBEntryFieldType.NumberArray,
|
|
26
|
+
// The values entered by the user
|
|
27
|
+
values: string[] | number[],
|
|
28
|
+
// Function to call when the values change
|
|
29
|
+
onChange: (values: string[] | number[]) => void,
|
|
30
|
+
// True if the field is disabled
|
|
31
|
+
disabled?: boolean,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// An option for the multiselect component
|
|
35
|
+
type Option = {
|
|
36
|
+
// What the user sees and types in for the option
|
|
37
|
+
label: string,
|
|
38
|
+
// Lowercase, no spaces, no special characters of the label
|
|
39
|
+
value: string,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/*------------------------------------------------------------------------*/
|
|
43
|
+
/* -------------------------------- State ------------------------------- */
|
|
44
|
+
/*------------------------------------------------------------------------*/
|
|
45
|
+
|
|
46
|
+
/* -------- State Definition -------- */
|
|
47
|
+
|
|
48
|
+
type State = {
|
|
49
|
+
// what the user has typed in so far(before pressing enter or tab)
|
|
50
|
+
inputValue: string,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/* ------------- Actions ------------ */
|
|
54
|
+
|
|
55
|
+
// Types of actions
|
|
56
|
+
enum ActionType {
|
|
57
|
+
// Update the input value
|
|
58
|
+
SetInputValue = 'SetInputValue',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Action definitions
|
|
62
|
+
type Action = (
|
|
63
|
+
| {
|
|
64
|
+
// Action type
|
|
65
|
+
type: ActionType.SetInputValue,
|
|
66
|
+
// New value
|
|
67
|
+
value: string,
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Reducer that executes actions
|
|
73
|
+
* @author Yuen Ler Chow
|
|
74
|
+
* @param state current state
|
|
75
|
+
* @param action action to execute
|
|
76
|
+
* @returns updated state
|
|
77
|
+
*/
|
|
78
|
+
const reducer = (state: State, action: Action): State => {
|
|
79
|
+
switch (action.type) {
|
|
80
|
+
case ActionType.SetInputValue: {
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
inputValue: action.value,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
default: {
|
|
87
|
+
return state;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/*------------------------------------------------------------------------*/
|
|
93
|
+
/* -------------------------------- State ------------------------------- */
|
|
94
|
+
/*------------------------------------------------------------------------*/
|
|
95
|
+
|
|
96
|
+
const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
97
|
+
// Destructure all props
|
|
98
|
+
const {
|
|
99
|
+
type,
|
|
100
|
+
values,
|
|
101
|
+
onChange,
|
|
102
|
+
disabled,
|
|
103
|
+
} = props;
|
|
104
|
+
|
|
105
|
+
/* -------------- State ------------- */
|
|
106
|
+
|
|
107
|
+
// Initial state
|
|
108
|
+
const initialState: State = {
|
|
109
|
+
inputValue: '',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Initialize state
|
|
113
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
114
|
+
|
|
115
|
+
// Destructure common state
|
|
116
|
+
const { inputValue } = state;
|
|
117
|
+
|
|
118
|
+
/*------------------------------------------------------------------------*/
|
|
119
|
+
/* ------------------------- Component Functions ------------------------ */
|
|
120
|
+
/*------------------------------------------------------------------------*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Creates an option for the multiselect component
|
|
124
|
+
* @author Yuen Ler Chow
|
|
125
|
+
* @param label label of the option
|
|
126
|
+
* @returns the new option
|
|
127
|
+
*/
|
|
128
|
+
const createOption = (label: string): Option => {
|
|
129
|
+
// set the value to the label without special characters and spaces, and lowercase
|
|
130
|
+
return {
|
|
131
|
+
label,
|
|
132
|
+
value: (
|
|
133
|
+
label
|
|
134
|
+
// Make lowercase
|
|
135
|
+
.toLowerCase()
|
|
136
|
+
// Replace special characters with nothing
|
|
137
|
+
.replace(/\W/g, '')
|
|
138
|
+
// Replace spaces with dashes
|
|
139
|
+
.replace(' ', '-')
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Adds the values to the multiselect component
|
|
146
|
+
* @author Yuen Ler Chow
|
|
147
|
+
* @param newValue new value to add
|
|
148
|
+
*/
|
|
149
|
+
const addValues = (newValue: string) => {
|
|
150
|
+
if (type === DBEntryFieldType.NumberArray) {
|
|
151
|
+
const newValues = (
|
|
152
|
+
newValue
|
|
153
|
+
// Split into items
|
|
154
|
+
.split(',')
|
|
155
|
+
// Trim strings
|
|
156
|
+
.map((val) => {
|
|
157
|
+
return val.trim();
|
|
158
|
+
})
|
|
159
|
+
// Filter zero length
|
|
160
|
+
.filter((trimmedVal) => {
|
|
161
|
+
return trimmedVal.length > 0;
|
|
162
|
+
})
|
|
163
|
+
// Parse to number
|
|
164
|
+
.map(Number.parseFloat)
|
|
165
|
+
// Filter out existing values
|
|
166
|
+
.filter((numberVal) => {
|
|
167
|
+
return !values.some((value) => {
|
|
168
|
+
return value === numberVal;
|
|
169
|
+
});
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Notify parent of new values
|
|
174
|
+
onChange([...values, ...newValues] as number[]);
|
|
175
|
+
} else {
|
|
176
|
+
const newValues = (
|
|
177
|
+
newValue
|
|
178
|
+
// Split into items
|
|
179
|
+
.split(',')
|
|
180
|
+
// Trim strings
|
|
181
|
+
.map((val) => {
|
|
182
|
+
return val.trim();
|
|
183
|
+
})
|
|
184
|
+
// Filter zero length
|
|
185
|
+
.filter((trimmedVal) => {
|
|
186
|
+
return (trimmedVal.length > 0);
|
|
187
|
+
})
|
|
188
|
+
// Filter out existing values
|
|
189
|
+
.filter((val) => {
|
|
190
|
+
return !values.some((value) => {
|
|
191
|
+
return value === val;
|
|
192
|
+
});
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Notify parent of new values
|
|
197
|
+
onChange([...values, ...newValues] as string[]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Reset text field to empty because the values have been added
|
|
201
|
+
dispatch({
|
|
202
|
+
type: ActionType.SetInputValue,
|
|
203
|
+
value: '',
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Adds the values to the multiselect component when enter or tab is pressed
|
|
209
|
+
* @author Yuen Ler Chow
|
|
210
|
+
* @param event keyboard event
|
|
211
|
+
*/
|
|
212
|
+
const handleKeyDown: KeyboardEventHandler = (event) => {
|
|
213
|
+
// Skip if no input value
|
|
214
|
+
if (!inputValue) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Add values if enter or tab is pressed
|
|
219
|
+
if (['Enter', 'Tab'].includes(event.key)) {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
addValues(inputValue);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Handles the input change of the multiselect component
|
|
227
|
+
* @author Yuen Ler Chow
|
|
228
|
+
* @param input input value
|
|
229
|
+
*/
|
|
230
|
+
const handleInputChange = (input: string) => {
|
|
231
|
+
// Create copy of input value so we can modify it
|
|
232
|
+
let newValue = input;
|
|
233
|
+
|
|
234
|
+
// Don't allow user to type in non numbers
|
|
235
|
+
if (type === DBEntryFieldType.NumberArray) {
|
|
236
|
+
newValue = input.replace(/[^0-9,]/g, '');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (input.includes(',')) {
|
|
240
|
+
// if the input has a comma, add the values separated by commas
|
|
241
|
+
addValues(newValue);
|
|
242
|
+
} else {
|
|
243
|
+
// simply update the input value to the new input value
|
|
244
|
+
dispatch({
|
|
245
|
+
type: ActionType.SetInputValue,
|
|
246
|
+
value: newValue,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Handles the value change of the multiselect component
|
|
253
|
+
* @author Yuen Ler Chow
|
|
254
|
+
* @param newValue new values
|
|
255
|
+
*/
|
|
256
|
+
const handleValueChange = (newValues: MultiValue<Option>) => {
|
|
257
|
+
// Skip if no new values
|
|
258
|
+
if (!newValues) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Update values based on type
|
|
263
|
+
if (type === DBEntryFieldType.NumberArray) {
|
|
264
|
+
const numberValues: number[] = newValues.map((val) => {
|
|
265
|
+
return Number.parseFloat(val.value);
|
|
266
|
+
});
|
|
267
|
+
onChange(numberValues);
|
|
268
|
+
} else {
|
|
269
|
+
const stringValues: string[] = newValues.map((val) => {
|
|
270
|
+
return val.value;
|
|
271
|
+
});
|
|
272
|
+
onChange(stringValues);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/*----------------------------------------*/
|
|
277
|
+
/* --------------- Main UI -------------- */
|
|
278
|
+
/*----------------------------------------*/
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<CreatableSelect
|
|
282
|
+
isDisabled={!!disabled}
|
|
283
|
+
components={{ DropdownIndicator: null }}
|
|
284
|
+
inputValue={inputValue}
|
|
285
|
+
isClearable
|
|
286
|
+
isMulti
|
|
287
|
+
menuIsOpen={false}
|
|
288
|
+
onChange={handleValueChange}
|
|
289
|
+
onInputChange={handleInputChange}
|
|
290
|
+
onKeyDown={handleKeyDown}
|
|
291
|
+
placeholder="Type/paste value and press enter..."
|
|
292
|
+
value={values.map((val) => {
|
|
293
|
+
return createOption(String(val));
|
|
294
|
+
})}
|
|
295
|
+
/>
|
|
296
|
+
);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/*------------------------------------------------------------------------*/
|
|
300
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
301
|
+
/*------------------------------------------------------------------------*/
|
|
302
|
+
|
|
303
|
+
export default CreatableMultiselect;
|