dce-reactkit 5.0.1 → 5.0.3
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 +338 -167
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/FakeProgressBar.d.ts +27 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +1 -0
- package/dist/cjs/types/components/SimpleMonthChooser.d.ts +23 -0
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/esm/index.js +337 -168
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/FakeProgressBar.d.ts +27 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +1 -0
- package/dist/esm/types/components/SimpleMonthChooser.d.ts +23 -0
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/index.d.ts +98 -49
- package/package.json +1 -1
- package/src/components/FakeProgressBar.tsx +274 -0
- package/src/components/SimpleDateChooser.tsx +31 -22
- package/src/components/SimpleMonthChooser.tsx +98 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fake progress bar that approaches completion but never fully finishes.
|
|
3
|
+
* Built on top of the existing ProgressBar component.
|
|
4
|
+
*
|
|
5
|
+
* Two phases:
|
|
6
|
+
* 1. Fast phase (0–90%): random jumps of 1–5% at randomized intervals,
|
|
7
|
+
* paced to reach ~90% in roughly estimatedTimeSec seconds.
|
|
8
|
+
* 2. Slow phase (90–99%): crawls 1% at a time with longer random delays,
|
|
9
|
+
* never reaching 100% on its own.
|
|
10
|
+
*
|
|
11
|
+
* When isFinished becomes true, the bar immediately jumps to 100%.
|
|
12
|
+
* @author Yuen Ler Chow
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Import React
|
|
16
|
+
import React, { useReducer, useEffect } from 'react';
|
|
17
|
+
|
|
18
|
+
// Import helpers
|
|
19
|
+
import { waitMs } from 'dce-commonkit';
|
|
20
|
+
|
|
21
|
+
// Import types
|
|
22
|
+
import Variant from '../types/Variant';
|
|
23
|
+
import ProgressBarSize from '../types/ProgressBarSize';
|
|
24
|
+
|
|
25
|
+
// Import components
|
|
26
|
+
import ProgressBar from './ProgressBar';
|
|
27
|
+
|
|
28
|
+
/*------------------------------------------------------------------------*/
|
|
29
|
+
/* ------------------------------ Constants ----------------------------- */
|
|
30
|
+
/*------------------------------------------------------------------------*/
|
|
31
|
+
|
|
32
|
+
// Percent where the bar switches from fast random jumps to a slow crawl
|
|
33
|
+
const SLOW_PHASE_START_PERCENT = 90;
|
|
34
|
+
|
|
35
|
+
// Highest percent the bar will reach on its own (before isFinished is true)
|
|
36
|
+
const MAX_UNFINISHED_PERCENT = 99;
|
|
37
|
+
|
|
38
|
+
// Average step size during the fast phase (uniform random from 1–5)
|
|
39
|
+
const AVG_FAST_PHASE_STEP = 3;
|
|
40
|
+
|
|
41
|
+
// Minimum delay (ms) between steps during the slow phase
|
|
42
|
+
const SLOW_PHASE_MIN_DELAY_MS = 2000;
|
|
43
|
+
|
|
44
|
+
// Maximum delay (ms) between steps during the slow phase
|
|
45
|
+
const SLOW_PHASE_MAX_DELAY_MS = 5000;
|
|
46
|
+
|
|
47
|
+
/*------------------------------------------------------------------------*/
|
|
48
|
+
/* -------------------------------- Types ------------------------------- */
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
|
|
51
|
+
// Props definition
|
|
52
|
+
type Props = {
|
|
53
|
+
// True if the task is finished (triggers animation to 100%)
|
|
54
|
+
isFinished?: boolean,
|
|
55
|
+
// Estimated time (in seconds) to finish the task (default 8)
|
|
56
|
+
estimatedTimeSec?: number,
|
|
57
|
+
// Whether to show striped effect (default false)
|
|
58
|
+
striped?: boolean,
|
|
59
|
+
// Variant of the progress bar (default warning)
|
|
60
|
+
variant?: Variant,
|
|
61
|
+
// Background variants (default secondary subtle)
|
|
62
|
+
bgVariant?: Variant,
|
|
63
|
+
// Hide outline (default false)
|
|
64
|
+
showOutline?: boolean,
|
|
65
|
+
// Size of the progress bar (default md)
|
|
66
|
+
size?: ProgressBarSize,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/*------------------------------------------------------------------------*/
|
|
70
|
+
/* -------------------------------- State ------------------------------- */
|
|
71
|
+
/*------------------------------------------------------------------------*/
|
|
72
|
+
|
|
73
|
+
/* -------- State Definition -------- */
|
|
74
|
+
|
|
75
|
+
type State = {
|
|
76
|
+
// Current progress percentage (0-99)
|
|
77
|
+
progress: number,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/* ------------- Actions ------------ */
|
|
81
|
+
|
|
82
|
+
// Types of actions
|
|
83
|
+
enum ActionType {
|
|
84
|
+
// Advance the progress by a given step
|
|
85
|
+
Advance = 'Advance',
|
|
86
|
+
// Set progress to 100% (task complete)
|
|
87
|
+
Complete = 'Complete',
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Action definitions
|
|
91
|
+
type Action = (
|
|
92
|
+
| {
|
|
93
|
+
// Action type
|
|
94
|
+
type: ActionType.Advance,
|
|
95
|
+
// Number of percent points to advance
|
|
96
|
+
step: number,
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
// Action type
|
|
100
|
+
type: ActionType.Complete,
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reducer that executes actions
|
|
106
|
+
* @author Yuen Ler Chow
|
|
107
|
+
* @param state current state
|
|
108
|
+
* @param action action to execute
|
|
109
|
+
*/
|
|
110
|
+
const reducer = (state: State, action: Action): State => {
|
|
111
|
+
switch (action.type) {
|
|
112
|
+
case ActionType.Advance: {
|
|
113
|
+
return {
|
|
114
|
+
...state,
|
|
115
|
+
progress: Math.min(100, state.progress + action.step),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
case ActionType.Complete: {
|
|
119
|
+
return {
|
|
120
|
+
...state,
|
|
121
|
+
progress: 100,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
default: {
|
|
125
|
+
return state;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/*------------------------------------------------------------------------*/
|
|
131
|
+
/* ------------------------------ Component ----------------------------- */
|
|
132
|
+
/*------------------------------------------------------------------------*/
|
|
133
|
+
|
|
134
|
+
const FakeProgressBar: React.FC<Props> = (props) => {
|
|
135
|
+
/*------------------------------------------------------------------------*/
|
|
136
|
+
/* -------------------------------- Setup ------------------------------- */
|
|
137
|
+
/*------------------------------------------------------------------------*/
|
|
138
|
+
|
|
139
|
+
/* -------------- Props ------------- */
|
|
140
|
+
|
|
141
|
+
// Destructure props
|
|
142
|
+
const {
|
|
143
|
+
isFinished,
|
|
144
|
+
estimatedTimeSec = 8,
|
|
145
|
+
striped,
|
|
146
|
+
variant,
|
|
147
|
+
bgVariant,
|
|
148
|
+
showOutline,
|
|
149
|
+
size,
|
|
150
|
+
} = props;
|
|
151
|
+
|
|
152
|
+
/* -------------- State ------------- */
|
|
153
|
+
|
|
154
|
+
// Initial state
|
|
155
|
+
const initialState: State = {
|
|
156
|
+
progress: 0,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Initialize state
|
|
160
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
161
|
+
|
|
162
|
+
// Destructure common state
|
|
163
|
+
const {
|
|
164
|
+
progress,
|
|
165
|
+
} = state;
|
|
166
|
+
|
|
167
|
+
// Convert estimate to milliseconds
|
|
168
|
+
const estimatedMs = estimatedTimeSec * 1000;
|
|
169
|
+
|
|
170
|
+
// Average interval between updates to reach ~90% in the estimated time
|
|
171
|
+
const avgIntervalMs = (estimatedMs / SLOW_PHASE_START_PERCENT) * AVG_FAST_PHASE_STEP;
|
|
172
|
+
|
|
173
|
+
/*------------------------------------------------------------------------*/
|
|
174
|
+
/* ------------------------- Lifecycle Functions ------------------------ */
|
|
175
|
+
/*------------------------------------------------------------------------*/
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Animate to 100% when isFinished becomes true
|
|
179
|
+
* @author Yuen Ler Chow
|
|
180
|
+
*/
|
|
181
|
+
useEffect(
|
|
182
|
+
() => {
|
|
183
|
+
if (isFinished) {
|
|
184
|
+
dispatch({
|
|
185
|
+
type: ActionType.Complete,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
[isFinished],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Advance the progress in two phases:
|
|
194
|
+
* 1) Random jumps up to 90%
|
|
195
|
+
* 2) Slow crawl from 90% to 99%
|
|
196
|
+
* @author Yuen Ler Chow
|
|
197
|
+
*/
|
|
198
|
+
useEffect(
|
|
199
|
+
() => {
|
|
200
|
+
// Stop if already finished or at max
|
|
201
|
+
if (isFinished || progress >= MAX_UNFINISHED_PERCENT) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Determine which phase we are in
|
|
206
|
+
const inFastPhase = progress < SLOW_PHASE_START_PERCENT;
|
|
207
|
+
|
|
208
|
+
// Default values
|
|
209
|
+
let delay = avgIntervalMs;
|
|
210
|
+
let step = 1;
|
|
211
|
+
|
|
212
|
+
if (inFastPhase) {
|
|
213
|
+
// Randomize delay around the average interval for variability
|
|
214
|
+
const randomnessMultiplier = 0.5 + (Math.random());
|
|
215
|
+
delay = avgIntervalMs * randomnessMultiplier;
|
|
216
|
+
|
|
217
|
+
// Randomly move forward by 1-5%, capped at 90%
|
|
218
|
+
step = Math.min(
|
|
219
|
+
1 + Math.floor(Math.random() * 5),
|
|
220
|
+
SLOW_PHASE_START_PERCENT - progress,
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
// Crawl slowly 1% at a time, clamped so we don't exceed 99%
|
|
224
|
+
delay = SLOW_PHASE_MIN_DELAY_MS + (Math.random() * (SLOW_PHASE_MAX_DELAY_MS - SLOW_PHASE_MIN_DELAY_MS));
|
|
225
|
+
step = Math.min(1, MAX_UNFINISHED_PERCENT - progress);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Track whether this effect invocation is still current
|
|
229
|
+
let cancelled = false;
|
|
230
|
+
|
|
231
|
+
// Wait then dispatch the next progress update
|
|
232
|
+
(async () => {
|
|
233
|
+
await waitMs(delay);
|
|
234
|
+
if (!cancelled) {
|
|
235
|
+
dispatch({
|
|
236
|
+
type: ActionType.Advance,
|
|
237
|
+
step,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
})();
|
|
241
|
+
|
|
242
|
+
return () => {
|
|
243
|
+
cancelled = true;
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
[progress, avgIntervalMs, isFinished],
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
/*------------------------------------------------------------------------*/
|
|
250
|
+
/* ------------------------------- Render ------------------------------- */
|
|
251
|
+
/*------------------------------------------------------------------------*/
|
|
252
|
+
|
|
253
|
+
/*----------------------------------------*/
|
|
254
|
+
/* --------------- Main UI -------------- */
|
|
255
|
+
/*----------------------------------------*/
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<ProgressBar
|
|
259
|
+
percentProgress={progress}
|
|
260
|
+
striped={striped}
|
|
261
|
+
variant={variant}
|
|
262
|
+
bgVariant={bgVariant}
|
|
263
|
+
showOutline={showOutline}
|
|
264
|
+
size={size}
|
|
265
|
+
/>
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/*------------------------------------------------------------------------*/
|
|
270
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
271
|
+
/*------------------------------------------------------------------------*/
|
|
272
|
+
|
|
273
|
+
// Export component
|
|
274
|
+
export default FakeProgressBar;
|
|
@@ -51,6 +51,8 @@ type Props = {
|
|
|
51
51
|
dontAllowFuture?: boolean,
|
|
52
52
|
// If true, the chooser is disabled
|
|
53
53
|
isDisabled?: boolean,
|
|
54
|
+
// If true, hide the "day" dropdown and just show the month and year
|
|
55
|
+
hideDay?: boolean,
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
/*------------------------------------------------------------------------*/
|
|
@@ -292,6 +294,7 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
292
294
|
day,
|
|
293
295
|
year,
|
|
294
296
|
isDisabled = false,
|
|
297
|
+
hideDay,
|
|
295
298
|
} = props;
|
|
296
299
|
|
|
297
300
|
// Get choices
|
|
@@ -442,24 +445,26 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
442
445
|
</select>
|
|
443
446
|
|
|
444
447
|
{/* Day Chooser */}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
448
|
+
{!hideDay && (
|
|
449
|
+
<select
|
|
450
|
+
aria-label={`day for ${ariaLabel}`}
|
|
451
|
+
className="custom-select form-select d-inline-block"
|
|
452
|
+
style={{ width: 'auto' }}
|
|
453
|
+
id={`SimpleDateChooser-${name}-day`}
|
|
454
|
+
value={day}
|
|
455
|
+
onChange={(e) => {
|
|
456
|
+
// Only change the day
|
|
457
|
+
onChange(
|
|
458
|
+
month,
|
|
459
|
+
Number.parseInt(e.target.value, 10),
|
|
460
|
+
year,
|
|
461
|
+
);
|
|
462
|
+
}}
|
|
463
|
+
disabled={isDisabled}
|
|
464
|
+
>
|
|
465
|
+
{dayOptions}
|
|
466
|
+
</select>
|
|
467
|
+
)}
|
|
463
468
|
</div>
|
|
464
469
|
);
|
|
465
470
|
}
|
|
@@ -476,10 +481,14 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
476
481
|
aria-label={`edit date for ${ariaLabel}`}
|
|
477
482
|
>
|
|
478
483
|
{getMonthName(month).full}
|
|
479
|
-
{
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
484
|
+
{!hideDay && (
|
|
485
|
+
<>
|
|
486
|
+
{' '}
|
|
487
|
+
{day}
|
|
488
|
+
{getOrdinal(day)}
|
|
489
|
+
,
|
|
490
|
+
</>
|
|
491
|
+
)}
|
|
483
492
|
{' '}
|
|
484
493
|
{year}
|
|
485
494
|
</button>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A very simple, lightweight month chooser
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Import other components
|
|
10
|
+
import SimpleDateChooser from './SimpleDateChooser';
|
|
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 month
|
|
22
|
+
month: number,
|
|
23
|
+
// Currently selected year
|
|
24
|
+
year: number,
|
|
25
|
+
/**
|
|
26
|
+
* Handler for when month changes
|
|
27
|
+
* @param month new 1-indexed month number
|
|
28
|
+
* @param year new full year number
|
|
29
|
+
*/
|
|
30
|
+
onChange: (month: number, year: number) => void,
|
|
31
|
+
// Number of months in either the past or future to allow the user to choose from
|
|
32
|
+
// If we aren't allowing the past or the future, we will throw an error
|
|
33
|
+
// (max is 12, default is 6 in the past and 6 in the future)
|
|
34
|
+
numMonthsToShow?: number,
|
|
35
|
+
// If true, the user isn't allowed to select dates in the past
|
|
36
|
+
dontAllowPast?: boolean,
|
|
37
|
+
// If true, the user isn't allowed to select dates in the future
|
|
38
|
+
dontAllowFuture?: boolean,
|
|
39
|
+
// If true, the chooser is disabled
|
|
40
|
+
isDisabled?: boolean,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/*------------------------------------------------------------------------*/
|
|
44
|
+
/* ------------------------------ Component ----------------------------- */
|
|
45
|
+
/*------------------------------------------------------------------------*/
|
|
46
|
+
|
|
47
|
+
const SimpleMonthChooser: React.FC<Props> = (props) => {
|
|
48
|
+
/*------------------------------------------------------------------------*/
|
|
49
|
+
/* -------------------------------- Setup ------------------------------- */
|
|
50
|
+
/*------------------------------------------------------------------------*/
|
|
51
|
+
|
|
52
|
+
/* -------------- Props ------------- */
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
ariaLabel,
|
|
56
|
+
name,
|
|
57
|
+
dontAllowPast,
|
|
58
|
+
dontAllowFuture,
|
|
59
|
+
numMonthsToShow,
|
|
60
|
+
onChange,
|
|
61
|
+
month,
|
|
62
|
+
year,
|
|
63
|
+
isDisabled,
|
|
64
|
+
} = props;
|
|
65
|
+
|
|
66
|
+
/*------------------------------------------------------------------------*/
|
|
67
|
+
/* ------------------------------- Render ------------------------------- */
|
|
68
|
+
/*------------------------------------------------------------------------*/
|
|
69
|
+
|
|
70
|
+
/*----------------------------------------*/
|
|
71
|
+
/* --------------- Main UI -------------- */
|
|
72
|
+
/*----------------------------------------*/
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<SimpleDateChooser
|
|
76
|
+
ariaLabel={ariaLabel}
|
|
77
|
+
name={name}
|
|
78
|
+
month={month}
|
|
79
|
+
day={1}
|
|
80
|
+
year={year}
|
|
81
|
+
numMonthsToShow={numMonthsToShow}
|
|
82
|
+
dontAllowPast={dontAllowPast}
|
|
83
|
+
dontAllowFuture={dontAllowFuture}
|
|
84
|
+
isDisabled={isDisabled}
|
|
85
|
+
hideDay
|
|
86
|
+
onChange={(newMonth, newDay, newYear) => {
|
|
87
|
+
onChange(newMonth, newYear);
|
|
88
|
+
}}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/*------------------------------------------------------------------------*/
|
|
94
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
95
|
+
/*------------------------------------------------------------------------*/
|
|
96
|
+
|
|
97
|
+
// Export component
|
|
98
|
+
export default SimpleMonthChooser;
|
package/src/index.ts
CHANGED
|
@@ -86,6 +86,7 @@ import RadioButton from './components/RadioButton';
|
|
|
86
86
|
import CheckboxButton from './components/CheckboxButton';
|
|
87
87
|
import ButtonInputGroup from './components/ButtonInputGroup';
|
|
88
88
|
import SimpleDateChooser from './components/SimpleDateChooser';
|
|
89
|
+
import SimpleMonthChooser from './components/SimpleMonthChooser';
|
|
89
90
|
import SimpleTimeChooser from './components/SimpleTimeChooser';
|
|
90
91
|
import Drawer from './components/Drawer';
|
|
91
92
|
import PopSuccessMark from './components/PopSuccessMark';
|
|
@@ -103,6 +104,7 @@ import AutoscrollToBottomContainer from './components/AutoscrollToBottomContaine
|
|
|
103
104
|
import MultiSwitch from './components/MultiSwitch';
|
|
104
105
|
import Dropdown from './components/Dropdown';
|
|
105
106
|
import ProgressBar from './components/ProgressBar';
|
|
107
|
+
import FakeProgressBar from './components/FakeProgressBar';
|
|
106
108
|
|
|
107
109
|
// Import dynamic constants
|
|
108
110
|
import DynamicWord from './dynamicConstants/DynamicWord';
|
|
@@ -146,6 +148,7 @@ export {
|
|
|
146
148
|
CheckboxButton,
|
|
147
149
|
ButtonInputGroup,
|
|
148
150
|
SimpleDateChooser,
|
|
151
|
+
SimpleMonthChooser,
|
|
149
152
|
SimpleTimeChooser,
|
|
150
153
|
Drawer,
|
|
151
154
|
PopSuccessMark,
|
|
@@ -163,6 +166,7 @@ export {
|
|
|
163
166
|
MultiSwitch,
|
|
164
167
|
Dropdown,
|
|
165
168
|
ProgressBar,
|
|
169
|
+
FakeProgressBar,
|
|
166
170
|
// Global functions
|
|
167
171
|
alert,
|
|
168
172
|
prompt,
|