diff --git a/tanstack-start/app/components/ThemeButton.tsx b/tanstack-start/app/components/ThemeButton.tsx index 1a089e1..591b3bf 100644 --- a/tanstack-start/app/components/ThemeButton.tsx +++ b/tanstack-start/app/components/ThemeButton.tsx @@ -22,7 +22,7 @@ export function ThemeButton() { await randomColorTheme() }) } - className={`bg-theme-button text-theme-button focus:ring-theme focus:ring-offset-theme rounded-md px-4 py-2 text-sm font-semibold transition ease-in-out focus:outline-none focus:ring-2 focus:ring-opacity-50 focus:ring-offset-2 focus:duration-0 disabled:cursor-not-allowed disabled:opacity-50 ${pending ? 'animate-pulse cursor-wait duration-150' : 'duration-1000'} `} + className={`bg-theme-button text-theme-button focus:ring-theme focus:ring-offset-theme rounded-md px-4 py-2 text-sm font-semibold transition ease-in-out [view-transition-name:theme-button] focus:outline-none focus:ring-2 focus:ring-opacity-50 focus:ring-offset-2 focus:duration-0 disabled:cursor-not-allowed disabled:opacity-50 ${pending ? 'animate-pulse cursor-wait duration-150' : 'duration-1000'} `} > {pending ? 'Generating...' : 'Random Color Theme'} diff --git a/tanstack-start/app/routeTree.gen.ts b/tanstack-start/app/routeTree.gen.ts index 3b0593f..349b401 100644 --- a/tanstack-start/app/routeTree.gen.ts +++ b/tanstack-start/app/routeTree.gen.ts @@ -10,11 +10,18 @@ // Import Routes -import {Route as rootRoute} from './routes/__root' -import {Route as IndexImport} from './routes/index' +import { Route as rootRoute } from './routes/__root' +import { Route as GotoImport } from './routes/goto' +import { Route as IndexImport } from './routes/index' // Create/Update Routes +const GotoRoute = GotoImport.update({ + id: '/goto', + path: '/goto', + getParentRoute: () => rootRoute, +} as any) + const IndexRoute = IndexImport.update({ id: '/', path: '/', @@ -32,6 +39,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexImport parentRoute: typeof rootRoute } + '/goto': { + id: '/goto' + path: '/goto' + fullPath: '/goto' + preLoaderRoute: typeof GotoImport + parentRoute: typeof rootRoute + } } } @@ -39,32 +53,37 @@ declare module '@tanstack/react-router' { export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/goto': typeof GotoRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/goto': typeof GotoRoute } export interface FileRoutesById { - '__root__': typeof rootRoute + __root__: typeof rootRoute '/': typeof IndexRoute + '/goto': typeof GotoRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' + fullPaths: '/' | '/goto' fileRoutesByTo: FileRoutesByTo - to: '/' - id: '__root__' | '/' + to: '/' | '/goto' + id: '__root__' | '/' | '/goto' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + GotoRoute: typeof GotoRoute } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + GotoRoute: GotoRoute, } export const routeTree = rootRoute @@ -77,11 +96,15 @@ export const routeTree = rootRoute "__root__": { "filePath": "__root.tsx", "children": [ - "/" + "/", + "/goto" ] }, "/": { "filePath": "index.tsx" + }, + "/goto": { + "filePath": "goto.tsx" } } } diff --git a/tanstack-start/app/router.tsx b/tanstack-start/app/router.tsx index b91f835..afbac36 100644 --- a/tanstack-start/app/router.tsx +++ b/tanstack-start/app/router.tsx @@ -4,6 +4,7 @@ import {routeTree} from './routeTree.gen' export function createRouter() { const router = createTanStackRouter({ routeTree, + defaultViewTransition: true, }) return router diff --git a/tanstack-start/app/routes/goto.tsx b/tanstack-start/app/routes/goto.tsx new file mode 100644 index 0000000..d30f45a --- /dev/null +++ b/tanstack-start/app/routes/goto.tsx @@ -0,0 +1,96 @@ +import {createFileRoute, Link} from '@tanstack/react-router' +import {createServerFn} from '@tanstack/start' +import {zodValidator} from '@tanstack/zod-adapter' +import {defineQuery} from 'groq' +import {useEffect, useId} from 'react' +import {z} from 'zod' +import {sanityFetch} from '../utils/sanity' + +const SEARCH_QUERY = defineQuery(`{ + "title": *[_type == "demo" && slug.current == $slug][0].title, + "urls": *[_type == "demo" && slug.current != $slug && [title,slug.current,url] match "*"+$q+"*"]{title,url} +}`) + +const getDemo = createServerFn({ + method: 'GET', +}) + .validator( + z.object({ + lastLiveEventId: z.string().optional(), + slug: z.string(), + q: z.string(), + }), + ) + .handler(({data: {slug, q, lastLiveEventId}}) => { + return sanityFetch({ + query: SEARCH_QUERY, + params: {slug, q}, + lastLiveEventId, + }) + }) + +export const Route = createFileRoute('/goto')({ + component: Home, + validateSearch: zodValidator( + z.object({ + q: z.string().default(''), + }), + ), + loaderDeps: ({search: {lastLiveEventId, q}}) => ({lastLiveEventId, q}), + loader: async ({deps: {lastLiveEventId, q}}) => + await getDemo({data: {slug: 'tanstack-start', q, lastLiveEventId}}), + head: (ctx) => ({ + meta: [{title: ctx.loaderData?.data?.title || 'TanStack Start Starter'}], + }), +}) + +function Home() { + const state = Route.useLoaderData() + const {q} = Route.useSearch() + const navigate = Route.useNavigate() + const id = useId() + + // @TODO handle this server side + useEffect(() => { + if (!q) return + const validUrl = state.data.urls.find((item) => item.url && new URL(item.url).hostname === q) + if (validUrl?.url) { + location.href = validUrl.url + } + }, [state.data.urls, q]) + + return ( + <> +

