@agg-build/ui 1.2.11 → 1.3.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 (99) hide show
  1. package/README.md +34 -1
  2. package/dist/{chunk-NWJHFGBZ.mjs → chunk-55ODXLOS.mjs} +1 -1
  3. package/dist/{chunk-3ZSNHGAB.mjs → chunk-5ALBEKAT.mjs} +842 -300
  4. package/dist/{chunk-YP75TIY6.mjs → chunk-6PQ6O6M5.mjs} +583 -186
  5. package/dist/{chunk-ENAGASVU.mjs → chunk-ONVP7YWS.mjs} +439 -339
  6. package/dist/{chunk-SJLHOAKK.mjs → chunk-QUZWA34R.mjs} +1256 -844
  7. package/dist/{chunk-54PCEK6G.mjs → chunk-UFC7L74C.mjs} +26 -27
  8. package/dist/{chunk-J7K2U44E.mjs → chunk-YWJIYEJV.mjs} +236 -121
  9. package/dist/events.js +2460 -1355
  10. package/dist/events.mjs +5 -3
  11. package/dist/index.js +5741 -3841
  12. package/dist/index.mjs +485 -106
  13. package/dist/modals.js +1399 -1093
  14. package/dist/modals.mjs +3 -3
  15. package/dist/pages.js +4039 -2687
  16. package/dist/pages.mjs +6 -6
  17. package/dist/primitives.js +1267 -895
  18. package/dist/primitives.mjs +3 -1
  19. package/dist/styles.css +1 -1
  20. package/dist/tailwind.css +1 -1
  21. package/dist/trading.js +1032 -695
  22. package/dist/trading.mjs +4 -4
  23. package/dist/types/agg-provider.d.mts +27 -0
  24. package/dist/types/agg-provider.d.ts +27 -0
  25. package/dist/types/events/item/event-list-item-v2.utils.d.mts +28 -0
  26. package/dist/types/events/item/event-list-item-v2.utils.d.ts +28 -0
  27. package/dist/types/events/item/event-list-item.constants.d.mts +1 -1
  28. package/dist/types/events/item/event-list-item.constants.d.ts +1 -1
  29. package/dist/types/events/item/index.d.mts +4 -0
  30. package/dist/types/events/item/index.d.ts +4 -0
  31. package/dist/types/events/list/event-list-tabs.d.mts +6 -1
  32. package/dist/types/events/list/event-list-tabs.d.ts +6 -1
  33. package/dist/types/events/list/event-list.types.d.mts +2 -0
  34. package/dist/types/events/list/event-list.types.d.ts +2 -0
  35. package/dist/types/events/list/event-list.utils.d.mts +19 -0
  36. package/dist/types/events/list/event-list.utils.d.ts +19 -0
  37. package/dist/types/events/list/index.d.mts +1 -1
  38. package/dist/types/events/list/index.d.ts +1 -1
  39. package/dist/types/index.d.mts +2 -0
  40. package/dist/types/index.d.ts +2 -0
  41. package/dist/types/notifications/agg-notification-events-provider.d.mts +35 -0
  42. package/dist/types/notifications/agg-notification-events-provider.d.ts +35 -0
  43. package/dist/types/notifications/agg-toast-provider.d.mts +27 -0
  44. package/dist/types/notifications/agg-toast-provider.d.ts +27 -0
  45. package/dist/types/notifications/deposit-notification-events.d.mts +10 -0
  46. package/dist/types/notifications/deposit-notification-events.d.ts +10 -0
  47. package/dist/types/notifications/index.d.mts +2 -0
  48. package/dist/types/notifications/index.d.ts +2 -0
  49. package/dist/types/pages/home/home.types.d.mts +1 -0
  50. package/dist/types/pages/home/home.types.d.ts +1 -0
  51. package/dist/types/pages/user-profile/user-profile.types.d.mts +30 -4
  52. package/dist/types/pages/user-profile/user-profile.types.d.ts +30 -4
  53. package/dist/types/primitives/button/button.types.d.mts +4 -0
  54. package/dist/types/primitives/button/button.types.d.ts +4 -0
  55. package/dist/types/primitives/button/index.d.mts +1 -1
  56. package/dist/types/primitives/button/index.d.ts +1 -1
  57. package/dist/types/primitives/icon/index.d.mts +2 -1
  58. package/dist/types/primitives/icon/index.d.ts +2 -1
  59. package/dist/types/primitives/icon/registry.d.mts +20 -0
  60. package/dist/types/primitives/icon/registry.d.ts +20 -0
  61. package/dist/types/primitives/icon/svg/gift-bonus.d.mts +5 -0
  62. package/dist/types/primitives/icon/svg/gift-bonus.d.ts +5 -0
  63. package/dist/types/primitives/icon/svg/sort-end-date.d.mts +5 -0
  64. package/dist/types/primitives/icon/svg/sort-end-date.d.ts +5 -0
  65. package/dist/types/primitives/icon/svg/sort-top-arbitrage.d.mts +5 -0
  66. package/dist/types/primitives/icon/svg/sort-top-arbitrage.d.ts +5 -0
  67. package/dist/types/primitives/icon/svg/sort-volume-24hr.d.mts +5 -0
  68. package/dist/types/primitives/icon/svg/sort-volume-24hr.d.ts +5 -0
  69. package/dist/types/primitives/icon/svg/sort-volume.d.mts +5 -0
  70. package/dist/types/primitives/icon/svg/sort-volume.d.ts +5 -0
  71. package/dist/types/primitives/search/search.types.d.mts +2 -1
  72. package/dist/types/primitives/search/search.types.d.ts +2 -1
  73. package/dist/types/primitives/select/index.d.mts +1 -1
  74. package/dist/types/primitives/select/index.d.ts +1 -1
  75. package/dist/types/primitives/select/select.types.d.mts +9 -0
  76. package/dist/types/primitives/select/select.types.d.ts +9 -0
  77. package/dist/types/primitives/skeleton/index.d.mts +1 -1
  78. package/dist/types/primitives/skeleton/index.d.ts +1 -1
  79. package/dist/types/primitives/skeleton/skeleton.types.d.mts +4 -0
  80. package/dist/types/primitives/skeleton/skeleton.types.d.ts +4 -0
  81. package/dist/types/primitives/skeleton/views/event-list-skeleton-view.d.mts +1 -1
  82. package/dist/types/primitives/skeleton/views/event-list-skeleton-view.d.ts +1 -1
  83. package/dist/types/primitives/toast/toast.types.d.mts +3 -0
  84. package/dist/types/primitives/toast/toast.types.d.ts +3 -0
  85. package/dist/types/profile/index.d.mts +1 -1
  86. package/dist/types/profile/index.d.ts +1 -1
  87. package/dist/types/profile/profile-modal.constants.d.mts +2 -2
  88. package/dist/types/profile/profile-modal.constants.d.ts +2 -2
  89. package/dist/types/profile/tabs/accounts-wallets-tab.d.mts +2 -17
  90. package/dist/types/profile/tabs/accounts-wallets-tab.d.ts +2 -17
  91. package/dist/types/profile/tabs/trading-access-tab.d.mts +15 -0
  92. package/dist/types/profile/tabs/trading-access-tab.d.ts +15 -0
  93. package/dist/types/trading/place-order/index.d.mts +1 -1
  94. package/dist/types/trading/place-order/index.d.ts +1 -1
  95. package/dist/types/trading/place-order/index.place-order.types.d.mts +4 -1
  96. package/dist/types/trading/place-order/index.place-order.types.d.ts +4 -1
  97. package/dist/types/trading/place-order/index.place-order.utils.d.mts +3 -1
  98. package/dist/types/trading/place-order/index.place-order.utils.d.ts +3 -1
  99. package/package.json +3 -3
