@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
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
yarn run v1.22.22
|
|
2
|
+
$ tsup --dts
|
|
3
|
+
CLI Building entry: src/index.ts
|
|
4
|
+
CLI Using tsconfig: tsconfig.json
|
|
5
|
+
CLI tsup v8.0.2
|
|
6
|
+
CLI Using tsup config: /home/runner/work/seeds/seeds/seeds-react/seeds-react-duration/tsup.config.ts
|
|
7
|
+
CLI Target: es2022
|
|
8
|
+
CLI Cleaning output folder
|
|
9
|
+
CJS Build start
|
|
10
|
+
ESM Build start
|
|
11
|
+
CJS dist/index.js 7.43 KB
|
|
12
|
+
CJS dist/index.js.map 12.26 KB
|
|
13
|
+
CJS ⚡️ Build success in 117ms
|
|
14
|
+
ESM dist/esm/index.js 5.42 KB
|
|
15
|
+
ESM dist/esm/index.js.map 12.18 KB
|
|
16
|
+
ESM ⚡️ Build success in 117ms
|
|
17
|
+
DTS Build start
|
|
18
|
+
DTS ⚡️ Build success in 19437ms
|
|
19
|
+
DTS dist/index.d.ts 1.70 KB
|
|
20
|
+
DTS dist/index.d.mts 1.70 KB
|
|
21
|
+
Done in 26.38s.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// src/Duration.tsx
|
|
2
|
+
import "react";
|
|
3
|
+
import memoize from "lru-memoize";
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var COMPARE_OBJECTS = true;
|
|
7
|
+
var MEMO_CACHE_SIZE = 10;
|
|
8
|
+
var UNITS = {
|
|
9
|
+
days: "days",
|
|
10
|
+
hours: "hours",
|
|
11
|
+
minutes: "minutes",
|
|
12
|
+
seconds: "seconds",
|
|
13
|
+
milliseconds: "milliseconds"
|
|
14
|
+
};
|
|
15
|
+
var MILLISECONDS_IN = {
|
|
16
|
+
[UNITS.days]: 1 * 1e3 * 60 * 60 * 24,
|
|
17
|
+
[UNITS.hours]: 1 * 1e3 * 60 * 60,
|
|
18
|
+
[UNITS.minutes]: 1 * 1e3 * 60,
|
|
19
|
+
[UNITS.seconds]: 1 * 1e3,
|
|
20
|
+
[UNITS.milliseconds]: 1
|
|
21
|
+
};
|
|
22
|
+
var ORDERED_UNITS = [
|
|
23
|
+
UNITS.days,
|
|
24
|
+
UNITS.hours,
|
|
25
|
+
UNITS.minutes,
|
|
26
|
+
UNITS.seconds,
|
|
27
|
+
UNITS.milliseconds
|
|
28
|
+
];
|
|
29
|
+
var DEFAULT_DISPLAY = "narrow";
|
|
30
|
+
var 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
|
+
var DEFAULT_LOCALE = "en-US";
|
|
38
|
+
var DEFAULT_MILLISECONDS = null;
|
|
39
|
+
var EM_DASH = "\u2014";
|
|
40
|
+
|
|
41
|
+
// src/Duration.tsx
|
|
42
|
+
import { VisuallyHidden } from "@sproutsocial/seeds-react-visually-hidden";
|
|
43
|
+
|
|
44
|
+
// src/styles.ts
|
|
45
|
+
import styled from "styled-components";
|
|
46
|
+
import Text from "@sproutsocial/seeds-react-text";
|
|
47
|
+
var Container = styled(Text)`
|
|
48
|
+
font-variant-numeric: tabular-nums;
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
// src/utils.ts
|
|
52
|
+
var getLowestUnit = (displayUnits) => [...ORDERED_UNITS].reverse().find((unit) => displayUnits[unit]) || UNITS.milliseconds;
|
|
53
|
+
var splitMillisecondsIntoUnits = (milliseconds, displayUnits) => {
|
|
54
|
+
const lowestUnit = getLowestUnit(displayUnits);
|
|
55
|
+
const remainder = milliseconds % MILLISECONDS_IN[lowestUnit];
|
|
56
|
+
if (2 * remainder >= MILLISECONDS_IN[lowestUnit]) {
|
|
57
|
+
milliseconds += MILLISECONDS_IN[lowestUnit] - remainder;
|
|
58
|
+
}
|
|
59
|
+
const units = {};
|
|
60
|
+
ORDERED_UNITS.forEach((unit) => {
|
|
61
|
+
if (displayUnits[unit]) {
|
|
62
|
+
units[unit] = Math.floor(milliseconds / MILLISECONDS_IN[unit]);
|
|
63
|
+
milliseconds -= units[unit] * MILLISECONDS_IN[unit];
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return units;
|
|
67
|
+
};
|
|
68
|
+
var isValidNumber = (value) => typeof value === "number" && isFinite(value);
|
|
69
|
+
var getDurationMaxDisplayUnits = ({
|
|
70
|
+
milliseconds,
|
|
71
|
+
maxDisplayUnits = ORDERED_UNITS.length
|
|
72
|
+
}) => {
|
|
73
|
+
const displayUnits = {};
|
|
74
|
+
if (!isValidNumber(milliseconds)) {
|
|
75
|
+
return displayUnits;
|
|
76
|
+
}
|
|
77
|
+
for (const unit of ORDERED_UNITS) {
|
|
78
|
+
if (Object.keys(displayUnits).length >= maxDisplayUnits) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
const millisecondsByUnit = splitMillisecondsIntoUnits(milliseconds, {
|
|
82
|
+
days: true,
|
|
83
|
+
hours: true,
|
|
84
|
+
minutes: true,
|
|
85
|
+
seconds: true,
|
|
86
|
+
milliseconds: true
|
|
87
|
+
});
|
|
88
|
+
if (milliseconds > MILLISECONDS_IN[unit] && millisecondsByUnit[unit] > 0) {
|
|
89
|
+
displayUnits[unit] = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return displayUnits;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/Duration.tsx
|
|
96
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
97
|
+
var _createDurationFormatter = (locale, unitDisplay, displayUnits) => {
|
|
98
|
+
const timeUnitFormatter = (locale2, unit, unitDisplay2) => Intl.NumberFormat(locale2, { style: "unit", unit, unitDisplay: unitDisplay2 }).format;
|
|
99
|
+
const formatterByUnit = {
|
|
100
|
+
[UNITS.days]: timeUnitFormatter(locale, "day", unitDisplay),
|
|
101
|
+
[UNITS.hours]: timeUnitFormatter(locale, "hour", unitDisplay),
|
|
102
|
+
[UNITS.minutes]: timeUnitFormatter(locale, "minute", unitDisplay),
|
|
103
|
+
[UNITS.seconds]: timeUnitFormatter(locale, "second", unitDisplay),
|
|
104
|
+
[UNITS.milliseconds]: timeUnitFormatter(locale, "millisecond", unitDisplay)
|
|
105
|
+
};
|
|
106
|
+
const formatList = new Intl.ListFormat(locale, {
|
|
107
|
+
style: "narrow",
|
|
108
|
+
type: "unit"
|
|
109
|
+
});
|
|
110
|
+
return (value) => {
|
|
111
|
+
const lowestUnit = getLowestUnit(displayUnits);
|
|
112
|
+
if (value <= 0) {
|
|
113
|
+
return formatterByUnit[lowestUnit](0);
|
|
114
|
+
}
|
|
115
|
+
const millisecondsByUnit = splitMillisecondsIntoUnits(value, displayUnits);
|
|
116
|
+
const list = [];
|
|
117
|
+
ORDERED_UNITS.forEach((unit) => {
|
|
118
|
+
if (unit in millisecondsByUnit) {
|
|
119
|
+
const unitValue = millisecondsByUnit[unit];
|
|
120
|
+
if (unitValue !== 0 || unitValue === 0 && unit === lowestUnit && list.length === 0) {
|
|
121
|
+
list.push(formatterByUnit[unit](millisecondsByUnit[unit]));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return formatList.format(list);
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
var memoizer = memoize(MEMO_CACHE_SIZE, COMPARE_OBJECTS);
|
|
129
|
+
var createDurationFormatter = memoizer(_createDurationFormatter);
|
|
130
|
+
var getDuration = ({
|
|
131
|
+
returnType,
|
|
132
|
+
props
|
|
133
|
+
}) => {
|
|
134
|
+
const {
|
|
135
|
+
display = DEFAULT_DISPLAY,
|
|
136
|
+
displayUnits = DEFAULT_DISPLAY_UNITS,
|
|
137
|
+
invalidMillisecondsLabel,
|
|
138
|
+
locale = DEFAULT_LOCALE,
|
|
139
|
+
milliseconds = DEFAULT_MILLISECONDS,
|
|
140
|
+
qa
|
|
141
|
+
} = props;
|
|
142
|
+
const isReturnTypeString = returnType === "string";
|
|
143
|
+
if (!isValidNumber(milliseconds)) {
|
|
144
|
+
return isReturnTypeString ? EM_DASH : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
145
|
+
invalidMillisecondsLabel ? (
|
|
146
|
+
// Give screen readers something useful to read off + hide the em dash
|
|
147
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: invalidMillisecondsLabel })
|
|
148
|
+
) : null,
|
|
149
|
+
/* @__PURE__ */ jsx(Container, { "aria-hidden": true, ...qa, children: EM_DASH })
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
const validatedDisplayUnits = Object.keys(displayUnits).length === 0 ? DEFAULT_DISPLAY_UNITS : displayUnits;
|
|
153
|
+
const fullText = createDurationFormatter(
|
|
154
|
+
locale,
|
|
155
|
+
display,
|
|
156
|
+
validatedDisplayUnits
|
|
157
|
+
)(milliseconds);
|
|
158
|
+
return isReturnTypeString ? fullText : /* @__PURE__ */ jsx(Container, { ...qa, children: fullText });
|
|
159
|
+
};
|
|
160
|
+
var formatDuration = (props) => {
|
|
161
|
+
return getDuration({ returnType: "string", props });
|
|
162
|
+
};
|
|
163
|
+
var Duration = (props) => {
|
|
164
|
+
return getDuration({ returnType: "component", props });
|
|
165
|
+
};
|
|
166
|
+
var Duration_default = Duration;
|
|
167
|
+
|
|
168
|
+
// src/index.ts
|
|
169
|
+
var src_default = Duration_default;
|
|
170
|
+
export {
|
|
171
|
+
Duration_default as Duration,
|
|
172
|
+
src_default as default,
|
|
173
|
+
formatDuration,
|
|
174
|
+
getDurationMaxDisplayUnits
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/Duration.tsx","../../src/constants.ts","../../src/styles.ts","../../src/utils.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\n// @ts-expect-error lru-memoize is not typed\nimport memoize from \"lru-memoize\";\nimport { EM_DASH } from \"./constants\";\nimport { VisuallyHidden } from \"@sproutsocial/seeds-react-visually-hidden\";\n\nimport {\n COMPARE_OBJECTS,\n DEFAULT_DISPLAY,\n DEFAULT_DISPLAY_UNITS,\n DEFAULT_LOCALE,\n DEFAULT_MILLISECONDS,\n MEMO_CACHE_SIZE,\n ORDERED_UNITS,\n UNITS,\n} from \"./constants\";\nimport { Container } from \"./styles\";\nimport type {\n TypeDurationProps,\n TypeDurationLocale,\n TypeDurationDisplay,\n TypeDurationDisplayUnits,\n} from \"./DurationTypes\";\nimport {\n isValidNumber,\n getLowestUnit,\n splitMillisecondsIntoUnits,\n} from \"./utils\";\n\nconst _createDurationFormatter = (\n locale: TypeDurationLocale,\n unitDisplay: TypeDurationDisplay,\n displayUnits: TypeDurationDisplayUnits\n) => {\n const timeUnitFormatter = (\n locale: TypeDurationProps[\"locale\"],\n unit: string,\n unitDisplay: TypeDurationProps[\"display\"]\n ) => Intl.NumberFormat(locale, { style: \"unit\", unit, unitDisplay }).format;\n\n const formatterByUnit = {\n [UNITS.days]: timeUnitFormatter(locale, \"day\", unitDisplay),\n [UNITS.hours]: timeUnitFormatter(locale, \"hour\", unitDisplay),\n [UNITS.minutes]: timeUnitFormatter(locale, \"minute\", unitDisplay),\n [UNITS.seconds]: timeUnitFormatter(locale, \"second\", unitDisplay),\n [UNITS.milliseconds]: timeUnitFormatter(locale, \"millisecond\", unitDisplay),\n };\n\n const formatList = new Intl.ListFormat(locale, {\n style: \"narrow\",\n type: \"unit\",\n });\n\n return (value: number) => {\n const lowestUnit = getLowestUnit(displayUnits);\n\n // if the value is zero or negative, we just want to return 0 for the lowest unit (ex \"0 minutes\")\n if (value <= 0) {\n // @ts-ignore TS error later\n return formatterByUnit[lowestUnit](0);\n }\n\n const millisecondsByUnit = splitMillisecondsIntoUnits(value, displayUnits);\n const list: string[] = [];\n\n ORDERED_UNITS.forEach((unit) => {\n if (unit in millisecondsByUnit) {\n // @ts-ignore TS error later\n const unitValue = millisecondsByUnit[unit];\n\n // we want to add to the list if one of two conditions are met:\n // 1) the unit has a value greater than 0 OR\n // 2) the unit has value 0 AND the unit is the lowest unit AND the list is already empty\n if (\n unitValue !== 0 ||\n (unitValue === 0 && unit === lowestUnit && list.length === 0)\n ) {\n // @ts-ignore TS error later\n list.push(formatterByUnit[unit](millisecondsByUnit[unit]));\n }\n }\n });\n\n return formatList.format(list);\n };\n};\n\n// Memoize to reduce the energy of creating new instances of Intl.NumberFormat\nconst memoizer = memoize(MEMO_CACHE_SIZE, COMPARE_OBJECTS);\nconst createDurationFormatter = memoizer(_createDurationFormatter);\n\nconst getDuration = ({\n returnType,\n props,\n}: {\n returnType: \"string\" | \"component\";\n props: TypeDurationProps;\n}): string | React.ReactNode => {\n const {\n display = DEFAULT_DISPLAY,\n displayUnits = DEFAULT_DISPLAY_UNITS,\n invalidMillisecondsLabel,\n locale = DEFAULT_LOCALE,\n milliseconds = DEFAULT_MILLISECONDS,\n qa,\n } = props;\n const isReturnTypeString = returnType === \"string\";\n\n if (!isValidNumber(milliseconds)) {\n return isReturnTypeString ? (\n EM_DASH\n ) : (\n <>\n {invalidMillisecondsLabel ? (\n // Give screen readers something useful to read off + hide the em dash\n <VisuallyHidden>{invalidMillisecondsLabel}</VisuallyHidden>\n ) : null}\n <Container aria-hidden {...qa}>\n {EM_DASH}\n </Container>\n </>\n );\n }\n\n const validatedDisplayUnits =\n Object.keys(displayUnits).length === 0\n ? DEFAULT_DISPLAY_UNITS\n : displayUnits;\n\n const fullText = createDurationFormatter(\n locale,\n display,\n validatedDisplayUnits\n )(milliseconds);\n\n return isReturnTypeString ? (\n fullText\n ) : (\n <Container {...qa}>{fullText}</Container>\n );\n};\n\nexport const formatDuration = (props: TypeDurationProps): string => {\n return getDuration({ returnType: \"string\", props }) as string;\n};\n\nconst Duration = (props: TypeDurationProps) => {\n return getDuration({ returnType: \"component\", props });\n};\n\nexport default Duration;\n","export const COMPARE_OBJECTS = true;\nexport const MEMO_CACHE_SIZE = 10;\n\nexport const UNITS = {\n days: \"days\",\n hours: \"hours\",\n minutes: \"minutes\",\n seconds: \"seconds\",\n milliseconds: \"milliseconds\",\n};\n\nexport const MILLISECONDS_IN = {\n [UNITS.days]: 1 * 1000 * 60 * 60 * 24,\n [UNITS.hours]: 1 * 1000 * 60 * 60,\n [UNITS.minutes]: 1 * 1000 * 60,\n [UNITS.seconds]: 1 * 1000,\n [UNITS.milliseconds]: 1,\n};\n\nexport const ORDERED_UNITS = [\n UNITS.days,\n UNITS.hours,\n UNITS.minutes,\n UNITS.seconds,\n UNITS.milliseconds,\n];\n\n// Duration props defaults\nexport const DEFAULT_DISPLAY = \"narrow\";\nexport const DEFAULT_DISPLAY_UNITS = {\n [UNITS.days]: true,\n [UNITS.hours]: true,\n [UNITS.minutes]: true,\n [UNITS.seconds]: true,\n [UNITS.milliseconds]: false,\n};\n\nexport const DEFAULT_LOCALE = \"en-US\";\nexport const DEFAULT_MILLISECONDS = null;\n\nexport const EM_DASH = \"—\"; // shift + option + hyphen on a mac keyboard\n","import styled from \"styled-components\";\nimport Text from \"@sproutsocial/seeds-react-text\";\n\nexport const Container = styled(Text)`\n font-variant-numeric: tabular-nums;\n`;\n","import type {\n TypeDurationDisplayUnits,\n TypeDurationMilliseconds,\n} from \"./DurationTypes\";\nimport { MILLISECONDS_IN, ORDERED_UNITS, UNITS } from \"./constants\";\n\nexport const getLowestUnit = (displayUnits: TypeDurationDisplayUnits) =>\n [...ORDERED_UNITS]\n .reverse()\n .find((unit) => displayUnits[unit as keyof TypeDurationDisplayUnits]) ||\n UNITS.milliseconds;\n\nexport const splitMillisecondsIntoUnits = (\n milliseconds: number,\n displayUnits: TypeDurationDisplayUnits\n): {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n} => {\n const lowestUnit = getLowestUnit(displayUnits);\n\n // @ts-ignore TS error later\n const remainder = milliseconds % MILLISECONDS_IN[lowestUnit];\n // @ts-ignore TS error later\n if (2 * remainder >= MILLISECONDS_IN[lowestUnit]) {\n // if the remainder is large, add enough seconds to increse the lowest unit\n // @ts-ignore TS error later\n milliseconds += MILLISECONDS_IN[lowestUnit] - remainder;\n }\n\n const units = {};\n\n ORDERED_UNITS.forEach((unit) => {\n // @ts-ignore TS error later\n if (displayUnits[unit]) {\n // @ts-ignore TS error later\n units[unit] = Math.floor(milliseconds / MILLISECONDS_IN[unit]);\n // @ts-ignore TS error later\n milliseconds -= units[unit] * MILLISECONDS_IN[unit];\n }\n });\n\n return units;\n};\n\nexport const isValidNumber = (value: unknown): boolean =>\n typeof value === \"number\" && isFinite(value);\n\nexport interface TypeGetDurationMaxDisplayUnitsProps {\n milliseconds: TypeDurationMilliseconds;\n maxDisplayUnits: number;\n}\n\nexport const getDurationMaxDisplayUnits = ({\n milliseconds,\n maxDisplayUnits = ORDERED_UNITS.length,\n}: TypeGetDurationMaxDisplayUnitsProps): TypeDurationDisplayUnits => {\n const displayUnits = {};\n\n if (!isValidNumber(milliseconds)) {\n return displayUnits;\n }\n\n for (const unit of ORDERED_UNITS) {\n if (Object.keys(displayUnits).length >= maxDisplayUnits) {\n break;\n }\n\n // @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\\_(ツ)_/¯\n const millisecondsByUnit = splitMillisecondsIntoUnits(milliseconds, {\n days: true,\n hours: true,\n minutes: true,\n seconds: true,\n milliseconds: true,\n });\n\n // @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\\_(ツ)_/¯\n if (milliseconds > MILLISECONDS_IN[unit] && millisecondsByUnit[unit] > 0) {\n // @ts-ignore TS error later\n displayUnits[unit] = true;\n }\n }\n\n return displayUnits;\n};\n","import Duration, { formatDuration } from \"./Duration\";\nimport {\n getDurationMaxDisplayUnits,\n type TypeGetDurationMaxDisplayUnitsProps,\n} from \"./utils\";\n\nexport default Duration;\nexport { Duration };\nexport { formatDuration };\nexport { getDurationMaxDisplayUnits };\nexport type { TypeGetDurationMaxDisplayUnitsProps };\nexport * from \"./DurationTypes\";\n"],"mappings":"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","names":["locale","unitDisplay"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TypeTextProps } from '@sproutsocial/seeds-react-text';
|
|
3
|
+
|
|
4
|
+
type TypeDurationMilliseconds = number | null;
|
|
5
|
+
type TypeDurationLocale = Intl.LocalesArgument;
|
|
6
|
+
type TypeDurationDisplay = "long" | "narrow";
|
|
7
|
+
interface TypeDurationDisplayUnits {
|
|
8
|
+
days?: boolean;
|
|
9
|
+
hours?: boolean;
|
|
10
|
+
minutes?: boolean;
|
|
11
|
+
seconds?: boolean;
|
|
12
|
+
milliseconds?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface TypeDurationProps extends Omit<TypeTextProps, "children"> {
|
|
15
|
+
/** The milliseconds to be formatted */
|
|
16
|
+
milliseconds: TypeDurationMilliseconds;
|
|
17
|
+
/** Locale to format. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument */
|
|
18
|
+
locale?: TypeDurationLocale;
|
|
19
|
+
/** The style of the formatted duration */
|
|
20
|
+
display?: TypeDurationDisplay;
|
|
21
|
+
/** The units of the duration to render */
|
|
22
|
+
displayUnits?: TypeDurationDisplayUnits;
|
|
23
|
+
/** Text to be read off by screen readers for invalid values (i.e., any value rendered as '—' (em dash)) */
|
|
24
|
+
invalidMillisecondsLabel?: string;
|
|
25
|
+
qa?: object;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const formatDuration: (props: TypeDurationProps) => string;
|
|
29
|
+
declare const Duration: (props: TypeDurationProps) => React.ReactNode;
|
|
30
|
+
|
|
31
|
+
interface TypeGetDurationMaxDisplayUnitsProps {
|
|
32
|
+
milliseconds: TypeDurationMilliseconds;
|
|
33
|
+
maxDisplayUnits: number;
|
|
34
|
+
}
|
|
35
|
+
declare const getDurationMaxDisplayUnits: ({ milliseconds, maxDisplayUnits, }: TypeGetDurationMaxDisplayUnitsProps) => TypeDurationDisplayUnits;
|
|
36
|
+
|
|
37
|
+
export { Duration, type TypeDurationDisplay, type TypeDurationDisplayUnits, type TypeDurationLocale, type TypeDurationMilliseconds, type TypeDurationProps, type TypeGetDurationMaxDisplayUnitsProps, Duration as default, formatDuration, getDurationMaxDisplayUnits };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TypeTextProps } from '@sproutsocial/seeds-react-text';
|
|
3
|
+
|
|
4
|
+
type TypeDurationMilliseconds = number | null;
|
|
5
|
+
type TypeDurationLocale = Intl.LocalesArgument;
|
|
6
|
+
type TypeDurationDisplay = "long" | "narrow";
|
|
7
|
+
interface TypeDurationDisplayUnits {
|
|
8
|
+
days?: boolean;
|
|
9
|
+
hours?: boolean;
|
|
10
|
+
minutes?: boolean;
|
|
11
|
+
seconds?: boolean;
|
|
12
|
+
milliseconds?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface TypeDurationProps extends Omit<TypeTextProps, "children"> {
|
|
15
|
+
/** The milliseconds to be formatted */
|
|
16
|
+
milliseconds: TypeDurationMilliseconds;
|
|
17
|
+
/** Locale to format. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument */
|
|
18
|
+
locale?: TypeDurationLocale;
|
|
19
|
+
/** The style of the formatted duration */
|
|
20
|
+
display?: TypeDurationDisplay;
|
|
21
|
+
/** The units of the duration to render */
|
|
22
|
+
displayUnits?: TypeDurationDisplayUnits;
|
|
23
|
+
/** Text to be read off by screen readers for invalid values (i.e., any value rendered as '—' (em dash)) */
|
|
24
|
+
invalidMillisecondsLabel?: string;
|
|
25
|
+
qa?: object;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const formatDuration: (props: TypeDurationProps) => string;
|
|
29
|
+
declare const Duration: (props: TypeDurationProps) => React.ReactNode;
|
|
30
|
+
|
|
31
|
+
interface TypeGetDurationMaxDisplayUnitsProps {
|
|
32
|
+
milliseconds: TypeDurationMilliseconds;
|
|
33
|
+
maxDisplayUnits: number;
|
|
34
|
+
}
|
|
35
|
+
declare const getDurationMaxDisplayUnits: ({ milliseconds, maxDisplayUnits, }: TypeGetDurationMaxDisplayUnitsProps) => TypeDurationDisplayUnits;
|
|
36
|
+
|
|
37
|
+
export { Duration, type TypeDurationDisplay, type TypeDurationDisplayUnits, type TypeDurationLocale, type TypeDurationMilliseconds, type TypeDurationProps, type TypeGetDurationMaxDisplayUnitsProps, Duration as default, formatDuration, getDurationMaxDisplayUnits };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
Duration: () => Duration_default,
|
|
34
|
+
default: () => src_default,
|
|
35
|
+
formatDuration: () => formatDuration,
|
|
36
|
+
getDurationMaxDisplayUnits: () => getDurationMaxDisplayUnits
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
|
|
40
|
+
// src/Duration.tsx
|
|
41
|
+
var React = require("react");
|
|
42
|
+
var import_lru_memoize = __toESM(require("lru-memoize"));
|
|
43
|
+
|
|
44
|
+
// src/constants.ts
|
|
45
|
+
var COMPARE_OBJECTS = true;
|
|
46
|
+
var MEMO_CACHE_SIZE = 10;
|
|
47
|
+
var UNITS = {
|
|
48
|
+
days: "days",
|
|
49
|
+
hours: "hours",
|
|
50
|
+
minutes: "minutes",
|
|
51
|
+
seconds: "seconds",
|
|
52
|
+
milliseconds: "milliseconds"
|
|
53
|
+
};
|
|
54
|
+
var MILLISECONDS_IN = {
|
|
55
|
+
[UNITS.days]: 1 * 1e3 * 60 * 60 * 24,
|
|
56
|
+
[UNITS.hours]: 1 * 1e3 * 60 * 60,
|
|
57
|
+
[UNITS.minutes]: 1 * 1e3 * 60,
|
|
58
|
+
[UNITS.seconds]: 1 * 1e3,
|
|
59
|
+
[UNITS.milliseconds]: 1
|
|
60
|
+
};
|
|
61
|
+
var ORDERED_UNITS = [
|
|
62
|
+
UNITS.days,
|
|
63
|
+
UNITS.hours,
|
|
64
|
+
UNITS.minutes,
|
|
65
|
+
UNITS.seconds,
|
|
66
|
+
UNITS.milliseconds
|
|
67
|
+
];
|
|
68
|
+
var DEFAULT_DISPLAY = "narrow";
|
|
69
|
+
var DEFAULT_DISPLAY_UNITS = {
|
|
70
|
+
[UNITS.days]: true,
|
|
71
|
+
[UNITS.hours]: true,
|
|
72
|
+
[UNITS.minutes]: true,
|
|
73
|
+
[UNITS.seconds]: true,
|
|
74
|
+
[UNITS.milliseconds]: false
|
|
75
|
+
};
|
|
76
|
+
var DEFAULT_LOCALE = "en-US";
|
|
77
|
+
var DEFAULT_MILLISECONDS = null;
|
|
78
|
+
var EM_DASH = "\u2014";
|
|
79
|
+
|
|
80
|
+
// src/Duration.tsx
|
|
81
|
+
var import_seeds_react_visually_hidden = require("@sproutsocial/seeds-react-visually-hidden");
|
|
82
|
+
|
|
83
|
+
// src/styles.ts
|
|
84
|
+
var import_styled_components = __toESM(require("styled-components"));
|
|
85
|
+
var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
|
|
86
|
+
var Container = (0, import_styled_components.default)(import_seeds_react_text.default)`
|
|
87
|
+
font-variant-numeric: tabular-nums;
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
// src/utils.ts
|
|
91
|
+
var getLowestUnit = (displayUnits) => [...ORDERED_UNITS].reverse().find((unit) => displayUnits[unit]) || UNITS.milliseconds;
|
|
92
|
+
var splitMillisecondsIntoUnits = (milliseconds, displayUnits) => {
|
|
93
|
+
const lowestUnit = getLowestUnit(displayUnits);
|
|
94
|
+
const remainder = milliseconds % MILLISECONDS_IN[lowestUnit];
|
|
95
|
+
if (2 * remainder >= MILLISECONDS_IN[lowestUnit]) {
|
|
96
|
+
milliseconds += MILLISECONDS_IN[lowestUnit] - remainder;
|
|
97
|
+
}
|
|
98
|
+
const units = {};
|
|
99
|
+
ORDERED_UNITS.forEach((unit) => {
|
|
100
|
+
if (displayUnits[unit]) {
|
|
101
|
+
units[unit] = Math.floor(milliseconds / MILLISECONDS_IN[unit]);
|
|
102
|
+
milliseconds -= units[unit] * MILLISECONDS_IN[unit];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return units;
|
|
106
|
+
};
|
|
107
|
+
var isValidNumber = (value) => typeof value === "number" && isFinite(value);
|
|
108
|
+
var getDurationMaxDisplayUnits = ({
|
|
109
|
+
milliseconds,
|
|
110
|
+
maxDisplayUnits = ORDERED_UNITS.length
|
|
111
|
+
}) => {
|
|
112
|
+
const displayUnits = {};
|
|
113
|
+
if (!isValidNumber(milliseconds)) {
|
|
114
|
+
return displayUnits;
|
|
115
|
+
}
|
|
116
|
+
for (const unit of ORDERED_UNITS) {
|
|
117
|
+
if (Object.keys(displayUnits).length >= maxDisplayUnits) {
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
const millisecondsByUnit = splitMillisecondsIntoUnits(milliseconds, {
|
|
121
|
+
days: true,
|
|
122
|
+
hours: true,
|
|
123
|
+
minutes: true,
|
|
124
|
+
seconds: true,
|
|
125
|
+
milliseconds: true
|
|
126
|
+
});
|
|
127
|
+
if (milliseconds > MILLISECONDS_IN[unit] && millisecondsByUnit[unit] > 0) {
|
|
128
|
+
displayUnits[unit] = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return displayUnits;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/Duration.tsx
|
|
135
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
136
|
+
var _createDurationFormatter = (locale, unitDisplay, displayUnits) => {
|
|
137
|
+
const timeUnitFormatter = (locale2, unit, unitDisplay2) => Intl.NumberFormat(locale2, { style: "unit", unit, unitDisplay: unitDisplay2 }).format;
|
|
138
|
+
const formatterByUnit = {
|
|
139
|
+
[UNITS.days]: timeUnitFormatter(locale, "day", unitDisplay),
|
|
140
|
+
[UNITS.hours]: timeUnitFormatter(locale, "hour", unitDisplay),
|
|
141
|
+
[UNITS.minutes]: timeUnitFormatter(locale, "minute", unitDisplay),
|
|
142
|
+
[UNITS.seconds]: timeUnitFormatter(locale, "second", unitDisplay),
|
|
143
|
+
[UNITS.milliseconds]: timeUnitFormatter(locale, "millisecond", unitDisplay)
|
|
144
|
+
};
|
|
145
|
+
const formatList = new Intl.ListFormat(locale, {
|
|
146
|
+
style: "narrow",
|
|
147
|
+
type: "unit"
|
|
148
|
+
});
|
|
149
|
+
return (value) => {
|
|
150
|
+
const lowestUnit = getLowestUnit(displayUnits);
|
|
151
|
+
if (value <= 0) {
|
|
152
|
+
return formatterByUnit[lowestUnit](0);
|
|
153
|
+
}
|
|
154
|
+
const millisecondsByUnit = splitMillisecondsIntoUnits(value, displayUnits);
|
|
155
|
+
const list = [];
|
|
156
|
+
ORDERED_UNITS.forEach((unit) => {
|
|
157
|
+
if (unit in millisecondsByUnit) {
|
|
158
|
+
const unitValue = millisecondsByUnit[unit];
|
|
159
|
+
if (unitValue !== 0 || unitValue === 0 && unit === lowestUnit && list.length === 0) {
|
|
160
|
+
list.push(formatterByUnit[unit](millisecondsByUnit[unit]));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return formatList.format(list);
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
var memoizer = (0, import_lru_memoize.default)(MEMO_CACHE_SIZE, COMPARE_OBJECTS);
|
|
168
|
+
var createDurationFormatter = memoizer(_createDurationFormatter);
|
|
169
|
+
var getDuration = ({
|
|
170
|
+
returnType,
|
|
171
|
+
props
|
|
172
|
+
}) => {
|
|
173
|
+
const {
|
|
174
|
+
display = DEFAULT_DISPLAY,
|
|
175
|
+
displayUnits = DEFAULT_DISPLAY_UNITS,
|
|
176
|
+
invalidMillisecondsLabel,
|
|
177
|
+
locale = DEFAULT_LOCALE,
|
|
178
|
+
milliseconds = DEFAULT_MILLISECONDS,
|
|
179
|
+
qa
|
|
180
|
+
} = props;
|
|
181
|
+
const isReturnTypeString = returnType === "string";
|
|
182
|
+
if (!isValidNumber(milliseconds)) {
|
|
183
|
+
return isReturnTypeString ? EM_DASH : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
184
|
+
invalidMillisecondsLabel ? (
|
|
185
|
+
// Give screen readers something useful to read off + hide the em dash
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_visually_hidden.VisuallyHidden, { children: invalidMillisecondsLabel })
|
|
187
|
+
) : null,
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Container, { "aria-hidden": true, ...qa, children: EM_DASH })
|
|
189
|
+
] });
|
|
190
|
+
}
|
|
191
|
+
const validatedDisplayUnits = Object.keys(displayUnits).length === 0 ? DEFAULT_DISPLAY_UNITS : displayUnits;
|
|
192
|
+
const fullText = createDurationFormatter(
|
|
193
|
+
locale,
|
|
194
|
+
display,
|
|
195
|
+
validatedDisplayUnits
|
|
196
|
+
)(milliseconds);
|
|
197
|
+
return isReturnTypeString ? fullText : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Container, { ...qa, children: fullText });
|
|
198
|
+
};
|
|
199
|
+
var formatDuration = (props) => {
|
|
200
|
+
return getDuration({ returnType: "string", props });
|
|
201
|
+
};
|
|
202
|
+
var Duration = (props) => {
|
|
203
|
+
return getDuration({ returnType: "component", props });
|
|
204
|
+
};
|
|
205
|
+
var Duration_default = Duration;
|
|
206
|
+
|
|
207
|
+
// src/index.ts
|
|
208
|
+
var src_default = Duration_default;
|
|
209
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
210
|
+
0 && (module.exports = {
|
|
211
|
+
Duration,
|
|
212
|
+
formatDuration,
|
|
213
|
+
getDurationMaxDisplayUnits
|
|
214
|
+
});
|
|
215
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Duration.tsx","../src/constants.ts","../src/styles.ts","../src/utils.ts"],"sourcesContent":["import Duration, { formatDuration } from \"./Duration\";\nimport {\n getDurationMaxDisplayUnits,\n type TypeGetDurationMaxDisplayUnitsProps,\n} from \"./utils\";\n\nexport default Duration;\nexport { Duration };\nexport { formatDuration };\nexport { getDurationMaxDisplayUnits };\nexport type { TypeGetDurationMaxDisplayUnitsProps };\nexport * from \"./DurationTypes\";\n","import * as React from \"react\";\n// @ts-expect-error lru-memoize is not typed\nimport memoize from \"lru-memoize\";\nimport { EM_DASH } from \"./constants\";\nimport { VisuallyHidden } from \"@sproutsocial/seeds-react-visually-hidden\";\n\nimport {\n COMPARE_OBJECTS,\n DEFAULT_DISPLAY,\n DEFAULT_DISPLAY_UNITS,\n DEFAULT_LOCALE,\n DEFAULT_MILLISECONDS,\n MEMO_CACHE_SIZE,\n ORDERED_UNITS,\n UNITS,\n} from \"./constants\";\nimport { Container } from \"./styles\";\nimport type {\n TypeDurationProps,\n TypeDurationLocale,\n TypeDurationDisplay,\n TypeDurationDisplayUnits,\n} from \"./DurationTypes\";\nimport {\n isValidNumber,\n getLowestUnit,\n splitMillisecondsIntoUnits,\n} from \"./utils\";\n\nconst _createDurationFormatter = (\n locale: TypeDurationLocale,\n unitDisplay: TypeDurationDisplay,\n displayUnits: TypeDurationDisplayUnits\n) => {\n const timeUnitFormatter = (\n locale: TypeDurationProps[\"locale\"],\n unit: string,\n unitDisplay: TypeDurationProps[\"display\"]\n ) => Intl.NumberFormat(locale, { style: \"unit\", unit, unitDisplay }).format;\n\n const formatterByUnit = {\n [UNITS.days]: timeUnitFormatter(locale, \"day\", unitDisplay),\n [UNITS.hours]: timeUnitFormatter(locale, \"hour\", unitDisplay),\n [UNITS.minutes]: timeUnitFormatter(locale, \"minute\", unitDisplay),\n [UNITS.seconds]: timeUnitFormatter(locale, \"second\", unitDisplay),\n [UNITS.milliseconds]: timeUnitFormatter(locale, \"millisecond\", unitDisplay),\n };\n\n const formatList = new Intl.ListFormat(locale, {\n style: \"narrow\",\n type: \"unit\",\n });\n\n return (value: number) => {\n const lowestUnit = getLowestUnit(displayUnits);\n\n // if the value is zero or negative, we just want to return 0 for the lowest unit (ex \"0 minutes\")\n if (value <= 0) {\n // @ts-ignore TS error later\n return formatterByUnit[lowestUnit](0);\n }\n\n const millisecondsByUnit = splitMillisecondsIntoUnits(value, displayUnits);\n const list: string[] = [];\n\n ORDERED_UNITS.forEach((unit) => {\n if (unit in millisecondsByUnit) {\n // @ts-ignore TS error later\n const unitValue = millisecondsByUnit[unit];\n\n // we want to add to the list if one of two conditions are met:\n // 1) the unit has a value greater than 0 OR\n // 2) the unit has value 0 AND the unit is the lowest unit AND the list is already empty\n if (\n unitValue !== 0 ||\n (unitValue === 0 && unit === lowestUnit && list.length === 0)\n ) {\n // @ts-ignore TS error later\n list.push(formatterByUnit[unit](millisecondsByUnit[unit]));\n }\n }\n });\n\n return formatList.format(list);\n };\n};\n\n// Memoize to reduce the energy of creating new instances of Intl.NumberFormat\nconst memoizer = memoize(MEMO_CACHE_SIZE, COMPARE_OBJECTS);\nconst createDurationFormatter = memoizer(_createDurationFormatter);\n\nconst getDuration = ({\n returnType,\n props,\n}: {\n returnType: \"string\" | \"component\";\n props: TypeDurationProps;\n}): string | React.ReactNode => {\n const {\n display = DEFAULT_DISPLAY,\n displayUnits = DEFAULT_DISPLAY_UNITS,\n invalidMillisecondsLabel,\n locale = DEFAULT_LOCALE,\n milliseconds = DEFAULT_MILLISECONDS,\n qa,\n } = props;\n const isReturnTypeString = returnType === \"string\";\n\n if (!isValidNumber(milliseconds)) {\n return isReturnTypeString ? (\n EM_DASH\n ) : (\n <>\n {invalidMillisecondsLabel ? (\n // Give screen readers something useful to read off + hide the em dash\n <VisuallyHidden>{invalidMillisecondsLabel}</VisuallyHidden>\n ) : null}\n <Container aria-hidden {...qa}>\n {EM_DASH}\n </Container>\n </>\n );\n }\n\n const validatedDisplayUnits =\n Object.keys(displayUnits).length === 0\n ? DEFAULT_DISPLAY_UNITS\n : displayUnits;\n\n const fullText = createDurationFormatter(\n locale,\n display,\n validatedDisplayUnits\n )(milliseconds);\n\n return isReturnTypeString ? (\n fullText\n ) : (\n <Container {...qa}>{fullText}</Container>\n );\n};\n\nexport const formatDuration = (props: TypeDurationProps): string => {\n return getDuration({ returnType: \"string\", props }) as string;\n};\n\nconst Duration = (props: TypeDurationProps) => {\n return getDuration({ returnType: \"component\", props });\n};\n\nexport default Duration;\n","export const COMPARE_OBJECTS = true;\nexport const MEMO_CACHE_SIZE = 10;\n\nexport const UNITS = {\n days: \"days\",\n hours: \"hours\",\n minutes: \"minutes\",\n seconds: \"seconds\",\n milliseconds: \"milliseconds\",\n};\n\nexport const MILLISECONDS_IN = {\n [UNITS.days]: 1 * 1000 * 60 * 60 * 24,\n [UNITS.hours]: 1 * 1000 * 60 * 60,\n [UNITS.minutes]: 1 * 1000 * 60,\n [UNITS.seconds]: 1 * 1000,\n [UNITS.milliseconds]: 1,\n};\n\nexport const ORDERED_UNITS = [\n UNITS.days,\n UNITS.hours,\n UNITS.minutes,\n UNITS.seconds,\n UNITS.milliseconds,\n];\n\n// Duration props defaults\nexport const DEFAULT_DISPLAY = \"narrow\";\nexport const DEFAULT_DISPLAY_UNITS = {\n [UNITS.days]: true,\n [UNITS.hours]: true,\n [UNITS.minutes]: true,\n [UNITS.seconds]: true,\n [UNITS.milliseconds]: false,\n};\n\nexport const DEFAULT_LOCALE = \"en-US\";\nexport const DEFAULT_MILLISECONDS = null;\n\nexport const EM_DASH = \"—\"; // shift + option + hyphen on a mac keyboard\n","import styled from \"styled-components\";\nimport Text from \"@sproutsocial/seeds-react-text\";\n\nexport const Container = styled(Text)`\n font-variant-numeric: tabular-nums;\n`;\n","import type {\n TypeDurationDisplayUnits,\n TypeDurationMilliseconds,\n} from \"./DurationTypes\";\nimport { MILLISECONDS_IN, ORDERED_UNITS, UNITS } from \"./constants\";\n\nexport const getLowestUnit = (displayUnits: TypeDurationDisplayUnits) =>\n [...ORDERED_UNITS]\n .reverse()\n .find((unit) => displayUnits[unit as keyof TypeDurationDisplayUnits]) ||\n UNITS.milliseconds;\n\nexport const splitMillisecondsIntoUnits = (\n milliseconds: number,\n displayUnits: TypeDurationDisplayUnits\n): {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n} => {\n const lowestUnit = getLowestUnit(displayUnits);\n\n // @ts-ignore TS error later\n const remainder = milliseconds % MILLISECONDS_IN[lowestUnit];\n // @ts-ignore TS error later\n if (2 * remainder >= MILLISECONDS_IN[lowestUnit]) {\n // if the remainder is large, add enough seconds to increse the lowest unit\n // @ts-ignore TS error later\n milliseconds += MILLISECONDS_IN[lowestUnit] - remainder;\n }\n\n const units = {};\n\n ORDERED_UNITS.forEach((unit) => {\n // @ts-ignore TS error later\n if (displayUnits[unit]) {\n // @ts-ignore TS error later\n units[unit] = Math.floor(milliseconds / MILLISECONDS_IN[unit]);\n // @ts-ignore TS error later\n milliseconds -= units[unit] * MILLISECONDS_IN[unit];\n }\n });\n\n return units;\n};\n\nexport const isValidNumber = (value: unknown): boolean =>\n typeof value === \"number\" && isFinite(value);\n\nexport interface TypeGetDurationMaxDisplayUnitsProps {\n milliseconds: TypeDurationMilliseconds;\n maxDisplayUnits: number;\n}\n\nexport const getDurationMaxDisplayUnits = ({\n milliseconds,\n maxDisplayUnits = ORDERED_UNITS.length,\n}: TypeGetDurationMaxDisplayUnitsProps): TypeDurationDisplayUnits => {\n const displayUnits = {};\n\n if (!isValidNumber(milliseconds)) {\n return displayUnits;\n }\n\n for (const unit of ORDERED_UNITS) {\n if (Object.keys(displayUnits).length >= maxDisplayUnits) {\n break;\n }\n\n // @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\\_(ツ)_/¯\n const millisecondsByUnit = splitMillisecondsIntoUnits(milliseconds, {\n days: true,\n hours: true,\n minutes: true,\n seconds: true,\n milliseconds: true,\n });\n\n // @ts-expect-error - stupid typescript isn't smart enough to check the isValidNumber check above ¯\\_(ツ)_/¯\n if (milliseconds > MILLISECONDS_IN[unit] && millisecondsByUnit[unit] > 0) {\n // @ts-ignore TS error later\n displayUnits[unit] = true;\n }\n }\n\n return displayUnits;\n};\n"],"mappings":"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","names":["styled","Text","locale","unitDisplay","memoize"]}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const baseConfig = require("@sproutsocial/seeds-testing");
|
|
2
|
+
|
|
3
|
+
/** * @type {import('jest').Config} */
|
|
4
|
+
const config = {
|
|
5
|
+
...baseConfig,
|
|
6
|
+
displayName: "seeds-react-duration",
|
|
7
|
+
testPathIgnorePatterns: [
|
|
8
|
+
...baseConfig.testPathIgnorePatterns,
|
|
9
|
+
"testDuration.tsx",
|
|
10
|
+
],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = config;
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sproutsocial/seeds-react-duration",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Seeds React Duration",
|
|
5
|
+
"author": "Sprout Social, Inc.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/esm/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup --dts",
|
|
12
|
+
"build:debug": "tsup --dts --metafile",
|
|
13
|
+
"dev": "tsup --watch --dts",
|
|
14
|
+
"clean": "rm -rf .turbo dist",
|
|
15
|
+
"clean:modules": "rm -rf node_modules",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"test:watch": "jest --watch --coverage=false"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@sproutsocial/seeds-react-theme": "^*",
|
|
22
|
+
"@sproutsocial/seeds-react-system-props": "^*",
|
|
23
|
+
"@sproutsocial/seeds-react-visually-hidden": "*",
|
|
24
|
+
"@sproutsocial/seeds-react-text": "*",
|
|
25
|
+
"lru-memoize": "~1.0.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.0.0",
|
|
29
|
+
"@types/styled-components": "^5.1.26",
|
|
30
|
+
"@sproutsocial/eslint-config-seeds": "*",
|
|
31
|
+
"react": "^18.0.0",
|
|
32
|
+
"styled-components": "^5.2.3",
|
|
33
|
+
"tsup": "^8.0.2",
|
|
34
|
+
"typescript": "^5.6.2",
|
|
35
|
+
"@sproutsocial/seeds-tsconfig": "*",
|
|
36
|
+
"@sproutsocial/seeds-testing": "*",
|
|
37
|
+
"@sproutsocial/seeds-react-testing-library": "*"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"styled-components": "^5.2.3"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
}
|
|
45
|
+
}
|