@mohasinac/appkit 4.13.1 → 4.15.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 (493) 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 +8 -2
  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/cart/service.d.ts +3 -3
  17. package/dist/_internal/server/features/checkout/actions.js +25 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  19. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  20. package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
  21. package/dist/_internal/server/features/checkout/source-context.js +157 -0
  22. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  23. package/dist/_internal/server/features/homepage/index.js +7 -1
  24. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  25. package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
  26. package/dist/_internal/server/features/offers/adapters.js +88 -0
  27. package/dist/_internal/server/features/offers/index.d.ts +2 -0
  28. package/dist/_internal/server/features/offers/index.js +1 -0
  29. package/dist/_internal/server/features/orders/adapters.js +24 -0
  30. package/dist/_internal/server/features/products/data.js +15 -37
  31. package/dist/_internal/server/features/products/index.d.ts +1 -1
  32. package/dist/_internal/server/features/products/index.js +1 -1
  33. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  34. package/dist/_internal/server/features/products/list-public.js +266 -90
  35. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  36. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  37. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  38. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  39. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  40. package/dist/_internal/server/features/site-settings/index.js +1 -0
  41. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  42. package/dist/_internal/server/features/stores/adapters.js +91 -0
  43. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  44. package/dist/_internal/server/features/stores/index.js +1 -0
  45. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  46. package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
  47. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  48. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  49. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  50. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  51. package/dist/_internal/shared/actions/action-registry.js +17 -1
  52. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  53. package/dist/_internal/shared/checkout/lanes.js +26 -0
  54. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  55. package/dist/_internal/shared/features/auctions/config.js +27 -0
  56. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  57. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  58. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  59. package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
  60. package/dist/_internal/shared/features/cart/schema.js +15 -0
  61. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  62. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  63. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  64. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  65. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  66. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  67. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  68. package/dist/_internal/shared/features/offers/config.js +59 -0
  69. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  70. package/dist/_internal/shared/features/products/schema.d.ts +30 -30
  71. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  72. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  73. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  74. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  75. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  76. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  77. package/dist/_internal/shared/history/history.d.ts +88 -0
  78. package/dist/_internal/shared/history/history.js +198 -0
  79. package/dist/_internal/shared/history/index.d.ts +4 -0
  80. package/dist/_internal/shared/history/index.js +2 -0
  81. package/dist/_internal/shared/history/types.d.ts +61 -0
  82. package/dist/_internal/shared/history/types.js +6 -0
  83. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  84. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  85. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  86. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  87. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  88. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  89. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  90. package/dist/_internal/shared/listing-types/availability.js +104 -0
  91. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  92. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  93. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  94. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  95. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  96. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  97. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  98. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  99. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  100. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  101. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  102. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  103. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  104. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  105. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  106. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  107. package/dist/client.d.ts +20 -7
  108. package/dist/client.js +22 -4
  109. package/dist/constants/api-endpoints.d.ts +6 -0
  110. package/dist/constants/api-endpoints.js +2 -0
  111. package/dist/constants/field-names.d.ts +57 -8
  112. package/dist/constants/field-names.js +101 -8
  113. package/dist/constants/table-keys.d.ts +17 -0
  114. package/dist/constants/table-keys.js +17 -0
  115. package/dist/errors/error-codes.d.ts +13 -1
  116. package/dist/errors/error-codes.js +13 -1
  117. package/dist/errors/error-display-map.js +12 -3
  118. package/dist/errors/messages.d.ts +10 -2
  119. package/dist/errors/messages.js +10 -2
  120. package/dist/features/about/components/AboutView.d.ts +3 -0
  121. package/dist/features/about/components/AboutView.js +3 -2
  122. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  123. package/dist/features/about/components/PolicyPageView.js +61 -23
  124. package/dist/features/about/components/PublicProfileView.js +5 -0
  125. package/dist/features/about/schemas/firestore.d.ts +3 -0
  126. package/dist/features/account/components/UserBidsView.js +12 -4
  127. package/dist/features/account/components/UserOrdersView.js +7 -0
  128. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  129. package/dist/features/account/components/UserReturnsView.js +17 -1
  130. package/dist/features/account/components/index.d.ts +0 -2
  131. package/dist/features/account/components/index.js +0 -1
  132. package/dist/features/account/schemas/index.d.ts +2 -2
  133. package/dist/features/account/types/index.d.ts +21 -2
  134. package/dist/features/addresses/schemas/index.d.ts +4 -4
  135. package/dist/features/admin/components/AdminArtView.js +8 -3
  136. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  137. package/dist/features/admin/components/AdminBidsView.js +15 -2
  138. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  139. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  140. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  141. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  142. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  143. package/dist/features/admin/components/AdminLiveView.js +8 -3
  144. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  145. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  146. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  147. package/dist/features/admin/components/AdminOffersView.js +230 -0
  148. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  149. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  150. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  151. package/dist/features/admin/components/AdminProductsView.js +16 -29
  152. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  153. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  154. package/dist/features/admin/components/AdminScammersView.js +3 -8
  155. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  156. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  157. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  158. package/dist/features/admin/components/AdminSiteView.js +6 -2
  159. package/dist/features/admin/components/AdminStickersView.js +8 -3
  160. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  161. package/dist/features/admin/components/index.d.ts +2 -0
  162. package/dist/features/admin/components/index.js +1 -0
  163. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  164. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  165. package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
  166. package/dist/features/admin/constants/filter-tabs.js +56 -0
  167. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  168. package/dist/features/admin/schemas/firestore.js +1 -0
  169. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  170. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  171. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  172. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  173. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  174. package/dist/features/auctions/actions/bid-actions.js +93 -20
  175. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  176. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  177. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  178. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  179. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  180. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  181. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  182. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  183. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  184. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  185. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  186. package/dist/features/auctions/repository/bid.repository.js +23 -0
  187. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  188. package/dist/features/auctions/schemas/index.d.ts +12 -12
  189. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  190. package/dist/features/audit-log/schemas/firestore.js +7 -0
  191. package/dist/features/auth/permissions/constants.d.ts +1 -1
  192. package/dist/features/auth/permissions/constants.js +10 -1
  193. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  194. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  195. package/dist/features/blog/schemas/blog-form.js +100 -0
  196. package/dist/features/blog/schemas/index.d.ts +6 -6
  197. package/dist/features/cart/repository/cart.repository.js +1 -0
  198. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  199. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  200. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  201. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  202. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  203. package/dist/features/categories/components/BundleCollage.js +1 -1
  204. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  205. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  206. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  207. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  208. package/dist/features/categories/components/ConcernCard.js +7 -1
  209. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  210. package/dist/features/categories/schemas/index.d.ts +26 -26
  211. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  212. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  213. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  214. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  215. package/dist/features/events/components/AdminEventsView.js +15 -11
  216. package/dist/features/events/components/EventFilters.d.ts +9 -1
  217. package/dist/features/events/components/EventFilters.js +45 -21
  218. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  219. package/dist/features/events/components/EventsIndexListing.js +2 -2
  220. package/dist/features/events/components/EventsListPageView.js +13 -4
  221. package/dist/features/events/repository/events.repository.d.ts +17 -0
  222. package/dist/features/events/repository/events.repository.js +27 -0
  223. package/dist/features/events/schemas/event-form.d.ts +113 -0
  224. package/dist/features/events/schemas/event-form.js +82 -0
  225. package/dist/features/events/schemas/firestore.d.ts +12 -0
  226. package/dist/features/events/schemas/firestore.js +12 -0
  227. package/dist/features/events/schemas/index.d.ts +50 -50
  228. package/dist/features/events/types/index.d.ts +11 -0
  229. package/dist/features/events/types/index.js +57 -1
  230. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  231. package/dist/features/faq/schemas/index.d.ts +8 -8
  232. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  233. package/dist/features/filters/FilterFacetSection.js +11 -3
  234. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  235. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  236. package/dist/features/grouped/schemas/index.d.ts +4 -4
  237. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  238. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  239. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  240. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  241. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  242. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  243. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  244. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  245. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  246. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  247. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  248. package/dist/features/homepage/schemas/index.d.ts +33 -2
  249. package/dist/features/homepage/schemas/index.js +24 -0
  250. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  251. package/dist/features/layout/AppLayoutShell.js +15 -14
  252. package/dist/features/layout/BackToTop.d.ts +9 -0
  253. package/dist/features/layout/BackToTop.js +10 -1
  254. package/dist/features/layout/BottomActions.js +28 -25
  255. package/dist/features/layout/BottomChrome.d.ts +34 -0
  256. package/dist/features/layout/BottomChrome.js +55 -0
  257. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  258. package/dist/features/layout/BottomNavLayout.js +7 -1
  259. package/dist/features/layout/BottomNavbar.js +15 -2
  260. package/dist/features/layout/FooterLayout.js +11 -1
  261. package/dist/features/layout/NavbarLayout.js +1 -1
  262. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  263. package/dist/features/layout/TitleBarLayout.js +28 -5
  264. package/dist/features/layout/index.d.ts +2 -0
  265. package/dist/features/layout/index.js +1 -0
  266. package/dist/features/lottery/types/index.d.ts +3 -0
  267. package/dist/features/media/MediaPickerModal.js +5 -1
  268. package/dist/features/messages/schemas/index.d.ts +2 -2
  269. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  270. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  271. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  272. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  273. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  274. package/dist/features/orders/constants/order-scope.js +111 -0
  275. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  276. package/dist/features/orders/repository/orders.repository.js +121 -22
  277. package/dist/features/orders/schemas/firestore.d.ts +152 -0
  278. package/dist/features/orders/schemas/firestore.js +30 -0
  279. package/dist/features/orders/schemas/index.d.ts +18 -18
  280. package/dist/features/orders/types/index.d.ts +35 -7
  281. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  282. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  283. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  284. package/dist/features/products/actions/product-actions.d.ts +14 -0
  285. package/dist/features/products/actions/product-actions.js +49 -11
  286. package/dist/features/products/api/route.d.ts +12 -0
  287. package/dist/features/products/api/route.js +42 -100
  288. package/dist/features/products/components/ArtStickersListView.js +2 -2
  289. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  290. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  291. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  292. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  293. package/dist/features/products/components/MakeOfferButton.js +48 -49
  294. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  295. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  296. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  297. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  298. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  299. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  300. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  301. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  302. package/dist/features/products/components/ProductForm.js +5 -2
  303. package/dist/features/products/components/ProductGrid.js +6 -1
  304. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  305. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  306. package/dist/features/products/components/index.d.ts +2 -2
  307. package/dist/features/products/components/index.js +1 -1
  308. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  309. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  310. package/dist/features/products/constants/action-defs.d.ts +2 -0
  311. package/dist/features/products/constants/action-defs.js +6 -0
  312. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  313. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  314. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  315. package/dist/features/products/hooks/useProducts.js +13 -2
  316. package/dist/features/products/repository/products.repository.d.ts +31 -2
  317. package/dist/features/products/repository/products.repository.js +53 -21
  318. package/dist/features/products/schemas/firestore.d.ts +17 -3
  319. package/dist/features/products/schemas/firestore.js +7 -2
  320. package/dist/features/products/schemas/index.d.ts +64 -64
  321. package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
  322. package/dist/features/products/schemas/product-features.validators.js +63 -0
  323. package/dist/features/products/types/index.d.ts +16 -1
  324. package/dist/features/promotions/schemas/index.d.ts +2 -2
  325. package/dist/features/reviews/schemas/index.d.ts +16 -16
  326. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  327. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  328. package/dist/features/search/schemas/index.d.ts +4 -4
  329. package/dist/features/seller/actions/offer-actions.js +57 -4
  330. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  331. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  332. package/dist/features/seller/components/SellerArtView.js +5 -0
  333. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  334. package/dist/features/seller/components/SellerBidsView.js +11 -4
  335. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  336. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  337. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  338. package/dist/features/seller/components/SellerLiveView.js +5 -0
  339. package/dist/features/seller/components/SellerOffersView.js +2 -1
  340. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  341. package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
  342. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  343. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  344. package/dist/features/seller/components/SellerProductShell.js +7 -1
  345. package/dist/features/seller/components/SellerProductsView.js +11 -8
  346. package/dist/features/seller/components/SellerStickersView.js +5 -0
  347. package/dist/features/seller/components/index.d.ts +0 -2
  348. package/dist/features/seller/components/index.js +0 -1
  349. package/dist/features/seller/repository/offer.repository.d.ts +142 -24
  350. package/dist/features/seller/repository/offer.repository.js +165 -26
  351. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  352. package/dist/features/seller/schemas/coupon-form.js +116 -0
  353. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  354. package/dist/features/seller/schemas/firestore.js +27 -0
  355. package/dist/features/seller/schemas/index.d.ts +2 -2
  356. package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
  357. package/dist/features/seller/schemas/offer-forms.js +42 -0
  358. package/dist/features/seller/server.d.ts +0 -1
  359. package/dist/features/seller/server.js +0 -1
  360. package/dist/features/shell/SectionForm.d.ts +99 -0
  361. package/dist/features/shell/SectionForm.js +128 -0
  362. package/dist/features/shell/build-sections.d.ts +63 -0
  363. package/dist/features/shell/build-sections.js +207 -0
  364. package/dist/features/shell/entity-form.d.ts +109 -0
  365. package/dist/features/shell/entity-form.js +96 -0
  366. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  367. package/dist/features/shell/field-ui-meta.js +324 -0
  368. package/dist/features/shell/index.d.ts +8 -0
  369. package/dist/features/shell/index.js +4 -0
  370. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  371. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  372. package/dist/features/status-history/index.d.ts +11 -0
  373. package/dist/features/status-history/index.js +10 -0
  374. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  375. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  376. package/dist/features/store-extensions/index.d.ts +12 -0
  377. package/dist/features/store-extensions/index.js +12 -0
  378. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  379. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  380. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  381. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  382. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  383. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  384. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  385. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  386. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  387. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  388. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  389. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  390. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  391. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  392. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  393. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  394. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  395. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  396. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  397. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  398. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  399. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  400. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  401. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  402. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  403. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  404. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  405. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  406. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  408. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  409. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  410. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  411. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  412. package/dist/features/stores/repository/store.repository.js +6 -0
  413. package/dist/features/stores/schemas/index.d.ts +6 -6
  414. package/dist/features/support/schemas/index.d.ts +4 -4
  415. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  416. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  417. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  418. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
  419. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  420. package/dist/index.d.ts +25 -12
  421. package/dist/index.js +26 -11
  422. package/dist/next/routing/route-map.d.ts +23 -3
  423. package/dist/next/routing/route-map.js +14 -2
  424. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  425. package/dist/providers/db-firebase/base.repository.js +50 -1
  426. package/dist/providers/db-firebase/index.d.ts +1 -1
  427. package/dist/providers/db-firebase/index.js +1 -1
  428. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  429. package/dist/providers/db-firebase/sieve.js +52 -18
  430. package/dist/react/hooks/useUrlTable.js +10 -1
  431. package/dist/schemas/firestore-value.d.ts +8 -0
  432. package/dist/schemas/firestore-value.js +44 -0
  433. package/dist/schemas/index.d.ts +1 -1
  434. package/dist/schemas/index.js +1 -1
  435. package/dist/schemas/registry.d.ts +702 -69
  436. package/dist/schemas/registry.js +64 -1
  437. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  438. package/dist/seed/_helpers/category-forest.js +309 -0
  439. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  440. package/dist/seed/_helpers/category-tree.js +103 -0
  441. package/dist/seed/bids-seed-data.js +30 -0
  442. package/dist/seed/catalogue-seed-data.js +12 -12
  443. package/dist/seed/categories-seed-data.js +177 -162
  444. package/dist/seed/events-seed-data.js +61 -10
  445. package/dist/seed/homepage-sections-seed-data.js +5 -1
  446. package/dist/seed/offers-seed-data.js +244 -34
  447. package/dist/seed/orders-seed-data.js +336 -1
  448. package/dist/seed/products-art-seed-data.js +50 -12
  449. package/dist/seed/products-auctions-seed-data.js +138 -18
  450. package/dist/seed/products-classifieds-seed-data.js +80 -16
  451. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  452. package/dist/seed/products-live-items-seed-data.js +64 -13
  453. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  454. package/dist/seed/products-preorders-seed-data.js +52 -13
  455. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  456. package/dist/seed/products-standard-seed-data.js +99 -26
  457. package/dist/seed/products-stickers-seed-data.js +47 -12
  458. package/dist/seed/reviews-seed-data.js +6 -1
  459. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  460. package/dist/seed/site-settings-seed-data.js +38 -76
  461. package/dist/seed/stores-seed-data.js +71 -1
  462. package/dist/seed/users-seed-data.js +15 -5
  463. package/dist/server-entry.d.ts +1 -2
  464. package/dist/server-entry.js +4 -3
  465. package/dist/server.d.ts +6 -3
  466. package/dist/server.js +13 -4
  467. package/dist/styles.css +78 -12
  468. package/dist/tailwind-utilities.css +1 -1
  469. package/dist/ui/components/Button.style.css +19 -4
  470. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  471. package/dist/ui/components/CollapsibleSection.js +6 -2
  472. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  473. package/dist/ui/components/FilterChipGroup.js +21 -4
  474. package/dist/ui/components/HandMode.style.css +47 -0
  475. package/dist/ui/components/Layout.d.ts +9 -2
  476. package/dist/ui/components/Layout.js +1 -1
  477. package/dist/ui/components/ListingLayout.js +8 -8
  478. package/dist/ui/components/ListingLayout.style.css +8 -7
  479. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  480. package/dist/ui/components/RecordDetailModal.js +2 -2
  481. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  482. package/dist/ui/components/StickyToolbar.js +62 -20
  483. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  484. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  485. package/dist/ui/components/index.style.css +3 -0
  486. package/dist/ui/forms/FormShell.d.ts +43 -13
  487. package/dist/ui/forms/FormShell.js +22 -11
  488. package/dist/ui/forms/index.d.ts +1 -1
  489. package/dist/ui/index.d.ts +1 -1
  490. package/package.json +2 -1
  491. package/scripts/node-esm-loader.mjs +65 -0
  492. package/scripts/seed-cli-loader.mjs +6 -49
  493. package/scripts/seed-cli.mjs +1 -1
