@mohasinac/appkit 4.13.1 → 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
  4. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
  7. package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
  8. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
  9. package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
  10. package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
  11. package/dist/_internal/server/features/auctions/actions.js +6 -0
  12. package/dist/_internal/server/features/bundles/data.d.ts +16 -0
  13. package/dist/_internal/server/features/bundles/data.js +26 -0
  14. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  15. package/dist/_internal/server/features/bundles/index.js +1 -1
  16. package/dist/_internal/server/features/checkout/actions.js +6 -0
  17. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  19. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  20. package/dist/_internal/server/features/homepage/index.js +7 -1
  21. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  22. package/dist/_internal/server/features/products/data.js +15 -37
  23. package/dist/_internal/server/features/products/index.d.ts +1 -1
  24. package/dist/_internal/server/features/products/index.js +1 -1
  25. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  26. package/dist/_internal/server/features/products/list-public.js +266 -90
  27. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  28. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  29. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  30. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  31. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  32. package/dist/_internal/server/features/site-settings/index.js +1 -0
  33. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  34. package/dist/_internal/server/features/stores/adapters.js +91 -0
  35. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  36. package/dist/_internal/server/features/stores/index.js +1 -0
  37. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  38. package/dist/_internal/server/jobs/core/offerExpiry.js +45 -9
  39. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  40. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  41. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  42. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  43. package/dist/_internal/shared/actions/action-registry.js +15 -1
  44. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  45. package/dist/_internal/shared/checkout/lanes.js +26 -0
  46. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  47. package/dist/_internal/shared/features/auctions/config.js +27 -0
  48. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  49. package/dist/_internal/shared/features/cart/schema.d.ts +12 -12
  50. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  51. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  52. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  53. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  54. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  55. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  56. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  57. package/dist/_internal/shared/features/offers/config.js +59 -0
  58. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  59. package/dist/_internal/shared/features/products/schema.d.ts +32 -32
  60. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  61. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  62. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  63. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  64. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  65. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  66. package/dist/_internal/shared/history/history.d.ts +66 -0
  67. package/dist/_internal/shared/history/history.js +158 -0
  68. package/dist/_internal/shared/history/index.d.ts +4 -0
  69. package/dist/_internal/shared/history/index.js +2 -0
  70. package/dist/_internal/shared/history/types.d.ts +61 -0
  71. package/dist/_internal/shared/history/types.js +6 -0
  72. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  73. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  74. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  75. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  76. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  77. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  78. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  79. package/dist/_internal/shared/listing-types/availability.js +104 -0
  80. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  81. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  82. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  83. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  84. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  85. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  86. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  87. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  88. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  89. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  90. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  91. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  92. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  93. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  94. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  95. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  96. package/dist/client.d.ts +7 -5
  97. package/dist/client.js +2 -2
  98. package/dist/constants/api-endpoints.d.ts +6 -0
  99. package/dist/constants/api-endpoints.js +2 -0
  100. package/dist/constants/field-names.d.ts +49 -2
  101. package/dist/constants/field-names.js +53 -2
  102. package/dist/constants/table-keys.d.ts +17 -0
  103. package/dist/constants/table-keys.js +17 -0
  104. package/dist/errors/error-codes.d.ts +13 -1
  105. package/dist/errors/error-codes.js +13 -1
  106. package/dist/errors/error-display-map.js +12 -3
  107. package/dist/errors/messages.d.ts +10 -2
  108. package/dist/errors/messages.js +10 -2
  109. package/dist/features/about/components/AboutView.d.ts +3 -0
  110. package/dist/features/about/components/AboutView.js +3 -2
  111. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  112. package/dist/features/about/components/PolicyPageView.js +61 -23
  113. package/dist/features/about/components/PublicProfileView.js +5 -0
  114. package/dist/features/about/schemas/firestore.d.ts +3 -0
  115. package/dist/features/account/components/UserOrdersView.js +7 -0
  116. package/dist/features/account/components/index.d.ts +0 -2
  117. package/dist/features/account/components/index.js +0 -1
  118. package/dist/features/account/schemas/index.d.ts +2 -2
  119. package/dist/features/account/types/index.d.ts +21 -2
  120. package/dist/features/addresses/schemas/index.d.ts +4 -4
  121. package/dist/features/admin/components/AdminArtView.js +8 -3
  122. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  123. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  124. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  125. package/dist/features/admin/components/AdminLiveView.js +8 -3
  126. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  127. package/dist/features/admin/components/AdminOffersView.js +186 -0
  128. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  129. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  130. package/dist/features/admin/components/AdminProductsView.js +16 -29
  131. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  132. package/dist/features/admin/components/AdminSiteView.js +6 -2
  133. package/dist/features/admin/components/AdminStickersView.js +8 -3
  134. package/dist/features/admin/components/index.d.ts +2 -0
  135. package/dist/features/admin/components/index.js +1 -0
  136. package/dist/features/admin/constants/filter-tabs.d.ts +86 -0
  137. package/dist/features/admin/constants/filter-tabs.js +55 -0
  138. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  139. package/dist/features/admin/schemas/firestore.js +1 -0
  140. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  141. package/dist/features/auctions/actions/bid-actions.js +93 -20
  142. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  143. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  144. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  145. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  146. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  147. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  148. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  149. package/dist/features/auctions/repository/bid.repository.js +23 -0
  150. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  151. package/dist/features/auctions/schemas/index.d.ts +12 -12
  152. package/dist/features/auth/permissions/constants.d.ts +1 -1
  153. package/dist/features/auth/permissions/constants.js +10 -1
  154. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  155. package/dist/features/blog/schemas/index.d.ts +6 -6
  156. package/dist/features/cart/repository/cart.repository.js +1 -0
  157. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  158. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  159. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  160. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  161. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  162. package/dist/features/categories/components/BundleCollage.js +1 -1
  163. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  164. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  165. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  166. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  167. package/dist/features/categories/components/ConcernCard.js +7 -1
  168. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  169. package/dist/features/categories/schemas/index.d.ts +26 -26
  170. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  171. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  172. package/dist/features/events/components/AdminEventsView.js +15 -11
  173. package/dist/features/events/components/EventFilters.d.ts +9 -1
  174. package/dist/features/events/components/EventFilters.js +45 -21
  175. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  176. package/dist/features/events/components/EventsIndexListing.js +2 -2
  177. package/dist/features/events/components/EventsListPageView.js +13 -4
  178. package/dist/features/events/repository/events.repository.d.ts +17 -0
  179. package/dist/features/events/repository/events.repository.js +27 -0
  180. package/dist/features/events/schemas/firestore.d.ts +12 -0
  181. package/dist/features/events/schemas/firestore.js +12 -0
  182. package/dist/features/events/schemas/index.d.ts +50 -50
  183. package/dist/features/events/types/index.d.ts +11 -0
  184. package/dist/features/events/types/index.js +57 -1
  185. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  186. package/dist/features/faq/schemas/index.d.ts +8 -8
  187. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  188. package/dist/features/filters/FilterFacetSection.js +11 -3
  189. package/dist/features/grouped/schemas/index.d.ts +4 -4
  190. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  191. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  192. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  193. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  194. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  195. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  196. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  197. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  198. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  199. package/dist/features/homepage/schemas/index.d.ts +2 -2
  200. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  201. package/dist/features/layout/AppLayoutShell.js +15 -14
  202. package/dist/features/layout/BackToTop.d.ts +9 -0
  203. package/dist/features/layout/BackToTop.js +10 -1
  204. package/dist/features/layout/BottomActions.js +28 -25
  205. package/dist/features/layout/BottomChrome.d.ts +34 -0
  206. package/dist/features/layout/BottomChrome.js +55 -0
  207. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  208. package/dist/features/layout/BottomNavLayout.js +7 -1
  209. package/dist/features/layout/BottomNavbar.js +15 -2
  210. package/dist/features/layout/FooterLayout.js +11 -1
  211. package/dist/features/layout/NavbarLayout.js +1 -1
  212. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  213. package/dist/features/layout/TitleBarLayout.js +28 -5
  214. package/dist/features/layout/index.d.ts +2 -0
  215. package/dist/features/layout/index.js +1 -0
  216. package/dist/features/lottery/types/index.d.ts +3 -0
  217. package/dist/features/media/MediaPickerModal.js +5 -1
  218. package/dist/features/messages/schemas/index.d.ts +2 -2
  219. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  220. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  221. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  222. package/dist/features/orders/constants/order-scope.js +111 -0
  223. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  224. package/dist/features/orders/repository/orders.repository.js +116 -22
  225. package/dist/features/orders/schemas/firestore.d.ts +117 -0
  226. package/dist/features/orders/schemas/firestore.js +30 -0
  227. package/dist/features/orders/schemas/index.d.ts +18 -18
  228. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  229. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  230. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  231. package/dist/features/products/actions/product-actions.d.ts +14 -0
  232. package/dist/features/products/actions/product-actions.js +49 -11
  233. package/dist/features/products/api/route.d.ts +12 -0
  234. package/dist/features/products/api/route.js +42 -100
  235. package/dist/features/products/components/ArtStickersListView.js +2 -2
  236. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  237. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  238. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  239. package/dist/features/products/components/MakeOfferButton.js +48 -49
  240. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  241. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  242. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  243. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  244. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  245. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  246. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  247. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  248. package/dist/features/products/components/ProductForm.js +5 -2
  249. package/dist/features/products/components/ProductGrid.js +6 -1
  250. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  251. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  252. package/dist/features/products/components/index.d.ts +2 -2
  253. package/dist/features/products/components/index.js +1 -1
  254. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  255. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  256. package/dist/features/products/constants/action-defs.d.ts +2 -0
  257. package/dist/features/products/constants/action-defs.js +6 -0
  258. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  259. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  260. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  261. package/dist/features/products/hooks/useProducts.js +13 -2
  262. package/dist/features/products/repository/products.repository.d.ts +31 -2
  263. package/dist/features/products/repository/products.repository.js +53 -21
  264. package/dist/features/products/schemas/firestore.d.ts +17 -3
  265. package/dist/features/products/schemas/firestore.js +7 -2
  266. package/dist/features/products/schemas/index.d.ts +66 -66
  267. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  268. package/dist/features/products/types/index.d.ts +16 -1
  269. package/dist/features/promotions/schemas/index.d.ts +2 -2
  270. package/dist/features/reviews/schemas/index.d.ts +16 -16
  271. package/dist/features/search/schemas/index.d.ts +4 -4
  272. package/dist/features/seller/actions/offer-actions.js +25 -2
  273. package/dist/features/seller/components/SellerArtView.js +5 -0
  274. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  275. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  276. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  277. package/dist/features/seller/components/SellerLiveView.js +5 -0
  278. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  279. package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -18
  280. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  281. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  282. package/dist/features/seller/components/SellerProductShell.js +7 -1
  283. package/dist/features/seller/components/SellerProductsView.js +11 -8
  284. package/dist/features/seller/components/SellerStickersView.js +5 -0
  285. package/dist/features/seller/components/index.d.ts +0 -2
  286. package/dist/features/seller/components/index.js +0 -1
  287. package/dist/features/seller/repository/offer.repository.d.ts +73 -16
  288. package/dist/features/seller/repository/offer.repository.js +38 -3
  289. package/dist/features/seller/schemas/index.d.ts +2 -2
  290. package/dist/features/seller/schemas/offer-forms.d.ts +25 -0
  291. package/dist/features/seller/schemas/offer-forms.js +21 -0
  292. package/dist/features/seller/server.d.ts +0 -1
  293. package/dist/features/seller/server.js +0 -1
  294. package/dist/features/shell/SectionForm.d.ts +99 -0
  295. package/dist/features/shell/SectionForm.js +128 -0
  296. package/dist/features/shell/entity-form.d.ts +109 -0
  297. package/dist/features/shell/entity-form.js +96 -0
  298. package/dist/features/shell/index.d.ts +4 -0
  299. package/dist/features/shell/index.js +2 -0
  300. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  301. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  302. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  303. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  304. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  305. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  306. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  307. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  308. package/dist/features/stores/repository/store.repository.js +6 -0
  309. package/dist/features/stores/schemas/index.d.ts +6 -6
  310. package/dist/features/support/schemas/index.d.ts +4 -4
  311. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  312. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  313. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  314. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +105 -1
  315. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  316. package/dist/index.d.ts +13 -10
  317. package/dist/index.js +14 -9
  318. package/dist/next/routing/route-map.d.ts +7 -1
  319. package/dist/next/routing/route-map.js +5 -0
  320. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  321. package/dist/providers/db-firebase/base.repository.js +50 -1
  322. package/dist/providers/db-firebase/index.d.ts +1 -1
  323. package/dist/providers/db-firebase/index.js +1 -1
  324. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  325. package/dist/providers/db-firebase/sieve.js +52 -18
  326. package/dist/react/hooks/useUrlTable.js +10 -1
  327. package/dist/schemas/registry.d.ts +68 -68
  328. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  329. package/dist/seed/_helpers/category-forest.js +309 -0
  330. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  331. package/dist/seed/_helpers/category-tree.js +103 -0
  332. package/dist/seed/catalogue-seed-data.js +12 -12
  333. package/dist/seed/categories-seed-data.js +177 -162
  334. package/dist/seed/events-seed-data.js +61 -10
  335. package/dist/seed/homepage-sections-seed-data.js +5 -1
  336. package/dist/seed/offers-seed-data.js +32 -27
  337. package/dist/seed/products-art-seed-data.js +50 -12
  338. package/dist/seed/products-auctions-seed-data.js +97 -18
  339. package/dist/seed/products-classifieds-seed-data.js +80 -16
  340. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  341. package/dist/seed/products-live-items-seed-data.js +64 -13
  342. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  343. package/dist/seed/products-preorders-seed-data.js +52 -13
  344. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  345. package/dist/seed/products-standard-seed-data.js +99 -26
  346. package/dist/seed/products-stickers-seed-data.js +47 -12
  347. package/dist/seed/reviews-seed-data.js +6 -1
  348. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  349. package/dist/seed/site-settings-seed-data.js +38 -76
  350. package/dist/seed/stores-seed-data.js +71 -1
  351. package/dist/seed/users-seed-data.js +15 -5
  352. package/dist/server-entry.d.ts +1 -2
  353. package/dist/server-entry.js +4 -3
  354. package/dist/server.d.ts +4 -3
  355. package/dist/server.js +8 -4
  356. package/dist/styles.css +78 -12
  357. package/dist/tailwind-utilities.css +1 -1
  358. package/dist/ui/components/Button.style.css +19 -4
  359. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  360. package/dist/ui/components/CollapsibleSection.js +6 -2
  361. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  362. package/dist/ui/components/FilterChipGroup.js +21 -4
  363. package/dist/ui/components/HandMode.style.css +47 -0
  364. package/dist/ui/components/Layout.d.ts +9 -2
  365. package/dist/ui/components/Layout.js +1 -1
  366. package/dist/ui/components/ListingLayout.js +8 -8
  367. package/dist/ui/components/ListingLayout.style.css +8 -7
  368. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  369. package/dist/ui/components/StickyToolbar.js +62 -20
  370. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  371. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  372. package/dist/ui/components/index.style.css +3 -0
  373. package/dist/ui/forms/FormShell.d.ts +43 -13
  374. package/dist/ui/forms/FormShell.js +22 -11
  375. package/dist/ui/forms/index.d.ts +1 -1
  376. package/dist/ui/index.d.ts +1 -1
  377. package/package.json +1 -1
