Prettier prettied
This commit is contained in:
+1
-4
@@ -7,8 +7,5 @@
|
||||
"git": true,
|
||||
"version": 1,
|
||||
"framework": "react-cra",
|
||||
"chosenAddOns": [
|
||||
"form",
|
||||
"tanstack-query"
|
||||
]
|
||||
"chosenAddOns": ["form", "tanstack-query"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const config = {
|
||||
trailingComma: "es5",
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
};
|
||||
|
||||
export default config;
|
||||
+6
-13
@@ -29,10 +29,8 @@ bunx --bun run test
|
||||
|
||||
This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
|
||||
|
||||
|
||||
|
||||
|
||||
## Routing
|
||||
|
||||
This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
|
||||
|
||||
### Adding A Route
|
||||
@@ -68,8 +66,8 @@ In the File Based Routing setup the layout is located in `src/routes/__root.tsx`
|
||||
Here is an example layout that includes a header:
|
||||
|
||||
```tsx
|
||||
import { Outlet, createRootRoute } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
@@ -86,14 +84,13 @@ export const Route = createRootRoute({
|
||||
<TanStackRouterDevtools />
|
||||
</>
|
||||
),
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
The `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.
|
||||
|
||||
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
|
||||
|
||||
|
||||
## Data Fetching
|
||||
|
||||
There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
|
||||
@@ -231,9 +228,7 @@ function App() {
|
||||
const count = useStore(countStore);
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => countStore.setState((n) => n + 1)}>
|
||||
Increment - {count}
|
||||
</button>
|
||||
<button onClick={() => countStore.setState((n) => n + 1)}>Increment - {count}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -264,9 +259,7 @@ function App() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => countStore.setState((n) => n + 1)}>
|
||||
Increment - {count}
|
||||
</button>
|
||||
<button onClick={() => countStore.setState((n) => n + 1)}>Increment - {count}</button>
|
||||
<div>Doubled - {doubledCount}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jsdom": "^26.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.1.0",
|
||||
"vitest": "^3.0.5",
|
||||
|
||||
+2
-5
@@ -1,14 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-tsrouter-app"
|
||||
/>
|
||||
<meta name="description" content="Web site created using create-tsrouter-app" />
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>Create TanStack App - .</title>
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jsdom": "^26.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.1.0",
|
||||
"vitest": "^3.0.5",
|
||||
"web-vitals": "^4.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
@@ -21,5 +21,5 @@ export default function Header() {
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useStore } from '@tanstack/react-form'
|
||||
import { useStore } from "@tanstack/react-form";
|
||||
|
||||
import { useFieldContext, useFormContext } from '../hooks/demo.form-context'
|
||||
import { useFieldContext, useFormContext } from "../hooks/demo.form-context";
|
||||
|
||||
export function SubscribeButton({ label }: { label: string }) {
|
||||
const form = useFormContext()
|
||||
const form = useFormContext();
|
||||
return (
|
||||
<form.Subscribe selector={(state) => state.isSubmitting}>
|
||||
{(isSubmitting) => (
|
||||
@@ -16,37 +16,24 @@ export function SubscribeButton({ label }: { label: string }) {
|
||||
</button>
|
||||
)}
|
||||
</form.Subscribe>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function ErrorMessages({
|
||||
errors,
|
||||
}: {
|
||||
errors: Array<string | { message: string }>
|
||||
}) {
|
||||
function ErrorMessages({ errors }: { errors: Array<string | { message: string }> }) {
|
||||
return (
|
||||
<>
|
||||
{errors.map((error) => (
|
||||
<div
|
||||
key={typeof error === 'string' ? error : error.message}
|
||||
className="text-red-500 mt-1 font-bold"
|
||||
>
|
||||
{typeof error === 'string' ? error : error.message}
|
||||
<div key={typeof error === "string" ? error : error.message} className="text-red-500 mt-1 font-bold">
|
||||
{typeof error === "string" ? error : error.message}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function TextField({
|
||||
label,
|
||||
placeholder,
|
||||
}: {
|
||||
label: string
|
||||
placeholder?: string
|
||||
}) {
|
||||
const field = useFieldContext<string>()
|
||||
const errors = useStore(field.store, (state) => state.meta.errors)
|
||||
export function TextField({ label, placeholder }: { label: string; placeholder?: string }) {
|
||||
const field = useFieldContext<string>();
|
||||
const errors = useStore(field.store, (state) => state.meta.errors);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -62,18 +49,12 @@ export function TextField({
|
||||
</label>
|
||||
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function TextArea({
|
||||
label,
|
||||
rows = 3,
|
||||
}: {
|
||||
label: string
|
||||
rows?: number
|
||||
}) {
|
||||
const field = useFieldContext<string>()
|
||||
const errors = useStore(field.store, (state) => state.meta.errors)
|
||||
export function TextArea({ label, rows = 3 }: { label: string; rows?: number }) {
|
||||
const field = useFieldContext<string>();
|
||||
const errors = useStore(field.store, (state) => state.meta.errors);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -89,19 +70,19 @@ export function TextArea({
|
||||
</label>
|
||||
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function Select({
|
||||
label,
|
||||
values,
|
||||
}: {
|
||||
label: string
|
||||
values: Array<{ label: string; value: string }>
|
||||
placeholder?: string
|
||||
label: string;
|
||||
values: Array<{ label: string; value: string }>;
|
||||
placeholder?: string;
|
||||
}) {
|
||||
const field = useFieldContext<string>()
|
||||
const errors = useStore(field.store, (state) => state.meta.errors)
|
||||
const field = useFieldContext<string>();
|
||||
const errors = useStore(field.store, (state) => state.meta.errors);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -123,5 +104,5 @@ export function Select({
|
||||
</select>
|
||||
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createFormHookContexts } from '@tanstack/react-form'
|
||||
import { createFormHookContexts } from "@tanstack/react-form";
|
||||
|
||||
export const { fieldContext, useFieldContext, formContext, useFormContext } =
|
||||
createFormHookContexts()
|
||||
export const { fieldContext, useFieldContext, formContext, useFormContext } = createFormHookContexts();
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { createFormHook } from '@tanstack/react-form'
|
||||
import { createFormHook } from "@tanstack/react-form";
|
||||
|
||||
import {
|
||||
Select,
|
||||
SubscribeButton,
|
||||
TextArea,
|
||||
TextField,
|
||||
} from '../components/demo.FormComponents'
|
||||
import { fieldContext, formContext } from './demo.form-context'
|
||||
import { Select, SubscribeButton, TextArea, TextField } from "../components/demo.FormComponents";
|
||||
import { fieldContext, formContext } from "./demo.form-context";
|
||||
|
||||
export const { useAppForm } = createFormHook({
|
||||
fieldComponents: {
|
||||
@@ -19,4 +14,4 @@ export const { useAppForm } = createFormHook({
|
||||
},
|
||||
fieldContext,
|
||||
formContext,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
|
||||
export default function LayoutAddition() {
|
||||
return <ReactQueryDevtools buttonPosition="bottom-right" />
|
||||
return <ReactQueryDevtools buttonPosition="bottom-right" />;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export function getContext() {
|
||||
return {
|
||||
queryClient,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function Provider({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,14 +1,14 @@
|
||||
import { StrictMode } from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { RouterProvider, createRouter } from '@tanstack/react-router'
|
||||
import { StrictMode } from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
|
||||
import * as TanStackQueryProvider from './integrations/tanstack-query/root-provider.tsx'
|
||||
import * as TanStackQueryProvider from "./integrations/tanstack-query/root-provider.tsx";
|
||||
|
||||
// Import the generated route tree
|
||||
import { routeTree } from './routeTree.gen'
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
|
||||
import './styles.css'
|
||||
import reportWebVitals from './reportWebVitals.ts'
|
||||
import "./styles.css";
|
||||
import reportWebVitals from "./reportWebVitals.ts";
|
||||
|
||||
// Create a new router instance
|
||||
const router = createRouter({
|
||||
@@ -16,33 +16,33 @@ const router = createRouter({
|
||||
context: {
|
||||
...TanStackQueryProvider.getContext(),
|
||||
},
|
||||
defaultPreload: 'intent',
|
||||
defaultPreload: "intent",
|
||||
scrollRestoration: true,
|
||||
defaultStructuralSharing: true,
|
||||
defaultPreloadStaleTime: 0,
|
||||
})
|
||||
});
|
||||
|
||||
// Register the router instance for type safety
|
||||
declare module '@tanstack/react-router' {
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: typeof router
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
|
||||
// Render the app
|
||||
const rootElement = document.getElementById('app')
|
||||
const rootElement = document.getElementById("app");
|
||||
if (rootElement && !rootElement.innerHTML) {
|
||||
const root = ReactDOM.createRoot(rootElement)
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<TanStackQueryProvider.Provider>
|
||||
<RouterProvider router={router} />
|
||||
</TanStackQueryProvider.Provider>
|
||||
</StrictMode>,
|
||||
)
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals()
|
||||
reportWebVitals();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
const reportWebVitals = (onPerfEntry?: () => void) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
|
||||
onCLS(onPerfEntry)
|
||||
onINP(onPerfEntry)
|
||||
onFCP(onPerfEntry)
|
||||
onLCP(onPerfEntry)
|
||||
onTTFB(onPerfEntry)
|
||||
})
|
||||
import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
|
||||
onCLS(onPerfEntry);
|
||||
onINP(onPerfEntry);
|
||||
onFCP(onPerfEntry);
|
||||
onLCP(onPerfEntry);
|
||||
onTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals
|
||||
export default reportWebVitals;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
||||
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
|
||||
import Header from '../components/Header'
|
||||
import Header from "../components/Header";
|
||||
|
||||
import TanStackQueryLayout from '../integrations/tanstack-query/layout.tsx'
|
||||
import TanStackQueryLayout from "../integrations/tanstack-query/layout.tsx";
|
||||
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
interface MyRouterContext {
|
||||
queryClient: QueryClient
|
||||
queryClient: QueryClient;
|
||||
}
|
||||
|
||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
@@ -22,4 +22,4 @@ export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
<TanStackQueryLayout />
|
||||
</>
|
||||
),
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,77 +1,74 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { useAppForm } from '../hooks/demo.form'
|
||||
import { useAppForm } from "../hooks/demo.form";
|
||||
|
||||
export const Route = createFileRoute('/demo/form/address')({
|
||||
export const Route = createFileRoute("/demo/form/address")({
|
||||
component: AddressForm,
|
||||
})
|
||||
});
|
||||
|
||||
function AddressForm() {
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
fullName: '',
|
||||
email: '',
|
||||
fullName: "",
|
||||
email: "",
|
||||
address: {
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zipCode: '',
|
||||
country: '',
|
||||
street: "",
|
||||
city: "",
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "",
|
||||
},
|
||||
phone: '',
|
||||
phone: "",
|
||||
},
|
||||
validators: {
|
||||
onBlur: ({ value }) => {
|
||||
const errors = {
|
||||
fields: {},
|
||||
} as {
|
||||
fields: Record<string, string>
|
||||
}
|
||||
fields: Record<string, string>;
|
||||
};
|
||||
if (value.fullName.trim().length === 0) {
|
||||
errors.fields.fullName = 'Full name is required'
|
||||
errors.fields.fullName = "Full name is required";
|
||||
}
|
||||
return errors
|
||||
return errors;
|
||||
},
|
||||
},
|
||||
onSubmit: ({ value }) => {
|
||||
console.log(value)
|
||||
console.log(value);
|
||||
// Show success message
|
||||
alert('Form submitted successfully!')
|
||||
alert("Form submitted successfully!");
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-center min-h-screen bg-gradient-to-br from-purple-100 to-blue-100 p-4 text-white"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'radial-gradient(50% 50% at 5% 40%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)',
|
||||
backgroundImage: "radial-gradient(50% 50% at 5% 40%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)",
|
||||
}}
|
||||
>
|
||||
<div className="w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
form.handleSubmit()
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="space-y-6"
|
||||
>
|
||||
<form.AppField name="fullName">
|
||||
{(field) => <field.TextField label="Full Name" />}
|
||||
</form.AppField>
|
||||
<form.AppField name="fullName">{(field) => <field.TextField label="Full Name" />}</form.AppField>
|
||||
|
||||
<form.AppField
|
||||
name="email"
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Email is required'
|
||||
return "Email is required";
|
||||
}
|
||||
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
|
||||
return 'Invalid email address'
|
||||
return "Invalid email address";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -83,9 +80,9 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Street address is required'
|
||||
return "Street address is required";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -98,9 +95,9 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'City is required'
|
||||
return "City is required";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -111,9 +108,9 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'State is required'
|
||||
return "State is required";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -124,12 +121,12 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Zip code is required'
|
||||
return "Zip code is required";
|
||||
}
|
||||
if (!/^\d{5}(-\d{4})?$/.test(value)) {
|
||||
return 'Invalid zip code format'
|
||||
return "Invalid zip code format";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -142,9 +139,9 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Country is required'
|
||||
return "Country is required";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -152,13 +149,13 @@ function AddressForm() {
|
||||
<field.Select
|
||||
label="Country"
|
||||
values={[
|
||||
{ label: 'United States', value: 'US' },
|
||||
{ label: 'Canada', value: 'CA' },
|
||||
{ label: 'United Kingdom', value: 'UK' },
|
||||
{ label: 'Australia', value: 'AU' },
|
||||
{ label: 'Germany', value: 'DE' },
|
||||
{ label: 'France', value: 'FR' },
|
||||
{ label: 'Japan', value: 'JP' },
|
||||
{ label: "United States", value: "US" },
|
||||
{ label: "Canada", value: "CA" },
|
||||
{ label: "United Kingdom", value: "UK" },
|
||||
{ label: "Australia", value: "AU" },
|
||||
{ label: "Germany", value: "DE" },
|
||||
{ label: "France", value: "FR" },
|
||||
{ label: "Japan", value: "JP" },
|
||||
]}
|
||||
placeholder="Select a country"
|
||||
/>
|
||||
@@ -170,22 +167,16 @@ function AddressForm() {
|
||||
validators={{
|
||||
onBlur: ({ value }) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Phone number is required'
|
||||
return "Phone number is required";
|
||||
}
|
||||
if (
|
||||
!/^(\+\d{1,3})?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/.test(
|
||||
value,
|
||||
)
|
||||
) {
|
||||
return 'Invalid phone number format'
|
||||
if (!/^(\+\d{1,3})?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/.test(value)) {
|
||||
return "Invalid phone number format";
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
}}
|
||||
>
|
||||
{(field) => (
|
||||
<field.TextField label="Phone" placeholder="123-456-7890" />
|
||||
)}
|
||||
{(field) => <field.TextField label="Phone" placeholder="123-456-7890" />}
|
||||
</form.AppField>
|
||||
|
||||
<div className="flex justify-end">
|
||||
@@ -196,5 +187,5 @@ function AddressForm() {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { z } from 'zod'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useAppForm } from '../hooks/demo.form'
|
||||
import { useAppForm } from "../hooks/demo.form";
|
||||
|
||||
export const Route = createFileRoute('/demo/form/simple')({
|
||||
// Routing
|
||||
export const Route = createFileRoute("/demo/form/simple")({
|
||||
component: SimpleForm,
|
||||
})
|
||||
});
|
||||
|
||||
const schema = z.object({
|
||||
title: z.string().min(1, 'Title is required'),
|
||||
description: z.string().min(1, 'Description is required'),
|
||||
})
|
||||
title: z.string().min(1, "Title is required"),
|
||||
description: z.string().min(1, "Description is required"),
|
||||
});
|
||||
|
||||
function SimpleForm() {
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
title: '',
|
||||
description: '',
|
||||
title: "",
|
||||
description: "",
|
||||
},
|
||||
validators: {
|
||||
onBlur: schema,
|
||||
},
|
||||
onSubmit: ({ value }) => {
|
||||
console.log(value)
|
||||
console.log(value);
|
||||
// Show success message
|
||||
alert('Form submitted successfully!')
|
||||
alert("Form submitted successfully!");
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-center min-h-screen bg-gradient-to-br from-purple-100 to-blue-100 p-4 text-white"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'radial-gradient(50% 50% at 5% 40%, #add8e6 0%, #0000ff 70%, #00008b 100%)',
|
||||
backgroundImage: "radial-gradient(50% 50% at 5% 40%, #add8e6 0%, #0000ff 70%, #00008b 100%)",
|
||||
}}
|
||||
>
|
||||
<div className="w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
form.handleSubmit()
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="space-y-6"
|
||||
>
|
||||
<form.AppField name="title">
|
||||
{(field) => <field.TextField label="Title" />}
|
||||
</form.AppField>
|
||||
<form.AppField name="title">{(field) => <field.TextField label="Title" />}</form.AppField>
|
||||
|
||||
<form.AppField name="description">
|
||||
{(field) => <field.TextArea label="Description" />}
|
||||
@@ -61,5 +59,5 @@ function SimpleForm() {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const Route = createFileRoute('/demo/tanstack-query')({
|
||||
export const Route = createFileRoute("/demo/tanstack-query")({
|
||||
component: TanStackQueryDemo,
|
||||
})
|
||||
});
|
||||
|
||||
function TanStackQueryDemo() {
|
||||
const { data } = useQuery({
|
||||
queryKey: ['people'],
|
||||
queryFn: () =>
|
||||
Promise.resolve([{ name: 'John Doe' }, { name: 'Jane Doe' }]),
|
||||
queryKey: ["people"],
|
||||
queryFn: () => Promise.resolve([{ name: "John Doe" }, { name: "Jane Doe" }]),
|
||||
initialData: [],
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
@@ -22,5 +21,5 @@ function TanStackQueryDemo() {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
body {
|
||||
@apply m-0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
|
||||
"Droid Sans", "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -1,24 +1,20 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import viteReact from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { defineConfig } from "vite";
|
||||
import viteReact from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
|
||||
import { resolve } from 'node:path'
|
||||
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
TanStackRouterVite({ autoCodeSplitting: true }),
|
||||
viteReact(),
|
||||
tailwindcss(),
|
||||
],
|
||||
plugins: [TanStackRouterVite({ autoCodeSplitting: true }), viteReact(), tailwindcss()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
environment: "jsdom",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
"@": resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
file=development.yml
|
||||
|
||||
docker compose -f $file down && docker compose -f $file up --build -d
|
||||
Reference in New Issue
Block a user