@@ -217,13 +217,28 @@
217
217
  * That is exactly what happened to every image-tile button in the app when
218
218
  * the ripple landed.
219
219
  *
220
- * `justify-content` / `gap` are inherited so the button's own `justify` and
221
- * `gap` props keep reaching the real children now that they sit one level
222
- * deeper than the flex container that declares them.
220
+ * ALL FOUR flex-container properties must be inherited — `flex-direction` and
221
+ * `align-items` as much as `justify-content` and `gap` — because the real
222
+ * children now sit one level deeper than the container declaring them. The
223
+ * first pass at this fix inherited only two and hard-coded `align-items:
224
+ * center`, so a Button asking for `flex-col` silently still laid its children
225
+ * out in a row: the prize-draw collage, the media picker and the concern card
226
+ * each rendered an image tile BESIDE its caption instead of above it, at a
227
+ * fraction of the intended width. `inherit` is behaviour-identical for every
228
+ * ordinary button — `.appkit-button` declares `align-items: center` and never
229
+ * declares `flex-direction`, so it computes to the initial `row`.
230
+ *
231
+ * One thing this CSS cannot defend against: a caller putting a bare `block` /
232
+ * `flex` / `hidden` Tailwind utility on the Button. The consumer's globals.css
233
+ * declares those un-layered and `!important`, which beats `.appkit-button`'s
234
+ * `display: inline-flex` — and every rule below applies only to a flex ITEM, so
235
+ * the whole contract goes inert and fill children collapse to 0x0 again. That
236
+ * is guarded from the call-site side by scripts/audit-primitive-child-wrappers.mjs.
223
237
  */
