@mx-cartographer/experiences 9.4.34 → 9.4.35

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [9.4.35] - 09-21-2026
2
+
3
+ - **FIXED** - Trends keyboard focus no longer skips interactive elements below the fold during forward Tab navigation; focused controls are now scrolled into view, keeping the focus order perceivable for keyboard users (MEW-3593)
4
+
1
5
  ## [9.4.34] - 09-21-2026
2
6
 
3
7
  - **ADDED** - refreshLoadedCategoryTotals to CategoryStore, which re-fetches category totals using the same accounts and date range as the last fetch
@@ -1,206 +1,213 @@
1
- import { useAccountStore as Ce, useAppConfig as _e, useCategoryStore as Te, useEvent as Se, useGlobalCopyStore as ye, useGlobalStore as we, useGlobalUiStore as Ee, useTransactionStore as De, useTrendsStore as Le } from "../common/context/hooks.es.js";
2
- import { useInsightsEnabled as Ae } from "../common/hooks/useInsightsEnabled.es.js";
3
- import { useScreenSize as be } from "../common/hooks/useScreenSize.es.js";
4
- import { useWidgetLoadTimer as Ie } from "../common/hooks/useWidgetLoadTimer.es.js";
1
+ import { useAccountStore as Te, useAppConfig as Se, useCategoryStore as Ee, useEvent as ye, useGlobalCopyStore as we, useGlobalStore as De, useGlobalUiStore as Le, useTransactionStore as be, useTrendsStore as Ae } from "../common/context/hooks.es.js";
2
+ import { useInsightsEnabled as Ie } from "../common/hooks/useInsightsEnabled.es.js";
3
+ import { useScreenSize as Re } from "../common/hooks/useScreenSize.es.js";
4
+ import { useWidgetLoadTimer as ve } from "../common/hooks/useWidgetLoadTimer.es.js";
5
5
  import xe from "../common/components/drawer/Drawer.es.js";
6
6
  import { ANALYTICS_EVENTS as a } from "../common/constants/Analytics.es.js";
7
- import Re from "../common/components/Loader.es.js";
8
- import { WidgetContainer as ve } from "../common/components/WidgetContainer.es.js";
9
- import Ne from "../common/components/ConnectDrawer.es.js";
10
- import { EmptyState as Be } from "../common/components/EmptyState.es.js";
11
- import { TRENDS_ANALYTICS_METADATA as c } from "./constants/TrendsWidget.es.js";
12
- import We from "./components/TrendsViewToggle.es.js";
13
- import ke from "./components/DateRangeText.es.js";
14
- import He from "./components/TrendsTableContainer.es.js";
15
- import Ge from "./components/CategoriesList.es.js";
16
- import Me from "./components/TrendsInsights.es.js";
17
- import { CategoryDetails as Oe } from "./components/CategoryDetails.es.js";
7
+ import Ne from "../common/components/Loader.es.js";
8
+ import { WidgetContainer as Be } from "../common/components/WidgetContainer.es.js";
9
+ import We from "../common/components/ConnectDrawer.es.js";
10
+ import { EmptyState as ke } from "../common/components/EmptyState.es.js";
11
+ import { TRENDS_ANALYTICS_METADATA as d } from "./constants/TrendsWidget.es.js";
12
+ import He from "./components/TrendsViewToggle.es.js";
13
+ import Ge from "./components/DateRangeText.es.js";
14
+ import Fe from "./components/TrendsTableContainer.es.js";
15
+ import Me from "./components/CategoriesList.es.js";
16
+ import Oe from "./components/TrendsInsights.es.js";
17
+ import { CategoryDetails as Ve } from "./components/CategoryDetails.es.js";
18
18
  import ze from "./components/TrendsChartTotals.es.js";
19
- import { TRENDS_BEAT_TEMPLATES as Fe } from "./constants/TrendsInsights.es.js";
20
- import { buildEmptyStateProps as Ke } from "./utils/TrendsWidget.es.js";
19
+ import { TRENDS_BEAT_TEMPLATES as Ke } from "./constants/TrendsInsights.es.js";
20
+ import { buildEmptyStateProps as je } from "./utils/TrendsWidget.es.js";
21
21
  import i from "react";
22
- import { observer as Ve } from "mobx-react-lite";
22
+ import { observer as Pe } from "mobx-react-lite";
23
23
  import f from "@mui/material/Stack";
24
- import je from "@mui/material/Button";
25
- import { ArrowBack as Pe } from "@mxenabled/mx-icons";
26
- import { Fragment as Ue, jsx as e, jsxs as s } from "react/jsx-runtime";
24
+ import Ue from "@mui/material/Button";
25
+ import { ArrowBack as Ye } from "@mxenabled/mx-icons";
26
+ import { Fragment as $e, jsx as t, jsxs as c } from "react/jsx-runtime";
27
27
  import w from "@mui/material/Box";