@@ -6,10 +6,12 @@ import { carouselRepository, faqsRepository, siteSettingsRepository } from "../.
6
6
  import { fetchLiveStats } from "../lib/live-stats";
7
7
  import { renderSection, AnnouncementBar } from "../lib/section-renderer";
8
8
  import { homepageSectionsRepository } from "../repository/homepage-sections.repository";
9
+ import { filterSectionsByFeatureFlags } from "../../../_internal/shared/features/homepage/section-gate";
9
10
  import { getFeaturedProducts, getFeaturedAuctions, getFeaturedPreOrders } from "../../products/actions/product-actions";
10
11
  import { listTopLevelCategories, listBrandCategories } from "../../categories/actions/category-actions";
11
12
  import { listFeaturedBundles } from "../../../_internal/server/features/bundles/data";
12
13
  import { listStores } from "../../stores/actions/store-query-actions";
14
+ import { toStoreListItem } from "../../../_internal/server/features/stores/adapters";
13
15
  import { getFeaturedBlogPosts } from "../../blog/actions/blog-actions";
14
16
  import { listPublicEvents } from "../../events/actions/event-actions";
15
17
  /**
@@ -31,10 +33,14 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
31
33
  const announcementMessage = siteSettings?.announcementBar?.message?.trim() ||
32
34
  "🎉 Up to 15% Off on Pokémon TCG this week — Use code SAVE15";
33
35
  const showAnnouncement = siteSettings?.announcementBar?.enabled ?? true;
34
- const [enabledSections, rawFaqItems] = await Promise.all([
36
+ const [allSections, rawFaqItems] = await Promise.all([
35
37
  homepageSectionsRepository.getEnabledSections().catch(() => []),
36
38
  faqsRepository.getHomepageFAQs().catch(() => []),
37
39
  ]);
40
+ // Respect the feature flags. This gate existed but lived inside a function
41
+ // with no call sites, so until 2026-08-24 turning off `auctions` hid auctions
42
+ // everywhere EXCEPT the homepage, which went on advertising them.
43
+ const enabledSections = filterSectionsByFeatureFlags(allSections, siteSettings?.featureFlags);
38
44
  // Collect live metric requests from all enabled stats sections
39
45
  const liveStatRequests = [];
40
46
  for (const section of enabledSections) {
@@ -74,9 +80,13 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
74
80
  // Determine which data-driven section types are active so we only fetch what's needed
75
81
  const activeTypes = new Set(orderedSections.map((s) => s.type));
76
82
  const [productsResult, auctionsResult, preOrdersResult, categoriesResult, brandsResult, bundlesResult, storesResult, blogResult, eventsResult,] = await Promise.all([
77
- activeTypes.has("products") ? getFeaturedProducts(12).catch(() => null) : null,
78
- activeTypes.has("auctions") ? getFeaturedAuctions(12).catch(() => null) : null,
79
- activeTypes.has("pre-orders") ? getFeaturedPreOrders(12).catch(() => null) : null,
83
+ // No `.catch(() => null)` on these three: they go through
84
+ // `listPublicProducts`, which already logs a failed query loudly and
85
+ // returns an empty result. Swallowing here would make a broken query
86
+ // indistinguishable from an empty catalogue (Root Cause #59).
87
+ activeTypes.has("products") ? getFeaturedProducts(12) : null,
88
+ activeTypes.has("auctions") ? getFeaturedAuctions(12) : null,
89
+ activeTypes.has("pre-orders") ? getFeaturedPreOrders(12) : null,
80
90
  activeTypes.has("categories") ? listTopLevelCategories(12).catch(() => null) : null,
81
91
  activeTypes.has("brands") ? listBrandCategories(12).catch(() => null) : null,
82
92
  activeTypes.has("featured-bundles") ? listFeaturedBundles(8).catch(() => null) : null,
@@ -104,8 +114,12 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
104
114
  ? brandsResult
105
115
  : undefined,
106
116
  bundles: bundlesResult?.length ? bundlesResult : undefined,
117
+ // Projected, NOT cast. FeaturedStoresSection is a Client Component and this
118
+ // value becomes its `initialItems`, so a raw StoreDocument here would be
119
+ // serialised into the homepage's public HTML — secrets included. Every
120
+ // other cast in this object maps a document that carries no secrets.
107
121
  stores: storesResult?.items?.length
108
- ? storesResult.items
122
+ ? storesResult.items.map((s) => toStoreListItem(s))
109
123
  : undefined,
110
124
  blog: blogResult?.length
111
125
  ? blogResult
@@ -115,5 +129,5 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
115
129
  : undefined,
116
130
  };
117
131
  const carouselSlides = toCarouselSlides(slides);
118
- return (_jsx(Main, { children: _jsxs(Div, { className: "relative", children: [showAnnouncement ? (_jsx(AnnouncementBar, { overlay: true, message: announcementMessage, onDismiss: onBannerDismiss })) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, carouselSlides, liveStats, sectionData))] }) }));
132
+ return (_jsx(Main, { children: _jsxs(Div, { className: "relative", children: [showAnnouncement ? (_jsx(AnnouncementBar, { overlay: true, message: announcementMessage, link: siteSettings?.announcementBar?.link, onDismiss: onBannerDismiss })) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, carouselSlides, liveStats, sectionData))] }) }));
119
133
  }
@@ -0,0 +1,16 @@
1
+ /** How many cards a homepage carousel wants before it stops back-filling. */
2
+ export declare const MIN_HOMEPAGE_COUNT = 12;
3
+ export interface HomepageListingQuery {
4
+ listingType?: string;
5
+ brand?: string;
6
+ featured?: boolean;
7
+ isPromoted?: boolean;
8
+ sorts?: string;
9
+ pageSize: number;
10
+ }
11
+ /**
12
+ * Always sends `availability=available`. The API treats an ABSENT value as
13
+ * "all" so that non-browse callers are unaffected — which means omitting it
14
+ * here is precisely how a homepage strip starts showing dead listings again.
15
+ */
16
+ export declare function buildHomepageListingQuery(input: HomepageListingQuery): string;
@@ -0,0 +1,45 @@
1
+ /*
2
+ * WHY: The three homepage listing hooks each hand-wrote a pre-URL-encoded
3
+ * `filters=` Sieve string — `listingType%3D%3Dauction%2Cstatus%3D%3D…`.
4
+ * Unreadable, unreviewable, and each one silently omitted any liveness
5
+ * bound, so all three showed sold-out / ended / closed rows. Worse, they
6
+ * run AFTER the SSR paint and replace it, so a fix to the server half
7
+ * alone would have been undone 30 seconds later (Root Cause #30).
8
+ *
9
+ * WHAT: Build the query string from the same TYPED params
10
+ * `parsePublicProductParams` reads on the other side, so a homepage
11
+ * strip asks precisely what its SSR counterpart asked.
12
+ *
13
+ * EXPORTS: buildHomepageListingQuery, HomepageListingQuery, MIN_HOMEPAGE_COUNT
14
+ *
15
+ * @tag domain:homepage
16
+ * @tag layer:hook
17
+ * @tag pattern:none
18
+ * @tag access:client
19
+ * @tag consumers:useFeaturedAuctions,useFeaturedPreOrders,useFeaturedProducts
20
+ * @tag sideEffects:none
21
+ */
22
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
23
+ /** How many cards a homepage carousel wants before it stops back-filling. */
24
+ export const MIN_HOMEPAGE_COUNT = 12;
25
+ /**
26
+ * Always sends `availability=available`. The API treats an ABSENT value as
27
+ * "all" so that non-browse callers are unaffected — which means omitting it
28
+ * here is precisely how a homepage strip starts showing dead listings again.
29
+ */
30
+ export function buildHomepageListingQuery(input) {
31
+ const sp = new URLSearchParams();
32
+ sp.set("availability", AVAILABILITY_VALUES.AVAILABLE);
33
+ if (input.listingType)
34
+ sp.set("listingType", input.listingType);
35
+ if (input.brand)
36
+ sp.set("brand", input.brand);
37
+ if (input.featured)
38
+ sp.set("featured", "true");
39
+ if (input.isPromoted)
40
+ sp.set("isPromoted", "true");
41
+ if (input.sorts)
42
+ sp.set("sorts", input.sorts);
43
+ sp.set("pageSize", String(input.pageSize));
44
+ return sp.toString();
45
+ }
@@ -1,4 +1,14 @@
1
1
  import type { ProductItem } from "../../products/types";