224
238
  .appkit-button__content {
225
239
  display: inline-flex;
226
- align-items: center;
240
+ flex-direction: inherit;
241
+ align-items: inherit;
227
242
  justify-content: inherit;
228
243
  gap: inherit;
229
244
  flex: 1 1 auto;
@@ -5,6 +5,22 @@ export interface CollapsibleSectionProps {
5
5
  onToggle: () => void;
6
6
  /** Optional right-aligned content next to the title (badges, counts). */
7
7
  renderHeaderExtra?: () => React.ReactNode;
8
+ /**
9
+ * Keep collapsed children mounted (hidden via CSS) instead of unmounting them.
10
+ *
11
+ * `<Collapse>` renders `{isOpen && <motion.div>}` — collapsing therefore
12
+ * UNMOUNTS the subtree and destroys any uncommitted state inside it. That is
13
+ * harmless for inputs driven entirely off a parent `values` object, but it
14
+ * discards in-flight work for anything holding its own: a `useMediaUpload`
15
+ * transfer, a rich-text editor buffer, a partially-filled multi-select.
16
+ *
17
+ * Set this on any section that can hold uncommitted state. The trade-off is
18
+ * that hidden children still render, so don't enable it for expensive
19
+ * subtrees that are purely presentational.
20
+ */
21
+ keepMounted?: boolean;
22
+ /** Anchor id on the section root — lets a caller scroll/link to it. */
23
+ id?: string;
8
24
  className?: string;
9
25
  children?: React.ReactNode;
10
26
  }
@@ -12,4 +28,4 @@ export interface CollapsibleSectionProps {
12
28
  * per-user, accordion-scoped collapse state (see that hook for the
13
29
  * server-side merge/default-collapsed semantics). Purely presentational
14
30
  * here: isCollapsed/onToggle are always controlled by the caller. */
15
- export declare function CollapsibleSection({ title, isCollapsed, onToggle, renderHeaderExtra, className, children, }: CollapsibleSectionProps): React.JSX.Element;
31
+ export declare function CollapsibleSection({ title, isCollapsed, onToggle, renderHeaderExtra, keepMounted, id, className, children, }: CollapsibleSectionProps): React.JSX.Element;
@@ -8,6 +8,10 @@ import { Collapse } from "./Motion";
8
8
  * per-user, accordion-scoped collapse state (see that hook for the
9
9
  * server-side merge/default-collapsed semantics). Purely presentational
10
10
  * here: isCollapsed/onToggle are always controlled by the caller. */
11
- export function CollapsibleSection({ title, isCollapsed, onToggle, renderHeaderExtra, className = "", children, }) {
12
- return (_jsxs(Div, { className: className, rounded: "xl", border: "default", surface: "default", children: [_jsxs(Row, { align: "center", justify: "between", paddingX: "x-md", paddingY: "y-sm", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: onToggle, paddingX: "none", paddingY: "none", rounded: "none", className: "min-w-0 flex-1", "aria-expanded": !isCollapsed, children: _jsxs(Row, { align: "center", justify: "between", className: "w-full", children: [_jsx(Span, { weight: "semibold", size: "sm", children: title }), _jsx("svg", { className: `w-4 h-4 transition-transform duration-150 ${isCollapsed ? "" : "rotate-180"}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }) }), renderHeaderExtra && _jsx(Div, { className: "ml-3 shrink-0", children: renderHeaderExtra() })] }), _jsx(Collapse, { isOpen: !isCollapsed, children: _jsx(Div, { padding: "md", border: "top", children: children }) })] }));
11
+ export function CollapsibleSection({ title, isCollapsed, onToggle, renderHeaderExtra, keepMounted = false, id, className = "", children, }) {
12
+ return (_jsxs(Div, { id: id, className: className, rounded: "xl", border: "default", surface: "default", children: [_jsxs(Row, { align: "center", justify: "between", paddingX: "x-md", paddingY: "y-sm", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: onToggle, paddingX: "none", paddingY: "none", rounded: "none", className: "min-w-0 flex-1", "aria-expanded": !isCollapsed, children: _jsxs(Row, { align: "center", justify: "between", className: "w-full", children: [_jsx(Span, { weight: "semibold", size: "sm", children: title }), _jsx("svg", { className: `w-4 h-4 transition-transform duration-150 ${isCollapsed ? "" : "rotate-180"}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }) }), renderHeaderExtra && _jsx(Div, { className: "ml-3 shrink-0", children: renderHeaderExtra() })] }), keepMounted ? (
13
+ // No <Collapse>: it unmounts on close, which would discard uncommitted
14
+ // child state (see `keepMounted` docs). `hidden` keeps the subtree
15
+ // mounted and removes it from the a11y tree at the same time.
16
+ _jsx(Div, { padding: "md", border: "top", hidden: isCollapsed, children: children })) : (_jsx(Collapse, { isOpen: !isCollapsed, children: _jsx(Div, { padding: "md", border: "top", children: children }) }))] }));
13
17
  }
@@ -22,6 +22,16 @@ import React from "react";
22
22
  export interface FilterChipGroupTab {
23
23
  id: string;
24
24
  label: string;
25
+ /**
26
+ * Rows matching this chip within the surface's current scope. Rendered as a
27
+ * trailing count and, with `hideEmpty`, used to drop chips that would return
28
+ * nothing.
29
+ *
30
+ * `undefined` means "not counted / count failed" — NOT zero. Such a chip is
31
+ * always kept, so a surface that supplies no counts behaves exactly as before
32
+ * and a failed count can never hide a filter that has rows (Root Cause #59).
33
+ */
34
+ count?: number;
25
35
  }
26
36
  export interface FilterChipGroupProps {
27
37
  /** Section label shown above the chips. */
@@ -52,7 +62,21 @@ export interface FilterChipGroupProps {
52
62
  * same state that every caller would have to remember to clear.
53
63
  */
54
64
  multiple?: boolean;
65
+ /**
66
+ * Drop chips whose `count` is exactly 0 — a filter that can only ever return
67
+ * an empty list is noise.
68
+ *
69
+ * Opt-in (default `false`) because most of this component's ~45 call sites
70
+ * supply no counts at all, and because it is only ever correct for a FILTER
71
+ * over existing rows. Never enable it on a picker that CREATES something: a
72
+ * seller with no auctions yet must still be able to make their first one.
73
+ *
74
+ * A chip whose count is `undefined` is always kept — see `FilterChipGroupTab.count`.
75
+ * The currently-selected chip is also always kept, so an active filter can
76
+ * never vanish from under the user mid-interaction.
77
+ */
78
+ hideEmpty?: boolean;
55
79
  /** Optional className spread onto the outer wrapper. */
56
80
  className?: string;
57
81
  }
58
- export declare function FilterChipGroup({ label, tabs, value, onChange, allId, multiple, className, }: FilterChipGroupProps): React.JSX.Element;
82
+ export declare function FilterChipGroup({ label, tabs, value, onChange, allId, multiple, hideEmpty, className, }: FilterChipGroupProps): React.JSX.Element;
@@ -6,16 +6,33 @@ const ACTIVE_CLS = "bg-[var(--appkit-color-primary)] text-white border-[var(--ap
6
6
  const INACTIVE_CLS = "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]";
7
7
  const CHIP_BASE_CLS = "rounded-full px-3 py-1 text-xs font-medium border transition-colors";
8
8
  const LABEL_CLS = "text-xs font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]";
9
+ // `opacity-70` rather than a fixed colour so the count stays legible against
10
+ // both the active (primary fill) and inactive (surface) chip backgrounds —
11
+ // a literal text colour would be invisible on one of them in some theme
12
+ // (Root Cause #67).
13
+ const COUNT_CLS = "ml-1.5 tabular-nums opacity-70";
9
14
  /** Split a pipe-joined multi-select value into its ids. */
10
15
  function parseMulti(value) {
11
16
  return value ? value.split("|").filter(Boolean) : [];
12
17
  }
13
- export function FilterChipGroup({ label, tabs, value, onChange, allId = "All", multiple = false, className, }) {
18
+ export function FilterChipGroup({ label, tabs, value, onChange, allId = "All", multiple = false, hideEmpty = false, className, }) {
14
19
  const selected = new Set(multiple ? parseMulti(value) : []);
15
20
  const current = value || allId;
16
21
  // In multi-select the "All" sentinel is redundant with an empty selection —
17
22
  // see the `multiple` prop docs.
18
- const visibleTabs = multiple ? tabs.filter((t) => t.id !== allId) : tabs;
23
+ const allTabs = multiple ? tabs.filter((t) => t.id !== allId) : tabs;
24
+ const visibleTabs = hideEmpty
25
+ ? allTabs.filter((t) => {
26
+ // Never hide the "All" sentinel, and never hide a chip the user has
27
+ // already picked — a filter disappearing while it is applied would
28
+ // leave the URL holding a value with no way to clear it.
29
+ if (t.id === allId)
30
+ return true;
31
+ if (multiple ? selected.has(t.id) : current === t.id)
32
+ return true;
33
+ return t.count === undefined || t.count > 0;
34
+ })
35
+ : allTabs;
19
36
  const handleClick = (tabId) => {
20
37
  if (!multiple) {
21
38
  onChange(tabId === allId ? "" : tabId);
@@ -36,9 +53,9 @@ export function FilterChipGroup({ label, tabs, value, onChange, allId = "All", m
36
53
  };
37
54
  return (_jsxs(Div, { className: `space-y-2 ${className ?? ""}`.trim(), children: [_jsx(Text, { as: "p", className: LABEL_CLS, children: label }), _jsx(Div, { className: "flex flex-wrap gap-2", role: multiple ? "group" : undefined, "aria-label": multiple ? label : undefined, children: visibleTabs.map((tab) => {
38
55
  const isActive = multiple ? selected.has(tab.id) : current === tab.id;
39
- return (_jsx("button", { type: "button",
56
+ return (_jsxs("button", { type: "button",
40
57
  // Checkboxes report checked state, not pressed state — a screen
41
58
  // reader announcing "pressed" for a filter that stays on is wrong.
42
- role: multiple ? "checkbox" : undefined, "aria-checked": multiple ? isActive : undefined, "aria-pressed": multiple ? undefined : isActive, onClick: () => handleClick(tab.id), className: `${CHIP_BASE_CLS} ${isActive ? ACTIVE_CLS : INACTIVE_CLS}`, children: tab.label }, tab.id));
59
+ role: multiple ? "checkbox" : undefined, "aria-checked": multiple ? isActive : undefined, "aria-pressed": multiple ? undefined : isActive, onClick: () => handleClick(tab.id), className: `${CHIP_BASE_CLS} ${isActive ? ACTIVE_CLS : INACTIVE_CLS}`, children: [tab.label, typeof tab.count === "number" && (_jsx("span", { className: COUNT_CLS, children: tab.count > 99 ? "99+" : tab.count }))] }, tab.id));
43
60
  }) })] }));
44
61
  }
@@ -0,0 +1,47 @@
1
+ /* Hand-mode mirroring — CSS-only, driven by `<html data-hand>`.
2
+ *
3
+ * `data-hand` is written before first paint by the blocking script in the
4
+ * consumer's root layout, so these rules apply on the FIRST paint: no flash,
5
+ * no server/client hydration mismatch (`HandModeProvider` reads localStorage
6
+ * in its useState initialiser, so a className derived from `hand` would
7
+ * differ server vs client), and no "use client" forced onto server-safe
8
+ * primitives (Root Cause #12). These are hand-authored classes, not Tailwind
9
+ * output, so they also survive the consumer's `@source not "../../appkit"`
10
+ * purge (Root Cause #3).
11
+ *
12
+ * NOTE: both appkit's and the consumer's Tailwind builds set
13
+ * `important: true`. Any call site using these classes MUST NOT also carry
14
+ * the equivalent utility (`flex-row-reverse`, `-right-*`) — an !important
15
+ * declaration in @layer utilities beats these unlayered rules, so the class
16
+ * would silently do nothing.
17
+ */
18
+
19
+ /* Mirror a flex row's VISUAL order only. DOM order — and therefore reading
20
+ order, aria-* and the focus sequence — is unchanged. */
21
+ [data-hand="left"] .appkit-hand-mirror {
22
+ flex-direction: row-reverse;
23
+ }
24
+
25
+ /* Corner count badge hugging the OUTER top corner of its icon: top-right by
26
+ default, top-left in left-hand mode. Owns the HORIZONTAL axis only — the
27
+ vertical offset stays a `-top-*` utility at the call site.
28
+
29
+ The offset comes from `--appkit-badge-offset`, declared only by the size
30
+ modifiers below. That matters: a `--lg { right: -0.5rem }` variant would be
31
+ specificity (0,1,0) and would LOSE to `[data-hand="left"] .appkit-hand-badge`
32
+ at (0,2,0), so left-hand mode would silently fall back to the small offset.
33
+ A custom property has no such race. */
34
+ .appkit-hand-badge {
35
+ right: calc(var(--appkit-badge-offset, 0.125rem) * -1);
36
+ left: auto;
37
+ }
38
+
39
+ [data-hand="left"] .appkit-hand-badge {
40
+ left: calc(var(--appkit-badge-offset, 0.125rem) * -1);
41
+ right: auto;
42
+ }
43
+
44
+ /* 0.5rem inset — the offset the bottom-nav badges expressed as `-right-2`. */
45
+ .appkit-hand-badge--lg {
46
+ --appkit-badge-offset: var(--appkit-space-2, 0.5rem);
47
+ }
@@ -281,8 +281,15 @@ export interface RowProps extends React.HTMLAttributes<HTMLElement>, SurfaceProp
281
281
  wrap?: boolean;
282
282
  /** Reverse the visual order of children (`flex-direction: row-reverse`) without
283
283
  * touching DOM/source order. Used for hand-mode-aware layouts (e.g. moving a
284
- * close button to the opposite header edge) — replaces raw `flex-row-reverse`. */
285
- reverse?: boolean;
284
+ * close button to the opposite header edge) — replaces raw `flex-row-reverse`.
285
+ *
286
+ * `true` reverses unconditionally. `"hand"` defers the decision to the
287
+ * `data-hand` preference: it mirrors only in left-hand mode, entirely in CSS
288
+ * (`HandMode.style.css`), so `Row` stays hook-free and server-safe and the
289
+ * flip lands on the FIRST paint — no flash, no hydration mismatch. Prefer
290
+ * `"hand"` over `useHandMode()` + `reverse={hand === "left"}` for anything
291
+ * rendered above the fold. */
292
+ reverse?: boolean | "hand";
286
293
  /** Zebra-stripe pattern when this Row is a repeating list element (rendered with `even:bg-*`). */
287
294
  oddEven?: "none" | "zebra";
288
295
  /**
@@ -235,7 +235,7 @@ export function Row({ gap = "md", centered = false, align = "center", justify =
235
235
  const Tag = (as ?? "div");
236
236
  const classes = [
237
237
  "appkit-row",
238
- reverse ? "appkit-row--reverse" : "",
238
+ reverse === "hand" ? "appkit-hand-mirror" : reverse ? "appkit-row--reverse" : "",
239
239
  centered ? "appkit-row--centered" : align !== "center" ? ITEMS_MAP[align] : "",
240
240
  !centered && justify !== "start" ? JUSTIFY_MAP[justify] : "",
241
241
  GAP_MAP[gap],
@@ -20,6 +20,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
20
20
  * └----------┴-------------------------------------------------------┘
21
21
  */
22
22
  import { useState } from "react";
23
+ import { createPortal } from "react-dom";
24
+ import { useBottomChromeSlot } from "./bottom-chrome-slot";
23
25
  import { Aside, Nav } from "./Semantic";
24
26
  import { Text, Span } from "./Typography";
25
27
  import { Button } from "./Button";
@@ -42,6 +44,9 @@ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filte
42
44
  // Rules of Hooks.
43
45
  const [sidebarOpen, setSidebarOpen] = useState(defaultSidebarOpen);
44
46
  const [mobileFilterOpen, setMobileFilterOpen] = useState(false);
47
+ // Must run before the detail-view short-circuit below — hooks cannot sit
48
+ // after a conditional return.
49
+ const bottomChromeSlot = useBottomChromeSlot();
45
50
  // Detail view short-circuit (folded from ListingViewShell)
46
51
  if (detailView) {
47
52
  return _jsx(_Fragment, { children: detailView });
@@ -92,14 +97,9 @@ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filte
92
97
  : "appkit-listing-layout__sidebar--collapsed",
93
98
  ].join(" "), children: _jsxs(Div, { className: "appkit-listing-layout__sidebar-panel", children: [_jsxs(Div, { className: "appkit-listing-layout__sidebar-header", children: [_jsx(Text, { weight: "semibold", size: "sm", children: panelTitle }), filterActiveCount > 0 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onFilterClear, className: "text-xs text-primary hover:text-primary/80 hover:underline p-0 h-auto leading-none font-medium", children: l.clearAll }))] }), _jsx(Div, { className: "appkit-listing-layout__sidebar-facets", children: filterContent }), _jsx(Div, { className: "appkit-listing-layout__sidebar-footer", children: _jsx(Button, { type: "button", variant: "primary", className: "w-full rounded-xl", size: "sm", onClick: onFilterApply, children: filterPendingCount != null && filterPendingCount > 0
94
99
  ? `${l.applyFilters} (${filterPendingCount})`
95
- : l.applyFilters }) })] }) })), _jsxs(Div, { className: "appkit-listing-layout__content", children: [activeFiltersSlot, resultCountSlot && (_jsx(Div, { className: "appkit-listing-layout__result-count", children: resultCountSlot })), errorSlot ? (errorSlot) : (_jsxs(_Fragment, { children: [children, paginationSlot && (_jsx(Div, { className: "appkit-listing-layout__pagination", children: paginationSlot }))] }))] })] }), toolbarPaginationSlot && (_jsx(Nav, { "aria-label": "Pagination", className: [
96
- "appkit-listing-layout__mobile-pagination",
97
- effectiveIsDashboard
98
- ? "appkit-listing-layout__mobile-pagination--dashboard"
99
- : selectedCount > 0
100
- ? "appkit-listing-layout__mobile-pagination--bulk"
101
- : "appkit-listing-layout__mobile-pagination--default",
102
- ].join(" "), children: toolbarPaginationSlot })), hasFilter && (_jsx(Drawer, { isOpen: mobileFilterOpen, onClose: () => setMobileFilterOpen(false), side: "bottom", title: filterActiveCount > 0
100
+ : l.applyFilters }) })] }) })), _jsxs(Div, { className: "appkit-listing-layout__content", children: [activeFiltersSlot, resultCountSlot && (_jsx(Div, { className: "appkit-listing-layout__result-count", children: resultCountSlot })), errorSlot ? (errorSlot) : (_jsxs(_Fragment, { children: [children, paginationSlot && (_jsx(Div, { className: "appkit-listing-layout__pagination", children: paginationSlot }))] }))] })] }), toolbarPaginationSlot &&
101
+ bottomChromeSlot &&
102
+ createPortal(_jsx(Nav, { "aria-label": "Pagination", className: "appkit-listing-layout__mobile-pagination", children: toolbarPaginationSlot }), bottomChromeSlot), hasFilter && (_jsx(Drawer, { isOpen: mobileFilterOpen, onClose: () => setMobileFilterOpen(false), side: "bottom", title: filterActiveCount > 0
103
103
  ? `${panelTitle} (${filterActiveCount})`
104
104
  : panelTitle, footer: _jsxs(Div, { className: "appkit-listing-layout__mobile-overlay-footer", children: [_jsx(Button, { type: "button", variant: "secondary", className: "flex-1 rounded-xl", onClick: () => onFilterClear?.(), children: l.clearAll }), _jsx(Button, { type: "button", variant: "primary", className: "flex-1 rounded-xl", onClick: handleMobileApply, children: filterPendingCount != null && filterPendingCount > 0
105
105
  ? `${l.applyFilters} (${filterPendingCount})`
@@ -376,11 +376,10 @@
376
376
 
377
377
  /* -- Mobile sticky pagination bar ------------------- */
378
378
 
379
+ /* Positioned by <BottomChrome>, the shell's bottom-edge tier — this element is
380
+ a flex child of it, so it owns no position, offset or z-index of its own. */
379
381
  .appkit-listing-layout__mobile-pagination {
380
- position: fixed;
381
- left: 0;
382
- right: 0;
383
- z-index: 39;
382
+ pointer-events: auto;
384
383
  background: rgba(255, 255, 255, 0.9);
385
384
  backdrop-filter: blur(12px);
386
385
  border-top: 1px solid rgba(228, 228, 231, 0.8);
@@ -406,9 +405,11 @@
406
405
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.2);
407
406
  }
408
407
 
409
- .appkit-listing-layout__mobile-pagination--dashboard { bottom: 0; }
410
- .appkit-listing-layout__mobile-pagination--default { bottom: var(--bottom-nav-height, 4rem); }
411
- .appkit-listing-layout__mobile-pagination--bulk { bottom: calc(var(--bottom-nav-height, 4rem) + 3.5rem); }
408
+ /* The --dashboard / --default / --bulk offset modifiers are gone. --bulk only
409
+ ever meant "the CTA bar is up", expressed as "rows are selected", and it
410
+ hardcoded 3.5rem for that bar's height; --dashboard's `bottom: 0` only
411
+ existed because the dashboard nav published no height. The tier answers both
412
+ questions directly now. */
412
413
 
413
414
  /* -- Mobile filter overlay --------------------------- */
414
415
 
@@ -60,9 +60,18 @@ export interface RecordDetailModalProps {
60
60
  heading: string;
61
61
  entries: RecordDetailItem[];
62
62
  };
63
+ /**
64
+ * Rich content rendered between `items` and `metadata` — a timeline rail, a
65
+ * chart, anything that is neither a scalar field nor a raw payload.
66
+ *
67
+ * `footer` cannot host this: that slot is an action-button row wrapped in
68
+ * `<Row justify="end">`, so a full-width block placed there is squeezed to
69
+ * the right and reads as a control.
70
+ */
71
+ extra?: ReactNode;
63
72
  /** Raw payload dump for records that carry an open-ended object. */
64
73
  metadata?: Record<string, JsonValue>;
65
74
  /** Action buttons rendered at the bottom (approve/reject/etc). */
66
75
  footer?: ReactNode;
67
76
  }
68
- export declare function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, metadata, footer, }: RecordDetailModalProps): import("react").JSX.Element;
77
+ export declare function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, extra, metadata, footer, }: RecordDetailModalProps): import("react").JSX.Element;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Anchor, Badge, Code, Div, Modal, Row, Stack, Text } from "../index";
4
4
  import { MediaImage } from "../../features/media/MediaImage";
5
- export function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, metadata, footer, }) {
5
+ export function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, extra, metadata, footer, }) {
6
6
  const hasMetadata = Boolean(metadata && Object.keys(metadata).length > 0);
7
- return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: title, children: _jsxs(Stack, { gap: "md", children: [badges && badges.length > 0 && (_jsx(Row, { gap: "sm", align: "center", wrap: true, children: badges.map((b) => (_jsx(Badge, { variant: b.variant ?? "default", children: b.label }, b.label))) })), description && (_jsx(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: _jsx(Text, { size: "sm", children: description }) })), fields && fields.length > 0 && (_jsx(Stack, { gap: "xs", children: fields.map((f) => (_jsxs(Row, { justify: "between", gap: "md", children: [_jsx(Text, { size: "sm", color: "muted", children: f.label }), _jsx(Text, { size: "sm", weight: "medium", className: "text-right", children: f.value })] }, f.label))) })), items && items.entries.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", weight: "semibold", color: "muted", children: [items.heading, " (", items.entries.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.entries.map((entry, i) => (_jsxs(Row, { gap: "sm", align: "center", justify: "between", paddingY: "y-xs-tall", padding: "x-sm", children: [_jsxs(Row, { gap: "sm", align: "center", className: "min-w-0", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: entry.image, alt: entry.title, size: "thumbnail" }) }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [entry.href ? (_jsx(Anchor, { href: entry.href, tone: "brand", size: "sm", weight: "medium", children: entry.title })) : (_jsx(Text, { size: "sm", weight: "medium", className: "truncate", children: entry.title })), entry.subtitle && (_jsx(Text, { size: "xs", color: "muted", children: entry.subtitle }))] })] }), entry.trailing && (_jsx(Text, { size: "sm", weight: "medium", className: "shrink-0", children: entry.trailing }))] }, `${entry.title}-${i}`))) })] })), hasMetadata && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", className: "mb-1", children: "Details" }), _jsx(Code, { size: "xs", className: "block whitespace-pre-wrap font-mono", children: JSON.stringify(metadata, null, 2) })] })), footer && _jsx(Row, { justify: "end", gap: "sm", children: footer })] }) }));
7
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: title, children: _jsxs(Stack, { gap: "md", children: [badges && badges.length > 0 && (_jsx(Row, { gap: "sm", align: "center", wrap: true, children: badges.map((b) => (_jsx(Badge, { variant: b.variant ?? "default", children: b.label }, b.label))) })), description && (_jsx(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: _jsx(Text, { size: "sm", children: description }) })), fields && fields.length > 0 && (_jsx(Stack, { gap: "xs", children: fields.map((f) => (_jsxs(Row, { justify: "between", gap: "md", children: [_jsx(Text, { size: "sm", color: "muted", children: f.label }), _jsx(Text, { size: "sm", weight: "medium", className: "text-right", children: f.value })] }, f.label))) })), items && items.entries.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", weight: "semibold", color: "muted", children: [items.heading, " (", items.entries.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.entries.map((entry, i) => (_jsxs(Row, { gap: "sm", align: "center", justify: "between", paddingY: "y-xs-tall", padding: "x-sm", children: [_jsxs(Row, { gap: "sm", align: "center", className: "min-w-0", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: entry.image, alt: entry.title, size: "thumbnail" }) }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [entry.href ? (_jsx(Anchor, { href: entry.href, tone: "brand", size: "sm", weight: "medium", children: entry.title })) : (_jsx(Text, { size: "sm", weight: "medium", className: "truncate", children: entry.title })), entry.subtitle && (_jsx(Text, { size: "xs", color: "muted", children: entry.subtitle }))] })] }), entry.trailing && (_jsx(Text, { size: "sm", weight: "medium", className: "shrink-0", children: entry.trailing }))] }, `${entry.title}-${i}`))) })] })), extra, hasMetadata && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", className: "mb-1", children: "Details" }), _jsx(Code, { size: "xs", className: "block whitespace-pre-wrap font-mono", children: JSON.stringify(metadata, null, 2) })] })), footer && _jsx(Row, { justify: "end", gap: "sm", children: footer })] }) }));
8
8
  }
