@sproutsocial/seeds-react-duration 1.0.0
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/.eslintignore +6 -0
- package/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +21 -0
- package/CHANGELOG.md +7 -0
- package/dist/esm/index.js +176 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +215 -0
- package/dist/index.js.map +1 -0
- package/jest.config.js +13 -0
- package/package.json +45 -0
- package/src/Duration.stories.tsx +82 -0
- package/src/Duration.tsx +151 -0
- package/src/DurationTypes.ts +34 -0
- package/src/__tests__/Duration.typetest.tsx +29 -0
- package/src/__tests__/features/A11y.test.tsx +11 -0
- package/src/__tests__/features/defaults.test.ts +116 -0
- package/src/__tests__/features/display.test.ts +102 -0
- package/src/__tests__/features/displayUnits.test.ts +548 -0
- package/src/__tests__/features/invalid.test.ts +32 -0
- package/src/__tests__/features/locale.test.ts +87 -0
- package/src/__tests__/features/negative.test.ts +82 -0
- package/src/__tests__/features/testDuration.tsx +37 -0
- package/src/__tests__/features/utils.test.ts +260 -0
- package/src/__tests__/features/zero.test.ts +82 -0
- package/src/constants.ts +41 -0
- package/src/index.ts +12 -0
- package/src/styles.ts +6 -0
- package/src/utils.ts +89 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +12 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import testDuration from "./testDuration";
|
|
2
|
+
|
|
3
|
+
describe("When using a negative value...", () => {
|
|
4
|
+
describe("... it renders properly", () => {
|
|
5
|
+
testDuration(-1, {}, { text: "0s" });
|
|
6
|
+
testDuration(
|
|
7
|
+
-1,
|
|
8
|
+
{
|
|
9
|
+
displayUnits: {
|
|
10
|
+
days: true,
|
|
11
|
+
hours: true,
|
|
12
|
+
minutes: true,
|
|
13
|
+
seconds: true,
|
|
14
|
+
milliseconds: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{ text: "0ms" }
|
|
18
|
+
);
|
|
19
|
+
testDuration(
|
|
20
|
+
-1,
|
|
21
|
+
{
|
|
22
|
+
displayUnits: { days: true, hours: true, minutes: true, seconds: true },
|
|
23
|
+
},
|
|
24
|
+
{ text: "0s" }
|
|
25
|
+
);
|
|
26
|
+
testDuration(
|
|
27
|
+
-1,
|
|
28
|
+
{ displayUnits: { days: true, hours: true, minutes: true } },
|
|
29
|
+
{ text: "0m" }
|
|
30
|
+
);
|
|
31
|
+
testDuration(
|
|
32
|
+
-1,
|
|
33
|
+
{ displayUnits: { days: true, hours: true } },
|
|
34
|
+
{ text: "0h" }
|
|
35
|
+
);
|
|
36
|
+
testDuration(-1, { displayUnits: { days: true } }, { text: "0d" });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("... should work with display long", () => {
|
|
40
|
+
testDuration(-1, { display: "long" }, { text: "0 seconds" });
|
|
41
|
+
testDuration(
|
|
42
|
+
-1,
|
|
43
|
+
{
|
|
44
|
+
display: "long",
|
|
45
|
+
displayUnits: {
|
|
46
|
+
days: true,
|
|
47
|
+
hours: true,
|
|
48
|
+
minutes: true,
|
|
49
|
+
seconds: true,
|
|
50
|
+
milliseconds: true,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{ text: "0 milliseconds" }
|
|
54
|
+
);
|
|
55
|
+
testDuration(
|
|
56
|
+
-1,
|
|
57
|
+
{
|
|
58
|
+
display: "long",
|
|
59
|
+
displayUnits: { days: true, hours: true, minutes: true, seconds: true },
|
|
60
|
+
},
|
|
61
|
+
{ text: "0 seconds" }
|
|
62
|
+
);
|
|
63
|
+
testDuration(
|
|
64
|
+
-1,
|
|
65
|
+
{
|
|
66
|
+
display: "long",
|
|
67
|
+
displayUnits: { days: true, hours: true, minutes: true },
|
|
68
|
+
},
|
|
69
|
+
{ text: "0 minutes" }
|
|
70
|
+
);
|
|
71
|
+
testDuration(
|
|
72
|
+
-1,
|
|
73
|
+
{ display: "long", displayUnits: { days: true, hours: true } },
|
|
74
|
+
{ text: "0 hours" }
|
|
75
|
+
);
|
|
76
|
+
testDuration(
|
|
77
|
+
-1,
|
|
78
|
+
{ display: "long", displayUnits: { days: true } },
|
|
79
|
+
{ text: "0 days" }
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* eslint-env jest, node */
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
4
|
+
import Duration, { formatDuration } from "../../Duration";
|
|
5
|
+
|
|
6
|
+
jest.mock("../../constants.ts", () => ({
|
|
7
|
+
...jest.requireActual("../../constants.ts"),
|
|
8
|
+
MEMO_CACHE_SIZE: 0,
|
|
9
|
+
COMPARE_OBJECTS: false,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
// @ts-ignore TS error later
|
|
13
|
+
const testDuration = (milliseconds, options = {}, texts) => {
|
|
14
|
+
const { text: mainText, label: labelText, format: formatText } = texts;
|
|
15
|
+
|
|
16
|
+
test(`Should display ${milliseconds} as -> ${mainText}`, () => {
|
|
17
|
+
render(<Duration milliseconds={milliseconds} {...options} />);
|
|
18
|
+
const duration = screen.getByText(mainText);
|
|
19
|
+
expect(duration).toBeInTheDocument();
|
|
20
|
+
if (labelText) {
|
|
21
|
+
expect(screen.getByText(labelText)).toBeInTheDocument();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// formatDuration test
|
|
25
|
+
if (formatText) {
|
|
26
|
+
expect(
|
|
27
|
+
formatDuration({ milliseconds: milliseconds, ...options })
|
|
28
|
+
).toEqual(formatText);
|
|
29
|
+
} else {
|
|
30
|
+
expect(
|
|
31
|
+
formatDuration({ milliseconds: milliseconds, ...options })
|
|
32
|
+
).toEqual(mainText);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default testDuration;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { getDurationMaxDisplayUnits } from "../../index";
|
|
2
|
+
|
|
3
|
+
// full
|
|
4
|
+
const millisecondsDuration = 123; // 123ms
|
|
5
|
+
const secondsDuration = 12345; // 12s 345ms
|
|
6
|
+
const minutesDuration = 1234567; // 20m 34s 567ms
|
|
7
|
+
const hoursDuration = 12345678; // 3h 25m 45s 678ms
|
|
8
|
+
const daysDuration = 123456789; // 1d 10h 17m 36s 789ms
|
|
9
|
+
|
|
10
|
+
// partial
|
|
11
|
+
const minutesPartialDuration = 1200567; // 20m 567ms
|
|
12
|
+
const hoursPartialDuration = 10845000; // 3h 45s
|
|
13
|
+
const daysPartialDuration = 87420789; // 1d 17m 789ms
|
|
14
|
+
|
|
15
|
+
describe("When calling getDurationMaxDisplayUnits...", () => {
|
|
16
|
+
describe("... it returns the correct value ...", () => {
|
|
17
|
+
test.each([
|
|
18
|
+
// null
|
|
19
|
+
{ milliseconds: null, maxDisplayUnits: 1, expected: {} },
|
|
20
|
+
// zero
|
|
21
|
+
{ milliseconds: 0, maxDisplayUnits: 1, expected: {} },
|
|
22
|
+
// milliseconds
|
|
23
|
+
{ milliseconds: millisecondsDuration, maxDisplayUnits: 0, expected: {} },
|
|
24
|
+
{
|
|
25
|
+
milliseconds: millisecondsDuration,
|
|
26
|
+
maxDisplayUnits: 1,
|
|
27
|
+
expected: { milliseconds: true },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
milliseconds: millisecondsDuration,
|
|
31
|
+
maxDisplayUnits: 2,
|
|
32
|
+
expected: { milliseconds: true },
|
|
33
|
+
},
|
|
34
|
+
// seconds
|
|
35
|
+
{ milliseconds: secondsDuration, maxDisplayUnits: 0, expected: {} },
|
|
36
|
+
{
|
|
37
|
+
milliseconds: secondsDuration,
|
|
38
|
+
maxDisplayUnits: 1,
|
|
39
|
+
expected: { seconds: true },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
milliseconds: secondsDuration,
|
|
43
|
+
maxDisplayUnits: 2,
|
|
44
|
+
expected: { seconds: true, milliseconds: true },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
milliseconds: secondsDuration,
|
|
48
|
+
maxDisplayUnits: 3,
|
|
49
|
+
expected: { seconds: true, milliseconds: true },
|
|
50
|
+
},
|
|
51
|
+
// minutes
|
|
52
|
+
{ milliseconds: minutesDuration, maxDisplayUnits: 0, expected: {} },
|
|
53
|
+
{
|
|
54
|
+
milliseconds: minutesDuration,
|
|
55
|
+
maxDisplayUnits: 1,
|
|
56
|
+
expected: { minutes: true },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
milliseconds: minutesDuration,
|
|
60
|
+
maxDisplayUnits: 2,
|
|
61
|
+
expected: { minutes: true, seconds: true },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
milliseconds: minutesDuration,
|
|
65
|
+
maxDisplayUnits: 3,
|
|
66
|
+
expected: { minutes: true, seconds: true, milliseconds: true },
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
milliseconds: minutesDuration,
|
|
70
|
+
maxDisplayUnits: 4,
|
|
71
|
+
expected: { minutes: true, seconds: true, milliseconds: true },
|
|
72
|
+
},
|
|
73
|
+
// hours
|
|
74
|
+
{ milliseconds: hoursDuration, maxDisplayUnits: 0, expected: {} },
|
|
75
|
+
{
|
|
76
|
+
milliseconds: hoursDuration,
|
|
77
|
+
maxDisplayUnits: 1,
|
|
78
|
+
expected: { hours: true },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
milliseconds: hoursDuration,
|
|
82
|
+
maxDisplayUnits: 2,
|
|
83
|
+
expected: { hours: true, minutes: true },
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
milliseconds: hoursDuration,
|
|
87
|
+
maxDisplayUnits: 3,
|
|
88
|
+
expected: { hours: true, minutes: true, seconds: true },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
milliseconds: hoursDuration,
|
|
92
|
+
maxDisplayUnits: 4,
|
|
93
|
+
expected: {
|
|
94
|
+
hours: true,
|
|
95
|
+
minutes: true,
|
|
96
|
+
seconds: true,
|
|
97
|
+
milliseconds: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
milliseconds: hoursDuration,
|
|
102
|
+
maxDisplayUnits: 5,
|
|
103
|
+
expected: {
|
|
104
|
+
hours: true,
|
|
105
|
+
minutes: true,
|
|
106
|
+
seconds: true,
|
|
107
|
+
milliseconds: true,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
// days
|
|
111
|
+
{ milliseconds: daysDuration, maxDisplayUnits: 0, expected: {} },
|
|
112
|
+
{
|
|
113
|
+
milliseconds: daysDuration,
|
|
114
|
+
maxDisplayUnits: 1,
|
|
115
|
+
expected: { days: true },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
milliseconds: daysDuration,
|
|
119
|
+
maxDisplayUnits: 2,
|
|
120
|
+
expected: { days: true, hours: true },
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
milliseconds: daysDuration,
|
|
124
|
+
maxDisplayUnits: 3,
|
|
125
|
+
expected: { days: true, hours: true, minutes: true },
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
milliseconds: daysDuration,
|
|
129
|
+
maxDisplayUnits: 4,
|
|
130
|
+
expected: { days: true, hours: true, minutes: true, seconds: true },
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
milliseconds: daysDuration,
|
|
134
|
+
maxDisplayUnits: 5,
|
|
135
|
+
expected: {
|
|
136
|
+
days: true,
|
|
137
|
+
hours: true,
|
|
138
|
+
minutes: true,
|
|
139
|
+
seconds: true,
|
|
140
|
+
milliseconds: true,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
milliseconds: daysDuration,
|
|
145
|
+
maxDisplayUnits: 6,
|
|
146
|
+
expected: {
|
|
147
|
+
days: true,
|
|
148
|
+
hours: true,
|
|
149
|
+
minutes: true,
|
|
150
|
+
seconds: true,
|
|
151
|
+
milliseconds: true,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
// partial minutes
|
|
155
|
+
{
|
|
156
|
+
milliseconds: minutesPartialDuration,
|
|
157
|
+
maxDisplayUnits: 0,
|
|
158
|
+
expected: {},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
milliseconds: minutesPartialDuration,
|
|
162
|
+
maxDisplayUnits: 1,
|
|
163
|
+
expected: {
|
|
164
|
+
minutes: true,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
milliseconds: minutesPartialDuration,
|
|
169
|
+
maxDisplayUnits: 2,
|
|
170
|
+
expected: {
|
|
171
|
+
minutes: true,
|
|
172
|
+
milliseconds: true,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
milliseconds: minutesPartialDuration,
|
|
177
|
+
maxDisplayUnits: 3,
|
|
178
|
+
expected: {
|
|
179
|
+
minutes: true,
|
|
180
|
+
milliseconds: true,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
// partial hours
|
|
184
|
+
{
|
|
185
|
+
milliseconds: hoursPartialDuration,
|
|
186
|
+
maxDisplayUnits: 0,
|
|
187
|
+
expected: {},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
milliseconds: hoursPartialDuration,
|
|
191
|
+
maxDisplayUnits: 1,
|
|
192
|
+
expected: {
|
|
193
|
+
hours: true,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
milliseconds: hoursPartialDuration,
|
|
198
|
+
maxDisplayUnits: 2,
|
|
199
|
+
expected: {
|
|
200
|
+
hours: true,
|
|
201
|
+
seconds: true,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
milliseconds: hoursPartialDuration,
|
|
206
|
+
maxDisplayUnits: 3,
|
|
207
|
+
expected: {
|
|
208
|
+
hours: true,
|
|
209
|
+
seconds: true,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
// partial days
|
|
213
|
+
{
|
|
214
|
+
milliseconds: daysPartialDuration,
|
|
215
|
+
maxDisplayUnits: 0,
|
|
216
|
+
expected: {},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
milliseconds: daysPartialDuration,
|
|
220
|
+
maxDisplayUnits: 1,
|
|
221
|
+
expected: {
|
|
222
|
+
days: true,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
milliseconds: daysPartialDuration,
|
|
227
|
+
maxDisplayUnits: 2,
|
|
228
|
+
expected: {
|
|
229
|
+
days: true,
|
|
230
|
+
minutes: true,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
milliseconds: daysPartialDuration,
|
|
235
|
+
maxDisplayUnits: 3,
|
|
236
|
+
expected: {
|
|
237
|
+
days: true,
|
|
238
|
+
minutes: true,
|
|
239
|
+
milliseconds: true,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
milliseconds: daysPartialDuration,
|
|
244
|
+
maxDisplayUnits: 4,
|
|
245
|
+
expected: {
|
|
246
|
+
days: true,
|
|
247
|
+
minutes: true,
|
|
248
|
+
milliseconds: true,
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
])(
|
|
252
|
+
"given milliseconds: $milliseconds and maxDisplayUnits: $maxDisplayUnits",
|
|
253
|
+
({ milliseconds, maxDisplayUnits, expected }) => {
|
|
254
|
+
expect(
|
|
255
|
+
getDurationMaxDisplayUnits({ milliseconds, maxDisplayUnits })
|
|
256
|
+
).toEqual(expected);
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import testDuration from "./testDuration";
|
|
2
|
+
|
|
3
|
+
describe("When using a zero value...", () => {
|
|
4
|
+
describe("... it renders properly", () => {
|
|
5
|
+
testDuration(0.0, {}, { text: "0s" });
|
|
6
|
+
testDuration(
|
|
7
|
+
0.0,
|
|
8
|
+
{
|
|
9
|
+
displayUnits: {
|
|
10
|
+
days: true,
|
|
11
|
+
hours: true,
|
|
12
|
+
minutes: true,
|
|
13
|
+
seconds: true,
|
|
14
|
+
milliseconds: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{ text: "0ms" }
|
|
18
|
+
);
|
|
19
|
+
testDuration(
|
|
20
|
+
0.0,
|
|
21
|
+
{
|
|
22
|
+
displayUnits: { days: true, hours: true, minutes: true, seconds: true },
|
|
23
|
+
},
|
|
24
|
+
{ text: "0s" }
|
|
25
|
+
);
|
|
26
|
+
testDuration(
|
|
27
|
+
0.0,
|
|
28
|
+
{ displayUnits: { days: true, hours: true, minutes: true } },
|
|
29
|
+
{ text: "0m" }
|
|
30
|
+
);
|
|
31
|
+
testDuration(
|
|
32
|
+
0.0,
|
|
33
|
+
{ displayUnits: { days: true, hours: true } },
|
|
34
|
+
{ text: "0h" }
|
|
35
|
+
);
|
|
36
|
+
testDuration(0.0, { displayUnits: { days: true } }, { text: "0d" });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("... should work with display long", () => {
|
|
40
|
+
testDuration(0.0, { display: "long" }, { text: "0 seconds" });
|
|
41
|
+
testDuration(
|
|
42
|
+
0.0,
|
|
43
|
+
{
|
|
44
|
+
display: "long",
|
|
45
|
+
displayUnits: {
|
|
46
|
+
days: true,
|
|
47
|
+
hours: true,
|
|
48
|
+
minutes: true,
|
|
49
|
+
seconds: true,
|
|
50
|
+
milliseconds: true,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{ text: "0 milliseconds" }
|
|
54
|
+
);
|
|
55
|
+
testDuration(
|
|
56
|
+
0.0,
|
|
57
|
+
{
|
|
58
|
+
display: "long",
|
|
59
|
+
displayUnits: { days: true, hours: true, minutes: true, seconds: true },
|
|
60
|
+
},
|
|
61
|
+
{ text: "0 seconds" }
|
|
62
|
+
);
|
|
63
|
+
testDuration(
|
|
64
|
+
0.0,
|
|
65
|
+
{
|
|
66
|
+
display: "long",
|
|
67
|
+
displayUnits: { days: true, hours: true, minutes: true },
|
|
68
|
+
},
|
|
69
|
+
{ text: "0 minutes" }
|
|
70
|
+
);
|
|
71
|
+
testDuration(
|
|
72
|
+
0.0,
|
|
73
|
+
{ display: "long", displayUnits: { days: true, hours: true } },
|
|
74
|
+
{ text: "0 hours" }
|
|
75
|
+
);
|
|
76
|
+
testDuration(
|
|
77
|
+
0.0,
|
|
78
|
+
{ display: "long", displayUnits: { days: true } },
|
|
79
|
+
{ text: "0 days" }
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const COMPARE_OBJECTS = true;
|
|
2
|
+
export const MEMO_CACHE_SIZE = 10;
|
|
3
|
+
|
|
4
|
+
export const UNITS = {
|
|
5
|
+
days: "days",
|
|
6
|
+
hours: "hours",
|
|
7
|
+
minutes: "minutes",
|
|
8
|
+
seconds: "seconds",
|
|
9
|
+
milliseconds: "milliseconds",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const MILLISECONDS_IN = {
|
|
13
|
+
[UNITS.days]: 1 * 1000 * 60 * 60 * 24,
|
|
14
|
+
[UNITS.hours]: 1 * 1000 * 60 * 60,
|
|
15
|
+
[UNITS.minutes]: 1 * 1000 * 60,
|
|
16
|
+
[UNITS.seconds]: 1 * 1000,
|
|
17
|
+
[UNITS.milliseconds]: 1,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const ORDERED_UNITS = [
|
|
21
|
+
UNITS.days,
|
|
22
|
+
UNITS.hours,
|
|
23
|
+
UNITS.minutes,
|
|
24
|
+
UNITS.seconds,
|
|
25
|
+
UNITS.milliseconds,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// Duration props defaults
|
|
29
|
+
export const DEFAULT_DISPLAY = "narrow";
|
|
30
|
+
export const DEFAULT_DISPLAY_UNITS = {
|
|
31
|
+
[UNITS.days]: true,
|
|
32
|
+
[UNITS.hours]: true,
|
|
33
|
+
[UNITS.minutes]: true,
|
|
34
|
+
[UNITS.seconds]: true,
|
|
35
|
+
[UNITS.milliseconds]: false,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const DEFAULT_LOCALE = "en-US";
|
|
39
|
+
export const DEFAULT_MILLISECONDS = null;
|
|
40
|
+
|
|
41
|
+
export const EM_DASH = "—"; // shift + option + hyphen on a mac keyboard
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Duration, { formatDuration } from "./Duration";
|
|
2
|
+
import {
|
|
3
|
+
getDurationMaxDisplayUnits,
|
|
4
|
+
type TypeGetDurationMaxDisplayUnitsProps,
|
|
5
|
+
} from "./utils";
|
|
6
|
+
|
|
7
|
+
export default Duration;
|
|
8
|
+
export { Duration };
|
|
9
|
+
export { formatDuration };
|
|
10
|
+
export { getDurationMaxDisplayUnits };
|
|
11
|
+
export type { TypeGetDurationMaxDisplayUnitsProps };
|
|
12
|
+
export * from "./DurationTypes";
|
package/src/styles.ts
ADDED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TypeDurationDisplayUnits,
|
|
3
|
+
TypeDurationMilliseconds,
|
|
4
|
+
} from "./DurationTypes";
|
|
5
|
+
import { MILLISECONDS_IN, ORDERED_UNITS, UNITS } from "./constants";
|
|
6
|
+
|
|
7
|
+
export const getLowestUnit = (displayUnits: TypeDurationDisplayUnits) =>
|
|
8
|
+
[...ORDERED_UNITS]
|
|
9
|
+
.reverse()
|
|
10
|
+
.find((unit) => displayUnits[unit as keyof TypeDurationDisplayUnits]) ||
|
|
11
|
+
UNITS.milliseconds;
|
|
12
|
+
|
|
13
|
+
export const splitMillisecondsIntoUnits = (
|
|
14
|
+
milliseconds: number,
|
|
15
|
+
displayUnits: TypeDurationDisplayUnits
|
|
16
|
+
): {
|
|
17
|
+
days?: number;
|
|
18
|
+
hours?: number;
|
|
19
|
+
minutes?: number;
|
|
20
|
+
seconds?: number;
|
|
21
|
+
milliseconds?: number;
|
|
22
|
+
} => {
|
|
23
|
+
const lowestUnit = getLowestUnit(displayUnits);
|
|
24
|
+
|
|
25
|
+
// @ts-ignore TS error later
|
|
26
|
+
const remainder = milliseconds % MILLISECONDS_IN[lowestUnit];
|
|
27
|
+
// @ts-ignore TS error later
|
|
28
|
+
if (2 * remainder >= MILLISECONDS_IN[lowestUnit]) {
|
|
29
|
+
// if the remainder is large, add enough seconds to increse the lowest unit
|
|
30
|
+
// @ts-ignore TS error later
|
|
31
|
+
milliseconds += MILLISECONDS_IN[lowestUnit] - remainder;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const units = {};
|
|
35
|
+
|
|
36
|
+
ORDERED_UNITS.forEach((unit) => {
|
|
37
|
+
// @ts-ignore TS error later
|
|
38
|
+
if (displayUnits[unit]) {
|
|
39
|
+
// @ts-ignore TS error later
|
|
40
|
+
units[unit] = Math.floor(milliseconds / MILLISECONDS_IN[unit]);
|
|
41
|
+
// @ts-ignore TS error later
|
|
42
|
+
milliseconds -= units[unit] * MILLISECONDS_IN[unit];
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return units;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const isValidNumber = (value: unknown): boolean =>
|
|
50
|
+
typeof value === "number" && isFinite(value);
|
|
51
|
+
|
|
52
|
+
export interface TypeGetDurationMaxDisplayUnitsProps {
|
|
53
|
+
milliseconds: TypeDurationMilliseconds;
|
|
54
|
+
maxDisplayUnits: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getDurationMaxDisplayUnits = ({
|
|
58
|
+
milliseconds,
|
|
59
|
+
maxDisplayUnits = ORDERED_UNITS.length,
|
|
60
|
+
}: TypeGetDurationMaxDisplayUnitsProps): TypeDurationDisplayUnits => {
|
|
61
|
+
const displayUnits = {};
|
|
62
|
+
|
|
63
|
+
if (!isValidNumber(milliseconds)) {
|
|
64
|
+
return displayUnits;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const unit of ORDERED_UNITS) {
|
|
68
|
+
if (Object.keys(displayUnits).length >= maxDisplayUnits) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\_(ツ)_/¯
|
|
73
|
+
const millisecondsByUnit = splitMillisecondsIntoUnits(milliseconds, {
|
|
74
|
+
days: true,
|
|
75
|
+
hours: true,
|
|
76
|
+
minutes: true,
|
|
77
|
+
seconds: true,
|
|
78
|
+
milliseconds: true,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\_(ツ)_/¯
|
|
82
|
+
if (milliseconds > MILLISECONDS_IN[unit] && millisecondsByUnit[unit] > 0) {
|
|
83
|
+
// @ts-ignore TS error later
|
|
84
|
+
displayUnits[unit] = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return displayUnits;
|
|
89
|
+
};
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig((options) => ({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
|
+
clean: true,
|
|
7
|
+
legacyOutput: true,
|
|
8
|
+
dts: options.dts,
|
|
9
|
+
external: ["react"],
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
metafile: options.metafile,
|
|
12
|
+
}));
|