@unofficialbox/box-open-elements 0.12.0 → 0.14.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.
Files changed (47) hide show
  1. package/dist/components/feedback/alert.d.ts +2 -0
  2. package/dist/components/feedback/alert.js +40 -2
  3. package/dist/components/feedback/indicator.d.ts +34 -0
  4. package/dist/components/feedback/indicator.js +167 -0
  5. package/dist/components/feedback/toast.js +1 -17
  6. package/dist/components/feedback/tone.d.ts +18 -0
  7. package/dist/components/feedback/tone.js +23 -0
  8. package/dist/components/forms/calendar.d.ts +25 -0
  9. package/dist/components/forms/calendar.js +94 -2
  10. package/dist/components/forms/tile-group.d.ts +58 -0
  11. package/dist/components/forms/tile-group.js +280 -0
  12. package/dist/components/output/code-block.d.ts +49 -0
  13. package/dist/components/output/code-block.js +236 -0
  14. package/dist/components/output/formatted-date.d.ts +38 -0
  15. package/dist/components/output/formatted-date.js +90 -0
  16. package/dist/components/output/formatted-file-size.d.ts +31 -0
  17. package/dist/components/output/formatted-file-size.js +61 -0
  18. package/dist/components/output/formatted-number.d.ts +53 -0
  19. package/dist/components/output/formatted-number.js +130 -0
  20. package/dist/components/output/formatted-value.d.ts +44 -0
  21. package/dist/components/output/formatted-value.js +100 -0
  22. package/dist/components/output/index.d.ts +6 -0
  23. package/dist/components/output/index.js +9 -0
  24. package/dist/components/output/relative-time.d.ts +41 -0
  25. package/dist/components/output/relative-time.js +82 -0
  26. package/dist/element-maps.d.ts +15 -1
  27. package/dist/entries/code-block.d.ts +1 -0
  28. package/dist/entries/code-block.js +1 -0
  29. package/dist/entries/formatted-date.d.ts +1 -0
  30. package/dist/entries/formatted-date.js +1 -0
  31. package/dist/entries/formatted-file-size.d.ts +1 -0
  32. package/dist/entries/formatted-file-size.js +1 -0
  33. package/dist/entries/formatted-number.d.ts +1 -0
  34. package/dist/entries/formatted-number.js +1 -0
  35. package/dist/entries/indicator.d.ts +1 -0
  36. package/dist/entries/indicator.js +1 -0
  37. package/dist/entries/relative-time.d.ts +1 -0
  38. package/dist/entries/relative-time.js +1 -0
  39. package/dist/entries/tile-group.d.ts +1 -0
  40. package/dist/entries/tile-group.js +1 -0
  41. package/dist/foundations/format/format.d.ts +82 -0
  42. package/dist/foundations/format/format.js +122 -0
  43. package/dist/foundations/format/index.d.ts +1 -0
  44. package/dist/foundations/format/index.js +3 -0
  45. package/dist/index.d.ts +9 -0
  46. package/dist/index.js +11 -0
  47. package/package.json +1 -1
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Typed-value formatting, as a foundation.
3
+ *
4
+ * Every read surface in a content platform renders the same handful of value
5
+ * types — a modified date, a file size, a count, a duration since something
6
+ * happened. Before this module each consumer reinvented that, which meant
7
+ * locale handling, `<time datetime>` semantics and invalid-input behaviour were
8
+ * reinvented with it, differently each time.
9
+ *
10
+ * The rules here are deliberately shared rather than per-component:
11
+ *
12
+ * - **A locale is never guessed.** An absent `locale` means "use the host's",
13
+ * which is `undefined` to `Intl` — not a hardcoded `en-US`. Substituting one
14
+ * would silently render American dates to a German reader.
15
+ * - **Invalid input renders nothing.** Not `Invalid Date`, not `NaN`, not the
16
+ * raw string. A malformed value is a host bug, and showing its wreckage to a
17
+ * reader helps nobody; the element collapses and the host sees an empty slot.
18
+ */
19
+ /**
20
+ * The locale to format in.
21
+ *
22
+ * `undefined` is meaningful: it tells `Intl` to use the runtime's own locale.
23
+ * An empty or absent attribute must therefore become `undefined` rather than
24
+ * any particular language tag.
25
+ */
26
+ export declare const resolveLocale: (value: string | null | undefined) => string | undefined;
27
+ /**
28
+ * Parse an instant from author input.
29
+ *
30
+ * Accepts an ISO 8601 string or epoch milliseconds, because hosts have both:
31
+ * a JSON API hands over strings, a `Date.now()` arithmetic result hands over
32
+ * numbers. Anything unparseable is `null` rather than an Invalid Date, so
33
+ * callers test one thing instead of remembering `Number.isNaN(d.getTime())`.
34
+ */
35
+ export declare const parseInstant: (value: string | number | null | undefined) => Date | null;
36
+ /** Parse a finite number from author input, or `null`. */
37
+ export declare const parseNumber: (value: string | number | null | undefined) => number | null;
38
+ /** Date and time presets, mirroring `Intl.DateTimeFormat`'s own vocabulary. */
39
+ export type BoeDateStyle = "full" | "long" | "medium" | "short";
40
+ /** Narrow an author-supplied style, or `undefined` to omit it entirely. */
41
+ export declare const resolveDateStyle: (value: string | null | undefined) => BoeDateStyle | undefined;
42
+ /** A relative duration, in the largest unit that yields a whole number ≥ 1. */
43
+ export interface BoeRelativeParts {
44
+ unit: Intl.RelativeTimeFormatUnit;
45
+ value: number;
46
+ }
47
+ /**
48
+ * Express the distance from `reference` to `instant` in one unit.
49
+ *
50
+ * The sign follows `Intl.RelativeTimeFormat`: negative is the past. The largest
51
+ * unit that yields a magnitude of at least one is chosen, so a four-day-old
52
+ * file reads "4 days ago" rather than "96 hours ago"; months and years are the
53
+ * usual approximations, which is what a reader wants from a relative label and
54
+ * why an exact timestamp stays available alongside it.
55
+ */
56
+ export declare const resolveRelativeParts: (instant: Date, reference: Date) => BoeRelativeParts;
57
+ /**
58
+ * Byte units.
59
+ *
60
+ * `binary` is powers of 1024 with IEC names (KiB, MiB); `decimal` is powers of
61
+ * 1000 with SI names (kB, MB). Box's own UI reports decimal, which is why it is
62
+ * the default — a file the product calls 1.2 MB should not become 1.1 MiB here.
63
+ */
64
+ export type BoeByteUnits = "decimal" | "binary";
65
+ /** Narrow author-supplied byte units, falling back to `decimal`. */
66
+ export declare const resolveByteUnits: (value: string | null | undefined) => BoeByteUnits;
67
+ /** A file size split into the number to format and the unit to append. */
68
+ export interface BoeFileSizeParts {
69
+ suffix: string;
70
+ value: number;
71
+ }
72
+ /**
73
+ * Reduce a byte count to a readable magnitude and its unit.
74
+ *
75
+ * The number is returned unformatted so the caller can run it through
76
+ * `Intl.NumberFormat` in the reader's locale — a size rendered as "1,5 MB" for
77
+ * a German reader is the whole point of not doing this with `toFixed`.
78
+ *
79
+ * Bytes themselves are never fractional: "1.5 B" is nonsense, so the base unit
80
+ * is reported whole.
81
+ */
82
+ export declare const resolveFileSizeParts: (bytes: number, units?: BoeByteUnits) => BoeFileSizeParts;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Typed-value formatting, as a foundation.
3
+ *
4
+ * Every read surface in a content platform renders the same handful of value
5
+ * types — a modified date, a file size, a count, a duration since something
6
+ * happened. Before this module each consumer reinvented that, which meant
7
+ * locale handling, `<time datetime>` semantics and invalid-input behaviour were
8
+ * reinvented with it, differently each time.
9
+ *
10
+ * The rules here are deliberately shared rather than per-component:
11
+ *
12
+ * - **A locale is never guessed.** An absent `locale` means "use the host's",
13
+ * which is `undefined` to `Intl` — not a hardcoded `en-US`. Substituting one
14
+ * would silently render American dates to a German reader.
15
+ * - **Invalid input renders nothing.** Not `Invalid Date`, not `NaN`, not the
16
+ * raw string. A malformed value is a host bug, and showing its wreckage to a
17
+ * reader helps nobody; the element collapses and the host sees an empty slot.
18
+ */
19
+ /**
20
+ * The locale to format in.
21
+ *
22
+ * `undefined` is meaningful: it tells `Intl` to use the runtime's own locale.
23
+ * An empty or absent attribute must therefore become `undefined` rather than
24
+ * any particular language tag.
25
+ */
26
+ export const resolveLocale = (value) => {
27
+ const trimmed = value?.trim();
28
+ return trimmed ? trimmed : undefined;
29
+ };
30
+ /**
31
+ * Parse an instant from author input.
32
+ *
33
+ * Accepts an ISO 8601 string or epoch milliseconds, because hosts have both:
34
+ * a JSON API hands over strings, a `Date.now()` arithmetic result hands over
35
+ * numbers. Anything unparseable is `null` rather than an Invalid Date, so
36
+ * callers test one thing instead of remembering `Number.isNaN(d.getTime())`.
37
+ */
38
+ export const parseInstant = (value) => {
39
+ if (value === null || value === undefined || value === "") {
40
+ return null;
41
+ }
42
+ // A bare number, or a numeric string, is epoch milliseconds. Checking the
43
+ // string form first matters: `new Date("1700000000000")` is not the same
44
+ // instant as `new Date(1700000000000)`.
45
+ const numeric = typeof value === "number" ? value : Number(value);
46
+ const date = Number.isFinite(numeric) ? new Date(numeric) : new Date(String(value));
47
+ return Number.isNaN(date.getTime()) ? null : date;
48
+ };
49
+ /** Parse a finite number from author input, or `null`. */
50
+ export const parseNumber = (value) => {
51
+ if (value === null || value === undefined || value === "") {
52
+ return null;
53
+ }
54
+ const parsed = typeof value === "number" ? value : Number(value);
55
+ return Number.isFinite(parsed) ? parsed : null;
56
+ };
57
+ const DATE_STYLES = new Set(["full", "long", "medium", "short"]);
58
+ /** Narrow an author-supplied style, or `undefined` to omit it entirely. */
59
+ export const resolveDateStyle = (value) => DATE_STYLES.has(value) ? value : undefined;
60
+ /**
61
+ * The units a relative time can be expressed in, largest first.
62
+ *
63
+ * Seconds are the floor: "0 minutes ago" reads as broken, and a surface that
64
+ * needs sub-second precision is not one a human is reading.
65
+ */
66
+ const RELATIVE_UNITS = [
67
+ { unit: "year", ms: 365 * 24 * 60 * 60 * 1000 },
68
+ { unit: "month", ms: 30 * 24 * 60 * 60 * 1000 },
69
+ { unit: "week", ms: 7 * 24 * 60 * 60 * 1000 },
70
+ { unit: "day", ms: 24 * 60 * 60 * 1000 },
71
+ { unit: "hour", ms: 60 * 60 * 1000 },
72
+ { unit: "minute", ms: 60 * 1000 },
73
+ { unit: "second", ms: 1000 },
74
+ ];
75
+ /**
76
+ * Express the distance from `reference` to `instant` in one unit.
77
+ *
78
+ * The sign follows `Intl.RelativeTimeFormat`: negative is the past. The largest
79
+ * unit that yields a magnitude of at least one is chosen, so a four-day-old
80
+ * file reads "4 days ago" rather than "96 hours ago"; months and years are the
81
+ * usual approximations, which is what a reader wants from a relative label and
82
+ * why an exact timestamp stays available alongside it.
83
+ */
84
+ export const resolveRelativeParts = (instant, reference) => {
85
+ const delta = instant.getTime() - reference.getTime();
86
+ const magnitude = Math.abs(delta);
87
+ for (const { unit, ms } of RELATIVE_UNITS) {
88
+ if (magnitude >= ms) {
89
+ // Truncate rather than round: 47 hours is "1 day ago", not "2 days ago".
90
+ // Rounding would let a label claim a boundary the instant has not crossed.
91
+ return { unit, value: Math.trunc(delta / ms) };
92
+ }
93
+ }
94
+ return { unit: "second", value: 0 };
95
+ };
96
+ const BYTE_UNITS = new Set(["decimal", "binary"]);
97
+ /** Narrow author-supplied byte units, falling back to `decimal`. */
98
+ export const resolveByteUnits = (value) => BYTE_UNITS.has(value) ? value : "decimal";
99
+ const DECIMAL_SUFFIXES = ["B", "kB", "MB", "GB", "TB", "PB"];
100
+ const BINARY_SUFFIXES = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
101
+ /**
102
+ * Reduce a byte count to a readable magnitude and its unit.
103
+ *
104
+ * The number is returned unformatted so the caller can run it through
105
+ * `Intl.NumberFormat` in the reader's locale — a size rendered as "1,5 MB" for
106
+ * a German reader is the whole point of not doing this with `toFixed`.
107
+ *
108
+ * Bytes themselves are never fractional: "1.5 B" is nonsense, so the base unit
109
+ * is reported whole.
110
+ */
111
+ export const resolveFileSizeParts = (bytes, units = "decimal") => {
112
+ const base = units === "binary" ? 1024 : 1000;
113
+ const suffixes = units === "binary" ? BINARY_SUFFIXES : DECIMAL_SUFFIXES;
114
+ const magnitude = Math.abs(bytes);
115
+ if (magnitude < base) {
116
+ return { suffix: suffixes[0], value: Math.trunc(bytes) };
117
+ }
118
+ // Clamped so a petabyte-plus value keeps the largest name we have rather than
119
+ // running off the end of the table.
120
+ const exponent = Math.min(Math.floor(Math.log(magnitude) / Math.log(base)), suffixes.length - 1);
121
+ return { suffix: suffixes[exponent], value: bytes / base ** exponent };
122
+ };
@@ -0,0 +1 @@
1
+ export * from "./format.js";
@@ -0,0 +1,3 @@
1
+ // Typed-value formatting: locale resolution, instant parsing, relative
2
+ // durations and byte magnitudes, shared by the box-formatted-* renderers.
3
+ export * from "./format.js";
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { DESIGN_PROFILE_CHANGE_EVENT, DESIGN_PROFILE_VARIABLES, DEFAULT_DESIGN_P
9
9
  export type { ApplyDesignProfileOptions, DesignProfileChangeDetail, DesignProfileController, DesignProfileControllerOptions, DesignProfileDefinition, DesignProfileDensity, DesignProfileElevation, DesignProfileMotion, DesignProfileRadius, DesignProfileSpacing, DesignProfileTypography, } from "./foundations/profiles/index.js";
10
10
  export { boxIconography, boxIconographyAliases } from "./foundations/icons/index.js";
11
11
  export * from "./foundations/layout/index.js";
12
+ export * from "./foundations/format/index.js";
12
13
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./foundations/overlay/index.js";
13
14
  export type { OverlayAlign, OverlayPlacement, OverlaySide, PositionOptions, PositionResult, } from "./foundations/overlay/index.js";
14
15
  export * from "./components/actions/button.js";
@@ -37,6 +38,7 @@ export * from "./components/feedback/due-types.js";
37
38
  export * from "./components/feedback/empty-state.js";
38
39
  export * from "./components/feedback/error-mask.js";
39
40
  export * from "./components/feedback/help-text.js";
41
+ export * from "./components/feedback/indicator.js";
40
42
  export * from "./components/feedback/nudge.js";
41
43
  export * from "./components/feedback/progress-bar.js";
42
44
  export * from "./components/feedback/progress-ring.js";
@@ -66,6 +68,7 @@ export * from "./components/forms/slider.js";
66
68
  export * from "./components/forms/spin-button.js";
67
69
  export * from "./components/forms/switch.js";
68
70
  export * from "./components/forms/tag-input.js";
71
+ export * from "./components/forms/tile-group.js";
69
72
  export * from "./components/forms/time-field.js";
70
73
  export * from "./components/forms/category-selector.js";
71
74
  export * from "./components/forms/checkbox-group.js";
@@ -80,6 +83,12 @@ export * from "./components/forms/search-field.js";
80
83
  export * from "./components/forms/select.js";
81
84
  export * from "./components/forms/text-area.js";
82
85
  export * from "./components/forms/text-field.js";
86
+ export * from "./components/output/formatted-value.js";
87
+ export * from "./components/output/code-block.js";
88
+ export * from "./components/output/formatted-date.js";
89
+ export * from "./components/output/relative-time.js";
90
+ export * from "./components/output/formatted-number.js";
91
+ export * from "./components/output/formatted-file-size.js";
83
92
  export * from "./components/layout/app-shell.js";
84
93
  export * from "./components/layout/divider.js";
85
94
  export * from "./components/layout/grid.js";
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ export { DESIGN_PROFILE_CHANGE_EVENT, DESIGN_PROFILE_VARIABLES, DEFAULT_DESIGN_P
7
7
  export { boxIconography, boxIconographyAliases } from "./foundations/icons/index.js";
8
8
  // Foundations: Adobe Spectrum's responsive grid (shared by box-grid and box-skeleton)
9
9
  export * from "./foundations/layout/index.js";
10
+ // Foundations: locale-aware typed-value formatting (shared by box-formatted-*)
11
+ export * from "./foundations/format/index.js";
10
12
  // Foundations: viewport-aware overlay positioning (for building custom overlays)
11
13
  export { anchorFloating, parsePlacement, resolvePosition, trackAnchor, } from "./foundations/overlay/index.js";
12
14
  // Components: actions
@@ -38,6 +40,7 @@ export * from "./components/feedback/due-types.js";
38
40
  export * from "./components/feedback/empty-state.js";
39
41
  export * from "./components/feedback/error-mask.js";
40
42
  export * from "./components/feedback/help-text.js";
43
+ export * from "./components/feedback/indicator.js";
41
44
  export * from "./components/feedback/nudge.js";
42
45
  export * from "./components/feedback/progress-bar.js";
43
46
  export * from "./components/feedback/progress-ring.js";
@@ -72,6 +75,7 @@ export * from "./components/forms/slider.js";
72
75
  export * from "./components/forms/spin-button.js";
73
76
  export * from "./components/forms/switch.js";
74
77
  export * from "./components/forms/tag-input.js";
78
+ export * from "./components/forms/tile-group.js";
75
79
  export * from "./components/forms/time-field.js";
76
80
  export * from "./components/forms/category-selector.js";
77
81
  export * from "./components/forms/checkbox-group.js";
@@ -86,6 +90,13 @@ export * from "./components/forms/search-field.js";
86
90
  export * from "./components/forms/select.js";
87
91
  export * from "./components/forms/text-area.js";
88
92
  export * from "./components/forms/text-field.js";
93
+ // Components: output (read-only typed-value renderers)
94
+ export * from "./components/output/formatted-value.js";
95
+ export * from "./components/output/code-block.js";
96
+ export * from "./components/output/formatted-date.js";
97
+ export * from "./components/output/relative-time.js";
98
+ export * from "./components/output/formatted-number.js";
99
+ export * from "./components/output/formatted-file-size.js";
89
100
  // Components: layout
90
101
  export * from "./components/layout/app-shell.js";
91
102
  export * from "./components/layout/divider.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unofficialbox/box-open-elements",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Box Open Elements: an open, framework-agnostic design system and web component library for Box-style experiences",
5
5
  "license": "MIT",
6
6
  "homepage": "https://unofficialbox.github.io/box-open-elements",