@mohasinac/appkit 4.52.4 → 4.53.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/.build-stamp.json +4 -4
- package/dist/_internal/client/features/sticker-trace/canvas-codec.d.ts +2 -0
- package/dist/_internal/client/features/sticker-trace/canvas-codec.js +148 -0
- package/dist/_internal/server/features/auctions/actions.js +1 -1
- package/dist/_internal/server/features/categories/data.js +23 -7
- package/dist/_internal/server/features/checkout/actions.js +10 -3
- package/dist/_internal/server/features/checkout/data.d.ts +15 -0
- package/dist/_internal/server/features/checkout/data.js +20 -3
- package/dist/_internal/server/features/lottery/actions.js +9 -1
- package/dist/_internal/server/features/lottery/og.d.ts +40 -0
- package/dist/_internal/server/features/lottery/og.js +79 -0
- package/dist/_internal/server/features/messages/actions.js +4 -0
- package/dist/_internal/server/features/offers/adapters.js +17 -3
- package/dist/_internal/server/features/orders/actions.js +7 -2
- package/dist/_internal/server/features/raffle/actions.d.ts +19 -0
- package/dist/_internal/server/features/raffle/actions.js +25 -0
- package/dist/_internal/server/features/reviews/actions.js +1 -1
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/search/index-build/artifact-store.d.ts +89 -0
- package/dist/_internal/server/features/search/index-build/artifact-store.js +208 -0
- package/dist/_internal/server/features/search/index-build/build.d.ts +57 -0
- package/dist/_internal/server/features/search/index-build/build.js +285 -0
- package/dist/_internal/server/features/search/index-build/sources.d.ts +53 -0
- package/dist/_internal/server/features/search/index-build/sources.js +385 -0
- package/dist/_internal/server/features/search/search-sources.d.ts +0 -15
- package/dist/_internal/server/features/search/search-sources.js +142 -7
- package/dist/_internal/server/features/seo/robots.js +21 -1
- package/dist/_internal/server/features/seo/sitemap.js +1 -1
- package/dist/_internal/server/features/shared/listing-data-factory.js +16 -4
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +26 -0
- package/dist/_internal/server/features/sticker-trace/actions.d.ts +109 -0
- package/dist/_internal/server/features/sticker-trace/actions.js +172 -0
- package/dist/_internal/server/features/sticker-trace/adapters.d.ts +80 -0
- package/dist/_internal/server/features/sticker-trace/adapters.js +174 -0
- package/dist/_internal/server/features/sticker-trace/build-cut-file.d.ts +26 -0
- package/dist/_internal/server/features/sticker-trace/build-cut-file.js +127 -0
- package/dist/_internal/server/features/sticker-trace/coordinator.d.ts +27 -0
- package/dist/_internal/server/features/sticker-trace/coordinator.js +169 -0
- package/dist/_internal/server/features/sticker-trace/fanout.d.ts +43 -0
- package/dist/_internal/server/features/sticker-trace/fanout.js +180 -0
- package/dist/_internal/server/features/sticker-trace/finalizer.d.ts +25 -0
- package/dist/_internal/server/features/sticker-trace/finalizer.js +123 -0
- package/dist/_internal/server/features/sticker-trace/mosaic-inputs.d.ts +30 -0
- package/dist/_internal/server/features/sticker-trace/mosaic-inputs.js +112 -0
- package/dist/_internal/server/features/sticker-trace/progress.d.ts +81 -0
- package/dist/_internal/server/features/sticker-trace/progress.js +198 -0
- package/dist/_internal/server/features/sticker-trace/promote.d.ts +23 -0
- package/dist/_internal/server/features/sticker-trace/promote.js +193 -0
- package/dist/_internal/server/features/sticker-trace/shard.d.ts +50 -0
- package/dist/_internal/server/features/sticker-trace/shard.js +174 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +141 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.js +279 -0
- package/dist/_internal/server/features/sticker-trace/storage.d.ts +135 -0
- package/dist/_internal/server/features/sticker-trace/storage.js +282 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +25 -1
- package/dist/_internal/server/jobs/core/_event-ref.d.ts +51 -0
- package/dist/_internal/server/jobs/core/_event-ref.js +63 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +18 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +77 -16
- package/dist/_internal/server/jobs/core/auctionSettlement.js +13 -4
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +44 -15
- package/dist/_internal/server/jobs/core/bundleStockSync.js +3 -4
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +44 -0
- package/dist/_internal/server/jobs/core/event-draw-reasons.d.ts +78 -0
- package/dist/_internal/server/jobs/core/event-draw-reasons.js +133 -0
- package/dist/_internal/server/jobs/core/listingProcessor.js +28 -8
- package/dist/_internal/server/jobs/core/onOrderCreate.js +12 -1
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +13 -2
- package/dist/_internal/server/jobs/core/onScamReportRejected.js +8 -2
- package/dist/_internal/server/jobs/core/onScamReportVerified.js +8 -2
- package/dist/_internal/server/jobs/core/onStoreWrite.js +11 -4
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +5 -4
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +1 -4
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +1 -2
- package/dist/_internal/server/jobs/core/promotions.js +7 -4
- package/dist/_internal/server/jobs/core/searchIndexBuild.d.ts +7 -0
- package/dist/_internal/server/jobs/core/searchIndexBuild.js +211 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.d.ts +35 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +213 -31
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +20 -14
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +8 -0
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +15 -15
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onProductWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onReviewWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onScamReportUpdate.js +18 -0
- package/dist/_internal/server/jobs/handlers/onStoreWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/searchIndexBuild.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/searchIndexBuild.js +2 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +8 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -6
- package/dist/_internal/shared/actions/action-registry.js +29 -0
- package/dist/_internal/shared/cache/revalidate-targets.d.ts +0 -1
- package/dist/_internal/shared/cache/revalidate-targets.js +10 -8
- package/dist/_internal/shared/features/events/config.d.ts +14 -0
- package/dist/_internal/shared/features/events/config.js +14 -0
- package/dist/_internal/shared/features/notifications/action-url.js +8 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/features/sticker-trace/codec.d.ts +81 -0
- package/dist/_internal/shared/features/sticker-trace/codec.js +30 -0
- package/dist/_internal/shared/features/sticker-trace/config.d.ts +81 -0
- package/dist/_internal/shared/features/sticker-trace/config.js +177 -0
- package/dist/_internal/shared/features/sticker-trace/cut-contour.d.ts +20 -0
- package/dist/_internal/shared/features/sticker-trace/cut-contour.js +60 -0
- package/dist/_internal/shared/features/sticker-trace/export/build-export-set.d.ts +65 -0
- package/dist/_internal/shared/features/sticker-trace/export/build-export-set.js +176 -0
- package/dist/_internal/shared/features/sticker-trace/export/cut-svg.d.ts +56 -0
- package/dist/_internal/shared/features/sticker-trace/export/cut-svg.js +120 -0
- package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.d.ts +92 -0
- package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.js +153 -0
- package/dist/_internal/shared/features/sticker-trace/export/nesting.d.ts +51 -0
- package/dist/_internal/shared/features/sticker-trace/export/nesting.js +110 -0
- package/dist/_internal/shared/features/sticker-trace/export/page-sizes.d.ts +68 -0
- package/dist/_internal/shared/features/sticker-trace/export/page-sizes.js +223 -0
- package/dist/_internal/shared/features/sticker-trace/export/part-tiling.d.ts +51 -0
- package/dist/_internal/shared/features/sticker-trace/export/part-tiling.js +178 -0
- package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.d.ts +26 -0
- package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.js +134 -0
- package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.d.ts +40 -0
- package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.js +195 -0
- package/dist/_internal/shared/features/sticker-trace/index.d.ts +2 -0
- package/dist/_internal/shared/features/sticker-trace/index.js +59 -0
- package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.d.ts +11 -0
- package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.js +66 -0
- package/dist/_internal/shared/features/sticker-trace/math/edt.d.ts +27 -0
- package/dist/_internal/shared/features/sticker-trace/math/edt.js +165 -0
- package/dist/_internal/shared/features/sticker-trace/math/label.d.ts +15 -0
- package/dist/_internal/shared/features/sticker-trace/math/label.js +156 -0
- package/dist/_internal/shared/features/sticker-trace/math/marching-squares.d.ts +16 -0
- package/dist/_internal/shared/features/sticker-trace/math/marching-squares.js +242 -0
- package/dist/_internal/shared/features/sticker-trace/math/morphology.d.ts +46 -0
- package/dist/_internal/shared/features/sticker-trace/math/morphology.js +168 -0
- package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.d.ts +88 -0
- package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.js +286 -0
- package/dist/_internal/shared/features/sticker-trace/math/rdp.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/math/rdp.js +74 -0
- package/dist/_internal/shared/features/sticker-trace/resume.d.ts +50 -0
- package/dist/_internal/shared/features/sticker-trace/resume.js +160 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cluster.d.ts +20 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cluster.js +132 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.d.ts +28 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.js +120 -0
- package/dist/_internal/shared/features/sticker-trace/trace/flood.d.ts +21 -0
- package/dist/_internal/shared/features/sticker-trace/trace/flood.js +104 -0
- package/dist/_internal/shared/features/sticker-trace/trace/geometry.d.ts +3 -0
- package/dist/_internal/shared/features/sticker-trace/trace/geometry.js +140 -0
- package/dist/_internal/shared/features/sticker-trace/trace/line-filter.d.ts +15 -0
- package/dist/_internal/shared/features/sticker-trace/trace/line-filter.js +138 -0
- package/dist/_internal/shared/features/sticker-trace/trace/merge.d.ts +27 -0
- package/dist/_internal/shared/features/sticker-trace/trace/merge.js +187 -0
- package/dist/_internal/shared/features/sticker-trace/trace/paper.d.ts +32 -0
- package/dist/_internal/shared/features/sticker-trace/trace/paper.js +147 -0
- package/dist/_internal/shared/features/sticker-trace/trace/prefilter.d.ts +30 -0
- package/dist/_internal/shared/features/sticker-trace/trace/prefilter.js +125 -0
- package/dist/_internal/shared/features/sticker-trace/trace/quantise.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/trace/quantise.js +153 -0
- package/dist/_internal/shared/features/sticker-trace/trace/ridge.d.ts +88 -0
- package/dist/_internal/shared/features/sticker-trace/trace/ridge.js +344 -0
- package/dist/_internal/shared/features/sticker-trace/trace/segment.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/trace/segment.js +251 -0
- package/dist/_internal/shared/features/sticker-trace/types.d.ts +197 -0
- package/dist/_internal/shared/features/sticker-trace/types.js +49 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +11 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/config.js +1 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/config.js +1 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -0
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +7 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +9 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/config.js +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/config.js +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +1 -0
- package/dist/_internal/shared/media/limits.d.ts +12 -2
- package/dist/_internal/shared/media/limits.js +12 -2
- package/dist/_internal/shared/search/artifact-url.d.ts +35 -0
- package/dist/_internal/shared/search/artifact-url.js +119 -0
- package/dist/_internal/shared/search/index-record.d.ts +68 -0
- package/dist/_internal/shared/search/index-record.js +96 -0
- package/dist/_internal/shared/search/match.d.ts +65 -0
- package/dist/_internal/shared/search/match.js +263 -0
- package/dist/client.d.ts +8 -7
- package/dist/client.js +12 -5
- package/dist/configs/eslint.js +22 -2
- package/dist/constants/api-endpoints.d.ts +68 -0
- package/dist/constants/api-endpoints.js +32 -0
- package/dist/constants/field-names.d.ts +7 -1
- package/dist/constants/field-names.js +36 -1
- package/dist/contracts/repository.d.ts +8 -7
- package/dist/contracts/search.d.ts +2 -2
- package/dist/contracts/table.d.ts +3 -3
- package/dist/contracts/table.js +3 -3
- package/dist/core/contact-submissions.repository.js +2 -2
- package/dist/core/integration-keys.js +7 -1
- package/dist/core/newsletter.repository.js +2 -2
- package/dist/core/site-config.js +1 -1
- package/dist/errors/error-codes.d.ts +30 -0
- package/dist/errors/error-codes.js +30 -0
- package/dist/errors/error-display-map.js +40 -1
- package/dist/features/account/api/[userId]/addresses/route.js +1 -1
- package/dist/features/account/components/UserBidsView.js +1 -1
- package/dist/features/account/components/index.d.ts +0 -8
- package/dist/features/account/components/index.js +0 -4
- package/dist/features/addresses/repository/addresses.repository.d.ts +17 -6
- package/dist/features/addresses/repository/addresses.repository.js +65 -9
- package/dist/features/addresses/schemas/firestore.d.ts +17 -6
- package/dist/features/admin/actions/notification-actions.js +15 -6
- package/dist/features/admin/api/bids/route.js +1 -1
- package/dist/features/admin/api/coupons/route.js +1 -1
- package/dist/features/admin/api/products/route.js +1 -1
- package/dist/features/admin/api/reviews/route.js +1 -1
- package/dist/features/admin/components/AdminAdEditorView.js +19 -5
- package/dist/features/admin/components/AdminAuditLogView.js +1 -1
- package/dist/features/admin/components/AdminBlogEditorView.js +0 -1
- package/dist/features/admin/components/AdminBlogView.js +1 -1
- package/dist/features/admin/components/AdminBrandsView.js +17 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +0 -1
- package/dist/features/admin/components/AdminContactView.js +1 -1
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -3
- package/dist/features/admin/components/AdminNewsletterView.js +1 -1
- package/dist/features/admin/components/AdminOrdersView.js +9 -2
- package/dist/features/admin/components/AdminPayoutsView.js +1 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +11 -0
- package/dist/features/admin/components/AdminProductEditorView.d.ts +10 -1
- package/dist/features/admin/components/AdminProductEditorView.js +24 -37
- package/dist/features/admin/components/AdminProductsView.js +10 -3
- package/dist/features/admin/components/AdminScammersView.js +1 -1
- package/dist/features/admin/components/AdminSectionsView.js +21 -5
- package/dist/features/admin/components/AdminSessionsView.js +1 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +22 -1
- package/dist/features/admin/components/AdminTeamView.js +1 -1
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +0 -1
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/DataListingView.d.ts +2 -1
- package/dist/features/admin/components/DataListingView.js +9 -4
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +0 -2
- package/dist/features/admin/hooks/useAdminListing.d.ts +1 -1
- package/dist/features/admin/repository/site-settings.repository.js +8 -3
- package/dist/features/admin/schemas/firestore.d.ts +62 -1
- package/dist/features/admin/schemas/firestore.js +32 -0
- package/dist/features/admin/types/index.d.ts +1 -1
- package/dist/features/auctions/actions/bid-actions.d.ts +2 -2
- package/dist/features/auctions/actions/bid-actions.js +16 -4
- package/dist/features/auctions/api/route.js +1 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +4 -0
- package/dist/features/auctions/repository/bid.repository.d.ts +46 -4
- package/dist/features/auctions/repository/bid.repository.js +114 -13
- package/dist/features/auctions/schemas/firestore.d.ts +24 -4
- package/dist/features/auctions/schemas/firestore.js +1 -0
- package/dist/features/auth/actions/profile-actions.js +30 -4
- package/dist/features/auth/components/index.d.ts +0 -1
- package/dist/features/auth/components/index.js +0 -1
- package/dist/features/auth/repository/session.repository.d.ts +17 -3
- package/dist/features/auth/repository/session.repository.js +46 -5
- package/dist/features/auth/repository/token.repository.d.ts +27 -4
- package/dist/features/auth/repository/token.repository.js +65 -11
- package/dist/features/auth/repository/user.repository.d.ts +33 -7
- package/dist/features/auth/repository/user.repository.js +100 -15
- package/dist/features/auth/schemas/firestore.d.ts +63 -12
- package/dist/features/auth/schemas/firestore.js +11 -0
- package/dist/features/before-after/api/route.js +1 -1
- package/dist/features/blog/components/BlogListView.js +16 -3
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/blog/schemas/index.d.ts +2 -2
- package/dist/features/cart/api/route.js +1 -1
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +7 -7
- package/dist/features/catalogue/repository/catalogue.repository.js +28 -10
- package/dist/features/catalogue/schemas/firestore.d.ts +11 -0
- package/dist/features/categories/api/route.js +10 -10
- package/dist/features/categories/components/CategoryDetailPageView.js +35 -5
- package/dist/features/categories/components/CategorySelectorCreate.js +0 -1
- package/dist/features/categories/components/index.d.ts +0 -2
- package/dist/features/categories/components/index.js +0 -1
- package/dist/features/categories/repository/categories.repository.d.ts +1 -0
- package/dist/features/categories/repository/categories.repository.js +31 -1
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/categories/utils/taxonomy-visual.d.ts +21 -0
- package/dist/features/categories/utils/taxonomy-visual.js +58 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +8 -1
- package/dist/features/collections/api/[slug]/route.js +3 -3
- package/dist/features/collections/api/route.js +1 -1
- package/dist/features/consultation/api/route.js +1 -1
- package/dist/features/consultation/repository/consultations.repository.js +1 -1
- package/dist/features/consultation/types/index.d.ts +1 -1
- package/dist/features/contact/components/ContactForm.js +1 -3
- package/dist/features/corporate/api/route.js +1 -1
- package/dist/features/corporate/repository/corporate.repository.js +1 -1
- package/dist/features/corporate/types/index.d.ts +1 -1
- package/dist/features/cron/jobs/auction-expiry.job.js +1 -1
- package/dist/features/cron/jobs/preorder-reminder.job.js +1 -1
- package/dist/features/events/actions/event-actions.d.ts +14 -0
- package/dist/features/events/actions/event-actions.js +82 -3
- package/dist/features/events/api/[id]/route.js +2 -2
- package/dist/features/events/components/AdminEventEditorView.js +22 -3
- package/dist/features/events/components/AdminEventsView.js +32 -8
- package/dist/features/events/components/EventRafflesSection.d.ts +34 -1
- package/dist/features/events/components/EventRafflesSection.js +52 -3
- package/dist/features/events/components/SpinWheelView.d.ts +2 -2
- package/dist/features/events/components/SpinWheelView.js +41 -4
- package/dist/features/events/repository/event-entry.repository.d.ts +14 -2
- package/dist/features/events/repository/event-entry.repository.js +41 -7
- package/dist/features/events/schemas/firestore.d.ts +17 -4
- package/dist/features/events/types/index.d.ts +37 -0
- package/dist/features/faq/repository/faqs.repository.js +1 -1
- package/dist/features/faq/schemas/index.d.ts +5 -2
- package/dist/features/faq/schemas/index.js +6 -0
- package/dist/features/faq/types/index.d.ts +0 -1
- package/dist/features/grouped/components/GroupedListingEditorView.js +17 -2
- package/dist/features/homepage/api/carousel/[id]/route.js +1 -1
- package/dist/features/homepage/api/carousel/route.js +4 -4
- package/dist/features/homepage/api/route.js +2 -2
- package/dist/features/homepage/api/sections/route.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.d.ts +1 -9
- package/dist/features/homepage/components/AnnouncementBar.js +13 -19
- package/dist/features/homepage/components/MarketplaceHomepageView.js +6 -1
- package/dist/features/homepage/components/index.d.ts +0 -2
- package/dist/features/homepage/components/index.js +0 -1
- package/dist/features/homepage/repository/homepage.repository.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +6 -0
- package/dist/features/homepage/schemas/index.d.ts +10 -10
- package/dist/features/layout/AppLayoutShell.d.ts +12 -3
- package/dist/features/layout/AppLayoutShell.js +65 -13
- package/dist/features/layout/BottomActionsContext.d.ts +16 -0
- package/dist/features/layout/BottomActionsContext.js +5 -0
- package/dist/features/layout/BottomChrome.js +48 -9
- package/dist/features/layout/BottomNavbar.d.ts +14 -0
- package/dist/features/layout/BottomNavbar.js +43 -3
- package/dist/features/layout/MainNavbar.d.ts +11 -0
- package/dist/features/layout/NavbarLayout.d.ts +3 -0
- package/dist/features/layout/NavbarLayout.js +88 -11
- package/dist/features/layout/SideActionDock.d.ts +1 -0
- package/dist/features/layout/SideActionDock.js +219 -0
- package/dist/features/layout/TitleBarLayout.d.ts +1 -3
- package/dist/features/layout/TitleBarLayout.js +3 -4
- package/dist/features/layout/cta-anchor.d.ts +31 -0
- package/dist/features/layout/cta-anchor.js +31 -0
- package/dist/features/layout/hooks/useBottomActions.d.ts +7 -0
- package/dist/features/layout/hooks/useBottomActions.js +6 -1
- package/dist/features/layout/index.d.ts +9 -1
- package/dist/features/layout/index.js +9 -1
- package/dist/features/layout/useCtaReveal.d.ts +24 -0
- package/dist/features/layout/useCtaReveal.js +82 -0
- package/dist/features/layout/useHoldToOpen.d.ts +41 -0
- package/dist/features/layout/useHoldToOpen.js +84 -0
- package/dist/features/lottery/repository/lottery-entry.repository.d.ts +18 -3
- package/dist/features/lottery/repository/lottery-entry.repository.js +48 -6
- package/dist/features/lottery/schemas/firestore.d.ts +33 -5
- package/dist/features/loyalty/api/balance/route.js +1 -1
- package/dist/features/loyalty/api/history/route.js +1 -1
- package/dist/features/media/MediaImage.js +0 -1
- package/dist/features/media/MediaPickerModal.js +7 -1
- package/dist/features/media/modals/ImageEditor.js +5 -1
- package/dist/features/media/modals/VideoThumbnailSelector.js +17 -1
- package/dist/features/media/repository/media-assets.repository.d.ts +19 -0
- package/dist/features/media/upload/CameraCapture.js +16 -1
- package/dist/features/media/upload/ImageUpload.js +42 -4
- package/dist/features/media/upload/MediaUploadField.js +69 -13
- package/dist/features/media/upload/downscale-image.d.ts +56 -0
- package/dist/features/media/upload/downscale-image.js +150 -0
- package/dist/features/messages/components/ConversationThreadView.js +45 -2
- package/dist/features/messages/components/MessageSellerButton.d.ts +15 -1
- package/dist/features/messages/components/MessageSellerButton.js +10 -2
- package/dist/features/messages/schemas/firestore.d.ts +15 -0
- package/dist/features/orders/hooks/useOrders.js +4 -2
- package/dist/features/orders/repository/orders.repository.d.ts +15 -4
- package/dist/features/orders/repository/orders.repository.js +52 -20
- package/dist/features/orders/schemas/firestore.d.ts +15 -4
- package/dist/features/orders/schemas/index.d.ts +8 -8
- package/dist/features/orders/types/index.d.ts +2 -2
- package/dist/features/payments/repository/payments.repository.js +2 -2
- package/dist/features/payments/repository/payout.repository.d.ts +23 -6
- package/dist/features/payments/repository/payout.repository.js +63 -23
- package/dist/features/payments/repository/saved-payment-methods.repository.d.ts +17 -6
- package/dist/features/payments/repository/saved-payment-methods.repository.js +59 -11
- package/dist/features/payments/schemas/firestore.d.ts +14 -3
- package/dist/features/payments/schemas/saved-methods-firestore.d.ts +12 -1
- package/dist/features/pre-orders/api/[slug]/route.js +1 -1
- package/dist/features/pre-orders/api/admin/route.js +1 -1
- package/dist/features/pre-orders/api/route.js +1 -1
- package/dist/features/pre-orders/repository/pre-orders.repository.d.ts +3 -3
- package/dist/features/pre-orders/repository/pre-orders.repository.js +6 -6
- package/dist/features/products/api/[id]/route.js +1 -1
- package/dist/features/products/components/ListingBottomActions.js +4 -0
- package/dist/features/products/components/ProductDetailPageView.js +2 -1
- package/dist/features/products/components/ProductGalleryClient.js +2 -2
- package/dist/features/products/components/ProductGrid.js +12 -1
- package/dist/features/products/components/index.d.ts +0 -2
- package/dist/features/products/components/index.js +0 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +0 -3
- package/dist/features/products/config/listing-type-listing-config.js +27 -2
- package/dist/features/products/constants/listing-tabs.d.ts +9 -0
- package/dist/features/products/constants/listing-tabs.js +3 -2
- package/dist/features/products/hooks/useProducts.js +2 -2
- package/dist/features/products/repository/products.repository.js +13 -1
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/promotions/api/route.js +1 -1
- package/dist/features/reviews/actions/review-actions.d.ts +6 -6
- package/dist/features/reviews/actions/review-actions.js +22 -8
- package/dist/features/reviews/components/ReviewDetailPageView.js +13 -1
- package/dist/features/reviews/components/ReviewsIndexPageView.js +21 -4
- package/dist/features/reviews/repository/reviews.repository.d.ts +19 -7
- package/dist/features/reviews/repository/reviews.repository.js +63 -12
- package/dist/features/reviews/schemas/firestore.d.ts +13 -2
- package/dist/features/reviews/schemas/index.d.ts +2 -2
- package/dist/features/scams/actions/scam-actions.js +16 -4
- package/dist/features/search/components/SearchResultsPageView.js +2 -1
- package/dist/features/search/hooks/useSearchIndex.d.ts +34 -0
- package/dist/features/search/hooks/useSearchIndex.js +172 -0
- package/dist/features/search/repository/search.repository.js +2 -2
- package/dist/features/seller/actions/offer-actions.d.ts +5 -5
- package/dist/features/seller/actions/offer-actions.js +38 -8
- package/dist/features/seller/actions/seller-actions.js +34 -7
- package/dist/features/seller/api/coupons/route.js +1 -1
- package/dist/features/seller/api/products/route.js +1 -1
- package/dist/features/seller/api/store/route.js +1 -1
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerListingTypeView.js +9 -1
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +0 -1
- package/dist/features/seller/components/SellerPayoutsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.js +0 -1
- package/dist/features/seller/components/SellerProductsView.js +1 -1
- package/dist/features/seller/components/SellerShippingView.js +0 -1
- package/dist/features/seller/components/SellerStorefrontView.js +0 -1
- package/dist/features/seller/components/index.d.ts +0 -4
- package/dist/features/seller/components/index.js +0 -2
- package/dist/features/seller/repository/offer.repository.d.ts +15 -3
- package/dist/features/seller/repository/offer.repository.js +43 -7
- package/dist/features/seller/schemas/firestore.d.ts +14 -3
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +39 -0
- package/dist/features/seller/schemas/order-forms.js +37 -0
- package/dist/features/shell/FormShell.js +6 -2
- package/dist/features/shell/QuickFormDrawer.js +15 -1
- package/dist/features/shipments/components/AdminShipmentEditorView.js +0 -1
- package/dist/features/shipments/components/AdminShipmentsView.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.d.ts +30 -0
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +93 -0
- package/dist/features/sticker-trace/components/StickerPrintPanel.d.ts +33 -0
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +104 -0
- package/dist/features/sticker-trace/components/StickerRetryPanel.d.ts +20 -0
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +128 -0
- package/dist/features/sticker-trace/components/StickerRunHistoryPanel.d.ts +45 -0
- package/dist/features/sticker-trace/components/StickerRunHistoryPanel.js +84 -0
- package/dist/features/sticker-trace/components/StickerTraceStudio.d.ts +27 -0
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +171 -0
- package/dist/features/sticker-trace/constants/help-content.d.ts +23 -0
- package/dist/features/sticker-trace/constants/help-content.js +172 -0
- package/dist/features/sticker-trace/hooks/useLiveTrace.d.ts +27 -0
- package/dist/features/sticker-trace/hooks/useLiveTrace.js +160 -0
- package/dist/features/sticker-trace/hooks/useStickerTraceProgress.d.ts +22 -0
- package/dist/features/sticker-trace/hooks/useStickerTraceProgress.js +106 -0
- package/dist/features/sticker-trace/repository/sticker-sheets.repository.d.ts +113 -0
- package/dist/features/sticker-trace/repository/sticker-sheets.repository.js +146 -0
- package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.d.ts +132 -0
- package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.js +174 -0
- package/dist/features/sticker-trace/schemas/firestore.d.ts +248 -0
- package/dist/features/sticker-trace/schemas/firestore.js +79 -0
- package/dist/features/sticker-trace/server.d.ts +8 -0
- package/dist/features/sticker-trace/server.js +51 -0
- package/dist/features/stores/actions/store-query-actions.d.ts +2 -2
- package/dist/features/stores/actions/store-query-actions.js +7 -2
- package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -3
- package/dist/features/stores/api/[storeSlug]/products/route.js +3 -3
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +1 -1
- package/dist/features/stores/api/[storeSlug]/route.js +1 -1
- package/dist/features/stores/components/StoreNavTabs.js +17 -2
- package/dist/features/stores/manifest.js +8 -4
- package/dist/features/support/repository/support.repository.d.ts +14 -3
- package/dist/features/support/repository/support.repository.js +93 -13
- package/dist/features/support/schemas/firestore.d.ts +16 -5
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +61 -19
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +19 -3
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +243 -0
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +36 -10
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.js +209 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.js +90 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-controls-and-messages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-controls-and-messages.js +85 -0
- package/dist/features/tester/seed-data/authored/index.js +10 -0
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +19 -17
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +10 -5
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +283 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +235 -22
- package/dist/index.d.ts +10 -23
- package/dist/index.js +27 -19
- package/dist/next/api/routeHandler.js +9 -0
- package/dist/next/routing/require-record.d.ts +13 -0
- package/dist/next/routing/require-record.js +62 -0
- package/dist/next/routing/route-map.d.ts +4 -4
- package/dist/next/routing/route-map.js +25 -3
- package/dist/providers/db-firebase/base.d.ts +4 -2
- package/dist/providers/db-firebase/base.js +21 -5
- package/dist/providers/db-firebase/base.repository.d.ts +11 -9
- package/dist/providers/db-firebase/base.repository.js +19 -0
- package/dist/providers/db-firebase/rtdb-paths.d.ts +12 -0
- package/dist/providers/db-firebase/rtdb-paths.js +12 -0
- package/dist/react/ErrorBoundary.js +6 -1
- package/dist/react/hooks/realtime-event-constants.d.ts +1 -0
- package/dist/react/hooks/realtime-event-constants.js +8 -0
- package/dist/react/hooks/useEntityDelete.js +5 -1
- package/dist/react/hooks/useMessage.js +5 -1
- package/dist/repositories/index.d.ts +2 -2
- package/dist/repositories/index.js +6 -1
- package/dist/schemas/registry.d.ts +13 -20
- package/dist/schemas/registry.js +31 -14
- package/dist/schemas/webhooks/google-oauth.d.ts +2 -2
- package/dist/security/encrypted.d.ts +117 -0
- package/dist/security/encrypted.js +100 -0
- package/dist/security/index.d.ts +5 -2
- package/dist/security/index.js +16 -2
- package/dist/security/pii-accessors.d.ts +71 -0
- package/dist/security/pii-accessors.js +150 -0
- package/dist/security/pii-encrypt.d.ts +0 -8
- package/dist/security/pii-encrypt.js +22 -15
- package/dist/security/pii-mask.d.ts +18 -0
- package/dist/security/pii-mask.js +27 -0
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +6 -6
- package/dist/seed/_helpers/search-txt-wrappers.js +22 -0
- package/dist/seed/addresses-seed-data.d.ts +2 -2
- package/dist/seed/authenticity-knowledge-seed-data.d.ts +34 -0
- package/dist/seed/authenticity-knowledge-seed-data.js +1277 -0
- package/dist/seed/bids-seed-data.d.ts +2 -2
- package/dist/seed/bids-seed-data.js +23 -2
- package/dist/seed/events-seed-data.d.ts +2 -2
- package/dist/seed/events-seed-data.js +5 -0
- package/dist/seed/faq-seed-data.js +175 -5
- package/dist/seed/homepage-sections-seed-data.js +39 -23
- package/dist/seed/index.d.ts +1 -0
- package/dist/seed/index.js +1 -0
- package/dist/seed/lottery-entries-seed-data.d.ts +2 -2
- package/dist/seed/offers-seed-data.d.ts +2 -2
- package/dist/seed/offers-seed-data.js +1 -1
- package/dist/seed/orders-seed-data.d.ts +2 -2
- package/dist/seed/payouts-seed-data.d.ts +2 -2
- package/dist/seed/payouts-seed-data.js +1 -1
- package/dist/seed/reviews-seed-data.d.ts +1 -2
- package/dist/seed/seed-purpose.js +7 -0
- package/dist/seed/sessions-seed-data.d.ts +2 -2
- package/dist/seed/sessions-seed-data.js +5 -0
- package/dist/seed/site-settings-seed-data.js +1 -1
- package/dist/seed/support-tickets-seed-data.d.ts +2 -2
- package/dist/seed/support-tickets-seed-data.js +1 -1
- package/dist/seed/users-seed-data.d.ts +2 -2
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seo/json-ld.js +5 -1
- package/dist/server.d.ts +18 -3
- package/dist/server.js +59 -3
- package/dist/styles.css +237 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.js +7 -1
- package/dist/ui/components/FilterChipGroup.d.ts +11 -0
- package/dist/ui/components/FilterChipGroup.js +1 -1
- package/dist/ui/components/ImageGallery.js +6 -1
- package/dist/ui/components/ListingLayout.js +14 -8
- package/dist/ui/components/OverflowRow.d.ts +92 -0
- package/dist/ui/components/OverflowRow.js +203 -0
- package/dist/ui/components/OverflowRow.style.css +235 -0
- package/dist/ui/components/RecordDetailModal.js +14 -1
- package/dist/ui/components/RowActionMenu.js +5 -1
- package/dist/ui/components/SideDrawer.js +5 -1
- package/dist/ui/components/SideModal.js +9 -1
- package/dist/ui/components/TabBarShell.d.ts +12 -1
- package/dist/ui/components/TabBarShell.js +3 -2
- package/dist/ui/components/Tabs.js +36 -24
- package/dist/ui/components/Toast.d.ts +3 -24
- package/dist/ui/components/Toast.js +2 -19
- package/dist/ui/components/TopBannerPortal.d.ts +15 -0
- package/dist/ui/components/TopBannerPortal.js +19 -0
- package/dist/ui/components/UnsavedChangesModal.js +5 -1
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/row-collapse.d.ts +15 -0
- package/dist/ui/components/row-collapse.js +127 -0
- package/dist/ui/components/toast-context.d.ts +63 -0
- package/dist/ui/components/toast-context.js +48 -0
- package/dist/ui/components/top-banner-slot.d.ts +37 -0
- package/dist/ui/components/top-banner-slot.js +45 -0
- package/dist/ui/icons/icon-registry.d.ts +6 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/array.helper.d.ts +2 -2
- package/dist/utils/array.helper.js +5 -5
- package/dist/utils/id-generators.d.ts +6 -0
- package/dist/utils/id-generators.js +10 -0
- package/dist/utils/media-url.js +0 -2
- package/dist/utils/search-txt-builders.d.ts +28 -4
- package/dist/utils/search-txt-builders.js +41 -1
- package/dist/validation/schemas.d.ts +8 -8
- package/package.json +6 -2
- package/scripts/seed-cli.mjs +3 -0
- package/dist/features/account/components/UserAddressesView.d.ts +0 -13
- package/dist/features/account/components/UserAddressesView.js +0 -5
- package/dist/features/account/components/UserNotificationsView.d.ts +0 -15
- package/dist/features/account/components/UserNotificationsView.js +0 -12
- package/dist/features/account/components/UserSettingsView.d.ts +0 -16
- package/dist/features/account/components/UserSettingsView.js +0 -13
- package/dist/features/account/components/UserSupportView.d.ts +0 -3
- package/dist/features/account/components/UserSupportView.js +0 -166
- package/dist/features/admin/components/DemoSeedView.d.ts +0 -11
- package/dist/features/admin/components/DemoSeedView.js +0 -5
- package/dist/features/auth/components/AuthStatusPanel.d.ts +0 -10
- package/dist/features/auth/components/AuthStatusPanel.js +0 -22
- package/dist/features/categories/components/CategoryProductsView.d.ts +0 -45
- package/dist/features/categories/components/CategoryProductsView.js +0 -21
- package/dist/features/homepage/components/HowItWorksInfoView.d.ts +0 -36
- package/dist/features/homepage/components/HowItWorksInfoView.js +0 -10
- package/dist/features/layout/BottomActions.d.ts +0 -1
- package/dist/features/layout/BottomActions.js +0 -311
- package/dist/features/products/components/ProductsView.d.ts +0 -6
- package/dist/features/products/components/ProductsView.js +0 -5
- package/dist/features/seller/components/SellerStoreView.d.ts +0 -8
- package/dist/features/seller/components/SellerStoreView.js +0 -8
|
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
5
5
|
// owns the singleton; nesting another would risk Turbopack chunk-splitting.
|
|
6
6
|
// The actual 2.8.8 SSR crash was a peer-dep duplicate fixed by the
|
|
7
7
|
// scripts/dedupe-peer-deps.mjs postinstall hook (see scripts/ and CLAUDE.md #19).
|
|
8
|
+
import { ChevronDown } from "lucide-react";
|
|
8
9
|
import { AvatarDisplay, BackgroundRenderer, Div, Li, Main, Row, Span, Stack, Text, TextLink, Ul, UnsavedChangesModal } from "../../ui/index.js";
|
|
9
10
|
// Role indicator dots — distinct hues per role tier.
|
|
10
11
|
const ROLE_DOT_BG_CLASS = {
|
|
@@ -18,7 +19,7 @@ import { useAuth } from "../../react/contexts/SessionContext.js";
|
|
|
18
19
|
import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset.js";
|
|
19
20
|
import { isBuyerUser } from "../auth/role-predicates.js";
|
|
20
21
|
import { NavbarWithSettings } from "./NavbarWithSettings.js";
|
|
21
|
-
import
|
|
22
|
+
import SideActionDock from "./SideActionDock.js";
|
|
22
23
|
import { BottomChrome } from "./BottomChrome.js";
|
|
23
24
|
import { AutoBreadcrumbs } from "./AutoBreadcrumbs.js";
|
|
24
25
|
import { BottomNavbar } from "./BottomNavbar.js";
|
|
@@ -27,6 +28,7 @@ import { SidebarLayout } from "./SidebarLayout.js";
|
|
|
27
28
|
import { TitleBar } from "./TitleBar.js";
|
|
28
29
|
import { BackToTop } from "./BackToTop.js";
|
|
29
30
|
import { useDashboardNav } from "./DashboardNavContext.js";
|
|
31
|
+
import { TOP_BANNER_SLOT_ID } from "../../ui/components/top-banner-slot.js";
|
|
30
32
|
import { usePathname } from "next/navigation.js";
|
|
31
33
|
import { findActiveNavItem } from "../../_internal/client/features/layout/navActive.js";
|
|
32
34
|
import { OVERLAY_FALLBACK_COLOR, SEED_DARK_BG as DEFAULT_DARK_BG, SEED_LIGHT_BG as DEFAULT_LIGHT_BG } from "./background-seed-defaults.js";
|
|
@@ -44,8 +46,64 @@ function CollapsibleNavGroup({ title, hasActive = false, children, }) {
|
|
|
44
46
|
}, [hasActive]);
|
|
45
47
|
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""} shrink-0`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && _jsx(Stack, { gap: "none", className: "", children: children })] }));
|
|
46
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Flatten a sidebar section's links for active-item resolution.
|
|
51
|
+
*
|
|
52
|
+
* 🛑 Children MUST be in here. `findActiveNavItem` picks the longest prefix
|
|
53
|
+
* match, so leaving them out means a page that is only reachable as a child —
|
|
54
|
+
* /auctions under "Products" — marks nothing at all, the same lost-your-place
|
|
55
|
+
* bug the navbar's own comment documents.
|
|
56
|
+
*/
|
|
57
|
+
function flattenSidebarLinks(items) {
|
|
58
|
+
return items.flatMap((item) => [item, ...(item.children ?? [])]);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A sidebar link that owns sub-links: FIRST tap expands, SECOND tap follows.
|
|
62
|
+
*
|
|
63
|
+
* 🛑 The second tap is what keeps this a link rather than a folder. A parent
|
|
64
|
+
* that only ever toggles makes its own page unreachable from the sidebar — the
|
|
65
|
+
* catalogue index would be the one shop page you could not open from the menu
|
|
66
|
+
* that lists the shop. So the destination is the parent's own href, falling
|
|
67
|
+
* back to its first child when the parent is a grouping with no page of its
|
|
68
|
+
* own, and `preventDefault` applies only on the expanding tap.
|
|
69
|
+
*
|
|
70
|
+
* It differs from the navbar on purpose: there, hover already reveals the menu,
|
|
71
|
+
* so a click can navigate immediately. A sidebar has no hover, and a thumb has
|
|
72
|
+
* no caret small enough to aim at reliably — the tap has to do both jobs, in
|
|
73
|
+
* order.
|
|
74
|
+
*/
|
|
75
|
+
function SidebarParentLink({ item, activeItem, activeHref, navItemClass, navItemActiveClass, }) {
|
|
76
|
+
const children = item.children ?? [];
|
|
77
|
+
const containsActive = !!activeItem && children.some((child) => child.href === activeItem.href);
|
|
78
|
+
const [expanded, setExpanded] = useState(containsActive);
|
|
79
|
+
// The sidebar stays mounted across client-side navigation, so the lazy
|
|
80
|
+
// initial state above only ever fires once — re-open when the route moves
|
|
81
|
+
// into this group.
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (containsActive)
|
|
84
|
+
setExpanded(true);
|
|
85
|
+
}, [containsActive]);
|
|
86
|
+
const isActive = activeItem?.href === item.href;
|
|
87
|
+
const target = item.href || children[0]?.href || "#";
|
|
88
|
+
return (_jsxs(Li, { children: [_jsxs(TextLink, { href: target, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, "aria-expanded": expanded, onClick: (e) => {
|
|
89
|
+
if (expanded)
|
|
90
|
+
return;
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
setExpanded(true);
|
|
93
|
+
}, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label, _jsx(ChevronDown, { className: `ms-auto w-3.5 h-3.5 shrink-0 opacity-70 transition-transform duration-200 ${expanded ? "rotate-180" : ""}`, "aria-hidden": "true" })] }), expanded && (_jsx(SidebarSectionLinks, { items: children, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, nested: true }))] }));
|
|
94
|
+
}
|
|
95
|
+
/** One sidebar link plus its indented sub-links. */
|
|
96
|
+
function SidebarSectionLinks({ items, activeHref, activeItem, navItemClass, navItemActiveClass, nested = false, }) {
|
|
97
|
+
return (_jsx(Ul, { spacing: "2xs", className: nested ? "ps-[var(--appkit-space-6)]" : undefined, children: items.map((item) => {
|
|
98
|
+
const isActive = activeItem?.href === item.href;
|
|
99
|
+
if (item.children && item.children.length > 0) {
|
|
100
|
+
return (_jsx(SidebarParentLink, { item: item, activeItem: activeItem, activeHref: activeHref, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }, `${item.href}-${item.label}`));
|
|
101
|
+
}
|
|
102
|
+
return (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`));
|
|
103
|
+
}) }));
|
|
104
|
+
}
|
|
47
105
|
function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass, activeHref, }) {
|
|
48
|
-
const activeItem = findActiveNavItem(section.items, activeHref);
|
|
106
|
+
const activeItem = findActiveNavItem(flattenSidebarLinks(section.items), activeHref);
|
|
49
107
|
const hasActive = !!activeItem;
|
|
50
108
|
const [open, setOpen] = useState((section.defaultOpen ?? false) || hasActive);
|
|
51
109
|
// Re-sync whenever the route changes — the sidebar stays mounted across
|
|
@@ -56,15 +114,9 @@ function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass,
|
|
|
56
114
|
}, [hasActive]);
|
|
57
115
|
const hasTitle = !!section.title;
|
|
58
116
|
if (!hasTitle) {
|
|
59
|
-
return (_jsx(
|
|
60
|
-
const isActive = activeItem?.href === item.href;
|
|
61
|
-
return (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`));
|
|
62
|
-
}) }));
|
|
117
|
+
return (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }));
|
|
63
118
|
}
|
|
64
|
-
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""} shrink-0`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(
|
|
65
|
-
const isActive = activeItem?.href === item.href;
|
|
66
|
-
return (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`));
|
|
67
|
-
}) }))] }));
|
|
119
|
+
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""} shrink-0`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }))] }));
|
|
68
120
|
}
|
|
69
121
|
/** Sidebar header when a user is logged in — avatar + display name + close button. */
|
|
70
122
|
function SidebarUserHeader({ user, onClose, }) {
|
|
@@ -127,7 +179,7 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
127
179
|
].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-surface 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
180
|
}
|
|
129
181
|
// ─── Main component ────────────────────────────────────────────────────────────
|
|
130
|
-
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, 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,
|
|
182
|
+
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, 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, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
131
183
|
// QueryClientProvider used to be created here so AppLayoutShell could stand
|
|
132
184
|
// alone. The real cause of the 2026-06-10/11 "No QueryClient set" prod crash
|
|
133
185
|
// turned out to be a peer-dep duplicate of @tanstack/query-core under
|
|
@@ -194,7 +246,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
194
246
|
opacity: darkBackground.overlay?.opacity ?? 0,
|
|
195
247
|
},
|
|
196
248
|
};
|
|
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, messagesHref: messagesHref, unreadMessageCount: unreadMessageCount, 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,
|
|
249
|
+
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 }), _jsx(Div, { id: TOP_BANNER_SLOT_ID, className: "w-full" }), _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, messagesHref: messagesHref, unreadMessageCount: unreadMessageCount, 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, 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
250
|
// No bottom-chrome clearance here on purpose. FooterLayout is a
|
|
199
251
|
// SIBLING rendered after <Main>, so a margin here only gaps
|
|
200
252
|
// main-to-footer — it never clears the bottom of the page, which is
|
|
@@ -207,5 +259,5 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
207
259
|
// is 0 below `lg` and 0 whenever no bar is published, so this
|
|
208
260
|
// reserves exactly nothing until there is something to reserve —
|
|
209
261
|
// which is why it can be unconditional rather than route-gated.
|
|
210
|
-
className: "w-full flex-1 flex flex-col pt-[var(--top-chrome-height,0px)] transition-[padding-top] duration-300 ease-out", 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(
|
|
262
|
+
className: "w-full flex-1 flex flex-col pt-[var(--top-chrome-height,0px)] transition-[padding-top] duration-300 ease-out", 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: null }), _jsx(SideActionDock, {}), 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: handleTogglePublicSidebar })), _jsx(UnsavedChangesModal, {})] }) }));
|
|
211
263
|
}
|
|
@@ -117,6 +117,20 @@ export interface BottomActionsState {
|
|
|
117
117
|
* tap, and stay dismissible afterwards.
|
|
118
118
|
*/
|
|
119
119
|
infoOpenSignal?: number;
|
|
120
|
+
/**
|
|
121
|
+
* `id` of this page's INLINE primary CTA. `SideActionDock` reveals itself
|
|
122
|
+
* only once that element has been seen and scrolled out of view.
|
|
123
|
+
*
|
|
124
|
+
* The alternative — the `data-cta-anchor` attribute — is for a page that
|
|
125
|
+
* renders its CTA block directly. This exists because the shared listing
|
|
126
|
+
* registrars already take an `anchorId` (the element their CTA scrolls to),
|
|
127
|
+
* which is the same element, so they can declare the gate without touching
|
|
128
|
+
* the view that owns the markup.
|
|
129
|
+
*
|
|
130
|
+
* Unset means "reveal immediately", which is correct for every bulk bar: an
|
|
131
|
+
* admin listing has no Buy button to scroll past.
|
|
132
|
+
*/
|
|
133
|
+
ctaAnchorId?: string;
|
|
120
134
|
}
|
|
121
135
|
interface BottomActionsContextValue {
|
|
122
136
|
state: BottomActionsState;
|
|
@@ -143,6 +157,8 @@ interface BottomActionsContextValue {
|
|
|
143
157
|
setInfoOpenSignal: (signal: number | undefined) => void;
|
|
144
158
|
/** Set the desktop visibility policy. */
|
|
145
159
|
setDesktopMode: (mode: BottomActionsDesktopMode | undefined) => void;
|
|
160
|
+
/** Set or clear the inline-CTA anchor id. See BottomActionsState.ctaAnchorId. */
|
|
161
|
+
setCtaAnchorId: (id: string | undefined) => void;
|
|
146
162
|
/** Clear all state (called on feature unmount). */
|
|
147
163
|
clearAll: () => void;
|
|
148
164
|
claim: (id: string) => void;
|
|
@@ -122,6 +122,9 @@ export function BottomActionsProvider({ children, }) {
|
|
|
122
122
|
const setDesktopMode = useCallback((desktop) => {
|
|
123
123
|
setState((prev) => (prev.desktop === desktop ? prev : { ...prev, desktop }));
|
|
124
124
|
}, []);
|
|
125
|
+
const setCtaAnchorId = useCallback((ctaAnchorId) => {
|
|
126
|
+
setState((prev) => (prev.ctaAnchorId === ctaAnchorId ? prev : { ...prev, ctaAnchorId }));
|
|
127
|
+
}, []);
|
|
125
128
|
const clearAll = useCallback(() => {
|
|
126
129
|
actionCallbacksRef.current = new Map();
|
|
127
130
|
bulkCallbacksRef.current = new Map();
|
|
@@ -140,6 +143,7 @@ export function BottomActionsProvider({ children, }) {
|
|
|
140
143
|
setInfoPanel,
|
|
141
144
|
setInfoOpenSignal,
|
|
142
145
|
setDesktopMode,
|
|
146
|
+
setCtaAnchorId,
|
|
143
147
|
clearAll,
|
|
144
148
|
claim,
|
|
145
149
|
release,
|
|
@@ -153,6 +157,7 @@ export function BottomActionsProvider({ children, }) {
|
|
|
153
157
|
setInfoPanel,
|
|
154
158
|
setInfoOpenSignal,
|
|
155
159
|
setDesktopMode,
|
|
160
|
+
setCtaAnchorId,
|
|
156
161
|
clearAll,
|
|
157
162
|
claim,
|
|
158
163
|
release,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { Div } from "../../ui/index.js";
|
|
5
5
|
import { BOTTOM_CHROME_SLOT_ID } from "../../ui/components/bottom-chrome-slot.js";
|
|
6
6
|
/**
|
|
@@ -97,7 +97,7 @@ export function BottomChrome({ children }) {
|
|
|
97
97
|
root.style.setProperty("--top-chrome-height", "0px");
|
|
98
98
|
};
|
|
99
99
|
}, [isDesktop]);
|
|
100
|
-
return (
|
|
100
|
+
return (_jsx(Div, { ref: ref, layout: "flex-col",
|
|
101
101
|
// `pointer-events-none` so an empty tier never eats clicks meant for the
|
|
102
102
|
// page underneath; every child re-enables with `pointer-events-auto`.
|
|
103
103
|
//
|
|
@@ -109,12 +109,51 @@ export function BottomChrome({ children }) {
|
|
|
109
109
|
// published by AppLayoutShell and already accounts for the announcement
|
|
110
110
|
// banner, so this needs no second measurement.
|
|
111
111
|
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
112
|
+
// The portalled slot and the CTA row stack in opposite visual orders at
|
|
113
|
+
// the two ends. That reversal is done by swapping the CHILDREN below, not
|
|
114
|
+
// with `flex-col-reverse` — see the 🛑 above the JSX.
|
|
115
|
+
//
|
|
116
|
+
// ── 🛑 WHY THIS BRANCHES IN JS AND NOT ON `lg:` ────────────────────────
|
|
117
|
+
//
|
|
118
|
+
// It used to be one class string carrying both states:
|
|
119
|
+
//
|
|
120
|
+
// bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))]
|
|
121
|
+
// lg:bottom-auto lg:top-[var(--header-height,0px)] lg:flex-col-reverse
|
|
122
|
+
//
|
|
123
|
+
// On a 1440px viewport `lg:top-[…]` applied and **`lg:bottom-auto` and
|
|
124
|
+
// `lg:flex-col-reverse` did not**. The element was therefore anchored to
|
|
125
|
+
// BOTH edges at once, and a `position: fixed` box with `left/right/top/
|
|
126
|
+
// bottom` all set stretches: measured on production it was **801px tall
|
|
127
|
+
// with both of its children 0px tall** (900 viewport − 99 header − 0 nav).
|
|
128
|
+
// `BottomChrome` publishes its own `offsetHeight`, so it reported 801px as
|
|
129
|
+
// `--top-chrome-height`, and `<Main>` reserved all of it — an 801px blank
|
|
130
|
+
// band under the navbar on every desktop page. Verified by removing the
|
|
131
|
+
// `bottom-[calc…]` class in the live DOM: height 801 → 0.
|
|
132
|
+
//
|
|
133
|
+
// The mechanism is Root Cause #79's. Both configs set `important: true`,
|
|
134
|
+
// so an arbitrary-value utility and a named one tie on specificity AND on
|
|
135
|
+
// `!important`, a media query adds no specificity, and the winner is
|
|
136
|
+
// decided by EMISSION ORDER in the built CSS — which no one can see from
|
|
137
|
+
// the call site. All four classes are present in `dist/tailwind-utilities.css`;
|
|
138
|
+
// this was never a purge problem, so no `@source inline(...)` fixes it.
|
|
139
|
+
//
|
|
140
|
+
// So: never express this as a `lg:` override of an arbitrary-value base.
|
|
141
|
+
// The component already computes `isDesktop` (the measurement has to know
|
|
142
|
+
// which variable to publish), so the conflicting class is simply never
|
|
143
|
+
// emitted — exactly one anchor exists in the DOM at any time, and the
|
|
144
|
+
// cascade has nothing to arbitrate.
|
|
115
145
|
className: [
|
|
116
146
|
"fixed left-0 right-0 z-[var(--appkit-z-bottom-nav)] pointer-events-none",
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
isDesktop
|
|
148
|
+
? "top-[var(--header-height,0px)]"
|
|
149
|
+
: "bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))]",
|
|
150
|
+
].join(" "), children: isDesktop
|
|
151
|
+
? [
|
|
152
|
+
_jsx(React.Fragment, { children: children }, "children"),
|
|
153
|
+
_jsx(Div, { id: BOTTOM_CHROME_SLOT_ID, className: "contents" }, "slot"),
|
|
154
|
+
]
|
|
155
|
+
: [
|
|
156
|
+
_jsx(Div, { id: BOTTOM_CHROME_SLOT_ID, className: "contents" }, "slot"),
|
|
157
|
+
_jsx(React.Fragment, { children: children }, "children"),
|
|
158
|
+
] }));
|
|
120
159
|
}
|
|
@@ -4,6 +4,20 @@ export interface BottomNavItem {
|
|
|
4
4
|
href: string;
|
|
5
5
|
label: string;
|
|
6
6
|
icon?: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Sub-items, reached by holding the slot or tapping its caret — the same
|
|
9
|
+
* contract as the navbar dropdown and the sidebar's nested links.
|
|
10
|
+
*
|
|
11
|
+
* 🛑 A tap still FOLLOWS the link. The bottom bar has five slots for a whole
|
|
12
|
+
* site, so an item that stands for six pages has to surface them somewhere;
|
|
13
|
+
* without this they exist on desktop and vanish on the surface phones use.
|
|
14
|
+
*/
|
|
15
|
+
children?: Array<{
|
|
16
|
+
href: string;
|
|
17
|
+
label: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
icon?: React.ReactNode;
|
|
20
|
+
}>;
|
|
7
21
|
}
|
|
8
22
|
export interface BottomNavbarUser {
|
|
9
23
|
displayName: string | null;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
3
4
|
import { usePathname } from "next/navigation.js";
|
|
4
|
-
import {
|
|
5
|
+
import { ChevronUp } from "lucide-react";
|
|
6
|
+
import { AvatarDisplay, Div, Drawer, IconButton, Li, Span, TextLink, Ul } from "../../ui/index.js";
|
|
5
7
|
import { BottomNavLayout } from "./BottomNavLayout.js";
|
|
6
8
|
import { NavItem } from "./NavItem.js";
|
|
9
|
+
import { useHoldToOpen } from "./useHoldToOpen.js";
|
|
7
10
|
import { useWishlistCount } from "../wishlist/hooks/useWishlistCount.js";
|
|
8
11
|
import { useCartCount } from "../cart/hooks/useCartCount.js";
|
|
9
12
|
/** Badge counter class for the cart/wishlist slots.
|
|
@@ -20,6 +23,23 @@ import { useCartCount } from "../cart/hooks/useCartCount.js";
|
|
|
20
23
|
* token as a fill, which audit-status-color-pairs can't see through arbitrary
|
|
21
24
|
* -value syntax. */
|
|
22
25
|
const CLS_COUNT_BADGE = "appkit-hand-badge appkit-hand-badge--lg absolute -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-solid px-[var(--appkit-space-1)] leading-none text-error-on-solid";
|
|
26
|
+
/**
|
|
27
|
+
* One bottom-nav slot. A plain item renders exactly as it always did; an item
|
|
28
|
+
* with children gains the hold gesture and a caret, both of which open the
|
|
29
|
+
* shared sheet rather than navigating.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 The caret is `absolute` and the link fills the slot, so the tap target for
|
|
32
|
+
* "go to this page" does not shrink — the caret overlays its top corner rather
|
|
33
|
+
* than taking a column from it.
|
|
34
|
+
*/
|
|
35
|
+
function BottomNavSlot({ item, isActive, onOpenChildren, activeClassName, inactiveClassName, iconClassName, labelClassName, fallbackIcon, }) {
|
|
36
|
+
const hasChildren = Boolean(item.children && item.children.length > 0);
|
|
37
|
+
const holdHandlers = useHoldToOpen(() => onOpenChildren(item), hasChildren);
|
|
38
|
+
const link = (_jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? fallbackIcon, isActive: isActive, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }));
|
|
39
|
+
if (!hasChildren)
|
|
40
|
+
return link;
|
|
41
|
+
return (_jsxs(Div, { className: "relative h-full w-full select-none touch-manipulation", ...holdHandlers, children: [link, _jsx(IconButton, { size: "sm", variant: "ghost", rounded: "full", onClick: () => onOpenChildren(item), "aria-haspopup": "dialog", "aria-label": `${item.label} menu`, className: "absolute top-0 end-0 text-[var(--appkit-color-text-muted)]", icon: _jsx(ChevronUp, { className: "h-3.5 w-3.5 shrink-0 opacity-70", "aria-hidden": "true" }) })] }));
|
|
42
|
+
}
|
|
23
43
|
/**
|
|
24
44
|
* BottomNavbar � mobile bottom navigation bar (5 slots: home, shop, search, cart, profile).
|
|
25
45
|
*
|
|
@@ -29,6 +49,19 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
29
49
|
const pathname = usePathname();
|
|
30
50
|
const wishlistCount = useWishlistCount(userId);
|
|
31
51
|
const cartCount = useCartCount(!!userId);
|
|
52
|
+
/*
|
|
53
|
+
* One sheet for the whole bar, keyed by which item opened it — not one
|
|
54
|
+
* <Drawer> per slot. Each Drawer portals a fixed overlay, so a sheet per slot
|
|
55
|
+
* is four overlays stacked on every page for a menu at most one of them is
|
|
56
|
+
* ever showing.
|
|
57
|
+
*/
|
|
58
|
+
const [openKey, setOpenKey] = useState(null);
|
|
59
|
+
const openItem = navItems?.find((i) => i.key === openKey) ?? null;
|
|
60
|
+
// Close on navigation: the sheet's own links are what navigate, and without
|
|
61
|
+
// this it stays open over the page it just took you to.
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
setOpenKey(null);
|
|
64
|
+
}, [pathname]);
|
|
32
65
|
const labels = {
|
|
33
66
|
mobileNav: "Mobile navigation",
|
|
34
67
|
home: "Home",
|
|
@@ -47,7 +80,14 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
47
80
|
// slot was Wishlist — Wishlist remains reachable from the header/app
|
|
48
81
|
// bar, it's just no longer duplicated in the bottom tab bar).
|
|
49
82
|
const visibleItems = navItems.slice(0, cartHref ? 3 : 4);
|
|
50
|
-
|
|
83
|
+
const isSlotActive = (item) => {
|
|
84
|
+
// 🛑 A child's page counts. "Products" stands for /auctions and /bundles
|
|
85
|
+
// too now, so matching its own href alone would leave the bar marking
|
|
86
|
+
// nothing on most of the pages that slot represents.
|
|
87
|
+
const hrefs = [item.href, ...(item.children ?? []).map((c) => c.href)];
|
|
88
|
+
return hrefs.some((href) => pathname === href || (href !== "/" && pathname.startsWith(`${href}/`)));
|
|
89
|
+
};
|
|
90
|
+
return (_jsxs(_Fragment, { children: [_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(BottomNavSlot, { item: item, isActive: isSlotActive(item), onOpenChildren: (it) => setOpenKey(it.key), activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName, fallbackIcon: defaultIcon }) }, item.key))), cartHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) })), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }), _jsx(Drawer, { isOpen: !!openItem, onClose: () => setOpenKey(null), title: openItem?.label, side: "bottom", children: _jsxs(Ul, { className: "list-none", children: [openItem && (_jsx(Li, { children: _jsx(TextLink, { href: openItem.href, variant: "none", className: "block rounded-md px-[var(--appkit-space-3)] py-[var(--appkit-space-3)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text)]", children: openItem.label }) })), (openItem?.children ?? []).map((child) => (_jsx(Li, { children: _jsxs(TextLink, { href: child.href, variant: "none", className: "flex items-start gap-[var(--appkit-space-2)] rounded-md px-[var(--appkit-space-3)] py-[var(--appkit-space-3)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text)]", children: [child.icon && (_jsx(Span, { className: "flex-shrink-0 mt-[var(--appkit-space-0-5)]", "aria-hidden": "true", children: child.icon })), _jsxs(Span, { className: "block", children: [_jsx(Span, { weight: "medium", className: "block", children: child.label }), child.description && (_jsx(Span, { size: "xs", color: "muted", className: "block mt-[var(--appkit-space-0-5)] whitespace-normal", children: child.description }))] })] }) }, child.href)))] }) })] }));
|
|
51
91
|
}
|
|
52
92
|
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_COUNT_BADGE, children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
53
93
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]"}`, weight: "semibold", children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
@@ -17,6 +17,17 @@ export interface MainNavbarItem {
|
|
|
17
17
|
href: string;
|
|
18
18
|
label: string;
|
|
19
19
|
description?: string;
|
|
20
|
+
adminOnly?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Optional stable key, mirroring the parent's. Consumers filter children on
|
|
23
|
+
* it — a listing type an admin has switched off has to disappear from the
|
|
24
|
+
* dropdown it moved into, and matching on a translated `label` would break
|
|
25
|
+
* the moment a second locale ships.
|
|
26
|
+
*/
|
|
27
|
+
key?: string;
|
|
28
|
+
/** Same icon the child carried as a top-level item — a merged entry keeps
|
|
29
|
+
* its visual in the dropdown, the sidebar and the mobile sheet. */
|
|
30
|
+
icon?: React.ReactNode;
|
|
20
31
|
}>;
|
|
21
32
|
}
|
|
22
33
|
export interface MainNavbarProps {
|
|
@@ -22,6 +22,9 @@ export interface NavbarLayoutItem {
|
|
|
22
22
|
href: string;
|
|
23
23
|
label: string;
|
|
24
24
|
description?: string;
|
|
25
|
+
/** Same icon the child carried as a top-level item, so merging an entry
|
|
26
|
+
* into a dropdown does not strip the only visual it had. */
|
|
27
|
+
icon?: React.ReactNode;
|
|
25
28
|
}>;
|
|
26
29
|
}
|
|
27
30
|
export interface NavbarLayoutProps {
|
|
@@ -3,9 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useRef, useState, useEffect, useCallback } from "react";
|
|
4
4
|
import Link from "next/link.js";
|
|
5
5
|
import { ChevronDown } from "lucide-react";
|
|
6
|
-
import { Div, Li, Nav, Row, Span, Ul } from "../../ui/index.js";
|
|
6
|
+
import { Div, IconButton, Li, Nav, Row, Span, Ul } from "../../ui/index.js";
|
|
7
7
|
import { NavbarChevron } from "../../ui/components/NavbarChevron.js";
|
|
8
8
|
import { findActiveNavItem } from "../../_internal/client/features/layout/navActive.js";
|
|
9
|
+
import { useHoldToOpen } from "./useHoldToOpen.js";
|
|
9
10
|
const __O = {
|
|
10
11
|
hidden: "overflow-hidden",
|
|
11
12
|
};
|
|
@@ -15,8 +16,64 @@ function DefaultNavItem({ item, isActive, }) {
|
|
|
15
16
|
: isActive
|
|
16
17
|
? "bg-primary-50 dark:bg-[var(--appkit-color-surface-elevated)] text-primary-800 dark:text-white font-semibold px-[var(--appkit-space-3)] border-b-2 border-primary-500 dark:border-secondary-400 rounded-none pb-[6px] transition-colors duration-150"
|
|
17
18
|
: "text-[var(--appkit-color-text-muted)] hover:bg-surface-hover hover:text-zinc-950 dark:hover:text-white transition-colors duration-150 px-[var(--appkit-space-3)]";
|
|
18
|
-
const
|
|
19
|
-
|
|
19
|
+
const hasChildren = Boolean(item.children && item.children.length > 0);
|
|
20
|
+
/*
|
|
21
|
+
* Click-to-open state, which exists ONLY for the caret.
|
|
22
|
+
*
|
|
23
|
+
* 🛑 Hover and focus stay pure CSS below, and this does not replace them. The
|
|
24
|
+
* panel opens on `group-hover` / `group-focus-within` with no JS at all, which
|
|
25
|
+
* is what keeps it usable for a keyboard user on Tab and before hydration.
|
|
26
|
+
* This adds the third affordance that was genuinely missing: a pointer user
|
|
27
|
+
* who CLICKS the caret expecting a menu got a navigation instead, because the
|
|
28
|
+
* caret was inside the parent `<Link>`.
|
|
29
|
+
*/
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const wrapRef = useRef(null);
|
|
32
|
+
const panelId = `nav-panel-${item.href.replace(/[^a-z0-9]+/gi, "-")}`;
|
|
33
|
+
/*
|
|
34
|
+
* Close on outside click, on Escape, and whenever the route changes.
|
|
35
|
+
*
|
|
36
|
+
* The route-change close is the one that is easy to miss and the most
|
|
37
|
+
* annoying to live with: the panel's own links navigate, and without this the
|
|
38
|
+
* menu stays open over the page it just took you to. `isActive` is derived
|
|
39
|
+
* from `pathname` upstream, so the effect re-runs on every navigation.
|
|
40
|
+
*/
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!open)
|
|
43
|
+
return;
|
|
44
|
+
const onDown = (e) => {
|
|
45
|
+
if (!wrapRef.current?.contains(e.target))
|
|
46
|
+
setOpen(false);
|
|
47
|
+
};
|
|
48
|
+
const onKey = (e) => {
|
|
49
|
+
if (e.key === "Escape")
|
|
50
|
+
setOpen(false);
|
|
51
|
+
};
|
|
52
|
+
document.addEventListener("mousedown", onDown);
|
|
53
|
+
document.addEventListener("keydown", onKey);
|
|
54
|
+
return () => {
|
|
55
|
+
document.removeEventListener("mousedown", onDown);
|
|
56
|
+
document.removeEventListener("keydown", onKey);
|
|
57
|
+
};
|
|
58
|
+
}, [open]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
setOpen(false);
|
|
61
|
+
}, [item.href, isActive]);
|
|
62
|
+
/*
|
|
63
|
+
* Hold-to-open — the touch affordance, shared with the mobile bottom nav so
|
|
64
|
+
* one gesture is defined once. See `useHoldToOpen` for why the "did we hold"
|
|
65
|
+
* flag has to be a ref.
|
|
66
|
+
*/
|
|
67
|
+
const holdHandlers = useHoldToOpen(() => setOpen(true), hasChildren);
|
|
68
|
+
const trigger = (_jsxs(Link, { href: item.href, "aria-current": isActive ? "page" : undefined, className: `flex items-center gap-[var(--appkit-space-1-5)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] rounded-lg font-medium transition-colors duration-150 whitespace-nowrap ${activeClasses}${hasChildren ? " select-none touch-manipulation" : ""}`, ...holdHandlers, children: [item.icon, _jsx(Span, { children: item.label })] }));
|
|
69
|
+
/*
|
|
70
|
+
* The explicit narrow, not just `hasChildren`. A boolean derived from
|
|
71
|
+
* `item.children` does not narrow the field itself, so the `.map` below
|
|
72
|
+
* cannot see that it is defined — this keeps both the readable guard above
|
|
73
|
+
* and a non-optional array to iterate.
|
|
74
|
+
*/
|
|
75
|
+
const children = item.children ?? [];
|
|
76
|
+
if (!hasChildren || children.length === 0)
|
|
20
77
|
return trigger;
|
|
21
78
|
/*
|
|
22
79
|
* 🛑 Hover/focus-within CSS, not React state, and the trigger is still a real
|
|
@@ -24,16 +81,24 @@ function DefaultNavItem({ item, isActive, }) {
|
|
|
24
81
|
*
|
|
25
82
|
* Two things this buys. The group stays usable without JS and without a
|
|
26
83
|
* pointer — `focus-within` opens it on Tab, so a keyboard user reaches the
|
|
27
|
-
* children
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
84
|
+
* children. And the parent is a real link whose children are also reachable
|
|
85
|
+
* from the page it leads to, which is the progressive-enhancement shape
|
|
86
|
+
* rather than a menu that traps people.
|
|
87
|
+
*
|
|
88
|
+
* 🛑 The CARET is now a separate button, and `aria-expanded` is real rather
|
|
89
|
+
* than faked. It used to live INSIDE the parent `<Link>`, so clicking the one
|
|
90
|
+
* control that looks like a disclosure navigated away instead of disclosing —
|
|
91
|
+
* the caret was announcing an affordance the markup did not have. It is the
|
|
92
|
+
* button that owns `aria-expanded`/`aria-controls`, because it is the only
|
|
93
|
+
* part that tracks state; the hover and focus paths still open the panel
|
|
94
|
+
* without touching it, which is why the attribute can be false while the
|
|
95
|
+
* panel is visible. That asymmetry is honest: the button reports what the
|
|
96
|
+
* BUTTON did, and a hover is not a disclosure a screen reader user performed.
|
|
32
97
|
*
|
|
33
98
|
* The `pt` on the panel is a hover bridge: without it the gap between the
|
|
34
99
|
* trigger and the panel closes the menu as the pointer crosses it.
|
|
35
100
|
*/
|
|
36
|
-
return (_jsxs(Div, { className: "relative group", children: [trigger, _jsx(Div, { className:
|
|
101
|
+
return (_jsxs(Div, { ref: wrapRef, className: "relative group", children: [_jsxs(Row, { align: "center", gap: "none", children: [trigger, _jsx(IconButton, { type: "button", size: "sm", variant: "ghost", rounded: "md", "aria-expanded": open, "aria-haspopup": "true", "aria-controls": panelId, "aria-label": `${item.label} menu`, onClick: () => setOpen((v) => !v), icon: _jsx(ChevronDown, { className: `w-3.5 h-3.5 shrink-0 opacity-70 transition-transform duration-150 ${open ? "rotate-180" : ""}`, "aria-hidden": "true" }) })] }), _jsx(Div, { id: panelId, className: `absolute left-0 top-full z-20 pt-[var(--appkit-space-1)] group-hover:block group-focus-within:block ${open ? "block" : "hidden"}`, children: _jsx(Div, { surface: "elevated", border: "default", rounded: "lg", shadow: "lg", padding: "xs", children: _jsx(Ul, { className: "min-w-[15rem] list-none", children: children.map((child) => (_jsx(Li, { children: _jsxs(Link, { href: child.href, className: "flex items-start gap-[var(--appkit-space-2)] rounded-md px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text)] hover:bg-surface-hover transition-colors duration-150", children: [child.icon && (_jsx(Span, { className: "flex-shrink-0 mt-[var(--appkit-space-0-5)]", "aria-hidden": "true", children: child.icon })), _jsxs(Span, { className: "block", children: [_jsx(Span, { weight: "medium", className: "block", children: child.label }), child.description && (_jsx(Span, { size: "xs", color: "muted", className: "block mt-[var(--appkit-space-0-5)] whitespace-normal", children: child.description }))] })] }) }, child.href))) }) }) })] }));
|
|
37
102
|
}
|
|
38
103
|
/**
|
|
39
104
|
* NavbarLayout — generic horizontal navigation bar shell.
|
|
@@ -86,8 +151,20 @@ export function NavbarLayout({ items, activeHref, id = "main-navbar", ariaLabel
|
|
|
86
151
|
// `findActiveNavItem` prefix-matches (`activeHref.startsWith(`${href}/`)`) and
|
|
87
152
|
// resolves ties by LONGEST href, so a root entry like "/" cannot light up on
|
|
88
153
|
// every page and a more specific entry always beats a shorter one.
|
|
89
|
-
|
|
90
|
-
|
|
154
|
+
//
|
|
155
|
+
// 🛑 CHILDREN COUNT. A dropdown parent links to one page and stands for
|
|
156
|
+
// several — "Products" owns /auctions, /bundles and the rest — so resolving
|
|
157
|
+
// against top-level hrefs alone would mark NOTHING on four of the six pages
|
|
158
|
+
// the parent represents, which is the same lost-your-place bug described
|
|
159
|
+
// above, just one level down. Each child is resolved as a candidate carrying
|
|
160
|
+
// its parent's href, so the longest-match rule still decides and a child
|
|
161
|
+
// always beats its own shorter parent.
|
|
162
|
+
const candidates = items.flatMap((item) => [
|
|
163
|
+
{ href: item.href, ownerHref: item.href },
|
|
164
|
+
...(item.children ?? []).map((child) => ({ href: child.href, ownerHref: item.href })),
|
|
165
|
+
]);
|
|
166
|
+
const activeItem = findActiveNavItem(candidates, activeHref);
|
|
167
|
+
const isItemActive = (item) => activeItem?.ownerHref === item.href;
|
|
91
168
|
if (inline) {
|
|
92
169
|
return (_jsx(Ul, { "aria-label": ariaLabel, className: "hidden xl:flex items-center justify-center gap-[var(--appkit-space-0-5)] xl:gap-[var(--appkit-space-1)] w-full", children: items.map((item) => (_jsx(Li, { children: renderItem ? (renderItem(item, isItemActive(item))) : (_jsx(DefaultNavItem, { item: item, isActive: isItemActive(item) })) }, item.href))) }));
|
|
93
170
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function SideActionDock(): import("react").JSX.Element | null;
|