@mt-gloss/utils 0.1.151 → 0.1.153

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/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { isStatusModeEnabled, v1_1_status_mode, V1_1_STATUS_MODE_ENV, isEdgeHoverResizeEnabled, v1_1_edge_hover_resize, V1_1_EDGE_HOVER_RESIZE_ENV } from './lib/featureFlags';
2
1
  export type { PresetDefinition, MarketPreset, DateRange, MarketEvent } from './lib/types';
3
2
  export { formatValue, getValueFontSize, pickMetricTier } from './lib/formatValue';
4
3
  export type { FormatValueOptions, FormattedValue, MetricTier, PickMetricTierResult } from './lib/formatValue';
@@ -8,6 +7,7 @@ export { hexToRgba, lerpHex } from './lib/color';
8
7
  export { getTransitionStyle, interpolate, parseNumericValue } from './lib/cssAnimation';
9
8
  export { daysBetween } from './lib/daysBetween';
10
9
  export type { DaysBetweenOptions, DaysBetweenRounding } from './lib/daysBetween';
10
+ export { toLocalCalendarDate } from './lib/toLocalCalendarDate';
11
11
  export { formatTrend } from './lib/formatTrend';
12
12
  export type { TrendDirection, FormattedTrend } from './lib/formatTrend';
13
13
  export { formatDateRange, formatMarketDates, isMarketActive, isSameDay, isInRange, generateMarketId } from './lib/dateHelpers';