+ {state.data?.title || 'TanStack Start Starter'} +

+ { + const q = event.currentTarget.value + navigate({ + search: (prev) => ({...prev, q}), + replace: true, + resetScroll: false, + }) + }} + /> + + {state.data.urls.map((item) => { + if (!item || !item.title || !item.url) return null + return ( + + ) + })} + + + ) +} diff --git a/tanstack-start/app/routes/index.tsx b/tanstack-start/app/routes/index.tsx index 969e85f..5dcc926 100644 --- a/tanstack-start/app/routes/index.tsx +++ b/tanstack-start/app/routes/index.tsx @@ -1,4 +1,4 @@ -import {createFileRoute} from '@tanstack/react-router' +import {createFileRoute, Link} from '@tanstack/react-router' import {createServerFn} from '@tanstack/start' import {defineQuery} from 'groq' import {z} from 'zod' @@ -33,8 +33,16 @@ function Home() { const state = Route.useLoaderData() return ( -

- {state.data || 'TanStack Start Starter'} -

+ <> +

+ {state.data || 'TanStack Start Starter'} +

+ + Go to... + + ) } diff --git a/tanstack-start/sanity.types.ts b/tanstack-start/sanity.types.ts index 46cf907..229bae2 100644 --- a/tanstack-start/sanity.types.ts +++ b/tanstack-start/sanity.types.ts @@ -184,6 +184,17 @@ export type THEME_QUERYResult = } | null +// Source: ./app/routes/goto.tsx +// Variable: SEARCH_QUERY +// Query: { "title": *[_type == "demo" && slug.current == $slug][0].title, "urls": *[_type == "demo" && slug.current != $slug && [title,slug.current,url] match "*"+$q+"*"]{title,url}} +export type SEARCH_QUERYResult = { + title: string | null + urls: Array<{ + title: string | null + url: string | null + }> +} + // Source: ./app/routes/index.tsx // Variable: DEMO_QUERY // Query: *[_type == "demo" && slug.current == $slug][0].title @@ -192,6 +203,7 @@ export type DEMO_QUERYResult = string | null declare module '@sanity/client' { interface SanityQueries { '*[_id == "theme"][0]{background,text}': THEME_QUERYResult + '{\n "title": *[_type == "demo" && slug.current == $slug][0].title,\n "urls": *[_type == "demo" && slug.current != $slug && [title,slug.current,url] match "*"+$q+"*"]{title,url}\n}': SEARCH_QUERYResult '*[_type == "demo" && slug.current == $slug][0].title': DEMO_QUERYResult } }