@mohasinac/appkit 2.7.27 → 2.7.30

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 (359) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/server/features/checkout/actions.js +42 -35
  3. package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
  4. package/dist/_internal/server/features/classified/adapters.js +24 -0
  5. package/dist/_internal/server/features/classified/index.d.ts +3 -0
  6. package/dist/_internal/server/features/classified/index.js +3 -0
  7. package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
  8. package/dist/_internal/server/features/classified/metadata.js +43 -0
  9. package/dist/_internal/server/features/classified/og.d.ts +30 -0
  10. package/dist/_internal/server/features/classified/og.js +76 -0
  11. package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
  12. package/dist/_internal/server/features/digital-code/adapters.js +29 -0
  13. package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
  14. package/dist/_internal/server/features/digital-code/index.js +3 -0
  15. package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
  16. package/dist/_internal/server/features/digital-code/metadata.js +36 -0
  17. package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
  18. package/dist/_internal/server/features/digital-code/og.js +77 -0
  19. package/dist/_internal/server/features/live/adapters.d.ts +21 -0
  20. package/dist/_internal/server/features/live/adapters.js +34 -0
  21. package/dist/_internal/server/features/live/index.d.ts +3 -0
  22. package/dist/_internal/server/features/live/index.js +3 -0
  23. package/dist/_internal/server/features/live/metadata.d.ts +8 -0
  24. package/dist/_internal/server/features/live/metadata.js +39 -0
  25. package/dist/_internal/server/features/live/og.d.ts +27 -0
  26. package/dist/_internal/server/features/live/og.js +72 -0
  27. package/dist/_internal/server/features/promotions/actions.js +4 -3
  28. package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
  29. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
  30. package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
  31. package/dist/_internal/shared/actions/action-registry.js +310 -8
  32. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  33. package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
  34. package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
  35. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  36. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
  37. package/dist/_internal/shared/features/events/schema.d.ts +8 -8
  38. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  39. package/dist/_internal/shared/features/products/schema.d.ts +42 -42
  40. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  41. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  42. package/dist/_internal/shared/media/limits.js +4 -3
  43. package/dist/client.d.ts +2 -0
  44. package/dist/client.js +1 -0
  45. package/dist/configs/next.js +4 -0
  46. package/dist/constants/table-keys.d.ts +6 -0
  47. package/dist/constants/table-keys.js +9 -0
  48. package/dist/core/hooks/useSyncManager.js +9 -8
  49. package/dist/features/about/components/FeesView.js +10 -7
  50. package/dist/features/about/components/HelpPageView.js +10 -1
  51. package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
  52. package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
  53. package/dist/features/about/components/HowOffersWorkView.js +10 -1
  54. package/dist/features/about/components/HowOrdersWorkView.js +10 -1
  55. package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
  56. package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
  57. package/dist/features/about/components/HowReviewsWorkView.js +10 -1
  58. package/dist/features/about/components/PublicProfileView.js +44 -14
  59. package/dist/features/about/components/SecurityPrivacyView.js +7 -1
  60. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  61. package/dist/features/account/components/NotificationBell.js +27 -16
  62. package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
  63. package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
  64. package/dist/features/account/components/UserSidebar.js +4 -4
  65. package/dist/features/account/components/UserSupportView.js +16 -6
  66. package/dist/features/account/components/index.d.ts +2 -0
  67. package/dist/features/account/components/index.js +1 -0
  68. package/dist/features/account/schemas/index.d.ts +4 -4
  69. package/dist/features/account/types/index.d.ts +34 -0
  70. package/dist/features/admin/actions/admin-actions.js +4 -3
  71. package/dist/features/admin/actions/notification-actions.d.ts +24 -22
  72. package/dist/features/admin/actions/notification-actions.js +122 -22
  73. package/dist/features/admin/components/AdminAdEditorView.js +29 -15
  74. package/dist/features/admin/components/AdminAdsView.js +22 -28
  75. package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
  76. package/dist/features/admin/components/AdminBidsView.js +10 -3
  77. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  78. package/dist/features/admin/components/AdminBlogView.js +8 -3
  79. package/dist/features/admin/components/AdminBrandsView.js +7 -2
  80. package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
  81. package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
  82. package/dist/features/admin/components/AdminCarouselView.js +13 -7
  83. package/dist/features/admin/components/AdminCategoriesView.js +8 -3
  84. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  85. package/dist/features/admin/components/AdminContactView.js +6 -1
  86. package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
  87. package/dist/features/admin/components/AdminCouponsView.js +6 -1
  88. package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
  89. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  90. package/dist/features/admin/components/AdminFaqsView.js +7 -2
  91. package/dist/features/admin/components/AdminFeaturesView.js +4 -3
  92. package/dist/features/admin/components/AdminMediaView.js +9 -8
  93. package/dist/features/admin/components/AdminNavigationView.js +10 -17
  94. package/dist/features/admin/components/AdminNewsletterView.js +6 -1
  95. package/dist/features/admin/components/AdminNotificationsView.js +11 -3
  96. package/dist/features/admin/components/AdminOrdersView.js +6 -1
  97. package/dist/features/admin/components/AdminPayoutsView.js +10 -3
  98. package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
  99. package/dist/features/admin/components/AdminProductsView.js +18 -12
  100. package/dist/features/admin/components/AdminReviewsView.js +33 -24
  101. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  102. package/dist/features/admin/components/AdminScammersView.js +28 -26
  103. package/dist/features/admin/components/AdminSectionsView.js +112 -99
  104. package/dist/features/admin/components/AdminSessionsView.js +10 -3
  105. package/dist/features/admin/components/AdminSidebar.js +4 -4
  106. package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
  107. package/dist/features/admin/components/AdminStoresView.js +6 -1
  108. package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
  109. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  110. package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
  111. package/dist/features/admin/components/AdminTeamView.js +6 -1
  112. package/dist/features/admin/components/AdminUserEditorView.js +8 -15
  113. package/dist/features/admin/components/AdminUsersView.js +6 -1
  114. package/dist/features/admin/components/AdminViewCards.js +4 -3
  115. package/dist/features/admin/components/DashboardStats.js +2 -2
  116. package/dist/features/admin/components/DataTable.js +2 -2
  117. package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
  118. package/dist/features/admin/repository/chat.repository.js +8 -8
  119. package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
  120. package/dist/features/admin/repository/site-settings.repository.js +11 -9
  121. package/dist/features/admin/schemas/firestore.d.ts +38 -0
  122. package/dist/features/admin/schemas/firestore.js +15 -0
  123. package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
  124. package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
  125. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
  126. package/dist/features/auctions/components/index.d.ts +0 -1
  127. package/dist/features/auctions/components/index.js +0 -1
  128. package/dist/features/auctions/schemas/index.d.ts +14 -14
  129. package/dist/features/auth/actions/realtime-token-actions.js +19 -4
  130. package/dist/features/auth/components/RegisterForm.js +8 -4
  131. package/dist/features/auth/consent-otp.d.ts +2 -0
  132. package/dist/features/auth/consent-otp.js +8 -0
  133. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  134. package/dist/features/auth/schemas/firestore.js +18 -25
  135. package/dist/features/auth/schemas/index.d.ts +2 -2
  136. package/dist/features/before-after/api/[id]/route.js +8 -6
  137. package/dist/features/before-after/schemas/index.d.ts +2 -2
  138. package/dist/features/blog/actions/blog-actions.d.ts +3 -3
  139. package/dist/features/blog/components/BlogIndexListing.js +21 -2
  140. package/dist/features/blog/components/BlogPostForm.js +11 -5
  141. package/dist/features/blog/components/BlogPostView.js +10 -2
  142. package/dist/features/blog/schemas/index.d.ts +18 -18
  143. package/dist/features/cart/components/CartDrawer.js +3 -2
  144. package/dist/features/cart/repository/cart.repository.js +4 -3
  145. package/dist/features/cart/utils/pending-ops.d.ts +1 -1
  146. package/dist/features/categories/api/[id]/route.js +4 -3
  147. package/dist/features/categories/api/route.js +4 -3
  148. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  149. package/dist/features/categories/components/BrandDetailTabs.js +2 -2
  150. package/dist/features/categories/components/BundleCollage.d.ts +8 -0
  151. package/dist/features/categories/components/BundleCollage.js +50 -0
  152. package/dist/features/categories/components/BundleDetailView.js +2 -10
  153. package/dist/features/categories/components/BundleItemsPicker.js +20 -13
  154. package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
  155. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  156. package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
  157. package/dist/features/categories/components/CategoryProductsListing.js +52 -13
  158. package/dist/features/categories/components/index.d.ts +2 -0
  159. package/dist/features/categories/components/index.js +1 -0
  160. package/dist/features/categories/schemas/index.d.ts +6 -6
  161. package/dist/features/checkout/actions/checkout-actions.js +4 -3
  162. package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
  163. package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
  164. package/dist/features/classified/components/ClassifiedFilters.js +19 -0
  165. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
  166. package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
  167. package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
  168. package/dist/features/classified/components/ClassifiedListView.js +43 -0
  169. package/dist/features/classified/components/index.d.ts +6 -0
  170. package/dist/features/classified/components/index.js +3 -0
  171. package/dist/features/collections/api/[slug]/route.js +16 -12
  172. package/dist/features/collections/schemas/index.d.ts +5 -5
  173. package/dist/features/consultation/api/[id]/route.js +12 -9
  174. package/dist/features/consultation/components/ConsultationForm.js +3 -2
  175. package/dist/features/consultation/schemas/index.d.ts +6 -6
  176. package/dist/features/contact/email.js +77 -116
  177. package/dist/features/copilot/components/AdminCopilotView.js +12 -2
  178. package/dist/features/corporate/api/[id]/route.js +12 -9
  179. package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
  180. package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
  181. package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
  182. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
  183. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
  184. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
  185. package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
  186. package/dist/features/digital-codes/components/index.d.ts +6 -0
  187. package/dist/features/digital-codes/components/index.js +3 -0
  188. package/dist/features/events/actions/event-actions.js +4 -3
  189. package/dist/features/events/components/AdminEventEditorView.js +3 -2
  190. package/dist/features/events/components/AdminEventsView.js +4 -2
  191. package/dist/features/events/components/EventPollWidget.js +5 -5
  192. package/dist/features/events/components/EventsIndexListing.js +2 -2
  193. package/dist/features/events/schemas/index.d.ts +20 -20
  194. package/dist/features/faq/actions/faq-actions.d.ts +8 -8
  195. package/dist/features/faq/components/ContactCTA.js +3 -1
  196. package/dist/features/faq/components/FAQAccordion.js +1 -1
  197. package/dist/features/faq/schemas/index.d.ts +4 -4
  198. package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
  199. package/dist/features/filters/AsyncFacetSection.js +65 -0
  200. package/dist/features/forms/Checkbox.js +2 -2
  201. package/dist/features/forms/Input.js +2 -2
  202. package/dist/features/forms/Radio.js +5 -5
  203. package/dist/features/forms/Select.js +2 -2
  204. package/dist/features/forms/Slider.js +2 -2
  205. package/dist/features/forms/Textarea.js +2 -2
  206. package/dist/features/homepage/api/[id]/route.js +4 -3
  207. package/dist/features/homepage/api/sections/[id]/route.js +12 -9
  208. package/dist/features/homepage/components/AdSlot.js +2 -1
  209. package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
  210. package/dist/features/homepage/components/BrandsSection.js +2 -2
  211. package/dist/features/homepage/components/CTABannerSection.js +2 -2
  212. package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
  213. package/dist/features/homepage/components/CharacterHotspot.js +119 -157
  214. package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
  215. package/dist/features/homepage/components/CustomCardsSection.js +8 -6
  216. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  217. package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
  218. package/dist/features/homepage/components/HeroBanner.js +5 -4
  219. package/dist/features/homepage/components/HeroCarousel.js +13 -26
  220. package/dist/features/homepage/components/HowItWorksSection.js +4 -2
  221. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  222. package/dist/features/homepage/components/SocialPostCard.js +3 -2
  223. package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
  224. package/dist/features/layout/AppLayoutShell.d.ts +8 -2
  225. package/dist/features/layout/AppLayoutShell.js +54 -78
  226. package/dist/features/layout/BottomActions.js +43 -48
  227. package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
  228. package/dist/features/live/components/LiveItemFilters.js +23 -0
  229. package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
  230. package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
  231. package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
  232. package/dist/features/live/components/LiveItemsListView.js +43 -0
  233. package/dist/features/live/components/index.d.ts +6 -0
  234. package/dist/features/live/components/index.js +3 -0
  235. package/dist/features/media/MediaLightbox.js +8 -14
  236. package/dist/features/media/types/index.d.ts +2 -2
  237. package/dist/features/media/upload/CameraCapture.js +2 -2
  238. package/dist/features/media/upload/ImageUpload.js +4 -2
  239. package/dist/features/media/upload/MediaUploadField.js +24 -8
  240. package/dist/features/media/upload/MediaUploadList.js +1 -1
  241. package/dist/features/orders/actions/order-actions.js +4 -3
  242. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
  243. package/dist/features/orders/components/OrderFilters.js +12 -9
  244. package/dist/features/orders/schemas/index.d.ts +6 -6
  245. package/dist/features/pre-orders/api/[id]/route.js +12 -9
  246. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
  247. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
  248. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
  249. package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
  250. package/dist/features/pre-orders/components/PreorderCard.js +2 -13
  251. package/dist/features/pre-orders/components/index.d.ts +1 -1
  252. package/dist/features/pre-orders/components/index.js +1 -1
  253. package/dist/features/pre-orders/schemas/index.d.ts +10 -10
  254. package/dist/features/products/api/[id]/route.js +9 -7
  255. package/dist/features/products/api/route.js +1 -1
  256. package/dist/features/products/columns/productTableColumns.js +2 -2
  257. package/dist/features/products/components/AuctionsIndexListing.js +15 -51
  258. package/dist/features/products/components/BidHistory.js +2 -2
  259. package/dist/features/products/components/CompareOverlay.js +3 -2
  260. package/dist/features/products/components/MakeOfferButton.js +20 -14
  261. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
  262. package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
  263. package/dist/features/products/components/PrizeDrawCollage.js +54 -14
  264. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  265. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  266. package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
  267. package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
  268. package/dist/features/products/components/PrizeRevealModal.js +24 -14
  269. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  270. package/dist/features/products/components/ProductFilters.js +8 -6
  271. package/dist/features/products/components/ProductForm.js +12 -10
  272. package/dist/features/products/components/ProductGalleryClient.js +2 -1
  273. package/dist/features/products/components/ProductGrid.js +11 -14
  274. package/dist/features/products/components/ProductsIndexListing.js +16 -52
  275. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  276. package/dist/features/products/schemas/index.d.ts +56 -56
  277. package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
  278. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  279. package/dist/features/promotions/schemas/index.d.ts +6 -6
  280. package/dist/features/reviews/actions/review-actions.js +6 -5
  281. package/dist/features/reviews/api/[id]/route.js +4 -3
  282. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  283. package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
  284. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  285. package/dist/features/reviews/schemas/index.d.ts +6 -6
  286. package/dist/features/scams/components/ScamProfileView.js +19 -10
  287. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  288. package/dist/features/search/components/Search.d.ts +1 -1
  289. package/dist/features/search/components/Search.js +56 -49
  290. package/dist/features/search/schemas/index.d.ts +5 -5
  291. package/dist/features/seller/actions/offer-actions.js +11 -9
  292. package/dist/features/seller/components/SellerAddressesView.js +6 -4
  293. package/dist/features/seller/components/SellerBidsView.js +2 -2
  294. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  295. package/dist/features/seller/components/SellerOffersPanel.js +6 -1
  296. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  297. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
  298. package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
  299. package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
  300. package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
  301. package/dist/features/seller/components/SellerProductShell.js +75 -9
  302. package/dist/features/seller/components/SellerProductsView.js +10 -11
  303. package/dist/features/seller/components/SellerShippingView.js +3 -3
  304. package/dist/features/seller/components/SellerSidebar.js +10 -7
  305. package/dist/features/seller/components/SellerStorefrontView.js +3 -3
  306. package/dist/features/seller/schemas/index.d.ts +4 -4
  307. package/dist/features/shell/FormShell.js +2 -1
  308. package/dist/features/shell/QuickFormDrawer.js +4 -3
  309. package/dist/features/shell/StepForm.js +4 -3
  310. package/dist/features/stores/actions/store-query-actions.js +4 -3
  311. package/dist/features/stores/components/StoreAboutView.js +2 -1
  312. package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
  313. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
  314. package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
  315. package/dist/features/stores/components/StoreProductsListing.js +16 -49
  316. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  317. package/dist/features/stores/schemas/index.d.ts +2 -2
  318. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
  319. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
  320. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
  321. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
  322. package/dist/index.d.ts +21 -3
  323. package/dist/index.js +1737 -1725
  324. package/dist/monitoring/server-logger.js +5 -9
  325. package/dist/next/api/apiHandler.js +56 -37
  326. package/dist/next/components/GlobalError.js +2 -1
  327. package/dist/next/routing/route-map.d.ts +27 -0
  328. package/dist/next/routing/route-map.js +10 -0
  329. package/dist/react/ErrorBoundary.js +2 -1
  330. package/dist/react/contexts/SessionContext.js +38 -28
  331. package/dist/react/hooks/useCamera.d.ts +1 -1
  332. package/dist/react/hooks/useCamera.js +8 -7
  333. package/dist/seed/site-settings-seed-data.js +6 -0
  334. package/dist/server-entry.d.ts +3 -0
  335. package/dist/server-entry.js +3 -0
  336. package/dist/server.d.ts +12 -0
  337. package/dist/server.js +15 -0
  338. package/dist/tailwind-utilities.css +1 -1
  339. package/dist/ui/DataTable.js +28 -7
  340. package/dist/ui/components/Button.d.ts +8 -1
  341. package/dist/ui/components/Button.js +47 -8
  342. package/dist/ui/components/CollageLightbox.d.ts +18 -0
  343. package/dist/ui/components/CollageLightbox.js +41 -0
  344. package/dist/ui/components/Dropdown.js +7 -6
  345. package/dist/ui/components/ImageLightbox.d.ts +7 -1
  346. package/dist/ui/components/ImageLightbox.js +15 -5
  347. package/dist/ui/components/PageLoader.js +4 -3
  348. package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
  349. package/dist/ui/components/PaginatedMultiSelect.js +141 -0
  350. package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
  351. package/dist/ui/components/Semantic.js +1 -1
  352. package/dist/ui/components/SideDrawer.js +17 -15
  353. package/dist/ui/components/index.style.css +1 -0
  354. package/dist/ui/index.d.ts +2 -0
  355. package/dist/ui/index.js +1 -0
  356. package/dist/utils/number.formatter.js +5 -13
  357. package/dist/utils/string.formatter.js +21 -18
  358. package/dist/validation/schemas.d.ts +2 -2
  359. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useMemo } from "react";
