From b10d01cae0a521239d22f4a87b3ad5fcdf7ca61c Mon Sep 17 00:00:00 2001 From: Valerio Ageno Date: Thu, 6 Jun 2024 19:41:21 +0200 Subject: [PATCH] doc: add tutorial example --- Cargo.toml | 3 +- examples/tutorial/.gitignore | 13 +++ examples/tutorial/Cargo.toml | 18 +++ examples/tutorial/README.md | 9 ++ examples/tutorial/package.json | 15 +++ examples/tutorial/public/favicon.ico | Bin 0 -> 39529 bytes examples/tutorial/public/react.svg | 5 + examples/tutorial/public/rust.svg | 4 + .../src/components/PokemonLink.module.css | 26 +++++ .../tutorial/src/components/PokemonLink.tsx | 23 ++++ .../src/components/PokemonView.module.css | 38 ++++++ .../tutorial/src/components/PokemonView.tsx | 41 +++++++ examples/tutorial/src/routes/__root.tsx | 9 ++ examples/tutorial/src/routes/index.rs | 27 +++++ examples/tutorial/src/routes/index.tsx | 46 ++++++++ .../tutorial/src/routes/pokemons/[pokemon].rs | 31 +++++ .../src/routes/pokemons/[pokemon].tsx | 6 + examples/tutorial/src/styles/global.css | 109 ++++++++++++++++++ examples/tutorial/tsconfig.json | 25 ++++ examples/tutorial/tsconfig.node.json | 11 ++ 20 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 examples/tutorial/.gitignore create mode 100644 examples/tutorial/Cargo.toml create mode 100644 examples/tutorial/README.md create mode 100644 examples/tutorial/package.json create mode 100644 examples/tutorial/public/favicon.ico create mode 100644 examples/tutorial/public/react.svg create mode 100644 examples/tutorial/public/rust.svg create mode 100644 examples/tutorial/src/components/PokemonLink.module.css create mode 100644 examples/tutorial/src/components/PokemonLink.tsx create mode 100644 examples/tutorial/src/components/PokemonView.module.css create mode 100644 examples/tutorial/src/components/PokemonView.tsx create mode 100644 examples/tutorial/src/routes/__root.tsx create mode 100644 examples/tutorial/src/routes/index.rs create mode 100644 examples/tutorial/src/routes/index.tsx create mode 100644 examples/tutorial/src/routes/pokemons/[pokemon].rs create mode 100644 examples/tutorial/src/routes/pokemons/[pokemon].tsx create mode 100644 examples/tutorial/src/styles/global.css create mode 100644 examples/tutorial/tsconfig.json create mode 100644 examples/tutorial/tsconfig.node.json diff --git a/Cargo.toml b/Cargo.toml index b4ba7ebb..f5a3efbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ ] exclude = [ "examples/playground", - "examples/tuono" + "examples/tuono", + "examples/tutorial" ] diff --git a/examples/tutorial/.gitignore b/examples/tutorial/.gitignore new file mode 100644 index 00000000..071acad0 --- /dev/null +++ b/examples/tutorial/.gitignore @@ -0,0 +1,13 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.tuono +out +target diff --git a/examples/tutorial/Cargo.toml b/examples/tutorial/Cargo.toml new file mode 100644 index 00000000..5ba88e75 --- /dev/null +++ b/examples/tutorial/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tuono" +version = "0.0.1" +edition = "2021" + +[[bin]] +name = "tuono" +path = ".tuono/main.rs" + +[dependencies] +axum = {version = "0.7.5", features = ["json"]} +tokio = { version = "1.37.0", features = ["full"] } +tower-http = {version = "0.5.2", features = ["fs"]} +serde = { version = "1.0.202", features = ["derive"] } +tuono_lib = { path = "../../crates/tuono_lib/"} +serde_json = "1.0" +reqwest = {version = "0.12.4", features = ["json"]} + diff --git a/examples/tutorial/README.md b/examples/tutorial/README.md new file mode 100644 index 00000000..72b0fe6d --- /dev/null +++ b/examples/tutorial/README.md @@ -0,0 +1,9 @@ +# Tuono starter + +This is the starter tuono project. To download it run in your terminal: + +```shell +$ tuono new [NAME] +``` + + diff --git a/examples/tutorial/package.json b/examples/tutorial/package.json new file mode 100644 index 00000000..b8745ca3 --- /dev/null +++ b/examples/tutorial/package.json @@ -0,0 +1,15 @@ +{ + "name": "tuono-tutorial", + "description": "The react/rust fullstack framework", + "version": "0.0.1", + "dependencies": { + "react": "18.3.1", + "react-dom": "18.3.1", + "tuono": "link:../../packages/tuono" + }, + "devDependencies": { + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "typescript": "^5.4.5" + } +} diff --git a/examples/tutorial/public/favicon.ico b/examples/tutorial/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d03bcc6640d69649846a0028bcfff9950c261789 GIT binary patch literal 39529 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 literal 0 HcmV?d00001 diff --git a/examples/tutorial/public/react.svg b/examples/tutorial/public/react.svg new file mode 100644 index 00000000..8fe604f2 --- /dev/null +++ b/examples/tutorial/public/react.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/tutorial/public/rust.svg b/examples/tutorial/public/rust.svg new file mode 100644 index 00000000..7f66780e --- /dev/null +++ b/examples/tutorial/public/rust.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/tutorial/src/components/PokemonLink.module.css b/examples/tutorial/src/components/PokemonLink.module.css new file mode 100644 index 00000000..d134c987 --- /dev/null +++ b/examples/tutorial/src/components/PokemonLink.module.css @@ -0,0 +1,26 @@ +.link { + width: 100%; + max-width: 216px; + position: relative; + background: white; + margin-bottom: 10px; + border: solid #f0f0f0 1px; + text-decoration: none; + color: black; + padding: 5px 5px 5px 15px; + border-radius: 10px; + display: flex; + justify-content: space-between; + transition: 0.2s; + align-items: center; +} + +.link:hover { + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; +} + +.link img { + width: 70px; + background: white; + border-radius: 50%; +} diff --git a/examples/tutorial/src/components/PokemonLink.tsx b/examples/tutorial/src/components/PokemonLink.tsx new file mode 100644 index 00000000..4e7c8b9f --- /dev/null +++ b/examples/tutorial/src/components/PokemonLink.tsx @@ -0,0 +1,23 @@ +import { Link } from 'tuono' +import styles from './PokemonLink.module.css' + +interface Pokemon { + name: string +} + +export default function PokemonLink({ + pokemon, + id, +}: { + pokemon: Pokemon + id: number +}): JSX.Element { + return ( + + {pokemon.name} + + + ) +} diff --git a/examples/tutorial/src/components/PokemonView.module.css b/examples/tutorial/src/components/PokemonView.module.css new file mode 100644 index 00000000..9b8f6f70 --- /dev/null +++ b/examples/tutorial/src/components/PokemonView.module.css @@ -0,0 +1,38 @@ +.back-btn { + background-color: white; + border-radius: 10px; + padding: 7px 15px; + color: black; + text-decoration: none; + border: solid #f0f0f0 1px; + font-size: 20px; +} + +.back-btn:hover { + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; +} + +.pokemon { + display: flex; + justify-content: space-between; + margin-top: 20px; +} + +.name { + font-size: 50px; + font-weight: 700; +} + +.pokemon img { + width: 400px; +} + +.spec { + display: flex; + font-size: 18px; + margin-top: 10px; +} + +.label { + font-weight: 700; +} diff --git a/examples/tutorial/src/components/PokemonView.tsx b/examples/tutorial/src/components/PokemonView.tsx new file mode 100644 index 00000000..0f643aab --- /dev/null +++ b/examples/tutorial/src/components/PokemonView.tsx @@ -0,0 +1,41 @@ +import { Link } from 'tuono' +import styles from './PokemonView.module.css' + +interface Pokemon { + name: string + id: string + weight: number + height: number +} + +export default function PokemonView({ + pokemon, +}: { + pokemon?: Pokemon +}): JSX.Element { + return ( +
+ + Back + + {pokemon?.name && ( +
+
+

{pokemon.name}

+
+
Weight:
+
{pokemon.weight}lbs
+
+
+
Height:
+
{pokemon.height}ft
+
+
+ +
+ )} +
+ ) +} diff --git a/examples/tutorial/src/routes/__root.tsx b/examples/tutorial/src/routes/__root.tsx new file mode 100644 index 00000000..1a39e91f --- /dev/null +++ b/examples/tutorial/src/routes/__root.tsx @@ -0,0 +1,9 @@ +import type { ReactNode } from 'react' + +interface RootRouteProps { + children: ReactNode +} + +export default function RootRoute({ children }: RootRouteProps): JSX.Element { + return
{children}
+} diff --git a/examples/tutorial/src/routes/index.rs b/examples/tutorial/src/routes/index.rs new file mode 100644 index 00000000..25649768 --- /dev/null +++ b/examples/tutorial/src/routes/index.rs @@ -0,0 +1,27 @@ +// src/routes/index.rs +use serde::{Deserialize, Serialize}; +use tuono_lib::{Request, Response}; + +const ALL_POKEMON: &str = "https://pokeapi.co/api/v2/pokemon?limit=151"; + +#[derive(Debug, Serialize, Deserialize)] +struct Pokemons { + results: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Pokemon { + name: String, + url: String, +} + +#[tuono_lib::handler] +async fn get_all_pokemons(_req: Request<'_>, fetch: reqwest::Client) -> Response { + return match fetch.get(ALL_POKEMON).send().await { + Ok(res) => { + let data = res.json::().await.unwrap(); + Response::Props(Box::new(data)) + } + Err(_err) => Response::Props(Box::new(Pokemons { results: vec![] })), + }; +} diff --git a/examples/tutorial/src/routes/index.tsx b/examples/tutorial/src/routes/index.tsx new file mode 100644 index 00000000..25105096 --- /dev/null +++ b/examples/tutorial/src/routes/index.tsx @@ -0,0 +1,46 @@ +// src/routes/index.tsx +import type { TuonoProps } from 'tuono' +import PokemonLink from '../components/PokemonLink' + +interface Pokemon { + name: string +} + +interface IndexProps { + results: Pokemon[] +} + +export default function IndexPage({ + data, +}: TuonoProps): JSX.Element { + if (!data?.results) { + return <> + } + + return ( + <> +
+ + Crates + + + Npm + +
+
+

