feat: added server and web base code
This commit is contained in:
10
packages/api/Cargo.toml
Normal file
10
packages/api/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
dioxus = { workspace = true, features = ["fullstack"] }
|
||||
|
||||
[features]
|
||||
server = ["dioxus/server"]
|
||||
13
packages/api/README.md
Normal file
13
packages/api/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# API
|
||||
|
||||
This crate contains all shared fullstack server functions. This is a great place to place any server-only logic you would like to expose in multiple platforms like a method that accesses your database or a method that sends an email.
|
||||
|
||||
This crate will be built twice:
|
||||
1. Once for the server build with the `dioxus/server` feature enabled
|
||||
2. Once for the client build with the client feature disabled
|
||||
|
||||
During the server build, the server functions will be collected and hosted on a public API for the client to call. During the client build, the server functions will be compiled into the client build.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Most server dependencies (like sqlx and tokio) will not compile on client platforms like WASM. To avoid building server dependencies on the client, you should add platform specific dependencies under the `server` feature in the [Cargo.toml](../Cargo.toml) file. More details about managing server only dependencies can be found in the [Dioxus guide](https://dioxuslabs.com/learn/0.7/guides/fullstack/managing_dependencies#adding-server-only-dependencies).
|
||||
8
packages/api/src/lib.rs
Normal file
8
packages/api/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! This crate contains all shared fullstack server functions.
|
||||
use dioxus::prelude::*;
|
||||
|
||||
/// Echo the user input on the server.
|
||||
#[post("/api/echo")]
|
||||
pub async fn echo(input: String) -> Result<String, ServerFnError> {
|
||||
Ok(input)
|
||||
}
|
||||
13
packages/web/Cargo.toml
Normal file
13
packages/web/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "web"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
dioxus = { workspace = true, features = ["router", "fullstack"] }
|
||||
dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
web = ["dioxus/web"]
|
||||
server = ["dioxus/server"]
|
||||
30
packages/web/README.md
Normal file
30
packages/web/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Development
|
||||
|
||||
The web crate defines the entrypoint for the web app along with any assets, components and dependencies that are specific to web builds. The web crate starts out something like this:
|
||||
|
||||
```
|
||||
web/
|
||||
├─ assets/ # Assets used by the web app - Any platform specific assets should go in this folder
|
||||
├─ src/
|
||||
│ ├─ main.rs # The entrypoint for the web app.It also defines the routes for the web platform
|
||||
│ ├─ views/ # The views each route will render in the web version of the app
|
||||
│ │ ├─ mod.rs # Defines the module for the views route and re-exports the components for each route
|
||||
│ │ ├─ blog.rs # The component that will render at the /blog/:id route
|
||||
│ │ ├─ home.rs # The component that will render at the / route
|
||||
├─ Cargo.toml # The web crate's Cargo.toml - This should include all web specific dependencies
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
Since you have fullstack enabled, the web crate will be built two times:
|
||||
1. Once for the server build with the `server` feature enabled
|
||||
2. Once for the client build with the `web` feature enabled
|
||||
|
||||
You should make all web specific dependencies optional and only enabled in the `web` feature. This will ensure that the server builds don't pull in web specific dependencies which cuts down on build times significantly.
|
||||
|
||||
### Serving Your Web App
|
||||
|
||||
You can start your web app with the following command:
|
||||
|
||||
```bash
|
||||
dx serve
|
||||
```
|
||||
8
packages/web/assets/blog.css
Normal file
8
packages/web/assets/blog.css
Normal file
@@ -0,0 +1,8 @@
|
||||
#blog {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
#blog a {
|
||||
color: #ffffff;
|
||||
margin-top: 50px;
|
||||
}
|
||||
87
packages/web/assets/dx-components-theme.css
Normal file
87
packages/web/assets/dx-components-theme.css
Normal file
@@ -0,0 +1,87 @@
|
||||
/* This file contains the global styles for the styled dioxus components. You only
|
||||
* need to import this file once in your project root.
|
||||
*/
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
|
||||
|
||||
body {
|
||||
color: var(--secondary-color-4);
|
||||
font-family: Inter, sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
--dark: initial;
|
||||
--light: ;
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
--dark: ;
|
||||
--light: initial;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--dark: initial;
|
||||
--light: ;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--dark: ;
|
||||
--light: initial;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Primary colors */
|
||||
--primary-color: var(--dark, #000) var(--light, #fff);
|
||||
--primary-color-1: var(--dark, #0e0e0e) var(--light, #fbfbfb);
|
||||
--primary-color-2: var(--dark, #0a0a0a) var(--light, #fff);
|
||||
--primary-color-3: var(--dark, #141313) var(--light, #f8f8f8);
|
||||
--primary-color-4: var(--dark, #1a1a1a) var(--light, #f8f8f8);
|
||||
--primary-color-5: var(--dark, #262626) var(--light, #f5f5f5);
|
||||
--primary-color-6: var(--dark, #232323) var(--light, #e5e5e5);
|
||||
--primary-color-7: var(--dark, #3e3e3e) var(--light, #b0b0b0);
|
||||
|
||||
/* Secondary colors */
|
||||
--secondary-color: var(--dark, #fff) var(--light, #000);
|
||||
--secondary-color-1: var(--dark, #fafafa) var(--light, #000);
|
||||
--secondary-color-2: var(--dark, #e6e6e6) var(--light, #0d0d0d);
|
||||
--secondary-color-3: var(--dark, #dcdcdc) var(--light, #2b2b2b);
|
||||
--secondary-color-4: var(--dark, #d4d4d4) var(--light, #111);
|
||||
--secondary-color-5: var(--dark, #a1a1a1) var(--light, #848484);
|
||||
--secondary-color-6: var(--dark, #5d5d5d) var(--light, #d0d0d0);
|
||||
|
||||
/* Highlight colors */
|
||||
--focused-border-color: var(--dark, #2b7fff) var(--light, #2b7fff);
|
||||
--primary-success-color: var(--dark, #02271c) var(--light, #ecfdf5);
|
||||
--secondary-success-color: var(--dark, #b6fae3) var(--light, #10b981);
|
||||
--primary-warning-color: var(--dark, #342203) var(--light, #fffbeb);
|
||||
--secondary-warning-color: var(--dark, #feeac7) var(--light, #f59e0b);
|
||||
--primary-error-color: var(--dark, #a22e2e) var(--light, #dc2626);
|
||||
--secondary-error-color: var(--dark, #9b1c1c) var(--light, #ef4444);
|
||||
--contrast-error-color: var(--dark, var(--secondary-color-3)) var(--light, var(--primary-color));
|
||||
--primary-info-color: var(--dark, var(--primary-color-5)) var(--light, var(--primary-color));
|
||||
--secondary-info-color: var(--dark, var(--primary-color-7)) var(--light, var(--secondary-color-3));
|
||||
}
|
||||
|
||||
/* Modern browsers with `scrollbar-*` support */
|
||||
@supports (scrollbar-width: auto) {
|
||||
:not(:hover) {
|
||||
scrollbar-color: rgb(0 0 0 / 0%) rgb(0 0 0 / 0%);
|
||||
}
|
||||
|
||||
:hover {
|
||||
scrollbar-color: var(--secondary-color-2) rgb(0 0 0 / 0%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Legacy browsers with `::-webkit-scrollbar-*` support */
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
:root::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
BIN
packages/web/assets/favicon.ico
Normal file
BIN
packages/web/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
6
packages/web/assets/main.css
Normal file
6
packages/web/assets/main.css
Normal file
@@ -0,0 +1,6 @@
|
||||
body {
|
||||
background-color: #0f1116;
|
||||
color: #ffffff;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
2
packages/web/src/components/mod.rs
Normal file
2
packages/web/src/components/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// AUTOGENERATED Components module
|
||||
pub mod toast;
|
||||
15
packages/web/src/components/toast/component.rs
Normal file
15
packages/web/src/components/toast/component.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_primitives::toast::{self, ToastProviderProps};
|
||||
|
||||
#[component]
|
||||
pub fn ToastProvider(props: ToastProviderProps) -> Element {
|
||||
rsx! {
|
||||
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||
toast::ToastProvider {
|
||||
default_duration: props.default_duration,
|
||||
max_toasts: props.max_toasts,
|
||||
render_toast: props.render_toast,
|
||||
{props.children}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
packages/web/src/components/toast/mod.rs
Normal file
2
packages/web/src/components/toast/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod component;
|
||||
pub use component::*;
|
||||
185
packages/web/src/components/toast/style.css
Normal file
185
packages/web/src/components/toast/style.css
Normal file
@@ -0,0 +1,185 @@
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.toast-list {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.toast-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.toast {
|
||||
z-index: calc(var(--toast-count) - var(--toast-index));
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 18rem;
|
||||
height: 4rem;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--light, var(--primary-color-6))
|
||||
var(--dark, var(--primary-color-7));
|
||||
border-radius: 0.5rem;
|
||||
margin-top: -4rem;
|
||||
box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
|
||||
filter: var(--light, none)
|
||||
var(
|
||||
--dark,
|
||||
brightness(calc(0.5 + 0.5 * (1 - ((var(--toast-index) + 1) / 4))))
|
||||
);
|
||||
opacity: calc(1 - var(--toast-hidden));
|
||||
transform: scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
transition: transform 0.2s ease, margin-top 0.2s ease, opacity 0.2s ease;
|
||||
|
||||
--toast-hidden: calc(min(max(0, var(--toast-index) - 2), 1));
|
||||
}
|
||||
|
||||
.toast-container:not(:hover, :focus-within)
|
||||
.toast[data-toast-even]:not([data-top]) {
|
||||
animation: slide-up-even 0.2s ease-out;
|
||||
}
|
||||
|
||||
.toast-container:not(:hover, :focus-within)
|
||||
.toast[data-toast-odd]:not([data-top]) {
|
||||
animation: slide-up-odd 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slide-up-even {
|
||||
from {
|
||||
transform: translateY(0.5rem)
|
||||
scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0)
|
||||
scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-up-odd {
|
||||
from {
|
||||
transform: translateY(0.5rem)
|
||||
scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0)
|
||||
scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.toast[data-top] {
|
||||
animation: slide-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
.toast-container:hover .toast[data-top],
|
||||
.toast-container:focus-within .toast[data-top] {
|
||||
animation: slide-in 0 ease-out;
|
||||
}
|
||||
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(100%)
|
||||
scale(
|
||||
calc(110% - var(--toast-index) * 5%),
|
||||
calc(110% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0)
|
||||
scale(
|
||||
calc(100% - var(--toast-index) * 5%),
|
||||
calc(100% - var(--toast-index) * 2%)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.toast-container:hover .toast,
|
||||
.toast-container:focus-within .toast {
|
||||
margin-top: var(--toast-padding);
|
||||
filter: brightness(1);
|
||||
opacity: 1;
|
||||
transform: scale(calc(100%));
|
||||
}
|
||||
|
||||
.toast[data-type="success"] {
|
||||
background-color: var(--primary-success-color);
|
||||
color: var(--secondary-success-color);
|
||||
}
|
||||
|
||||
.toast[data-type="error"] {
|
||||
background-color: var(--primary-error-color);
|
||||
color: var(--contrast-error-color);
|
||||
}
|
||||
|
||||
.toast[data-type="warning"] {
|
||||
background-color: var(--primary-warning-color);
|
||||
color: var(--secondary-warning-color);
|
||||
}
|
||||
|
||||
.toast[data-type="info"] {
|
||||
background-color: var(--primary-info-color);
|
||||
color: var(--secondary-info-color);
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
margin-bottom: 4px;
|
||||
color: var(--secondary-color-4);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toast-description {
|
||||
color: var(--secondary-color-3);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
align-self: flex-start;
|
||||
padding: 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
background: none;
|
||||
color: var(--secondary-color-3);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.toast-close:hover {
|
||||
color: var(--secondary-color-1);
|
||||
}
|
||||
45
packages/web/src/main.rs
Normal file
45
packages/web/src/main.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use views::{Blog, Home};
|
||||
|
||||
mod views;
|
||||
|
||||
#[derive(Debug, Clone, Routable, PartialEq)]
|
||||
#[rustfmt::skip]
|
||||
enum Route {
|
||||
#[layout(WebNavbar)]
|
||||
#[route("/")]
|
||||
Home {},
|
||||
#[route("/blog/:id")]
|
||||
Blog { id: i32 },
|
||||
}
|
||||
|
||||
const FAVICON: Asset = asset!("/assets/favicon.ico");
|
||||
const MAIN_CSS: Asset = asset!("/assets/main.css");
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
// Build cool things ✌️
|
||||
|
||||
rsx! {
|
||||
// Global app resources
|
||||
document::Link { rel: "icon", href: FAVICON }
|
||||
document::Link { rel: "stylesheet", href: MAIN_CSS }
|
||||
|
||||
Router::<Route> {}
|
||||
}
|
||||
}
|
||||
|
||||
/// A web-specific Router around the shared `Navbar` component
|
||||
/// which allows us to use the web-specific `Route` enum.
|
||||
#[component]
|
||||
fn WebNavbar() -> Element {
|
||||
rsx! {
|
||||
|
||||
Outlet::<Route> {}
|
||||
}
|
||||
}
|
||||
30
packages/web/src/views/blog.rs
Normal file
30
packages/web/src/views/blog.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::Route;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
const BLOG_CSS: Asset = asset!("/assets/blog.css");
|
||||
|
||||
#[component]
|
||||
pub fn Blog(id: i32) -> Element {
|
||||
rsx! {
|
||||
document::Link { rel: "stylesheet", href: BLOG_CSS}
|
||||
|
||||
div {
|
||||
id: "blog",
|
||||
|
||||
// Content
|
||||
h1 { "This is blog #{id}!" }
|
||||
p { "In blog #{id}, we show how the Dioxus router works and how URL parameters can be passed as props to our route components." }
|
||||
|
||||
// Navigation links
|
||||
Link {
|
||||
to: Route::Blog { id: id - 1 },
|
||||
"Previous"
|
||||
}
|
||||
span { " <---> " }
|
||||
Link {
|
||||
to: Route::Blog { id: id + 1 },
|
||||
"Next"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/web/src/views/home.rs
Normal file
6
packages/web/src/views/home.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Home() -> Element {
|
||||
rsx! {}
|
||||
}
|
||||
5
packages/web/src/views/mod.rs
Normal file
5
packages/web/src/views/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod home;
|
||||
pub use home::Home;
|
||||
|
||||
mod blog;
|
||||
pub use blog::Blog;
|
||||
Reference in New Issue
Block a user