2
+ /**
3
+ * The soonest-ending LIVE auctions, for the homepage strip.
4
+ *
5
+ * This runs 30s after the SSR paint and REPLACES it, so it has to ask the
6
+ * server exactly what `getFeaturedAuctions` asked. It did not: both queries
7
+ * here were `listingType==auction,status==published` with no end-date bound,
8
+ * hand-written as a raw pre-encoded `filters=` string. So even once the SSR
9
+ * half was fixed, the strip would have reverted to showing expired auctions
10
+ * half a minute later (Root Cause #30).
11
+ */
2
12
  export declare function useFeaturedAuctions(options?: {
3
13
  filterByBrand?: string;
4
14
  initialData?: ProductItem[];
@@ -1,21 +1,41 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { apiClient } from "../../../http";
3
3
  import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints";
4
- const MIN_COUNT = 12;
4
+ import { buildHomepageListingQuery, MIN_HOMEPAGE_COUNT } from "./homepage-query";
5
+ import { sortBy } from "../../../constants/sort";
6
+ import { PRODUCT_FIELDS } from "../../../constants/field-names";
7
+ /**
8
+ * The soonest-ending LIVE auctions, for the homepage strip.
9
+ *
10
+ * This runs 30s after the SSR paint and REPLACES it, so it has to ask the
11
+ * server exactly what `getFeaturedAuctions` asked. It did not: both queries
12
+ * here were `listingType==auction,status==published` with no end-date bound,
13
+ * hand-written as a raw pre-encoded `filters=` string. So even once the SSR
14
+ * half was fixed, the strip would have reverted to showing expired auctions
15
+ * half a minute later (Root Cause #30).
16
+ */
5
17
  export function useFeaturedAuctions(options) {
6
- const brandFilter = options?.filterByBrand
7
- ? `%2Cbrand%3D%3D${encodeURIComponent(options.filterByBrand)}`
8
- : "";
9
18
  return useQuery({
10
19
  queryKey: ["auctions", "featured", options?.filterByBrand ?? "all"],
11
20
  initialData: options?.initialData,
12
21
  queryFn: async () => {
13
- const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dauction%2Cstatus%3D%3Dpublished%2CisPromoted%3D%3Dtrue${brandFilter}&pageSize=18`);
22
+ const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
23
+ listingType: "auction",
24
+ brand: options?.filterByBrand,
25
+ isPromoted: true,
26
+ sorts: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC"),
27
+ pageSize: 18,
28
+ })}`);
14
29
  const promoted = promotedRes?.items ?? [];
15
- if (promoted.length >= MIN_COUNT)
30
+ if (promoted.length >= MIN_HOMEPAGE_COUNT)
16
31
  return promoted;
17
- const remaining = MIN_COUNT - promoted.length;
18
- const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dauction%2Cstatus%3D%3Dpublished${brandFilter}&sorts=-createdAt&pageSize=${remaining + promoted.length}`);
32
+ const remaining = MIN_HOMEPAGE_COUNT - promoted.length;
33
+ const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
34
+ listingType: "auction",
35
+ brand: options?.filterByBrand,
36
+ sorts: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC"),
37
+ pageSize: remaining + promoted.length,
38
+ })}`);
19
39
  const latest = latestRes?.items ?? [];
20
40
  const existingIds = new Set(promoted.map((a) => a.id));
21
41
  const filler = latest
@@ -1,21 +1,30 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { apiClient } from "../../../http";
3
3
  import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints";
4
- const MIN_COUNT = 12;
4
+ import { buildHomepageListingQuery, MIN_HOMEPAGE_COUNT } from "./homepage-query";
5
+ import { sortBy } from "../../../constants/sort";
6
+ import { PRODUCT_FIELDS } from "../../../constants/field-names";
5
7
  export function useFeaturedPreOrders(options) {
6
- const brandFilter = options?.filterByBrand
7
- ? `%2Cbrand%3D%3D${encodeURIComponent(options.filterByBrand)}`
8
- : "";
9
8
  return useQuery({
10
9
  queryKey: ["pre-orders", "featured", options?.filterByBrand ?? "all"],
11
10
  initialData: options?.initialData,
12
11
  queryFn: async () => {
13
- const featuredRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dpre-order%2Cstatus%3D%3Dpublished${brandFilter}&sorts=preOrderDeliveryDate&pageSize=6`);
12
+ const featuredRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
13
+ listingType: "pre-order",
14
+ brand: options?.filterByBrand,
15
+ sorts: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE, "ASC"),
16
+ pageSize: 6,
17
+ })}`);
14
18
  const featured = featuredRes?.items ?? [];
15
- if (featured.length >= MIN_COUNT)
19
+ if (featured.length >= MIN_HOMEPAGE_COUNT)
16
20
  return featured;
17
- const remaining = MIN_COUNT - featured.length;
18
- const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dpre-order%2Cstatus%3D%3Dpublished${brandFilter}&sorts=-createdAt&pageSize=${remaining + featured.length}`);
21
+ const remaining = MIN_HOMEPAGE_COUNT - featured.length;
22
+ const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
23
+ listingType: "pre-order",
24
+ brand: options?.filterByBrand,
25
+ sorts: sortBy(PRODUCT_FIELDS.CREATED_AT, "DESC"),
26
+ pageSize: remaining + featured.length,
27
+ })}`);
19
28
  const latest = latestRes?.items ?? [];
20
29
  const existingIds = new Set(featured.map((p) => p.id));
21
30
  const filler = latest
@@ -1,20 +1,27 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { apiClient } from "../../../http";
3
3
  import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints";
4
- const MIN_COUNT = 12;
4
+ import { buildHomepageListingQuery, MIN_HOMEPAGE_COUNT } from "./homepage-query";
5
+ import { sortBy } from "../../../constants/sort";
6
+ import { PRODUCT_FIELDS } from "../../../constants/field-names";
5
7
  export function useFeaturedProducts(options) {
6
- const brandFilter = options?.filterByBrand
7
- ? `%2Cbrand%3D%3D${encodeURIComponent(options.filterByBrand)}`
8
- : "";
9
8
  return useQuery({
10
9
  queryKey: ["products", "featured", options?.filterByBrand ?? "all"],
11
10
  queryFn: async () => {
12
- const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=isPromoted%3D%3Dtrue%2Cstatus%3D%3Dpublished${brandFilter}&pageSize=18`);
11
+ const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
12
+ brand: options?.filterByBrand,
13
+ isPromoted: true,
14
+ pageSize: 18,
15
+ })}`);
13
16
  const promoted = promotedRes?.items ?? [];
14
- if (promoted.length >= MIN_COUNT)
17
+ if (promoted.length >= MIN_HOMEPAGE_COUNT)
15
18
  return promotedRes;
16
- const remaining = MIN_COUNT - promoted.length;
17
- const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=status%3D%3Dpublished${brandFilter}&sorts=-createdAt&pageSize=${remaining + promoted.length}`);
19
+ const remaining = MIN_HOMEPAGE_COUNT - promoted.length;
20
+ const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?${buildHomepageListingQuery({
21
+ brand: options?.filterByBrand,
22
+ sorts: sortBy(PRODUCT_FIELDS.CREATED_AT, "DESC"),
23
+ pageSize: remaining + promoted.length,
24
+ })}`);
18
25
  const latest = latestRes?.items ?? [];
19
26
  const existingIds = new Set(promoted.map((p) => p.id));
20
27
  const filler = latest
@@ -720,6 +720,7 @@ export declare const carouselSlideFirestoreSchema: z.ZodObject<{
720
720
  }, z.ZodTypeAny, "passthrough">;
721
721
  active: boolean;
722
722
  order: number;
723
+ createdBy: string;
723
724
  cards: {
724
725
  id: string;
725
726
  background: {
@@ -762,7 +763,6 @@ export declare const carouselSlideFirestoreSchema: z.ZodObject<{
762
763
  isButtonOnly?: boolean | undefined;
763
764
  gridCol?: 3 | 1 | 2 | undefined;
764
765
  }[];
765
- createdBy: string;
766
766
  link?: {
767
767
  url: string;
768
768
  openInNewTab: boolean;
@@ -829,6 +829,7 @@ export declare const carouselSlideFirestoreSchema: z.ZodObject<{
829
829
  }, z.ZodTypeAny, "passthrough">;
830
830
  active: boolean;
831
831
  order: number;
832
+ createdBy: string;
832
833
  cards: {
833
834
  id: string;
834
835
  background: {
@@ -871,7 +872,6 @@ export declare const carouselSlideFirestoreSchema: z.ZodObject<{
871
872
  isButtonOnly?: boolean | undefined;
872
873
  gridCol?: 3 | 1 | 2 | undefined;
873
874
  }[];
874
- createdBy: string;
875
875
  link?: {
876
876
  url: string;
877
877
  openInNewTab: boolean;
@@ -88,6 +88,16 @@ export interface AppLayoutShellProps {
88
88
  logout?: string;
89
89
  };
90
90
  eventBannerSlot?: React.ReactNode;
91
+ /**
92
+ * Render the public mobile tab bar. Defaults to `true`.
93
+ *
94
+ * Set `false` on routes that mount their own bottom nav — `DashboardLayoutClient`
95
+ * renders a section-scoped one for /admin, /store and /user. Only ONE bottom nav
96
+ * may be mounted at a time: both are `fixed bottom-0` at the same z-index, so two
97
+ * stack on the identical pixels, and both publish `--bottom-nav-height`, which
98
+ * every offset above them reads.
99
+ */
100
+ showBottomNav?: boolean;
91
101
  /** When provided, renders a tour-start icon button in the title bar. Null in Patch 1. */
92
102
  onTourStart?: () => void;
93
103
  /**
@@ -115,4 +125,4 @@ export interface AppLayoutShellProps {
115
125
  };
116
126
  };
117
127
  }
118
- export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, onTourStart, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
128
+ export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, showBottomNav, onTourStart, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
@@ -18,8 +18,8 @@ import { useAuth } from "../../react/contexts/SessionContext";
18
18
  import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset";
19
19
  import { isBuyerUser } from "../auth/role-predicates";
20
20
  import { NavbarWithSettings } from "./NavbarWithSettings";
21
- import { useBottomActionsContext } from "./BottomActionsContext";
22
21
  import BottomActions from "./BottomActions";
22
+ import { BottomChrome } from "./BottomChrome";
23
23
  import { AutoBreadcrumbs } from "./AutoBreadcrumbs";
24
24
  import { BottomNavbar } from "./BottomNavbar";
25
25
  import { FooterLayout } from "./FooterLayout";
@@ -127,7 +127,7 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
127
127
  ].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.itemsSold }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, hasActive: !!activeDashboardLink, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: activeDashboardLink?.href === adminHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === adminHref ? "page" : undefined, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: activeDashboardLink?.href === resolvedStoreHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === resolvedStoreHref ? "page" : undefined, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, activeHref: activeHref }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-50 hover:text-primary-800", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
128
128
  }
129
129
  // ─── Main component ────────────────────────────────────────────────────────────
130
- export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, onTourStart, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
130
+ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, showBottomNav = true, onTourStart, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
131
131
  // QueryClientProvider used to be created here so AppLayoutShell could stand
132
132
  // alone. The real cause of the 2026-06-10/11 "No QueryClient set" prod crash
133
133
  // turned out to be a peer-dep duplicate of @tanstack/query-core under
@@ -140,7 +140,6 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
140
140
  useEffect(() => { setSidebarOpen(false); }, [pathname]);
141
141
  const { theme, toggleTheme, activeTheme } = useTheme();
142
142
  const { closeNav: closeDashboardNav, hasNav: hasDashboardNav, toggleNav: toggleDashboardNav } = useDashboardNav();
143
- const { state: bottomActionsState } = useBottomActionsContext();
144
143
  const { user: authUser } = useAuth();
145
144
  const headerRef = useRef(null);
146
145
  useEffect(() => {
@@ -170,16 +169,12 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
170
169
  const handleBeforeDashboardNavToggle = useCallback(() => {
171
170
  setSidebarOpen(false);
172
171
  }, []);
173
- // Must stay in sync with BottomActions' own `isVisible` — `secondaryLabel` was missing
174
- // here, so an auction bar shown by its countdown alone got no bottom clearance.
175
- const hasBottomActions = bottomActionsState.actions.length > 0 ||
176
- !!(bottomActionsState.bulk && bottomActionsState.bulk.selectedCount > 0) ||
177
- !!bottomActionsState.infoLabel ||
178
- !!bottomActionsState.secondaryLabel;
179
- /** The bar overlays desktop content too once a page opts in, so reserve space there. */
180
- const hasDesktopBottomActions = hasBottomActions &&
181
- (bottomActionsState.desktop === "always" ||
182
- bottomActionsState.desktop === "after-scroll");
172
+ // The shell used to mirror BottomActions' `isVisible` here to pick a margin
173
+ // for <Main>, and a comment warned it had to be kept in sync by hand (it had
174
+ // already drifted once — `secondaryLabel` was missing, so an auction bar shown
175
+ // by its countdown alone got no clearance). That mirror is gone: BottomChrome
176
+ // measures the real tier and publishes --bottom-chrome-height, and the footer
177
+ // reserves space from that. Nothing needs to guess any more.
183
178
  const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
184
179
  const normalizedLightBackground = {
185
180
  type: lightBackground.type,
@@ -199,5 +194,11 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
199
194
  opacity: darkBackground.overlay?.opacity ?? 0,
200
195
  },
201
196
  };
202
- return (_jsx(_Fragment, { children: _jsxs(Stack, { className: "min-h-screen w-full overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onTourStart: onTourStart, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot), eventBannerSlot] }), _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { layout: "flex", className: "relative w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 flex flex-col ${hasBottomActions ? "mb-28" : "mb-16"} ${hasDesktopBottomActions ? "lg:mb-24" : "lg:mb-0"}`, children: _jsx(Div, { padding: "y-lg", className: `flex-1 ${contentClassName ?? "mx-auto w-full max-w-screen-xl px-[var(--appkit-space-5)] md:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)]"}`, children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, userId: userId, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, wishlistHref: wishlistHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
197
+ return (_jsx(_Fragment, { children: _jsxs(Stack, { className: "min-h-screen w-full overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onTourStart: onTourStart, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot), eventBannerSlot] }), _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { layout: "flex", className: "relative w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content",
198
+ // No bottom-chrome clearance here on purpose. FooterLayout is a
199
+ // SIBLING rendered after <Main>, so a margin here only gaps
200
+ // main-to-footer — it never clears the bottom of the page, which is
201
+ // what the fixed nav + tier actually overlap. That reservation now
202
+ // lives on the footer's own padding-bottom.
203
+ className: "w-full flex-1 flex flex-col", children: _jsx(Div, { padding: "y-lg", className: `flex-1 ${contentClassName ?? "mx-auto w-full max-w-screen-xl px-[var(--appkit-space-5)] md:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)]"}`, children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomChrome, { children: _jsx(BottomActions, {}) }), showBottomNav && (_jsx(BottomNavbar, { user: user, userId: userId, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, wishlistHref: wishlistHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar })), _jsx(UnsavedChangesModal, {})] }) }));
203
204
  }
@@ -6,6 +6,15 @@ export interface BackToTopProps {
6
6
  }
7
7
  /**
8
8
  * BackToTop — floating scroll-to-top button.
9
+ *
10
+ * Sits at the top of the three-tier bottom edge: the nav bar is the floor
11
+ * (`--bottom-nav-height`), everything non-nav is one measured tier in between
12
+ * (`--bottom-chrome-height`, published by `<BottomChrome>`), and this floats a
13
+ * gutter above the sum. The offset is deliberately expressed against the tier
14
+ * rather than against any individual bar, so a CTA row changing height, a bar
15
+ * revealing on desktop scroll, or a new bottom bar added later all just work —
16
+ * this `calc()` never has to grow again.
17
+ *
9
18
  * Appears after the page has scrolled past `threshold` pixels. A small
10
19
  * dismiss control hides it for the current page view only — dismissal is
11
20
  * plain in-memory component state, reset on every route change (via
@@ -8,6 +8,15 @@ import { Button, Div } from "../../ui";
8
8
  import { useHandMode } from "../../_internal/client/hand-mode";
9
9
  /**
10
10
  * BackToTop — floating scroll-to-top button.
11
+ *
12
+ * Sits at the top of the three-tier bottom edge: the nav bar is the floor
13
+ * (`--bottom-nav-height`), everything non-nav is one measured tier in between
14
+ * (`--bottom-chrome-height`, published by `<BottomChrome>`), and this floats a
15
+ * gutter above the sum. The offset is deliberately expressed against the tier
16
+ * rather than against any individual bar, so a CTA row changing height, a bar
17
+ * revealing on desktop scroll, or a new bottom bar added later all just work —
18
+ * this `calc()` never has to grow again.
19
+ *
11
20
  * Appears after the page has scrolled past `threshold` pixels. A small
12
21
  * dismiss control hides it for the current page view only — dismissal is
13
22
  * plain in-memory component state, reset on every route change (via
@@ -40,7 +49,7 @@ export function BackToTop({ threshold = 400, ariaLabel = "Back to top", classNam
40
49
  const handleDismiss = () => {
41
50
  setDismissed(true);
42
51
  };
43
- return (_jsxs(Div, { layout: "flex", align: "center", gap: "1", className: `fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem)+var(--appkit-space-4))] ${hand === "left" ? "left-4" : "right-4"} z-[var(--appkit-z-back-to-top)] ${className}`, children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: "flex items-center justify-center w-10 h-10 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] dark:hover:bg-slate-700 transition-colors", children: _jsx(ChevronUp, { className: "w-5 h-5", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": "Hide back-to-top button", onClick: handleDismiss, className: "flex items-center justify-center w-6 h-6 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text)] transition-colors", children: _jsx(X, { className: "w-3 h-3", "aria-hidden": "true" }) })] }));
52
+ return (_jsxs(Div, { layout: "flex", align: "center", gap: "1", className: `fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem)+var(--bottom-chrome-height,0px)+var(--appkit-space-4))] transition-[bottom] duration-300 ease-out ${hand === "left" ? "left-4" : "right-4"} z-[var(--appkit-z-back-to-top)] ${className}`, children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: "flex items-center justify-center w-10 h-10 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] dark:hover:bg-slate-700 transition-colors", children: _jsx(ChevronUp, { className: "w-5 h-5", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": "Hide back-to-top button", onClick: handleDismiss, className: "flex items-center justify-center w-6 h-6 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text)] transition-colors", children: _jsx(X, { className: "w-3 h-3", "aria-hidden": "true" }) })] }));
44
53
  }
45
54
  /** SkipToMain — visually hidden link that becomes visible on focus for keyboard users. */
46
55
  export function SkipToMain({ mainId = "main-content" }) {
@@ -36,7 +36,6 @@ import { useClickOutside } from "../../react";
36
36
  import { Div, Row, Span, Text, Button } from "../../ui";
37
37
  // Token values inlined from @mohasinac/appkit/tokens
38
38
  const BOTTOM_NAV_BG = "bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)]";
39
- const Z_BOTTOM_ACTIONS = "z-[var(--appkit-z-bottom-nav)]";
40
39
  const BOTTOM_NAV_HEIGHT = "h-14";
41
40
  const FLEX_CENTER = "flex items-center justify-center";
42
41
  const CLS_COUNT_BADGE = "bg-error-solid text-error-on-solid";
@@ -173,31 +172,35 @@ export default function BottomActions() {
173
172
  setPickerOpen(false);
174
173
  };
175
174
  return (_jsxs(Div, { ref: containerRef, role: "toolbar", "aria-label": isBulkMode ? "Bulk actions" : "Page actions", "aria-hidden": !isVisible, className: [
176
- "fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] left-0 right-0",
177
- // `--bottom-nav-height` resolves to 0px above lg, so the same offset lands the
178
- // bar on the viewport bottom on desktop with no extra positioning.
175
+ // Position, z-index and the offset above the nav all belong to
176
+ // <BottomChrome>, the tier container this renders inside. `relative` is
177
+ // kept because the two panels below are `absolute bottom-full` against it.
178
+ "relative pointer-events-auto",
179
+ // Desktop opt-in stays per-bar. `lg:hidden` is display:none, so a bar the
180
+ // page hasn't opted into contributes nothing to the tier's height.
179
181
  showOnDesktop ? "" : "lg:hidden",
180
- Z_BOTTOM_ACTIONS,
181
- BOTTOM_NAV_BG,
182
- "shadow-[0_-4px_20px_rgba(0,0,0,0.08)] dark:shadow-[0_-4px_20px_rgba(0,0,0,0.30)]",
183
- "will-change-transform transition-transform duration-300 ease-out",
184
- isVisible ? "translate-y-0" : "translate-y-full pointer-events-none",
185
182
  ]
186
183
  .filter(Boolean)
187
- .join(" "), "data-section": "bottomactions-div-401", children: [isBulkMode && (_jsx(BulkPickerPanel, { bulkActions: bulkActions, selectedActionId: selectedActionId, pickerOpen: pickerOpen, onSelect: (id) => { setSelectedActionId(id); setPickerOpen(false); } })), hasInfoPanel && !isBulkMode && (_jsx(InfoPanel, { id: infoPanelId, open: infoOpen, children: infoPanel })), isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-100 active:bg-primary-200 dark:bg-primary-950/30 dark:hover:bg-primary-900/50 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
188
- ? `${bulk.selectedCount} ${bulk.noun}`
189
- : `${bulk.selectedCount} selected` })] }), bulkActions.length > 0 && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setPickerOpen((o) => !o), "aria-haspopup": "listbox", "aria-expanded": pickerOpen, className: [
190
- "flex-1 min-w-0 h-10 flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-3)] rounded-lg border text-[length:var(--appkit-text-sm)] font-medium transition-colors",
191
- "bg-zinc-50 hover:bg-[var(--appkit-color-surface)] active:bg-zinc-200 bg-[var(--appkit-color-surface-input)] dark:hover:bg-slate-700/60",
192
- "border-[var(--appkit-color-border)]",
193
- selectedAction?.variant === "danger"
194
- ? "text-error"
195
- : "text-[var(--appkit-color-text)]",
196
- ]
197
- .filter(Boolean)
198
- .join(" "), children: [selectedAction?.icon && (_jsx(Span, { className: `flex-shrink-0 w-4 h-4 ${FLEX_CENTER}`, "aria-hidden": "true", children: selectedAction.icon })), _jsx(Span, { className: "flex-1 truncate leading-none", align: "start", children: selectedAction?.label ?? "Bulk actions" }), pickerOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 flex-shrink-0 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 flex-shrink-0 text-zinc-400", "aria-hidden": "true" }))] })), bulkActions.length > 0 && (_jsx(Button, { type: "button", variant: selectedAction?.variant ?? "primary", size: "sm", isLoading: selectedAction?.loading, disabled: !selectedActionId ||
199
- selectedAction?.disabled ||
200
- selectedAction?.loading, onClick: handleApply, className: "h-10 flex-shrink-0", children: _jsx(Span, { className: "leading-none", children: "Apply" }) }))] })) : (
201
- /* Page mode — action buttons inline ------------------------------ */
202
- _jsx(PageActionsRow, { pageActions: pageActions, dispatchAction: dispatchAction })) })] }));
184
+ .join(" "), "data-section": "bottomactions-div-401", children: [isBulkMode && (_jsx(BulkPickerPanel, { bulkActions: bulkActions, selectedActionId: selectedActionId, pickerOpen: pickerOpen, onSelect: (id) => { setSelectedActionId(id); setPickerOpen(false); } })), hasInfoPanel && !isBulkMode && (_jsx(InfoPanel, { id: infoPanelId, open: infoOpen, children: infoPanel })), _jsx(Div, { className: `grid transition-[grid-template-rows] duration-300 ease-out ${isVisible ? "grid-rows-[1fr]" : "grid-rows-[0fr]"}`, children: _jsx(Div, { overflow: "hidden", className: "min-h-0", children: _jsxs(Div, { className: [
185
+ BOTTOM_NAV_BG,
186
+ "shadow-[0_-4px_20px_rgba(0,0,0,0.08)] dark:shadow-[0_-4px_20px_rgba(0,0,0,0.30)]",
187
+ isVisible ? "" : "pointer-events-none",
188
+ ]
189
+ .filter(Boolean)
190
+ .join(" "), children: [isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-100 active:bg-primary-200 dark:bg-primary-950/30 dark:hover:bg-primary-900/50 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
191
+ ? `${bulk.selectedCount} ${bulk.noun}`
192
+ : `${bulk.selectedCount} selected` })] }), bulkActions.length > 0 && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setPickerOpen((o) => !o), "aria-haspopup": "listbox", "aria-expanded": pickerOpen, className: [
193
+ "flex-1 min-w-0 h-10 flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-3)] rounded-lg border text-[length:var(--appkit-text-sm)] font-medium transition-colors",
194
+ "bg-zinc-50 hover:bg-[var(--appkit-color-surface)] active:bg-zinc-200 bg-[var(--appkit-color-surface-input)] dark:hover:bg-slate-700/60",
195
+ "border-[var(--appkit-color-border)]",
196
+ selectedAction?.variant === "danger"
197
+ ? "text-error"
198
+ : "text-[var(--appkit-color-text)]",
199
+ ]
200
+ .filter(Boolean)
201
+ .join(" "), children: [selectedAction?.icon && (_jsx(Span, { className: `flex-shrink-0 w-4 h-4 ${FLEX_CENTER}`, "aria-hidden": "true", children: selectedAction.icon })), _jsx(Span, { className: "flex-1 truncate leading-none", align: "start", children: selectedAction?.label ?? "Bulk actions" }), pickerOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 flex-shrink-0 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 flex-shrink-0 text-zinc-400", "aria-hidden": "true" }))] })), bulkActions.length > 0 && (_jsx(Button, { type: "button", variant: selectedAction?.variant ?? "primary", size: "sm", isLoading: selectedAction?.loading, disabled: !selectedActionId ||
202
+ selectedAction?.disabled ||
203
+ selectedAction?.loading, onClick: handleApply, className: "h-10 flex-shrink-0", children: _jsx(Span, { className: "leading-none", children: "Apply" }) }))] })) : (
204
+ /* Page mode — action buttons inline ------------------------------ */
205
+ _jsx(PageActionsRow, { pageActions: pageActions, dispatchAction: dispatchAction })) })] }) }) })] }));
203
206
  }
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ export interface BottomChromeProps {
3
+ children: React.ReactNode;
4
+ }
5
+ /**
6
+ * BottomChrome — the middle tier of the bottom edge.
7
+ *
8
+ * The bottom edge is three tiers: the nav bar is the floor, `BackToTop` is the
9
+ * ceiling, and *everything else* lives in here. This container is what makes
10
+ * that middle tier a single thing with a single height:
11
+ *
12
+ * --keyboard-inset-height useVisualViewportInset (the on-screen keyboard)
13
+ * --bottom-nav-height BottomNavLayout (the tab bar)
14
+ * --bottom-chrome-height THIS COMPONENT (CTA row, pagination, …)
15
+ * ↑ BackToTop floats above the sum of all three
16
+ *
17
+ * Because it is a flex column, its own `offsetHeight` IS the sum of whatever is
18
+ * inside it — a bar changing height, revealing on scroll, entering bulk mode,
19
+ * or being added years from now all just resize this box, and the one
20
+ * ResizeObserver below picks every case up. Consumers read one variable and
21
+ * never have to learn about individual bars. Publishing a separate var per bar
22
+ * was the alternative, and it fails the moment someone adds bar number three
23
+ * without also editing every consumer's `calc()`.
24
+ *
25
+ * Two constraints this container places on its children:
26
+ * - A child must hide by COLLAPSING, not by transforming. `translate-y-full`
27
+ * leaves layout height behind, so a hidden bar would still be reserved here
28
+ * on every page that has no CTA. See BottomActions' grid-rows collapse.
29
+ * - No `overflow: hidden` here, ever. BottomActions' bulk-picker and info
30
+ * panels are `absolute bottom-full` — deliberately outside the flow (they
31
+ * are transient tap-to-open overlays, not persistent bands, so they are
32
+ * excluded from the published height) — and clipping would erase them.
33
+ */
34
+ export declare function BottomChrome({ children }: BottomChromeProps): React.JSX.Element;