diff --git a/package.json b/package.json index a1e819f..345a6f4 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@radix-ui/react-slot": "^1.2.3", + "@base-ui-components/react": "1.0.0-beta.4", "@tailwindcss/vite": "^4.1.14", "@tanstack/react-devtools": "^0.7.1", "@tanstack/react-query": "^5.90.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39a3d56..5a9c3b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - '@radix-ui/react-slot': - specifier: ^1.2.3 - version: 1.2.3(@types/react@19.2.0)(react@19.2.0) + '@base-ui-components/react': + specifier: 1.0.0-beta.4 + version: 1.0.0-beta.4(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tailwindcss/vite': specifier: ^4.1.14 version: 4.1.14(vite@7.1.8(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)) @@ -164,6 +164,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -176,6 +180,27 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@base-ui-components/react@1.0.0-beta.4': + resolution: {integrity: sha512-sPYKj26gbFHD2ZsrMYqQshXnMuomBodzPn+d0dDxWieTj232XCQ9QGt9fU9l5SDGC9hi8s24lDlg9FXPSI7T8A==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@base-ui-components/utils@0.1.2': + resolution: {integrity: sha512-aEitDGpMsYO2qnSpYOwZNykn9Rzn2ioyEVk2fyDRH7t+TIHVKpp9CeV7SPTq43M9mMSDxQ+7UeZJVkrj2dCVIQ==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + '@esbuild/aix-ppc64@0.25.10': resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} @@ -370,6 +395,21 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -418,24 +458,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@rolldown/pluginutils@1.0.0-beta.38': resolution: {integrity: sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==} @@ -1329,6 +1351,9 @@ packages: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1390,6 +1415,9 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} @@ -1449,6 +1477,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + vite@7.1.8: resolution: {integrity: sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1612,6 +1645,8 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.28.4': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -1635,6 +1670,31 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@base-ui-components/react@1.0.0-beta.4(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@base-ui-components/utils': 0.1.2(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@floating-ui/utils': 0.2.10 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + reselect: 5.1.1 + tabbable: 6.2.0 + use-sync-external-store: 1.6.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.0 + + '@base-ui-components/utils@0.1.2(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@floating-ui/utils': 0.2.10 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + reselect: 5.1.1 + use-sync-external-store: 1.6.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.0 + '@esbuild/aix-ppc64@0.25.10': optional: true @@ -1757,6 +1817,23 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + '@floating-ui/utils@0.2.10': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -1803,19 +1880,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.0)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.0 - - '@radix-ui/react-slot@1.2.3(@types/react@19.2.0)(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.0)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.0 - '@rolldown/pluginutils@1.0.0-beta.38': {} '@rollup/rollup-android-arm-eabi@4.52.3': @@ -2650,6 +2714,8 @@ snapshots: react@19.2.0: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} reusify@1.1.0: {} @@ -2718,6 +2784,8 @@ snapshots: dependencies: has-flag: 4.0.0 + tabbable@6.2.0: {} + tailwind-merge@3.3.1: {} tailwindcss@4.1.14: {} @@ -2776,6 +2844,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.6.0(react@19.2.0): + dependencies: + react: 19.2.0 + vite@7.1.8(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1): dependencies: esbuild: 0.25.10 diff --git a/src/components/pokemon-tabs.tsx b/src/components/pokemon-tabs.tsx index 13908c7..fd52b38 100644 --- a/src/components/pokemon-tabs.tsx +++ b/src/components/pokemon-tabs.tsx @@ -1,4 +1,5 @@ import { Activity, Suspense } from "react"; +import { Tabs, TabsPanel, TabsList, TabsTrigger } from "./ui/tabs"; import { useQueryState, parseAsStringLiteral } from "nuqs"; import { useActivityEnabled, useSuspenseEnabled } from "../lib/utils"; @@ -23,6 +24,7 @@ export function PokemonTabs() { clearOnDefault: false, }) ); + const [activityEnabled] = useActivityEnabled(); const [suspenseEnabled] = useSuspenseEnabled(); @@ -30,64 +32,48 @@ export function PokemonTabs() { ? SuspendingPokemonCard : NonSuspendingPokemonCard; - const content = activityEnabled ? ( - <> - - - - - - - - - - - ) : ( - <> - {activePokemon === "bulbasaur" && ( - - )} - {activePokemon === "charmander" && ( - - )} - {activePokemon === "squirtle" && ( - - )} - + const renderActivityTab = (tab: (typeof POKEMON_TABS)[number]) => ( + + {/* Always mount the tab content, let activity handle the visibility */} + + + + + ); + + const renderNonActivityTab = (tab: (typeof POKEMON_TABS)[number]) => ( + + + ); return ( -
- {/* Custom Tab List */} -
+ { + setActivePokemon(value); + }} + > + {POKEMON_TABS.map((tab) => ( - + ))} -
- - {/* Custom Tab Content */} -
- {suspenseEnabled ? ( - }>{content} - ) : ( - content - )} -
-
+ + {suspenseEnabled ? ( + }> + {POKEMON_TABS.map((tab) => + activityEnabled ? renderActivityTab(tab) : renderNonActivityTab(tab) + )} + + ) : ( + <> + {POKEMON_TABS.map((tab) => + activityEnabled ? renderActivityTab(tab) : renderNonActivityTab(tab) + )} + + )} + ); } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 3afec80..9ca8ab7 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,8 +1,7 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import { useRender, mergeProps } from "@base-ui-components/react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "~/lib/utils" +import { cn } from "~/lib/utils"; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", @@ -32,27 +31,22 @@ const buttonVariants = cva( size: "default", }, } -) +); -function Button({ - className, - variant, - size, - asChild = false, - ...props -}: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { - const Comp = asChild ? Slot : "button" +interface ButtonProps extends useRender.ComponentProps<"button"> { + variant?: VariantProps["variant"]; + size?: VariantProps["size"]; +} - return ( - - ) +function Button({ className, variant, size, render, ...props }: ButtonProps) { + return useRender({ + defaultTagName: "button", + render, + props: mergeProps(props, { + "data-slot": "button", + className: cn(buttonVariants({ variant, size, className })), + }), + }); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..4495700 --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,51 @@ +import { Tabs as TabsPrimitive } from "@base-ui-components/react/tabs"; + +import { cn } from "~/lib/utils"; + +function Tabs({ className, ...props }: TabsPrimitive.Root.Props) { + return ( + + ); +} + +function TabsList({ className, ...props }: TabsPrimitive.List.Props) { + return ( + + ); +} + +function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) { + return ( + + ); +} + +function TabsPanel({ className, ...props }: TabsPrimitive.Panel.Props) { + return ( + + ); +} + +export { Tabs, TabsList, TabsTrigger, TabsPanel }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a5d6c7e..cb7997e 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -25,7 +25,7 @@ export function useActivityEnabled() { export function useSuspenseEnabled() { const queryClient = useQueryClient(); const [suspenseEnabled, _setSuspenseEnabled] = useQueryState( - "activitySuspending", + "suspenseEnabled", parseAsBoolean.withDefault(false) );