@@ -12,7 +12,9 @@ import { type ReactNode } from "react";
12
12
  * Dismiss/hide: pass `dismissible` (with a stable `id`) to let the user
13
13
  * collapse the bar to a thin re-expand strip for the rest of the browser
14
14
  * session — a viewport-annoyance-avoidance toggle, not a durable
15
- * preference, so it resets on reload rather than persisting forever. This
15
+ * preference, so it resets on reload rather than persisting forever. Both
16
+ * directions render the same centred, labelled strip ("Hide Toolbar" /
17
+ * "Show Toolbar") so the control is symmetric and self-describing. This
16
18
  * is what closes the mobile-overlap complaint the sticky toolbar pattern
17
19
  * caused across listing pages: at narrow widths, any sticky bar competing
18
20
  * with page content for vertical space needs a way out that doesn't require
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from "react";
3
+ import { useEffect, useRef, useState } from "react";
4
4
  import { ChevronDown } from "lucide-react";
5
5
  import { normalizeError } from "../../errors/normalize";
6
6
  const TONE_CLS = {
@@ -24,19 +24,22 @@ function resolveOffsetClass(offset) {
24
24
  return "top-[calc(var(--header-height,0px)+var(--appkit-navbar-height,2.5rem))]";
25
25
  }
26
26
  if (offset === "header+pagination") {
27
- // Matches the sticky pagination-row offset repeated verbatim across
28
- // every DataListingView-style index listing (64px = the ListingToolbar
29
- // row height above it — py-3/py-3.5 padding + its ~38px content row) —
30
- // consolidated here instead of copy-pasted. Keep in sync with
31
- // ListingToolbar's own padding classes; a mismatch makes the pagination
32
- // row overlap/hide behind the toolbar (the "too compact when sticky"
33
- // regression fixed 2026-08-19).
34
- return "top-[calc(var(--header-height,0px)+64px)]";
27
+ // The sticky pagination-row offset repeated verbatim across every
28
+ // DataListingView-style index listing — consolidated here instead of
29
+ // copy-pasted. It tracks the toolbar's REAL height via
30
+ // --appkit-toolbar-height, which a dismissible StickyToolbar publishes
31
+ // from its own ResizeObserver (see TOOLBAR_HEIGHT_VAR below). A fixed
32
+ // constant can only ever be right in one state: the toolbar is ~64px
33
+ // expanded but ~20px collapsed, so hard-coding 64px left a ~44px gap
34
+ // through which page content scrolled between the two pinned bars.
35
+ // The 64px fallback is the expanded height, used on any page whose
36
+ // toolbar isn't dismissible and therefore publishes nothing.
37
+ return "top-[calc(var(--header-height,0px)+var(--appkit-toolbar-height,64px))]";
35
38
  }
36
39
  if (offset === "header+bulk-actions") {
37
- // Matches the sticky bulk-action-bar offset that stacks below both the
38
- // filter toolbar (64px) and the pagination row above it (~52px more).
39
- return "top-[calc(var(--header-height,0px)+116px)]";
40
+ // Stacks below both the filter toolbar (same variable as above) and the
41
+ // pagination row between them (~52px).
42
+ return "top-[calc(var(--header-height,0px)+var(--appkit-toolbar-height,64px)+52px)]";
40
43
  }
41
44
  // Numeric offsets are applied via inline style instead (see below) —
42
45
  // Tailwind's static scanner can never see a dynamically-interpolated
@@ -44,6 +47,13 @@ function resolveOffsetClass(offset) {
44
47
  // generated for it in the compiled stylesheet.
45
48
  return "";
46
49
  }
50
+ /**
51
+ * Written to `<html>` by every dismissible StickyToolbar from a
52
+ * ResizeObserver, and consumed by the `header+pagination` /
53
+ * `header+bulk-actions` offsets so a bar stacked underneath tracks the real
54
+ * height in both the expanded and collapsed states.
55
+ */
56
+ const TOOLBAR_HEIGHT_VAR = "--appkit-toolbar-height";
47
57
  const STORAGE_PREFIX = "appkit:sticky-toolbar-collapsed:";
48
58
  function readCollapsed(id) {
49
59
  if (typeof window === "undefined")
@@ -69,12 +79,45 @@ function writeCollapsed(id, value) {
69
79
  // works for the current render, just doesn't survive a re-mount.
70
80
  }
71
81
  }
82
+ /**
83
+ * The collapse/expand control — one component for both states so the two
84
+ * halves of the affordance can't drift apart (the collapsed strip read
85
+ * "Show Toolbar" while the expanded one was a bare, unlabelled chevron
86
+ * pinned to the far right until 2026-08-24).
87
+ */
88
+ function CollapseStrip({ collapsed, label, onToggle, className, }) {
89
+ return (_jsx("div", { className: `flex justify-center ${className ?? ""}`, children: _jsxs("button", { type: "button", onClick: onToggle, "aria-label": `${collapsed ? "Show" : "Hide"} ${label}`, "aria-expanded": !collapsed, className: "flex items-center gap-1 px-4 py-0.5 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(ChevronDown, { className: `h-3.5 w-3.5 transition-transform duration-150 ${collapsed ? "" : "rotate-180"}`, "aria-hidden": "true" }), collapsed ? "Show" : "Hide", " ", label] }) }));
90
+ }
72
91
  export function StickyToolbar({ children, offset = "header", tone = "translucent", border = true, padding = "toolbar", z = "above-content", role, className, dataTestId, dismissible = false, id, label = "Toolbar", }) {
73
92
  const [collapsed, setCollapsed] = useState(false);
93
+ const rootRef = useRef(null);
74
94
  useEffect(() => {
75
95
  if (dismissible && id)
76
96
  setCollapsed(readCollapsed(id));
77
97
  }, [dismissible, id]);
98
+ // Publish the rendered height so a bar stacked below (sticky pagination,
99
+ // bulk-action bar) can offset itself against what's actually on screen.
100
+ // `collapsed` is a dependency because the two states are distinct elements
101
+ // — React remounts the node, so the observer must re-attach to the new one.
102
+ useEffect(() => {
103
+ if (!dismissible || !id)
104
+ return;
105
+ const el = rootRef.current;
106
+ if (!el)
107
+ return;
108
+ const write = () => {
109
+ document.documentElement.style.setProperty(TOOLBAR_HEIGHT_VAR, `${Math.round(el.getBoundingClientRect().height)}px`);
110
+ };
111
+ write();
112
+ const observer = new ResizeObserver(write);
113
+ observer.observe(el);
114
+ return () => {
115
+ observer.disconnect();
116
+ // Drop it on unmount so a route with no dismissible toolbar falls back
117
+ // to the 64px default instead of inheriting the previous page's value.
118
+ document.documentElement.style.removeProperty(TOOLBAR_HEIGHT_VAR);
119
+ };
120
+ }, [dismissible, id, collapsed]);
78
121
  const offsetCls = resolveOffsetClass(offset);
79
122
  const offsetStyle = typeof offset === "number" ? { top: `${offset}px` } : undefined;
80
123
  const borderCls = border ? "border-b border-[var(--appkit-color-border)]" : "";
@@ -86,15 +129,14 @@ export function StickyToolbar({ children, offset = "header", tone = "translucent
86
129
  writeCollapsed(id, next);
87
130
  };
88
131
  if (dismissible && id && collapsed) {
89
- return (_jsx("div", { "data-testid": dataTestId, style: offsetStyle, className: `sticky ${offsetCls} ${zCls} ${TONE_CLS[tone]} ${borderCls} flex justify-center`, children: _jsxs("button", { type: "button", onClick: toggle, "aria-label": `Show ${label}`, "aria-expanded": false, className: "flex items-center gap-1 px-4 py-0.5 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(ChevronDown, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), "Show ", label] }) }));
132
+ return (_jsx("div", { ref: rootRef, "data-testid": dataTestId, style: offsetStyle, className: `sticky ${offsetCls} ${zCls} ${TONE_CLS[tone]} ${borderCls}`, children: _jsx(CollapseStrip, { collapsed: true, label: label, onToggle: toggle }) }));
90
133
  }
91
- return (_jsxs("div", { role: role, "data-testid": dataTestId, style: offsetStyle,
134
+ return (_jsxs("div", { ref: rootRef, role: role, "data-testid": dataTestId, style: offsetStyle,
92
135
  // for the translucent sticky-toolbar pattern. The header offset is
93
136
  // sourced from --header-height (set by AppLayoutShell at runtime).
94
- // `relative` only when dismissible — the absolutely-positioned toggle
95
- // button anchors to it without disturbing the existing internal
96
- // layout of `children` (each of the 27+ migrated call sites has its
97
- // own Row/Div structure; a flex wrapper here would double-nest and
98
- // risk breaking their centering).
99
- className: `sticky ${offsetCls} ${zCls} ${TONE_CLS[tone]} ${borderCls} ${PADDING_CLS[padding]} ${dismissible ? "relative pr-8" : ""} ${className ?? ""}`, children: [children, dismissible && id && (_jsx("button", { type: "button", onClick: toggle, "aria-label": `Hide ${label}`, "aria-expanded": true, className: "absolute right-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-[var(--appkit-color-text)]", children: _jsx(ChevronDown, { className: "h-3.5 w-3.5 rotate-180 transition-transform duration-150", "aria-hidden": "true" }) }))] }));
137
+ // The collapse control is a full-width strip appended AFTER `children`
138
+ // rather than an element positioned over them, so it never competes
139
+ // with the internal Row/Div layout each of the 27+ migrated call sites
140
+ // brings of its own.
141
+ className: `sticky ${offsetCls} ${zCls} ${TONE_CLS[tone]} ${borderCls} ${PADDING_CLS[padding]} ${className ?? ""}`, children: [children, dismissible && id && (_jsx(CollapseStrip, { collapsed: false, label: label, onToggle: toggle, className: "mt-1.5" }))] }));
100
142
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The portal target inside `<BottomChrome>` — the middle tier of the bottom
3
+ * edge, between the nav bar and BackToTop.
4
+ *
5
+ * Bars that live in the app shell (BottomActions) are rendered as children of
6
+ * the tier directly. Bars authored deep in the tree (ListingLayout's mobile
7
+ * pagination) portal into this node instead, so the tier still measures them
8
+ * and every offset above stays correct.
9
+ *
10
+ * This lives in `ui/` rather than next to `<BottomChrome>` in `features/layout/`
11
+ * on purpose: `features/layout` imports from `ui`, so a `ui` component reaching
12
+ * back into `features/layout` would close an import cycle through the `ui`
13
+ * barrel. Nothing here imports anything but React.
14
+ */
15
+ export declare const BOTTOM_CHROME_SLOT_ID = "appkit-bottom-chrome-slot";
16
+ /**
17
+ * Resolves the tier's portal target. Returns `null` until after mount, so a
18
+ * consumer never touches the DOM during render — render nothing while it is
19
+ * null, and portal into it once it resolves.
20
+ */
21
+ export declare function useBottomChromeSlot(): HTMLElement | null;
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { useEffect, useState } from "react";
3
+ /**
4
+ * The portal target inside `<BottomChrome>` — the middle tier of the bottom
5
+ * edge, between the nav bar and BackToTop.
6
+ *
7
+ * Bars that live in the app shell (BottomActions) are rendered as children of
8
+ * the tier directly. Bars authored deep in the tree (ListingLayout's mobile
9
+ * pagination) portal into this node instead, so the tier still measures them
10
+ * and every offset above stays correct.
11
+ *
12
+ * This lives in `ui/` rather than next to `<BottomChrome>` in `features/layout/`
13
+ * on purpose: `features/layout` imports from `ui`, so a `ui` component reaching
14
+ * back into `features/layout` would close an import cycle through the `ui`
15
+ * barrel. Nothing here imports anything but React.
16
+ */
17
+ export const BOTTOM_CHROME_SLOT_ID = "appkit-bottom-chrome-slot";
18
+ /**
19
+ * Resolves the tier's portal target. Returns `null` until after mount, so a
20
+ * consumer never touches the DOM during render — render nothing while it is
21
+ * null, and portal into it once it resolves.
22
+ */
23
+ export function useBottomChromeSlot() {
24
+ const [slot, setSlot] = useState(null);
25
+ useEffect(() => {
26
+ setSlot(document.getElementById(BOTTOM_CHROME_SLOT_ID));
27
+ }, []);
28
+ return slot;
29
+ }
@@ -74,3 +74,6 @@
74
74
  @import "./UnsavedChangesModal.style.css";
75
75
  @import "../DataTable.style.css";
76
76
  @import "../rich-text/RichText.style.css";
77
+ /* Last on purpose: hand-mode rules are cross-cutting overrides keyed on
78
+ `[data-hand]`, not a component's own base styles. */
79
+ @import "./HandMode.style.css";