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