dce-reactkit 4.0.0-beta.3 → 4.0.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/.vscode/settings.json +3 -1
- package/dist/cjs/index.js +392 -167
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/cjs/types/components/Modal/ModalProps.d.ts +2 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +3 -1
- package/dist/cjs/types/components/SimpleTimeChooser.d.ts +20 -0
- package/dist/cjs/types/components/TabBox.d.ts +1 -0
- package/dist/cjs/types/helpers/getWordCount.d.ts +10 -0
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/esm/index.js +392 -169
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/esm/types/components/Modal/ModalProps.d.ts +2 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +3 -1
- package/dist/esm/types/components/SimpleTimeChooser.d.ts +20 -0
- package/dist/esm/types/components/TabBox.d.ts +1 -0
- package/dist/esm/types/helpers/getWordCount.d.ts +10 -0
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
- package/dist/index.d.ts +55 -17
- package/package.json +2 -3
- package/rollup.config.js +0 -2
- package/src/components/IntelliTable.tsx +1 -1
- package/src/components/ItemPicker/NestableItemList.tsx +1 -0
- package/src/components/ItemPicker/index.tsx +96 -0
- package/src/components/LogReviewer.tsx +2 -2
- package/src/components/Modal/ModalProps.ts +4 -0
- package/src/components/Modal/index.tsx +59 -20
- package/src/components/SimpleDateChooser.tsx +62 -26
- package/src/components/SimpleTimeChooser.tsx +196 -0
- package/src/components/TabBox.tsx +27 -9
- package/src/helpers/getWordCount.ts +26 -0
- package/src/index.ts +4 -0
- package/src/types/LogSource.ts +1 -1
- package/src/types/ReactKitErrorCode.tsx +3 -1
- package/genEncodedSecret.ts +0 -84
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A very simple, lightweight date chooser
|
|
3
3
|
* @author Gabe Abrams
|
|
4
|
+
* @author Gardenia Liu
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
// Import React
|
|
7
8
|
import React from 'react';
|
|
8
|
-
import getMonthName from '../helpers/getMonthName';
|
|
9
9
|
|
|
10
10
|
// Import helpers
|
|
11
11
|
import getOrdinal from '../helpers/getOrdinal';
|
|
12
|
+
import getMonthName from '../helpers/getMonthName';
|
|
12
13
|
import getTimeInfoInET from '../helpers/getTimeInfoInET';
|
|
13
14
|
|
|
15
|
+
// Import classes
|
|
16
|
+
import ErrorWithCode from '../errors/ErrorWithCode';
|
|
17
|
+
|
|
18
|
+
// Import types
|
|
19
|
+
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
20
|
+
|
|
14
21
|
/*------------------------------------------------------------------------*/
|
|
15
22
|
/* -------------------------------- Types ------------------------------- */
|
|
16
23
|
/*------------------------------------------------------------------------*/
|
|
@@ -33,12 +40,14 @@ type Props = {
|
|
|
33
40
|
* @param year new full year number
|
|
34
41
|
*/
|
|
35
42
|
onChange: (month: number, day: number, year: number) => void,
|
|
36
|
-
// Number of months to allow the user to choose from
|
|
37
|
-
//
|
|
43
|
+
// Number of months in either the past or future to allow the user to choose from
|
|
44
|
+
// If we aren't allowing the past or the future, we will throw an error
|
|
45
|
+
// (max is 12, default is 6 in the past and 6 in the future)
|
|
38
46
|
numMonthsToShow?: number,
|
|
39
|
-
// If true,
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
// If true, the user isn't allowed to select dates in the past
|
|
48
|
+
dontAllowPast?: boolean,
|
|
49
|
+
// If true, the user isn't allowed to select dates in the future
|
|
50
|
+
dontAllowFuture?: boolean,
|
|
42
51
|
};
|
|
43
52
|
|
|
44
53
|
/*------------------------------------------------------------------------*/
|
|
@@ -56,8 +65,9 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
56
65
|
ariaLabel,
|
|
57
66
|
name,
|
|
58
67
|
onChange,
|
|
59
|
-
chooseFromPast,
|
|
60
68
|
numMonthsToShow = 6,
|
|
69
|
+
dontAllowFuture,
|
|
70
|
+
dontAllowPast,
|
|
61
71
|
} = props;
|
|
62
72
|
|
|
63
73
|
/*------------------------------------------------------------------------*/
|
|
@@ -76,16 +86,41 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
76
86
|
days: number[],
|
|
77
87
|
year: number,
|
|
78
88
|
}[] = [];
|
|
89
|
+
|
|
79
90
|
let startYear = today.year;
|
|
80
91
|
let startMonth = today.month;
|
|
81
|
-
|
|
92
|
+
|
|
93
|
+
// Don't allow past or future dates
|
|
94
|
+
if (dontAllowPast && dontAllowFuture) {
|
|
95
|
+
throw new ErrorWithCode(
|
|
96
|
+
'No past or future dates allowed',
|
|
97
|
+
ReactKitErrorCode.SimpleDateChooserInvalidDateRange,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Require numMonthsToShow to be positive
|
|
102
|
+
if (numMonthsToShow <= 0) {
|
|
103
|
+
throw new ErrorWithCode(
|
|
104
|
+
'numMonthsToShow must be positive',
|
|
105
|
+
ReactKitErrorCode.SimpleDateChooserInvalidNumMonths,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Recalculate startMonth and startYear when allowing past dates
|
|
110
|
+
if (!dontAllowPast) {
|
|
82
111
|
startMonth -= Math.max(0, numMonthsToShow - 1);
|
|
83
112
|
while (startMonth <= 0) {
|
|
84
113
|
startMonth += 12;
|
|
85
114
|
startYear -= 1;
|
|
86
115
|
}
|
|
87
116
|
}
|
|
88
|
-
|
|
117
|
+
// Calculate total number of months to show
|
|
118
|
+
let totalMonthsToShow = numMonthsToShow;
|
|
119
|
+
if (!dontAllowPast && !dontAllowFuture) {
|
|
120
|
+
totalMonthsToShow = totalMonthsToShow * 2 - 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < totalMonthsToShow; i++) {
|
|
89
124
|
// Get month and year info
|
|
90
125
|
const unmoddedMonth = (startMonth + i);
|
|
91
126
|
let month = unmoddedMonth;
|
|
@@ -105,24 +140,25 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
105
140
|
// Figure out which days are allowed
|
|
106
141
|
const days = [];
|
|
107
142
|
const numDaysInMonth = (new Date(year, month, 0)).getDate();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
days.push(day);
|
|
143
|
+
|
|
144
|
+
// Current month
|
|
145
|
+
if (month === today.month && year === today.year) {
|
|
146
|
+
// Past selection: add all previous days of the month
|
|
147
|
+
if (!dontAllowPast) {
|
|
148
|
+
for (let day = 1; day < today.day; day++) {
|
|
149
|
+
days.push(day);
|
|
150
|
+
}
|
|
117
151
|
}
|
|
118
|
-
|
|
152
|
+
days.push(today.day); // Add current day
|
|
119
153
|
// Future selection: add all remaining days of the month
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
if (!dontAllowFuture) {
|
|
155
|
+
for (let day = today.day + 1; day <= numDaysInMonth; day++) {
|
|
156
|
+
days.push(day);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else { // Past or future month
|
|
160
|
+
// Include all days in the month
|
|
161
|
+
for (let day = 1; day <= numDaysInMonth; day++) {
|
|
126
162
|
days.push(day);
|
|
127
163
|
}
|
|
128
164
|
}
|
|
@@ -158,8 +194,8 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
158
194
|
</option>,
|
|
159
195
|
);
|
|
160
196
|
|
|
197
|
+
// This is the currently selected month
|
|
161
198
|
if (month === choice.month) {
|
|
162
|
-
// This is the currently selected month
|
|
163
199
|
// Create day options
|
|
164
200
|
choice.days.forEach((dayChoice) => {
|
|
165
201
|
const ordinal = getOrdinal(dayChoice);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A very simple, lightweight time chooser
|
|
3
|
+
* @author Gardenia Liu
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Import helpers
|
|
10
|
+
import padZerosLeft from '../helpers/padZerosLeft';
|
|
11
|
+
|
|
12
|
+
/*------------------------------------------------------------------------*/
|
|
13
|
+
/* -------------------------------- Types ------------------------------- */
|
|
14
|
+
/*------------------------------------------------------------------------*/
|
|
15
|
+
|
|
16
|
+
type Props = {
|
|
17
|
+
// Aria label
|
|
18
|
+
ariaLabel: string,
|
|
19
|
+
// Name of the chooser (machine-readable, hyphenated)
|
|
20
|
+
name: string,
|
|
21
|
+
// Currently selected hour of the day (24hr)
|
|
22
|
+
hour: number,
|
|
23
|
+
// Currently selected minute within the hour
|
|
24
|
+
minute: number,
|
|
25
|
+
/**
|
|
26
|
+
* Handler for when time changes
|
|
27
|
+
* @param hour new 24hr hour number
|
|
28
|
+
* @param minute new minute number
|
|
29
|
+
*/
|
|
30
|
+
onChange: (hour: number, minute: number) => void,
|
|
31
|
+
// Interval in minutes between each choice
|
|
32
|
+
// Allowed options: 15, 30, 60, defaults to 15
|
|
33
|
+
// If an unsupported interval is passed in, it will default to 15
|
|
34
|
+
intervalMin?: number,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/*------------------------------------------------------------------------*/
|
|
38
|
+
/* ------------------------------ Constants ----------------------------- */
|
|
39
|
+
/*------------------------------------------------------------------------*/
|
|
40
|
+
|
|
41
|
+
// Allowed intervals between options
|
|
42
|
+
const ALLOWED_INTERVALS = [15, 30, 60]; // min
|
|
43
|
+
|
|
44
|
+
// Default interval to use if an unsupported interval is passed in
|
|
45
|
+
const DEFAULT_INTERVAL = ALLOWED_INTERVALS[0]; // min
|
|
46
|
+
|
|
47
|
+
/*------------------------------------------------------------------------*/
|
|
48
|
+
/* ------------------------------ Component ----------------------------- */
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
|
|
51
|
+
const SimpleTimeChooser: React.FC<Props> = (props) => {
|
|
52
|
+
/*------------------------------------------------------------------------*/
|
|
53
|
+
/* -------------------------------- Setup ------------------------------- */
|
|
54
|
+
/*------------------------------------------------------------------------*/
|
|
55
|
+
|
|
56
|
+
/* -------------- Props ------------- */
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
ariaLabel,
|
|
60
|
+
name,
|
|
61
|
+
hour,
|
|
62
|
+
minute,
|
|
63
|
+
onChange,
|
|
64
|
+
} = props;
|
|
65
|
+
let {
|
|
66
|
+
intervalMin = DEFAULT_INTERVAL,
|
|
67
|
+
} = props;
|
|
68
|
+
|
|
69
|
+
// Use default interval if not supported
|
|
70
|
+
if (!ALLOWED_INTERVALS.includes(intervalMin)) {
|
|
71
|
+
intervalMin = DEFAULT_INTERVAL;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*------------------------------------------------------------------------*/
|
|
75
|
+
/* ------------------------- Component Functions ------------------------ */
|
|
76
|
+
/*------------------------------------------------------------------------*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Convert number of minutes since midnight into 24hour and minute format
|
|
80
|
+
* @author Gabe Abrams
|
|
81
|
+
* @param minSinceMidnight total minutes since midnight
|
|
82
|
+
* @returns hours (24) and minutes
|
|
83
|
+
*/
|
|
84
|
+
const convertMinSinceMidnightToHoursAndMin = (minSinceMidnight: number): {
|
|
85
|
+
hours: number,
|
|
86
|
+
minutes: number,
|
|
87
|
+
} => {
|
|
88
|
+
return {
|
|
89
|
+
hours: Math.floor(minSinceMidnight / 60),
|
|
90
|
+
minutes: minSinceMidnight % 60,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Convert time in minutes into HH:MM format
|
|
96
|
+
* @author Gardenia Liu
|
|
97
|
+
* @param totalMinutes total minutes since midnight
|
|
98
|
+
* @returns formatted time string
|
|
99
|
+
*/
|
|
100
|
+
const formatTime = (totalMinutes: number): string => {
|
|
101
|
+
// Handle special cases
|
|
102
|
+
if (totalMinutes === 0) {
|
|
103
|
+
return '12:00 Midnight';
|
|
104
|
+
}
|
|
105
|
+
if (totalMinutes === 12 * 60) {
|
|
106
|
+
return '12:00 Noon';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// All normal cases:
|
|
110
|
+
const timeInfo = convertMinSinceMidnightToHoursAndMin(totalMinutes);
|
|
111
|
+
let { hours } = timeInfo;
|
|
112
|
+
const { minutes } = timeInfo;
|
|
113
|
+
|
|
114
|
+
// Process 24hr -> 12hr
|
|
115
|
+
const isAM = (hours < 12);
|
|
116
|
+
if (hours === 0) {
|
|
117
|
+
hours = 12;
|
|
118
|
+
} else if (hours > 12) {
|
|
119
|
+
hours %= 12;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Pad with zeros
|
|
123
|
+
const paddedMinutes = padZerosLeft(minutes, 2);
|
|
124
|
+
|
|
125
|
+
// Assemble time string
|
|
126
|
+
return `${hours}:${paddedMinutes} ${isAM ? 'AM' : 'PM'}`;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/*------------------------------------------------------------------------*/
|
|
130
|
+
/* ------------------------------- Render ------------------------------- */
|
|
131
|
+
/*------------------------------------------------------------------------*/
|
|
132
|
+
|
|
133
|
+
/*----------------------------------------*/
|
|
134
|
+
/* --------------- Main UI -------------- */
|
|
135
|
+
/*----------------------------------------*/
|
|
136
|
+
|
|
137
|
+
// Generate list of time options
|
|
138
|
+
const times: string[] = [];
|
|
139
|
+
for (let time = 0; time < 24 * 60; time += intervalMin) {
|
|
140
|
+
times.push(formatTime(time));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Currently selected time in minutes since midnight
|
|
144
|
+
const selectedTimeMin = hour * 60 + minute;
|
|
145
|
+
|
|
146
|
+
// Create choice options
|
|
147
|
+
const timeOptions: React.ReactNode[] = times.map((timeString, timeIndex) => {
|
|
148
|
+
const numMinutesForChoice = timeIndex * intervalMin;
|
|
149
|
+
|
|
150
|
+
// Render the option
|
|
151
|
+
return (
|
|
152
|
+
<option
|
|
153
|
+
key={numMinutesForChoice}
|
|
154
|
+
value={numMinutesForChoice}
|
|
155
|
+
aria-label={`choose ${timeString}`}
|
|
156
|
+
>
|
|
157
|
+
{timeString}
|
|
158
|
+
</option>
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div
|
|
164
|
+
className="SimpleTimeChooser-container"
|
|
165
|
+
aria-label={`time chooser with selected time: ${formatTime(selectedTimeMin)}`}
|
|
166
|
+
>
|
|
167
|
+
{/* Time Chooser */}
|
|
168
|
+
<select
|
|
169
|
+
aria-label={`time for ${ariaLabel}`}
|
|
170
|
+
className="custom-select d-inline-block"
|
|
171
|
+
style={{ width: 'auto' }}
|
|
172
|
+
id={`SimpleTimeChooser-${name}-time`}
|
|
173
|
+
value={selectedTimeMin}
|
|
174
|
+
onChange={(e) => {
|
|
175
|
+
// Parse selector value (string)
|
|
176
|
+
const newTime = Number.parseInt(e.target.value, 10);
|
|
177
|
+
|
|
178
|
+
// Convert minutes since midnight to hour and minute
|
|
179
|
+
const timeInfo = convertMinSinceMidnightToHoursAndMin(newTime);
|
|
180
|
+
|
|
181
|
+
// Notify parent
|
|
182
|
+
onChange(timeInfo.hours, timeInfo.minutes);
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
{timeOptions}
|
|
186
|
+
</select>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/*------------------------------------------------------------------------*/
|
|
192
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
193
|
+
/*------------------------------------------------------------------------*/
|
|
194
|
+
|
|
195
|
+
// Export component
|
|
196
|
+
export default SimpleTimeChooser;
|
|
@@ -15,6 +15,8 @@ type Props = {
|
|
|
15
15
|
title: React.ReactNode,
|
|
16
16
|
// Children/contents inside the box
|
|
17
17
|
children: React.ReactNode,
|
|
18
|
+
// Children to display on the top right of the tab box
|
|
19
|
+
topRightChildren?: React.ReactNode,
|
|
18
20
|
// If true, don't add margin below the tab box
|
|
19
21
|
noBottomMargin?: boolean,
|
|
20
22
|
// If true, don't add padding to bottom of tab box
|
|
@@ -49,7 +51,9 @@ const style = `
|
|
|
49
51
|
|
|
50
52
|
/* Container for Title */
|
|
51
53
|
.TabBox-title-container {
|
|
52
|
-
|
|
54
|
+
display: flex;
|
|
55
|
+
justify-content: space-between;
|
|
56
|
+
align-items: center;
|
|
53
57
|
position: relative;
|
|
54
58
|
left: 0;
|
|
55
59
|
text-align: left;
|
|
@@ -82,6 +86,19 @@ const style = `
|
|
|
82
86
|
background: #fdfdfd;
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
.TabBox-title-right-container {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: row;
|
|
92
|
+
align-items: bottom;
|
|
93
|
+
height: 2.4rem;
|
|
94
|
+
overflow: visible;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.TabBox-title-right-contents {
|
|
98
|
+
margin-right: 0.5rem;
|
|
99
|
+
margin-bottom: 0.2rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
85
102
|
/* Make the TabBox's Children Appear Above Title if Overlap Occurs */
|
|
86
103
|
.TabBox-children {
|
|
87
104
|
position: relative;
|
|
@@ -98,11 +115,10 @@ const TabBox: React.FC<Props> = (props) => {
|
|
|
98
115
|
/* -------------------------------- Setup ------------------------------- */
|
|
99
116
|
/*------------------------------------------------------------------------*/
|
|
100
117
|
|
|
101
|
-
/* -------------- Props ------------- */
|
|
102
|
-
|
|
103
118
|
const {
|
|
104
119
|
title,
|
|
105
120
|
children,
|
|
121
|
+
topRightChildren,
|
|
106
122
|
noBottomPadding,
|
|
107
123
|
noBottomMargin,
|
|
108
124
|
} = props;
|
|
@@ -111,21 +127,23 @@ const TabBox: React.FC<Props> = (props) => {
|
|
|
111
127
|
/* ------------------------------- Render ------------------------------- */
|
|
112
128
|
/*------------------------------------------------------------------------*/
|
|
113
129
|
|
|
114
|
-
/*----------------------------------------*/
|
|
115
|
-
/* --------------- Main UI -------------- */
|
|
116
|
-
/*----------------------------------------*/
|
|
117
|
-
|
|
118
|
-
// Full UI
|
|
119
130
|
return (
|
|
120
131
|
<div className={`TabBox-container ${noBottomMargin ? '' : 'mb-2'}`}>
|
|
121
132
|
{/* Style */}
|
|
122
133
|
<style>{style}</style>
|
|
123
134
|
|
|
124
|
-
{/* Title */}
|
|
135
|
+
{/* Title Row with Left and Right sections */}
|
|
125
136
|
<div className="TabBox-title-container">
|
|
126
137
|
<div className="TabBox-title">
|
|
127
138
|
{title}
|
|
128
139
|
</div>
|
|
140
|
+
{topRightChildren && (
|
|
141
|
+
<div className="TabBox-title-right-container">
|
|
142
|
+
<div className="TabBox-title-right-contents">
|
|
143
|
+
{topRightChildren}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
129
147
|
</div>
|
|
130
148
|
|
|
131
149
|
{/* Contents */}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const punctuationRegex = /[!@#$%^&*(),.?\/;:'"\-\[\]]/g;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get number of words in string
|
|
5
|
+
* @author Gardenia Liu
|
|
6
|
+
* @author Allison Zhang
|
|
7
|
+
* @author Gabe Abrams
|
|
8
|
+
* @param text the string to check
|
|
9
|
+
* @returns number of words in the string
|
|
10
|
+
*/
|
|
11
|
+
const getWordCount = (text: string): number => {
|
|
12
|
+
const trimmedTextWithoutPunctuation = (
|
|
13
|
+
text
|
|
14
|
+
// Remove leading and trailing whitespace
|
|
15
|
+
.trim()
|
|
16
|
+
// Remove punctuation
|
|
17
|
+
.replace(punctuationRegex, '')
|
|
18
|
+
);
|
|
19
|
+
if (trimmedTextWithoutPunctuation.length === 0) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return trimmedTextWithoutPunctuation.split(/\s+/g).length;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default getWordCount;
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import RadioButton from './components/RadioButton';
|
|
|
15
15
|
import CheckboxButton from './components/CheckboxButton';
|
|
16
16
|
import ButtonInputGroup from './components/ButtonInputGroup';
|
|
17
17
|
import SimpleDateChooser from './components/SimpleDateChooser';
|
|
18
|
+
import SimpleTimeChooser from './components/SimpleTimeChooser';
|
|
18
19
|
import Drawer from './components/Drawer';
|
|
19
20
|
import PopSuccessMark from './components/PopSuccessMark';
|
|
20
21
|
import PopFailureMark from './components/PopFailureMark';
|
|
@@ -91,6 +92,7 @@ import mapAsync from './helpers/asyncArrayFunctions/mapAsync';
|
|
|
91
92
|
import someAsync from './helpers/asyncArrayFunctions/someAsync';
|
|
92
93
|
import capitalize from './helpers/capitalize';
|
|
93
94
|
import shuffleArray from './helpers/shuffleArray';
|
|
95
|
+
import getWordCount from './helpers/getWordCount';
|
|
94
96
|
|
|
95
97
|
// Import types
|
|
96
98
|
import ParamType from './types/ParamType';
|
|
@@ -132,6 +134,7 @@ export {
|
|
|
132
134
|
CheckboxButton,
|
|
133
135
|
ButtonInputGroup,
|
|
134
136
|
SimpleDateChooser,
|
|
137
|
+
SimpleTimeChooser,
|
|
135
138
|
Drawer,
|
|
136
139
|
PopSuccessMark,
|
|
137
140
|
PopFailureMark,
|
|
@@ -204,6 +207,7 @@ export {
|
|
|
204
207
|
someAsync,
|
|
205
208
|
capitalize,
|
|
206
209
|
shuffleArray,
|
|
210
|
+
getWordCount,
|
|
207
211
|
// Client helpers
|
|
208
212
|
initClient,
|
|
209
213
|
visitServerEndpoint,
|
package/src/types/LogSource.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Highest error code =
|
|
1
|
+
// Highest error code = DRK36
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* List of error codes built into the react kit
|
|
@@ -9,6 +9,8 @@ enum ReactKitErrorCode {
|
|
|
9
9
|
NoCode = 'DRK2',
|
|
10
10
|
SessionExpired = 'DRK3',
|
|
11
11
|
NoCACCLSendRequestFunction = 'DRK7',
|
|
12
|
+
SimpleDateChooserInvalidDateRange = 'DRK35',
|
|
13
|
+
SimpleDateChooserInvalidNumMonths = 'DRK36',
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export default ReactKitErrorCode;
|
package/genEncodedSecret.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// Import crypto lib
|
|
2
|
-
import crypto from 'crypto';
|
|
3
|
-
|
|
4
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
5
|
-
|
|
6
|
-
// Get args
|
|
7
|
-
const REACTKIT_CRED_ENCODING_SALT = process.env.npm_config_salt;
|
|
8
|
-
if (!REACTKIT_CRED_ENCODING_SALT) {
|
|
9
|
-
console.log('Encoding salt is required: --salt=...');
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Get the key
|
|
14
|
-
let key = process.env.npm_config_key;
|
|
15
|
-
if (!key) {
|
|
16
|
-
console.log('Key is required: --key=...');
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Get the description
|
|
21
|
-
let description = process.env.npm_config_description;
|
|
22
|
-
if (!description) {
|
|
23
|
-
console.log('Description is required: --description=...');
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Get secret
|
|
28
|
-
let secret = process.env.npm_config_secret;
|
|
29
|
-
if (!secret) {
|
|
30
|
-
// Generate a random secret
|
|
31
|
-
secret = '';
|
|
32
|
-
for (let i = 0; i < 32; i++) {
|
|
33
|
-
secret += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
34
|
-
}
|
|
35
|
-
console.log('Generated a random secret. If you have one in mind, use --secret=...');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Get the host name
|
|
39
|
-
const host = process.env.npm_config_host;
|
|
40
|
-
if (!host) {
|
|
41
|
-
console.log('Host of the receiving server is required: --host=...');
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Encryption process based on:
|
|
46
|
-
// https://medium.com/@tony.infisical/guide-to-nodes-crypto-module-for-encryption-decryption-65c077176980
|
|
47
|
-
|
|
48
|
-
// Create a random initialization vector
|
|
49
|
-
const iv = crypto.randomBytes(12).toString('base64');
|
|
50
|
-
|
|
51
|
-
// Create a cipher
|
|
52
|
-
const cipher = crypto.createCipheriv(
|
|
53
|
-
'aes-256-gcm',
|
|
54
|
-
Buffer.from(secret, 'base64'),
|
|
55
|
-
Buffer.from(iv, 'base64'),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Encrypt the string
|
|
59
|
-
let ciphertext = cipher.update(secret, 'utf8', 'base64');
|
|
60
|
-
|
|
61
|
-
// Finalize the encryption
|
|
62
|
-
ciphertext += cipher.final('base64');
|
|
63
|
-
|
|
64
|
-
// Get the authentication tag
|
|
65
|
-
const tag = cipher.getAuthTag();
|
|
66
|
-
|
|
67
|
-
// JSONify the encrypted data
|
|
68
|
-
const encryptionPack = encodeURIComponent(JSON.stringify({
|
|
69
|
-
ciphertext,
|
|
70
|
-
iv,
|
|
71
|
-
tag,
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
|
-
// Show the encrypted data
|
|
75
|
-
console.log('\n\n');
|
|
76
|
-
console.log('––––– Done! What\'s Next: –––––');
|
|
77
|
-
console.log('');
|
|
78
|
-
console.log('On the server *sending* the requests, append the following to the REACTKIT_CROSS_SERVER_CREDENTIALS env var:');
|
|
79
|
-
console.log(`|${host}:${key}:${secret}|`);
|
|
80
|
-
console.log('');
|
|
81
|
-
console.log('On the server *receiving* the requests, add an entry to the "CrossServerCredential" collection:');
|
|
82
|
-
console.log(`{ "description": "${description}", "key": "${key}", "encodedeSecret": "${encryptionPack}", "scopes": [] }`);
|
|
83
|
-
console.log('');
|
|
84
|
-
console.log('For all scopes that the server should have access to, add them to the "scopes" array.');
|