package/index.js CHANGED
@@ -1,35 +1,20 @@
1
- import { D as G, y as z, P as H, n as k, o as B, R as C, T as Z, d as j, t as K, f as X, b as q, k as W, l as J, c as Q, h as tt, x as et, g as nt, q as rt, j as at, m as ot, u as st, i as it, v as ut, p as ct, a as lt, r as ft, s as gt, e as mt, w as dt } from "./formatting-dHqp6ksD.js";
2
- const m = "NEXT_PUBLIC_V1_1_STATUS_MODE";
3
- function $() {
4
- return typeof globalThis < "u" && globalThis.__GLOSS_FLAGS__?.v1_1_status_mode === !0 ? !0 : typeof process < "u" && process.env ? process.env[m] === "true" : !1;
5
- }
6
- const p = {
7
- env: m,
8
- check: $
9
- }, d = "NEXT_PUBLIC_V1_1_EDGE_HOVER_RESIZE";
10
- function h() {
11
- return typeof globalThis < "u" && globalThis.__GLOSS_FLAGS__?.v1_1_edge_hover_resize === !0 ? !0 : typeof process < "u" && process.env ? process.env[d] === "true" : !1;
12
- }
13
- const M = {
14
- env: d,
15
- check: h
16
- };
17
- function D(t, { prefix: e = "", suffix: n = "", abbreviated: r = !0, kThreshold: o = 1e3 } = {}) {
1
+ import { D as Y, y as w, P as O, n as v, o as C, R as V, T as z, d as H, t as j, f as k, b as B, k as G, l as K, c as q, h as W, x as Z, g as J, q as Q, j as X, m as tt, u as et, i as nt, v as rt, p as at, a as ot, r as st, s as it, e as ct, w as ut } from "./formatting-dHqp6ksD.js";
2
+ function m(t, { prefix: e = "", suffix: n = "", abbreviated: r = !0, kThreshold: o = 1e3 } = {}) {
18
3
  if (t == null) {
19
4
  const l = e ? `${e} —${n}` : `—${n}`;
20
5
  return { display: l.trim(), full: l.trim(), length: l.trim().length };
21
6
  }
22
7
  const a = typeof t == "number" ? t : parseFloat(String(t).replace(/,/g, ""));
23
8
  if (isNaN(a)) {
24
- const u = `${e}${t}${n}`;
25
- return { display: u, full: u, length: u.length };
9
+ const c = `${e}${t}${n}`;
10
+ return { display: c, full: c, length: c.length };
26
11
  }
27
12
  let s;
28
13
  r !== !1 && Math.abs(a) >= 1e6 ? s = (a / 1e6).toFixed(1).replace(/\.0$/, "") + "M" : r !== !1 && Math.abs(a) >= o ? s = (a / 1e3).toFixed(1).replace(/\.0$/, "") + "K" : s = a.toLocaleString("en-US");
29
- const i = `${e}${s}${n}`, c = `${e}${a.toLocaleString("en-US")}${n}`;
30
- return { display: i, full: c, length: i.length };
14
+ const i = `${e}${s}${n}`, u = `${e}${a.toLocaleString("en-US")}${n}`;
15
+ return { display: i, full: u, length: i.length };
31
16
  }
32
- function E(t, e) {
17
+ function h(t, e) {
33
18
  return (e ?? 1) >= 2 ? t <= 4 || t <= 6 ? 30 : t <= 8 ? 24 : 20 : t <= 5 ? 28 : t <= 8 ? 20 : 17;
34
19
  }
35
20
  const f = {
@@ -40,21 +25,21 @@ const f = {
40
25
  3: 12
41
26
  // "$1,234,567"
42
27
  };
43
- function _(t) {
28
+ function $(t) {
44
29
  return t <= 1 ? f[1] : t >= 3 ? f[3] : f[t] ?? f[1];
45
30
  }
46
- function y(t, e = {}, n = 1) {
47
- const r = _(n), o = e.prefix ?? "", a = e.suffix ?? "", s = typeof t == "number" ? t : parseFloat(String(t ?? "").replace(/,/g, ""));
31
+ function d(t, e = {}, n = 1) {
32
+ const r = $(n), o = e.prefix ?? "", a = e.suffix ?? "", s = typeof t == "number" ? t : parseFloat(String(t ?? "").replace(/,/g, ""));
48
33
  if (t != null && !Number.isNaN(s)) {
49
- const c = `${o}${s.toLocaleString("en-US", { minimumFractionDigits: Number.isInteger(s) ? 0 : 2, maximumFractionDigits: 2 })}${a}`;
50
- if (c.length <= r) return { formatted: c, tier: "wide", length: c.length };
51
- const u = `${o}${Math.round(s).toLocaleString("en-US")}${a}`;
52
- if (u.length <= r) return { formatted: u, tier: "medium", length: u.length };
34
+ const u = `${o}${s.toLocaleString("en-US", { minimumFractionDigits: Number.isInteger(s) ? 0 : 2, maximumFractionDigits: 2 })}${a}`;
35
+ if (u.length <= r) return { formatted: u, tier: "wide", length: u.length };
36
+ const c = `${o}${Math.round(s).toLocaleString("en-US")}${a}`;
37
+ if (c.length <= r) return { formatted: c, tier: "medium", length: c.length };
53
38
  }
54
- const i = D(t, { ...e, abbreviated: !0 });
39
+ const i = m(t, { ...e, abbreviated: !0 });
55
40
  return i.display.length <= r ? { formatted: i.display, tier: "narrow", length: i.display.length } : { formatted: i.display, tier: "very-narrow", length: i.display.length };
56
41
  }
57
- function T(t, { notation: e = "standard", fractionDigits: n, currency: r = "USD" } = {}) {
42
+ function p(t, { notation: e = "standard", fractionDigits: n, currency: r = "USD" } = {}) {
58
43
  const o = { style: "currency", currency: r };
59
44
  if (e === "compact")
60
45
  o.notation = "compact", o.compactDisplay = "short", n !== void 0 && (o.minimumFractionDigits = n, o.maximumFractionDigits = n);
@@ -72,115 +57,113 @@ function g(t) {
72
57
  parseInt(e.slice(4, 6), 16)
73
58
  ];
74
59
  }
75
- function b(t, e) {
60
+ function M(t, e) {
76
61
  const n = t.replace("#", "");
77
62
  if (!/^[0-9a-fA-F]{6}$/.test(n))
78
63
  return `rgba(0,0,0,${e})`;
79
64
  const [r, o, a] = g(t);
80
65
  return `rgba(${r},${o},${a},${e})`;
81
66
  }
82
- function F(t, e, n) {
83
- const r = Math.max(0, Math.min(1, n)), [o, a, s] = g(t), [i, c, u] = g(e);
84
- return `rgb(${Math.round(o + (i - o) * r)}, ${Math.round(a + (c - a) * r)}, ${Math.round(
85
- s + (u - s) * r
67
+ function S(t, e, n) {
68
+ const r = Math.max(0, Math.min(1, n)), [o, a, s] = g(t), [i, u, c] = g(e);
69
+ return `rgb(${Math.round(o + (i - o) * r)}, ${Math.round(a + (u - a) * r)}, ${Math.round(
70
+ s + (c - s) * r
86
71
  )})`;
87
72
  }
88
- function R(t, e = 200, n = "cubic-bezier(0, 0, 0.2, 1)") {
73
+ function y(t, e = 200, n = "cubic-bezier(0, 0, 0.2, 1)") {
89
74
  return t.map((r) => `${r} ${e}ms ${n}`).join(", ");
90
75
  }
91
- function P(t, e, n) {
76
+ function F(t, e, n) {
92
77
  return t + (e - t) * n;
93
78
  }
94
- function I(t) {
79
+ function T(t) {
95
80
  const e = t.match(/^([-\d.]+)(.*)$/);
96
81
  return e ? { value: parseFloat(e[1]), unit: e[2] || "" } : { value: 0, unit: "" };
97
82
  }
98
- const S = 1e3 * 60 * 60 * 24;
99
- function L(t, e, n = {}) {
100
- const { rounding: r = "round", inclusive: o = !1, min: a, absolute: s = !0 } = n, i = e.getTime() - t.getTime(), u = (s ? Math.abs(i) : i) / S;
101
- let l = r === "ceil" ? Math.ceil(u) : r === "floor" ? Math.floor(u) : r === "round" ? Math.round(u) : u;
83
+ const D = 1e3 * 60 * 60 * 24;
84
+ function b(t, e, n = {}) {
85
+ const { rounding: r = "round", inclusive: o = !1, min: a, absolute: s = !0 } = n, i = e.getTime() - t.getTime(), c = (s ? Math.abs(i) : i) / D;
86
+ let l = r === "ceil" ? Math.ceil(c) : r === "floor" ? Math.floor(c) : r === "round" ? Math.round(c) : c;
102
87
  return o && (l += 1), a !== void 0 && (l = Math.max(l, a)), l;
103
88
  }
104
- function v(t) {
89
+ function P(t) {
90
+ return new Date(t.getUTCFullYear(), t.getUTCMonth(), t.getUTCDate());
91
+ }
92
+ function R(t) {
105
93
  if (t == null || t === 0)
106
94
  return { text: "0%", direction: "flat" };
107
95
  const e = t > 0 ? "up" : t < 0 ? "down" : "flat", n = Math.abs(t);
108
96
  return { text: n >= 3 ? `${Math.round(n)}%` : `${n.toFixed(1)}%`, direction: e };
109
97
  }
110
- function U(t, e) {
98
+ function E(t, e) {
111
99
  if (!t || !e) return "";
112
100
  const n = t.toLocaleDateString("en-US", { month: "short" }), r = e.toLocaleDateString("en-US", { month: "short" }), o = t.getDate(), a = e.getDate(), s = t.getFullYear(), i = e.getFullYear();
113
101
  return t.getTime() === e.getTime() || n === r && o === a && s === i ? `${n} ${o}, ${s}` : n === r && s === i ? `${n} ${o}–${a}` : s === i ? `${n} ${o} – ${r} ${a}` : `${n} ${o}, ${s} – ${r} ${a}, ${i}`;
114
102
  }
115
- function N(t, e) {
103
+ function I(t, e) {
116
104
  const n = t.toLocaleDateString("en-US", { month: "short" }), r = e.toLocaleDateString("en-US", { month: "short" }), o = t.getDate(), a = e.getDate();
117
105
  return n === r ? `${n} ${o}–${a}` : `${n} ${o} – ${r} ${a}`;
118
106
  }
119
- function A(t, e = /* @__PURE__ */ new Date()) {
107
+ function U(t, e = /* @__PURE__ */ new Date()) {
120
108
  return !t?.startDate || !t?.endDate ? !1 : e >= t.startDate && e <= t.endDate;
121
109
  }
122
- function V(t, e) {
110
+ function L(t, e) {
123
111
  return !t || !e ? !1 : t.getFullYear() === e.getFullYear() && t.getMonth() === e.getMonth() && t.getDate() === e.getDate();
124
112
  }
125
- function x(t, e, n) {
113
+ function _(t, e, n) {
126
114
  if (!t || !e || !n) return !1;
127
115
  const r = t.getTime(), o = e.getTime(), a = n.getTime();
128
116
  return r >= Math.min(o, a) && r <= Math.max(o, a);
129
117
  }
130
- function O() {
118
+ function x() {
131
119
  return `custom-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
132
120
  }
133
121
  export {
134
- G as DEFAULT_MARKET_PRESETS,
135
- z as DEFAULT_PINNED,
136
- H as PERIOD_OPTIONS,
137
- k as PRESET_ID_MAP,
138
- B as PRESET_ID_REVERSE_MAP,
139
- C as ROLLING_OPTIONS,
140
- Z as TODAY_PRESET,
141
- d as V1_1_EDGE_HOVER_RESIZE_ENV,
142
- m as V1_1_STATUS_MODE_ENV,
143
- L as daysBetween,
144
- j as detectPreset,
145
- K as findPreset,
146
- T as formatCurrency,
147
- U as formatDateRange,
148
- X as formatDateRangeSmart,
149
- q as formatDateRangeWithPreset,
150
- W as formatDuration,
151
- N as formatMarketDates,
152
- J as formatPreset,
153
- Q as formatShortYear,
154
- tt as formatSingleDate,
155
- v as formatTrend,
156
- D as formatValue,
157
- O as generateMarketId,
158
- et as getAllPresets,
159
- nt as getDateRangeDisplayInfo,
160
- rt as getDateRangeForPreset,
161
- at as getDurationDays,
162
- ot as getPresetDateRange,
163
- st as getPresetLabel,
164
- R as getTransitionStyle,
165
- E as getValueFontSize,
166
- b as hexToRgba,
167
- P as interpolate,
168
- h as isEdgeHoverResizeEnabled,
169
- x as isInRange,
170
- A as isMarketActive,
171
- V as isSameDay,
172
- $ as isStatusModeEnabled,
173
- it as isValidDateRangeFormat,
174
- F as lerpHex,
175
- ut as normalizePresetId,
176
- ct as parseDateRange,
177
- I as parseNumericValue,
178
- y as pickMetricTier,
179
- lt as resolveEvent,
180
- ft as resolvePreset,
181
- gt as smartDateFormat,
182
- mt as stripLeadingZeros,
183
- dt as toUppercasePresetId,
184
- M as v1_1_edge_hover_resize,
185
- p as v1_1_status_mode
122
+ Y as DEFAULT_MARKET_PRESETS,
123
+ w as DEFAULT_PINNED,
124
+ O as PERIOD_OPTIONS,
125
+ v as PRESET_ID_MAP,
126
+ C as PRESET_ID_REVERSE_MAP,
127
+ V as ROLLING_OPTIONS,
128
+ z as TODAY_PRESET,
129
+ b as daysBetween,
130
+ H as detectPreset,
131
+ j as findPreset,
132
+ p as formatCurrency,
133
+ E as formatDateRange,
134
+ k as formatDateRangeSmart,
135
+ B as formatDateRangeWithPreset,
136
+ G as formatDuration,
137
+ I as formatMarketDates,
138
+ K as formatPreset,
139
+ q as formatShortYear,
140
+ W as formatSingleDate,
141
+ R as formatTrend,
142
+ m as formatValue,
143
+ x as generateMarketId,
144
+ Z as getAllPresets,
145
+ J as getDateRangeDisplayInfo,
146
+ Q as getDateRangeForPreset,
147
+ X as getDurationDays,
148
+ tt as getPresetDateRange,
149
+ et as getPresetLabel,
150
+ y as getTransitionStyle,
151
+ h as getValueFontSize,
152
+ M as hexToRgba,
153
+ F as interpolate,
154
+ _ as isInRange,
155
+ U as isMarketActive,
156
+ L as isSameDay,
157
+ nt as isValidDateRangeFormat,
158
+ S as lerpHex,
159
+ rt as normalizePresetId,
160
+ at as parseDateRange,
161
+ T as parseNumericValue,
162
+ d as pickMetricTier,
163
+ ot as resolveEvent,
164
+ st as resolvePreset,
165
+ it as smartDateFormat,
166
+ ct as stripLeadingZeros,
167
+ P as toLocalCalendarDate,
168
+ ut as toUppercasePresetId
186
169
  };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Re-project a date onto **local midnight of its UTC calendar date**.
3
+ *
4
+ * Order dates arrive from the BFF as date-only ISO strings (e.g. `"2026-03-09"`).
5
+ * `new Date("2026-03-09")` parses this as **UTC midnight**, which in timezones
6
+ * west of UTC falls on the *previous* local calendar day (e.g. March 8 at
7
+ * 19:00 EST). To bucket an order by its intended calendar day regardless of the
8
+ * viewer's timezone, both the dashboard's table filter and its chart grouping
9
+ * extract the date's **UTC** year/month/day and rebuild a `Date` at **local**
10
+ * midnight of those components. Doing this on both surfaces guarantees the
11
+ * table filter (which uses local-midnight boundaries) and the chart's UTC
12
+ * date-grouping agree on which day an order belongs to.
13
+ *
14
+ * Regress this and table totals silently disagree with chart bars: the only
15
+ * orders affected are those whose date sits on a UTC-vs-local day boundary, so
16
+ * nothing throws — the two surfaces just stop matching.
17
+ *
18
+ * Purity note: the input `Date` only needs to carry the right instant; this
19
+ * helper reads its UTC Y/M/D and nothing else. String parsing and null handling
20
+ * stay with the caller — one consumer coerces with `new Date(str)`, another with
21
+ * date-fns `parseISO` — and both delegate the projection here.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { toLocalCalendarDate } from '@mt-gloss/utils';
26
+ *
27
+ * // A date-only ISO string parses as UTC midnight; the helper re-keys it to
28
+ * // local midnight of the SAME calendar date — timezone-independent.
29
+ * const d = toLocalCalendarDate(new Date('2026-03-09'));
30
+ * d.getFullYear(); // 2026
31
+ * d.getMonth(); // 2 (March, 0-indexed)
32
+ * d.getDate(); // 9 — never the 8th, even west of UTC
33
+ * ```
34
+ */
35
+ export declare function toLocalCalendarDate(date: Date): Date;
36
+ //# sourceMappingURL=toLocalCalendarDate.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mt-gloss/utils",
3
- "version": "0.1.151",
3
+ "version": "0.1.153",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,2 +0,0 @@
1
- export { isStatusModeEnabled, v1_1_status_mode, V1_1_STATUS_MODE_ENV, isEdgeHoverResizeEnabled, v1_1_edge_hover_resize, V1_1_EDGE_HOVER_RESIZE_ENV } from './v1_1_flags';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1,45 +0,0 @@
1
- /**
2
- * Phase 10: v1.1 feature flags for Status Mode.
3
- *
4
- * Flag is OFF by default in production bundles.
5
- * Enable via env var (server/build-time) or globalThis override (runtime/test).
6
- */
7
- export declare const V1_1_STATUS_MODE_ENV = "NEXT_PUBLIC_V1_1_STATUS_MODE";
8
- /**
9
- * Returns true when Status Mode rendering is enabled.
10
- *
11
- * Priority order:
12
- * 1. globalThis.__GLOSS_FLAGS__.v1_1_status_mode === true (runtime override / tests)
13
- * 2. process.env.NEXT_PUBLIC_V1_1_STATUS_MODE === 'true' (build-time env var)
14
- * 3. false (default — flag OFF)
15
- */
16
- export declare function isStatusModeEnabled(): boolean;
17
- export declare const v1_1_status_mode: {
18
- env: string;
19
- check: typeof isStatusModeEnabled;
20
- };
21
- /**
22
- * Phase 10.5 WR-01 — edge-hover resize decoupling.
23
- *
24
- * Independent feature flag for edge-hover resize. Previously the consumer
25
- * gated `enableEdgeHover` on `isStatusModeEnabled()`, which coupled two
26
- * independent features behind a single flag. This helper lets consumers
27
- * toggle edge-hover-resize on its own.
28
- *
29
- * Flag is OFF by default in production bundles.
30
- */
31
- export declare const V1_1_EDGE_HOVER_RESIZE_ENV = "NEXT_PUBLIC_V1_1_EDGE_HOVER_RESIZE";
32
- /**
33
- * Returns true when edge-hover resize is enabled.
34
- *
35
- * Priority order (mirrors isStatusModeEnabled):
36
- * 1. globalThis.__GLOSS_FLAGS__.v1_1_edge_hover_resize === true (runtime override / tests)
37
- * 2. process.env.NEXT_PUBLIC_V1_1_EDGE_HOVER_RESIZE === 'true' (build-time env var)
38
- * 3. false (default — flag OFF)
39
- */
40
- export declare function isEdgeHoverResizeEnabled(): boolean;
41
- export declare const v1_1_edge_hover_resize: {
42
- env: string;
43
- check: typeof isEdgeHoverResizeEnabled;
44
- };
45
- //# sourceMappingURL=v1_1_flags.d.ts.map