@@ -20,10 +20,9 @@ import {
20
20
  resolveTradingStateBadgeClassName,
21
21
  resolveTradingStatePresentation,
22
22
  resolveUnifiedOrderBookEntries,
23
- selectOutcomePrice,
24
23
  sortOutcomeSelectorOutcomes,
25
24
  useEventTradingContext
26
- } from "./chunk-54PCEK6G.mjs";
25
+ } from "./chunk-UFC7L74C.mjs";
27
26
  import {
28
27
  AutocompleteSelect,
29
28
  Badge,
@@ -33,8 +32,10 @@ import {
33
32
  LineChart,
34
33
  MOBILE_TABS_MEDIA_QUERY,
35
34
  MarketDetailsOderbookSkeleton,
35
+ Modal,
36
36
  RemoteImage,
37
37
  SearchEmptyIcon,
38
+ Select,
38
39
  Skeleton,
39
40
  StateMessage,
40
41
  SwitchButton,
@@ -46,12 +47,10 @@ import {
46
47
  __objRest,
47
48
  __spreadProps,
48
49
  __spreadValues,
49
- baseCardClassName,
50
50
  cn,
51
51
  dedupeVenueMarketsById,
52
52
  detailsBaseCardClassName,
53
53
  filterEventsByTabValue,
54
- formatCountLabel,
55
54
  formatMarketProbabilityPercent,
56
55
  formatPriceGapPercent,
57
56
  getMarketDetailsTabs,
@@ -62,22 +61,24 @@ import {
62
61
  normalizeProbability,
63
62
  normalizeVenueMarketCluster,
64
63
  orderBookRowLimitDefault,
64
+ resolveCategoryIdsFromPath,
65
65
  resolveDisplayVolume,
66
66
  resolveEventListItemEvent,
67
67
  resolveOutcomeTitle,
68
68
  resolveTabVenus,
69
+ resolveVenueLabel,
69
70
  resolveYesOutcome,
71
+ sortCategoriesForNavigation,
72
+ sortMarketsByVolumeDesc,
70
73
  sortMarketsByYesOddsDesc,
71
74
  sortOutcomes,
72
75
  splitEventsByLifecycle
73
- } from "./chunk-SJLHOAKK.mjs";
76
+ } from "./chunk-QUZWA34R.mjs";
74
77
 
75
78
  // src/events/item/index.tsx
76
79
  import {
77
- computePriceGaps,
78
80
  optimizedImageUrl,
79
81
  sortVenues,
80
- useEventTradingContext as useEventTradingContext2,
81
82
  useLabels,
82
83
  useMidpoints,
83
84
  useSdkUiConfig,
@@ -97,34 +98,155 @@ var isErrorWithStatus = (error, status) => {
97
98
  return getErrorStatus(error) === status;
98
99
  };
99
100
 
100
- // src/events/shared/display-outcome-venue.ts
101
- var normalizeOutcomeLabel = (label) => {
102
- return typeof label === "string" ? label.trim().toLowerCase() : "";
101
+ // src/events/item/event-list-item-v2.utils.ts
102
+ var isNonEmptyString = (value) => {
103
+ return typeof value === "string" && value.trim().length > 0;
103
104
  };
104
- var getDisplayOutcomeVenue = ({
105
- outcomeId,
106
- outcomeLabel,
107
- selection,
108
- bestMidpointVenue,
109
- bestVenueByOutcomeId,
110
- bestPriceVenuesByOutcomeId,
111
- fallbackVenue
112
- }) => {
105
+ var getOutcomeSortPrice = (outcome) => {
113
106
  var _a;
114
- if (selection && outcomeId) {
115
- const sideVenues = bestPriceVenuesByOutcomeId == null ? void 0 : bestPriceVenuesByOutcomeId.get(outcomeId);
116
- if (selection === "buy" && (sideVenues == null ? void 0 : sideVenues.bestAskVenue)) return sideVenues.bestAskVenue;
117
- if (selection === "sell" && (sideVenues == null ? void 0 : sideVenues.bestBidVenue)) return sideVenues.bestBidVenue;
107
+ return (_a = normalizeProbability(outcome.price)) != null ? _a : -1;
108
+ };
109
+ var MAX_VISIBLE_OUTCOME_PRICE_PILLS = 3;
110
+ var resolveDisplayOutcome = (market) => {
111
+ var _a;
112
+ return (_a = resolveYesOutcome(market)) != null ? _a : [...market.venueMarketOutcomes].sort((left, right) => {
113
+ return getOutcomeSortPrice(right) - getOutcomeSortPrice(left);
114
+ })[0];
115
+ };
116
+ var resolveOutcomeRows = ({
117
+ venueMarkets,
118
+ marketCount
119
+ }) => {
120
+ if (venueMarkets.length === 0) return [];
121
+ if (marketCount === 1 && venueMarkets.length === 1) {
122
+ const [market] = venueMarkets;
123
+ return sortOutcomes(market.venueMarketOutcomes).map((outcome) => ({
124
+ id: outcome.id,
125
+ label: resolveOutcomeTitle(outcome),
126
+ market,
127
+ outcome
128
+ }));
118
129
  }
119
- if (normalizeOutcomeLabel(outcomeLabel) === "yes" && bestMidpointVenue) {
120
- return bestMidpointVenue;
130
+ return sortMarketsByYesOddsDesc(venueMarkets).map((market) => {
131
+ const outcome = resolveDisplayOutcome(market);
132
+ if (!outcome) return void 0;
133
+ return {
134
+ id: market.id,
135
+ label: isNonEmptyString(market.question) ? market.question : resolveOutcomeTitle(outcome),
136
+ market,
137
+ outcome
138
+ };
139
+ }).filter((row) => row != null);
140
+ };
141
+ var collectOutcomePrices = (market, outcome, priceByOutcomeId) => {
142
+ var _a, _b, _c;
143
+ const prices = [];
144
+ const seenOutcomeKeys = /* @__PURE__ */ new Set();
145
+ const addPrice = (priceMarket, priceOutcome) => {
146
+ var _a2;
147
+ if (!priceMarket || !priceOutcome) return;
148
+ const resolvedPrice = (_a2 = priceByOutcomeId == null ? void 0 : priceByOutcomeId.get(priceOutcome.id)) != null ? _a2 : priceOutcome.price;
149
+ const normalizedPrice = normalizeProbability(resolvedPrice);
150
+ if (normalizedPrice == null) return;
151
+ const key = `${priceMarket.id}:${priceOutcome.id}`;
152
+ if (seenOutcomeKeys.has(key)) return;
153
+ seenOutcomeKeys.add(key);
154
+ prices.push({
155
+ venue: priceMarket.venue,
156
+ market: priceMarket,
157
+ outcome: priceOutcome,
158
+ price: normalizedPrice,
159
+ isBest: false
160
+ });
161
+ };
162
+ addPrice(market, outcome);
163
+ for (const matchedOutcomeRef of (_a = outcome.matchedVenueMarketOutcomes) != null ? _a : []) {
164
+ const matchedMarket = (_b = market.matchedVenueMarkets) == null ? void 0 : _b.find((candidate) => {
165
+ return candidate.id === matchedOutcomeRef.venueMarketId;
166
+ });
167
+ const matchedOutcome = (_c = matchedMarket == null ? void 0 : matchedMarket.venueMarketOutcomes) == null ? void 0 : _c.find((candidate) => {
168
+ return candidate.id === matchedOutcomeRef.venueMarketOutcomeId;
169
+ });
170
+ addPrice(matchedMarket, matchedOutcome);
121
171
  }
122
- const bestVenue = outcomeId ? bestVenueByOutcomeId == null ? void 0 : bestVenueByOutcomeId.get(outcomeId) : void 0;
123
- return (_a = bestVenue != null ? bestVenue : fallbackVenue) != null ? _a : void 0;
172
+ if (prices.length === 0) return [];
173
+ const bestPriceByVenue = /* @__PURE__ */ new Map();
174
+ for (const price of prices) {
175
+ const existing = bestPriceByVenue.get(price.venue);
176
+ if (!existing || price.price < existing.price) {
177
+ bestPriceByVenue.set(price.venue, price);
178
+ }
179
+ }
180
+ const dedupedPrices = Array.from(bestPriceByVenue.values());
181
+ const sortedPricesByBestOdds = [...dedupedPrices].sort((left, right) => {
182
+ return left.price - right.price;
183
+ });
184
+ const bestPrice = sortedPricesByBestOdds[0];
185
+ if (!bestPrice) return [];
186
+ const visiblePrices = [
187
+ sortedPricesByBestOdds.at(-1),
188
+ sortedPricesByBestOdds[1],
189
+ bestPrice
190
+ ].reduce((accumulator, price) => {
191
+ if (!price) return accumulator;
192
+ const isDuplicate = accumulator.some((candidate) => {
193
+ return candidate.market.id === price.market.id && candidate.outcome.id === price.outcome.id;
194
+ });
195
+ if (isDuplicate) return accumulator;
196
+ accumulator.push(price);
197
+ return accumulator;
198
+ }, []);
199
+ return visiblePrices.slice(0, MAX_VISIBLE_OUTCOME_PRICE_PILLS).map((price) => __spreadProps(__spreadValues({}, price), {
200
+ isBest: price.market.id === bestPrice.market.id && price.outcome.id === bestPrice.outcome.id
201
+ }));
202
+ };
203
+ var collectMatchedVenues = (rows) => {
204
+ const venues = /* @__PURE__ */ new Set();
205
+ for (const row of rows) {
206
+ if (row.prices.length < 2) continue;
207
+ for (const price of row.prices) {
208
+ venues.add(price.venue);
209
+ }
210
+ }
211
+ return Array.from(venues);
212
+ };
213
+ var computePriceGapsFromComparisonRows = (rows) => {
214
+ const gapsByVenueMarketId = /* @__PURE__ */ new Map();
215
+ for (const row of rows) {
216
+ if (row.prices.length < 2) continue;
217
+ const prices = row.prices.map((price) => price.price);
218
+ const minPrice = Math.min(...prices);
219
+ const maxPrice = Math.max(...prices);
220
+ if (minPrice <= 0 || maxPrice <= minPrice) continue;
221
+ gapsByVenueMarketId.set(row.market.id, (maxPrice - minPrice) * 100);
222
+ }
223
+ return gapsByVenueMarketId;
224
+ };
225
+ var normalizeEventListItemV2Comparison = ({
226
+ venueMarkets,
227
+ marketCount,
228
+ priceByOutcomeId
229
+ }) => {
230
+ const outcomeRows = resolveOutcomeRows({ venueMarkets, marketCount });
231
+ const rows = outcomeRows.map((row) => {
232
+ const prices = collectOutcomePrices(row.market, row.outcome, priceByOutcomeId);
233
+ return __spreadProps(__spreadValues({}, row), {
234
+ prices
235
+ });
236
+ });
237
+ const matchedVenues = collectMatchedVenues(rows);
238
+ return {
239
+ rows,
240
+ matchedVenues,
241
+ hasVenueCompetition: matchedVenues.length > 1
242
+ };
124
243
  };
125
244
 
245
+ // src/events/item/event-list-item.constants.ts
246
+ var baseCardClassName = "gap-3 overflow-hidden p-4 w-full";
247
+
126
248
  // src/events/item/index.tsx
127
- import { jsx, jsxs } from "react/jsx-runtime";
249
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
128
250
  var EventListItemLoadingState = ({
129
251
  classNames,
130
252
  ariaLabel
@@ -186,6 +308,25 @@ var EventListItemNotFoundState = ({
186
308
  var OUTCOMES_SCROLL_LOAD_THRESHOLD_PX = 16;
187
309
  var MARKET_ID_QUERY_PARAM = "marketId";
188
310
  var OUTCOME_ID_QUERY_PARAM = "outcomeId";
311
+ var buildOutcomeMidpointsByOutcomeId = (midpointRows) => {
312
+ const outcomeMidpointsByOutcomeId = /* @__PURE__ */ new Map();
313
+ const addOutcomeMidpoints = (value) => {
314
+ var _a, _b;
315
+ if (!value || typeof value !== "object") return;
316
+ const row = value;
317
+ for (const outcome of (_a = row.outcomes) != null ? _a : []) {
318
+ if (outcome.midpoint == null) continue;
319
+ outcomeMidpointsByOutcomeId.set(outcome.venueMarketOutcomeId, outcome.midpoint);
320
+ }
321
+ for (const matchedRow of (_b = row.matched) != null ? _b : []) {
322
+ addOutcomeMidpoints(matchedRow);
323
+ }
324
+ };
325
+ for (const row of midpointRows) {
326
+ addOutcomeMidpoints(row);
327
+ }
328
+ return outcomeMidpointsByOutcomeId;
329
+ };
189
330
  var resolveMarketHrefFromEventHref = (eventHref, marketId, outcomeId) => {
190
331
  if (!eventHref) return void 0;
191
332
  if (typeof window === "undefined") return void 0;
@@ -212,8 +353,6 @@ var EventListItemContent = ({
212
353
  var _a;
213
354
  const config = useSdkUiConfig();
214
355
  const labels = useLabels();
215
- const tradingContext = useEventTradingContext2();
216
- const tradeSide = (_a = tradingContext == null ? void 0 : tradingContext.tradeSide) != null ? _a : "buy";
217
356
  const allVenueMarkets = useMemo(() => event.venueMarkets, [event.venueMarkets]);
218
357
  const resolvedTitle = event.title;
219
358
  const resolvedImage = event.image;
@@ -260,32 +399,42 @@ var EventListItemContent = ({
260
399
  }
261
400
  return [...new Set(ids)];
262
401
  }, [resolvedOutcomeMarkets]);
263
- const {
264
- midpointsByVenueMarketId,
265
- isLoading: isLoadingMidpoints,
266
- isFetching: isFetchingMidpoints
267
- } = useVenueMarketMidpoints({
402
+ const { midpointRows, isLoading: isVenueMarketMidpointsLoading } = useVenueMarketMidpoints({
268
403
  venueMarketIds: midpointVenueMarketIds,
269
404
  enabled: midpointVenueMarketIds.length > 0
270
405
  });
271
- const {
272
- prices: displayMidpointsByOutcomeId,
273
- venueByOutcomeId: displayMidpointVenueByOutcomeId,
274
- bestPrices: displayBestPricesByOutcomeId,
275
- bestPriceVenuesByOutcomeId: displayBestPriceVenuesByOutcomeId,
276
- isLoading: isDisplayMidpointsLoading
277
- } = useMidpoints(resolvedOutcomeMarkets);
278
- const gapsByVenueMarketId = useMemo(
279
- () => computePriceGaps({
280
- markets: resolvedOutcomeMarkets,
281
- midpointsByVenueMarketId
282
- }),
283
- [resolvedOutcomeMarkets, midpointsByVenueMarketId]
284
- );
285
- const isMidpointQueryInFlight = isLoadingMidpoints || isFetchingMidpoints || isDisplayMidpointsLoading;
406
+ const { prices: displayMidpointsByOutcomeId, isLoading: isOutcomeMidpointsLoading } = useMidpoints(resolvedOutcomeMarkets);
407
+ const isAwaitingMidpoints = isOutcomeMidpointsLoading || midpointVenueMarketIds.length > 0 && isVenueMarketMidpointsLoading;
408
+ const displayPriceByOutcomeId = useMemo(() => {
409
+ const priceByOutcomeId = new Map(displayMidpointsByOutcomeId);
410
+ for (const [outcomeId, price] of buildOutcomeMidpointsByOutcomeId(midpointRows)) {
411
+ priceByOutcomeId.set(outcomeId, price);
412
+ }
413
+ return priceByOutcomeId;
414
+ }, [displayMidpointsByOutcomeId, midpointRows]);
415
+ const comparison = useMemo(() => {
416
+ return normalizeEventListItemV2Comparison({
417
+ venueMarkets: resolvedOutcomeMarkets,
418
+ marketCount: resolvedMarketCount,
419
+ priceByOutcomeId: displayPriceByOutcomeId
420
+ });
421
+ }, [displayPriceByOutcomeId, resolvedMarketCount, resolvedOutcomeMarkets]);
422
+ const gapsByVenueMarketId = useMemo(() => {
423
+ return computePriceGapsFromComparisonRows(comparison.rows);
424
+ }, [comparison.rows]);
286
425
  const shouldRenderLoadingOutcomeRow = shouldEnableLazyMarketLoading && (!isLazyMarketsQueryEnabled || isLoadingLazyMarkets || isFetchingNextLazyMarketsPage || hasNextLazyMarketsPage);
287
- const resolvedVolume = resolveDisplayVolume(event.volume, allVenueMarkets);
288
- const volumeLabel = typeof resolvedVolume === "number" ? `${config.formatting.formatCompactCurrency(resolvedVolume)} ${labels.eventItem.volumeSuffix}` : "";
426
+ const resolvedVolume = (_a = resolveDisplayVolume(event.volume, allVenueMarkets)) != null ? _a : 0;
427
+ const volumeLabel = `${config.formatting.formatCompactCurrency(resolvedVolume)} ${labels.eventItem.volumeSuffix}`;
428
+ const orderedFooterVenues = useMemo(() => {
429
+ const venues = visibleVenueLogos.length > 0 ? visibleVenueLogos : comparison.matchedVenues.length > 0 ? comparison.matchedVenues : event.venue ? [event.venue] : [];
430
+ return Array.from(new Set(venues));
431
+ }, [comparison.matchedVenues, event.venue, visibleVenueLogos]);
432
+ const shouldShowMatchedVenueSummary = resolvedVenueCount > 1 || orderedFooterVenues.length > 1;
433
+ const visibleFooterVenues = shouldShowMatchedVenueSummary ? orderedFooterVenues : orderedFooterVenues.slice(0, 1);
434
+ const singleFooterVenue = shouldShowMatchedVenueSummary ? void 0 : visibleFooterVenues[0];
435
+ const matchedCountLabel = labels.eventItem.matchedCount(
436
+ Math.max(resolvedVenueCount, visibleFooterVenues.length)
437
+ );
289
438
  const shouldUseNativeLinkNavigation = (eventToHandle) => {
290
439
  return eventToHandle.metaKey || eventToHandle.ctrlKey || eventToHandle.shiftKey || eventToHandle.altKey || eventToHandle.button === 1;
291
440
  };
@@ -376,62 +525,116 @@ var EventListItemContent = ({
376
525
  )
377
526
  ] });
378
527
  };
379
- const _renderPriceGapPill = (value) => {
528
+ const renderPriceStripSkeleton = (count) => {
529
+ const safeCount = Math.max(1, Math.min(count || 1, 3));
530
+ const collapsedCount = safeCount - 1;
531
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
532
+ Array.from({ length: collapsedCount }).map((_, index) => /* @__PURE__ */ jsx(
533
+ "span",
534
+ {
535
+ "aria-hidden": "true",
536
+ className: cn(
537
+ "agg-outcome-price-loading agg-outcome-price-loading-collapsed",
538
+ "h-[30px] w-[30px] shrink-0 rounded-agg-full bg-agg-separator animate-pulse",
539
+ index > 0 && "-ml-3"
540
+ )
541
+ },
542
+ `agg-outcome-price-loading-collapsed-${index}`
543
+ )),
544
+ /* @__PURE__ */ jsx(
545
+ "span",
546
+ {
547
+ "aria-hidden": "true",
548
+ className: cn(
549
+ "agg-outcome-price-loading agg-outcome-price-loading-best",
550
+ "ml-1.5 h-[30px] w-[68px] shrink-0 rounded-agg-full bg-agg-separator animate-pulse"
551
+ )
552
+ }
553
+ )
554
+ ] });
555
+ };
556
+ const renderPriceGapPill = (value) => {
557
+ if (!config.features.enablePriceGap) return null;
558
+ if (isAwaitingMidpoints) return null;
380
559
  const formattedPriceGap = formatPriceGapPercent(value);
381
560
  if (!formattedPriceGap) return null;
382
561
  return /* @__PURE__ */ jsxs(
383
- "div",
562
+ "span",
384
563
  {
385
564
  className: cn(
386
565
  "agg-price-gap-pill",
387
- "inline-flex shrink-0 items-center gap-1 rounded-agg-sm border border-agg-success/50 px-2 py-0.5",
388
- "bg-agg-success/10"
566
+ "inline-flex max-w-[52px] shrink-0 items-center gap-1 overflow-hidden whitespace-nowrap",
567
+ "text-agg-2xs font-agg-bold leading-agg-14 text-agg-success",
568
+ "opacity-100 transition-all duration-200 ease-out",
569
+ "group-hover/agg-event-card:mr-0 group-hover/agg-event-card:max-w-0 group-hover/agg-event-card:opacity-0"
389
570
  ),
571
+ title: labels.eventItem.priceGap,
572
+ "aria-label": `${formattedPriceGap} ${labels.eventItem.priceGap}`,
390
573
  children: [
391
- /* @__PURE__ */ jsx(Typography, { variant: "overline", className: "text-agg-success", children: formattedPriceGap }),
392
- /* @__PURE__ */ jsx(Typography, { variant: "overline", className: "text-agg-success uppercase", children: labels.eventItem.priceGap })
574
+ /* @__PURE__ */ jsx(
575
+ "i",
576
+ {
577
+ "aria-hidden": "true",
578
+ className: "fi-rr-arrow-down inline-flex size-3! shrink-0 items-center justify-center text-agg-xs text-agg-success"
579
+ }
580
+ ),
581
+ /* @__PURE__ */ jsx("span", { children: formattedPriceGap })
393
582
  ]
394
583
  }
395
584
  );
396
585
  };
397
- const renderOutcomePriceBadge = (probability, venue, shouldRenderLoadingSkeleton, market, outcome) => {
586
+ const renderComparisonPriceBadge = (row, price, priceIndex) => {
398
587
  var _a2;
399
- if (shouldRenderLoadingSkeleton) {
400
- return /* @__PURE__ */ jsx(
401
- "div",
402
- {
403
- "aria-hidden": "true",
404
- className: cn(
405
- "agg-outcome-price-loading bg-agg-separator animate-pulse h-8 w-[100px] rounded-agg-full"
406
- )
407
- }
408
- );
409
- }
410
- if (typeof probability !== "number") return null;
411
- const marketHref = (_a2 = getMarketHref == null ? void 0 : getMarketHref(event, market, outcome)) != null ? _a2 : resolveMarketHrefFromEventHref(href, market.id, outcome.id);
588
+ const marketHref = (_a2 = getMarketHref == null ? void 0 : getMarketHref(event, row.market, row.outcome)) != null ? _a2 : resolveMarketHrefFromEventHref(href, row.market.id, row.outcome.id);
412
589
  const isInteractive = Boolean(onMarketClick || marketHref);
590
+ const hasVenueCompetition = row.prices.length > 1;
591
+ const isHighlightedBestPrice = hasVenueCompetition && price.isBest;
592
+ const shouldCollapsePriceText = hasVenueCompetition && !isHighlightedBestPrice;
413
593
  return /* @__PURE__ */ jsx(
414
594
  Badge,
415
595
  {
416
- text: formatMarketProbabilityPercent(probability, config.formatting.formatPercent),
417
- prefix: /* @__PURE__ */ jsx(VenueLogo, { venue, size: "small" }),
596
+ text: formatMarketProbabilityPercent(price.price, config.formatting.formatPercent),
597
+ prefix: /* @__PURE__ */ jsx(
598
+ VenueLogo,
599
+ {
600
+ venue: price.venue,
601
+ size: "small",
602
+ isMonochromatic: !isHighlightedBestPrice,
603
+ className: cn(!isHighlightedBestPrice && "text-agg-muted-foreground!")
604
+ }
605
+ ),
418
606
  size: "small",
419
607
  classNames: {
420
608
  root: cn(
421
609
  "agg-outcome-price",
422
- "h-8 min-w-25 justify-center px-4 text-agg-base leading-agg-6",
610
+ "h-[30px] justify-center overflow-hidden px-0 text-agg-sm leading-agg-5",
611
+ "shrink-0 p-0! transition-all duration-300 ease-out",
612
+ isHighlightedBestPrice ? "agg-outcome-price-best ml-2 w-[68px] min-w-[68px] shadow-none" : cn(
613
+ "bg-agg-secondary-hover hover:bg-agg-tertiary",
614
+ hasVenueCompetition ? "agg-outcome-price-collapsed relative box-border w-[30px] min-w-[30px] group-hover/agg-event-card:w-[68px] group-hover/agg-event-card:min-w-[68px] group-hover/agg-event-card:gap-1" : "w-[68px] min-w-[68px]",
615
+ hasVenueCompetition && priceIndex > 0 && "-ml-3 group-hover/agg-event-card:ml-2"
616
+ ),
423
617
  isInteractive && "cursor-pointer",
424
618
  classNames == null ? void 0 : classNames.badge
425
619
  ),
426
- text: "text-agg-foreground"
620
+ prefix: cn(
621
+ "flex shrink-0 items-center justify-center [&>svg]:size-3.5",
622
+ shouldCollapsePriceText && "pointer-events-none absolute inset-0 transition-[opacity] duration-300 ease-in group-hover/agg-event-card:pointer-events-auto group-hover/agg-event-card:static group-hover/agg-event-card:inset-auto"
623
+ ),
624
+ text: cn(
625
+ "transition-[max-width,opacity] duration-300 ease-in",
626
+ price.isBest ? "text-agg-foreground" : "text-agg-muted-foreground",
627
+ shouldCollapsePriceText && "max-w-0 overflow-hidden! opacity-0 group-hover/agg-event-card:max-w-[40px] group-hover/agg-event-card:opacity-100"
628
+ )
427
629
  },
428
630
  onClick: isInteractive ? (eventToHandle) => {
429
- handleOutcomeBadgeClick(eventToHandle, market, outcome, marketHref);
631
+ handleOutcomeBadgeClick(eventToHandle, row.market, row.outcome, marketHref);
430
632
  } : void 0,
431
633
  onAuxClick: isInteractive ? (eventToHandle) => {
432
- handleOutcomeBadgeClick(eventToHandle, market, outcome, marketHref);
634
+ handleOutcomeBadgeClick(eventToHandle, row.market, row.outcome, marketHref);
433
635
  } : void 0
434
- }
636
+ },
637
+ `${price.market.id}:${price.outcome.id}`
435
638
  );
436
639
  };
437
640
  return /* @__PURE__ */ jsxs(
@@ -451,153 +654,72 @@ var EventListItemContent = ({
451
654
  "aria-label": ariaLabel != null ? ariaLabel : resolvedTitle,
452
655
  withGradient: true,
453
656
  children: [
454
- /* @__PURE__ */ jsxs(
455
- "div",
456
- {
457
- className: cn("agg-event-card-header min-h-12 flex items-center gap-3", classNames == null ? void 0 : classNames.header),
458
- children: [
459
- /* @__PURE__ */ jsx(
460
- RemoteImage,
461
- {
462
- src: optimizedImageUrl(resolvedImage, 40),
463
- alt: "",
464
- className: cn("agg-event-image h-10 w-10 rounded-agg-lg object-cover")
465
- }
657
+ /* @__PURE__ */ jsxs("div", { className: cn("agg-event-card-header flex h-12 items-center gap-4", classNames == null ? void 0 : classNames.header), children: [
658
+ /* @__PURE__ */ jsx(
659
+ RemoteImage,
660
+ {
661
+ src: optimizedImageUrl(resolvedImage, 40),
662
+ alt: "",
663
+ classNames: {
664
+ root: "agg-event-image h-10 w-10 shrink-0 rounded-agg-lg object-cover",
665
+ image: "transition-transform duration-300 group-hover/agg-event-card:scale-110"
666
+ }
667
+ }
668
+ ),
669
+ /* @__PURE__ */ jsx(
670
+ Typography,
671
+ {
672
+ variant: "body-strong",
673
+ className: cn(
674
+ "agg-event-title",
675
+ "min-w-0 flex-1 text-agg-base font-agg-bold leading-agg-6",
676
+ "truncate text-wrap wrap-break-word line-clamp-2",
677
+ classNames == null ? void 0 : classNames.title
466
678
  ),
467
- /* @__PURE__ */ jsx(
468
- Typography,
469
- {
470
- variant: "body-strong",
471
- className: cn(
472
- "agg-event-title",
473
- "min-w-0 text-agg-base font-agg-bold leading-agg-6 ",
474
- "truncate text-wrap wrap-break-word line-clamp-2",
475
- classNames == null ? void 0 : classNames.title
476
- ),
477
- children: resolvedTitle
478
- }
479
- )
480
- ]
481
- }
482
- ),
679
+ children: resolvedTitle
680
+ }
681
+ )
682
+ ] }),
483
683
  /* @__PURE__ */ jsxs(
484
684
  "div",
485
685
  {
486
686
  className: cn(
487
- "agg-outcomes flex flex-col gap-3 max-h-20 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
687
+ "agg-outcomes flex max-h-[76px] flex-col gap-4 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
488
688
  classNames == null ? void 0 : classNames.outcomes
489
689
  ),
490
690
  onScroll: handleOutcomesScroll,
491
691
  children: [
492
- resolvedMarketCount === 1 && resolvedOutcomeMarkets.length === 1 ? (() => {
493
- const market = resolvedOutcomeMarkets[0];
494
- return sortOutcomes(market.venueMarketOutcomes).map((outcome) => {
495
- var _a2;
496
- const resolvedDisplayPrice = selectOutcomePrice({
497
- outcomeId: outcome.id,
498
- selection: tradeSide,
499
- bestPrices: displayBestPricesByOutcomeId,
500
- bestMidpointsByOutcomeId: displayMidpointsByOutcomeId
501
- });
502
- const midpointProbability = resolvedDisplayPrice;
503
- const probability = midpointProbability != null ? normalizeProbability(midpointProbability) : isMidpointQueryInFlight ? void 0 : normalizeProbability(outcome.price);
504
- const displayVenue = (_a2 = getDisplayOutcomeVenue({
505
- outcomeId: outcome.id,
506
- selection: tradeSide,
507
- bestVenueByOutcomeId: displayMidpointVenueByOutcomeId,
508
- bestPriceVenuesByOutcomeId: displayBestPriceVenuesByOutcomeId,
509
- fallbackVenue: market.venue
510
- })) != null ? _a2 : market.venue;
511
- const shouldRenderMidpointSkeleton = midpointProbability == null && isMidpointQueryInFlight;
512
- const arbitragePercent = arbitrageByOutcomeId == null ? void 0 : arbitrageByOutcomeId[outcome.id];
513
- const _priceGapPct = gapsByVenueMarketId.get(market.id);
514
- return /* @__PURE__ */ jsxs(
515
- "div",
516
- {
517
- className: cn(
518
- "agg-outcome-row",
519
- "flex flex-row gap-3 w-full items-center justify-between",
520
- classNames == null ? void 0 : classNames.outcomeRow
521
- ),
522
- children: [
523
- /* @__PURE__ */ jsx("div", { className: "agg-outcome-label-row flex min-w-0 flex-row items-center justify-start gap-2", children: /* @__PURE__ */ jsx(
524
- Typography,
525
- {
526
- variant: "body",
527
- className: "agg-outcome-label min-w-0 truncate text-agg-sm leading-agg-5 md:text-agg-base md:leading-agg-6",
528
- children: resolveOutcomeTitle(outcome)
529
- }
530
- ) }),
531
- /* @__PURE__ */ jsxs("div", { className: "agg-outcome-meta flex flex-row items-center justify-end gap-3", children: [
532
- renderArbitrage(arbitragePercent),
533
- renderOutcomePriceBadge(
534
- probability,
535
- displayVenue,
536
- shouldRenderMidpointSkeleton,
537
- market,
538
- outcome
539
- )
540
- ] })
541
- ]
542
- },
543
- outcome.id
544
- );
545
- });
546
- })() : sortMarketsByYesOddsDesc(resolvedOutcomeMarkets).map((market) => {
547
- var _a2, _b, _c;
548
- const yesOutcome = resolveYesOutcome(market);
549
- const displayOutcome = yesOutcome != null ? yesOutcome : (_b = market.venueMarketOutcomes) == null ? void 0 : _b.reduce(
550
- (acc, o) => o.price > acc.price ? o : acc,
551
- (_a2 = market.venueMarketOutcomes) == null ? void 0 : _a2[0]
552
- );
553
- const midpointProbability = displayOutcome != null ? selectOutcomePrice({
554
- outcomeId: displayOutcome.id,
555
- selection: tradeSide,
556
- bestPrices: displayBestPricesByOutcomeId,
557
- bestMidpointsByOutcomeId: displayMidpointsByOutcomeId
558
- }) : void 0;
559
- const bestVenue = (_c = displayOutcome != null ? getDisplayOutcomeVenue({
560
- outcomeId: displayOutcome.id,
561
- selection: tradeSide,
562
- bestVenueByOutcomeId: displayMidpointVenueByOutcomeId,
563
- bestPriceVenuesByOutcomeId: displayBestPriceVenuesByOutcomeId,
564
- fallbackVenue: market.venue
565
- }) : void 0) != null ? _c : market.venue;
566
- const probability = midpointProbability != null ? normalizeProbability(midpointProbability) : isMidpointQueryInFlight ? void 0 : normalizeProbability(displayOutcome == null ? void 0 : displayOutcome.price);
567
- const shouldRenderMidpointSkeleton = midpointProbability == null && isMidpointQueryInFlight;
568
- const arbitragePercent = arbitrageByOutcomeId == null ? void 0 : arbitrageByOutcomeId[market.id];
569
- const outcomeTitle = market.question;
570
- const _priceGapPct = gapsByVenueMarketId.get(market.id);
692
+ comparison.rows.map((row) => {
693
+ var _a2;
694
+ const arbitragePercent = (_a2 = arbitrageByOutcomeId == null ? void 0 : arbitrageByOutcomeId[row.outcome.id]) != null ? _a2 : arbitrageByOutcomeId == null ? void 0 : arbitrageByOutcomeId[row.market.id];
695
+ const priceGapPercent = gapsByVenueMarketId.get(row.market.id);
571
696
  return /* @__PURE__ */ jsxs(
572
697
  "div",
573
698
  {
574
699
  className: cn(
575
700
  "agg-outcome-row",
576
- "flex flex-row gap-3 w-full items-center justify-between",
701
+ "flex h-10 w-full flex-row items-center justify-between gap-3",
577
702
  classNames == null ? void 0 : classNames.outcomeRow
578
703
  ),
579
704
  children: [
580
- /* @__PURE__ */ jsx("div", { className: "agg-outcome-label-row flex min-w-0 flex-row items-center justify-start gap-2", children: /* @__PURE__ */ jsx(
705
+ /* @__PURE__ */ jsx("div", { className: "agg-outcome-label-row flex min-w-0 flex-1 flex-row items-center justify-start gap-2", children: /* @__PURE__ */ jsx(
581
706
  Typography,
582
707
  {
583
708
  variant: "body",
584
- className: "agg-outcome-label min-w-0 truncate text-agg-sm leading-agg-5 md:text-agg-base md:leading-agg-6",
585
- children: outcomeTitle
709
+ className: "agg-outcome-label min-w-0 truncate text-agg-xs leading-agg-5 md:text-agg-base md:leading-agg-6",
710
+ children: row.label
586
711
  }
587
712
  ) }),
588
- /* @__PURE__ */ jsxs("div", { className: "agg-outcome-meta flex flex-row items-center justify-end gap-3", children: [
713
+ /* @__PURE__ */ jsxs("div", { className: "agg-outcome-meta flex shrink-0 flex-row items-center justify-end gap-2", children: [
589
714
  renderArbitrage(arbitragePercent),
590
- renderOutcomePriceBadge(
591
- probability,
592
- bestVenue,
593
- shouldRenderMidpointSkeleton,
594
- market,
595
- displayOutcome
596
- )
715
+ renderPriceGapPill(priceGapPercent),
716
+ /* @__PURE__ */ jsx("div", { className: "agg-outcome-price-strip relative flex shrink-0 items-center justify-end", children: isAwaitingMidpoints ? renderPriceStripSkeleton(row.prices.length) : row.prices.map(
717
+ (price, priceIndex) => renderComparisonPriceBadge(row, price, priceIndex)
718
+ ) })
597
719
  ] })
598
720
  ]
599
721
  },
600
- `${market.id}`
722
+ row.id
601
723
  );
602
724
  }),
603
725
  shouldRenderLoadingOutcomeRow ? /* @__PURE__ */ jsxs("div", { className: "agg-outcome-row-loading flex w-full items-center justify-between gap-3", children: [
@@ -624,7 +746,7 @@ var EventListItemContent = ({
624
746
  {
625
747
  className: cn(
626
748
  "agg-event-card-footer",
627
- "flex items-center justify-between gap-2",
749
+ "flex items-center justify-between gap-5",
628
750
  "text-agg-muted-foreground",
629
751
  classNames == null ? void 0 : classNames.footer
630
752
  ),
@@ -633,41 +755,43 @@ var EventListItemContent = ({
633
755
  "div",
634
756
  {
635
757
  className: cn(
636
- "agg-event-meta flex items-center gap-1 text-agg-sm leading-agg-5 truncate"
758
+ "agg-event-meta flex min-w-0 flex-1 items-center gap-2 text-agg-sm leading-agg-5 truncate"
637
759
  ),
638
760
  children: [
639
- /* @__PURE__ */ jsx("span", { className: "truncate text-agg-muted-foreground", children: formatCountLabel(
640
- resolvedMarketCount,
641
- labels.eventItem.marketSingular,
642
- labels.eventItem.marketPlural
643
- ) }),
644
- /* @__PURE__ */ jsx("span", { className: "text-agg-muted-foreground", children: "\xD7" }),
645
- /* @__PURE__ */ jsx("span", { className: "truncate text-agg-muted-foreground", children: formatCountLabel(
646
- resolvedVenueCount,
647
- labels.eventItem.venueSingular,
648
- labels.eventItem.venuePlural
649
- ) }),
650
- visibleVenueLogos.length > 0 ? /* @__PURE__ */ jsx("span", { className: "agg-event-venues flex items-center gap-1 overflow-hidden", children: visibleVenueLogos.map((venue) => /* @__PURE__ */ jsx(
761
+ shouldShowMatchedVenueSummary ? /* @__PURE__ */ jsx("span", { className: "agg-event-matched-badge rounded-agg-full bg-agg-primary/10 px-2 py-[3px] text-agg-2xs font-agg-bold uppercase leading-agg-14 text-agg-primary", children: matchedCountLabel }) : null,
762
+ singleFooterVenue ? /* @__PURE__ */ jsxs("span", { className: "agg-event-single-venue flex min-w-0 items-center gap-1 overflow-hidden", children: [
763
+ /* @__PURE__ */ jsx(
764
+ VenueLogo,
765
+ {
766
+ venue: singleFooterVenue,
767
+ size: "small",
768
+ isMonochromatic: true,
769
+ className: "text-agg-muted-foreground! size-[14px]!"
770
+ }
771
+ ),
772
+ /* @__PURE__ */ jsx("span", { className: "truncate text-agg-muted-foreground", children: resolveVenueLabel(singleFooterVenue, void 0, labels) })
773
+ ] }) : null,
774
+ shouldShowMatchedVenueSummary && visibleFooterVenues.length > 0 ? /* @__PURE__ */ jsx("span", { className: "agg-event-venues flex items-center gap-1 overflow-hidden", children: visibleFooterVenues.map((venue) => /* @__PURE__ */ jsx(
651
775
  VenueLogo,
652
776
  {
653
777
  venue,
654
778
  size: "small",
655
779
  isMonochromatic: true,
656
- className: "text-agg-muted-foreground! size-[14px]!"
780
+ className: "text-agg-muted-foreground! size-3.5!"
657
781
  },
658
782
  venue
659
783
  )) }) : null
660
784
  ]
661
785
  }
662
786
  ),
663
- volumeLabel ? /* @__PURE__ */ jsx(
787
+ /* @__PURE__ */ jsx(
664
788
  Typography,
665
789
  {
666
790
  variant: "label",
667
- className: "agg-event-volume max-w-16 sm:max-w-20 min-w-12 sm:min-w-14 truncate whitespace-pre text-agg-sm text-agg-muted-foreground line-clamp-1",
791
+ className: "agg-event-volume min-w-fit shrink-0 truncate whitespace-pre text-agg-sm text-agg-muted-foreground line-clamp-1",
668
792
  children: volumeLabel
669
793
  }
670
- ) : null
794
+ )
671
795
  ]
672
796
  }
673
797
  )
@@ -721,6 +845,10 @@ var EventListItem = (props) => {
721
845
  return /* @__PURE__ */ jsx(EventListItemByEventId, __spreadValues({}, props));
722
846
  };
723
847
  EventListItem.displayName = "EventListItem";
848
+ var EventListItemV2 = (props) => {
849
+ return /* @__PURE__ */ jsx(EventListItem, __spreadValues({}, props));
850
+ };
851
+ EventListItemV2.displayName = "EventListItemV2";
724
852
 
725
853
  // src/events/market-details/orderbook-aggregation.ts
726
854
  import {
@@ -742,12 +870,12 @@ var fromScaled = (value, precision) => {
742
870
  var toPriceKey = (price) => {
743
871
  return String(toScaled(price, PRICE_PRECISION));
744
872
  };
745
- var normalizeOutcomeLabel2 = (value) => {
873
+ var normalizeOutcomeLabel = (value) => {
746
874
  if (typeof value !== "string") return "";
747
875
  return value.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ");
748
876
  };
749
877
  var toSemanticOutcomeLabel = (value) => {
750
- const normalizedValue = normalizeOutcomeLabel2(value);
878
+ const normalizedValue = normalizeOutcomeLabel(value);
751
879
  if (!normalizedValue) return "";
752
880
  if (POSITIVE_OUTCOME_KEYS.has(normalizedValue)) return "yes";
753
881
  if (NEGATIVE_OUTCOME_KEYS.has(normalizedValue)) return "no";
@@ -761,10 +889,10 @@ var getOutcomeLabelCandidates = (outcome) => {
761
889
  );
762
890
  };
763
891
  var matchesSelectedOutcomeLabel = (outcome, selectedOutcomeLabel) => {
764
- const normalizedSelectedLabel = normalizeOutcomeLabel2(selectedOutcomeLabel);
892
+ const normalizedSelectedLabel = normalizeOutcomeLabel(selectedOutcomeLabel);
765
893
  const semanticSelectedLabel = toSemanticOutcomeLabel(selectedOutcomeLabel);
766
894
  return getOutcomeLabelCandidates(outcome).some((candidate) => {
767
- const normalizedCandidate = normalizeOutcomeLabel2(candidate);
895
+ const normalizedCandidate = normalizeOutcomeLabel(candidate);
768
896
  if (!normalizedCandidate) return false;
769
897
  return normalizedCandidate === normalizedSelectedLabel || toSemanticOutcomeLabel(candidate) === semanticSelectedLabel;
770
898
  });
@@ -2356,7 +2484,7 @@ var EventListItemDetailsContent = ({
2356
2484
  () => resolvedEventTradingState.displayMarkets,
2357
2485
  [resolvedEventTradingState.displayMarkets]
2358
2486
  );
2359
- const sortedVenueMarkets = useMemo5(() => sortMarketsByYesOddsDesc(venueMarkets), [venueMarkets]);
2487
+ const sortedVenueMarkets = useMemo5(() => sortMarketsByVolumeDesc(venueMarkets), [venueMarkets]);
2360
2488
  const marketOptions = useMemo5(
2361
2489
  () => sortedVenueMarkets.map((vm) => ({
2362
2490
  value: vm.id,
@@ -3108,7 +3236,7 @@ import {
3108
3236
  mergeBestPricesPreferringLive as mergeBestPricesPreferringLive2,
3109
3237
  optimizedImageUrl as optimizedImageUrl3,
3110
3238
  resolveMarketTradingState,
3111
- useEventTradingContext as useEventTradingContext3,
3239
+ useEventTradingContext as useEventTradingContext2,
3112
3240
  useLabels as useLabels6,
3113
3241
  useLiveBestPrices as useLiveBestPrices2,
3114
3242
  useLiveOutcomePrices as useLiveOutcomePrices2,
@@ -3121,6 +3249,32 @@ import {
3121
3249
  } from "@agg-build/hooks";
3122
3250
  import { useEffect as useEffect4, useId, useMemo as useMemo6, useRef as useRef5, useState as useState4 } from "react";
3123
3251
 
3252
+ // src/events/shared/display-outcome-venue.ts
3253
+ var normalizeOutcomeLabel2 = (label) => {
3254
+ return typeof label === "string" ? label.trim().toLowerCase() : "";
3255
+ };
3256
+ var getDisplayOutcomeVenue = ({
3257
+ outcomeId,
3258
+ outcomeLabel,
3259
+ selection,
3260
+ bestMidpointVenue,
3261
+ bestVenueByOutcomeId,
3262
+ bestPriceVenuesByOutcomeId,
3263
+ fallbackVenue
3264
+ }) => {
3265
+ var _a;
3266
+ if (selection && outcomeId) {
3267
+ const sideVenues = bestPriceVenuesByOutcomeId == null ? void 0 : bestPriceVenuesByOutcomeId.get(outcomeId);
3268
+ if (selection === "buy" && (sideVenues == null ? void 0 : sideVenues.bestAskVenue)) return sideVenues.bestAskVenue;
3269
+ if (selection === "sell" && (sideVenues == null ? void 0 : sideVenues.bestBidVenue)) return sideVenues.bestBidVenue;
3270
+ }
3271
+ if (normalizeOutcomeLabel2(outcomeLabel) === "yes" && bestMidpointVenue) {
3272
+ return bestMidpointVenue;
3273
+ }
3274
+ const bestVenue = outcomeId ? bestVenueByOutcomeId == null ? void 0 : bestVenueByOutcomeId.get(outcomeId) : void 0;
3275
+ return (_a = bestVenue != null ? bestVenue : fallbackVenue) != null ? _a : void 0;
3276
+ };
3277
+
3124
3278
  // src/events/market-details/market-details-outcome-button.tsx
3125
3279
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3126
3280
  var marketDetailsOutcomeButtonToneClasses = {
@@ -3217,7 +3371,7 @@ var MarketDetailsOutcomeButton = ({
3217
3371
  MarketDetailsOutcomeButton.displayName = "MarketDetailsOutcomeButton";
3218
3372
 
3219
3373
  // src/events/market-details/index.tsx
3220
- import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3374
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3221
3375
  var MarketDetailsUnavailableState = ({
3222
3376
  ariaLabel,
3223
3377
  classNames
@@ -3284,7 +3438,7 @@ var MarketDetailsContent = ({
3284
3438
  } = config;
3285
3439
  const isDarkTheme = theme === "dark";
3286
3440
  const labels = useLabels6();
3287
- const tradingContext = useEventTradingContext3();
3441
+ const tradingContext = useEventTradingContext2();
3288
3442
  const detailsContentId = useId();
3289
3443
  const tradeSide = (_a = tradingContext == null ? void 0 : tradingContext.tradeSide) != null ? _a : "buy";
3290
3444
  const wsLivePrices = useLiveOutcomePrices2(venueMarkets);
@@ -4022,7 +4176,7 @@ var MarketDetailsContent = ({
4022
4176
  ) : "translate-y-5 opacity-0"
4023
4177
  ),
4024
4178
  children: [
4025
- !isResolvedMarket ? /* @__PURE__ */ jsxs7(Fragment, { children: [
4179
+ !isResolvedMarket ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
4026
4180
  /* @__PURE__ */ jsx9("div", { className: cn("block", classNames == null ? void 0 : classNames.tabs), children: /* @__PURE__ */ jsx9(
4027
4181
  Tabs,
4028
4182
  {
@@ -4315,7 +4469,7 @@ var MarketDetailsList = ({
4315
4469
  }) => {
4316
4470
  var _a, _b;
4317
4471
  const labels = useLabels6();
4318
- const tradingContext = useEventTradingContext3();
4472
+ const tradingContext = useEventTradingContext2();
4319
4473
  const selectedMarketId = (_a = tradingContext == null ? void 0 : tradingContext.selectedMarketId) != null ? _a : null;
4320
4474
  const [expandedMarketId, setExpandedMarketId] = useState4(null);
4321
4475
  const [areResolvedMarketsVisible, setAreResolvedMarketsVisible] = useState4(false);
@@ -4343,7 +4497,7 @@ var MarketDetailsList = ({
4343
4497
  return resolveIsResolvedEvent(sourceMarkets, eventTradingState);
4344
4498
  }, [eventTradingState, sourceMarkets]);
4345
4499
  const groupedMarkets = useMemo6(() => {
4346
- const sortedMarkets = sortMarketsByYesOddsDesc(sourceMarkets);
4500
+ const sortedMarkets = sortMarketsByVolumeDesc(sourceMarkets);
4347
4501
  if (sortedMarkets.length === 0) {
4348
4502
  return {
4349
4503
  primary: [],
@@ -4530,12 +4684,22 @@ var MarketDetailsList = ({
4530
4684
  MarketDetailsList.displayName = "MarketDetailsList";
4531
4685
 
4532
4686
  // src/events/list/index.tsx
4533
- import { MarketStatus as MarketStatus2, useAppConfig as useAppConfig2, useLabels as useLabels8, useVenueEvents } from "@agg-build/hooks";
4687
+ import {
4688
+ MarketStatus as MarketStatus2,
4689
+ getVisibleVenueIdsByConfig,
4690
+ useAppConfig as useAppConfig2,
4691
+ useCategories,
4692
+ useCategoryChildren,
4693
+ useLabels as useLabels8,
4694
+ useSdkUiConfig as useSdkUiConfig4,
4695
+ useVenueEvents
4696
+ } from "@agg-build/hooks";
4534
4697
  import { VENUES } from "@agg-build/sdk";
4698
+ import * as Dialog from "@radix-ui/react-dialog";
4535
4699
  import { useEffect as useEffect6, useMemo as useMemo8, useRef as useRef7, useState as useState6 } from "react";
4536
4700
 
4537
4701
  // src/events/list/event-list-tabs.tsx
4538
- import { useAppConfig, useLabels as useLabels7 } from "@agg-build/hooks";
4702
+ import { getVenueAvailabilityState, useAppConfig, useGeoBlock, useLabels as useLabels7 } from "@agg-build/hooks";
4539
4703
  import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef6, useState as useState5 } from "react";
4540
4704
 
4541
4705
  // src/events/list/event-list.constants.ts
@@ -4605,6 +4769,9 @@ var getDefaultEventListTabs = (labels) => {
4605
4769
  // src/events/list/event-list-tabs.tsx
4606
4770
  import { jsx as jsx10 } from "react/jsx-runtime";
4607
4771
  var renderTabIcon = (tab, isActive) => {
4772
+ if (tab.iconName === "warning-filled") {
4773
+ return /* @__PURE__ */ jsx10(Icon, { name: "warning-filled", size: "small", color: "currentColor" });
4774
+ }
4608
4775
  if (tab.venueLogo) {
4609
4776
  return /* @__PURE__ */ jsx10(VenueLogo, { venue: tab.venueLogo, size: "small" });
4610
4777
  }
@@ -4621,15 +4788,26 @@ var renderTabIcon = (tab, isActive) => {
4621
4788
  var useEventListTabs = (tabs) => {
4622
4789
  const labels = useLabels7();
4623
4790
  const { disabledVenues } = useAppConfig();
4791
+ const { isLocationBlocked } = useGeoBlock();
4624
4792
  return useMemo7(() => {
4625
4793
  const baseTabs = tabs != null ? tabs : getDefaultEventListTabs(labels);
4626
- if (disabledVenues.length === 0) return baseTabs;
4627
- const disabled = new Set(disabledVenues);
4628
- return baseTabs.filter((tab) => {
4629
- if (!tab.venueLogo) return true;
4630
- return !disabled.has(tab.venueLogo);
4794
+ return baseTabs.flatMap((tab) => {
4795
+ if (!tab.venueLogo) return [tab];
4796
+ const availabilityState = getVenueAvailabilityState({
4797
+ venueId: tab.venueLogo,
4798
+ disabledVenues,
4799
+ isLocationBlocked
4800
+ });
4801
+ if (availabilityState === "hidden") return [];
4802
+ return [
4803
+ __spreadProps(__spreadValues({}, tab), {
4804
+ disabled: tab.disabled || availabilityState === "geoblocked",
4805
+ label: availabilityState === "geoblocked" ? `${tab.label} (${labels.trading.venueUnavailableInRegion})` : tab.label,
4806
+ iconName: availabilityState === "geoblocked" ? "warning-filled" : tab.iconName
4807
+ })
4808
+ ];
4631
4809
  });
4632
- }, [tabs, labels, disabledVenues]);
4810
+ }, [tabs, labels, disabledVenues, isLocationBlocked]);
4633
4811
  };
4634
4812
  var EventListTabs = ({
4635
4813
  tabs,
@@ -4641,9 +4819,10 @@ var EventListTabs = ({
4641
4819
  }) => {
4642
4820
  const resolvedTabs = useEventListTabs(tabs);
4643
4821
  useEffect5(() => {
4644
- if (resolvedTabs.length === 0) return;
4645
- if (resolvedTabs.some((tab) => tab.value === activeTab)) return;
4646
- onTabChange(resolvedTabs[0].value);
4822
+ const fallbackTab = resolvedTabs.find((tab) => !tab.disabled);
4823
+ if (!fallbackTab) return;
4824
+ if (resolvedTabs.some((tab) => tab.value === activeTab && !tab.disabled)) return;
4825
+ onTabChange(fallbackTab.value);
4647
4826
  }, [activeTab, onTabChange, resolvedTabs]);
4648
4827
  const items = useMemo7(() => {
4649
4828
  return resolvedTabs.map((tab) => {
@@ -4670,12 +4849,13 @@ var EventListTabs = ({
4670
4849
  );
4671
4850
  };
4672
4851
  EventListTabs.displayName = "EventListTabs";
4673
- var useEventListTabsHeaderOverflow = (recomputeOn) => {
4852
+ var useEventListTabsHeaderOverflow = (recomputeOn, options) => {
4674
4853
  const headerRef = useRef6(null);
4675
4854
  const titleRef = useRef6(null);
4676
4855
  const requiredTabsWidthRef = useRef6(0);
4677
4856
  const [shouldUseSelectOverflow, setShouldUseSelectOverflow] = useState5(false);
4678
4857
  const updateTabsOverflowBehavior = useCallback3(() => {
4858
+ var _a;
4679
4859
  if (typeof window === "undefined") return;
4680
4860
  const headerElement = headerRef.current;
4681
4861
  const titleElement = titleRef.current;
@@ -4688,6 +4868,16 @@ var useEventListTabsHeaderOverflow = (recomputeOn) => {
4688
4868
  setShouldUseSelectOverflow(true);
4689
4869
  return;
4690
4870
  }
4871
+ const minTabsInlineWidthForSelect = options == null ? void 0 : options.minTabsInlineWidthForSelect;
4872
+ if (typeof minTabsInlineWidthForSelect === "number" && availableTabsWidth < minTabsInlineWidthForSelect) {
4873
+ setShouldUseSelectOverflow(true);
4874
+ return;
4875
+ }
4876
+ const preferSelectOnOverflow = (_a = options == null ? void 0 : options.preferSelectOnOverflow) != null ? _a : true;
4877
+ if (!preferSelectOnOverflow) {
4878
+ setShouldUseSelectOverflow(false);
4879
+ return;
4880
+ }
4691
4881
  const tabListElement = headerElement.querySelector(".agg-tab-list");
4692
4882
  if (tabListElement) {
4693
4883
  const currentRequiredWidth = tabListElement.scrollWidth;
@@ -4700,7 +4890,7 @@ var useEventListTabsHeaderOverflow = (recomputeOn) => {
4700
4890
  }
4701
4891
  if (requiredTabsWidthRef.current <= 0) return;
4702
4892
  setShouldUseSelectOverflow(requiredTabsWidthRef.current > availableTabsWidth);
4703
- }, []);
4893
+ }, [options == null ? void 0 : options.minTabsInlineWidthForSelect, options == null ? void 0 : options.preferSelectOnOverflow]);
4704
4894
  useEffect5(() => {
4705
4895
  updateTabsOverflowBehavior();
4706
4896
  }, [recomputeOn, updateTabsOverflowBehavior]);
@@ -4732,13 +4922,48 @@ var useEventListTabsHeaderOverflow = (recomputeOn) => {
4732
4922
  };
4733
4923
 
4734
4924
  // src/events/list/index.tsx
4735
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
4925
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
4926
+ var subcategoryMenuRowClassName = cn(
4927
+ "flex min-h-11 w-full items-center justify-between rounded-[6px] px-3 py-3",
4928
+ "text-left text-agg-sm font-agg-normal leading-agg-5",
4929
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-agg-primary focus-visible:ring-offset-1 focus-visible:ring-offset-agg-secondary"
4930
+ );
4931
+ var subcategoryMenuIdleRowClassName = cn(
4932
+ subcategoryMenuRowClassName,
4933
+ "cursor-pointer text-agg-foreground hover:bg-agg-secondary-hover"
4934
+ );
4935
+ var subcategoryMenuSelectedRowClassName = cn(
4936
+ subcategoryMenuRowClassName,
4937
+ "cursor-pointer bg-agg-primary/10 text-agg-primary"
4938
+ );
4939
+ var SORT_ICON_BY_VALUE = {
4940
+ volume24hr: "sort-volume-24hr",
4941
+ topArbitrage: "sort-top-arbitrage",
4942
+ volume: "sort-volume",
4943
+ endDate: "sort-end-date"
4944
+ };
4945
+ var resolveSortQueryParams = (value) => {
4946
+ if (value === "volume") {
4947
+ return { sortBy: "volume", sortDir: "desc" };
4948
+ }
4949
+ if (value === "endDate") {
4950
+ return { sortBy: "endDate", sortDir: "asc" };
4951
+ }
4952
+ if (value === "topArbitrage") {
4953
+ return {
4954
+ sortBy: "arbReturn",
4955
+ sortDir: "desc"
4956
+ };
4957
+ }
4958
+ return { sortBy: "volume24hr", sortDir: "desc" };
4959
+ };
4736
4960
  var EventList = ({
4737
4961
  title,
4738
4962
  limit = DEFAULT_EVENTS_LIMIT,
4739
4963
  maxVisibleItems,
4740
4964
  search,
4741
4965
  categoryIds,
4966
+ onCategoryRootChange,
4742
4967
  onEventClick,
4743
4968
  onMarketClick,
4744
4969
  getMarketHref,
@@ -4747,20 +4972,141 @@ var EventList = ({
4747
4972
  initialLoadedPageCount,
4748
4973
  stateRef
4749
4974
  }) => {
4750
- var _a, _b;
4975
+ var _a, _b, _c;
4751
4976
  const labels = useLabels8();
4752
4977
  const { disabledVenues } = useAppConfig2();
4978
+ const {
4979
+ features: { enableVenueEventDiscoveryFilters }
4980
+ } = useSdkUiConfig4();
4981
+ const isDiscoveryFiltersEnabled = enableVenueEventDiscoveryFilters && !search;
4753
4982
  const resolvedTabs = useEventListTabs();
4754
4983
  const visibleVenues = useMemo8(
4755
- () => disabledVenues.length === 0 ? void 0 : VENUES.filter(
4756
- (v) => !disabledVenues.includes(v)
4757
- ),
4984
+ () => disabledVenues.length === 0 ? void 0 : getVisibleVenueIdsByConfig(VENUES, disabledVenues),
4758
4985
  [disabledVenues]
4759
4986
  );
4760
4987
  const [activeTabValue, setActiveTabValue] = useState6(
4761
4988
  (_b = initialVenueTab != null ? initialVenueTab : (_a = resolvedTabs[0]) == null ? void 0 : _a.value) != null ? _b : "matched"
4762
4989
  );
4763
- const { headerRef, titleRef, shouldUseSelectOverflow } = useEventListTabsHeaderOverflow(resolvedTabs);
4990
+ const [selectedSort, setSelectedSort] = useState6("volume24hr");
4991
+ const [isSubcategoriesModalOpen, setIsSubcategoriesModalOpen] = useState6(false);
4992
+ const [categoryPath, setCategoryPath] = useState6([]);
4993
+ const [collapsedCategoryIds, setCollapsedCategoryIds] = useState6(() => /* @__PURE__ */ new Set());
4994
+ const { headerRef, titleRef, shouldUseSelectOverflow } = useEventListTabsHeaderOverflow(resolvedTabs, { minTabsInlineWidthForSelect: 400, preferSelectOnOverflow: false });
4995
+ const sortItems = useMemo8(() => {
4996
+ return [
4997
+ {
4998
+ value: "volume24hr",
4999
+ label: labels.eventList.sortBy24hVolume,
5000
+ icon: /* @__PURE__ */ jsx11(Icon, { name: SORT_ICON_BY_VALUE.volume24hr, size: "small", color: "currentColor" })
5001
+ },
5002
+ {
5003
+ value: "topArbitrage",
5004
+ label: labels.eventList.sortByTopArbitrage,
5005
+ icon: /* @__PURE__ */ jsx11(Icon, { name: SORT_ICON_BY_VALUE.topArbitrage, size: "small", color: "currentColor" })
5006
+ },
5007
+ {
5008
+ value: "volume",
5009
+ label: labels.eventList.sortByTotalVolume,
5010
+ icon: /* @__PURE__ */ jsx11(Icon, { name: SORT_ICON_BY_VALUE.volume, size: "small", color: "currentColor" })
5011
+ },
5012
+ {
5013
+ value: "endDate",
5014
+ label: labels.eventList.sortByEndingSoon,
5015
+ icon: /* @__PURE__ */ jsx11(Icon, { name: SORT_ICON_BY_VALUE.endDate, size: "small", color: "currentColor" })
5016
+ }
5017
+ ];
5018
+ }, [
5019
+ labels.eventList.sortBy24hVolume,
5020
+ labels.eventList.sortByEndingSoon,
5021
+ labels.eventList.sortByTopArbitrage,
5022
+ labels.eventList.sortByTotalVolume
5023
+ ]);
5024
+ const baseCategoryId = (_c = categoryIds == null ? void 0 : categoryIds[0]) != null ? _c : null;
5025
+ useEffect6(() => {
5026
+ if (!isDiscoveryFiltersEnabled) return;
5027
+ if (!baseCategoryId) {
5028
+ setCategoryPath([]);
5029
+ setCollapsedCategoryIds(/* @__PURE__ */ new Set());
5030
+ return;
5031
+ }
5032
+ setCategoryPath([{ id: baseCategoryId, label: baseCategoryId, eventCount: 0 }]);
5033
+ setCollapsedCategoryIds(/* @__PURE__ */ new Set());
5034
+ }, [baseCategoryId, isDiscoveryFiltersEnabled]);
5035
+ const { categories: topLevelCategories } = useCategories({
5036
+ queryKeyScope: "event-list-top-categories",
5037
+ enabled: isDiscoveryFiltersEnabled,
5038
+ limit: 100
5039
+ });
5040
+ const sortedTopLevelCategories = useMemo8(() => {
5041
+ return sortCategoriesForNavigation(topLevelCategories);
5042
+ }, [topLevelCategories]);
5043
+ const baseCategoryChildPrefetchIds = useMemo8(() => {
5044
+ return Array.from(
5045
+ /* @__PURE__ */ new Set([
5046
+ ...sortedTopLevelCategories.map((category) => category.id),
5047
+ ...categoryPath.map((category) => category.id)
5048
+ ])
5049
+ );
5050
+ }, [categoryPath, sortedTopLevelCategories]);
5051
+ const { childrenByParentId: baseChildrenByParentId } = useCategoryChildren({
5052
+ queryKeyScope: "event-list-category-children",
5053
+ enabled: isDiscoveryFiltersEnabled,
5054
+ parentIds: baseCategoryChildPrefetchIds,
5055
+ limit: 100
5056
+ });
5057
+ const visibleChildPrefetchIds = useMemo8(() => {
5058
+ var _a2;
5059
+ const ids = /* @__PURE__ */ new Set();
5060
+ for (const expandedCategory of categoryPath) {
5061
+ for (const childCategory of (_a2 = baseChildrenByParentId.get(expandedCategory.id)) != null ? _a2 : []) {
5062
+ ids.add(childCategory.id);
5063
+ }
5064
+ }
5065
+ return Array.from(ids);
5066
+ }, [baseChildrenByParentId, categoryPath]);
5067
+ const { childrenByParentId: visibleChildrenByParentId } = useCategoryChildren({
5068
+ queryKeyScope: "event-list-category-children",
5069
+ enabled: isDiscoveryFiltersEnabled,
5070
+ parentIds: visibleChildPrefetchIds,
5071
+ limit: 100
5072
+ });
5073
+ const visibleGrandchildPrefetchIds = useMemo8(() => {
5074
+ var _a2;
5075
+ const ids = /* @__PURE__ */ new Set();
5076
+ for (const expandedCategory of categoryPath) {
5077
+ for (const childCategory of (_a2 = visibleChildrenByParentId.get(expandedCategory.id)) != null ? _a2 : []) {
5078
+ ids.add(childCategory.id);
5079
+ }
5080
+ }
5081
+ return Array.from(ids);
5082
+ }, [categoryPath, visibleChildrenByParentId]);
5083
+ const { childrenByParentId: visibleGrandchildrenByParentId } = useCategoryChildren({
5084
+ queryKeyScope: "event-list-category-children",
5085
+ enabled: isDiscoveryFiltersEnabled,
5086
+ parentIds: visibleGrandchildPrefetchIds,
5087
+ limit: 100
5088
+ });
5089
+ const childrenByParentId = useMemo8(() => {
5090
+ return new Map([
5091
+ ...baseChildrenByParentId,
5092
+ ...visibleChildrenByParentId,
5093
+ ...visibleGrandchildrenByParentId
5094
+ ]);
5095
+ }, [baseChildrenByParentId, visibleChildrenByParentId, visibleGrandchildrenByParentId]);
5096
+ const shouldRenderSubcategoryFilters = isDiscoveryFiltersEnabled && (sortedTopLevelCategories.length > 0 || categoryPath.length > 0);
5097
+ const totalTopLevelEventCount = useMemo8(() => {
5098
+ return sortedTopLevelCategories.reduce((accumulator, category) => {
5099
+ var _a2;
5100
+ return accumulator + ((_a2 = category.eventCount) != null ? _a2 : 0);
5101
+ }, 0);
5102
+ }, [sortedTopLevelCategories]);
5103
+ const resolvedCategoryIds = useMemo8(() => {
5104
+ if (!isDiscoveryFiltersEnabled) return categoryIds;
5105
+ return resolveCategoryIdsFromPath(categoryPath, void 0);
5106
+ }, [categoryIds, categoryPath, isDiscoveryFiltersEnabled]);
5107
+ const sortQueryParams = useMemo8(() => {
5108
+ return resolveSortQueryParams(selectedSort);
5109
+ }, [selectedSort]);
4764
5110
  const activeTab = useMemo8(() => {
4765
5111
  return resolvedTabs.find((tab) => tab.value === activeTabValue);
4766
5112
  }, [activeTabValue, resolvedTabs]);
@@ -4796,12 +5142,12 @@ var EventList = ({
4796
5142
  queryKeyScope: "event-list",
4797
5143
  venues,
4798
5144
  search,
4799
- categoryIds,
5145
+ categoryIds: resolvedCategoryIds,
4800
5146
  matchStatus,
4801
5147
  limit: requestLimit,
4802
5148
  status: [MarketStatus2.open],
4803
- sortBy: "volume24hr",
4804
- sortDir: "desc",
5149
+ sortBy: sortQueryParams.sortBy,
5150
+ sortDir: sortQueryParams.sortDir,
4805
5151
  endDateFrom,
4806
5152
  initialPages: initialLoadedPageCount
4807
5153
  });
@@ -4853,10 +5199,121 @@ var EventList = ({
4853
5199
  const gridClassName = cn(
4854
5200
  "grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] md:grid-cols-[repeat(auto-fill,minmax(360px,1fr))] gap-4"
4855
5201
  );
5202
+ const isDrilledIn = categoryPath.length > 0;
5203
+ const handleSelectAllCategories = () => {
5204
+ setCategoryPath([]);
5205
+ setCollapsedCategoryIds(/* @__PURE__ */ new Set());
5206
+ onCategoryRootChange == null ? void 0 : onCategoryRootChange(void 0);
5207
+ setIsSubcategoriesModalOpen(false);
5208
+ };
5209
+ const handleToggleSubcategory = ({
5210
+ entry,
5211
+ depth,
5212
+ hasKnownChildren,
5213
+ isExpanded,
5214
+ isSelected
5215
+ }) => {
5216
+ var _a2;
5217
+ setCategoryPath((prev) => [...prev.slice(0, depth), entry]);
5218
+ setCollapsedCategoryIds((prev) => {
5219
+ const next = new Set(prev);
5220
+ if (hasKnownChildren && isExpanded && isSelected) {
5221
+ next.add(entry.id);
5222
+ return next;
5223
+ }
5224
+ next.delete(entry.id);
5225
+ return next;
5226
+ });
5227
+ onCategoryRootChange == null ? void 0 : onCategoryRootChange(depth === 0 ? entry.id : (_a2 = categoryPath[0]) == null ? void 0 : _a2.id);
5228
+ };
5229
+ const renderSubcategoryTreeRow = ({
5230
+ entry,
5231
+ isExpanded,
5232
+ isSelected,
5233
+ onClick
5234
+ }) => {
5235
+ var _a2, _b2;
5236
+ const hasKnownChildren = ((_b2 = (_a2 = childrenByParentId.get(entry.id)) == null ? void 0 : _a2.length) != null ? _b2 : 0) > 0;
5237
+ return /* @__PURE__ */ jsxs8(
5238
+ "button",
5239
+ {
5240
+ type: "button",
5241
+ "aria-current": isSelected ? "true" : void 0,
5242
+ className: isSelected ? subcategoryMenuSelectedRowClassName : subcategoryMenuIdleRowClassName,
5243
+ onClick,
5244
+ children: [
5245
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate first-letter:uppercase", children: entry.label }),
5246
+ hasKnownChildren ? /* @__PURE__ */ jsx11(
5247
+ Icon,
5248
+ {
5249
+ name: isExpanded ? "chevron-up" : "chevron-down",
5250
+ size: "small",
5251
+ color: "currentColor",
5252
+ className: "shrink-0 text-agg-muted-foreground"
5253
+ }
5254
+ ) : /* @__PURE__ */ jsx11("span", { className: "shrink-0 text-agg-muted-foreground", children: entry.eventCount })
5255
+ ]
5256
+ },
5257
+ entry.id
5258
+ );
5259
+ };
5260
+ const renderSubcategoryBranch = (category, depth) => {
5261
+ var _a2, _b2, _c2, _d, _e;
5262
+ const label = (_a2 = category.displayName) != null ? _a2 : category.name;
5263
+ const entry = {
5264
+ id: category.id,
5265
+ label,
5266
+ eventCount: (_b2 = category.eventCount) != null ? _b2 : 0
5267
+ };
5268
+ const childCategories = (_c2 = childrenByParentId.get(category.id)) != null ? _c2 : [];
5269
+ const hasKnownChildren = childCategories.length > 0;
5270
+ const isExpanded = ((_d = categoryPath[depth]) == null ? void 0 : _d.id) === category.id && !collapsedCategoryIds.has(category.id);
5271
+ const isSelected = ((_e = categoryPath[depth]) == null ? void 0 : _e.id) === category.id && categoryPath.length === depth + 1;
5272
+ return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-1", children: [
5273
+ renderSubcategoryTreeRow({
5274
+ entry,
5275
+ isExpanded,
5276
+ isSelected,
5277
+ onClick: () => handleToggleSubcategory({
5278
+ entry,
5279
+ depth,
5280
+ hasKnownChildren,
5281
+ isExpanded,
5282
+ isSelected
5283
+ })
5284
+ }),
5285
+ isExpanded ? /* @__PURE__ */ jsx11("div", { className: "flex w-full flex-col gap-1 pl-4", children: childCategories.map(
5286
+ (childCategory) => renderSubcategoryBranch(childCategory, depth + 1)
5287
+ ) }) : null
5288
+ ] }, category.id);
5289
+ };
5290
+ const subcategoriesListContent = /* @__PURE__ */ jsxs8(Fragment3, { children: [
5291
+ /* @__PURE__ */ jsxs8(
5292
+ "button",
5293
+ {
5294
+ type: "button",
5295
+ "aria-current": !isDrilledIn ? "true" : void 0,
5296
+ className: !isDrilledIn ? subcategoryMenuSelectedRowClassName : subcategoryMenuIdleRowClassName,
5297
+ onClick: handleSelectAllCategories,
5298
+ children: [
5299
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: labels.eventList.subcategoriesAll }),
5300
+ /* @__PURE__ */ jsx11("span", { className: "text-agg-muted-foreground", children: totalTopLevelEventCount })
5301
+ ]
5302
+ }
5303
+ ),
5304
+ sortedTopLevelCategories.map((category) => renderSubcategoryBranch(category, 0))
5305
+ ] });
4856
5306
  if (shouldRenderLoadingState) {
4857
- return /* @__PURE__ */ jsx11(Skeleton, { view: "event-list", ariaLabel: labels.eventList.loading(title) });
5307
+ return /* @__PURE__ */ jsx11(
5308
+ Skeleton,
5309
+ {
5310
+ view: "event-list",
5311
+ ariaLabel: labels.eventList.loading(title),
5312
+ withSidebar: isDiscoveryFiltersEnabled
5313
+ }
5314
+ );
4858
5315
  }
4859
- return /* @__PURE__ */ jsxs8("section", { className: "agg-event-list flex w-full flex-col gap-5", children: [
5316
+ return /* @__PURE__ */ jsxs8("section", { className: "agg-event-list flex w-full flex-col gap-2 md:gap-5 t", children: [
4860
5317
  /* @__PURE__ */ jsxs8(
4861
5318
  "header",
4862
5319
  {
@@ -4868,62 +5325,146 @@ var EventList = ({
4868
5325
  ),
4869
5326
  children: [
4870
5327
  /* @__PURE__ */ jsx11("div", { ref: titleRef, className: "agg-event-list-title min-w-32", children: /* @__PURE__ */ jsx11(Typography, { as: "h2", variant: "title", className: "truncate first-letter:uppercase", children: title }) }),
4871
- /* @__PURE__ */ jsx11(
4872
- EventListTabs,
4873
- {
4874
- ariaLabel: labels.eventList.tabsAria(title),
4875
- className: "agg-event-list-tabs min-w-0 max-w-full",
4876
- activeTab: activeTabValue,
4877
- onTabChange: setActiveTabValue,
4878
- overflowBehavior: shouldUseSelectOverflow ? "select" : void 0
4879
- }
4880
- )
5328
+ /* @__PURE__ */ jsxs8("div", { className: "hidden min-w-0 max-w-full items-center gap-3 md:flex", children: [
5329
+ /* @__PURE__ */ jsx11(
5330
+ EventListTabs,
5331
+ {
5332
+ ariaLabel: labels.eventList.tabsAria(title),
5333
+ className: cn(
5334
+ "agg-event-list-tabs w-fit max-w-full flex-1",
5335
+ shouldUseSelectOverflow ? "min-w-[220px]" : "min-w-[400px]"
5336
+ ),
5337
+ activeTab: activeTabValue,
5338
+ onTabChange: setActiveTabValue,
5339
+ overflowBehavior: shouldUseSelectOverflow ? "select" : "scroll"
5340
+ }
5341
+ ),
5342
+ /* @__PURE__ */ jsx11(
5343
+ Select,
5344
+ {
5345
+ ariaLabel: labels.eventList.sortByLabel,
5346
+ className: "min-w-[200px] w-auto! shrink-0 min-h-10.5",
5347
+ value: selectedSort,
5348
+ onChange: setSelectedSort,
5349
+ items: sortItems,
5350
+ contentClassName: "py-2",
5351
+ triggerClassName: "rounded-agg-lg!"
5352
+ }
5353
+ )
5354
+ ] })
4881
5355
  ]
4882
5356
  }
4883
5357
  ),
4884
- /* @__PURE__ */ jsxs8("div", { className: cn("agg-event-list-grid", isPlaceholderData && "opacity-60", gridClassName), children: [
4885
- tileEvents.map((event) => /* @__PURE__ */ jsx11(
4886
- EventListItem,
4887
- {
4888
- event,
4889
- classNames: {
4890
- root: "agg-event-list-item w-full min-w-0 max-w-none"
4891
- },
4892
- onEventClick,
4893
- onMarketClick,
4894
- getMarketHref,
4895
- href: getEventHref == null ? void 0 : getEventHref(event),
4896
- marketStatus: MarketStatus2.open
4897
- },
4898
- event.id
4899
- )),
4900
- shouldRenderPaginationLoadingItems ? Array.from({ length: 6 }).map((_, index) => /* @__PURE__ */ jsx11(
4901
- EventListItem,
5358
+ /* @__PURE__ */ jsxs8("div", { className: "flex w-full gap-2 md:hidden mb-3", children: [
5359
+ /* @__PURE__ */ jsx11(
5360
+ EventListTabs,
4902
5361
  {
4903
- isLoading: true,
4904
- classNames: {
4905
- root: "agg-event-list-item w-full min-w-0 max-w-none"
4906
- }
4907
- },
4908
- `loading-${index}`
4909
- )) : null,
4910
- shouldRenderEmptyState ? /* @__PURE__ */ jsx11("div", { className: "col-span-full w-full", children: /* @__PURE__ */ jsx11(
4911
- StateMessage,
5362
+ ariaLabel: labels.eventList.tabsAria(title),
5363
+ className: "agg-event-list-tabs min-w-0 flex-1",
5364
+ activeTab: activeTabValue,
5365
+ onTabChange: setActiveTabValue,
5366
+ overflowBehavior: "select"
5367
+ }
5368
+ ),
5369
+ /* @__PURE__ */ jsx11(
5370
+ Select,
4912
5371
  {
4913
- title: labels.eventList.emptyTitle,
4914
- description: labels.eventList.emptyDescription,
4915
- icon: /* @__PURE__ */ jsx11(Icon, { name: "search-empty" })
5372
+ ariaLabel: labels.eventList.sortByLabel,
5373
+ className: "min-w-0 flex-1 md:min-w-[144px]",
5374
+ value: selectedSort,
5375
+ onChange: setSelectedSort,
5376
+ items: sortItems,
5377
+ contentClassName: "rounded-[12px] py-2 shadow-[0_8px_16px_rgba(0,0,0,0.1)]"
4916
5378
  }
4917
- ) }) : null,
4918
- isError ? /* @__PURE__ */ jsx11("div", { className: "col-span-full w-full", children: /* @__PURE__ */ jsx11(
4919
- StateMessage,
5379
+ ),
5380
+ shouldRenderSubcategoryFilters ? /* @__PURE__ */ jsx11(
5381
+ "button",
4920
5382
  {
4921
- title: labels.eventList.errorTitle,
4922
- description: labels.eventList.errorDescription,
4923
- icon: /* @__PURE__ */ jsx11(Icon, { name: "warning" })
5383
+ type: "button",
5384
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-[8px] border-2 border-agg-primary bg-agg-secondary text-agg-foreground cursor-pointer",
5385
+ "aria-label": labels.eventList.subcategoriesOpenAria,
5386
+ onClick: () => setIsSubcategoriesModalOpen(true),
5387
+ children: /* @__PURE__ */ jsx11(Icon, { name: "apps", size: "small", color: "currentColor" })
4924
5388
  }
4925
- ) }) : null
5389
+ ) : null
4926
5390
  ] }),
5391
+ /* @__PURE__ */ jsxs8(
5392
+ "div",
5393
+ {
5394
+ className: cn("flex w-full gap-6", shouldRenderSubcategoryFilters ? "md:items-start" : ""),
5395
+ children: [
5396
+ shouldRenderSubcategoryFilters ? /* @__PURE__ */ jsx11("aside", { className: "hidden w-[200px] shrink-0 md:block", children: /* @__PURE__ */ jsx11("div", { className: "flex w-full flex-col gap-1 rounded-[8px] border border-agg-separator bg-agg-secondary p-2", children: subcategoriesListContent }) }) : null,
5397
+ /* @__PURE__ */ jsxs8(
5398
+ "div",
5399
+ {
5400
+ className: cn(
5401
+ "agg-event-list-grid",
5402
+ "grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] md:grid-cols-[repeat(auto-fill,minmax(360px,1fr))] gap-4",
5403
+ "w-full",
5404
+ isPlaceholderData && "opacity-60",
5405
+ gridClassName
5406
+ ),
5407
+ children: [
5408
+ tileEvents.map((event) => /* @__PURE__ */ jsx11(
5409
+ EventListItem,
5410
+ {
5411
+ event,
5412
+ classNames: {
5413
+ root: "agg-event-list-item w-full min-w-0! max-w-none justify-between!"
5414
+ },
5415
+ onEventClick,
5416
+ onMarketClick,
5417
+ getMarketHref,
5418
+ href: getEventHref == null ? void 0 : getEventHref(event),
5419
+ marketStatus: MarketStatus2.open
5420
+ },
5421
+ event.id
5422
+ )),
5423
+ shouldRenderPaginationLoadingItems ? Array.from({ length: 6 }).map((_, index) => /* @__PURE__ */ jsx11(
5424
+ EventListItem,
5425
+ {
5426
+ isLoading: true,
5427
+ classNames: {
5428
+ root: "agg-event-list-item w-full min-w-0! max-w-none justify-between!"
5429
+ }
5430
+ },
5431
+ `loading-${index}`
5432
+ )) : null,
5433
+ shouldRenderEmptyState ? /* @__PURE__ */ jsx11("div", { className: "col-span-full w-full", children: /* @__PURE__ */ jsx11(
5434
+ StateMessage,
5435
+ {
5436
+ title: labels.eventList.emptyTitle,
5437
+ description: labels.eventList.emptyDescription,
5438
+ icon: /* @__PURE__ */ jsx11(Icon, { name: "search-empty" })
5439
+ }
5440
+ ) }) : null,
5441
+ isError ? /* @__PURE__ */ jsx11("div", { className: "col-span-full w-full", children: /* @__PURE__ */ jsx11(
5442
+ StateMessage,
5443
+ {
5444
+ title: labels.eventList.errorTitle,
5445
+ description: labels.eventList.errorDescription,
5446
+ icon: /* @__PURE__ */ jsx11(Icon, { name: "warning" })
5447
+ }
5448
+ ) }) : null
5449
+ ]
5450
+ }
5451
+ )
5452
+ ]
5453
+ }
5454
+ ),
5455
+ shouldRenderSubcategoryFilters ? /* @__PURE__ */ jsx11(Modal, { open: isSubcategoriesModalOpen, onOpenChange: setIsSubcategoriesModalOpen, children: /* @__PURE__ */ jsxs8(
5456
+ Modal.Container,
5457
+ {
5458
+ classNames: {
5459
+ content: "items-end p-0",
5460
+ container: "w-full max-w-none border-0 bg-transparent shadow-none"
5461
+ },
5462
+ children: [
5463
+ /* @__PURE__ */ jsx11(Dialog.Title, { className: "sr-only", children: labels.eventList.subcategoriesLabel }),
5464
+ /* @__PURE__ */ jsx11("div", { className: "w-full rounded-t-[12px] border border-agg-separator bg-agg-secondary p-4", children: /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-1", children: subcategoriesListContent }) })
5465
+ ]
5466
+ }
5467
+ ) }) : null,
4927
5468
  shouldPaginate && hasNextPage ? /* @__PURE__ */ jsx11("div", { ref: loadMoreRef, className: "agg-event-list-sentinel h-px w-full", "aria-hidden": true }) : null
4928
5469
  ] });
4929
5470
  };
@@ -4932,6 +5473,7 @@ EventList.displayName = "EventList";
4932
5473
  export {
4933
5474
  isErrorWithStatus,
4934
5475
  EventListItem,
5476
+ EventListItemV2,
4935
5477
  collectEligibleVenueOutcomes,
4936
5478
  collectEligibleVenueOutcomeIds,
4937
5479
  mergeVenueOutcomeOrderbooks,