@mohasinac/appkit 4.27.0 → 4.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +16 -16
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +29 -6
- package/dist/_internal/client/features/lottery/LotteryListView.js +13 -1
- package/dist/_internal/server/features/checkout/actions.js +6 -4
- package/dist/_internal/server/features/classified/metadata.js +3 -1
- package/dist/_internal/server/features/digital-code/metadata.js +3 -1
- package/dist/_internal/server/features/live/metadata.js +3 -1
- package/dist/_internal/server/features/refunds/actions.js +3 -2
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/seo/sitemap.js +189 -40
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +5 -0
- package/dist/_internal/server/jobs/core/listingProcessor.d.ts +0 -1
- package/dist/_internal/server/jobs/core/listingProcessor.js +19 -1
- package/dist/_internal/shared/actions/action-registry.js +14 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +25 -3
- package/dist/_internal/shared/features/checkout/config.d.ts +3 -3
- package/dist/_internal/shared/features/checkout/config.js +6 -3
- package/dist/client.d.ts +32 -2
- package/dist/client.js +37 -2
- package/dist/constants/api-endpoints.d.ts +21 -3
- package/dist/constants/api-endpoints.js +14 -1
- package/dist/constants/field-names.d.ts +2 -2
- package/dist/constants/field-names.js +2 -2
- package/dist/core/newsletter.repository.js +14 -1
- package/dist/features/about/components/FAQPageView.js +1 -1
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/account/components/AddressForm.d.ts +2 -0
- package/dist/features/account/components/AddressForm.js +5 -1
- package/dist/features/account/components/UserBidsView.js +1 -1
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/components/UserSupportView.js +24 -2
- package/dist/features/account/hooks/useAddresses.d.ts +29 -1
- package/dist/features/account/hooks/useAddresses.js +4 -1
- package/dist/features/account/hooks/useProfile.d.ts +1 -1
- package/dist/features/account/hooks/useProfile.js +1 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +13 -0
- package/dist/features/addresses/repository/addresses.repository.js +19 -0
- package/dist/features/admin/actions/chat-actions.js +5 -1
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +6 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +9 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +42 -10
- package/dist/features/admin/components/AdminCarouselView.js +1 -1
- package/dist/features/admin/components/AdminContentGuideView.js +1 -1
- package/dist/features/admin/components/AdminFeatureEditorView.js +1 -1
- package/dist/features/admin/components/AdminGroupedListingsView.js +14 -2
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/AdminViewCards.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/schemas/firestore.js +9 -3
- package/dist/features/auctions/actions/bid-actions.js +45 -4
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +21 -6
- package/dist/features/auctions/components/AuctionBottomActions.js +42 -11
- package/dist/features/auctions/components/AuctionDetailPageView.d.ts +8 -3
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +4 -5
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +27 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +42 -20
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +23 -1
- package/dist/features/auth/hooks/useAuth.js +27 -1
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +3 -0
- package/dist/features/auth/repository/session.repository.d.ts +16 -1
- package/dist/features/auth/repository/session.repository.js +23 -0
- package/dist/features/auth/repository/user.repository.js +13 -2
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +3 -1
- package/dist/features/blog/schemas/firestore.js +1 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +11 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +3 -3
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +9 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +15 -5
- package/dist/features/cart/components/index.d.ts +1 -1
- package/dist/features/cart/components/index.js +1 -1
- package/dist/features/cart/hooks/useGuestCartMerge.d.ts +8 -1
- package/dist/features/cart/hooks/useGuestCartMerge.js +2 -1
- package/dist/features/cart/utils/line-total.d.ts +25 -0
- package/dist/features/cart/utils/line-total.js +26 -0
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +12 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +15 -0
- package/dist/features/categories/components/CategoryTree.js +2 -2
- package/dist/features/contact/components/ContactForm.js +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.d.ts +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.js +1 -1
- package/dist/features/events/components/AdminEventEditorView.js +16 -9
- package/dist/features/events/components/AdminEventEntriesView.js +7 -13
- package/dist/features/events/components/EventRafflesSection.js +14 -2
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/events/components/event-entry-detail-fields.d.ts +24 -0
- package/dist/features/events/components/event-entry-detail-fields.js +13 -0
- package/dist/features/events/repository/events.repository.js +1 -0
- package/dist/features/events/schemas/firestore.d.ts +3 -1
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/faq/components/ContactCTA.js +1 -1
- package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
- package/dist/features/faq/repository/faqs.repository.d.ts +16 -1
- package/dist/features/faq/repository/faqs.repository.js +64 -54
- package/dist/features/faq/schemas/firestore.d.ts +3 -2
- package/dist/features/faq/schemas/firestore.js +2 -2
- package/dist/features/faq/schemas/index.d.ts +3 -3
- package/dist/features/faq/schemas/index.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +2 -2
- package/dist/features/forms/Toggle.js +1 -1
- package/dist/features/grouped/components/GroupedListingEditorView.d.ts +18 -0
- package/dist/features/grouped/components/GroupedListingEditorView.js +192 -0
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/HomepageSectionBoundary.d.ts +39 -0
- package/dist/features/homepage/components/HomepageSectionBoundary.js +49 -0
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/homepage/lib/section-renderer.js +2 -1
- package/dist/features/layout/AppLayoutShell.js +5 -5
- package/dist/features/layout/BottomActions.js +5 -5
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/MainNavbar.js +1 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +11 -1
- package/dist/features/layout/hooks/useFormBottomActions.js +16 -2
- package/dist/features/lottery/schemas/config-write.d.ts +141 -0
- package/dist/features/lottery/schemas/config-write.js +139 -0
- package/dist/features/media/MediaPickerModal.js +3 -1
- package/dist/features/media/MediaVideo.js +4 -2
- package/dist/features/media/components/MediaSlider.js +1 -1
- package/dist/features/messages/repository/conversations.repository.js +15 -5
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/payments/repository/payout.repository.d.ts +11 -1
- package/dist/features/payments/repository/payout.repository.js +14 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -2
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/products/columns/productTableColumns.js +3 -3
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.js +5 -1
- package/dist/features/products/components/ProductDetailActions.js +11 -7
- package/dist/features/products/components/ProductDetailPageView.js +3 -3
- package/dist/features/products/components/ProductFeatureBadges.js +2 -2
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductFilters.js +15 -8
- package/dist/features/products/components/ProductGrid.js +8 -17
- package/dist/features/products/components/ProductTabs.js +7 -4
- package/dist/features/products/components/ProductTabsShell.js +2 -7
- package/dist/features/products/constants/action-defs.d.ts +21 -4
- package/dist/features/products/constants/action-defs.js +7 -3
- package/dist/features/products/hooks/useRelatedProducts.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -1
- package/dist/features/products/repository/products.repository.js +38 -16
- package/dist/features/products/schemas/firestore.d.ts +3 -2
- package/dist/features/products/schemas/firestore.js +1 -1
- package/dist/features/reviews/actions/review-actions.d.ts +8 -1
- package/dist/features/reviews/actions/review-actions.js +14 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +10 -4
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +29 -1
- package/dist/features/reviews/repository/reviews.repository.js +54 -13
- package/dist/features/reviews/schemas/firestore.d.ts +9 -1
- package/dist/features/reviews/schemas/firestore.js +1 -0
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/components/Search.js +3 -3
- package/dist/features/search/components/SearchResultsSection.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +3 -1
- package/dist/features/seller/components/SellerBidsView.js +6 -1
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/shell/SectionForm.d.ts +21 -1
- package/dist/features/shell/SectionForm.js +15 -3
- package/dist/features/shell/build-sections.js +10 -1
- package/dist/features/shell/field-ui-meta.d.ts +21 -0
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +23 -6
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +21 -4
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +24 -4
- package/dist/features/store-extensions/schemas/shipping-config-form.js +13 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +1 -1
- package/dist/features/stores/actions/store-query-actions.js +25 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreNavTabs.js +20 -8
- package/dist/features/stores/repository/store.repository.js +1 -0
- package/dist/features/stores/schemas/firestore.d.ts +2 -0
- package/dist/features/support/repository/support.repository.d.ts +12 -0
- package/dist/features/support/repository/support.repository.js +15 -0
- package/dist/features/support/schemas/ticket-create-form.d.ts +33 -0
- package/dist/features/support/schemas/ticket-create-form.js +82 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +0 -1
- package/dist/features/tester/repository/tester-checklist-item.repository.js +6 -15
- package/dist/features/tester/schemas/firestore.d.ts +5 -4
- package/dist/features/tester/schemas/firestore.js +3 -3
- package/dist/features/tester/seed-data/products-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +138 -1
- package/dist/features/wishlist/components/WishlistHeartButton.d.ts +55 -0
- package/dist/features/wishlist/components/WishlistHeartButton.js +52 -0
- package/dist/features/wishlist/components/index.d.ts +2 -1
- package/dist/features/wishlist/components/index.js +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.js +19 -4
- package/dist/monitoring/error-tracking.js +39 -5
- package/dist/monitoring/server-logger.js +70 -32
- package/dist/next/components/ErrorView.js +1 -1
- package/dist/next/routing/route-map.d.ts +39 -7
- package/dist/next/routing/route-map.js +34 -4
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +56 -9
- package/dist/react/ErrorBoundary.js +9 -1
- package/dist/react/contexts/SessionContext.js +6 -2
- package/dist/react/hooks/useAuthGate.d.ts +22 -0
- package/dist/react/hooks/useAuthGate.js +86 -7
- package/dist/schemas/registry.d.ts +3 -3
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-encrypt.d.ts +13 -5
- package/dist/security/pii-encrypt.js +132 -6
- package/dist/security/pii-schemas.d.ts +32 -4
- package/dist/security/pii-schemas.js +38 -5
- package/dist/seed/_helpers/product-search-txt.d.ts +18 -0
- package/dist/seed/_helpers/product-search-txt.js +33 -0
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +20 -0
- package/dist/seed/_helpers/search-txt-wrappers.js +73 -0
- package/dist/seed/blog-posts-seed-data.d.ts +4 -0
- package/dist/seed/blog-posts-seed-data.js +7 -1
- package/dist/seed/events-seed-data.d.ts +5 -1
- package/dist/seed/events-seed-data.js +7 -1
- package/dist/seed/faq-seed-data.d.ts +16 -0
- package/dist/seed/faq-seed-data.js +22 -1
- package/dist/seed/products-art-seed-data.js +49 -7
- package/dist/seed/products-auctions-seed-data.js +12 -4
- package/dist/seed/products-classifieds-seed-data.d.ts +5 -0
- package/dist/seed/products-classifieds-seed-data.js +65 -9
- package/dist/seed/products-digital-codes-seed-data.d.ts +5 -0
- package/dist/seed/products-digital-codes-seed-data.js +65 -9
- package/dist/seed/products-live-items-seed-data.d.ts +5 -0
- package/dist/seed/products-live-items-seed-data.js +33 -5
- package/dist/seed/products-preorders-seed-data.d.ts +1 -1
- package/dist/seed/products-preorders-seed-data.js +6 -2
- package/dist/seed/products-prize-draws-seed-data.d.ts +5 -0
- package/dist/seed/products-prize-draws-seed-data.js +57 -8
- package/dist/seed/products-standard-seed-data.js +6 -2
- package/dist/seed/products-stickers-seed-data.js +49 -7
- package/dist/seed/reviews-seed-data.js +2 -1
- package/dist/seed/site-settings-seed-data.js +5 -3
- package/dist/seed/stores-seed-data.d.ts +4 -0
- package/dist/seed/stores-seed-data.js +7 -1
- package/dist/seo/json-ld.d.ts +44 -2
- package/dist/seo/json-ld.js +57 -11
- package/dist/styles.css +368 -90
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/themes/default-dark.js +6 -0
- package/dist/tokens/themes/default-light.js +5 -0
- package/dist/tokens/tokens.css +84 -0
- package/dist/ui/DataTable.js +1 -1
- package/dist/ui/DataTable.style.css +3 -3
- package/dist/ui/components/BulkActionBar.style.css +1 -3
- package/dist/ui/components/Button.js +15 -6
- package/dist/ui/components/Dropdown.style.css +1 -4
- package/dist/ui/components/FilterChipGroup.js +1 -1
- package/dist/ui/components/FilterDrawer.style.css +1 -1
- package/dist/ui/components/Icon.d.ts +53 -0
- package/dist/ui/components/Icon.js +41 -0
- package/dist/ui/components/IconButton.d.ts +13 -0
- package/dist/ui/components/IconButton.js +13 -0
- package/dist/ui/components/IconButton.style.css +18 -10
- package/dist/ui/components/ListingLayout.style.css +2 -10
- package/dist/ui/components/ListingToolbar.js +6 -6
- package/dist/ui/components/Modal.js +1 -1
- package/dist/ui/components/PaginatedSelect.style.css +3 -3
- package/dist/ui/components/Pagination.style.css +1 -6
- package/dist/ui/components/RichTextEditor.js +1 -1
- package/dist/ui/components/RowActionMenu.style.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +1 -1
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/TabBarShell.d.ts +78 -0
- package/dist/ui/components/TabBarShell.js +95 -0
- package/dist/ui/components/Tabs.js +12 -8
- package/dist/ui/components/Tabs.style.css +249 -41
- package/dist/ui/components/ViewToggle.style.css +1 -5
- package/dist/ui/components/tabs-overflow.d.ts +17 -0
- package/dist/ui/components/tabs-overflow.js +142 -0
- package/dist/ui/icons/icon-registry.d.ts +38 -0
- package/dist/ui/icons/icon-registry.js +37 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/media-url.d.ts +27 -4
- package/dist/utils/media-url.js +37 -6
- package/dist/utils/search-txt.d.ts +71 -0
- package/dist/utils/search-txt.js +118 -0
- package/package.json +1 -1
|
@@ -120,5 +120,5 @@ export function RichTextEditor({ value, onChange, disabled = false, className =
|
|
|
120
120
|
editor.innerHTML = safeValue;
|
|
121
121
|
}
|
|
122
122
|
}, [value]);
|
|
123
|
-
return (_jsxs("div", { className: `appkit-rich-text-editor rounded-lg border border-zinc-200 bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${className}`, "data-section": "richtexteditor-div-582", children: [_jsx("div", { className: "appkit-rich-text-editor__toolbar flex flex-wrap gap-1 border-b
|
|
123
|
+
return (_jsxs("div", { className: `appkit-rich-text-editor rounded-lg border border-zinc-200 bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${className}`, "data-section": "richtexteditor-div-582", children: [_jsx("div", { className: "appkit-rich-text-editor__toolbar flex flex-wrap gap-1 border-b p-2 border-[var(--appkit-color-border)]", "data-section": "richtexteditor-div-583", children: toolbarActions.map((action) => (_jsx("button", { type: "button", title: action.title, onClick: action.run, disabled: disabled, className: "appkit-rich-text-editor__btn rounded px-2 py-1 text-xs font-medium transition hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50 text-[var(--appkit-color-text-muted)]", children: action.label }, action.title))) }), _jsx("div", { ref: editorRef, role: "textbox", "aria-label": "Rich text editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, onInput: emitChange, onBlur: emitChange, className: `prose prose-sm max-w-none p-3 focus:outline-none dark:prose-invert ${minHeightClassName} ${disabled ? "cursor-not-allowed opacity-70" : ""}` })] }));
|
|
124
124
|
}
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
box-shadow: 0 0 0 1px var(--appkit-color-border);
|
|
126
126
|
}
|
|
127
127
|
.appkit-side-drawer__close-btn:hover {
|
|
128
|
-
background-color: var(--appkit-color-
|
|
128
|
+
background-color: var(--appkit-color-surface-hover);
|
|
129
129
|
box-shadow: 0 0 0 1px var(--appkit-color-zinc-300);
|
|
130
130
|
}
|
|
131
131
|
:is(.dark) .appkit-side-drawer__close-btn {
|
|
@@ -25,7 +25,7 @@ export function Slider({ value: controlledValue, defaultValue = 0, min = 0, max
|
|
|
25
25
|
onChangeEnd?.(value);
|
|
26
26
|
};
|
|
27
27
|
return (_jsxs("div", { className: ["w-full", className].filter(Boolean).join(" "), "data-section": "slider-div-607", children: [(label || showValue) && (_jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", "data-section": "slider-div-608", children: [label ? _jsx(Label, { htmlFor: inputId, children: label }) : _jsx("span", {}), showValue ? (_jsx(Span, { size: "sm", className: "tabular-nums text-[var(--appkit-color-text-muted)]", children: value })) : null] })), _jsxs("div", { className: "relative", "data-section": "slider-div-609", children: [_jsx("div", { "aria-hidden": "true", className: [
|
|
28
|
-
"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 overflow-hidden rounded-full bg-
|
|
28
|
+
"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 overflow-hidden rounded-full bg-[var(--appkit-color-border)]",
|
|
29
29
|
SIZE_CLASSES[size],
|
|
30
30
|
].join(" "), "data-section": "slider-div-610", children: _jsx("div", { className: [
|
|
31
31
|
"h-full rounded-full bg-primary",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type SelectOption } from "./Select";
|
|
3
|
+
export interface TabBarShellProps {
|
|
4
|
+
/**
|
|
5
|
+
* Applied to the inner `role="tablist"` element — unchanged from the
|
|
6
|
+
* pre-shell `TabsList` behaviour, so existing consumers keep getting it on
|
|
7
|
+
* the element that actually holds the tabs. Note it is no longer the
|
|
8
|
+
* OUTERMOST node: a margin or width passed here applies inside the shell's
|
|
9
|
+
* flex row.
|
|
10
|
+
*/
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Accessible name for the tablist. */
|
|
13
|
+
ariaLabel?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Rendered before the tablist, inside the shell but OUTSIDE the
|
|
16
|
+
* `role="tablist"` element — for controls that are not tabs (StoreNavTabs'
|
|
17
|
+
* listing-type picker). A `<select>` inside a tablist is invalid ARIA,
|
|
18
|
+
* since it is not a `role="tab"`.
|
|
19
|
+
*/
|
|
20
|
+
leading?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* The currently-selected tab's value. Keys the scroll-into-view effect, so a
|
|
23
|
+
* selection that starts off-screen in an overflowing strip is scrolled to.
|
|
24
|
+
* Omit and that repositioning simply does not happen — overflow detection
|
|
25
|
+
* and the arrows work regardless.
|
|
26
|
+
*/
|
|
27
|
+
activeKey?: string;
|
|
28
|
+
children?: React.ReactNode;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `TabBarShell` — the rail + overflow arrows around a `role="tablist"`.
|
|
32
|
+
*
|
|
33
|
+
* The arrows are SIBLINGS of the tablist, never children, so its accessibility
|
|
34
|
+
* -tree children stay exactly the `role="tab"` elements — no `aria-owns`, no
|
|
35
|
+
* invalid-child warnings.
|
|
36
|
+
*/
|
|
37
|
+
export declare function TabBarShell({ className, ariaLabel, leading, activeKey, children, }: TabBarShellProps): React.JSX.Element;
|
|
38
|
+
export interface TabBarButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "role" | "aria-selected"> {
|
|
39
|
+
selected: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `TabBarButton` — a bare `role="tab"` button carrying `.appkit-tabs-trigger`.
|
|
43
|
+
*
|
|
44
|
+
* Exists because appkit's `<Button variant="ghost">` is NOT transparent: it
|
|
45
|
+
* has a `--appkit-color-border-subtle` fill, a 44px min-height and a
|
|
46
|
+
* `rounded-xl` radius, so the hand-rolled bars that used it were rendering
|
|
47
|
+
* grey pill chips rather than text tabs. It also wraps children in
|
|
48
|
+
* `.appkit-button__content` (Root Cause #68), which a tab does not want.
|
|
49
|
+
*/
|
|
50
|
+
export declare function TabBarButton({ selected, className, children, ...rest }: TabBarButtonProps): React.JSX.Element;
|
|
51
|
+
export interface TabsNavSelectProps {
|
|
52
|
+
ariaLabel: string;
|
|
53
|
+
options: SelectOption[];
|
|
54
|
+
value: string;
|
|
55
|
+
placeholder?: string;
|
|
56
|
+
/**
|
|
57
|
+
* `false` renders the `--unselected` modifier (transparent rail) — the
|
|
58
|
+
* control is present but is making no "you are here" claim, which is
|
|
59
|
+
* StoreNavTabs' state while a non-listing tab is active.
|
|
60
|
+
*/
|
|
61
|
+
selected?: boolean;
|
|
62
|
+
onValueChange: (value: string) => void;
|
|
63
|
+
className?: string;
|
|
64
|
+
role?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* `TabsNavSelect` — the collapsed tab bar, past `TABS_DROPDOWN_THRESHOLD`.
|
|
68
|
+
*
|
|
69
|
+
* A `<select>` that reads as navigation rather than as a form field: the
|
|
70
|
+
* enclosing rectangle is gone and the only chrome left is the same 2px brand
|
|
71
|
+
* rail an active row-tab uses. See the block comment in Tabs.style.css for
|
|
72
|
+
* the full reasoning, and the specificity warning above those rules.
|
|
73
|
+
*
|
|
74
|
+
* `variant="default"` and NOT `"ghost"` — ghost's `border-transparent
|
|
75
|
+
* bg-transparent` are `!important` utilities that would pin the border to
|
|
76
|
+
* transparent and block the indicator rail.
|
|
77
|
+
*/
|
|
78
|
+
export declare function TabsNavSelect({ ariaLabel, options, value, placeholder, selected, onValueChange, className, role, }: TabsNavSelectProps): React.JSX.Element;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/*
|
|
4
|
+
* WHY: Seven tab bars existed across this codebase, four of them hand-rolled
|
|
5
|
+
* copies of the same `border-b-2 / -mb-px` underline with a DIFFERENT
|
|
6
|
+
* active colour each. This is the one shell they all render through, so
|
|
7
|
+
* the look is shared by construction rather than by discipline.
|
|
8
|
+
*
|
|
9
|
+
* WHAT: The chrome around a `role="tablist"` — the rail, the overflow scroll
|
|
10
|
+
* arrows, and a `leading` slot for controls that are NOT tabs. Plus the
|
|
11
|
+
* two trigger flavours a folded bar needs: `TabBarButton` (a bare
|
|
12
|
+
* `role="tab"` button) and `TabsNavSelect` (the >5-tab dropdown,
|
|
13
|
+
* stripped of Select's input chrome).
|
|
14
|
+
*
|
|
15
|
+
* EXPORTS: TabBarShell, TabBarShellProps, TabBarButton, TabBarButtonProps,
|
|
16
|
+
* TabsNavSelect, TabsNavSelectProps
|
|
17
|
+
*
|
|
18
|
+
* @tag domain:ui
|
|
19
|
+
* @tag layer:ui
|
|
20
|
+
* @tag pattern:none
|
|
21
|
+
* @tag access:client
|
|
22
|
+
* @tag consumers:Tabs,StoreNavTabs,EventTabBar
|
|
23
|
+
* @tag sideEffects:none
|
|
24
|
+
*/
|
|
25
|
+
import { useRef } from "react";
|
|
26
|
+
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
|
|
27
|
+
import { Div } from "./Div";
|
|
28
|
+
import { Select } from "./Select";
|
|
29
|
+
import { useTabsOverflow } from "./tabs-overflow";
|
|
30
|
+
const UI_TAB_BAR = {
|
|
31
|
+
shell: "appkit-tabs-shell",
|
|
32
|
+
arrow: "appkit-tabs-shell__arrow",
|
|
33
|
+
list: "appkit-tabs-list",
|
|
34
|
+
trigger: "appkit-tabs-trigger",
|
|
35
|
+
dropdown: "appkit-tabs-dropdown",
|
|
36
|
+
dropdownUnselected: "appkit-tabs-dropdown--unselected",
|
|
37
|
+
dropdownSelect: "appkit-tabs-dropdown__select",
|
|
38
|
+
dropdownCaret: "appkit-tabs-dropdown__caret",
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* `TabBarShell` — the rail + overflow arrows around a `role="tablist"`.
|
|
42
|
+
*
|
|
43
|
+
* The arrows are SIBLINGS of the tablist, never children, so its accessibility
|
|
44
|
+
* -tree children stay exactly the `role="tab"` elements — no `aria-owns`, no
|
|
45
|
+
* invalid-child warnings.
|
|
46
|
+
*/
|
|
47
|
+
export function TabBarShell({ className = "", ariaLabel = "Tabs", leading, activeKey = "", children, }) {
|
|
48
|
+
const listRef = useRef(null);
|
|
49
|
+
const { hasArrows, atStart, atEnd, scrollByPage } = useTabsOverflow(listRef, activeKey);
|
|
50
|
+
/*
|
|
51
|
+
* The arrows are aria-hidden + tabIndex={-1} deliberately. They duplicate
|
|
52
|
+
* scrolling that keyboard users already get for free: every trigger is a
|
|
53
|
+
* natural tab stop and browsers scroll a focused element into view, so
|
|
54
|
+
* WCAG 2.1.1 is satisfied by that path. Making them focusable would add two
|
|
55
|
+
* extra tab stops to every tab bar in the app, in front of the content the
|
|
56
|
+
* user is actually navigating to — a net keyboard regression for a
|
|
57
|
+
* redundant affordance. (HorizontalScroller's arrows ARE focusable, and
|
|
58
|
+
* correctly so: there they page through card content with no other
|
|
59
|
+
* keyboard path.)
|
|
60
|
+
*/
|
|
61
|
+
return (_jsxs(Div, { className: UI_TAB_BAR.shell, "data-arrows": hasArrows ? "true" : "false", children: [leading, hasArrows && (_jsx("button", { type: "button", "aria-hidden": "true", tabIndex: -1, disabled: atStart, onClick: () => scrollByPage(-1), className: UI_TAB_BAR.arrow, children: _jsx(ChevronLeft, { className: "w-4 h-4" }) })), _jsx(Div, { ref: listRef, role: "tablist", "aria-label": ariaLabel, className: [UI_TAB_BAR.list, className].filter(Boolean).join(" "), children: children }), hasArrows && (_jsx("button", { type: "button", "aria-hidden": "true", tabIndex: -1, disabled: atEnd, onClick: () => scrollByPage(1), className: UI_TAB_BAR.arrow, children: _jsx(ChevronRight, { className: "w-4 h-4" }) }))] }));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `TabBarButton` — a bare `role="tab"` button carrying `.appkit-tabs-trigger`.
|
|
65
|
+
*
|
|
66
|
+
* Exists because appkit's `<Button variant="ghost">` is NOT transparent: it
|
|
67
|
+
* has a `--appkit-color-border-subtle` fill, a 44px min-height and a
|
|
68
|
+
* `rounded-xl` radius, so the hand-rolled bars that used it were rendering
|
|
69
|
+
* grey pill chips rather than text tabs. It also wraps children in
|
|
70
|
+
* `.appkit-button__content` (Root Cause #68), which a tab does not want.
|
|
71
|
+
*/
|
|
72
|
+
export function TabBarButton({ selected, className = "", children, ...rest }) {
|
|
73
|
+
return (_jsx("button", { ...rest, type: rest.type ?? "button", role: "tab", "aria-selected": selected, className: [UI_TAB_BAR.trigger, className].filter(Boolean).join(" "), children: children }));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `TabsNavSelect` — the collapsed tab bar, past `TABS_DROPDOWN_THRESHOLD`.
|
|
77
|
+
*
|
|
78
|
+
* A `<select>` that reads as navigation rather than as a form field: the
|
|
79
|
+
* enclosing rectangle is gone and the only chrome left is the same 2px brand
|
|
80
|
+
* rail an active row-tab uses. See the block comment in Tabs.style.css for
|
|
81
|
+
* the full reasoning, and the specificity warning above those rules.
|
|
82
|
+
*
|
|
83
|
+
* `variant="default"` and NOT `"ghost"` — ghost's `border-transparent
|
|
84
|
+
* bg-transparent` are `!important` utilities that would pin the border to
|
|
85
|
+
* transparent and block the indicator rail.
|
|
86
|
+
*/
|
|
87
|
+
export function TabsNavSelect({ ariaLabel, options, value, placeholder, selected = true, onValueChange, className = "", role, }) {
|
|
88
|
+
return (_jsxs(Div, { className: [
|
|
89
|
+
UI_TAB_BAR.dropdown,
|
|
90
|
+
selected ? "" : UI_TAB_BAR.dropdownUnselected,
|
|
91
|
+
className,
|
|
92
|
+
]
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join(" "), children: [_jsx(Select, { bare: true, variant: "default", "aria-label": ariaLabel, role: role, options: options, value: value, placeholder: placeholder, onValueChange: onValueChange, className: UI_TAB_BAR.dropdownSelect }), _jsx(Div, { className: UI_TAB_BAR.dropdownCaret, "aria-hidden": "true", children: _jsx(ChevronDown, { className: "w-4 h-4" }) })] }));
|
|
95
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { createContext, useContext, useMemo, useState } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { TabBarShell, TabsNavSelect } from "./TabBarShell";
|
|
5
5
|
const UI_TABS = {
|
|
6
|
-
list: "appkit-tabs-list",
|
|
7
|
-
listDropdown: "appkit-tabs-list--dropdown",
|
|
8
6
|
trigger: "appkit-tabs-trigger",
|
|
9
7
|
content: "appkit-tabs-content",
|
|
10
8
|
badge: "appkit-tabs-trigger__badge",
|
|
11
9
|
};
|
|
12
|
-
/** Past this many items, TabsList renders a
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
/** Past this many items, TabsList renders a dropdown instead of a horizontal
|
|
11
|
+
* row — a scrollable row of 6+ tabs is unreadable/unreachable on mobile
|
|
12
|
+
* widths.
|
|
13
|
+
*
|
|
14
|
+
* The dropdown is styled as NAVIGATION, not as a form field: see
|
|
15
|
+
* `TabsNavSelect` and the `.appkit-tabs-dropdown` rules in Tabs.style.css. It
|
|
16
|
+
* used to render a raw `<Select bare>`, which carried the full
|
|
17
|
+
* `.appkit-select__trigger` input chrome (bordered, rounded, surface-filled,
|
|
18
|
+
* 40px tall) — which is exactly why a tab bar read as an input. */
|
|
15
19
|
const TABS_DROPDOWN_THRESHOLD = 5;
|
|
16
20
|
const TabsContext = createContext({
|
|
17
21
|
active: "",
|
|
@@ -44,9 +48,9 @@ export function TabsList({ className = "", children }) {
|
|
|
44
48
|
disabled: item.props.disabled,
|
|
45
49
|
};
|
|
46
50
|
});
|
|
47
|
-
return (_jsx(
|
|
51
|
+
return (_jsx(TabsNavSelect, { ariaLabel: "Tabs", role: "tablist", options: options, value: active, onValueChange: setActive, className: className }));
|
|
48
52
|
}
|
|
49
|
-
return (_jsx(
|
|
53
|
+
return (_jsx(TabBarShell, { className: className, ariaLabel: "Tabs", activeKey: active, children: children }));
|
|
50
54
|
}
|
|
51
55
|
export function TabsTrigger({ value, disabled, className = "", children, badge, }) {
|
|
52
56
|
const { active, setActive } = useContext(TabsContext);
|
|
@@ -1,84 +1,292 @@
|
|
|
1
|
-
/* Tabs — tabbed navigation
|
|
1
|
+
/* Tabs — tabbed navigation: shell (scroll arrows) + list + triggers + panels
|
|
2
|
+
*
|
|
3
|
+
* WHY THIS FILE LOOKS THE WAY IT DOES (2026-08-26):
|
|
4
|
+
* The active trigger used to be `var(--color-primary, #65a30d)` — a variable
|
|
5
|
+
* that does not exist in this token system — so EVERY active tab in the app
|
|
6
|
+
* rendered hardcoded lime, ignoring the theme entirely. And past
|
|
7
|
+
* TABS_DROPDOWN_THRESHOLD triggers the bar collapses to a <Select bare>,
|
|
8
|
+
* which carried the full `.appkit-select__trigger` input chrome: a bordered,
|
|
9
|
+
* rounded, surface-filled 40px box that read as a form field rather than as
|
|
10
|
+
* navigation. Both are fixed below.
|
|
11
|
+
*/
|
|
2
12
|
|
|
3
|
-
|
|
13
|
+
/* ── Shell ──────────────────────────────────────────────────────────────
|
|
14
|
+
Owns the rail and the arrow gutters.
|
|
15
|
+
|
|
16
|
+
The rail lives HERE and not on .appkit-tabs-list because the list is the
|
|
17
|
+
scroll container: `overflow-y: hidden` clips anything a trigger pushes
|
|
18
|
+
past its own padding box, so the old `margin-bottom: -1px` overlap never
|
|
19
|
+
actually rendered. On the shell the rail also runs unbroken beneath the
|
|
20
|
+
arrow gutters. */
|
|
21
|
+
.appkit-tabs-shell {
|
|
22
|
+
--appkit-tabs-indicator: var(--appkit-color-primary);
|
|
23
|
+
/* Fallback first, for engines without color-mix (Safari < 16.2): plain ink
|
|
24
|
+
is legible everywhere, just unbranded. */
|
|
25
|
+
--appkit-tabs-ink-active: var(--appkit-color-text);
|
|
26
|
+
/* Blend the brand toward the theme's own ink. --appkit-color-text is by
|
|
27
|
+
definition the max-contrast ink against that theme's background, so this
|
|
28
|
+
self-corrects per theme: light themes darken the brand, dark themes
|
|
29
|
+
lighten it. Using --appkit-color-primary raw for TEXT fails WCAG AA in
|
|
30
|
+
the shipped `sunset` (amber on white, ~2.2:1) and `default-light`
|
|
31
|
+
(~3.7:1) presets. The 2px indicator below stays pure brand — the mix
|
|
32
|
+
applies only to text, which is the part with a contrast requirement. */
|
|
33
|
+
--appkit-tabs-ink-active: color-mix(in srgb, var(--appkit-color-primary) 60%, var(--appkit-color-text));
|
|
34
|
+
--appkit-tabs-arrow-size: 2rem;
|
|
35
|
+
--appkit-tabs-veil: color-mix(in srgb, var(--appkit-color-text) 5%, transparent);
|
|
36
|
+
|
|
37
|
+
position: relative;
|
|
4
38
|
display: flex;
|
|
39
|
+
align-items: stretch;
|
|
40
|
+
min-width: 0; /* survives being a flex/grid item */
|
|
5
41
|
border-bottom: 1px solid var(--appkit-color-border);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ── List — the role="tablist" element, and the scroll container ──────── */
|
|
45
|
+
.appkit-tabs-list {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: stretch;
|
|
48
|
+
gap: var(--appkit-space-1);
|
|
49
|
+
flex: 1 1 auto;
|
|
50
|
+
min-width: 0;
|
|
12
51
|
overflow-x: auto;
|
|
13
52
|
overflow-y: hidden;
|
|
53
|
+
scroll-behavior: smooth;
|
|
54
|
+
/* The thin scrollbar is the no-JS / pre-hydration affordance. Once the
|
|
55
|
+
client shell mounts and stamps data-arrows="true", the arrows take over
|
|
56
|
+
and it is hidden. */
|
|
14
57
|
scrollbar-width: thin;
|
|
15
58
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.appkit-tabs-list
|
|
21
|
-
display:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
59
|
+
.appkit-tabs-shell[data-arrows="true"] .appkit-tabs-list {
|
|
60
|
+
scrollbar-width: none;
|
|
61
|
+
-ms-overflow-style: none;
|
|
62
|
+
}
|
|
63
|
+
.appkit-tabs-shell[data-arrows="true"] .appkit-tabs-list::-webkit-scrollbar {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
66
|
+
@media (prefers-reduced-motion: reduce) {
|
|
67
|
+
.appkit-tabs-list {
|
|
68
|
+
scroll-behavior: auto;
|
|
69
|
+
}
|
|
25
70
|
}
|
|
26
71
|
|
|
72
|
+
/* ── Trigger ─────────────────────────────────────────────────────────────
|
|
73
|
+
Applies to <button> (TabsTrigger, TabBarButton) AND <a> (StoreNavTabs,
|
|
74
|
+
EventTabBar), so `border: 0; background: transparent; font: inherit`
|
|
75
|
+
normalise the UA button styles rather than assuming one element. */
|
|
27
76
|
.appkit-tabs-trigger {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
77
|
+
position: relative;
|
|
78
|
+
flex: none;
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: var(--appkit-space-2);
|
|
82
|
+
padding: var(--appkit-space-2-5) var(--appkit-space-3);
|
|
83
|
+
border: 0;
|
|
84
|
+
border-radius: var(--appkit-radius-sm) var(--appkit-radius-sm) 0 0;
|
|
85
|
+
background-color: transparent;
|
|
86
|
+
font: inherit;
|
|
87
|
+
font-size: var(--appkit-text-base);
|
|
88
|
+
/* Weight stays 500 in EVERY state on purpose: a 500 -> 600 swap on select
|
|
89
|
+
shifts every following tab a few px and re-triggers the overflow
|
|
90
|
+
measurement in tabs-overflow.ts. */
|
|
32
91
|
font-weight: 500;
|
|
33
|
-
|
|
92
|
+
line-height: 1.25rem;
|
|
93
|
+
color: var(--appkit-color-text-muted);
|
|
94
|
+
text-decoration: none;
|
|
34
95
|
white-space: nowrap;
|
|
35
|
-
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
transition:
|
|
98
|
+
color var(--appkit-duration-fast) ease,
|
|
99
|
+
box-shadow var(--appkit-duration-fast) ease,
|
|
100
|
+
background-color var(--appkit-duration-fast) ease;
|
|
36
101
|
}
|
|
102
|
+
|
|
103
|
+
/* The indicator is an INSET box-shadow, not a border:
|
|
104
|
+
- it paints inside the padding box, so the list's `overflow-y: hidden`
|
|
105
|
+
can never clip it (a bottom border + negative margin can, and did);
|
|
106
|
+
- it costs zero layout, so switching tabs cannot reflow the strip;
|
|
107
|
+
- it renders identically on <a> and <button>. */
|
|
37
108
|
.appkit-tabs-trigger[aria-selected="true"] {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
.appkit-tabs-trigger:not([aria-selected="true"]) {
|
|
42
|
-
color: var(--appkit-color-zinc-600);
|
|
109
|
+
color: var(--appkit-tabs-ink-active);
|
|
110
|
+
box-shadow: inset 0 -2px 0 0 var(--appkit-tabs-indicator);
|
|
43
111
|
}
|
|
112
|
+
|
|
44
113
|
.appkit-tabs-trigger:not([aria-selected="true"]):hover {
|
|
45
114
|
color: var(--appkit-color-text);
|
|
115
|
+
background-color: var(--appkit-tabs-veil);
|
|
116
|
+
box-shadow: inset 0 -2px 0 0 var(--appkit-color-border);
|
|
46
117
|
}
|
|
47
|
-
|
|
118
|
+
|
|
119
|
+
/* outline-offset MUST be negative — a positive or zero offset draws outside
|
|
120
|
+
the padding box and is clipped by the scroll container. */
|
|
121
|
+
.appkit-tabs-trigger:focus-visible {
|
|
122
|
+
outline: 2px solid var(--appkit-color-focus-ring);
|
|
123
|
+
outline-offset: -2px;
|
|
124
|
+
border-radius: var(--appkit-radius-sm);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.appkit-tabs-trigger:disabled,
|
|
128
|
+
.appkit-tabs-trigger[aria-disabled="true"] {
|
|
48
129
|
cursor: not-allowed;
|
|
49
130
|
opacity: 0.5;
|
|
131
|
+
box-shadow: none;
|
|
132
|
+
background-color: transparent;
|
|
50
133
|
}
|
|
51
134
|
|
|
135
|
+
/* ── Badge ───────────────────────────────────────────────────────────── */
|
|
52
136
|
.appkit-tabs-trigger__badge {
|
|
53
137
|
display: inline-flex;
|
|
54
138
|
align-items: center;
|
|
55
139
|
justify-content: center;
|
|
56
|
-
margin-left: 0.5rem;
|
|
57
140
|
padding: 0 0.4rem;
|
|
58
141
|
min-width: 1.25rem;
|
|
59
142
|
height: 1.25rem;
|
|
60
143
|
border-radius: 9999px;
|
|
61
144
|
font-size: 0.6875rem;
|
|
62
145
|
font-weight: 600;
|
|
63
|
-
|
|
146
|
+
/* NOT --appkit-color-surface-elevated: that token is #ffffff in light mode,
|
|
147
|
+
so the inactive badge was invisible on every white card. */
|
|
148
|
+
background-color: color-mix(in srgb, var(--appkit-color-text) 8%, transparent);
|
|
64
149
|
color: var(--appkit-color-text-muted);
|
|
65
150
|
}
|
|
66
151
|
.appkit-tabs-trigger[aria-selected="true"] .appkit-tabs-trigger__badge {
|
|
67
|
-
background: color-mix(in srgb, var(--appkit-color-primary) 15%, transparent);
|
|
68
|
-
color: var(--appkit-
|
|
152
|
+
background-color: color-mix(in srgb, var(--appkit-color-primary) 15%, transparent);
|
|
153
|
+
color: var(--appkit-tabs-ink-active);
|
|
69
154
|
}
|
|
70
155
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
156
|
+
/* ── Scroll arrows ───────────────────────────────────────────────────────
|
|
157
|
+
In-flow flex siblings of the tablist, NOT absolutely-positioned overlays.
|
|
158
|
+
An overlay would need a background matching whatever surface the bar
|
|
159
|
+
happens to sit on (bg / surface / surface-elevated all occur in this app)
|
|
160
|
+
and would cover the leftmost/rightmost tab. In-flow arrows need no
|
|
161
|
+
background at all. */
|
|
162
|
+
.appkit-tabs-shell__arrow {
|
|
163
|
+
flex: none;
|
|
164
|
+
align-self: stretch;
|
|
165
|
+
display: inline-flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: center;
|
|
168
|
+
width: var(--appkit-tabs-arrow-size);
|
|
169
|
+
padding: 0;
|
|
170
|
+
border: 0;
|
|
171
|
+
background-color: transparent;
|
|
172
|
+
color: var(--appkit-color-text-muted);
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
transition:
|
|
175
|
+
color var(--appkit-duration-fast) ease,
|
|
176
|
+
background-color var(--appkit-duration-fast) ease,
|
|
177
|
+
opacity var(--appkit-duration-fast) ease;
|
|
178
|
+
}
|
|
179
|
+
.appkit-tabs-shell__arrow:hover:not([disabled]) {
|
|
180
|
+
color: var(--appkit-color-text);
|
|
181
|
+
background-color: var(--appkit-tabs-veil);
|
|
182
|
+
}
|
|
183
|
+
.appkit-tabs-shell__arrow[disabled] {
|
|
184
|
+
opacity: 0.3;
|
|
185
|
+
cursor: default;
|
|
74
186
|
}
|
|
187
|
+
/* RTL: the chevron must point the way the content actually moves. */
|
|
188
|
+
[dir="rtl"] .appkit-tabs-shell__arrow > svg {
|
|
189
|
+
transform: scaleX(-1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ── Dropdown (past TABS_DROPDOWN_THRESHOLD triggers) ─────────────────────
|
|
193
|
+
The wrapper carries the sizing and the indicator rail; the <select> itself
|
|
194
|
+
is stripped back to a bare label.
|
|
75
195
|
|
|
76
|
-
|
|
77
|
-
|
|
196
|
+
What makes this read as NAVIGATION rather than as an input:
|
|
197
|
+
1. the enclosing rectangle is gone (border, radius, surface fill) — a
|
|
198
|
+
form field is legible as a field almost entirely because of its box;
|
|
199
|
+
2. the only chrome left is the same 2px brand rail an active row-tab
|
|
200
|
+
uses, so it speaks the same language as the bars beside it;
|
|
201
|
+
3. height collapses from --appkit-size-input-md (40px) to a row tab's;
|
|
202
|
+
4. weight 600 + active-tab ink — input VALUES are 400-weight plain text;
|
|
203
|
+
5. padding-left: 0 aligns the label flush with the content column below,
|
|
204
|
+
the way a section heading does (inputs are inset ~12px);
|
|
205
|
+
6. a caret finally exists — Select's chevron SVG only renders in its
|
|
206
|
+
non-`bare` branch, while `appearance-none` applies in both, so this
|
|
207
|
+
control previously had no disclosure affordance whatsoever. */
|
|
208
|
+
.appkit-tabs-dropdown {
|
|
209
|
+
position: relative;
|
|
210
|
+
display: block;
|
|
211
|
+
flex: none; /* when it sits in TabBarShell's `leading` slot */
|
|
212
|
+
/* Select hardcodes a `w-full` utility, which is !important and unbeatable,
|
|
213
|
+
so the SELECT can never shrink to its content — the WRAPPER has to. With
|
|
214
|
+
`fit-content` the child's percentage width is treated as auto for
|
|
215
|
+
intrinsic sizing, so the box hugs the widest option (native <select>
|
|
216
|
+
behaviour) and the rail hugs the label rather than running a fixed 20rem
|
|
217
|
+
under a short one, which would read as an underlined input again. */
|
|
218
|
+
width: fit-content;
|
|
219
|
+
max-width: 20rem;
|
|
220
|
+
min-width: 0;
|
|
221
|
+
border-bottom: 2px solid var(--appkit-tabs-indicator);
|
|
222
|
+
}
|
|
223
|
+
/* StoreNavTabs' listing-type picker uses this while no listing tab is the
|
|
224
|
+
active one — same control, but making no "you are here" claim. */
|
|
225
|
+
.appkit-tabs-dropdown--unselected {
|
|
226
|
+
border-bottom-color: transparent;
|
|
78
227
|
}
|
|
79
|
-
|
|
80
|
-
|
|
228
|
+
|
|
229
|
+
/* 🛑 The compound selectors below are sized to out-specify Select.style.css.
|
|
230
|
+
Do NOT "simplify" them to single-class selectors: the input chrome comes
|
|
231
|
+
straight back and the tie falls to @import order in index.style.css.
|
|
232
|
+
.appkit-select__trigger (0,1,0)
|
|
233
|
+
.appkit-select__trigger--default:focus-visible (0,2,0)
|
|
234
|
+
.appkit-select__trigger--enabled:hover:not(:focus-visible) (0,3,0)
|
|
235
|
+
.dark .appkit-select__trigger (0,2,0) */
|
|
236
|
+
.appkit-tabs-dropdown__select.appkit-select__trigger {
|
|
237
|
+
min-height: 0; /* kills --appkit-size-input-md (40px) */
|
|
238
|
+
border: 0; /* kills the 1px border */
|
|
239
|
+
border-radius: 0; /* kills the .5rem radius */
|
|
240
|
+
background-color: transparent; /* kills the surface fill */
|
|
241
|
+
box-shadow: none;
|
|
242
|
+
/* padding-right is deliberately NOT set: Select hardcodes the `pr-10`
|
|
243
|
+
utility, which is !important and unbeatable. 2.5rem is exactly the caret
|
|
244
|
+
gutter we want anyway. */
|
|
245
|
+
padding-top: var(--appkit-space-2-5);
|
|
246
|
+
padding-bottom: var(--appkit-space-2-5);
|
|
247
|
+
padding-left: 0;
|
|
248
|
+
font-size: var(--appkit-text-base);
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
color: var(--appkit-tabs-ink-active);
|
|
251
|
+
cursor: pointer;
|
|
81
252
|
}
|
|
82
|
-
.
|
|
253
|
+
.appkit-tabs-dropdown__select.appkit-select__trigger:hover:not(:focus-visible) {
|
|
254
|
+
border-color: transparent;
|
|
83
255
|
color: var(--appkit-color-text);
|
|
84
256
|
}
|
|
257
|
+
.appkit-tabs-dropdown__select.appkit-select__trigger:focus-visible {
|
|
258
|
+
outline: 2px solid var(--appkit-color-focus-ring);
|
|
259
|
+
outline-offset: 2px;
|
|
260
|
+
border-color: transparent;
|
|
261
|
+
box-shadow: none;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.appkit-tabs-dropdown__caret {
|
|
265
|
+
position: absolute;
|
|
266
|
+
top: 50%;
|
|
267
|
+
right: var(--appkit-space-2);
|
|
268
|
+
transform: translateY(-50%);
|
|
269
|
+
display: inline-flex;
|
|
270
|
+
pointer-events: none;
|
|
271
|
+
color: var(--appkit-tabs-ink-active);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* ── Panels ──────────────────────────────────────────────────────────── */
|
|
275
|
+
.appkit-tabs-content {
|
|
276
|
+
padding-top: var(--appkit-space-4);
|
|
277
|
+
padding-bottom: var(--appkit-space-4);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* ── Dark mode ───────────────────────────────────────────────────────────
|
|
281
|
+
Exactly ONE override is needed. Every colour above is either a semantic
|
|
282
|
+
token that flips with the theme, or a color-mix against
|
|
283
|
+
--appkit-color-text / --appkit-color-primary, which flip with it too. This
|
|
284
|
+
rule exists solely because Select.style.css hardcodes a dark background at
|
|
285
|
+
(0,2,0). The old `.dark .appkit-tabs-trigger { color: slate-400 }` pair is
|
|
286
|
+
gone — it pinned a fixed ramp value that ignored the cobalt-night and
|
|
287
|
+
sunset themes. */
|
|
288
|
+
.dark .appkit-tabs-dropdown__select.appkit-select__trigger {
|
|
289
|
+
background-color: transparent;
|
|
290
|
+
border-color: transparent;
|
|
291
|
+
color: var(--appkit-tabs-ink-active);
|
|
292
|
+
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
.appkit-view-toggle__button--inactive:hover {
|
|
29
|
-
background: var(--appkit-color-
|
|
29
|
+
background: var(--appkit-color-surface-hover);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.appkit-view-toggle__icon {
|
|
@@ -44,7 +44,3 @@
|
|
|
44
44
|
color: var(--appkit-color-slate-400);
|
|
45
45
|
border-color: var(--appkit-color-border);
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
.dark .appkit-view-toggle__button--inactive:hover {
|
|
49
|
-
background: var(--appkit-color-slate-800);
|
|
50
|
-
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface TabsOverflowState {
|
|
2
|
+
/** True only once the client has mounted AND the strip actually overflows. */
|
|
3
|
+
hasArrows: boolean;
|
|
4
|
+
atStart: boolean;
|
|
5
|
+
atEnd: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface TabsOverflowApi extends TabsOverflowState {
|
|
8
|
+
scrollByPage: (direction: 1 | -1) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Drives the scroll-arrow affordances on a horizontally scrollable tab list.
|
|
12
|
+
*
|
|
13
|
+
* `activeKey` identifies the currently-selected tab. It exists solely to key
|
|
14
|
+
* the scroll-into-view effect, so the strip repositions when the selection
|
|
15
|
+
* changes and stays put while the user scrolls.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useTabsOverflow(listRef: React.RefObject<HTMLElement | null>, activeKey: string): TabsOverflowApi;
|