28
- import { differenceInCalendarMonths as k } from "date-fns/differenceInCalendarMonths";
29
- import { addSeconds as Ye } from "date-fns/addSeconds";
30
- var $e = ({ onBackClick: H, onMenuClick: G, onInsightCardClick: M, sx: O }) => {
31
- const { onEvent: r } = Se(), { config: E } = _e(), { isDesktop: z, isLargeDesktop: F, isMobile: C } = be(), { isAccountDataLoaded: p, loadAccountData: K, visibleAccounts: V } = Ce(), { categoriesLoaded: j, loadCategories: P, monthlyCategoryTotals: d, monthlyTotalsLoaded: U, reloadCategoryTotals: Y } = Te(), { isTransactionDataLoaded: $, loadTransactionData: q, setFilter: J } = De(), { selectedCategoryData: _, selectedDateRange: o, setSelectedCategoryData: D, setSelectedDateRange: Q } = Le(), { isInitialized: T, selectedAccounts: X, selectedAccountGuids: L } = Ee(), { trends: l, connect: Z } = ye(), { beatStore: A } = we(), b = Ae() && E.show_insights_widget_in_master, m = z || F, [I, ee] = i.useState(!1), [S, te] = i.useState(!1), [h, oe] = i.useState("Chart"), [y, x] = i.useState(""), [R, ie] = i.useState(window.innerHeight), ne = m ? 56 : 0, v = R - 208, re = R - (b ? 550 + ne : 266), [ae, N] = i.useState(!1), se = E.show_connections_widget_in_master;
32
- Ie({
28
+ import { differenceInCalendarMonths as H } from "date-fns/differenceInCalendarMonths";
29
+ import { addSeconds as qe } from "date-fns/addSeconds";
30
+ var Je = ({ onBackClick: G, onMenuClick: F, onInsightCardClick: M, sx: O }) => {
31
+ const { onEvent: s } = ye(), { config: D } = Se(), { isDesktop: V, isLargeDesktop: z, isMobile: C } = Re(), { isAccountDataLoaded: u, loadAccountData: K, visibleAccounts: j } = Te(), { categoriesLoaded: P, loadCategories: U, monthlyCategoryTotals: m, monthlyTotalsLoaded: Y, reloadCategoryTotals: $ } = Ee(), { isTransactionDataLoaded: q, loadTransactionData: J, setFilter: Q } = be(), { selectedCategoryData: _, selectedDateRange: o, setSelectedCategoryData: L, setSelectedDateRange: X } = Ae(), { isInitialized: T, selectedAccounts: Z, selectedAccountGuids: b } = Le(), { trends: l, connect: ee } = we(), { beatStore: A } = De(), I = Ie() && D.show_insights_widget_in_master, g = V || z, [R, te] = i.useState(!1), [S, oe] = i.useState(!1), [h, ie] = i.useState("Chart"), [E, v] = i.useState(""), [x, ne] = i.useState(window.innerHeight), re = g ? 56 : 0, N = x - 208, se = x - (I ? 550 + re : 266), [ae, B] = i.useState(!1), ce = D.show_connections_widget_in_master, y = i.useRef(void 0), le = i.useCallback((e) => {
32
+ if (y.current?.(), y.current = void 0, !e) return;
33
+ const n = (r) => {
34
+ r.target instanceof HTMLElement && r.target.matches(":focus-visible") && r.target.scrollIntoView({ block: "nearest" });
35
+ };
36
+ e.addEventListener("focusin", n), y.current = () => e.removeEventListener("focusin", n);
37
+ }, []);
38
+ ve({
33
39
  widgetName: "TrendsWidget",
34
- isLoaded: I
40
+ isLoaded: R
35
41
  });
36
- const B = i.useMemo(() => S ? A.getFilteredBeats({ templates: Fe }) : [], [S]);
42
+ const W = i.useMemo(() => S ? A.getFilteredBeats({ templates: Ke }) : [], [S]);
37
43
  i.useEffect(() => {
38
- const t = () => ie(window.innerHeight);
39
- return window.addEventListener("resize", t), p || K().finally(), j || P().finally(), $ || q().finally(), () => window.removeEventListener("resize", t);
44
+ const e = () => ne(window.innerHeight);
45
+ return window.addEventListener("resize", e), u || K().finally(), P || U().finally(), q || J().finally(), () => window.removeEventListener("resize", e);
40
46
  }, []), i.useEffect(() => {
41
- T && p && (A.loadBeats().finally(() => te(!0)), Y(X, o.start, o.end).finally(() => {
42
- ee(!0), r(a.TRENDS_LOAD_WIDGET, {
43
- ...c,
44
- time_period: `${k(o.end, o.start)}M`
47
+ T && u && (A.loadBeats().finally(() => oe(!0)), $(Z, o.start, o.end).finally(() => {
48
+ te(!0), s(a.TRENDS_LOAD_WIDGET, {
49
+ ...d,
50
+ time_period: `${H(o.end, o.start)}M`
45
51
  });
46
52
  }));
47
53
  }, [
48
54
  T,
49
- p,
50
- L.join(","),
55
+ u,
56
+ b.join(","),
51
57
  o.start.getTime(),
52
58
  o.end.getTime()
53
59
  ]), i.useEffect(() => {
54
- if (d.length !== 0) {
55
- const t = {
56
- accounts: L,
60
+ if (m.length !== 0) {
61
+ const e = {
62
+ accounts: b,
57
63
  dateRange: {
58
64
  start: o.start,
59
65
  end: o.end
60
66
  }
61
67
  };
62
- J({
63
- ...t,
64
- custom: (n) => !!d.find((g) => g.top_level_category_guid === n.top_level_category_guid || g.category_guid === n.category_guid)
68
+ Q({
69
+ ...e,
70
+ custom: (n) => !!m.find((r) => r.top_level_category_guid === n.top_level_category_guid || r.category_guid === n.category_guid)
65
71
  });
66
72
  }
67
- }, [d]);
68
- const le = (t) => {
69
- D(t);
70
- }, ce = () => {
71
- D(null), r(a.TRENDS_CLICK_BACK, c);
72
- }, de = () => {
73
- N(!0), r(a.TRENDS_CLICK_CONNECT_ACCOUNTS);
74
- }, me = (t, n) => {
75
- oe(n ?? h), r(a.TRENDS_CLICK_TOGGLE_VIEW, {
76
- ...c,
73
+ }, [m]);
74
+ const de = (e) => {
75
+ L(e);
76
+ }, me = () => {
77
+ L(null), s(a.TRENDS_CLICK_BACK, d);
78
+ }, ge = () => {
79
+ B(!0), s(a.TRENDS_CLICK_CONNECT_ACCOUNTS);
80
+ }, fe = (e, n) => {
81
+ ie(n ?? h), s(a.TRENDS_CLICK_TOGGLE_VIEW, {
82
+ ...d,
77
83
  toggleView: n
78
84
  });
79
- }, ge = (t) => {
80
- const n = Ye(t?.[0], 1), g = t?.[1], ue = k(g, n);
81
- Q({
85
+ }, ue = (e) => {
86
+ const n = qe(e?.[0], 1), r = e?.[1], _e = H(r, n);
87
+ X({
82
88
  start: n,
83
- end: g
84
- }), r(a.TRENDS_CLICK_TIME_WINDOW, {
85
- ...c,
86
- time_period: ue + "M"
89
+ end: r
90
+ }), s(a.TRENDS_CLICK_TIME_WINDOW, {
91
+ ...d,
92
+ time_period: _e + "M"
87
93
  });
88
- }, fe = (t) => {
89
- r(a.TRENDS_CLICK_FILTER, {
90
- ...c,
91
- filterValue: t
94
+ }, he = (e) => {
95
+ s(a.TRENDS_CLICK_FILTER, {
96
+ ...d,
97
+ filterValue: e
92
98
  });
93
- }, pe = (t) => {
94
- x(t);
95
- }, he = () => {
96
- x(""), r(a.TRENDS_CLICK_ALL_CATEGORIES, c);
99
+ }, pe = (e) => {
100
+ v(e);
101
+ }, Ce = () => {
102
+ v(""), s(a.TRENDS_CLICK_ALL_CATEGORIES, d);
97
103
  };
98
- if (!T || !p || !I || !U) return /* @__PURE__ */ e(Re, {});
99
- const W = V.length === 0 || d.length === 0, u = Ke(l, se, de);
100
- return /* @__PURE__ */ s(ve, {
101
- calendarActions: { onRangeChanged: ge },
104
+ if (!T || !u || !R || !Y) return /* @__PURE__ */ t(Ne, {});
105
+ const k = j.length === 0 || m.length === 0, p = je(l, ce, ge);
106
+ return /* @__PURE__ */ c(Be, {
107
+ calendarActions: { onRangeChanged: ue },
102
108
  dateRange: o,
103
109
  dateRangeVariant: C ? "timeframetabs" : "timeframebuttons",
104
- onAccountsFilterClick: fe,
105
- onBackClick: H,
106
- onMenuClick: G,
110
+ onAccountsFilterClick: he,
111
+ onBackClick: G,
112
+ onMenuClick: F,
107
113
  sx: O,
108
114
  title: l.title,
109
- children: [/* @__PURE__ */ s(f, {
115
+ children: [/* @__PURE__ */ c(f, {
116
+ ref: le,
110
117
  sx: { px: C ? 0 : 48 },
111
118
  children: [
112
- /* @__PURE__ */ s(f, {
119
+ /* @__PURE__ */ c(f, {
113
120
  flexDirection: "row",
114
121
  justifyContent: "space-between",
115
122
  sx: {
116
123
  px: C ? 16 : 0,
117
124
  py: 16
118
125
  },
119
- children: [/* @__PURE__ */ e(ke, {
120
- selectedCategoryGuid: y,
126
+ children: [/* @__PURE__ */ t(Ge, {
127
+ selectedCategoryGuid: E,
121
128
  selectedDateRange: o
122
- }), /* @__PURE__ */ e(We, {
123
- onTabChange: me,
129
+ }), /* @__PURE__ */ t(He, {
130
+ onTabChange: fe,
124
131
  selectedTab: h
125
132
  })]
126
133
  }),
127
- /* @__PURE__ */ e(f, {
134
+ /* @__PURE__ */ t(f, {
128
135
  alignItems: "center",
129
136
  flexDirection: "row",
130
137
  sx: { pb: 8 },
131
138
  width: "100%",
132
- children: /* @__PURE__ */ e(w, {
139
+ children: /* @__PURE__ */ t(w, {
133
140
  flexGrow: 1,
134
- children: y && /* @__PURE__ */ s(je, {
135
- onClick: he,
141
+ children: E && /* @__PURE__ */ c(Ue, {
142
+ onClick: Ce,
136
143
  sx: {
137
144
  p: 0,
138
145
  pr: 5
139
146
  },
140
- children: [/* @__PURE__ */ e(Pe, {}), l.all_categories]
147
+ children: [/* @__PURE__ */ t(Ye, {}), l.all_categories]
141
148
  })
142
149
  })
143
150
  }),
144
- W && /* @__PURE__ */ e(Be, {
145
- header: u.header,
151
+ k && /* @__PURE__ */ t(ke, {
152
+ header: p.header,
146
153
  icon: "multiline_chart",
147
- onClick: u.onClickHandler,
148
- primaryButton: u.primaryButton,
149
- subText: u.description,
154
+ onClick: p.onClickHandler,
155
+ primaryButton: p.primaryButton,
156
+ subText: p.description,
150
157
  sx: {
151
- height: v,
158
+ height: N,
152
159
  maxWidth: 432
153
160
  }
154
161
  }),
155
- !W && /* @__PURE__ */ s(f, {
156
- flexDirection: m ? "row" : "column",
157
- gap: m ? 48 : 16,
158
- children: [h === "Chart" && /* @__PURE__ */ s(Ue, { children: [/* @__PURE__ */ e(w, {
159
- sx: { width: m ? "68%" : "100%" },
160
- children: /* @__PURE__ */ e(ze, {
161
- availableHeight: v,
162
+ !k && /* @__PURE__ */ c(f, {
163
+ flexDirection: g ? "row" : "column",
164
+ gap: g ? 48 : 16,
165
+ children: [h === "Chart" && /* @__PURE__ */ c($e, { children: [/* @__PURE__ */ t(w, {
166
+ sx: { width: g ? "68%" : "100%" },
167
+ children: /* @__PURE__ */ t(ze, {
168
+ availableHeight: N,
162
169
  minHeight: 450,
163
170
  selectedDateRange: o,
164
- totals: d
171
+ totals: m
165
172
  })
166
- }), /* @__PURE__ */ s(f, {
173
+ }), /* @__PURE__ */ c(f, {
167
174
  gap: 16,
168
- sx: { width: m ? "32%" : "100%" },
169
- children: [b && S && B.length > 0 && /* @__PURE__ */ e(Me, {
170
- beats: B,
175
+ sx: { width: g ? "32%" : "100%" },
176
+ children: [I && S && W.length > 0 && /* @__PURE__ */ t(Oe, {
177
+ beats: W,
171
178
  onInsightCardClick: M
172
- }), /* @__PURE__ */ e(Ge, {
173
- availableHeight: re,
174
- onCategoryClick: le
179
+ }), /* @__PURE__ */ t(Me, {
180
+ availableHeight: se,
181
+ onCategoryClick: de
175
182
  })]
176
- })] }), h === "Table" && /* @__PURE__ */ e(w, {
183
+ })] }), h === "Table" && /* @__PURE__ */ t(w, {
177
184
  sx: { width: "100%" },
178
- children: /* @__PURE__ */ e(He, {
185
+ children: /* @__PURE__ */ t(Fe, {
179
186
  height: "unset",
180
187
  onClickRow: pe,
181
- selectedCategory: y,
188
+ selectedCategory: E,
182
189
  selectedDateRange: o
183
190
  })
184
191
  })]
185
192
  }),
186
- /* @__PURE__ */ e(xe, {
193
+ /* @__PURE__ */ t(xe, {
187
194
  ariaLabelClose: l.close_category_details,
188
195
  isOpen: !!_,
189
- onClose: ce,
196
+ onClose: me,
190
197
  shouldShowHeaderShadow: !0,
191
198
  title: _?.is_income ? l.category_income : l.category_spending,
192
- children: _ && /* @__PURE__ */ e(Oe, {})
199
+ children: _ && /* @__PURE__ */ t(Ve, {})
193
200
  })
194
201
  ]
195
- }), /* @__PURE__ */ e(Ne, {
196
- onClose: () => N(!1),
202
+ }), /* @__PURE__ */ t(We, {
203
+ onClose: () => B(!1),
197
204
  showConnectWidget: ae,
198
- title: Z.mini_title
205
+ title: ee.mini_title
199
206
  })]
200
207
  });
201
- }, Dt = Ve($e);
208
+ }, bt = Pe(Je);
202
209
  export {
203
- Dt as default
210
+ bt as default
204
211
  };
205
212
 
206
213
  //# sourceMappingURL=TrendsWidget.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TrendsWidget.es.js","names":[],"sources":["../../src/trends/TrendsWidget.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\nimport React from 'react'\nimport { addSeconds } from 'date-fns/addSeconds'\nimport { differenceInCalendarMonths } from 'date-fns/differenceInCalendarMonths'\n\nimport { DateRange } from '@mui/x-date-pickers-pro'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Stack from '@mui/material/Stack'\n\nimport { ArrowBack } from '@mxenabled/mx-icons'\n\nimport {\n ANALYTICS_EVENTS,\n ConnectDrawer,\n Drawer,\n EmptyState,\n Loader,\n WidgetContainer,\n useAccountStore,\n useAppConfig,\n useCategoryStore,\n useEvent,\n useGlobalCopyStore,\n useGlobalStore,\n useGlobalUiStore,\n useInsightsEnabled,\n useScreenSize,\n useTransactionStore,\n useTrendsStore,\n useWidgetLoadTimer,\n} from '../common'\nimport { type Beat, type DetailedCategoryWithTransactions, Transaction, WidgetProps } from '../core'\n\nimport DateRangeText from './components/DateRangeText'\nimport TrendsViewToggle, { type TrendsTabValue } from './components/TrendsViewToggle'\nimport TrendsTableContainer from './components/TrendsTableContainer'\nimport CategoriesList from './components/CategoriesList'\nimport InsightsCarousel from './components/TrendsInsights'\nimport { CategoryDetails } from './components/CategoryDetails'\nimport TrendsChartTotals from './components/TrendsChartTotals'\nimport { TRENDS_ANALYTICS_METADATA } from './constants/TrendsWidget'\nimport { TRENDS_BEAT_TEMPLATES } from './constants/TrendsInsights'\nimport { buildEmptyStateProps } from './utils/TrendsWidget'\n\ninterface TrendsWidgetProps extends WidgetProps {\n onInsightCardClick?: (beat?: Beat) => void\n}\n\nconst TrendsWidget: React.FC<TrendsWidgetProps> = ({\n onBackClick,\n onMenuClick,\n onInsightCardClick,\n sx,\n}) => {\n const { onEvent } = useEvent()\n const { config } = useAppConfig()\n const { isDesktop, isLargeDesktop, isMobile } = useScreenSize()\n const { isAccountDataLoaded, loadAccountData, visibleAccounts: accounts } = useAccountStore()\n const {\n categoriesLoaded,\n loadCategories,\n monthlyCategoryTotals,\n monthlyTotalsLoaded,\n reloadCategoryTotals,\n } = useCategoryStore()\n const { isTransactionDataLoaded, loadTransactionData, setFilter } = useTransactionStore()\n const { selectedCategoryData, selectedDateRange, setSelectedCategoryData, setSelectedDateRange } =\n useTrendsStore()\n const { isInitialized, selectedAccounts, selectedAccountGuids } = useGlobalUiStore()\n const { trends: copy, connect: connectCopy } = useGlobalCopyStore()\n const { beatStore } = useGlobalStore()\n const insightsEnabled = useInsightsEnabled()\n\n // allows clients to disable insights in trends while still enabling insights\n const showInsights = insightsEnabled && config.show_insights_widget_in_master\n const isLargeLayout = isDesktop || isLargeDesktop\n\n const [isWidgetInitialized, setIsWidgetInitialized] = React.useState(false)\n const [isBeatsLoaded, setIsBeatsLoaded] = React.useState(false)\n const [selectedTab, setSelectedTab] = React.useState<TrendsTabValue>('Chart')\n const [selectedCategory, setSelectedCategory] = React.useState('')\n\n // Control responsive chart height based on available widget iframe viewport height\n const [viewportHeight, setViewportHeight] = React.useState(window.innerHeight)\n const insightsPadding = isLargeLayout ? 56 : 0\n const availableChartHeight = viewportHeight - 208 // unavailable height on desktop\n const availableListHeight = viewportHeight - (showInsights ? 550 + insightsPadding : 266)\n\n const [showConnectWidget, setShowConnectWidget] = React.useState(false)\n const hasAggregation = config.show_connections_widget_in_master\n\n useWidgetLoadTimer({\n widgetName: 'TrendsWidget',\n isLoaded: isWidgetInitialized,\n })\n const filteredBeats = React.useMemo(() => {\n if (!isBeatsLoaded) return []\n return beatStore.getFilteredBeats({ templates: TRENDS_BEAT_TEMPLATES })\n }, [isBeatsLoaded])\n\n React.useEffect(() => {\n // Setup resize listener for responsive chart and list heights\n const handleResize = () => setViewportHeight(window.innerHeight)\n window.addEventListener('resize', handleResize)\n\n if (!isAccountDataLoaded) {\n loadAccountData().finally()\n }\n if (!categoriesLoaded) {\n loadCategories().finally()\n }\n if (!isTransactionDataLoaded) {\n loadTransactionData().finally()\n }\n\n return () => window.removeEventListener('resize', handleResize)\n }, [])\n\n // Initialize monthlyCategoryTotals, and reload on account or date range change.\n // Depends on primitives rather than `selectedAccounts` / `selectedDateRange`,\n // whose object identities change even when the values do not. (MEW-3910)\n //\n // Both totals are loaded in parallel rather than chaining monthly inside the\n // date-range `.then()`. Chaining defers the monthly request until the first\n // resolves, so a slow superseded range would claim the newest monthly request\n // token and write stale data over the current range. (MEW-3910)\n React.useEffect(() => {\n if (isInitialized && isAccountDataLoaded) {\n beatStore.loadBeats().finally(() => setIsBeatsLoaded(true))\n reloadCategoryTotals(\n selectedAccounts,\n selectedDateRange.start,\n selectedDateRange.end,\n ).finally(() => {\n setIsWidgetInitialized(true)\n onEvent(ANALYTICS_EVENTS.TRENDS_LOAD_WIDGET, {\n ...TRENDS_ANALYTICS_METADATA,\n time_period: `${differenceInCalendarMonths(selectedDateRange.end, selectedDateRange.start)}M`,\n })\n })\n }\n }, [\n isInitialized,\n isAccountDataLoaded,\n selectedAccountGuids.join(','),\n selectedDateRange.start.getTime(),\n selectedDateRange.end.getTime(),\n ])\n React.useEffect(() => {\n // if monthlyCategoryTotals has data, filter transactions\n if (monthlyCategoryTotals.length !== 0) {\n const transactionFilter = {\n accounts: selectedAccountGuids,\n dateRange: { start: selectedDateRange.start, end: selectedDateRange.end },\n }\n\n // this filter sets sortedTransactions in the transaction store\n setFilter({\n ...transactionFilter,\n custom: (t: Transaction) =>\n Boolean(\n monthlyCategoryTotals.find(\n (cat) =>\n cat.top_level_category_guid === t.top_level_category_guid ||\n cat.category_guid === t.category_guid,\n ),\n ),\n })\n }\n }, [monthlyCategoryTotals])\n\n const handleDetailOpen = (selected: DetailedCategoryWithTransactions) => {\n setSelectedCategoryData(selected)\n }\n\n const handleDetailClose = () => {\n setSelectedCategoryData(null)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_BACK, TRENDS_ANALYTICS_METADATA)\n }\n\n const handleEmptyStateClick = () => {\n setShowConnectWidget(true)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_CONNECT_ACCOUNTS)\n }\n\n const onTabChange = (_event: React.SyntheticEvent, newValue: TrendsTabValue) => {\n setSelectedTab(newValue ?? selectedTab)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_TOGGLE_VIEW, {\n ...TRENDS_ANALYTICS_METADATA,\n toggleView: newValue,\n })\n }\n\n const onRangeChanged = (dateRange: DateRange<Date>) => {\n const startDate = addSeconds(dateRange?.[0] as Date, 1)\n const endDate = dateRange?.[1] as Date\n const months = differenceInCalendarMonths(endDate, startDate)\n\n setSelectedDateRange({ start: startDate, end: endDate })\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_TIME_WINDOW, {\n ...TRENDS_ANALYTICS_METADATA,\n time_period: months + 'M',\n })\n }\n\n const onAccountsFilterClick = (selectedAccountGuids?: string[]) => {\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_FILTER, {\n ...TRENDS_ANALYTICS_METADATA,\n filterValue: selectedAccountGuids,\n })\n }\n\n const onClickRow = (guid: string) => {\n setSelectedCategory(guid)\n }\n\n const onViewAllCategories = () => {\n setSelectedCategory('')\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_ALL_CATEGORIES, TRENDS_ANALYTICS_METADATA)\n }\n\n if (!isInitialized || !isAccountDataLoaded || !isWidgetInitialized || !monthlyTotalsLoaded)\n return <Loader />\n\n const isEmptyTrendsState = accounts.length === 0 || monthlyCategoryTotals.length === 0\n\n const emptyStateProps = buildEmptyStateProps(copy, hasAggregation, handleEmptyStateClick)\n\n return (\n <WidgetContainer\n calendarActions={{ onRangeChanged }}\n dateRange={selectedDateRange}\n dateRangeVariant={isMobile ? 'timeframetabs' : 'timeframebuttons'}\n onAccountsFilterClick={onAccountsFilterClick}\n onBackClick={onBackClick}\n onMenuClick={onMenuClick}\n sx={sx}\n title={copy.title}\n >\n <Stack\n sx={{\n px: isMobile ? 0 : 48,\n }}\n >\n <Stack\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n sx={{ px: isMobile ? 16 : 0, py: 16 }}\n >\n <DateRangeText\n selectedCategoryGuid={selectedCategory}\n selectedDateRange={selectedDateRange}\n />\n <TrendsViewToggle onTabChange={onTabChange} selectedTab={selectedTab} />\n </Stack>\n <Stack alignItems=\"center\" flexDirection=\"row\" sx={{ pb: 8 }} width=\"100%\">\n <Box flexGrow={1}>\n {selectedCategory && (\n <Button onClick={onViewAllCategories} sx={{ p: 0, pr: 5 }}>\n <ArrowBack />\n {copy.all_categories}\n </Button>\n )}\n </Box>\n </Stack>\n\n {isEmptyTrendsState && (\n <EmptyState\n header={emptyStateProps.header}\n icon=\"multiline_chart\"\n onClick={emptyStateProps.onClickHandler}\n primaryButton={emptyStateProps.primaryButton}\n subText={emptyStateProps.description}\n sx={{ height: availableChartHeight, maxWidth: 432 }}\n />\n )}\n {!isEmptyTrendsState && (\n <Stack flexDirection={isLargeLayout ? 'row' : 'column'} gap={isLargeLayout ? 48 : 16}>\n {selectedTab === 'Chart' && (\n <>\n <Box sx={{ width: isLargeLayout ? '68%' : '100%' }}>\n <TrendsChartTotals\n availableHeight={availableChartHeight}\n minHeight={450}\n selectedDateRange={selectedDateRange}\n totals={monthlyCategoryTotals}\n />\n </Box>\n <Stack gap={16} sx={{ width: isLargeLayout ? '32%' : '100%' }}>\n {/* Only show insights enabled and there are beats to show */}\n {showInsights && isBeatsLoaded && filteredBeats.length > 0 && (\n <InsightsCarousel\n beats={filteredBeats}\n onInsightCardClick={onInsightCardClick}\n />\n )}\n <CategoriesList\n availableHeight={availableListHeight}\n onCategoryClick={handleDetailOpen}\n />\n </Stack>\n </>\n )}\n {selectedTab === 'Table' && (\n <Box sx={{ width: '100%' }}>\n <TrendsTableContainer\n height=\"unset\"\n onClickRow={onClickRow}\n selectedCategory={selectedCategory}\n selectedDateRange={selectedDateRange}\n />\n </Box>\n )}\n </Stack>\n )}\n <Drawer\n ariaLabelClose={copy.close_category_details}\n isOpen={Boolean(selectedCategoryData)}\n onClose={handleDetailClose}\n shouldShowHeaderShadow={true}\n title={selectedCategoryData?.is_income ? copy.category_income : copy.category_spending}\n >\n {selectedCategoryData && <CategoryDetails />}\n </Drawer>\n </Stack>\n\n <ConnectDrawer\n onClose={() => setShowConnectWidget(false)}\n showConnectWidget={showConnectWidget}\n title={connectCopy.mini_title}\n />\n </WidgetContainer>\n )\n}\n\nexport default observer(TrendsWidget)\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrendsWidget.es.js","names":[],"sources":["../../src/trends/TrendsWidget.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\nimport React from 'react'\nimport { addSeconds } from 'date-fns/addSeconds'\nimport { differenceInCalendarMonths } from 'date-fns/differenceInCalendarMonths'\n\nimport { DateRange } from '@mui/x-date-pickers-pro'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Stack from '@mui/material/Stack'\n\nimport { ArrowBack } from '@mxenabled/mx-icons'\n\nimport {\n ANALYTICS_EVENTS,\n ConnectDrawer,\n Drawer,\n EmptyState,\n Loader,\n WidgetContainer,\n useAccountStore,\n useAppConfig,\n useCategoryStore,\n useEvent,\n useGlobalCopyStore,\n useGlobalStore,\n useGlobalUiStore,\n useInsightsEnabled,\n useScreenSize,\n useTransactionStore,\n useTrendsStore,\n useWidgetLoadTimer,\n} from '../common'\nimport { type Beat, type DetailedCategoryWithTransactions, Transaction, WidgetProps } from '../core'\n\nimport DateRangeText from './components/DateRangeText'\nimport TrendsViewToggle, { type TrendsTabValue } from './components/TrendsViewToggle'\nimport TrendsTableContainer from './components/TrendsTableContainer'\nimport CategoriesList from './components/CategoriesList'\nimport InsightsCarousel from './components/TrendsInsights'\nimport { CategoryDetails } from './components/CategoryDetails'\nimport TrendsChartTotals from './components/TrendsChartTotals'\nimport { TRENDS_ANALYTICS_METADATA } from './constants/TrendsWidget'\nimport { TRENDS_BEAT_TEMPLATES } from './constants/TrendsInsights'\nimport { buildEmptyStateProps } from './utils/TrendsWidget'\n\ninterface TrendsWidgetProps extends WidgetProps {\n onInsightCardClick?: (beat?: Beat) => void\n}\n\nconst TrendsWidget: React.FC<TrendsWidgetProps> = ({\n onBackClick,\n onMenuClick,\n onInsightCardClick,\n sx,\n}) => {\n const { onEvent } = useEvent()\n const { config } = useAppConfig()\n const { isDesktop, isLargeDesktop, isMobile } = useScreenSize()\n const { isAccountDataLoaded, loadAccountData, visibleAccounts: accounts } = useAccountStore()\n const {\n categoriesLoaded,\n loadCategories,\n monthlyCategoryTotals,\n monthlyTotalsLoaded,\n reloadCategoryTotals,\n } = useCategoryStore()\n const { isTransactionDataLoaded, loadTransactionData, setFilter } = useTransactionStore()\n const { selectedCategoryData, selectedDateRange, setSelectedCategoryData, setSelectedDateRange } =\n useTrendsStore()\n const { isInitialized, selectedAccounts, selectedAccountGuids } = useGlobalUiStore()\n const { trends: copy, connect: connectCopy } = useGlobalCopyStore()\n const { beatStore } = useGlobalStore()\n const insightsEnabled = useInsightsEnabled()\n\n // allows clients to disable insights in trends while still enabling insights\n const showInsights = insightsEnabled && config.show_insights_widget_in_master\n const isLargeLayout = isDesktop || isLargeDesktop\n\n const [isWidgetInitialized, setIsWidgetInitialized] = React.useState(false)\n const [isBeatsLoaded, setIsBeatsLoaded] = React.useState(false)\n const [selectedTab, setSelectedTab] = React.useState<TrendsTabValue>('Chart')\n const [selectedCategory, setSelectedCategory] = React.useState('')\n\n // Control responsive chart height based on available widget iframe viewport height\n const [viewportHeight, setViewportHeight] = React.useState(window.innerHeight)\n const insightsPadding = isLargeLayout ? 56 : 0\n const availableChartHeight = viewportHeight - 208 // unavailable height on desktop\n const availableListHeight = viewportHeight - (showInsights ? 550 + insightsPadding : 266)\n\n const [showConnectWidget, setShowConnectWidget] = React.useState(false)\n const hasAggregation = config.show_connections_widget_in_master\n\n // iOS Safari doesn't scroll off-screen elements into view on forward Tab\n // inside this non-document scroll container. `:focus-visible` limits the\n // scroll to keyboard focus so pointer clicks on clipped rows don't jump.\n // `block: 'nearest'` is a no-op when already visible. Callback ref (not\n // useEffect) because the content mounts after the loading gate.\n const cleanupFocusScroll = React.useRef<(() => void) | undefined>(undefined)\n const contentRef = React.useCallback((node: HTMLDivElement | null) => {\n cleanupFocusScroll.current?.()\n cleanupFocusScroll.current = undefined\n\n if (!node) return\n\n const handleFocusIn = (event: FocusEvent) => {\n if (event.target instanceof HTMLElement && event.target.matches(':focus-visible')) {\n event.target.scrollIntoView({ block: 'nearest' })\n }\n }\n\n node.addEventListener('focusin', handleFocusIn)\n cleanupFocusScroll.current = () => node.removeEventListener('focusin', handleFocusIn)\n }, [])\n\n useWidgetLoadTimer({\n widgetName: 'TrendsWidget',\n isLoaded: isWidgetInitialized,\n })\n const filteredBeats = React.useMemo(() => {\n if (!isBeatsLoaded) return []\n return beatStore.getFilteredBeats({ templates: TRENDS_BEAT_TEMPLATES })\n }, [isBeatsLoaded])\n\n React.useEffect(() => {\n // Setup resize listener for responsive chart and list heights\n const handleResize = () => setViewportHeight(window.innerHeight)\n window.addEventListener('resize', handleResize)\n\n if (!isAccountDataLoaded) {\n loadAccountData().finally()\n }\n if (!categoriesLoaded) {\n loadCategories().finally()\n }\n if (!isTransactionDataLoaded) {\n loadTransactionData().finally()\n }\n\n return () => window.removeEventListener('resize', handleResize)\n }, [])\n\n // Initialize monthlyCategoryTotals, and reload on account or date range change.\n // Depends on primitives rather than `selectedAccounts` / `selectedDateRange`,\n // whose object identities change even when the values do not. (MEW-3910)\n //\n // Both totals are loaded in parallel rather than chaining monthly inside the\n // date-range `.then()`. Chaining defers the monthly request until the first\n // resolves, so a slow superseded range would claim the newest monthly request\n // token and write stale data over the current range. (MEW-3910)\n React.useEffect(() => {\n if (isInitialized && isAccountDataLoaded) {\n beatStore.loadBeats().finally(() => setIsBeatsLoaded(true))\n reloadCategoryTotals(\n selectedAccounts,\n selectedDateRange.start,\n selectedDateRange.end,\n ).finally(() => {\n setIsWidgetInitialized(true)\n onEvent(ANALYTICS_EVENTS.TRENDS_LOAD_WIDGET, {\n ...TRENDS_ANALYTICS_METADATA,\n time_period: `${differenceInCalendarMonths(selectedDateRange.end, selectedDateRange.start)}M`,\n })\n })\n }\n }, [\n isInitialized,\n isAccountDataLoaded,\n selectedAccountGuids.join(','),\n selectedDateRange.start.getTime(),\n selectedDateRange.end.getTime(),\n ])\n React.useEffect(() => {\n // if monthlyCategoryTotals has data, filter transactions\n if (monthlyCategoryTotals.length !== 0) {\n const transactionFilter = {\n accounts: selectedAccountGuids,\n dateRange: { start: selectedDateRange.start, end: selectedDateRange.end },\n }\n\n // this filter sets sortedTransactions in the transaction store\n setFilter({\n ...transactionFilter,\n custom: (t: Transaction) =>\n Boolean(\n monthlyCategoryTotals.find(\n (cat) =>\n cat.top_level_category_guid === t.top_level_category_guid ||\n cat.category_guid === t.category_guid,\n ),\n ),\n })\n }\n }, [monthlyCategoryTotals])\n\n const handleDetailOpen = (selected: DetailedCategoryWithTransactions) => {\n setSelectedCategoryData(selected)\n }\n\n const handleDetailClose = () => {\n setSelectedCategoryData(null)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_BACK, TRENDS_ANALYTICS_METADATA)\n }\n\n const handleEmptyStateClick = () => {\n setShowConnectWidget(true)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_CONNECT_ACCOUNTS)\n }\n\n const onTabChange = (_event: React.SyntheticEvent, newValue: TrendsTabValue) => {\n setSelectedTab(newValue ?? selectedTab)\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_TOGGLE_VIEW, {\n ...TRENDS_ANALYTICS_METADATA,\n toggleView: newValue,\n })\n }\n\n const onRangeChanged = (dateRange: DateRange<Date>) => {\n const startDate = addSeconds(dateRange?.[0] as Date, 1)\n const endDate = dateRange?.[1] as Date\n const months = differenceInCalendarMonths(endDate, startDate)\n\n setSelectedDateRange({ start: startDate, end: endDate })\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_TIME_WINDOW, {\n ...TRENDS_ANALYTICS_METADATA,\n time_period: months + 'M',\n })\n }\n\n const onAccountsFilterClick = (selectedAccountGuids?: string[]) => {\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_FILTER, {\n ...TRENDS_ANALYTICS_METADATA,\n filterValue: selectedAccountGuids,\n })\n }\n\n const onClickRow = (guid: string) => {\n setSelectedCategory(guid)\n }\n\n const onViewAllCategories = () => {\n setSelectedCategory('')\n onEvent(ANALYTICS_EVENTS.TRENDS_CLICK_ALL_CATEGORIES, TRENDS_ANALYTICS_METADATA)\n }\n\n if (!isInitialized || !isAccountDataLoaded || !isWidgetInitialized || !monthlyTotalsLoaded)\n return <Loader />\n\n const isEmptyTrendsState = accounts.length === 0 || monthlyCategoryTotals.length === 0\n\n const emptyStateProps = buildEmptyStateProps(copy, hasAggregation, handleEmptyStateClick)\n\n return (\n <WidgetContainer\n calendarActions={{ onRangeChanged }}\n dateRange={selectedDateRange}\n dateRangeVariant={isMobile ? 'timeframetabs' : 'timeframebuttons'}\n onAccountsFilterClick={onAccountsFilterClick}\n onBackClick={onBackClick}\n onMenuClick={onMenuClick}\n sx={sx}\n title={copy.title}\n >\n <Stack\n ref={contentRef}\n sx={{\n px: isMobile ? 0 : 48,\n }}\n >\n <Stack\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n sx={{ px: isMobile ? 16 : 0, py: 16 }}\n >\n <DateRangeText\n selectedCategoryGuid={selectedCategory}\n selectedDateRange={selectedDateRange}\n />\n <TrendsViewToggle onTabChange={onTabChange} selectedTab={selectedTab} />\n </Stack>\n <Stack alignItems=\"center\" flexDirection=\"row\" sx={{ pb: 8 }} width=\"100%\">\n <Box flexGrow={1}>\n {selectedCategory && (\n <Button onClick={onViewAllCategories} sx={{ p: 0, pr: 5 }}>\n <ArrowBack />\n {copy.all_categories}\n </Button>\n )}\n </Box>\n </Stack>\n\n {isEmptyTrendsState && (\n <EmptyState\n header={emptyStateProps.header}\n icon=\"multiline_chart\"\n onClick={emptyStateProps.onClickHandler}\n primaryButton={emptyStateProps.primaryButton}\n subText={emptyStateProps.description}\n sx={{ height: availableChartHeight, maxWidth: 432 }}\n />\n )}\n {!isEmptyTrendsState && (\n <Stack flexDirection={isLargeLayout ? 'row' : 'column'} gap={isLargeLayout ? 48 : 16}>\n {selectedTab === 'Chart' && (\n <>\n <Box sx={{ width: isLargeLayout ? '68%' : '100%' }}>\n <TrendsChartTotals\n availableHeight={availableChartHeight}\n minHeight={450}\n selectedDateRange={selectedDateRange}\n totals={monthlyCategoryTotals}\n />\n </Box>\n <Stack gap={16} sx={{ width: isLargeLayout ? '32%' : '100%' }}>\n {/* Only show insights enabled and there are beats to show */}\n {showInsights && isBeatsLoaded && filteredBeats.length > 0 && (\n <InsightsCarousel\n beats={filteredBeats}\n onInsightCardClick={onInsightCardClick}\n />\n )}\n <CategoriesList\n availableHeight={availableListHeight}\n onCategoryClick={handleDetailOpen}\n />\n </Stack>\n </>\n )}\n {selectedTab === 'Table' && (\n <Box sx={{ width: '100%' }}>\n <TrendsTableContainer\n height=\"unset\"\n onClickRow={onClickRow}\n selectedCategory={selectedCategory}\n selectedDateRange={selectedDateRange}\n />\n </Box>\n )}\n </Stack>\n )}\n <Drawer\n ariaLabelClose={copy.close_category_details}\n isOpen={Boolean(selectedCategoryData)}\n onClose={handleDetailClose}\n shouldShowHeaderShadow={true}\n title={selectedCategoryData?.is_income ? copy.category_income : copy.category_spending}\n >\n {selectedCategoryData && <CategoryDetails />}\n </Drawer>\n </Stack>\n\n <ConnectDrawer\n onClose={() => setShowConnectWidget(false)}\n showConnectWidget={showConnectWidget}\n title={connectCopy.mini_title}\n />\n </WidgetContainer>\n )\n}\n\nexport default observer(TrendsWidget)\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.4.34",
3
+ "version": "9.4.35",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",