4
+ const CLS_CHECKBOX = "rounded border-zinc-300";
4
5
  import { Button } from "./components/Button";
5
6
  import { Spinner } from "./components/Spinner";
6
7
  import { Pagination } from "./components/Pagination";
@@ -78,6 +79,10 @@ pageSize: pageSizeProp, stickyHeader: stickyHeaderProp, striped: stripedProp, sh
78
79
  }
79
80
  return sorted;
80
81
  }, [data, sortKey, sortDirection]);
82
+ const handleRowSelectionChange = (item, checked) => {
83
+ const id = keyExtractor(item);
84
+ onSelectionChange?.(checked ? [...selectedIds, id] : selectedIds.filter((s) => s !== id));
85
+ };
81
86
  const paginatedData = useMemo(() => {
82
87
  if (externalPagination)
83
88
  return sortedData;
@@ -115,7 +120,7 @@ pageSize: pageSizeProp, stickyHeader: stickyHeaderProp, striped: stripedProp, sh
115
120
  mobileCardRender &&
116
121
  renderCardGrid(activeViewMode), activeViewMode === "table" && mobileCardRender && (_jsx("div", { className: "appkit-data-table__mobile-cards", "data-section": "datatable-div-638", children: paginatedData.map((item) => (_jsx(SelectableCard, { id: keyExtractor(item), selectable: selectable, selected: selectedIds.includes(keyExtractor(item)), onToggle: (id, checked) => onSelectionChange?.(checked
117
122
  ? [...selectedIds, id]
118
- : selectedIds.filter((s) => s !== id)), children: mobileCardRender(item) }, keyExtractor(item)))) })), activeViewMode === "table" && (_jsx("div", { className: "appkit-data-table__wrapper", "data-section": "datatable-div-639", children: _jsx("div", { className: `appkit-data-table__scroll ${stickyHeader ? "appkit-data-table__scroll--sticky" : ""}`, "data-section": "datatable-div-640", children: _jsxs("table", { className: "appkit-data-table__table", children: [_jsx("thead", { className: `appkit-data-table__thead ${stickyHeader ? "appkit-data-table__thead--sticky" : ""}`, children: _jsxs("tr", { children: [selectable && (_jsx("th", { scope: "col", className: "px-4 py-3 w-8", children: _jsx("input", { type: "checkbox", className: "rounded border-zinc-300", "aria-label": "Select all on page", checked: paginatedData.length > 0 &&
123
+ : selectedIds.filter((s) => s !== id)), children: mobileCardRender(item) }, keyExtractor(item)))) })), activeViewMode === "table" && (_jsx("div", { className: "appkit-data-table__wrapper", "data-section": "datatable-div-639", children: _jsx("div", { className: `appkit-data-table__scroll ${stickyHeader ? "appkit-data-table__scroll--sticky" : ""}`, "data-section": "datatable-div-640", children: _jsxs("table", { className: "appkit-data-table__table", children: [_jsx("thead", { className: `appkit-data-table__thead ${stickyHeader ? "appkit-data-table__thead--sticky" : ""}`, children: _jsxs("tr", { children: [selectable && (_jsx("th", { scope: "col", className: "px-4 py-3 w-8", children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select all on page", checked: paginatedData.length > 0 &&
119
124
  paginatedData.every((item) => selectedIds.includes(keyExtractor(item))), onChange: (e) => {
120
125
  const pageIds = paginatedData.map(keyExtractor);
121
126
  onSelectionChange?.(e.target.checked
@@ -133,15 +138,31 @@ pageSize: pageSizeProp, stickyHeader: stickyHeaderProp, striped: stripedProp, sh
133
138
  ? "appkit-data-table__row--striped"
134
139
  : "",
135
140
  onRowClick ? "appkit-data-table__row--clickable" : "",
136
- ].join(" "), onClick: () => onRowClick?.(item), children: [selectable && (_jsx("td", { className: "px-4 py-4 w-8", onClick: (e) => e.stopPropagation(), children: _jsx("input", { type: "checkbox", className: "rounded border-zinc-300", "aria-label": "Select row", checked: selectedIds.includes(keyExtractor(item)), onChange: (e) => {
137
- const id = keyExtractor(item);
138
- onSelectionChange?.(e.target.checked
139
- ? [...selectedIds, id]
140
- : selectedIds.filter((s) => s !== id));
141
- } }) })), columns.map((col) => (_jsx("td", { className: "appkit-data-table__td", children: col.render
141
+ ].join(" "), onClick: () => onRowClick?.(item), children: [selectable && (_jsx("td", { className: "px-4 py-4 w-8", onClick: (e) => e.stopPropagation(), children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select row", checked: selectedIds.includes(keyExtractor(item)), onChange: (e) => handleRowSelectionChange(item, e.target.checked) }) })), columns.map((col) => (_jsx("td", { className: "appkit-data-table__td", children: col.render
142
142
  ? col.render(item)
143
143
  : (item[col.key] ?? "-") }, col.key))), actions && (_jsx("td", { className: "appkit-data-table__td appkit-data-table__td--actions", onClick: (e) => e.stopPropagation(), children: actions(item) }))] }, keyExtractor(item)))) })] }) }) })), !externalPagination && totalPages > 1 && (_jsx("div", { className: "appkit-data-table__pagination", "data-section": "datatable-div-641", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: setCurrentPage, maxVisible: resolvedPag.maxVisible, showFirstLast: resolvedPag.showFirstLast, showPrevNext: resolvedPag.showPrevNext, size: resolvedPag.size }) }))] }));
144
144
  }
145
+ function DataTableViewToggle({ showViewToggle, showTableView, activeViewMode, labels, onViewModeChange, }) {
146
+ if (!showViewToggle)
147
+ return null;
148
+ return (_jsxs("div", { className: "appkit-data-table__view-toggle", role: "toolbar", "aria-label": "View mode", "data-section": "datatable-div-635", children: [showTableView && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("table"), "aria-label": labels.tableView, "aria-pressed": activeViewMode === "table", className: `appkit-data-table__view-btn ${activeViewMode === "table" ? "appkit-data-table__view-btn--active" : ""} hidden sm:flex`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 10h18M3 6h18M3 14h18M3 18h18" }) }) })), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("grid"), "aria-label": labels.gridView, "aria-pressed": activeViewMode === "grid", className: `appkit-data-table__view-btn ${activeViewMode === "grid" ? "appkit-data-table__view-btn--active" : ""}`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" }) }) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("list"), "aria-label": labels.listView, "aria-pressed": activeViewMode === "list", className: `appkit-data-table__view-btn ${activeViewMode === "list" ? "appkit-data-table__view-btn--active" : ""}`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" }) }) })] }));
149
+ }
150
+ function DataTableTableView({ columns, paginatedData, keyExtractor, selectable, selectedIds, stickyHeader, striped, sortKey, sortDirection, actions, labelActions, onRowClick, onSelectionChange, onSort, onRowSelectionChange, }) {
151
+ return (_jsx("div", { className: "appkit-data-table__wrapper", "data-section": "datatable-div-639", children: _jsx("div", { className: `appkit-data-table__scroll ${stickyHeader ? "appkit-data-table__scroll--sticky" : ""}`, "data-section": "datatable-div-640", children: _jsxs("table", { className: "appkit-data-table__table", children: [_jsx("thead", { className: `appkit-data-table__thead ${stickyHeader ? "appkit-data-table__thead--sticky" : ""}`, children: _jsxs("tr", { children: [selectable && (_jsx("th", { scope: "col", className: "px-4 py-3 w-8", children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select all on page", checked: paginatedData.length > 0 && paginatedData.every((item) => selectedIds.includes(keyExtractor(item))), onChange: (e) => {
152
+ const pageIds = paginatedData.map(keyExtractor);
153
+ onSelectionChange?.(e.target.checked
154
+ ? [...new Set([...selectedIds, ...pageIds])]
155
+ : selectedIds.filter((id) => !pageIds.includes(id)));
156
+ } }) })), columns.map((col) => (_jsx("th", { scope: "col", "aria-sort": col.sortable
157
+ ? sortKey === col.key
158
+ ? sortDirection === "asc" ? "ascending" : "descending"
159
+ : "none"
160
+ : undefined, className: `appkit-data-table__th ${col.sortable ? "appkit-data-table__th--sortable" : ""}`, style: { width: col.width }, onClick: () => col.sortable && onSort(col.key), children: _jsxs(Row, { gap: "sm", children: [col.header, col.sortable && (_jsx("span", { className: "text-zinc-400", "aria-hidden": "true", children: sortKey === col.key ? (sortDirection === "asc" ? "↑" : "↓") : _jsx("span", { className: "opacity-30", children: "\u2195" }) }))] }) }, col.key))), actions && (_jsx("th", { scope: "col", className: "appkit-data-table__th appkit-data-table__td--actions", children: labelActions }))] }) }), _jsx("tbody", { className: "appkit-data-table__tbody divide-y divide-zinc-200 dark:divide-slate-700", children: paginatedData.map((item, index) => (_jsxs("tr", { className: [
161
+ "appkit-data-table__row",
162
+ striped && index % 2 === 1 ? "appkit-data-table__row--striped" : "",
163
+ onRowClick ? "appkit-data-table__row--clickable" : "",
164
+ ].join(" "), onClick: () => onRowClick?.(item), children: [selectable && (_jsx("td", { className: "px-4 py-4 w-8", onClick: (e) => e.stopPropagation(), children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select row", checked: selectedIds.includes(keyExtractor(item)), onChange: (e) => onRowSelectionChange(item, e.target.checked) }) })), columns.map((col) => (_jsx("td", { className: "appkit-data-table__td", children: col.render ? col.render(item) : (item[col.key] ?? "-") }, col.key))), actions && (_jsx("td", { className: "appkit-data-table__td appkit-data-table__td--actions", onClick: (e) => e.stopPropagation(), children: actions(item) }))] }, keyExtractor(item)))) })] }) }) }));
165
+ }
145
166
  function SelectableCard({ id, selectable, selected, onToggle, children, listMode = false, }) {
146
167
  if (!selectable)
147
168
  return _jsx("div", { className: "h-full", "data-section": "datatable-div-642", children: children });
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import type { ActionDef } from "../../_internal/shared/actions/action-registry";
2
3
  /**
3
4
  * Button — versatile button with multiple variants, sizes, and loading state.
4
5
  *
@@ -28,6 +29,12 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
28
29
  children?: React.ReactNode;
29
30
  /** Render as the child element (e.g. next/link) with button styling applied */
30
31
  asChild?: boolean;
32
+ /**
33
+ * CTA registry action — auto-fills children (label), aria-label, variant, and
34
+ * shows a confirmation dialog before firing onClick when action.confirmation is set.
35
+ * Children and aria-label props override the action defaults when explicitly provided.
36
+ */
37
+ action?: ActionDef;
31
38
  }
32
- export declare function Button({ variant, size, className, isLoading, disabled, children, asChild, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function Button({ variant, size, className, isLoading, disabled, children, asChild, action, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
33
40
  export {};
@@ -1,6 +1,7 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { useCallback } from "react";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { useCallback, useState } from "react";
4
+ import { createPortal } from "react-dom";
4
5
  import { twMerge } from "tailwind-merge";
5
6
  import { Loader2 } from "lucide-react";
6
7
  function spawnRipple(host, clientX, clientY) {
@@ -37,22 +38,60 @@ const UI_BUTTON = {
37
38
  lg: "appkit-button--lg",
38
39
  },
39
40
  };
40
- export function Button({ variant = "primary", size = "md", className = "", isLoading = false, disabled, children, asChild = false, ...props }) {
41
- const classes = twMerge(UI_BUTTON.base, UI_BUTTON.variants[variant], UI_BUTTON.sizes[size], className);
41
+ // Map ActionKind → Button variant (caller can override via explicit variant prop)
42
+ const ACTION_KIND_VARIANT = {
43
+ primary: "primary",
44
+ secondary: "secondary",
45
+ danger: "danger",
46
+ ghost: "ghost",
47
+ link: "ghost",
48
+ chip: "outline",
49
+ };
50
+ export function Button({ variant, size = "md", className = "", isLoading = false, disabled, children, asChild = false, action, ...props }) {
51
+ const [confirmOpen, setConfirmOpen] = useState(false);
52
+ // Resolve defaults from action registry
53
+ const resolvedVariant = variant ?? (action ? (ACTION_KIND_VARIANT[action.kind] ?? "primary") : "primary");
54
+ const resolvedChildren = children ?? (action ? action.label : undefined);
55
+ const resolvedAriaLabel = props["aria-label"] ?? (action ? (action.ariaLabel ?? action.label) : undefined);
56
+ const classes = twMerge(UI_BUTTON.base, UI_BUTTON.variants[resolvedVariant], UI_BUTTON.sizes[size], className);
42
57
  const userOnClick = props.onClick;
43
58
  const handleClick = useCallback((event) => {
44
59
  if (!disabled && !isLoading) {
45
60
  spawnRipple(event.currentTarget, event.clientX, event.clientY);
46
61
  }
62
+ if (action?.confirmation) {
63
+ event.preventDefault();
64
+ setConfirmOpen(true);
65
+ return;
66
+ }
67
+ userOnClick?.(event);
68
+ }, [disabled, isLoading, action, userOnClick]);
69
+ const handleConfirm = useCallback((event) => {
70
+ setConfirmOpen(false);
47
71
  userOnClick?.(event);
48
- }, [disabled, isLoading, userOnClick]);
49
- if (asChild && React.isValidElement(children)) {
50
- const child = children;
72
+ }, [userOnClick]);
73
+ if (asChild && React.isValidElement(resolvedChildren)) {
74
+ const child = resolvedChildren;
51
75
  return React.cloneElement(child, {
52
76
  ...props,
53
77
  className: twMerge(String(child.props.className ?? ""), classes),
54
78
  ...(disabled ? { "aria-disabled": true } : {}),
55
79
  });
56
80
  }
57
- return (_jsxs("button", { className: classes, disabled: disabled || isLoading, "aria-busy": isLoading || undefined, ...props, onClick: handleClick, children: [isLoading && (_jsx(Loader2, { className: "appkit-button__spinner", "aria-hidden": "true" })), isLoading ? (_jsx("span", { className: "appkit-button__content appkit-button__content--loading", children: children })) : (_jsx("span", { className: "appkit-button__content", children: children }))] }));
81
+ const confirmDef = action?.confirmation;
82
+ const confirmVariant = confirmDef?.confirmKind
83
+ ? (ACTION_KIND_VARIANT[confirmDef.confirmKind] ?? "primary")
84
+ : "primary";
85
+ return (_jsxs(_Fragment, { children: [_jsxs("button", { className: classes, disabled: disabled || isLoading, "aria-busy": isLoading || undefined, ...props, "aria-label": resolvedAriaLabel, onClick: handleClick, children: [isLoading && (_jsx(Loader2, { className: "appkit-button__spinner", "aria-hidden": "true" })), isLoading ? (_jsx("span", { className: "appkit-button__content appkit-button__content--loading", children: resolvedChildren })) : (_jsx("span", { className: "appkit-button__content", children: resolvedChildren }))] }), confirmOpen && confirmDef && typeof document !== "undefined" && createPortal(_jsx("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": "appkit-action-confirm-title", style: {
86
+ position: "fixed", inset: 0, zIndex: "var(--appkit-z-modal, 1000)",
87
+ display: "flex", alignItems: "center", justifyContent: "center",
88
+ background: "rgba(0,0,0,0.45)", backdropFilter: "blur(2px)",
89
+ }, onClick: (e) => { if (e.target === e.currentTarget)
90
+ setConfirmOpen(false); }, children: _jsxs("div", { style: {
91
+ background: "var(--appkit-surface, #fff)",
92
+ borderRadius: "var(--appkit-radius-lg, 12px)",
93
+ padding: "1.5rem",
94
+ maxWidth: "380px", width: "calc(100% - 2rem)",
95
+ boxShadow: "var(--appkit-shadow-lg, 0 20px 60px rgba(0,0,0,0.2))",
96
+ }, children: [_jsx("p", { id: "appkit-action-confirm-title", style: { fontWeight: 600, marginBottom: "0.5rem", fontSize: "1rem" }, children: confirmDef.title }), _jsx("p", { style: { fontSize: "0.875rem", color: "var(--appkit-color-muted, #6b7280)", marginBottom: "1.25rem" }, children: confirmDef.body }), _jsxs("div", { style: { display: "flex", gap: "0.5rem", justifyContent: "flex-end" }, children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setConfirmOpen(false), children: confirmDef.cancelLabel ?? "Cancel" }), _jsx(Button, { variant: confirmVariant, size: "sm", onClick: handleConfirm, children: confirmDef.confirmLabel })] })] }) }), document.body)] }));
58
97
  }
@@ -0,0 +1,18 @@
1
+ export interface LightboxImage {
2
+ src: string;
3
+ alt: string;
4
+ /** Short label shown in top-left corner of the enlarged image (e.g. "#1"). */
5
+ badge?: string;
6
+ /** Title/caption rendered below the image. */
7
+ caption?: string;
8
+ /** Secondary line below caption (e.g. formatted price or estimated value). */
9
+ sub?: string;
10
+ }
11
+ export interface CollageLightboxProps {
12
+ images: LightboxImage[];
13
+ /** Index of the image that triggered the open — lightbox starts here. */
14
+ initialIndex: number;
15
+ onClose: () => void;
16
+ }
17
+ export declare function CollageLightbox({ images, initialIndex, onClose, }: CollageLightboxProps): import("react/jsx-runtime").JSX.Element;
18
+ export default CollageLightbox;
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * CollageLightbox — full-screen image viewer for collage grids.
5
+ *
6
+ * Opens at the clicked index and cycles circularly through all images.
7
+ * Keyboard: Escape = close, ArrowLeft/Right = navigate.
8
+ * Clicking the backdrop closes the lightbox.
9
+ */
10
+ import React, { useCallback, useEffect } from "react";
11
+ import { Div, Text } from "./index";
12
+ export function CollageLightbox({ images, initialIndex, onClose, }) {
13
+ const [current, setCurrent] = React.useState(initialIndex);
14
+ const count = images.length;
15
+ const prev = useCallback(() => setCurrent((i) => (i - 1 + count) % count), [count]);
16
+ const next = useCallback(() => setCurrent((i) => (i + 1) % count), [count]);
17
+ useEffect(() => {
18
+ const onKey = (e) => {
19
+ if (e.key === "Escape")
20
+ onClose();
21
+ else if (e.key === "ArrowLeft")
22
+ prev();
23
+ else if (e.key === "ArrowRight")
24
+ next();
25
+ };
26
+ document.addEventListener("keydown", onKey);
27
+ return () => document.removeEventListener("keydown", onKey);
28
+ }, [onClose, prev, next]);
29
+ const img = images[current];
30
+ return (_jsx(Div, { className: "fixed inset-0 z-[var(--appkit-z-modal,9999)] flex items-center justify-center bg-black/92 backdrop-blur-sm", onClick: onClose, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: _jsxs(Div, { className: "relative flex w-full max-w-5xl flex-col items-center px-4", onClick: (e) => e.stopPropagation(), children: [_jsxs(Div, { className: "mb-3 flex w-full items-center justify-between", children: [_jsxs(Text, { className: "text-sm font-medium text-white/60", children: [current + 1, " / ", count] }), _jsx("button", { onClick: onClose, className: "rounded-full bg-white/10 p-2 text-white transition-colors hover:bg-white/25", "aria-label": "Close lightbox", children: _jsx("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsxs(Div, { className: "flex w-full items-center gap-3", children: [_jsx("button", { onClick: prev, className: "flex-shrink-0 rounded-full bg-white/10 p-3 text-white transition-colors hover:bg-white/25 disabled:opacity-30", "aria-label": "Previous image", disabled: count <= 1, children: _jsx("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), _jsxs(Div, { className: "relative flex max-h-[68vh] flex-1 items-center justify-center overflow-hidden rounded-2xl bg-black/30", children: [img?.src ? (
31
+ /* eslint-disable-next-line @next/next/no-img-element */
32
+ _jsx("img", { src: img.src, alt: img.alt, className: "max-h-[68vh] w-full object-contain" }, img.src + current)) : (_jsx(Div, { className: "flex h-64 w-full items-center justify-center text-6xl text-white/30", children: "\uD83D\uDDBC\uFE0F" })), img?.badge ? (_jsx(Div, { className: "absolute left-3 top-3 rounded bg-black/70 px-2 py-1 text-xs font-semibold text-white", children: img.badge })) : null] }), _jsx("button", { onClick: next, className: "flex-shrink-0 rounded-full bg-white/10 p-3 text-white transition-colors hover:bg-white/25 disabled:opacity-30", "aria-label": "Next image", disabled: count <= 1, children: _jsx("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }) })] }), (img?.caption || img?.sub) && (_jsxs(Div, { className: "mt-3 text-center", children: [img.caption && (_jsx(Text, { className: "text-sm font-medium text-white", children: img.caption })), img.sub && (_jsx(Text, { className: "text-xs text-white/60", children: img.sub }))] })), count > 1 && (_jsx(Div, { className: "mt-4 flex max-w-full gap-2 overflow-x-auto pb-2", children: images.map((thumb, i) => (_jsx("button", { onClick: () => setCurrent(i), className: [
33
+ "h-14 w-14 flex-shrink-0 overflow-hidden rounded-lg border-2 transition-all",
34
+ i === current
35
+ ? "scale-110 border-white"
36
+ : "border-transparent opacity-50 hover:opacity-90",
37
+ ].join(" "), "aria-label": `Go to image ${i + 1}`, children: thumb.src ? (
38
+ /* eslint-disable-next-line @next/next/no-img-element */
39
+ _jsx("img", { src: thumb.src, alt: thumb.alt, className: "h-full w-full object-cover" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center bg-white/10 text-white/30 text-lg", children: "\uD83D\uDDBC\uFE0F" })) }, i))) }))] }) }));
40
+ }
41
+ export default CollageLightbox;
@@ -89,6 +89,12 @@ export function Dropdown({ trigger, items, align = "right", className = "", menu
89
89
  }
90
90
  }
91
91
  };
92
+ const handleMenuItemClick = (item, isDisabled) => {
93
+ if (isDisabled)
94
+ return;
95
+ item.onSelect?.();
96
+ setIsOpen(false);
97
+ };
92
98
  return (_jsx(DropdownContext.Provider, { value: { isOpen, setIsOpen, close: () => setIsOpen(false) }, children: _jsxs("div", { ref: containerRef, className: [UI_DROPDOWN.root, className].filter(Boolean).join(" "), onKeyDown: onKeyDown, onClick: () => {
93
99
  if (trigger)
94
100
  return;
@@ -111,12 +117,7 @@ export function Dropdown({ trigger, items, align = "right", className = "", menu
111
117
  item.destructive ? UI_DROPDOWN.itemDestructive : "",
112
118
  ]
113
119
  .filter(Boolean)
114
- .join(" "), onClick: () => {
115
- if (isDisabled)
116
- return;
117
- item.onSelect?.();
118
- setIsOpen(false);
119
- }, onMouseEnter: () => setActiveIndex(index), children: item.label }, item.id ?? `${String(item.label)}-${index}`));
120
+ .join(" "), onClick: () => handleMenuItemClick(item, isDisabled), onMouseEnter: () => setActiveIndex(index), children: item.label }, item.id ?? `${String(item.label)}-${index}`));
120
121
  }) })) : null] }) }));
121
122
  }
122
123
  export function DropdownTrigger({ className, children, }) {
@@ -3,6 +3,10 @@ export interface LightboxImage {
3
3
  src: string;
4
4
  alt?: string;
5
5
  caption?: string;
6
+ /** Secondary line below caption (e.g. price or estimated value). */
7
+ sub?: string;
8
+ /** Badge rendered top-left on the enlarged image (e.g. "#1"). */
9
+ badge?: string;
6
10
  }
7
11
  export interface ImageLightboxProps {
8
12
  images: LightboxImage[];
@@ -10,5 +14,7 @@ export interface ImageLightboxProps {
10
14
  activeIndex: number | null;
11
15
  onClose: () => void;
12
16
  onNavigate?: (index: number) => void;
17
+ /** Show clickable thumbnail strip below the main image. */
18
+ showThumbnails?: boolean;
13
19
  }
14
- export declare function ImageLightbox({ images, activeIndex, onClose, onNavigate, }: ImageLightboxProps): React.ReactPortal | null;
20
+ export declare function ImageLightbox({ images, activeIndex, onClose, onNavigate, showThumbnails, }: ImageLightboxProps): React.ReactPortal | null;
@@ -8,7 +8,7 @@ import { Text, Span } from "./Typography";
8
8
  const MIN_ZOOM = 10;
9
9
  const MAX_ZOOM = 300;
10
10
  const ZOOM_STEP = 20;
11
- export function ImageLightbox({ images, activeIndex, onClose, onNavigate, }) {
11
+ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showThumbnails = false, }) {
12
12
  const [currentIndex, setCurrentIndex] = useState(activeIndex ?? 0);
13
13
  const [zoom, setZoom] = useState(100);
14
14
  const [rotation, setRotation] = useState(0);
@@ -87,8 +87,18 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, }) {
87
87
  const image = images[currentIndex];
88
88
  const hasMultiple = images.length > 1;
89
89
  const iconBtnClass = "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white flex items-center justify-center";
90
- return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { className: "text-white/70 text-sm font-medium", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: iconBtnClass, "aria-label": "Reset", children: _jsx(Maximize2, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-red-500/60 text-white flex items-center justify-center", "aria-label": "Close lightbox", children: _jsx(X, { className: "w-5 h-5" }) })] })] }), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(-1), className: "absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Previous image", children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsx("div", { className: "appkit-lightbox__image-wrap", style: { cursor: zoom > 100 ? "grab" : "default" }, children: _jsx("img", { src: image.src, alt: image.alt ?? "", className: "appkit-lightbox__img", draggable: false, style: {
91
- transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
92
- transition: "transform 0.2s ease",
93
- } }) }), image.caption && (_jsx(Text, { size: "sm", variant: "secondary", className: "flex-shrink-0 !text-white/70 text-center px-8 pb-4", children: image.caption })), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(1), className: "absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Next image", children: _jsx(ChevronRight, { className: "w-7 h-7" }) })), _jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 text-white/30 text-[11px] flex gap-4", children: [_jsx("span", { children: "\u2190 \u2192 navigate" }), _jsx("span", { children: "scroll to zoom" }), _jsx("span", { children: "R to rotate" }), _jsx("span", { children: "0 to reset" })] })] }), document.body);
90
+ return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { className: "text-white/70 text-sm font-medium", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: iconBtnClass, "aria-label": "Reset", children: _jsx(Maximize2, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-red-500/60 text-white flex items-center justify-center", "aria-label": "Close lightbox", children: _jsx(X, { className: "w-5 h-5" }) })] })] }), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(-1), className: "absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Previous image", children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsxs("div", { className: "appkit-lightbox__image-wrap", style: { cursor: zoom > 100 ? "grab" : "default" }, children: [_jsx("img", { src: image.src, alt: image.alt ?? "", className: "appkit-lightbox__img", draggable: false, style: {
91
+ transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
92
+ transition: "transform 0.2s ease",
93
+ } }), image.badge && (_jsx("div", { className: "absolute left-3 top-3 rounded bg-black/70 px-2 py-1 text-xs font-semibold text-white pointer-events-none", children: image.badge }))] }), (image.caption || image.sub) && (_jsxs("div", { className: "flex-shrink-0 text-center px-8 pb-2", children: [image.caption && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/80", children: image.caption })), image.sub && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/50", children: image.sub }))] })), showThumbnails && images.length > 1 && (_jsx("div", { className: "flex-shrink-0 flex gap-2 overflow-x-auto px-4 pb-3 justify-center", children: images.map((thumb, i) => (_jsx("button", { type: "button", onClick: () => {
94
+ setCurrentIndex(i);
95
+ setZoom(100);
96
+ setRotation(0);
97
+ onNavigate?.(i);
98
+ }, className: [
99
+ "h-14 w-14 flex-shrink-0 overflow-hidden rounded-lg border-2 transition-all",
100
+ i === currentIndex
101
+ ? "scale-110 border-white"
102
+ : "border-transparent opacity-50 hover:opacity-90",
103
+ ].join(" "), "aria-label": `Go to image ${i + 1}`, children: _jsx("img", { src: thumb.src, alt: thumb.alt ?? `Image ${i + 1}`, className: "h-full w-full object-cover" }) }, i))) })), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(1), className: "absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Next image", children: _jsx(ChevronRight, { className: "w-7 h-7" }) })), _jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 text-white/30 text-[11px] flex gap-4", children: [_jsx("span", { children: "\u2190 \u2192 navigate" }), _jsx("span", { children: "scroll to zoom" }), _jsx("span", { children: "R to rotate" }), _jsx("span", { children: "0 to reset" })] })] }), document.body);
94
104
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React from "react";
4
4
  import { Spinner } from "./Spinner";
5
5
  import { Button } from "./Button";
6
+ import { Text } from "./Typography";
6
7
  export function PageLoader({ children }) {
7
8
  const [timedOut, setTimedOut] = React.useState(false);
8
9
  React.useEffect(() => {
@@ -12,10 +13,10 @@ export function PageLoader({ children }) {
12
13
  if (!children) {
13
14
  // No skeleton — simple centred spinner / timeout message
14
15
  if (timedOut) {
15
- return (_jsxs("div", { className: "flex min-h-[60vh] flex-col items-center justify-center gap-4 p-8 text-center", children: [_jsx("p", { className: "text-base font-medium text-zinc-700 dark:text-zinc-300", children: "Something went wrong. Please refresh the page." }), _jsx(Button, { variant: "secondary", onClick: () => window.location.reload(), children: "Refresh" })] }));
16
+ return (_jsxs("div", { className: "flex min-h-[60vh] flex-col items-center justify-center gap-4 p-8 text-center", children: [_jsx(Text, { className: "text-base font-medium text-zinc-700 dark:text-zinc-300", children: "Something went wrong. Please refresh the page." }), _jsx(Button, { variant: "secondary", onClick: () => window.location.reload(), children: "Refresh" })] }));
16
17
  }
17
- return (_jsxs("div", { className: "flex min-h-[60vh] flex-col items-center justify-center gap-3", children: [_jsx(Spinner, { size: "lg" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Loading\u2026" })] }));
18
+ return (_jsxs("div", { className: "flex min-h-[60vh] flex-col items-center justify-center gap-3", children: [_jsx(Spinner, { size: "lg" }), _jsx(Text, { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Loading\u2026" })] }));
18
19
  }
19
20
  // Skeleton provided — render it behind a floating overlay
20
- return (_jsxs("div", { className: "relative", children: [_jsx("div", { "aria-hidden": "true", children: children }), _jsx("div", { className: "pointer-events-none fixed inset-0 z-50 flex flex-col items-center justify-center gap-4 bg-white/60 p-8 text-center backdrop-blur-sm dark:bg-zinc-900/60", children: timedOut ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-base font-medium text-zinc-700 dark:text-zinc-300", children: "Something went wrong. Please refresh the page." }), _jsx("div", { className: "pointer-events-auto", children: _jsx(Button, { variant: "secondary", onClick: () => window.location.reload(), children: "Refresh" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "lg" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Loading\u2026" })] })) })] }));
21
+ return (_jsxs("div", { className: "relative", children: [_jsx("div", { "aria-hidden": "true", children: children }), _jsx("div", { className: "pointer-events-none fixed inset-0 z-50 flex flex-col items-center justify-center gap-4 bg-white/60 p-8 text-center backdrop-blur-sm dark:bg-zinc-900/60", children: timedOut ? (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-base font-medium text-zinc-700 dark:text-zinc-300", children: "Something went wrong. Please refresh the page." }), _jsx("div", { className: "pointer-events-auto", children: _jsx(Button, { variant: "secondary", onClick: () => window.location.reload(), children: "Refresh" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "lg" }), _jsx(Text, { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Loading\u2026" })] })) })] }));
21
22
  }
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from "react";
2
+ import type { QuickFieldDef } from "../../features/shell/QuickFormDrawer";
3
+ import type { DynamicSelectOption, AsyncPage } from "./DynamicSelect";
4
+ export type { DynamicSelectOption, AsyncPage };
5
+ export interface PaginatedMultiSelectProps {
6
+ value: string[];
7
+ onChange: (values: string[], options: DynamicSelectOption[]) => void;
8
+ loadOptions?: (query: string, page: number) => Promise<AsyncPage<DynamicSelectOption>>;
9
+ options?: DynamicSelectOption[];
10
+ placeholder?: string;
11
+ searchPlaceholder?: string;
12
+ noResultsText?: string;
13
+ maxChipsVisible?: number;
14
+ disabled?: boolean;
15
+ className?: string;
16
+ ariaLabel?: string;
17
+ /** "+ Create new" support — same pattern as InlineCreateSelect */
18
+ createLabel?: string;
19
+ drawerTitle?: string;
20
+ renderCreateForm?: (props: {
21
+ onCreated: (option: DynamicSelectOption) => void;
22
+ onCancel: () => void;
23
+ }) => ReactNode;
24
+ createFields?: QuickFieldDef[];
25
+ onCreateSubmit?: (values: Record<string, unknown>) => Promise<DynamicSelectOption>;
26
+ createSubmitLabel?: string;
27
+ }
28
+ export declare function PaginatedMultiSelect({ value, onChange, loadOptions, options, placeholder, searchPlaceholder, noResultsText, maxChipsVisible, disabled, className, ariaLabel, createLabel, drawerTitle, renderCreateForm, createFields, onCreateSubmit, createSubmitLabel, }: PaginatedMultiSelectProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,141 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { Input } from "./Input";
5
+ import { SideDrawer } from "./SideDrawer";
6
+ import { Button } from "./Button";
7
+ import { QuickFormDrawer } from "../../features/shell/QuickFormDrawer";
8
+ const C = {
9
+ root: "appkit-pms",
10
+ chips: "appkit-pms__chips",
11
+ chip: "appkit-pms__chip",
12
+ chipLabel: "appkit-pms__chip-label",
13
+ chipRemove: "appkit-pms__chip-remove",
14
+ overflowChip: "appkit-pms__overflow-chip",
15
+ trigger: "appkit-pms__trigger",
16
+ triggerDisabled: "appkit-pms__trigger--disabled",
17
+ placeholder: "appkit-pms__placeholder",
18
+ triggerLeft: "appkit-pms__trigger-left",
19
+ triggerCount: "appkit-pms__trigger-count",
20
+ dropdown: "appkit-pms__dropdown",
21
+ search: "appkit-pms__search",
22
+ list: "appkit-pms__list",
23
+ option: "appkit-pms__option",
24
+ optionSelected: "appkit-pms__option--selected",
25
+ checkbox: "appkit-pms__checkbox",
26
+ checkmark: "appkit-pms__checkmark",
27
+ optionLabel: "appkit-pms__option-label",
28
+ empty: "appkit-pms__empty",
29
+ loading: "appkit-pms__loading",
30
+ loadMore: "appkit-pms__load-more",
31
+ createBtn: "appkit-pms__create-btn",
32
+ };
33
+ const CREATE_SENTINEL = "__pms_create__";
34
+ export function PaginatedMultiSelect({ value, onChange, loadOptions, options, placeholder = "Select...", searchPlaceholder = "Search...", noResultsText = "No results", maxChipsVisible = 3, disabled, className = "", ariaLabel, createLabel, drawerTitle, renderCreateForm, createFields, onCreateSubmit, createSubmitLabel, }) {
35
+ const [query, setQuery] = useState("");
36
+ const [open, setOpen] = useState(false);
37
+ const [asyncOptions, setAsyncOptions] = useState([]);
38
+ const [page, setPage] = useState(1);
39
+ const [hasMore, setHasMore] = useState(false);
40
+ const [loading, setLoading] = useState(false);
41
+ const [drawerOpen, setDrawerOpen] = useState(false);
42
+ const containerRef = useRef(null);
43
+ const hasCreate = Boolean(createLabel && (renderCreateForm ?? (createFields && onCreateSubmit)));
44
+ const resolvedOptions = options ?? asyncOptions;
45
+ const selectedOptions = resolvedOptions.filter((o) => value.includes(o.value));
46
+ // Build a label map from whatever is loaded so chips show labels even when not in current page
47
+ const labelMap = useRef(new Map());
48
+ selectedOptions.forEach((o) => labelMap.current.set(o.value, o.label));
49
+ const load = useCallback(async (search, nextPage, reset = false) => {
50
+ if (!loadOptions)
51
+ return;
52
+ setLoading(true);
53
+ try {
54
+ const response = await loadOptions(search, nextPage);
55
+ setAsyncOptions((prev) => {
56
+ const merged = reset ? response.items : [...prev, ...response.items];
57
+ merged.forEach((o) => labelMap.current.set(o.value, o.label));
58
+ return merged;
59
+ });
60
+ setPage(nextPage);
61
+ setHasMore(response.hasMore);
62
+ }
63
+ finally {
64
+ setLoading(false);
65
+ }
66
+ }, [loadOptions]);
67
+ useEffect(() => {
68
+ if (!open || !loadOptions)
69
+ return;
70
+ void load(query, 1, true);
71
+ }, [open, query, load, loadOptions]);
72
+ useEffect(() => {
73
+ const onClickOutside = (e) => {
74
+ if (!containerRef.current?.contains(e.target))
75
+ setOpen(false);
76
+ };
77
+ const onKeydown = (e) => {
78
+ if (e.key === "Escape")
79
+ setOpen(false);
80
+ };
81
+ document.addEventListener("mousedown", onClickOutside);
82
+ document.addEventListener("keydown", onKeydown);
83
+ return () => {
84
+ document.removeEventListener("mousedown", onClickOutside);
85
+ document.removeEventListener("keydown", onKeydown);
86
+ };
87
+ }, []);
88
+ const filtered = options
89
+ ? options.filter((o) => o.label.toLowerCase().includes(query.toLowerCase()))
90
+ : resolvedOptions;
91
+ function toggle(opt) {
92
+ if (opt.value === CREATE_SENTINEL) {
93
+ setOpen(false);
94
+ setDrawerOpen(true);
95
+ return;
96
+ }
97
+ const isSelected = value.includes(opt.value);
98
+ const nextValues = isSelected
99
+ ? value.filter((v) => v !== opt.value)
100
+ : [...value, opt.value];
101
+ const nextOptions = nextValues.map((v) => {
102
+ const found = resolvedOptions.find((o) => o.value === v);
103
+ return found ?? { value: v, label: labelMap.current.get(v) ?? v };
104
+ });
105
+ onChange(nextValues, nextOptions);
106
+ }
107
+ function removeChip(v) {
108
+ const nextValues = value.filter((x) => x !== v);
109
+ const nextOptions = nextValues.map((x) => {
110
+ const found = resolvedOptions.find((o) => o.value === x);
111
+ return found ?? { value: x, label: labelMap.current.get(x) ?? x };
112
+ });
113
+ onChange(nextValues, nextOptions);
114
+ }
115
+ function handleCreated(option) {
116
+ setDrawerOpen(false);
117
+ labelMap.current.set(option.value, option.label);
118
+ const nextValues = [...value, option.value];
119
+ const nextOptions = nextValues.map((v) => {
120
+ const found = resolvedOptions.find((o) => o.value === v);
121
+ return found ?? { value: v, label: labelMap.current.get(v) ?? v };
122
+ });
123
+ onChange(nextValues, nextOptions);
124
+ }
125
+ const visibleChips = value.slice(0, maxChipsVisible);
126
+ const overflowCount = value.length - maxChipsVisible;
127
+ const displayOptions = hasCreate
128
+ ? [...filtered, { value: CREATE_SENTINEL, label: `+ Create new ${createLabel}` }]
129
+ : filtered;
130
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { ref: containerRef, className: [C.root, className].filter(Boolean).join(" "), "aria-label": ariaLabel, children: [value.length > 0 && (_jsxs("div", { className: C.chips, children: [visibleChips.map((v) => (_jsxs("span", { className: C.chip, children: [_jsx("span", { className: C.chipLabel, children: labelMap.current.get(v) ?? v }), !disabled && (_jsx("button", { type: "button", "aria-label": "Remove", className: C.chipRemove, onClick: (e) => { e.stopPropagation(); removeChip(v); }, children: "\u2715" }))] }, v))), overflowCount > 0 && (_jsxs("span", { className: C.overflowChip, children: ["+", overflowCount, " more"] }))] })), _jsxs("button", { type: "button", disabled: disabled, onClick: () => setOpen((prev) => !prev), className: [C.trigger, disabled ? C.triggerDisabled : ""].filter(Boolean).join(" "), "aria-expanded": open, "aria-haspopup": "listbox", children: [_jsx("span", { className: C.triggerLeft, children: _jsx("span", { className: value.length === 0 ? C.placeholder : "", children: value.length === 0 ? placeholder : `${value.length} selected` }) }), _jsx("span", { "aria-hidden": "true", children: "\u25BE" })] }), open && (_jsxs("div", { className: C.dropdown, role: "listbox", "aria-multiselectable": "true", children: [_jsx(Input, { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: searchPlaceholder, className: C.search }), _jsxs("ul", { className: C.list, children: [displayOptions.map((opt, idx) => {
131
+ const isCreate = opt.value === CREATE_SENTINEL;
132
+ const isSelected = !isCreate && value.includes(opt.value);
133
+ if (isCreate) {
134
+ return (_jsx("li", { children: _jsx("button", { type: "button", className: C.createBtn, onClick: () => toggle(opt), children: opt.label }) }, "__create__"));
135
+ }
136
+ return (_jsx("li", { children: _jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, className: [C.option, isSelected ? C.optionSelected : ""].filter(Boolean).join(" "), onClick: () => toggle(opt), children: [_jsx("span", { className: C.checkbox, "aria-hidden": "true", children: _jsx("span", { className: C.checkmark, children: "\u2713" }) }), _jsx("span", { className: C.optionLabel, children: opt.label })] }) }, `${opt.value}-${idx}`));
137
+ }), displayOptions.filter((o) => o.value !== CREATE_SENTINEL).length === 0 && !loading && (_jsx("li", { className: C.empty, children: noResultsText })), loading && _jsx("li", { className: C.loading, children: "Loading\u2026" }), hasMore && !loading && (_jsx("li", { children: _jsx("button", { type: "button", className: C.loadMore, onClick: () => void load(query, page + 1), children: "Load more" }) }))] })] }))] }), renderCreateForm ? (_jsx(SideDrawer, { isOpen: drawerOpen, onClose: () => setDrawerOpen(false), title: drawerTitle ?? `Create ${createLabel}`, mode: "create", footer: _jsx(Button, { variant: "ghost", onClick: () => setDrawerOpen(false), children: "Cancel" }), children: renderCreateForm({ onCreated: handleCreated, onCancel: () => setDrawerOpen(false) }) })) : createFields && onCreateSubmit ? (_jsx(QuickFormDrawer, { isOpen: drawerOpen, onClose: () => setDrawerOpen(false), title: drawerTitle ?? `Create ${createLabel}`, fields: createFields, submitLabel: createSubmitLabel ?? `Create ${createLabel}`, onSubmit: async (values) => {
138
+ const option = await onCreateSubmit(values);
139
+ handleCreated(option);
140
+ } })) : null] }));
141
+ }