+ TUONO +

+
+ + +
+
+
    + {data.results.map((pokemon, i) => { + return + })} +
+ + ) +} diff --git a/examples/tutorial/src/routes/pokemons/[pokemon].rs b/examples/tutorial/src/routes/pokemons/[pokemon].rs new file mode 100644 index 00000000..1251e91e --- /dev/null +++ b/examples/tutorial/src/routes/pokemons/[pokemon].rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; +use tuono_lib::{Request, Response}; + +const POKEMON_API: &str = "https://pokeapi.co/api/v2/pokemon"; + +#[derive(Debug, Serialize, Deserialize)] +struct Pokemon { + name: String, + id: u16, + weight: u16, + height: u16, +} + +#[tuono_lib::handler] +async fn get_pokemon(req: Request<'_>, fetch: reqwest::Client) -> Response { + // The param `pokemon` is defined in the route filename [pokemon].rs + let pokemon = req.params.get("pokemon").unwrap(); + + return match fetch.get(format!("{POKEMON_API}/{pokemon}")).send().await { + Ok(res) => { + let data = res.json::().await.unwrap(); + Response::Props(Box::new(data)) + } + Err(_err) => Response::Props(Box::new(Pokemon { + name: "Nope".to_string(), + id: 0, + weight: 0, + height: 0, + })), + }; +} diff --git a/examples/tutorial/src/routes/pokemons/[pokemon].tsx b/examples/tutorial/src/routes/pokemons/[pokemon].tsx new file mode 100644 index 00000000..7cf77395 --- /dev/null +++ b/examples/tutorial/src/routes/pokemons/[pokemon].tsx @@ -0,0 +1,6 @@ +import { TuonoProps } from 'tuono' +import PokemonView from '../../components/PokemonView' + +export default function Pokemon({ data }: TuonoProps): JSX.Element { + return +} diff --git a/examples/tutorial/src/styles/global.css b/examples/tutorial/src/styles/global.css new file mode 100644 index 00000000..84fc0842 --- /dev/null +++ b/examples/tutorial/src/styles/global.css @@ -0,0 +1,109 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); + +@keyframes rotate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: 'Poppins', sans-serif; + font-weight: 400; + font-style: normal; +} + +body { + background: #fbfbfb; +} + +main { + width: 673px; + margin: 20px auto; +} + +.header { + display: flex; + gap: 20px; +} + +.header a { + color: black; + font-size: 18px; + font-weight: 600; + text-decoration: none; + z-index: 2; +} + +.title-wrap { + height: 200px; +} + +.title { + position: absolute; + font-size: 200px; + line-height: 200px; + z-index: 0; + letter-spacing: -2px; + margin-left: -8px; + user-select: none; + pointer-events: none; +} + +.title span { + opacity: 0; +} + +.button { + width: 140px; + height: 30px; + border: solid 3px black; + border-radius: 10px; + color: black; + text-decoration: none; + display: flex; + justify-content: center; + align-items: center; + transition: 0.2s; +} + +.button:hover { + color: white; + background: black; +} + +.logo { + margin-left: 240px; + position: relative; + top: 25px; +} + +.logo img { + position: absolute; +} + +.rust { + animation: rotate 6s ease-in-out infinite; +} + +.react { + top: 33px; + left: 28px; + animation: rotate 6s linear infinite reverse; +} + +.subtitle { + font-size: 30px; + line-height: 30px; + letter-spacing: -1px; +} + +.subtitle-wrap { + display: flex; + justify-content: space-between; +} diff --git a/examples/tutorial/tsconfig.json b/examples/tutorial/tsconfig.json new file mode 100644 index 00000000..a7fc6fbf --- /dev/null +++ b/examples/tutorial/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/tutorial/tsconfig.node.json b/examples/tutorial/tsconfig.node.json new file mode 100644 index 00000000..97ede7ee --- /dev/null +++ b/examples/tutorial/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +}