commit 1b1cd2e06479691045a2ed8fbbd27d063ddf3435 Author: aOK Date: Mon Sep 25 22:51:18 2023 +0300 first commit diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b0014df --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[build] +# `leptonic` depends on some `leptos-use` functions requiring this opt-in. This may change in the future. +rustflags = ["--cfg=web_sys_unstable_apis"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a30ece6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +/generated/** +!/generated/.gitkeep +/dist +/Cargo.lock diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..57060dc --- /dev/null +++ b/Caddyfile @@ -0,0 +1,55 @@ +{ + # https://caddyserver.com/docs/caddyfile/options + email "privat@lukas-potthast.de" + http_port {$HTTP_PORT} + https_port {$HTTPS_PORT} + storage consul { + address "{$CONSUL_HTTP_ADDR}" + token "consul-access-token" + timeout 10 + prefix "" + value_prefix "caddytlsprefix" + aes_key "consultls-1234567890-caddytls-32" + tls_enabled "false" + tls_insecure "true" + } +} + +{$ADDRESS_PREFIX}:{$HTTPS_PORT} { + tls /etc/certs/letsencrypt/www.leptonic.dev-cert.pem /etc/certs/letsencrypt/www.leptonic.dev-key.pem + + # redir {$URL_PREFIX} {$URL_PREFIX}/ + + handle_path {$URL_PREFIX}* { + root * /srv + try_files {path} /index.html + encode { + gzip + match { + header Content-Type text/* + header Content-Type application/json* + header Content-Type application/js* + header Content-Type application/javascript* + header Content-Type application/wasm* + header Content-Type application/xhtml+xml* + header Content-Type application/atom+xml* + header Content-Type application/rss+xml* + header Content-Type image/svg+xml* + header Content-Type image/* + } + } + # header Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header index.html Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header service-worker.js Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header manifest.json Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header robots.txt Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header js/* Cache-Control max-age=0,no-cache,no-store,must-revalidate + # header * Cache-Control max-age=31536000 + header Cache-Control max-age=0,no-cache,no-store,must-revalidate + file_server + } + + handle { + respond "Not found" 404 + } +} \ No newline at end of file diff --git a/Caddyfile.local b/Caddyfile.local new file mode 100644 index 0000000..ea871b7 --- /dev/null +++ b/Caddyfile.local @@ -0,0 +1,35 @@ +{ + # https://caddyserver.com/docs/caddyfile/options + email "privat@lukas-potthast.de" + http_port {$HTTP_PORT} + auto_https disable_redirects +} + +{$ADDRESS_PREFIX}:{$HTTP_PORT} { + + handle_path {$URL_PREFIX}* { + root * /srv + try_files {path} /index.html + encode { + gzip + match { + header Content-Type text/* + header Content-Type application/json* + header Content-Type application/js* + header Content-Type application/javascript* + header Content-Type application/wasm* + header Content-Type application/xhtml+xml* + header Content-Type application/atom+xml* + header Content-Type application/rss+xml* + header Content-Type image/svg+xml* + header Content-Type image/* + } + } + header Cache-Control max-age=0,no-cache,no-store,must-revalidate + file_server + } + + handle { + respond "Not found" 404 + } +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..56a86cb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "book-ui" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[profile.release] +opt-level = "z" +lto = "fat" +debug = 0 +strip = true +codegen-units = 1 + +[dependencies] +console_error_panic_hook = "0.1.7" +indoc = "2.0.1" +leptonic = { git = "https://github.com/lpotthast/leptonic", default-features = false, features = ["csr"] } +leptos = { version = "0.5.0-rc1", features = ["csr"] } +leptos-use = { git = "https://github.com/Synphonyte/leptos-use" } +leptos_icons = { version = "0.0.16-beta", features = [ + "BsGithub", + "BsSearch", + "BsList", + "BsThreeDots", + "BsFolder", + "BsFolderFill", + "BsBook", + "BsColumnsGap", + "BsToggles", + "BsChatSquare", + "BsCircleSquare", + "BsArrowsMove", + "BsVolumeDownFill", + "BsVolumeUpFill", + "BsBell", + "BsPower", +] } +leptos_meta = { version = "0.5.0-rc1", features = ["csr"] } +leptos_router = { version = "0.5.0-rc1", features = ["csr"] } +ordered-float = "3.9.0" +serde = "1.0.171" +serde-wasm-bindgen = "0.5.0" +serde_json = "1.0.103" +strum = { version = "0.25.0", features = ["derive"] } +time = { version = "0.3.23", features = ["wasm-bindgen"] } +tracing = "0.1.37" +tracing-subscriber = "0.3.17" +tracing-wasm = "0.2.1" +uuid = { version = "1.4.0", features = ["js", "v4", "v7", "serde"] } +wasm-bindgen = "0.2.87" + +[build-dependencies] +leptonic-theme = "0.2.0" +leptos-tiptap-build = "0.2.3" + +[workspace] +members = ["src-tauri"] \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cf1c9e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM registry.gitlab.com/lukaspotthast/docker-images/caddy-consul:latest + +COPY {{LEPTONIC_DEV_CERT_PEM_FILE}} /etc/certs/letsencrypt/www.leptonic.dev-cert.pem +COPY {{LEPTONIC_DEV_KEY_PEM_FILE}} /etc/certs/letsencrypt/www.leptonic.dev-key.pem + +COPY Caddyfile /etc/caddy/Caddyfile + +COPY dist/ /srv diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 0000000..e27ba64 --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,4 @@ +FROM caddy:latest + +COPY Caddyfile.local /etc/caddy/Caddyfile +COPY dist/ /srv diff --git a/README.md b/README.md new file mode 100644 index 0000000..d10e7b4 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Leptonic book + + +to run it with [trunk](https://trunkrs.dev/) use `trunk serve --open` in the root of this example directory. diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 0000000..040642d --- /dev/null +++ b/Trunk.toml @@ -0,0 +1,14 @@ +[build] +target = "./index.html" + +[watch] +ignore = [ + "./src-tauri", + "./generated", + ] + +[serve] +address = "0.0.0.0" +port = 1420 +open = false +ws_protocol = "ws" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..e7e892b --- /dev/null +++ b/build.rs @@ -0,0 +1,28 @@ +use std::io::Write; + +pub fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=Cargo.lock"); + + let root: std::path::PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); + let generated_dir = root.join("generated"); + let js_dir = generated_dir.join("js"); + + leptonic_theme::generate(generated_dir.join("leptonic")); + println!("cargo:warning=theme written"); + + std::fs::create_dir_all(js_dir.clone()).unwrap(); + println!("cargo:warning=js dir created"); + + std::fs::File::create(js_dir.join("tiptap-bundle.min.js")) + .unwrap() + .write_all(leptos_tiptap_build::TIPTAP_BUNDLE_MIN_JS.as_bytes()) + .unwrap(); + println!("cargo:warning=tiptap-bundle.min.js written"); + + std::fs::File::create(js_dir.join("tiptap.js")) + .unwrap() + .write_all(leptos_tiptap_build::TIPTAP_JS.as_bytes()) + .unwrap(); + println!("cargo:warning=tiptap.js written"); +} diff --git a/deployment/leptonic-book.jobspec.hcl b/deployment/leptonic-book.jobspec.hcl new file mode 100644 index 0000000..fc89d8a --- /dev/null +++ b/deployment/leptonic-book.jobspec.hcl @@ -0,0 +1,63 @@ +job "leptonic-book" { + datacenters = ["dc1"] + type = "service" + + group "leptonic-book" { + count = 1 + + network { + port "http" {} + port "https" {} + } + + restart { + attempts = 10 + interval = "5m" + delay = "25s" + mode = "delay" + } + + task "leptonic-book" { + driver = "docker" + + env { + DOMAIN = "leptonic.dev" + CONSUL_HTTP_ADDR = "172.17.0.1:8500" + HTTP_PORT = "${NOMAD_PORT_http}" + HTTPS_PORT = "${NOMAD_PORT_https}" + URL_PREFIX = "/" + } + + config { + image = "registry.gitlab.com/lukaspotthast/leptonic/leptonic-book:{{TAG}}" + auth { + username = "{{REGISTRY_USERNAME}}" + password = "{{REGISTRY_TOKEN}}" + } + ports = ["http", "https"] + } + + resources { + cpu = 256 + memory = 128 + } + + service { + name = "leptonic-book" + tags = [ + # Redirect HTTP to HTTPS + "urlprefix-leptonic.dev:80/ redirect=301,https://leptonic.dev/", + # Serve the app using delayed SSL termination + "urlprefix-leptonic.dev/ proto=https tlsskipverify=true", + ] + port = "https" + + check { + type = "tcp" + interval = "10s" + timeout = "2s" + } + } + } + } +} diff --git a/extra.txt b/extra.txt new file mode 100644 index 0000000..72561f8 --- /dev/null +++ b/extra.txt @@ -0,0 +1,17 @@ +cargo leptos new --git https://github.com/leptos-rs/start + + +Tauri CLI │ Run `cargo install tauri-cli --version '^2.0.0-alpha'` +cargo create-tauri-app nigiginc --alpha +cd nigiginc +cargo tauri android init +cargo tauri ios init + +For Desktop development, run: +cargo tauri dev + +For Android development, run: +cargo tauri android dev + +For iOS development, run: +cargo tauri ios dev \ No newline at end of file diff --git a/generated/.gitkeep b/generated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html new file mode 100644 index 0000000..1e13bb1 --- /dev/null +++ b/index.html @@ -0,0 +1,29 @@ + + + + + + + + + + + Leptonic + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/icon/ferris-does-not-compute.svg b/res/icon/ferris-does-not-compute.svg new file mode 100644 index 0000000..57764ee --- /dev/null +++ b/res/icon/ferris-does-not-compute.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icon/ferris-does-not-compute_transparent.svg b/res/icon/ferris-does-not-compute_transparent.svg new file mode 100644 index 0000000..42bd1bc --- /dev/null +++ b/res/icon/ferris-does-not-compute_transparent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icon/ferris-gesture.svg b/res/icon/ferris-gesture.svg new file mode 100644 index 0000000..d96dd2f --- /dev/null +++ b/res/icon/ferris-gesture.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icon/ferris-panic.svg b/res/icon/ferris-panic.svg new file mode 100644 index 0000000..d1ad341 --- /dev/null +++ b/res/icon/ferris-panic.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icon/ferris-panic_transparent.svg b/res/icon/ferris-panic_transparent.svg new file mode 100644 index 0000000..253573a --- /dev/null +++ b/res/icon/ferris-panic_transparent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icon/ferris-unsafe.svg b/res/icon/ferris-unsafe.svg new file mode 100644 index 0000000..1f6c0bc --- /dev/null +++ b/res/icon/ferris-unsafe.svg @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icon/ferris.svg b/res/icon/ferris.svg new file mode 100644 index 0000000..900007d --- /dev/null +++ b/res/icon/ferris.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icon/leptonic_x1024.png b/res/icon/leptonic_x1024.png new file mode 100644 index 0000000..6f8cc5e Binary files /dev/null and b/res/icon/leptonic_x1024.png differ diff --git a/res/icon/leptonic_x128.png b/res/icon/leptonic_x128.png new file mode 100644 index 0000000..d5c8084 Binary files /dev/null and b/res/icon/leptonic_x128.png differ diff --git a/res/icon/leptonic_x256.png b/res/icon/leptonic_x256.png new file mode 100644 index 0000000..b36cf83 Binary files /dev/null and b/res/icon/leptonic_x256.png differ diff --git a/res/icon/leptonic_x512.png b/res/icon/leptonic_x512.png new file mode 100644 index 0000000..b9460f5 Binary files /dev/null and b/res/icon/leptonic_x512.png differ diff --git a/res/icon/leptonic_x64.png b/res/icon/leptonic_x64.png new file mode 100644 index 0000000..2c55e5c Binary files /dev/null and b/res/icon/leptonic_x64.png differ diff --git a/res/icon/maskable_icon.png b/res/icon/maskable_icon.png new file mode 100644 index 0000000..320fa97 Binary files /dev/null and b/res/icon/maskable_icon.png differ diff --git a/res/icon/maskable_icon_x128.png b/res/icon/maskable_icon_x128.png new file mode 100644 index 0000000..ec6af29 Binary files /dev/null and b/res/icon/maskable_icon_x128.png differ diff --git a/res/icon/maskable_icon_x192.png b/res/icon/maskable_icon_x192.png new file mode 100644 index 0000000..a38db7e Binary files /dev/null and b/res/icon/maskable_icon_x192.png differ diff --git a/res/icon/maskable_icon_x384.png b/res/icon/maskable_icon_x384.png new file mode 100644 index 0000000..70d52c0 Binary files /dev/null and b/res/icon/maskable_icon_x384.png differ diff --git a/res/icon/maskable_icon_x48.png b/res/icon/maskable_icon_x48.png new file mode 100644 index 0000000..0c9a625 Binary files /dev/null and b/res/icon/maskable_icon_x48.png differ diff --git a/res/icon/maskable_icon_x512.png b/res/icon/maskable_icon_x512.png new file mode 100644 index 0000000..e5ccaf1 Binary files /dev/null and b/res/icon/maskable_icon_x512.png differ diff --git a/res/icon/maskable_icon_x72.png b/res/icon/maskable_icon_x72.png new file mode 100644 index 0000000..62f422c Binary files /dev/null and b/res/icon/maskable_icon_x72.png differ diff --git a/res/icon/maskable_icon_x96.png b/res/icon/maskable_icon_x96.png new file mode 100644 index 0000000..dfa9e28 Binary files /dev/null and b/res/icon/maskable_icon_x96.png differ diff --git a/res/leptonic.svg b/res/leptonic.svg new file mode 100644 index 0000000..663ae4d --- /dev/null +++ b/res/leptonic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..c2a49f4 --- /dev/null +++ b/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/scss/style.scss b/scss/style.scss new file mode 100644 index 0000000..3ae23c4 --- /dev/null +++ b/scss/style.scss @@ -0,0 +1,286 @@ +* { + font-family: Roboto, sans-serif; +} + +html { + font-size: 16px; + height: 100%; +} + +body { + margin: 0; + min-height: var(--leptonic-vh, 100vh); + height: 100%; + display: flex; + flex-direction: column; +} + +@import "../generated/leptonic/leptonic-themes"; + +[data-theme="light"] { + --brand-color: #e66956; + + --drawer-background-color: none; + --drawer-box-shadow: none; + + --book-menu-background-color: var(--box-background-color); + --book-menu-header-color: #3e3434; + --book-menu-item-color: #3e3434; + --book-menu-item-active-color: var(--std-text-bright); + --book-menu-item-hover-color: var(--std-text-bright); +} + +[data-theme="dark"] { + --brand-color: #e66956; + + --drawer-background-color: none; + --drawer-box-shadow: none; + + --book-menu-background-color: var(--box-background-color); + --book-menu-header-color: var(--std-text-bright); + --book-menu-item-color: var(--std-text-bright); + --book-menu-item-active-color: var(--std-text-bright); + --book-menu-item-hover-color: var(--std-text-bright); +} + +#app-bar { + display: flex; + justify-content: center; + + #app-bar-content { + display: flex; + justify-content: space-between; + width: 100%; + max-width: 1450px; + + #mobile-menu-trigger { + font-size: 1.5em; + padding: 0.7em; + cursor: pointer; + } + } + + #logo { + display: inline-flex; + height: 2.5em; + width: 2.5em; + margin-left: 0.5em; + } + + #github-icon { + font-size: 1.5em; + } +} + +#content { + display: flex; + flex-direction: row; + width: 100%; + padding: 0; + overflow-y: auto; +} + +#main-drawer { + overflow: none; + + position: absolute; + right: 0; + top: var(--app-bar-height); + bottom: 0; + left: 0; + + width: 100%; + + background-color: var(--book-menu-background-color); + padding: 1em 0; +} + +#doc-layout { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; + max-width: 1400px; + margin: auto; + + #doc-drawer { + overflow: auto; + width: auto; + min-width: 15em; + padding: 1em 0; + margin: 0; + + &.mobile { + width: 100%; + min-width: 100%; + overflow: none; + position: absolute; + right: 0; + top: var(--app-bar-height); + bottom: 0; + left: 0; + background-color: var(--book-menu-background-color); + padding: 1em 0; + } + + .drawer-section { + display: inline-block; + width: 100%; + margin: 1em 0; + padding-left: 3.5em; + font-size: 0.9em; + + .section-header { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + color: var(--book-menu-header-color); + font-weight: 900; + margin-bottom: 0.25em; + font-size: 1.1em; + } + + .item { + width: 100%; + + a { + display: flex; + justify-content: flex-start; + align-items: center; + color: var(--book-menu-item-color); + font-weight: 400; + border-radius: 0.4em; + padding: 0.25em 0.75em; + margin: 0.075em 0.75em; + margin-left: 1.5em; + cursor: pointer; + user-select: none; + } + + a[aria-current] { + background-color: var(--brand-color); + color: var(--book-menu-item-active-color); + } + + a:hover { + background-color: var(--brand-color); + color: var(--book-menu-item-hover-color); + } + } + } + + @media only screen and (max-width: 800px) { + .menu { + display: block; + column-count: 2; + column-gap: 0; + column-fill: balance; + + } + + .drawer-section { + padding-left: 1.5em; + } + } + } + + #doc-content { + display: block; + height: 100%; + width: 100%; + padding: 0 1.5em; + padding-bottom: 6em; + overflow-y: auto; + } +} + +.search-link { + width: 100%; + height: 100%; + + a { + width: 100%; + height: 100%; + display: flex; + justify-content: flex-start; + padding: 1em; + } +} + +#welcome-page { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 1em; + + #big-logo { + height: 10em; + width: 10em; + margin: 3.5em 0; + } + + #slogan { + font-family: monospace; + font-size: 4em; + letter-spacing: 0.2em; + margin-top: 0em; + } + + #sub-slogan { + font-size: 1.75em; + max-width: 75%; + text-align: center; + } + + @media only screen and (max-width: 600px) { + #big-logo { + margin: 2.5em 0; + } + + #slogan { + font-size: 3em; + } + + #sub-slogan { + font-size: 1.5em; + } + } +} + +.err-404 { + margin: auto; + max-width: 60em; + margin-top: 2em; + width: 100%; + + .info { + #error { + font-size: 6em; + font-weight: bold; + margin: 0; + margin-bottom: 0.1em; + } + #whoops { + font-size: 3em; + font-weight: 500; + margin: 0; + margin-left: 0.5rem; + margin-right: 0.5rem; + margin-bottom: 1.5em; + text-align: center; + } + #back-btn { + font-size: 1.5em; + } + } + + #ferris { + height: 14em; + width: auto; + margin-top: 2em; + } +} diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml new file mode 100644 index 0000000..7eaca81 --- /dev/null +++ b/src-tauri/.cargo/config.toml @@ -0,0 +1,4 @@ +[build] +target = 'x86_64-apple-darwin' + +[target] diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..f4dfb82 --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..d90ffed --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "book" +version = "0.0.0" +description = "A Tauri App" +authors = ["you"] +license = "" +repository = "" +edition = "2021" + +[lib] +name = "book_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "2.0.0-alpha", features = [] } + +[dependencies] +tauri = { version = "2.0.0-alpha", features = [] } +tauri-plugin-window = "2.0.0-alpha" +tauri-plugin-shell = "2.0.0-alpha" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem +# DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/gen/android/.editorconfig b/src-tauri/gen/android/.editorconfig new file mode 100644 index 0000000..ebe51d3 --- /dev/null +++ b/src-tauri/gen/android/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/src-tauri/gen/android/.gitignore b/src-tauri/gen/android/.gitignore new file mode 100644 index 0000000..b248203 --- /dev/null +++ b/src-tauri/gen/android/.gitignore @@ -0,0 +1,19 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +build +/captures +.externalNativeBuild +.cxx +local.properties +key.properties + +/.tauri +/tauri.settings.gradle \ No newline at end of file diff --git a/src-tauri/gen/android/app/.gitignore b/src-tauri/gen/android/app/.gitignore new file mode 100644 index 0000000..eddfaf3 --- /dev/null +++ b/src-tauri/gen/android/app/.gitignore @@ -0,0 +1,5 @@ +/src/main/java/com/book/book/generated +/src/main/jniLibs/**/*.so +/src/main/assets/tauri.conf.json +/tauri.build.gradle.kts +/proguard-tauri.pro \ No newline at end of file diff --git a/src-tauri/gen/android/app/build.gradle.kts b/src-tauri/gen/android/app/build.gradle.kts new file mode 100644 index 0000000..8b9b008 --- /dev/null +++ b/src-tauri/gen/android/app/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rust") +} + +android { + compileSdk = 33 + namespace = "com.book.book" + defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "false" + applicationId = "com.book.book" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + } + buildTypes { + getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") + jniLibs.keepDebugSymbols.add("*/x86/*.so") + jniLibs.keepDebugSymbols.add("*/x86_64/*.so") + } + } + getByName("release") { + isMinifyEnabled = true + proguardFiles( + *fileTree(".") { include("**/*.pro") } + .plus(getDefaultProguardFile("proguard-android-optimize.txt")) + .toList().toTypedArray() + ) + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +rust { + rootDirRel = "../../../" +} + +dependencies { + implementation("androidx.webkit:webkit:1.6.1") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.8.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") +} + +apply(from = "tauri.build.gradle.kts") diff --git a/src-tauri/gen/android/app/proguard-rules.pro b/src-tauri/gen/android/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/src-tauri/gen/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e1866f5 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + diff --git a/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt b/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt new file mode 100644 index 0000000..d3f547f --- /dev/null +++ b/src-tauri/gen/android/app/src/main/java/com/book/book/MainActivity.kt @@ -0,0 +1,3 @@ +package com.book.book + +class MainActivity : TauriActivity() diff --git a/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..4fc2444 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..28f1aa1 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..85d0c88 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..28f1aa1 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..73e48db Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..13dd214 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..73e48db Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..1d98044 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a888b33 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..1d98044 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..0818324 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a2a838e Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..0818324 Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b18bceb Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..3f8a57f Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b18bceb Binary files /dev/null and b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/gen/android/app/src/main/res/values-night/themes.xml b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..ec25883 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src-tauri/gen/android/app/src/main/res/values/colors.xml b/src-tauri/gen/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/values/strings.xml b/src-tauri/gen/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..e3d8b10 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + book + book + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/res/values/themes.xml b/src-tauri/gen/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..ec25883 --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/values/themes.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..782d63b --- /dev/null +++ b/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src-tauri/gen/android/build.gradle.kts b/src-tauri/gen/android/build.gradle.kts new file mode 100644 index 0000000..5ce764e --- /dev/null +++ b/src-tauri/gen/android/build.gradle.kts @@ -0,0 +1,22 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:8.0.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +tasks.register("clean").configure { + delete("build") +} + diff --git a/src-tauri/gen/android/buildSrc/build.gradle.kts b/src-tauri/gen/android/buildSrc/build.gradle.kts new file mode 100644 index 0000000..099feff --- /dev/null +++ b/src-tauri/gen/android/buildSrc/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + `kotlin-dsl` +} + +gradlePlugin { + plugins { + create("pluginsForCoolKids") { + id = "rust" + implementationClass = "RustPlugin" + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + compileOnly(gradleApi()) + implementation("com.android.tools.build:gradle:8.0.0") +} + diff --git a/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt new file mode 100644 index 0000000..13e84b7 --- /dev/null +++ b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/BuildTask.kt @@ -0,0 +1,52 @@ +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @Input + var rootDirRel: String? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun assemble() { + val executable = """/Users/aok/.cargo/bin/cargo-tauri"""; + try { + runTauriCli(executable) + } catch (e: Exception) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + runTauriCli("$executable.cmd") + } else { + throw e; + } + } + } + + fun runTauriCli(executable: String) { + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") + val args = listOf("tauri", "android", "android-studio-script"); + + project.exec { + workingDir(File(project.projectDir, rootDirRel)) + executable(executable) + args(args) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args(listOf("--target", target)) + }.assertNormalExitValue() + } +} \ No newline at end of file diff --git a/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt new file mode 100644 index 0000000..4aa7fca --- /dev/null +++ b/src-tauri/gen/android/buildSrc/src/main/java/com/book/book/kotlin/RustPlugin.kt @@ -0,0 +1,85 @@ +import com.android.build.api.dsl.ApplicationExtension +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.get + +const val TASK_GROUP = "rust" + +open class Config { + lateinit var rootDirRel: String +} + +open class RustPlugin : Plugin { + private lateinit var config: Config + + override fun apply(project: Project) = with(project) { + config = extensions.create("rust", Config::class.java) + + val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64"); + val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList + + val defaultArchList = listOf("arm64", "arm", "x86", "x86_64"); + val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList + + val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64") + + extensions.configure { + @Suppress("UnstableApiUsage") + flavorDimensions.add("abi") + productFlavors { + create("universal") { + dimension = "abi" + ndk { + abiFilters += abiList + } + } + defaultArchList.forEachIndexed { index, arch -> + create(arch) { + dimension = "abi" + ndk { + abiFilters.add(defaultAbiList[index]) + } + } + } + } + } + + afterEvaluate { + for (profile in listOf("debug", "release")) { + val profileCapitalized = profile.replaceFirstChar { it.uppercase() } + val buildTask = tasks.maybeCreate( + "rustBuildUniversal$profileCapitalized", + DefaultTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for all targets" + } + + tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask) + + for (targetPair in targetsList.withIndex()) { + val targetName = targetPair.value + val targetArch = archList[targetPair.index] + val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() } + val targetBuildTask = project.tasks.maybeCreate( + "rustBuild$targetArchCapitalized$profileCapitalized", + BuildTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for $targetArch" + rootDirRel = config.rootDirRel + target = targetName + release = profile == "release" + } + + buildTask.dependsOn(targetBuildTask) + tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn( + targetBuildTask + ) + } + } + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/android/gradle.properties b/src-tauri/gen/android/gradle.properties new file mode 100644 index 0000000..022338b --- /dev/null +++ b/src-tauri/gen/android/gradle.properties @@ -0,0 +1,25 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..40a4350 --- /dev/null +++ b/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 10 19:22:52 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/src-tauri/gen/android/gradlew b/src-tauri/gen/android/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/src-tauri/gen/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/src-tauri/gen/android/gradlew.bat b/src-tauri/gen/android/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/src-tauri/gen/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src-tauri/gen/android/settings.gradle b/src-tauri/gen/android/settings.gradle new file mode 100644 index 0000000..3939116 --- /dev/null +++ b/src-tauri/gen/android/settings.gradle @@ -0,0 +1,3 @@ +include ':app' + +apply from: 'tauri.settings.gradle' diff --git a/src-tauri/gen/apple/.gitignore b/src-tauri/gen/apple/.gitignore new file mode 100644 index 0000000..6726e2f --- /dev/null +++ b/src-tauri/gen/apple/.gitignore @@ -0,0 +1,3 @@ +xcuserdata/ +build/ +Externals/ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000..f8b128e Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000..6bbd9e3 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000..6bbd9e3 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000..f702cc0 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 0000000..c5e92f7 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000..1c607d5 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 0000000..1c607d5 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 0000000..60e93a6 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000..6bbd9e3 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000..819410f Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 0000000..819410f Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 0000000..e00ae5a Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000..f5301f3 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png new file mode 100644 index 0000000..5e9add7 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000..e00ae5a Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000..3546ca1 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000..d836710 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 0000000..29925f2 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000..dfd2261 Binary files /dev/null and b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..90eea7e --- /dev/null +++ b/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppIcon-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppIcon-512@2x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/src-tauri/gen/apple/Assets.xcassets/Contents.json b/src-tauri/gen/apple/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/src-tauri/gen/apple/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/src-tauri/gen/apple/ExportOptions.plist b/src-tauri/gen/apple/ExportOptions.plist new file mode 100644 index 0000000..b69cf1d --- /dev/null +++ b/src-tauri/gen/apple/ExportOptions.plist @@ -0,0 +1,8 @@ + + + + + method + development + + diff --git a/src-tauri/gen/apple/Podfile b/src-tauri/gen/apple/Podfile new file mode 100644 index 0000000..f1ce4c9 --- /dev/null +++ b/src-tauri/gen/apple/Podfile @@ -0,0 +1,21 @@ +# Uncomment the next line to define a global platform for your project + +target 'book_iOS' do +platform :ios, '13.0' + # Pods for book_iOS +end + +target 'book_macOS' do +platform :osx, '11.0' + # Pods for book_macOS +end + +# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET' + end + end +end diff --git a/src-tauri/gen/apple/Sources/book/bindings/bindings.h b/src-tauri/gen/apple/Sources/book/bindings/bindings.h new file mode 100644 index 0000000..5152200 --- /dev/null +++ b/src-tauri/gen/apple/Sources/book/bindings/bindings.h @@ -0,0 +1,8 @@ +#pragma once + +namespace ffi { + extern "C" { + void start_app(); + } +} + diff --git a/src-tauri/gen/apple/Sources/book/main.mm b/src-tauri/gen/apple/Sources/book/main.mm new file mode 100644 index 0000000..7793a9d --- /dev/null +++ b/src-tauri/gen/apple/Sources/book/main.mm @@ -0,0 +1,6 @@ +#include "bindings/bindings.h" + +int main(int argc, char * argv[]) { + ffi::start_app(); + return 0; +} diff --git a/src-tauri/gen/apple/book.xcodeproj/project.pbxproj b/src-tauri/gen/apple/book.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b6816df --- /dev/null +++ b/src-tauri/gen/apple/book.xcodeproj/project.pbxproj @@ -0,0 +1,456 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 07EB9EB55F68AFDC19286EB1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 502FD66F2D828502B6937E84 /* QuartzCore.framework */; }; + 1F9EC8B62EE609F4DBC46C39 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19272F761A553F8712399DDB /* UIKit.framework */; }; + 3697FED17A38FE770D96191C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9D8315FCB24011671E081CB /* WebKit.framework */; }; + 47F0B0477D7FB05BC10C3404 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */; }; + 6621EB723ECC6FFE5DEC54D1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A4E645CA73EFA14278503F9B /* Assets.xcassets */; }; + 8381B7964402AC6E747AFB60 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1437ED7A1A5ADBD385AE606 /* Metal.framework */; }; + 8DD816FF5392E18B02C30F53 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 4D55D8A35C2F7A8DF97EDDB5 /* assets */; }; + C362EA7E1CC525DE197BEEA5 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D11288099C194F3FC723F1 /* MetalKit.framework */; }; + C60953C455E968E495E01F54 /* libbook_lib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D955991A316E82D5DB047DCD /* libbook_lib.a */; }; + F687BCB8A175588940AEAA95 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAABE02D4E665E5685D26317 /* Security.framework */; }; + FD7F76E5402B6D3EA93CFB50 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC1C9BD5E9ECC5AA72C14E55 /* main.mm */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 19272F761A553F8712399DDB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 223F6FA376249CCB1A474447 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 34C6202E04831D540886F324 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = ""; }; + 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 4D55D8A35C2F7A8DF97EDDB5 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = SOURCE_ROOT; }; + 502FD66F2D828502B6937E84 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + A33B99D3ADEA2990135854C7 /* book_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = book_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A4E645CA73EFA14278503F9B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B47A9B12CF9D828061100052 /* lib.rs */ = {isa = PBXFileReference; path = lib.rs; sourceTree = ""; }; + B9D8315FCB24011671E081CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + BC1C9BD5E9ECC5AA72C14E55 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; + D1437ED7A1A5ADBD385AE606 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + D955991A316E82D5DB047DCD /* libbook_lib.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libbook_lib.a; sourceTree = ""; }; + D96D9232829CE72A68D66701 /* book_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = book_iOS.entitlements; sourceTree = ""; }; + DC024FE1F811AD3F9CF97367 /* main.rs */ = {isa = PBXFileReference; path = main.rs; sourceTree = ""; }; + EAABE02D4E665E5685D26317 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + F8D11288099C194F3FC723F1 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2773691936923BDC5A173329 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C60953C455E968E495E01F54 /* libbook_lib.a in Frameworks */, + 47F0B0477D7FB05BC10C3404 /* CoreGraphics.framework in Frameworks */, + 8381B7964402AC6E747AFB60 /* Metal.framework in Frameworks */, + C362EA7E1CC525DE197BEEA5 /* MetalKit.framework in Frameworks */, + 07EB9EB55F68AFDC19286EB1 /* QuartzCore.framework in Frameworks */, + F687BCB8A175588940AEAA95 /* Security.framework in Frameworks */, + 1F9EC8B62EE609F4DBC46C39 /* UIKit.framework in Frameworks */, + 3697FED17A38FE770D96191C /* WebKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2A199E4F9B7AED783C841B21 /* Externals */ = { + isa = PBXGroup; + children = ( + ); + path = Externals; + sourceTree = ""; + }; + 30C5B33B7BC6299E874056AB /* book_iOS */ = { + isa = PBXGroup; + children = ( + D96D9232829CE72A68D66701 /* book_iOS.entitlements */, + 223F6FA376249CCB1A474447 /* Info.plist */, + ); + path = book_iOS; + sourceTree = ""; + }; + 49B669CD106B148DDBE79AD3 /* Products */ = { + isa = PBXGroup; + children = ( + A33B99D3ADEA2990135854C7 /* book_iOS.app */, + ); + name = Products; + sourceTree = ""; + }; + 91C5C5ABC648A50BC038E9B0 /* src */ = { + isa = PBXGroup; + children = ( + B47A9B12CF9D828061100052 /* lib.rs */, + DC024FE1F811AD3F9CF97367 /* main.rs */, + ); + name = src; + path = ../../src; + sourceTree = ""; + }; + 91FA5F2617012ECB991EA4B8 /* bindings */ = { + isa = PBXGroup; + children = ( + 34C6202E04831D540886F324 /* bindings.h */, + ); + path = bindings; + sourceTree = ""; + }; + A1DAE41A85C4748AE7627587 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3D86B221DC7163BC0FAA2A25 /* CoreGraphics.framework */, + D955991A316E82D5DB047DCD /* libbook_lib.a */, + D1437ED7A1A5ADBD385AE606 /* Metal.framework */, + F8D11288099C194F3FC723F1 /* MetalKit.framework */, + 502FD66F2D828502B6937E84 /* QuartzCore.framework */, + EAABE02D4E665E5685D26317 /* Security.framework */, + 19272F761A553F8712399DDB /* UIKit.framework */, + B9D8315FCB24011671E081CB /* WebKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + CD2F05A02DDE2A901917CC91 /* book */ = { + isa = PBXGroup; + children = ( + BC1C9BD5E9ECC5AA72C14E55 /* main.mm */, + 91FA5F2617012ECB991EA4B8 /* bindings */, + ); + path = book; + sourceTree = ""; + }; + D2D94A4F380D848B244C8EEA /* Sources */ = { + isa = PBXGroup; + children = ( + CD2F05A02DDE2A901917CC91 /* book */, + ); + path = Sources; + sourceTree = ""; + }; + EAA221D6DC39CFA4976B82C0 = { + isa = PBXGroup; + children = ( + 4D55D8A35C2F7A8DF97EDDB5 /* assets */, + A4E645CA73EFA14278503F9B /* Assets.xcassets */, + 30C5B33B7BC6299E874056AB /* book_iOS */, + 2A199E4F9B7AED783C841B21 /* Externals */, + D2D94A4F380D848B244C8EEA /* Sources */, + 91C5C5ABC648A50BC038E9B0 /* src */, + A1DAE41A85C4748AE7627587 /* Frameworks */, + 49B669CD106B148DDBE79AD3 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 298FD83A23DC2F275E467343 /* book_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3C0367FC0AEC53BA985AD036 /* Build configuration list for PBXNativeTarget "book_iOS" */; + buildPhases = ( + 010947584CD0C112EEA21FA4 /* Build Rust Code */, + 45AB425E9092D28C15026378 /* Sources */, + 20E0C8EC49AE78785547EC11 /* Resources */, + 2773691936923BDC5A173329 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = book_iOS; + productName = book_iOS; + productReference = A33B99D3ADEA2990135854C7 /* book_iOS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 76D41858814BBE80481186FD /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + TargetAttributes = { + }; + }; + buildConfigurationList = 75E8567F6082E44D7D3577E8 /* Build configuration list for PBXProject "book" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = EAA221D6DC39CFA4976B82C0; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 298FD83A23DC2F275E467343 /* book_iOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 20E0C8EC49AE78785547EC11 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6621EB723ECC6FFE5DEC54D1 /* Assets.xcassets in Resources */, + 8DD816FF5392E18B02C30F53 /* assets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 010947584CD0C112EEA21FA4 /* Build Rust Code */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Build Rust Code"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a", + "$(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/Users/aok/.cargo/bin/cargo-tauri tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths \"${FRAMEWORK_SEARCH_PATHS:?}\" --header-search-paths \"${HEADER_SEARCH_PATHS:?}\" --gcc-preprocessor-definitions \"${GCC_PREPROCESSOR_DEFINITIONS:-}\" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 45AB425E9092D28C15026378 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FD7F76E5402B6D3EA93CFB50 /* main.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6E917E4BF8CB18D312EAFCBE /* debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = debug; + }; + 8FC2757CBDB1C98971E170A1 /* debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ARCHS = ( + arm64, + x86_64, + ); + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = book_iOS/book_iOS.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = 12345; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\".\"", + ); + INFOPLIST_FILE = book_iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + "LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + "LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = com.book.book; + PRODUCT_NAME = book; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 x86_64"; + }; + name = debug; + }; + CA70F0C850C9C2B1A2C72960 /* release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = release; + }; + F551B91FFE058B0C9E3492B7 /* release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ARCHS = ( + arm64, + x86_64, + ); + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = book_iOS/book_iOS.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = 12345; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\".\"", + ); + INFOPLIST_FILE = book_iOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + "LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + "LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = com.book.book; + PRODUCT_NAME = book; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 x86_64"; + }; + name = release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3C0367FC0AEC53BA985AD036 /* Build configuration list for PBXNativeTarget "book_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8FC2757CBDB1C98971E170A1 /* debug */, + F551B91FFE058B0C9E3492B7 /* release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = debug; + }; + 75E8567F6082E44D7D3577E8 /* Build configuration list for PBXProject "book" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6E917E4BF8CB18D312EAFCBE /* debug */, + CA70F0C850C9C2B1A2C72960 /* release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 76D41858814BBE80481186FD /* Project object */; +} diff --git a/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..ac90d5a --- /dev/null +++ b/src-tauri/gen/apple/book.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + BuildSystemType + Original + DisableBuildSystemDeprecationDiagnostic + + + diff --git a/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme b/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme new file mode 100644 index 0000000..187da61 --- /dev/null +++ b/src-tauri/gen/apple/book.xcodeproj/xcshareddata/xcschemes/book_iOS.xcscheme @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-tauri/gen/apple/book_iOS/Info.plist b/src-tauri/gen/apple/book_iOS/Info.plist new file mode 100644 index 0000000..9cbeec6 --- /dev/null +++ b/src-tauri/gen/apple/book_iOS/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.0 + CFBundleVersion + 0.0.0 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + metal + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/src-tauri/gen/apple/book_iOS/book_iOS.entitlements b/src-tauri/gen/apple/book_iOS/book_iOS.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/src-tauri/gen/apple/book_iOS/book_iOS.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/src-tauri/gen/apple/project.yml b/src-tauri/gen/apple/project.yml new file mode 100644 index 0000000..4ca58e5 --- /dev/null +++ b/src-tauri/gen/apple/project.yml @@ -0,0 +1,88 @@ +name: book +options: + bundleIdPrefix: com.book + deploymentTarget: + iOS: 13.0 +fileGroups: [../../src] +configs: + debug: debug + release: release +settingGroups: + app: + base: + PRODUCT_NAME: book + PRODUCT_BUNDLE_IDENTIFIER: com.book.book + DEVELOPMENT_TEAM: 12345 +targetTemplates: + app: + type: application + sources: + - path: Sources + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + groups: [app] +targets: + book_iOS: + type: application + platform: iOS + sources: + - path: Sources + - path: Assets.xcassets + - path: Externals + - path: book_iOS + - path: assets + buildPhase: resources + type: folder + info: + path: book_iOS/Info.plist + properties: + LSRequiresIPhoneOS: true + UILaunchStoryboardName: LaunchScreen + UIRequiredDeviceCapabilities: [arm64, metal] + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationPortraitUpsideDown + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + CFBundleShortVersionString: 0.0.0 + CFBundleVersion: 0.0.0 + entitlements: + path: book_iOS/book_iOS.entitlements + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + base: + ENABLE_BITCODE: false + ARCHS: [arm64, x86_64] + VALID_ARCHS: arm64 x86_64 + LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true + groups: [app] + dependencies: + - framework: libbook_lib.a + embed: false + - sdk: CoreGraphics.framework + - sdk: Metal.framework + - sdk: MetalKit.framework + - sdk: QuartzCore.framework + - sdk: Security.framework + - sdk: UIKit.framework + - sdk: WebKit.framework + preBuildScripts: + - script: /Users/aok/.cargo/bin/cargo-tauri tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + name: Build Rust Code + basedOnDependencyAnalysis: false + outputFiles: + - $(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a + - $(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/libbook_lib.a \ No newline at end of file diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..6be5e50 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..e81bece Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..a437dd5 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..0ca4f27 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..b81f820 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..624c7bf Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..c021d2b Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..6219700 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..f9bc048 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..d5fbfb2 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..63440d7 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..f3f705a Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..4556388 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..12a5bce Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..b3636e4 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..e1cd261 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..5f48bdb --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,15 @@ +// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command +#[tauri::command] +fn greet(name: &str) -> String { + format!("Hello, {}! You've been greeted from Rust!", name) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_window::init()) + .plugin(tauri_plugin_shell::init()) + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..914ef39 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,7 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + #[cfg(desktop)] + nigiginc_lib::run(); +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..e5a9328 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,44 @@ +{ + "build": { + "beforeDevCommand": "trunk serve", + "beforeBuildCommand": "trunk build", + "devPath": "http://localhost:1420", + "distDir": "../dist", + "withGlobalTauri": true + }, + "package": { + "productName": "book", + "version": "0.0.0" + }, + "tauri": { + "bundle": { + "active": true, + "targets": "all", + "identifier": "com.book.dev", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + }, + "security": { + "csp": null + }, + "windows": [ + { + "fullscreen": false, + "resizable": true, + "title": "book", + "width": 800, + "height": 600 + } + ] + }, + "plugins": { + "shell": { + "open": true + } + } +} diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..6e771f2 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,273 @@ +use std::fmt::Display; + +use leptos::{leptos_dom::Callback, *}; +use leptos_icons::BsIcon; +use leptos_meta::{provide_meta_context, Title}; +use leptos_router::*; +use leptos_use::use_media_query; + +use leptonic::prelude::*; + +use crate::pages::{documentation::doc_root::DocRoutes, err404::PageErr404, welcome::PageWelcome}; + +#[derive(Debug, Copy, Clone)] +pub enum AppRoutes { + Welcome, + Doc, + NotFound, +} + +impl AppRoutes { + pub fn route(self) -> &'static str { + match self { + AppRoutes::Welcome => "", + AppRoutes::Doc => "doc", + AppRoutes::NotFound => "*", // Leptos requires this to be be named "*"! + } + } +} + +/// Required so that `Routes` variants can be used in `` definitions. +impl Display for AppRoutes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.route()) + } +} + +/// Required so that `Routes` variants can be used in `` definitions. +impl ToHref for AppRoutes { + fn to_href(&self) -> Box String + '_> { + Box::new(move || format!("/{}", self.route())) + } +} + +#[component] +pub fn App() -> impl IntoView { + provide_meta_context(); + + view! { + + <Root default_theme=LeptonicTheme::default()> + <Router> + <Routes> + <Route path="" view=|| view! { <Layout/> }> + <Route path=AppRoutes::Welcome view=|| view! { <PageWelcome/> }/> + <DocRoutes path=AppRoutes::Doc/> + <Route path=AppRoutes::NotFound view=|| view! { <PageErr404 /> }/> + </Route> + </Routes> + </Router> + </Root> + } +} + +pub const APP_BAR_HEIGHT: Height = Height::Em(3.5); + +#[derive(Debug, Clone, Copy)] +pub struct AppLayoutContext { + pub is_small: Signal<bool>, + pub main_drawer_closed: Signal<bool>, + set_main_drawer_closed: WriteSignal<bool>, + pub doc_drawer_closed: Signal<bool>, + set_doc_drawer_closed: WriteSignal<bool>, +} + +impl AppLayoutContext { + #[allow(unused)] + pub fn close_main_drawer(&self) { + self.set_main_drawer_closed.set(true); + } + + pub fn close_doc_drawer(&self) { + self.set_doc_drawer_closed.set(true); + } + + pub fn toggle_main_drawer(&self) { + let currently_closed = self.main_drawer_closed.get_untracked(); + self.set_main_drawer_closed.set(!currently_closed); + if currently_closed { + self.close_doc_drawer(); + } + } + + pub fn toggle_doc_drawer(&self) { + let currently_closed = self.doc_drawer_closed.get_untracked(); + self.set_doc_drawer_closed.set(!currently_closed); + if currently_closed { + self.close_main_drawer(); + } + } +} + +#[component] +pub fn Layout() -> impl IntoView { + let is_small = use_media_query("(max-width: 800px)"); + let router_context = use_router(); + let is_doc = create_memo(move |_| router_context.pathname().get().starts_with("/doc")); + + // The main drawer is only used on mobile / small screens!. + let (main_drawer_closed, set_main_drawer_closed) = create_signal(true); + let (doc_drawer_closed, set_doc_drawer_closed) = create_signal(false); + + // Always close the doc-drawer when the application is now small. + // Always open the doc-drawer when the application is no longer small. + create_effect(move |_| { + if is_small.get() { + set_doc_drawer_closed.set(true); + } else { + set_doc_drawer_closed.set(false); + } + }); + + // Always close the main-drawer when the application is no longer small. + create_effect(move |_| { + if !is_small.get() { + set_main_drawer_closed.set(true); + } + }); + + let ctx = AppLayoutContext { + is_small, + main_drawer_closed: main_drawer_closed.into(), + set_main_drawer_closed, + doc_drawer_closed: doc_drawer_closed.into(), + set_doc_drawer_closed, + }; + + provide_context(ctx); + + let search_options = vec![ + ( + "overview", + QuicksearchOption { + view: Callback::new(move |()| { + view! { + <Link href=DocRoutes::Overview class="search-link"> + "Overview" + </Link> + } + }), + on_select: Callback::new(move |()| {}), + }, + ), + ( + "installation", + QuicksearchOption { + view: Callback::new(move |()| { + view! { + <Link href=DocRoutes::Installation class="search-link"> + "Installation" + </Link> + } + }), + on_select: Callback::new(move |()| {}), + }, + ), + ( + "usage", + QuicksearchOption { + view: Callback::new(move |()| { + view! { + <Link href=DocRoutes::Usage class="search-link"> + "Usage" + </Link> + } + }), + on_select: Callback::new(move |()| {}), + }, + ), + ]; + + let logo = move || { + view! { + <Link href=""> + <img src="/res/leptonic.svg" id="logo" alt="Leptonic logo"/> + </Link> + } + }; + + view! { + <AppBar id="app-bar" height=APP_BAR_HEIGHT> + <div id="app-bar-content"> + <Stack id="left" orientation=StackOrientation::Horizontal spacing=Size::Zero> + { move || match (is_doc.get(), is_small.get()) { + (false, true) => logo().into_view(), + (true, true) => view! { + <Icon id="mobile-menu-trigger" icon=BsIcon::BsList on:click=move |_| ctx.toggle_doc_drawer()/> + { logo } + }.into_view(), + (_, false) => view! { + { logo } + <Link href=AppRoutes::Doc> + <H3 style="margin: 0 0 0 0.5em"> + "Docs" + </H3> + </Link> + }.into_view(), + } } + </Stack> + + <Stack id="center" orientation=StackOrientation::Horizontal spacing=Size::Em(1.0)> + <Quicksearch + id="quicksearch" + trigger=move |set_quicksearch| view! { + <QuicksearchTrigger id="quicksearch-trigger" set_quicksearch=set_quicksearch> + { move || match is_small.get() { + true => view! { <Icon icon=BsIcon::BsSearch />}.into_view(), + false => view! { "Search"}.into_view(), + } } + </QuicksearchTrigger> + } + query=move |search: String| { + if search.is_empty() { + return vec![]; + } + let lower_search = search.to_lowercase(); + // TODO: Re-enable in rc3 + //search_options.iter() + // .filter(|it| it.0.to_lowercase().contains(&lower_search)) + // .map(|it| it.1.clone()) + // .collect::<Vec<_>>() + Vec::new() + } + /> + </Stack> + + <Stack id="right" orientation=StackOrientation::Horizontal spacing=Size::Em(1.0)> + { move || match is_small.get() { + true => view! { + <Icon id="mobile-menu-trigger" icon=BsIcon::BsThreeDots on:click=move |_| ctx.toggle_main_drawer()/> + }.into_view(), + false => view! { + <Link href=DocRoutes::Changelog>"v0.2.0"</Link> + + <LinkExt href="https://github.com/lpotthast/leptonic" target=LinkExtTarget::Blank> + <Icon id="github-icon" icon=BsIcon::BsGithub aria_label="GitHub icon"/> + </LinkExt> + + <ThemeToggle off=LeptonicTheme::Light on=LeptonicTheme::Dark style="margin-right: 1em"/> + }.into_view(), + } } + </Stack> + </div> + </AppBar> + + <Box id="content" style=format!("height: calc(var(--leptonic-vh, 100vh) - {APP_BAR_HEIGHT}); max-height: calc(var(--leptonic-vh, 100vh) - {APP_BAR_HEIGHT});")> + // <Outlet/> will show nested child routes. + <Outlet/> + + <Drawer id="main-drawer" shown=Signal::derive(move || !main_drawer_closed.get()) side=DrawerSide::Right style=format!("top: {APP_BAR_HEIGHT}")> + <Stack orientation=StackOrientation::Vertical spacing=Size::Em(2.0) class="menu"> + + <LinkExt href="https://github.com/lpotthast/leptonic" target=LinkExtTarget::Blank style="font-size: 3em;"> + <Icon id="github-icon" icon=BsIcon::BsGithub/> + </LinkExt> + + <ThemeToggle off=LeptonicTheme::Light on=LeptonicTheme::Dark style="margin-right: 1em"/> + + "Currently - v0.2.0" + </Stack> + </Drawer> + </Box> + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7d4bcb2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +use leptos::*; + +mod app; +mod pages; + +use crate::app::*; + +fn main() { + console_error_panic_hook::set_once(); + tracing_wasm::set_as_global_default_with_config( + tracing_wasm::WASMLayerConfigBuilder::default() + .set_max_level(tracing::Level::DEBUG) + .build(), + ); + mount_to_body(|| { + view! { <App/> } + }) +} diff --git a/src/pages/documentation/alert.rs b/src/pages/documentation/alert.rs new file mode 100644 index 0000000..2dd6310 --- /dev/null +++ b/src/pages/documentation/alert.rs @@ -0,0 +1,47 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageAlert() -> impl IntoView { + let (centered, set_centered) = create_signal(false); + view! { + <H1>"Alerts"</H1> + + <Code> + {indoc!(r#" + <Alert variant=AlertVariant::Success title=|_| "Success".into_view()>"Action completed."</Alert> + "#)} + </Code> + + <Alert variant=AlertVariant::Success title=|| "Success".into_view() centered=centered>"Action completed."</Alert> + <Alert variant=AlertVariant::Info title=|| "Info".into_view() centered=centered>"This concept is based on [...]"</Alert> + <Alert variant=AlertVariant::Warn title=|| "Warn".into_view() centered=centered>"This seems not plausible."</Alert> + <Alert variant=AlertVariant::Danger title=|| "Danger".into_view() centered=centered>"There was an error!"</Alert> + + <P>"Alerts can be dynamically centered using a signal."</P> + + <Button on_click=move |_| set_centered.update(|it| *it = !*it)>"Center toggle"</Button> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --alert-margin + --alert-padding + --alert-primary-background-color + --alert-primary-color + --alert-info-background-color + --alert-info-color + --alert-success-background-color + --alert-success-color + --alert-warn-background-color + --alert-warn-color + --alert-danger-background-color + --alert-danger-color + "#)} + </Code> + } +} diff --git a/src/pages/documentation/anchor.rs b/src/pages/documentation/anchor.rs new file mode 100644 index 0000000..b24bb98 --- /dev/null +++ b/src/pages/documentation/anchor.rs @@ -0,0 +1,14 @@ +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageAnchor() -> impl IntoView { + view! { + <H1>"Anchor"</H1> + + <H3 id="#test-heading"> + "Test Heading" + <Anchor href="#test-heading" title="The title describes this anchor." /> + </H3> + } +} diff --git a/src/pages/documentation/app_bar.rs b/src/pages/documentation/app_bar.rs new file mode 100644 index 0000000..adfac60 --- /dev/null +++ b/src/pages/documentation/app_bar.rs @@ -0,0 +1,66 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; + +#[component] +pub fn PageAppBar() -> impl IntoView { + let app_bar_height = Height::Em(3.0); + + view! { + <H1>"App Bar"</H1> + + <P>"The "<Code inline=true>"<AppBar>"</Code>" component sticks to the top of its parent and provides a convenient entrypoint for many app layouts."</P> + + <Box style="position: relative; border: 4px solid gray; width: 100%; height: 20em; overflow: auto;"> + <AppBar height=app_bar_height style="z-index: 1; background: var(--brand-color); color: white;"> + <H3 style="margin-left: 1em; color: white;">"Leptonic"</H3> + <Stack orientation=StackOrientation::Horizontal spacing=Size::Em(1.0) style="margin-right: 1em"> + <Icon icon=BsIcon::BsBell></Icon> + <Icon icon=BsIcon::BsPower></Icon> + </Stack> + </AppBar> + + <Box style="padding: 0.5em;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..10).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + </Box> + + <Code> + {indoc!(r#" + <Box style="position: relative; border: 4px solid gray; width: 100%; height: 20em; overflow: auto;"> + <AppBar height=app_bar_height style="z-index: 1; background: var(--brand-color); color: white;"> + <H3 style="margin-left: 1em; color: white;">"Leptonic"</H3> + <Stack orientation=StackOrientation::Horizontal spacing=Size::Em(1.0) style="margin-right: 1em"> + <Icon icon=BsIcon::BsGithub></Icon> + <Icon icon=BsIcon::BsPower></Icon> + </Stack> + </AppBar> + + <Box style="padding: 0.5em;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..10).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + </Box> + "#)} + </Code> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --app-bar-height + --app-bar-background-color + --app-bar-border-bottom + --app-bar-box-shadow + "#)} + </Code> + } +} diff --git a/src/pages/documentation/button.rs b/src/pages/documentation/button.rs new file mode 100644 index 0000000..ff5a443 --- /dev/null +++ b/src/pages/documentation/button.rs @@ -0,0 +1,240 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageButton() -> impl IntoView { + let (disabled, set_disabled) = create_signal(false); + let (num_main_action_activated, set_num_main_action_activated) = create_signal(0); + let (num_secondary_action_activated, set_num_secondary_action_activated) = create_signal(0); + view! { + <H1>"Buttons"</H1> + + <P>"Buttons are one of the most common input mechanisms with which your users can interact with your software."</P> + <P>"Buttons only require an action handler and can therefor be created like in this minimal example."</P> + + <Code> + {indoc!(r#" + <Button on_click=move |_| {}> + "My Button" + </Button> + "#)} + </Code> + + <div> + <Button on_click=move |_| {}>"My Button"</Button> + </div> + + <h2>"Variants"</h2> + + <P> + "Buttons come in three different " <Code inline=true>"ButtonVariant"</Code> "s." + <Code inline=true>"Flat"</Code> ", " + <Code inline=true>"Outlined"</Code> " and " + <Code inline=true>"Filled"</Code> ", with the Filled variant being the default, hence the visual of our simple button above." + </P> + + <Code> + {indoc!(r#" + <Button on_click=move |_| {} variant=ButtonVariant::Flat>"Flat"</Button> + <Button on_click=move |_| {} variant=ButtonVariant::Outlined>"Outlined"</Button> + <Button on_click=move |_| {} variant=ButtonVariant::Filled>"Filled"</Button> + "#)} + </Code> + + <Stack orientation=StackOrientation::Horizontal spacing=Size::Em(0.6) style="justify-content: flex-start;"> + <Button on_click=move |_| {} variant=ButtonVariant::Flat>"Flat"</Button> + <Button on_click=move |_| {} variant=ButtonVariant::Outlined>"Outlined"</Button> + <Button on_click=move |_| {} variant=ButtonVariant::Filled>"Filled"</Button> + </Stack> + + <h2>"Button group"</h2> + + <P>"Buttons can be displayed in a group. This lets adjacent buttons snap to each other, creating a seamless row of buttons. It is recommended to only use the Filled button variant when putting buttons inside a group."</P> + + <Code> + {indoc!(r#" + <ButtonGroup> + <Button on_click=move |_| {}>"Button 1"</Button> + <Button on_click=move |_| {}>"Button 2"</Button> + <Button on_click=move |_| {}>"Button 3"</Button> + </ButtonGroup> + "#)} + </Code> + + <ButtonGroup> + <Button on_click=move |_| {}>"Button 1"</Button> + <Button on_click=move |_| {}>"Button 2"</Button> + <Button on_click=move |_| {}>"Button 3"</Button> + </ButtonGroup> + + <H2>"Disabled"</H2> + + <P>"Buttons can be set disabled using a signal."</P> + + <P> + "Buttons can be disabled using the " + <Code inline=true>"disabled"</Code> + " property. You can supply anything evaluating to a boolean, including signals." + </P> + + <Code> + {indoc!(r#" + <Button on_click=move |_| {} disabled=true>"Disabled"</Button> + <Button on_click=move |_| {} disabled=disabled>"Disabled"</Button> + <Button on_click=move |_| {} disabled=Signal::derive(move || !disabled.get())>"Disabled"</Button> + "#)} + </Code> + + <div> + "Disable: " <Toggle state=disabled set_state=set_disabled/> + </div> + + <ButtonWrapper> + <Button on_click=move |_| {} disabled=true>"Disabled"</Button> + <Button on_click=move |_| {} disabled=disabled>"Disabled"</Button> + <Button on_click=move |_| {} disabled=Signal::derive(move || !disabled.get())>"Disabled"</Button> + </ButtonWrapper> + + <H2>"Variations"</H2> + + <Code> + {indoc!(r#" + <Button on_click=move |_| {} variations=view! { + <Button on_click=move |_| {}>"Secondary action"</Button> + }.into_view()> + "MainAction" + </Button> + "#)} + </Code> + + <Button on_click=move |_| {set_num_main_action_activated.update(|it| *it += 1)} variations=view! { + <Button on_click=move |_| {set_num_secondary_action_activated.update(|it| *it += 1)}>"Secondary action"</Button> + }.into_view()> + "MainAction" + </Button> + + <div>"Main action activated: " { move || num_main_action_activated.get() }</div> + <div>"Secondary action activated: " { move || num_secondary_action_activated.get() }</div> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --button-border-size + --button-border-radius + --button-box-shadow-opacity + + --button-flat-primary-text-color + --button-flat-primary-text-color-hover + --button-flat-primary-background-color-hover + + --button-outlined-primary-text-color + --button-outlined-primary-text-color-hover + --button-outlined-primary-border-color + --button-outlined-primary-border-color-hover + --button-outlined-primary-box-shadow-color + + --button-filled-primary-text-color + --button-filled-primary-text-color-hover + --button-filled-primary-background-color + --button-filled-primary-background-color-hover + --button-filled-primary-border-color + --button-filled-primary-border-color-hover + --button-filled-primary-box-shadow-color + + --button-flat-secondary-text-color + --button-flat-secondary-text-color-hover + --button-flat-secondary-background-color-hover + + --button-outlined-secondary-text-color + --button-outlined-secondary-text-color-hover + --button-outlined-secondary-border-color + --button-outlined-secondary-border-color-hover + --button-outlined-secondary-box-shadow-color + + --button-filled-secondary-text-color + --button-filled-secondary-text-color-hover + --button-filled-secondary-background-color + --button-filled-secondary-background-color-hover + --button-filled-secondary-border-color + --button-filled-secondary-border-color-hover + --button-filled-secondary-box-shadow-color + + --button-flat-success-text-color + --button-flat-success-text-color-hover + --button-flat-success-background-color-hover + + --button-outlined-success-text-color + --button-outlined-success-text-color-hover + --button-outlined-success-border-color + --button-outlined-success-border-color-hover + --button-outlined-success-box-shadow-color + + --button-filled-success-text-color + --button-filled-success-text-color-hover + --button-filled-success-background-color + --button-filled-success-background-color-hover + --button-filled-success-border-color + --button-filled-success-border-color-hover + --button-filled-success-box-shadow-color + + --button-flat-info-text-color + --button-flat-info-text-color-hover + --button-flat-info-background-color-hover + + --button-outlined-info-text-color + --button-outlined-info-text-color-hover + --button-outlined-info-border-color + --button-outlined-info-border-color-hover + --button-outlined-info-box-shadow-color + + --button-filled-info-text-color + --button-filled-info-text-color-hover + --button-filled-info-background-color + --button-filled-info-background-color-hover + --button-filled-info-border-color + --button-filled-info-border-color-hover + --button-filled-info-box-shadow-color + + --button-flat-warning-text-color + --button-flat-warning-text-color-hover + --button-flat-warning-background-color-hover + + --button-outlined-warning-text-color + --button-outlined-warning-text-color-hover + --button-outlined-warning-border-color + --button-outlined-warning-border-color-hover + --button-outlined-warning-box-shadow-color + + --button-filled-warning-text-color + --button-filled-warning-text-color-hover + --button-filled-warning-background-color + --button-filled-warning-background-color-hover + --button-filled-warning-border-color + --button-filled-warning-border-color-hover + --button-filled-warning-box-shadow-color + + --button-flat-danger-text-color + --button-flat-danger-text-color-hover + --button-flat-danger-background-color-hover + + --button-outlined-danger-text-color + --button-outlined-danger-text-color-hover + --button-outlined-danger-border-color + --button-outlined-danger-border-color-hover + --button-outlined-danger-box-shadow-color + + --button-filled-danger-text-color + --button-filled-danger-text-color-hover + --button-filled-danger-background-color + --button-filled-danger-background-color-hover + --button-filled-danger-border-color + --button-filled-danger-border-color-hover + --button-filled-danger-box-shadow-color + "#)} + </Code> + } +} diff --git a/src/pages/documentation/callback.rs b/src/pages/documentation/callback.rs new file mode 100644 index 0000000..e6f12e8 --- /dev/null +++ b/src/pages/documentation/callback.rs @@ -0,0 +1,130 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageCallback() -> impl IntoView { + view! { + <H1>"Callbacks"</H1> + + <Code> + {indoc!(r#" + #[prop(into)] set_value: Callback<u32>, + #[prop(into, optional)] maybe_render: Option<Callback<(Scope, String), View>> + + ... + + set_value.call(42); + + if let Some(render) = maybe_render { + let _view = render.call(("foo".to_owned())); + } + "#)} + </Code> + + <P> + "Easy, isn't it?" + </P> + + <P> + "Callbacks can sometimes be tricky in Leptos. When realized with generics, they are easy to use as a component-consumer and fast, " + "but hard(er) to write as an author and, because of their generic nature, increase WASM binary size." + </P> + + <P> + "If the property of a component specifying a callback should be "<Code inline=true>"Option"</Code>"al, the generic approach is of no help, " + "as Rust will have problems inferring unspecified, and not otherwise explicitly nameable, types " + "for the properties left out when instantiating such a component." + </P> + + <P> + "An additional burden with the generic approach: "<Code inline=true>"Copy"</Code>". " + "A simple generic "<Code inline=true>"Fn"</Code>" prop can only be moved once inside your component when not specified with an additional " + <Code inline=true>"Copy"</Code>" bound. " + "But doing so will limit your users ability to write compiling closures for the callback, " + "as only closures not taking ownership of non-copyable-values, implicitly making the closure copy, can be used." + </P> + + <P> + "If you, as a component author, need to move a callback into more places, you can put it inside a call to " + <Code inline=true>"store_value"</Code>" to get an easily copyable and therefore moveable value. " + "Using a generic "<Code inline=true>"Fn"</Code>" in the non-optional case or a "<Code inline=true>"Box<dyn Fn>"</Code>" in " + "case the prop is optional and always storing it boxed in a "<Code inline=true>"StoredValue"</Code>" is good solution. " + "Arguments could be made that " + </P> + + <ul> + <li>"(a) components are not often recreated, as we always want to reactively updated their content instead"</li> + <li>"(b) some callbacks aren't called often, as they are only used to propagate user input and users are incredibly slow compared to machines."</li> + </ul> + + <P> + "These make both the performance hit of creating a Box<dyn Fn> and calling it later somewhat irrelevant. " + "The penalty of managing an extra StoredValue through Leptos should not be overlooked though." + </P> + + <P> + "Because creating boxed closures manually and storing them in is not ergonomic, " + "Leptonic provides an easy to use "<Code inline=true>"Callback"</Code>" type, which is defined as" + </P> + + <Code> + "pub struct Callback<T: 'static, R: 'static = ()>(leptos::StoredValue<Box<dyn Fn(T) -> R>>);" + </Code> + + <P> + "This callback type is both Clone and Copy and can be used at multiple locations in a component " + "without requiring an explicitly Clone/Copy closure to be provided by the user of the component." + </P> + + <P> + "Some Leptonic components use this type for some of their callbacks when the generic approach was not ergonomic. " + "You can also use the Callback type in your own components props. For example:" + </P> + + <Code> + {indoc!(r#" + #[prop(into, optional)] set_value: Option<Callback<String>> + "#)} + </Code> + + <P> + "As seen in the definition. Two generic arguments are available. Use a tuple for T if the callback should receive multiple values. " + "Provide the second type, otherwise defaulting to (), when the callback should return a value. " + </P> + + <Code> + {indoc!(r#" + #[prop(into, optional)] render: Option<Callback<(Scope, MyType), View>> + "#)} + </Code> + + <P>"Create it when instantiating your component using the "<Code inline=true>"create_callback"</Code>" convenience function."</P> + + <Code> + {indoc!(r#" + view! { + <MyComponent set_value=create_callback(move |v| {}) /> + } + "#)} + </Code> + + <P> + "Calling a callback in a component is as simple as this:" + </P> + + <Code> + {indoc!(r#" + set_value.call("foo".to_owned()) + "#)} + </Code> + + <P>"Passing the callback further down to children is no problem."</P> + + <P>"There are two drawbacks though:"</P> + <ul> + <li>"Callbacks cannot take (non-static) references, only owned values."</li> + <li>"If a child requires a callback of slightly changed signature, you have to pay for creating an intermediate callback.."</li> + </ul> + } +} diff --git a/src/pages/documentation/changelog.rs b/src/pages/documentation/changelog.rs new file mode 100644 index 0000000..fe8cc79 --- /dev/null +++ b/src/pages/documentation/changelog.rs @@ -0,0 +1,105 @@ +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageChangelog() -> impl IntoView { + view! { + <H1>"Changelog"</H1> + + <H2>"0.2.0"</H2> + + <H3>"Added:"</H3> + <ul> + <li> + "Added the `Out` type. An enum abstracting over `Callback`s and `WriteSignal`s." + " Components can use this type when it is equally likely that a user will provide either of the previously mentioned types." + " In case of the WriteSignal, the user just wants a new value to be stored." + " In case of a callback, the user wants fine control over how a new value is handled." + " The input component is the first one using it, as mentioned in the `Changed` section." + </li> + <li>"The Select, OptionalSelect and Multiselect components now accept a `class` prop with which custom classes can be attached to a rendered <leptonic-select> element."</li> + <li>"The `Kbd` component together with `KbdShortcut`, displaying keyboard keys and shortcuts."</li> + <li>"The `Chip` component now accepts custom `id`, `class` and `style` props."</li> + <li>"You can now use the new `--slider-bar-background-image` CSS variable to style the bar of a `Slider` with more control. Defaults to `none`. `--slider-bar-background-color` is still the preferred way to style the bar if no image is needed. The image property will overwrite the color."</li> + <li>"Also added `--slider-range-background-color`, `--slider-range-background-image`, `--slider-knob-border-width`, `--slider-knob-border-color`, `--slider-knob-border-style`, `--slider-knob-background-color` and `--slider-knob-halo-background-color`."</li> + <li>"The background color/image of the selection of a `Slider` can now be styled using `--slider-range-background-color`, defaulting to `var(--brand-color)`, and `--slider-range-background-image`, defaulting to `none`."</li> + <li>"Initial version of a `ColorPicker` component."</li> + </ul> + + <H3>"Changed:"</H3> + <ul> + <li>"The DateSelector components on_change prop now takes a Callback instead of a generic function."</li> + <li>"Buttons of type `outlined` now use --button-outlined-[color]-... variables for their styling."</li> + <li>"Buttons of type `filled` now use --button-filled-[color]-... variables for their styling."</li> + <li>"Buttons of type `outlined` and color `primary` now use a dark text color, contrasting the default bright background."</li> + <li>"When using an input of type `Number`, you now have to supply optional `min`, `max` and `step` values which are propagated to the underlying input element."</li> + <li>"The Input `set` prop is now optional."</li> + <li>"The Input `set` prop is no longer generic. It now expects an `Out<String>`, which can either be a `WriteSignal` or a custom `Callback`."</li> + <li>"The Slider and RangerSlider `set_value` props are no longer generic. They now expect an `Out<f64>`, which can either be a `WriteSignal` or a custom `Callback`."</li> + <li>"The Toggle `on_toggle` prop is now called `set_value` and is no longer generic. It now expect an `Out<bool>`, which can either be a `WriteSignal` or a custom `Callback`."</li> + <li>"The TiptapEditor `set_value` prop is no longer generic. It now expect an `Option<Out<TiptapContent>>`, which can either be a `WriteSignal` or a custom `Callback`."</li> + <li> + "All components using custom attributes now render them with a \"data-\" prefix, allowing them to be standard-compliant and distinguishable from well-known / standard attributes." + " `leptonic-theme` styling changed appropriately." + </li> + <li>"Prop `max` of the ProgressBar is now a MaybeSignal."</li> + <li>"Prop `progress` of the ProgressBar is now a MaybeSignal."</li> + <li> + "Prop `title` of the Alert is now a Callback instead of a generic Fn closure." + " Expect the now necessary `create_callback(move |()| {})` when instantiating a component with a callback prop to become a simple `move || {}` after a migration to leptos 0.5!" + </li> + <li>"The Slider `step` prop is now optional, making continuous sliders with maximum precision easier to set up."</li> + <li>"The `Input` component was split into `TextInput`, `PasswordInput` and `NumberInput`. Their `label` prop was renamed to `placeholder`. The `InputType` enum was removed."</li> + <li>"All `Select` components now require a `search_text_provider` prop. The `SelectOption` trait no longer enforces `Display` to be implemented."</li> + </ul> + + <H3>"Fixed:"</H3> + <ul> + <li>"A button with variants now properly respects its disabled state."</li> + <li>"A button with variants now only triggers one of its actions (either main or variant) per interaction."</li> + <li>"Buttons of type `flat` and color `info` now receive correct styling."</li> + <li>"The installation instructions now include a section describing how to enable the required web_sys_unstable_apis opt-in."</li> + </ul> + + <H2>"0.1.0"</H2> + + <P>"Initial release."</P> + + <H3>"Added utilities:"</H3> + <ul> + <li>"Callback types"</li> + <li>"OptionalMaybeSignal type"</li> + <li>"Global event listener contexts"</li> + </ul> + + <H3>"Added components:"</H3> + <ul> + <li>"Root component"</li> + <li>"Skeleton component and styles"</li> + <li>"Stack component and styles"</li> + <li>"Grid component and styles"</li> + <li>"Separator component and styles"</li> + <li>"Tab components and styles"</li> + <li>"Collapsible components and styles"</li> + <li>"AppBar components and styles"</li> + <li>"Drawer components and styles"</li> + <li>"Button component and styles"</li> + <li>"Input component and styles"</li> + <li>"Date selector component and styles"</li> + <li>"Slider component and styles"</li> + <li>"Select component and styles"</li> + <li>"Toggle component and styles"</li> + <li>"Alert component and styles"</li> + <li>"Toast component and styles"</li> + <li>"Modal components and styles"</li> + <li>"Progress component and styles"</li> + <li>"Popover component and styles"</li> + <li>"Chip component and styles"</li> + <li>"Icon component and styles"</li> + <li>"Link component and styles"</li> + <li>"Anchor component and styles"</li> + <li>"Typography components and styles"</li> + <li>"Transition components and styles"</li> + </ul> + } +} diff --git a/src/pages/documentation/chip.rs b/src/pages/documentation/chip.rs new file mode 100644 index 0000000..3e59c6a --- /dev/null +++ b/src/pages/documentation/chip.rs @@ -0,0 +1,88 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageChip() -> impl IntoView { + let (dismissed, set_dismissed) = create_signal(false); + + view! { + <H1>"Chips"</H1> + + <Code> + {indoc!(r#" + <Chip color=ChipColor::Primary>"Primary"</Chip> + "#)} + </Code> + + <Chip color=ChipColor::Primary>"Primary"</Chip> + <Chip color=ChipColor::Secondary>"Secondary"</Chip> + <Chip color=ChipColor::Success>"Success"</Chip> + <Chip color=ChipColor::Info>"Info"</Chip> + <Chip color=ChipColor::Warn>"Warn"</Chip> + <Chip color=ChipColor::Danger>"Danger"</Chip> + + <H2>"Dismissible chips"</H2> + + <P> + "As chips are often used to convey mutable state, we allow chips to be dismissible. " + "Dismissible chips display an "<Code inline=true>"X"</Code>" icon which lets the user dismiss the chip. " + "The component embedding the chip is responsible of actually removing it, e.g. not rendering it again." + </P> + + <Code> + {indoc!(r#" + <Chip color=ChipColor::Secondary dismissible=move |()| set_dismissed.set(true)> + "Dismissible" + </Chip> + "#)} + </Code> + + <Show + when=move || !dismissed.get() + fallback=move || view! { <Button on_click=move |_| set_dismissed.set(false)>"Reveal chip"</Button>} + > + <Chip color=ChipColor::Secondary dismissible=move |_| set_dismissed.set(true)> + "Dismissible" + </Chip> + </Show> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --chip-font-size + --chip-margin + --chip-padding + --chip-border + --chip-border-radius + --chip-primary-text-color + --chip-primary-text-color-hover + --chip-primary-background-color + --chip-primary-background-color-hover + --chip-secondary-text-color + --chip-secondary-text-color-hover + --chip-secondary-background-color + --chip-secondary-background-color-hover + --chip-success-text-color + --chip-success-text-color-hover + --chip-success-background-color + --chip-success-background-color-hover + --chip-info-text-color + --chip-info-text-color-hover + --chip-info-background-color + --chip-info-background-color-hover + --chip-warn-text-color + --chip-warn-text-color-hover + --chip-warn-background-color + --chip-warn-background-color-hover + --chip-danger-text-color + --chip-danger-text-color-hover + --chip-danger-background-color + --chip-danger-background-color-hover + "#)} + </Code> + } +} diff --git a/src/pages/documentation/collapsible.rs b/src/pages/documentation/collapsible.rs new file mode 100644 index 0000000..3f0df96 --- /dev/null +++ b/src/pages/documentation/collapsible.rs @@ -0,0 +1,24 @@ +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageCollapsible() -> impl IntoView { + view! { + <H1>"Collapsibles"</H1> + + <Collapsibles default_on_open=OnOpen::CloseOthers> + <Stack spacing=Size::Em(0.6)> + <Collapsible + header=move || "Header1".into_view() + body=move || view! { "Body1" }.into_view() /> + <Collapsible + header=move || "Header2".into_view() + body=move || view! { "Body2" }.into_view() /> + <Collapsible + header=move || "Header3 - on_open::DoNothing".into_view() + body=move || view! { "Body3" }.into_view() + on_open=OnOpen::DoNothing /> + </Stack> + </Collapsibles> + } +} diff --git a/src/pages/documentation/color_picker.rs b/src/pages/documentation/color_picker.rs new file mode 100644 index 0000000..7cf7871 --- /dev/null +++ b/src/pages/documentation/color_picker.rs @@ -0,0 +1,129 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageColorPicker() -> impl IntoView { + let (hsv, set_hsv) = create_signal(HSV::new()); + + let (hsv_test, set_hsv_test) = create_signal(HSV::new()); + let hsv_test_rgb_preview = Signal::derive(move || hsv_test.get().into_rgb8()); + + view! { + <H1>"Color Picker"</H1> + + <P>"Select colors using the "<Code inline=true>"<ColorPicker>"</Code>" component."</P> + + <Code> + {indoc!(r#" + let (hsv, set_hsv) = create_signal(HSV::new()); + view! { + <ColorPicker hsv=hsv set_hsv=set_hsv/> + } + "#)} + </Code> + + <ColorPicker hsv=hsv set_hsv=set_hsv/> + + <H2>"Parts"</H2> + + <P>"The "<Code inline=true>"<ColorPicker>"</Code>" build on top of a few other components build to help work with colors. You may use them directly and build your own color picker."</P> + + <P>"Let's define a HSV color with a derived RGB representation. We will use them for the next component on this page."</P> + + <Code> + {indoc!(r#" + let (hsv, set_hsv) = create_signal(HSV::new()); + let rgb = Signal::derive(move || hsv.get().into_rgb8()); + "#)} + </Code> + + <H3>ColorPreview</H3> + + <P>"The "<Code inline=true>"<ColorPreview>"</Code>" component simply displays a reactive color patch based on the given RGB color signal."</P> + + <Code> + {indoc!(r#" + view! { + <ColorPreview rgb=rgb style="width: 5em%; height: 5em;"/> + } + "#)} + </Code> + + <ColorPreview rgb=hsv_test_rgb_preview style="width: 5em%; height: 5em;"/> + + <H3>ColorPalette</H3> + + <P> + "The "<Code inline=true>"<ColorPalette>"</Code>" component works on an HSV color signal, " + "displays the color-gradient field for any given hue value and allows selecting new values for " + "saturation (S, x-axis) and value (V, y-axis) of the HSV color by dragging a handle on the displayed surface." + </P> + + <Code> + {indoc!(r#" + view! { + <ColorPalette + hsv=hsv_test + set_saturation=move |s| set_hsv.update(|hsv| hsv.saturation = s) + set_value=move |v| set_hsv.update(|hsv| hsv.value = v) + style="width: 10em; height: 5em;" + /> + } + "#)} + </Code> + + <ColorPalette + hsv=hsv_test + set_saturation=move |s| set_hsv_test.update(|hsv| hsv.saturation = s) + set_value=move |v| set_hsv_test.update(|hsv| hsv.value = v) + style="width: 10em; height: 5em;" + /> + + <H3>HueSlider</H3> + + <P> + "The "<Code inline=true>"<HueSlider>"</Code>" component renders a specialized "<Code inline=true>"<Slider>"</Code>", " + "allowing you to pick a hue, a floating-point value between 0° and 360°. " + "The slider background displays the hue range as a color band, the knob displays the currently selected hue value at maximum saturation and value." + </P> + + <Code> + {indoc!(r#" + view! { + <HueSlider + hue=Signal::derive(move || hsv.get().hue) + set_hue=move |hue| set_hsv.update(|hsv| hsv.hue = hue) + /> + } + "#)} + </Code> + + <HueSlider + hue=Signal::derive(move || hsv_test.get().hue) + set_hue=move |hue| set_hsv_test.update(|hsv| hsv.hue = hue) + /> + + <P>"If you look at the source of Leptonic's <ColorPicker>, you will see that there is not much more to it as what you saw here!"</P> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --color-palette-knob-size + --color-palette-knob-border-width + --color-palette-knob-border-color + --color-palette-knob-border-style + --color-palette-knob-background-color + --color-palette-knob-halo-size + --color-palette-knob-halo-size-while-dragged + --color-palette-knob-halo-opacity + --color-palette-knob-halo-background-color + --color-palette-knob-transition-speed + --color-palette-knob-box-shadow + "#)} + </Code> + } +} diff --git a/src/pages/documentation/date_time.rs b/src/pages/documentation/date_time.rs new file mode 100644 index 0000000..d004311 --- /dev/null +++ b/src/pages/documentation/date_time.rs @@ -0,0 +1,35 @@ +use indoc::indoc; +use leptonic::{datetime::GuideMode, prelude::*}; +use leptos::*; +use time::OffsetDateTime; + +#[component] +pub fn PageDateTime() -> impl IntoView { + view! { + <H1>"Date & Time"</H1> + + <P>"Select dates using the calendar-like "<Code inline=true>"<DateSelector>"</Code>" component."</P> + + <Code> + {indoc!(r#" + <DateSelector value=OffsetDateTime::now_utc() on_change=move |_v| {}/> + "#)} + </Code> + + <DateSelector value=OffsetDateTime::now_utc() on_change=move |_v| {}/> + + <P>"The date selector can also start with the year selection."</P> + + <Code> + {indoc!(r#" + <DateSelector value=OffsetDateTime::now_utc() on_change=move |_v| {} guide_mode=GuideMode::YearFirst/> + "#)} + </Code> + + <DateSelector value=OffsetDateTime::now_utc() on_change=move |_v| {} guide_mode=GuideMode::YearFirst/> + + <H2>"Input fields"</H2> + + <P>"Selecting dates and times through input fields will be supported soon. Stay tuned."</P> + } +} diff --git a/src/pages/documentation/doc_root.rs b/src/pages/documentation/doc_root.rs new file mode 100644 index 0000000..066b5bd --- /dev/null +++ b/src/pages/documentation/doc_root.rs @@ -0,0 +1,365 @@ +use std::fmt::Display; + +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; +use leptos_router::*; + +use crate::app::{AppLayoutContext, AppRoutes}; +use crate::pages::documentation::alert::PageAlert; +use crate::pages::documentation::anchor::PageAnchor; +use crate::pages::documentation::app_bar::PageAppBar; +use crate::pages::documentation::button::PageButton; +use crate::pages::documentation::callback::PageCallback; +use crate::pages::documentation::changelog::PageChangelog; +use crate::pages::documentation::chip::PageChip; +use crate::pages::documentation::collapsible::PageCollapsible; +use crate::pages::documentation::color_picker::PageColorPicker; +use crate::pages::documentation::date_time::PageDateTime; +use crate::pages::documentation::drawer::PageDrawer; +use crate::pages::documentation::grid::PageGrid; +use crate::pages::documentation::icon::PageIcon; +use crate::pages::documentation::input::PageInput; +use crate::pages::documentation::installation::PageInstallation; +use crate::pages::documentation::kbd::PageKbd; +use crate::pages::documentation::link::PageLink; +use crate::pages::documentation::modal::PageModal; +use crate::pages::documentation::overview::PageOverview; +use crate::pages::documentation::popover::PagePopover; +use crate::pages::documentation::progress::PageProgress; +use crate::pages::documentation::select::PageSelect; +use crate::pages::documentation::separator::PageSeparator; +use crate::pages::documentation::skeleton::PageSkeleton; +use crate::pages::documentation::slider::PageSlider; +use crate::pages::documentation::stack::PageStack; +use crate::pages::documentation::tab::PageTab; +use crate::pages::documentation::table::PageTable; +use crate::pages::documentation::themes::PageThemes; +use crate::pages::documentation::tiptap_editor::PageTiptapEditor; +use crate::pages::documentation::toast::PageToast; +use crate::pages::documentation::toggle::PageToggle; +use crate::pages::documentation::transition::PageTransition; +use crate::pages::documentation::typography::PageTypography; +use crate::pages::documentation::usage::PageUsage; +use crate::APP_BAR_HEIGHT; + +#[derive(Debug, Copy, Clone)] +pub enum DocRoutes { + // Getting started + Overview, + Installation, + Usage, + Themes, + Changelog, + + // Layout + Stack, + Grid, + Separator, + Skeleton, + AppBar, + Drawer, + Tab, + Table, + Collapsible, + + // Input + Button, + Input, + TiptapEditor, + DateTime, + Slider, + Select, + Toggle, + ColorPicker, + + // Feedback + Alert, + Toast, + Modal, + Progress, + Popover, + Chip, + Kbd, + + // General + Typography, + Icon, + Link, + Anchor, + Callback, + + // Animation + Transition, + + // Technical + NotFound, +} + +impl DocRoutes { + pub fn route(self) -> &'static str { + match self { + DocRoutes::Overview => "overview", + DocRoutes::Installation => "installation", + DocRoutes::Usage => "usage", + DocRoutes::Themes => "themes", + DocRoutes::Changelog => "changelog", + + DocRoutes::Stack => "stack", + DocRoutes::Grid => "grid", + DocRoutes::Separator => "separator", + DocRoutes::Skeleton => "skeleton", + DocRoutes::AppBar => "app-bar", + DocRoutes::Drawer => "drawer", + DocRoutes::Tab => "tabs", + DocRoutes::Table => "table", + DocRoutes::Collapsible => "collapsible", + + DocRoutes::Button => "button", + DocRoutes::Input => "input", + DocRoutes::TiptapEditor => "tiptap-editor", + DocRoutes::DateTime => "date-time", + DocRoutes::Slider => "slider", + DocRoutes::Select => "select", + DocRoutes::Toggle => "toggle", + DocRoutes::ColorPicker => "color-picker", + + DocRoutes::Alert => "alert", + DocRoutes::Toast => "toast", + DocRoutes::Modal => "modal", + DocRoutes::Progress => "progress", + DocRoutes::Popover => "popover", + DocRoutes::Chip => "chip", + DocRoutes::Kbd => "kbd", + + DocRoutes::Typography => "typography", + DocRoutes::Icon => "icon", + DocRoutes::Link => "link", + DocRoutes::Anchor => "anchor", + DocRoutes::Callback => "callback", + + DocRoutes::Transition => "transition", + DocRoutes::NotFound => "*", // Leptos requires this to be be named "*"! + } + } +} + +/// Required so that `Routes` variants can be used in `<Route path=Routes::Foo ...>` definitions. +impl Display for DocRoutes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.route()) + } +} + +/// Required so that `Routes` variants can be used in `<Link href=Routes::Foo ...>` definitions. +impl ToHref for DocRoutes { + fn to_href(&self) -> Box<dyn Fn() -> String + '_> { + Box::new(move || format!("/{}/{}", AppRoutes::Doc.route(), self.route())) + } +} + +// You can define other routes in their own component. +// Use a #[component(transparent)] that returns a <Route/>. +#[component(transparent)] +pub fn DocRoutes<P>(path: P) -> impl IntoView +where + P: std::fmt::Display, +{ + view! { + <Route path=path view=|| view! { <DocLayout/>}> + <Route path="" view=|| view! { <Redirect path=DocRoutes::Overview/> }/> + <Route path=DocRoutes::Overview view=|| view! { <PageOverview/> }/> + <Route path=DocRoutes::Installation view=|| view! { <PageInstallation/> }/> + <Route path=DocRoutes::Usage view=|| view! { <PageUsage/> }/> + <Route path=DocRoutes::Themes view=|| view! { <PageThemes/> }/> + <Route path=DocRoutes::Changelog view=|| view! { <PageChangelog/> }/> + + <Route path=DocRoutes::Stack view=|| view! { <PageStack/> }/> + <Route path=DocRoutes::Grid view=|| view! { <PageGrid/> }/> + <Route path=DocRoutes::Separator view=|| view! { <PageSeparator/> }/> + <Route path=DocRoutes::Skeleton view=|| view! { <PageSkeleton/> }/> + <Route path=DocRoutes::AppBar view=|| view! { <PageAppBar/> }/> + <Route path=DocRoutes::Drawer view=|| view! { <PageDrawer/> }/> + <Route path=DocRoutes::Tab view=|| view! { <PageTab/> }/> + <Route path=DocRoutes::Table view=|| view! { <PageTable/> }/> + <Route path=DocRoutes::Collapsible view=|| view! { <PageCollapsible/> }/> + + <Route path=DocRoutes::Button view=|| view! { <PageButton/> }/> + <Route path=DocRoutes::Input view=|| view! { <PageInput/> }/> + <Route path=DocRoutes::TiptapEditor view=|| view! { <PageTiptapEditor/> }/> + <Route path=DocRoutes::DateTime view=|| view! { <PageDateTime/> }/> + <Route path=DocRoutes::Slider view=|| view! { <PageSlider/> }/> + <Route path=DocRoutes::Select view=|| view! { <PageSelect/> }/> + <Route path=DocRoutes::Toggle view=|| view! { <PageToggle/> }/> + <Route path=DocRoutes::ColorPicker view=|| view! { <PageColorPicker/> }/> + + <Route path=DocRoutes::Alert view=|| view! { <PageAlert/> }/> + <Route path=DocRoutes::Toast view=|| view! { <PageToast/> }/> + <Route path=DocRoutes::Modal view=|| view! { <PageModal/> }/> + <Route path=DocRoutes::Progress view=|| view! { <PageProgress/> }/> + <Route path=DocRoutes::Popover view=|| view! { <PagePopover/> }/> + <Route path=DocRoutes::Chip view=|| view! { <PageChip/> }/> + <Route path=DocRoutes::Kbd view=|| view! { <PageKbd/> }/> + + <Route path=DocRoutes::Typography view=|| view! { <PageTypography/> }/> + <Route path=DocRoutes::Icon view=|| view! { <PageIcon/> }/> + <Route path=DocRoutes::Link view=|| view! { <PageLink/> }/> + <Route path=DocRoutes::Anchor view=|| view! { <PageAnchor/> }/> + <Route path=DocRoutes::Callback view=|| view! { <PageCallback/> }/> + + <Route path=DocRoutes::Transition view=|| view! { <PageTransition/> }/> + + <Route path=DocRoutes::NotFound view=|| view! { <Redirect path=AppRoutes::NotFound.to_href()() /> }/> + </Route> + } +} + +#[component] +pub fn DocLayout() -> impl IntoView { + let app_layout_context = expect_context::<AppLayoutContext>(); + + let drawer_class = move || match app_layout_context.is_small.get() { + true => "mobile", + false => "", + }; + + let close_doc_drawer_on_mobile = move || { + if app_layout_context.is_small.get_untracked() { + app_layout_context.close_doc_drawer(); + } + }; + + let drawer_content = view! { + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsBook margin=Margin::Right(Size::Em(1.0))></Icon> "Getting started" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Overview class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Overview"</Link> + <Link href=DocRoutes::Installation class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Installation"</Link> + <Link href=DocRoutes::Usage class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Usage"</Link> + <Link href=DocRoutes::Themes class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Themes"</Link> + <Link href=DocRoutes::Changelog class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Changelog"</Link> + </Stack> + </DrawerSection> + + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsColumnsGap margin=Margin::Right(Size::Em(1.0))></Icon> "Layout" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Stack class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Stack"</Link> + <Link href=DocRoutes::Grid class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Grid"</Link> + <Link href=DocRoutes::Separator class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Separator"</Link> + <Link href=DocRoutes::Skeleton class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Skeleton"</Link> + <Link href=DocRoutes::AppBar class="item" on:click=move |_| close_doc_drawer_on_mobile()>"App Bar"</Link> + <Link href=DocRoutes::Drawer class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Drawer"</Link> + <Link href=DocRoutes::Tab class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Tabs"</Link> + <div class="item"> + <Link href=DocRoutes::Table on:click=move |_| close_doc_drawer_on_mobile()>"Table"</Link> + <New/> + </div> + <Link href=DocRoutes::Collapsible class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Collapsible"</Link> + </Stack> + </DrawerSection> + + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsToggles margin=Margin::Right(Size::Em(1.0))></Icon> "Input" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Button class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Button"</Link> + <Link href=DocRoutes::Input class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Input"</Link> + <Link href=DocRoutes::TiptapEditor class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Tiptap editor"</Link> + <Link href=DocRoutes::DateTime class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Date & Time"</Link> + <Link href=DocRoutes::Slider class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Slider"</Link> + <Link href=DocRoutes::Select class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Select"</Link> + <Link href=DocRoutes::Toggle class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Toggle"</Link> + <div class="item"> + <Link href=DocRoutes::ColorPicker on:click=move |_| close_doc_drawer_on_mobile()>"Color Picker"</Link> + <New/> + </div> + </Stack> + </DrawerSection> + + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsChatSquare margin=Margin::Right(Size::Em(1.0))></Icon> "Feedback" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Alert class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Alert"</Link> + <Link href=DocRoutes::Toast class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Toast"</Link> + <Link href=DocRoutes::Modal class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Modal"</Link> + <Link href=DocRoutes::Progress class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Progress"</Link> + <Link href=DocRoutes::Popover class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Popover"</Link> + <Link href=DocRoutes::Chip class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Chip"</Link> + <div class="item"> + <Link href=DocRoutes::Kbd on:click=move |_| close_doc_drawer_on_mobile()>"Kbd"</Link> + <New/> + </div> + </Stack> + </DrawerSection> + + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsCircleSquare margin=Margin::Right(Size::Em(1.0))></Icon> "General" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Typography class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Typography"</Link> + <Link href=DocRoutes::Icon class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Icon"</Link> + <Link href=DocRoutes::Link class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Link"</Link> + <Link href=DocRoutes::Anchor class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Anchor"</Link> + <Link href=DocRoutes::Callback class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Callback"</Link> + </Stack> + </DrawerSection> + + <DrawerSection header=move || view! { + <Icon icon=BsIcon::BsArrowsMove margin=Margin::Right(Size::Em(1.0))></Icon> "Animation" + }> + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="link-stack"> + <Link href=DocRoutes::Transition class="item" on:click=move |_| close_doc_drawer_on_mobile()>"Transitions"</Link> + </Stack> + </DrawerSection> + }; + + view! { + <Box id="doc-layout"> + <Drawer + side=DrawerSide::Left + id="doc-drawer" + shown=Signal::derive(move || !app_layout_context.doc_drawer_closed.get()) + class=drawer_class + style=format!("top: {APP_BAR_HEIGHT}") + > + <Stack orientation=StackOrientation::Vertical spacing=Size::Zero class="menu"> + { drawer_content } + </Stack> + </Drawer> + + <Box id="doc-content"> + // <Outlet/> will show nested child routes. + <Outlet/> + </Box> + </Box> + } +} + +#[component] +pub fn DrawerSection<H, IV>(header: H, children: Children) -> impl IntoView +where + H: Fn() -> IV + 'static, + IV: IntoView + 'static, +{ + view! { + <div class="drawer-section"> + <div class="section-header"> + { header() } + </div> + { children() } + </div> + } +} + +#[component] +pub fn New() -> impl IntoView { + view! { + <Chip style="color: var(--primary-color); background-color: transparent; margin: 0; padding: 0;"> + "NEW" + </Chip> + } +} diff --git a/src/pages/documentation/drawer.rs b/src/pages/documentation/drawer.rs new file mode 100644 index 0000000..09e9a7c --- /dev/null +++ b/src/pages/documentation/drawer.rs @@ -0,0 +1,105 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageDrawer() -> impl IntoView { + let (shown, set_shown) = create_signal(true); + let (shown2, set_shown2) = create_signal(true); + + view! { + <H1>"Drawer"</H1> + + <P> + "The "<Code inline=true>"<Drawer>"</Code>" component is intended to be used as a side menu. It is animated to conditionally move in and out of visibility." + "The required "<Code inline=true>"side"</Code>" prop controls to which side the drawer should move when hiding." + </P> + + <Toggle state=shown set_state=set_shown/> + + <Box style="display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; border: 4px solid gray; width: 100%; height: 20em; overflow: hidden;"> + <Drawer side=DrawerSide::Left shown=shown style="overflow-y: scroll; padding: 0.5em; background-color: var(--brand-color); border-right: 1px solid gray; z-index: 1;"> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Drawer> + <Box style="padding: 0.5em; display: flex; flex-direction: column; overflow-y: scroll; width: 100%; height: 100%;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + </Box> + + <Code> + {indoc!(r#" + <Box style="display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; border: 4px solid gray; width: 100%; height: 20em; overflow: hidden;"> + <Drawer side=DrawerSide::Left shown=shown style="overflow-y: scroll; padding: 0.5em; background-color: var(--brand-color); border-right: 1px solid gray;"> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Drawer> + <Box style="padding: 0.5em; display: flex; flex-direction: column; overflow-y: scroll; width: 100%; height: 100%;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + </Box> + "#)} + </Code> + + <H2>"Layout shifts"</H2> + + <P> + "To avoid layout shifts, you may declare the drawer as absolutely positioned to let it overlay your content when shown. " + "This is especially useful when the menu is only animated on user action on small / mobile screens and fills the whole width of the viewport when shown. " + "When viewing this documentation on a small device, the open- and closeable main and documentation menus are created this way." + </P> + + <Toggle state=shown2 set_state=set_shown2/> + + <Box style="position: relative; display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; border: 4px solid gray; width: 100%; height: 20em; overflow: hidden;"> + <Box style="padding: 0.5em; display: flex; flex-direction: column; overflow-y: scroll; width: 100%; height: 100%;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + <Drawer side=DrawerSide::Right shown=shown2 style="padding: 0.5em; height: 19.5em; overflow: scroll; position: absolute; top: 0; right: 0; background-color: var(--brand-color); border-left: 1px solid gray; z-index: 1;"> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Drawer> + </Box> + + <Code> + {indoc!(r#" + <Box style="position: relative; display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; border: 4px solid gray; width: 100%; height: 20em; overflow: hidden;"> + <Box style="padding: 0.5em; display: flex; flex-direction: column; overflow-y: scroll; width: 100%; height: 100%;"> + <P>"Scroll ↓"</P> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Box> + <Drawer side=DrawerSide::Right shown=shown2 style="padding: 0.5em; height: 19.5em; overflow: scroll; position: absolute; top: 0; right: 0; background-color: var(--brand-color); border-left: 1px solid gray;"> + <Stack spacing=Size::Em(0.5)> + {(0..8).map(|_| view! { <Skeleton height=Size::Em(3.0)/> }).collect_view()} + </Stack> + </Drawer> + </Box> + "#)} + </Code> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --drawer-background-color + --drawer-box-shadow + "#)} + </Code> + } +} diff --git a/src/pages/documentation/grid.rs b/src/pages/documentation/grid.rs new file mode 100644 index 0000000..06d38bb --- /dev/null +++ b/src/pages/documentation/grid.rs @@ -0,0 +1,64 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageGrid() -> impl IntoView { + view! { + <H1>"Grid"</H1> + + <Code> + {indoc!(r#" + <Grid spacing=Size::Em(0.6)> + <Row> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 1"</Skeleton> + </Col> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 2"</Skeleton> + </Col> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 3"</Skeleton> + </Col> + <Col md=3 sm=12 xs=6> + <Skeleton animated=false>"Item 4"</Skeleton> + </Col> + </Row> + <Row> + <Col md=8 sm=6 xs=12> + <Skeleton animated=false>"Item 5"</Skeleton> + </Col> + <Col md=4 sm=6 xs=12> + <Skeleton animated=false>"Item 6"</Skeleton> + </Col> + </Row> + </Grid> + "#)} + </Code> + + <Grid spacing=Size::Em(0.6)> + <Row> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 1"</Skeleton> + </Col> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 2"</Skeleton> + </Col> + <Col md=3 sm=4 xs=6> + <Skeleton animated=false>"Item 3"</Skeleton> + </Col> + <Col md=3 sm=12 xs=6> + <Skeleton animated=false>"Item 4"</Skeleton> + </Col> + </Row> + <Row> + <Col md=8 sm=6 xs=12> + <Skeleton animated=false>"Item 5"</Skeleton> + </Col> + <Col md=4 sm=6 xs=12> + <Skeleton animated=false>"Item 6"</Skeleton> + </Col> + </Row> + </Grid> + } +} diff --git a/src/pages/documentation/icon.rs b/src/pages/documentation/icon.rs new file mode 100644 index 0000000..c0ce207 --- /dev/null +++ b/src/pages/documentation/icon.rs @@ -0,0 +1,48 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; + +#[component] +pub fn PageIcon() -> impl IntoView { + view! { + <H1>"Icons"</H1> + + <P>"Icons are supported through the "<LinkExt target=LinkExtTarget::Blank href="https://github.com/Carlosted/leptos-icons">"https://github.com/Carlosted/leptos-icons"</LinkExt> " crate."</P> + <P>"Feel free to check out the associated icon index site at "<LinkExt target=LinkExtTarget::Blank href="https://carlosted.github.io/icondata/">"https://carlosted.github.io/icondata/"</LinkExt> "."</P> + + <P>"With the dependency set up, adding our required icons as features."</P> + + <Code> + {indoc!(r#" + leptos_icons = { version = "0.0.15", features = [ + "BsFolderFill", + "BsFolder", + ] } + "#)} + </Code> + + <P>"We can simply include these icons like this, using the "<Code inline=true>"<Icon>"</Code> " component provided by this library."</P> + + <Code> + {indoc!(r#" + <Icon icon=BsIcon::BsFolderFill/> + <Icon icon=BsIcon::BsFolder/> + "#)} + </Code> + + <Icon icon=BsIcon::BsFolderFill style="font-size: 6em;"/> + <Icon icon=BsIcon::BsFolder style="font-size: 6em;"/> + + <H2>"Styling"</H2> + + <P>"There are currently no CSS variables exposed, targeting the "<Code inline=true>"<Icon>"</Code> " component."</P> + + <P>"Notes:"</P> + <ul> + <li>"Scale icons by setting the css "<Code inline=true>"font-size"</Code> " attribute."</li> + <li>"Color icons by setting the css "<Code inline=true>"color"</Code> " attribute."</li> + <li>"Some icons may also render different when altering the css "<Code inline=true>"background-color"</Code> " attribute."</li> + </ul> + } +} diff --git a/src/pages/documentation/input.rs b/src/pages/documentation/input.rs new file mode 100644 index 0000000..8d47778 --- /dev/null +++ b/src/pages/documentation/input.rs @@ -0,0 +1,146 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageInput() -> impl IntoView { + let (text, set_text) = create_signal("text".to_owned()); + let (password, set_password) = create_signal("secret".to_owned()); + let (number, set_number) = create_signal(4.2); + let number_string = Signal::derive(move || format!("{:.1}", number.get())); + + let (placeholder_input, set_placeholder_input) = create_signal("".to_owned()); + + view! { + <H1>"Inputs"</H1> + + <P>"Creating an input is as simple as doing the following"</P> + <Code> + {indoc!(r#" + let (text, set_text) = create_signal("text".to_owned()); + view! { + <TextInput get=text set=set_text/> + } + "#)} + </Code> + + <TextInput get=text set=set_text/> + <P style="color: gray; margin-top: 0; font-style: italic;">"Text is: " {move || text.get()}</P> + + <H2>"Types"</H2> + + <P> + "You can use the "<Code inline=true>"InputType"</Code>" enum, to either create a " + <Code inline=true>"Text"</Code> + ", "<Code inline=true>"Password"</Code> + " or "<Code inline=true>"Number"</Code> + " input, "<Code inline=true>"Text"</Code>" being the default type when unspecified." + </P> + + <Code> + {indoc!(r#" + let (password, set_password) = create_signal("secret".to_owned()); + view! { + <PasswordInput get=password set=set_password/> + } + "#)} + </Code> + + <PasswordInput get=password set=set_password/> + <P style="color: gray; margin-top: 0; font-style: italic;">"Password is: " {move || password.get()}</P> + + <P> + "Note that the input is always given back to you as a "<Code inline=true>"String"</Code>" no matter the type." + " When using a number input, you may want to convert the String like this: " + <Code inline=true>"str::parse::<f64>(v.as_str()).ok()"</Code>" to receive an "<Code inline=true>"Option<f64>"</Code> + ", being "<Code inline=true>"None"</Code>" on invalid input." + </P> + + <Code> + {indoc!(r#" + let (number, set_number) = create_signal(Some(42.0)); + let number_string = Signal::derive(move || format!("{:.1}", number.get())); + view! { + <NumberInput min=0.0 max=10.0 step=0.1 + get=number + set=set_number + /> + } + "#)} + </Code> + + <NumberInput min=0.0 max=10.0 step=0.1 + get=number + set=set_number + /> + <P style="color: gray; margin-top: 0; font-style: italic;">"Number is: " {move || number_string.get()}</P> + + <H2>"Value updates"</H2> + + <P> + "An input can be used without providing the "<Code inline=true>"set"</Code>" prop. " + "You will not be notified about changes to the input value. " + "This can be useful when you know that the input will always be "<Code inline=true>"disabled"</Code>" and you never expect changes." + </P> + + <P> + "The "<Code inline=true>"set"</Code>" prop, providing you with new values whenever the inputs content changes" + ", accepts an "<Code inline=true>"Out<String>"</Code> + ", allowing you to either provide a "<Code inline=true>"WriteSignal"</Code>" whose value is set when the input changes" + " or a custom "<Code inline=true>"Callback"</Code>" called whenever the input changes" + ", allowing you to handle values by yourself if required." + </P> + + <P> + "You can define the "<Code inline=true>"set"</Code>" prop in one of the following ways." + </P> + + <Code> + {indoc!(r#" + view! { + <Input get=text set=set_text/> + <Input get=text set=create_callback(move |v: String| set_text.set(v))/> + } + "#)} + </Code> + + <H2>"Placeholder"</H2> + + <P>"You can supply a placeholder to the input. It is shown as when the input is empty."</P> + + <Code> + {indoc!(r#" + let (text, set_text) = create_signal("".to_owned()); + view! { + <TextInput get=text set=set_text placeholder="This is a placeholder"/> + <Button + variant=ButtonVariant::Flat + size=ButtonSize::Small + on_click=move |_| set_text.set("".to_owned())> + "Clear input" + </Button> + } + "#)} + </Code> + + <TextInput get=placeholder_input set=set_placeholder_input placeholder="This is a placeholder"/> + <Button variant=ButtonVariant::Flat size=ButtonSize::Small on_click=move |_| set_placeholder_input.set("".to_owned())>"Clear input"</Button> + + <H2>"Styling"</H2> + + <P>"You may overwrite any of the following CSS variables to meet your styling needs."</P> + + <Code> + {indoc!(r#" + --input-padding + --input-color + --input-background-color + --input-border: + --input-border-bottom + --input-border-radius + --input-min-height + --input-focused-border-color + "#)} + </Code> + } +} diff --git a/src/pages/documentation/installation.rs b/src/pages/documentation/installation.rs new file mode 100644 index 0000000..08899a5 --- /dev/null +++ b/src/pages/documentation/installation.rs @@ -0,0 +1,165 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageInstallation() -> impl IntoView { + view! { + <H1 id="#installation"> + "Installation" + <Anchor href="#installation" title="Direct link to an overview of installation instructions."/> + </H1> + + <P> + "We assume that you already have an app depending on "<Code inline=true>"leptos"</Code>" in version "<Code inline=true>"0.4.3"</Code>" or higher." + </P> + + <P> + "Start by adding "<Code inline=true>"leptonic"</Code>", "<Code inline=true>"leptonic-theme"</Code>" and "<Code inline=true>"leptos-tiptap-build"</Code>" as dependencies of your app. " + "The later ones are "<Code inline=true>"[build-dependencies]"</Code>" as they will only be used in a "<Code inline=true>"build.rs"</Code>" script which we define later." + </P> + + <Code> + {indoc!(r#" + cargo add leptonic + cargo add --build leptonic-theme + cargo add --build leptos-tiptap-build + "#)} + </Code> + + <P> + "Leptonic comes with default styling in form of the "<Code inline=true>"leptonic-theme"</Code>" crate. " + "In order to build your app with these styles, a build script is required. " + </P> + + <P>"Let's create our "<Code inline=true>"build.rs"</Code>" file, generating our theme and copying required JS files."</P> + + <Code> + {indoc!(r#" + use std::io::Write; + + pub fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=Cargo.lock"); + + let root_dir: std::path::PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); + let generated_dir = root_dir.join("generated"); + let js_dir = generated_dir.join("js"); + + leptonic_theme::generate(generated_dir.join("leptonic")); + println!("cargo:warning=theme written"); + + std::fs::create_dir_all(js_dir.clone()).unwrap(); + println!("cargo:warning=js dir created"); + + std::fs::File::create(js_dir.join("tiptap-bundle.min.js")) + .unwrap() + .write_all(leptos_tiptap_build::TIPTAP_BUNDLE_MIN_JS.as_bytes()) + .unwrap(); + println!("cargo:warning=tiptap-bundle.min.js written"); + + std::fs::File::create(js_dir.join("tiptap.js")) + .unwrap() + .write_all(leptos_tiptap_build::TIPTAP_JS.as_bytes()) + .unwrap(); + println!("cargo:warning=tiptap.js written"); + } + "#)} + </Code> + + <P> + "Currently, Leptonic focuses on integration with client-side-rendering and building with Trunk. " + "Let's set up a custom "<Code inline=true>"Trunk.toml"</Code>" file:" + </P> + + <P> + "The "<Code inline=true>"[watch]"</Code>" section is used to ignore changes in the \"./generated\" directory (which our build script writes to). When omitted, Trunk would recompile our app in an endless loop."<br /> + </P> + + <Code> + {indoc!(r#" + [watch] + # Paths to watch. The `build.target`'s parent folder is watched by default. + ignore = [ + # These files are generated from our build.rs script, not excluding them would result in an endless restart-cycle! + # Keep this list in sync with what the build script generates. + "./generated", + ] + + [serve] + address = "127.0.0.1" + port = 4001 + open = false + "#)} + </Code> + + <P>"The styling of our app must include the leptonic themes. Let's ensure that by adding the following line to our "<Code inline=true>"scss/style.scss"</Code>" file. This is the default location for a Trunk-based project. Create the file if you do not have it already."</P> + + <Code> + {indoc!(r#" + @import "../generated/leptonic/leptonic-themes"; + "#)} + </Code> + + <P>"You can overwrite or add styles for a particular theme using a "<Code inline=true>"[data-theme=\"...\"]"</Code>" selector like so:"</P> + + <Code> + {indoc!(r#" + [data-theme="light"] { + --brand-color: #e66956; + } + "#)} + </Code> + + <P>"Make sure that you are using a reasonable index.html file like the following. This should work out of the box when you followed the previous instructions."</P> + + <Code> + {indoc!(r##" + <!DOCTYPE html> + <html lang="en"> + + <head> + <meta charset="UTF-8" /> + + <meta name="description" content="Leptonic" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="theme-color" content="#e66956" /> + + <title>Leptonic + + + + + + + + + + + + + + + + "##)} + + +

+ "Leptonic depends on the ""leptos-use"" crate. Some of the features used require an opt-in." + " In order for your app to compile properly, add a folder named "".cargo"" besides your ""Cargo.toml"" file." + " Place a ""config.toml"" file inside it containing the following content:" +

+ + + {indoc!(r#" + [build] + # `leptonic` depends on some `leptos-use` functions requiring this opt-in. This may change in the future. + rustflags = ["--cfg=web_sys_unstable_apis"] + "#)} + + +

"You should now be ready to use leptonic components in your leptos app. Continue reading the ""Usage"" section, to see how to use your first component."

+ } +} diff --git a/src/pages/documentation/kbd.rs b/src/pages/documentation/kbd.rs new file mode 100644 index 0000000..56427a7 --- /dev/null +++ b/src/pages/documentation/kbd.rs @@ -0,0 +1,106 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use strum::IntoEnumIterator; + +#[component] +pub fn PageKbd() -> impl IntoView { + let all_keys = Key::iter(); + + view! { +

"Keyboard"

+ +

+ "Display labeled keyboard key-caps using the """" component." + " Leptonic provides the ""Key"" enum which provides well-known keys and their display properties." +

+ + + {indoc!(r#" + + "#)} + + + + +

"Shortcuts"

+ +

+ "You may use this component to display a hint to a keyboard shortcut your users can use to interact with your app." + " As shortcuts mostly consist of two or more keys, Leptonic also provide the """ + " component to make this task as easy as possible. Simply provide the keys which must be pressed in order to activate the shortcut." +

+ +

"Note that these component do not listen for key-presses. Their sole purpose is to unify rendering of key caps and shortcuts!"

+ + + {indoc!(r#" + + "#)} + + + + +

"This could also be rendered manually using the following markup."

+ + + {indoc!(r#" + + + + + + "#)} + + + + + + + + +

Keys

+ +

"Here is a list of all keys provided by the ""Key"" enum."

+ + { + all_keys + .into_iter() + .filter(|key| std::mem::discriminant(key) != std::mem::discriminant(&Key::Custom(""))) + .map(|key: Key| view! { + + }) + .collect_view() + } + +

"If you need custom content in a """" element, use the ""Key::Custom(String)"" variant."

+ + + {indoc!(r#" + + "#)} + + + + +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --leptonic-kbd-key-color + --leptonic-kbd-key-background-color + --leptonic-kbd-key-margin + --leptonic-kbd-key-padding + --leptonic-kbd-key-border-radius + --leptonic-kbd-key-border-color + --leptonic-kbd-concatenate-color + --leptonic-kbd-concatenate-background-color + --leptonic-kbd-concatenate-margin + --leptonic-kbd-concatenate-padding + --leptonic-kbd-concatenate-border-radius + "#)} + + } +} diff --git a/src/pages/documentation/link.rs b/src/pages/documentation/link.rs new file mode 100644 index 0000000..0333baf --- /dev/null +++ b/src/pages/documentation/link.rs @@ -0,0 +1,76 @@ +use indoc::{formatdoc, indoc}; +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; +use leptos_router::ToHref; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageLink() -> impl IntoView { + view! { +

"Links"

+ +

"Links bring your users to a different place of your application."

+ +

"Internal links"

+ +

"These links, created with the """" component, use the leptos router under the hood and are meant to direct users to a different location inside your app, as the given ""href"" prop is always considered to be relative to your site."

+ + + {formatdoc!(r#" + + "This is a link to the current route." + + "#, DocRoutes::Link.to_href()())} + + + "This is a link to the current route." + +

"External links"

+ +

"These links, created with the """" component, do not use the leptos router and must be used when directing users to external sources."

+ + + {indoc!(r#" + + + + "#)} + + + + + + +

"Link Buttons"

+ +

"It is likely that you want to render a link in the form of a button. Please respect the HTML standard and do not render a

+

+ + + "Sure?" + "This ia a test modal." + + + + + + + + + + + "Next one" + "This overlays..." + + + + + + + + + +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --modal-color + --modal-background-color + --modal-padding + --modal-font-size + --modal-header-padding + --modal-body-padding + --modal-footer-padding + --modal-border-radius + --modal-box-shadow + "#)} + + } +} + +#[component] +pub fn ConfirmModal( + #[prop(into)] show_when: Signal, + requires_confirmation_of: String, + on_accept: A, + on_cancel: C, +) -> impl IntoView +where + A: Fn() + Copy + 'static, + C: Fn() + Copy + 'static, +{ + let (input, set_input) = create_signal("".to_owned()); + let required = requires_confirmation_of.clone(); + let confirmed = Signal::derive(move || input.get() == required); + let disabled: OptionalMaybeSignal = Signal::derive(move || !confirmed.get()).into(); + + view! { + + "Delete repository?" + + "Please enter \""{requires_confirmation_of}"\" to confirm." + + + + + + + + + + } +} diff --git a/src/pages/documentation/overview.rs b/src/pages/documentation/overview.rs new file mode 100644 index 0000000..14b3277 --- /dev/null +++ b/src/pages/documentation/overview.rs @@ -0,0 +1,28 @@ +use leptonic::prelude::*; +use leptos::*; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageOverview() -> impl IntoView { + view! { +

"Overview"

+ +

+ "Nigig Limited is a consulatncy for the ""Civil and Structural Engineering"" Projects." +

+ +

+ "It provides \"ready to be used\" components for capturing user input through buttons, inputs fields, select inputs, sliders, date & time inputs or even a rich text editor. " + "Well known mechanisms for providing user-feedback are also available, ranging from modal windows, toast messages and alerts to progress indicators. " + "Leptonic also includes components helping you lay out all these elements on your pages. These include stacks, a full grid system, tabs and collapsibles as well as components for app bars and side drawers. " + "Common tasks such as linking to other parts in or outside your site or including a plethora of icons are also made simple. " +

+ +

"Explore the available components and other features using the side menu to get acquainted with what Leptonic has to offer."

+ +

+ "If you want to dive right in, follow our " "Installation" " instructions." +

+ } +} diff --git a/src/pages/documentation/popover.rs b/src/pages/documentation/popover.rs new file mode 100644 index 0000000..75a2bbd --- /dev/null +++ b/src/pages/documentation/popover.rs @@ -0,0 +1,53 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use leptos_use::use_element_hover; + +#[component] +pub fn PagePopover() -> impl IntoView { + let el = create_node_ref(); + let is_hovered = use_element_hover(el); + + view! { +

"Popover"

+ +

+ "Floating information which can be shown or hidden using a signal." +

+ + + {indoc!(r#" + let el = create_node_ref(); + let is_hovered = use_element_hover(el); + + view! { + + + "1" + + "Hover me!" + + } + "#)} + + +
+ + + "1" + + "Hover me!" + +
+ +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --popover-content-background-color + "#)} + + } +} diff --git a/src/pages/documentation/progress.rs b/src/pages/documentation/progress.rs new file mode 100644 index 0000000..4c5a1e0 --- /dev/null +++ b/src/pages/documentation/progress.rs @@ -0,0 +1,76 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageProgress() -> impl IntoView { + let (progress, set_progress) = create_signal(Some(34.0)); + + view! { +

"Progress"

+ +

+ "Display how much work of an operation is already completed using the """" component." +

+ + + {indoc!(r#" + let (progress, set_progress) = create_signal(Some(34.0)); + + view! { + + } + "#)} + + + + + + + + +

"Indeterminate state"

+ +

+ "As you have probably spotted in the above example, progress is stored as ""Option"". " + "In our earlier example, we always had ""Some(progress)"" which the progress bar displayed for us. " + "Whenever the signal stores a ""None"" value, the progress bar is in the ""indeterminate"" state, " + "telling the user that something is going on, but we cannot exactly say how much of the total work already completed." +

+ + + {indoc!(r#" + + "#)} + + + + +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --progress-bar-height + --progress-bar-border-radius + --progress-bar-background-color + --progress-bar-background-color-transparent + --progress-bar-background-box-shadow + --progress-bar-fill-background-color + --progress-bar-fill-transition + --progress-bar-color + "#)} + + } +} diff --git a/src/pages/documentation/select.rs b/src/pages/documentation/select.rs new file mode 100644 index 0000000..cf6014a --- /dev/null +++ b/src/pages/documentation/select.rs @@ -0,0 +1,278 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Foo { + A, + B, + C, +} + +impl std::fmt::Display for Foo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Foo::A => f.write_str("A"), + Foo::B => f.write_str("B"), + Foo::C => f.write_str("C"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct User { + name: String, + value: ordered_float::OrderedFloat, +} + +impl std::fmt::Display for User { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{} - {}", &self.name, &self.value)) + } +} + +#[component] +pub fn PageSelect() -> impl IntoView { + let (selected, set_selected) = create_signal(Foo::A); + let (selected_opt, set_selected_opt) = create_signal(Option::::None); + let (selected_multi, set_selected_multi) = create_signal(vec![Foo::A, Foo::B]); + let (selected_multi2, set_selected_multi2) = create_signal(vec![Foo::A]); + + let selectable_users = vec![ + User { + name: "Tom".to_owned(), + value: ordered_float::OrderedFloat(1.0), + }, + User { + name: "Bob".to_owned(), + value: ordered_float::OrderedFloat(42.0), + }, + ]; + + let (selected_user, set_selected_user) = create_signal(selectable_users[0].clone()); + + view! { +

"Selects"

+ +

"Select inputs allow you to choose between different predefined values."

+ +

"Lets assume this type definition, providing us with a set of values to choose from."

+ + + {indoc!(r#" + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + enum Foo { + A, + B, + C, + } + "#)} + + +

"Variants"

+ +

"There are three variants of the select component, accepting different inputs and changing only slightly in its behavior."

+ +

"Select"

+ +

"The simplest form, requiring a selected option to be present the whole time."

+ + + {indoc!(r#" + let (selected, set_selected) = create_signal(Foo::A); + + view! { + + +

"OptionalSelect"

+ +

"As the name implies, this variant stores its chosen value in an " "Option" ", allowing the select to be initialized without a value and optionally allowing the user to deselect the current value."

+ + + {indoc!(r#" + let (selected_opt, set_selected_opt) = create_signal(Option::::None); + + view! { + + } + "#)} + + + + +

"Multiselect"

+ +

"In its simplest form, the Select component can be created with a static list of options to choose from."

+ + + {indoc!(r#" + let (selected_multi, set_selected_multi) = create_signal(vec![Foo::A, Foo::B]); + + view! { + + } + "#)} + + + + +

"Using the ""max"" prop, a maximum number of selectable elements can be specified. Here: 2"

+ + + +

"Keyboard navigation"

+ +

+ "The select component was designed with keyboard navigation in mind. " + "Press ""Tab"" to jump to the next or ""Shift + Tab"" to jump to the previous select. " + "Open the dropdown using ""Enter"". " + "Preselect an available option using the ""ArrowDown"" and ""ArrowUp"" keys. " + "When the dropdown is not open, starting to preselect an element using the arrow keys will open it. " + "Choose an option by pressing ""Enter"". " + "Close the dropdown by pressing ""Escape""." +

+ +

+ "Select options can be searched. When opening the dropdown of available options, focus will automatically jump to the search input, allowing you to type instantly. " + "When closing the dropdown, focus is automatically restored to the select, allowing you to ""Tab"" to the next element." +

+ +

"Customization"

+ +

"Let's define a select component which allows selection from a list of struct values."

+ + + {indoc!(r#" + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct User { + name: String, + value: ordered_float::OrderedFloat, + } + + impl std::fmt::Display for User { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{} - {}", &self.name, &self.value)) + } + } + + let selectable_users = vec![ + User { + name: "Tom".to_owned(), + value: ordered_float::OrderedFloat(1.0), + }, + User { + name: "Bob".to_owned(), + value: ordered_float::OrderedFloat(42.0), + }, + ]; + + let (selected_user, set_selected_user) = create_signal(selectable_users[0].clone()); + + view! { +

"Selected user is: " { move || selected_user.get().to_string() }

+ + +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --select-padding + --select-min-height + --select-selected-color + --select-selected-background-color + --select-selected-border + --select-selected-border-bottom + --select-selected-border-radius + --select-selected-badge-color + --select-selected-badge-background-color + --select-selected-placeholder-color + --select-focused-border-color + --select-dropdown-background-color + --select-dropdown-shadow + --select-search-color + --select-search-background-color + --select-no-items-color + --select-no-items-background-color + --select-item-color + --select-item-background-color + --select-item-padding + --select-item-disabled-background-color + --select-item-disabled-color + --select-item-preselected-background-color + --select-item-hover-background-color + --select-item-selected-background-color + "#)} + + } +} diff --git a/src/pages/documentation/separator.rs b/src/pages/documentation/separator.rs new file mode 100644 index 0000000..31546ab --- /dev/null +++ b/src/pages/documentation/separator.rs @@ -0,0 +1,18 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageSeparator() -> impl IntoView { + view! { +

"Separators"

+ + + {indoc!(r#" + + "#)} + + + + } +} diff --git a/src/pages/documentation/skeleton.rs b/src/pages/documentation/skeleton.rs new file mode 100644 index 0000000..8e024e5 --- /dev/null +++ b/src/pages/documentation/skeleton.rs @@ -0,0 +1,91 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageSkeleton() -> impl IntoView { + view! { +

"Skeletons"

+ +

+ "A skeleton is a placeholder element of a specific shape and size which can be displayed in place of some actual content, " + "whenever, for example, this content cannot be displayed because required data is still being fetched from a network resource. " + "This reduces layout shifts and prepares the user for where content will be visible when available." +

+ + + {indoc!(r#" + + "#)} + + +

+ "An explicit ""width"" property of ""Size::Percent(100.0)"" can be omitting as this is the default width of any skeleton."
+ "The ""height"" property defaults to ""Size::Auto"", so setting an explicit height is alway advised when not embedding children in the skeleton." +

+ +

"The skeleton will render as:"

+ + + +

"Animation"

+ +

+ "By default, skeleton components contain an animation, suggesting that something is waiting to replace this component shortly. " + "If for any reason, this animation is not desired, it can be disabled using the ""animated"" property." +

+ + + {indoc!(r#" + + "#)} + + +

"The skeleton will render as:"

+ + + +

+ "Albeit used quite often these days, I would like to remind you that this concept is only tries to mitigate the problem of slowly loading resources. " + "All that might just not be required, if resources are preloaded, if services providing data do that in a few milliseconds, and so on and so forth... Try avoiding overly aggressive use of the skeleton component." + "But, even if your services respond quickly, keep in mind that the (uncontrollable) user-network-speeds may still result in slow resources." +

+ +

"Children"

+ +

+ "As already mentioned briefly, the skeleton component optionally accepts children, so that content con be rendered if desired. " + "In this case, a specific ""height"" property may not be specified." +

+ + + {indoc!(r#" + + "I am a skeleton!" + + "#)} + + +

"The skeleton will render as:"

+ + + "I am a skeleton!" + + +

"We will encounter these skeletons on other pages of this layout chapter."

+ +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --skeleton-background-color + --skeleton-animation-highlight-color + --skeleton-border-radius + --skeleton-padding + --skeleton-cursor + "#)} + + } +} diff --git a/src/pages/documentation/slider.rs b/src/pages/documentation/slider.rs new file mode 100644 index 0000000..ffc0340 --- /dev/null +++ b/src/pages/documentation/slider.rs @@ -0,0 +1,210 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; + +#[component] +pub fn PageSlider() -> impl IntoView { + let (value1, set_value1) = create_signal(6.0); + let (value2, set_value2) = create_signal(4.2); + let (value3, set_value3) = create_signal(-3.0); + let (value4, set_value4) = create_signal(0.5); + let (value5, set_value5) = create_signal(0.5); + let (range_a, set_range_a) = create_signal(0.5); + let (range_b, set_range_b) = create_signal(0.75); + let (range_a_step, set_range_a_step) = create_signal(2.0); + let (range_b_step, set_range_b_step) = create_signal(4.0); + + view! { +

"Slider"

+ +

"Allow users to adjust a value within a specified range by sliding a handle."

+ +

+ "All sliders require the ""min"", ""max"" and ""step"" properties, specifying the range of values the slider provides. " + "Using smaller step values results in ever so slightly smoother sliders until they can be considered \"continuous\". " + "You may exclude the ""step"" prop altogether to let the sliders use its full ""f64"" precision." +

+ + + {indoc!(r#" + let (value, set_value) = create_signal(6.0); + view! { + + } + "#)} + + +

+ "The slider always operates with ""f64"" values and may suffer from typical IEEE-math rounding problems. " + "We use the ""value_display"" property to specify how a selected value should be rendered." +

+ + + +

"Volume slider"

+ +

"Continuous sliders are perfect when the exact value selected is of no particular interest to your user. For example, when operating a volume slider."

+ + + {indoc!(r#" + let (value, set_value) = create_signal(0.5); + view! { + + + + + + } + "#)} + + + + + + + + +

"Marks"

+ +

+ "Small step values result in lesser selectable values, as only values starting from min and increased by multiples of step are selectable. " + "To help visualize the selectable values of the slider, marks can be automatically generated." +

+ + + {indoc!(r#" + let (value, set_value) = create_signal(6.0); + view! { + + } + "#)} + + + + +

+ "Note that marks are only helpful when dealing with sliders having a limited number of selectable values, meaning ones with small ranges and a big stepping values. " + "Automatic mark generation is currently limited to creating 20 evenly spaced marks. Continuous sliders will not create thousands of them." +

+ +

"Arbitrary ranges"

+ +

"Sliders can use any combination of min, max and step values."

+ + + +

"You can also use a positive value for the ""min"" prop, and a negative value for the ""max"" prop, resulting in a reversed axis."

+ + + +

"Range sliders"

+ +

+ "A range of values can be selected using the ""RangeSlider"" component. " + "The component requires two values and in return provides a slider with two control knobs, allowing you to select a range of values. " + "One knob can be dragged over the other, letting them switch places." +

+ + + {indoc!(r#" + let (value_a, set_value_a) = create_signal(0.5); + let (value_b, set_value_b) = create_signal(0.75); + view! { + + } + "#)} + + + + +

"Range sliders can also use marks, just like the normal slider."

+ + + +

"Keyboard input"

+ +

+ "Slider knobs are keyboard-interactable and can be cycled through using the ""Tab"" key. " + "Manipulation of slider knobs using the error keys will come in a future update." +

+ +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --slider-margin + --slider-bar-height + --slider-bar-background-color + --slider-bar-background-image + --slider-range-height + --slider-range-background-color + --slider-range-background-image + --slider-knob-size + --slider-knob-border-width + --slider-knob-border-color + --slider-knob-border-style + --slider-knob-background-color + --slider-knob-halo-size + --slider-knob-halo-size-while-dragged + --slider-knob-halo-opacity + --slider-knob-halo-background-color + --slider-knob-transition-speed + --slider-knob-box-shadow + --slider-mark-size + --slider-mark-color + --slider-mark-color-in-range + --slider-mark-title-color + --slider-mark-title-color-in-range + "#)} + + } +} diff --git a/src/pages/documentation/stack.rs b/src/pages/documentation/stack.rs new file mode 100644 index 0000000..79ba749 --- /dev/null +++ b/src/pages/documentation/stack.rs @@ -0,0 +1,54 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageStack() -> impl IntoView { + view! { +

"Stack"

+ +

"Vertical stacks"

+ +

"Use a stack to create a container displaying its list of children one after the other while spacing them out by a predefined distance."

+ + + {indoc!(r#" + + "Item 1" + "Item 2" + "Item 3" + + "#)} + + + + "Item 1" + "Item 2" + "Item 3" + + +

"Horizontal stacks"

+ +

+ "A stacks default orientation is ""StackOrientation::Vertical"". " + "You can explicitly set the orientation to be ""StackOrientation::Horizontal"" " + "to let the stack display its children horizontally." +

+ + + {indoc!(r#" + + "Item 1" + "Item 2" + "Item 3" + + "#)} + + + + "Item 1" + "Item 2" + "Item 3" + + } +} diff --git a/src/pages/documentation/tab.rs b/src/pages/documentation/tab.rs new file mode 100644 index 0000000..14049c7 --- /dev/null +++ b/src/pages/documentation/tab.rs @@ -0,0 +1,147 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageTab() -> impl IntoView { + let (test_reactive_label_bool, set_test_reactive_label_bool) = create_signal(false); + view! { +

"Tabs"

+ +

+ """ allow you to spread out your UI components into multiple pages, where only one page is shown at any given time. " + "Every """" inside represents a page with a label to select it. A user can interact with labels to bring the tab associated to it into view." +

+ + + {indoc!(r#" + + "Content of tab 1" + "Content of tab 2" + "Content of tab 3" + + "#)} + + + + "Content of tab 1" + "Content of tab 2" + "Content of tab 3" + + +

"Reactivity"

+ +

+ "Labels can be anything implementing Leptos's ""IntoView"" trait and are therefore as reactive as anything else." +

+ + + {indoc!(r#" + let (bool, set_bool) = create_signal(false); + + view! { + + + + + + "Content of tab 2" + + + } + "#)} + + + + + + + + "Content of tab 2" + + + +

"Nesting"

+ +

+ "Tabs can be nested just as one would expect." +

+ + + {indoc!(r#" + + + + + "This is a nested tab." + + + "This tah is nested as well." + + + + + + "#)} + + + + + + + "This is a nested tab." + + + "This tab is nested as well." + + + + + + +

"When are tabs rendered?"

+ +

+ "You might have spotted a particular behavior in the above example. " + "When switching to the \"Inner 2\" tab, then switching to \"Outer 2\" and back to \"Outer 1\", " + "we still see \"Inner 2\" and not the default tab \"Inner 1\" again." +

+ +

+ "This is where the ""mount"" property comes into play. We had it set to ""Mount::Once"" in all of our examples. " + "There are two variants to choose from:" +

+ +
    +
  • + "Mount::Once" +

    + "Tab content is rendered once. Tabs are simply hidden when not shown." +

    +
  • +
  • + "Mount::WhenShown" +

    + "Tab content is rendered every time a tab is shown. The dom of the tab is unmounted when hidden. " + "This means that there is only ever one tab in the final dom, not requiring any hiding-mechanism as in the ""Mount::Once"" case." +

    +
  • +
+ + + + + + "This is a nested tab." + + + "This tab is nested as well." + + + + + + + //

"Default tab"

+ } +} diff --git a/src/pages/documentation/table.rs b/src/pages/documentation/table.rs new file mode 100644 index 0000000..d8a35d9 --- /dev/null +++ b/src/pages/documentation/table.rs @@ -0,0 +1,100 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageTable() -> impl IntoView { + view! { +

"Table"

+ +

"Tables..."

+ + + {indoc!(r##" + + + + + + + + + + + + + + + + + + // ... + +
"#""Name""Appearance""Num. eyes"
"1""Kevin""Tall""2"
+
+ "##)} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
"#""Name""Appearance""Num. eyes"
"1""Kevin""Tall""2"
"2""Bob""Short""2"
"3""Stuart""Medium""1"
"4""Otto""Round""2"
+
+ +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + // Table wrapper + --table-wrapper-box-shadow-color + + // Table + --table-color + --table-background-color + --table-background-color-on-hover + --table-background-color-of-striped-rows + --table-header-background-color + --table-border-color + --table-cell-box-shadow-on-hover + --table-column-background-if-ordered + --table-header-cell-padding + --table-body-cell-padding + "#)} + + } +} diff --git a/src/pages/documentation/themes.rs b/src/pages/documentation/themes.rs new file mode 100644 index 0000000..a0ad9fa --- /dev/null +++ b/src/pages/documentation/themes.rs @@ -0,0 +1,80 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageThemes() -> impl IntoView { + view! { +

"Theming"

+ +

"From the first day on, Leptonic was built with theming in mind."

+ +

+ "Styles are not attached to components directly, are not injected in any way upon rendering, " + "which means that all components could be describes as \"headless\" in that regard." +

+ +

+ "All styling is provided through the ""leptonic-theme"" crate. " + "When used as a build.rs dependency, this crate can write out SCSS code, styling the Leptonic components in two themes: ""light"" and ""dark""." +

+ +

+ "The """" component already discussed in the ""Usage"" section provides everything required (namely rendering the """" component) to active a theme." +

+ +

+ "A simple theme-switching component, realized using a ""Toggle"", is provided, which can be used simply like this anywhere you want:" +

+ + + {indoc!(r#" + + "#)} + + +

+ "We are using the """" enum here, which describes the two out-of-the-box themes (light and dark). " + "This is not mandatory though as you could create your own theme-defining type and your own theme toggle components." +

+ +

"Customization"

+ +

+ "Having a theme, even better a theme provided by default is, is great, but only if that them can be customized in a meaningful way." +

+ +

+ "All components styles therefore make broad use of CSS variables, with which many aspects of the two default themes can be changed. " + "In every page of this book explaining a component, we hint you to the styles you might want to override. A theme generator may come in the future..." +

+ +

+ "Have a look at this excerpt from this book's main ""style.scss"" file, " + "showing you how we include the leptonic standard themes previously written by our build script " + "and overwrite a few variables to meet our design needs." +

+ + + {indoc!(r#" + @import "../generated/leptonic/leptonic-themes"; + + [data-theme="light"] { + --brand-color: #e66956; + + --drawer-background-color: none; + --drawer-box-shadow: none; + } + + [data-theme="dark"] { + --brand-color: #e66956; + + --drawer-background-color: none; + --drawer-box-shadow: none; + } + "#)} + + } +} diff --git a/src/pages/documentation/tiptap_editor.rs b/src/pages/documentation/tiptap_editor.rs new file mode 100644 index 0000000..c2d5fc8 --- /dev/null +++ b/src/pages/documentation/tiptap_editor.rs @@ -0,0 +1,44 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageTiptapEditor() -> impl IntoView { + let (value, set_value) = create_signal(r#"

This is a simple paragraph ... H1!

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

"#.to_owned()); + let (disabled, set_disabled) = create_signal(false); + + view! { +

"Tiptap editor"

+ +

"Embed a simple WYSIWYG editor into your app."

+ +

+ "The provided editor is a wrapper around a headless Tiptap editor instance obtainable via the leptos-tiptap integration. " + "You may want to look into building you own editor UI!" +

+ +

+ + + { + move || match disabled.get() { + true => "disabled", + false => "enabled", + } + } + +

+ + + {indoc!(r#" + set_value.set(content), + }/> + "#)} + + + set_value.set(content), + }/> + } +} diff --git a/src/pages/documentation/toast.rs b/src/pages/documentation/toast.rs new file mode 100644 index 0000000..1ef22a0 --- /dev/null +++ b/src/pages/documentation/toast.rs @@ -0,0 +1,101 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use strum::IntoEnumIterator; +use uuid::Uuid; + +#[component] +pub fn PageToast() -> impl IntoView { + let (variant, set_variant) = create_signal(ToastVariant::Success); + let (timeout, set_timeout) = create_signal(ToastTimeout::DefaultDelay); + let (header, set_header) = create_signal("Header".to_owned()); + let (body, set_body) = create_signal("Body".to_owned()); + + let toasts = expect_context::(); + + view! { +

"Toasts"

+ + + + + + + + + + {indoc!(r#" + let toasts = expect_context::(); + + view! { + + } + "#)} + + +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --toast-border-radius + --toast-header-border-bottom + --toast-header-padding + --toast-message-padding + --toast-info-header-background + --toast-info-header-color + --toast-info-message-background + --toast-info-message-color + --toast-success-header-background + --toast-success-header-color + --toast-success-message-background + --toast-success-message-color + --toast-warn-header-background + --toast-warn-header-color + --toast-warn-message-background + --toast-warn-message-color + --toast-error-header-background + --toast-error-header-color + --toast-error-message-background + --toast-error-message-color + "#)} + + } +} diff --git a/src/pages/documentation/toggle.rs b/src/pages/documentation/toggle.rs new file mode 100644 index 0000000..2f17663 --- /dev/null +++ b/src/pages/documentation/toggle.rs @@ -0,0 +1,59 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; +use leptos_icons::BsIcon; + +#[component] +pub fn PageToggle() -> impl IntoView { + let (state, set_state) = create_signal(false); + + view! { +

"Toggle"

+ +

"A toggle is a representation of a boolean value."

+ + + {indoc!(r#" + let (state, set_state) = create_signal(false); + + view! { + + } + "#)} + + + + +

"Icons"

+ +

"A toggle can be configured with a pair of icons. One icon being rendered in the off position, the other being rendered in the on position."

+ + + {indoc!(r#" + let (state, set_state) = create_signal(false); + + view! { + + } + "#)} + + + + +

"Variations"

+ +

"The toggle comes in two variants: Sliding and Stationary. Sliding toggles are the default and the ones we have used so far."

+

"Stationary toggles are not animated and only consist of a single circle."

+ + + } +} diff --git a/src/pages/documentation/transition.rs b/src/pages/documentation/transition.rs new file mode 100644 index 0000000..2b51a23 --- /dev/null +++ b/src/pages/documentation/transition.rs @@ -0,0 +1,62 @@ +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageTransition() -> impl IntoView { + let (transition_collapse_h, set_transition_collapse_h) = create_signal(false); + let (transition_collapse_v, set_transition_collapse_v) = create_signal(false); + let (transition_fade, set_transition_fade) = create_signal(false); + //let (transition_grow, set_transition_grow) = create_signal(false); + //let (transition_slide, set_transition_slide) = create_signal(false); + //let (transition_zoom, set_transition_zoom) = create_signal(false); + + view! { +

"Transitions"

+ +

"Transition - Collapse - horizontally"

+ + + "Collapse" + + + + +

"Transition - Collapse - vertically"

+ + + "Collapse" + + + + +

"Transition - Fade"

+ + + "Fade" + + + + + //

"Transition - Grow"

+ // + // + // "Grow" + // + + // + + //

"Transition - Slide"

+ // + // + // "Slide" + // + + // + + //

"Transition - Zoom"

+ // + // + // "Zoom" + // + } +} diff --git a/src/pages/documentation/typography.rs b/src/pages/documentation/typography.rs new file mode 100644 index 0000000..4237981 --- /dev/null +++ b/src/pages/documentation/typography.rs @@ -0,0 +1,72 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +#[component] +pub fn PageTypography() -> impl IntoView { + view! { +

"Typography"

+ + + +

"Typography - H1"

+

"Typography - H2"

+

"Typography - H3"

+

"Typography - H4"

+
"Typography - H5"
+
"Typography - H6"
+ +

"This is a paragraph"

+ + "Typography - Code" + +

+ "This is a paragraph containing an " + "inlined" + " piece of code." +

+ +

"Styling"

+ +

"You may overwrite any of the following CSS variables to meet your styling needs."

+ + + {indoc!(r#" + --typography-font-family + --typography-h1-margin + --typography-h1-font-size + --typography-h1-font-weight + --typography-h2-margin + --typography-h2-font-size + --typography-h2-font-weight + --typography-h3-margin + --typography-h3-font-size + --typography-h3-font-weight + --typography-h4-margin + --typography-h4-font-size + --typography-h4-font-weight + --typography-h5-margin + --typography-h5-font-size + --typography-h5-font-weight + --typography-h6-margin + --typography-h6-font-size + --typography-h6-font-weight + --typography-p-margin + --typography-p-font-size + --typography-p-font-weight + --typography-p-line-height + --typography-code-margin + --typography-code-padding + --typography-code-font-size + --typography-code-font-weight + --typography-code-line-height + --typography-code-border-radius + --typography-code-background-color + --typography-code-color + --typography-inline-code-margin + --typography-inline-code-padding + --typography-inline-code-line-height + "#)} + + } +} diff --git a/src/pages/documentation/usage.rs b/src/pages/documentation/usage.rs new file mode 100644 index 0000000..67843f4 --- /dev/null +++ b/src/pages/documentation/usage.rs @@ -0,0 +1,71 @@ +use indoc::indoc; +use leptonic::prelude::*; +use leptos::*; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageUsage() -> impl IntoView { + view! { +

"Usage"

+ +

"Similar to Leptos, this crate comes with a prelude module."

+ +

+ "Just " + "use leptonic::prelude::*;" + " and you are almost ready to go." +

+ +

+ "Leptonic provides the """" component. " + "It is responsible for enabling the ""Theming"", ""Modal"" and " + "Toast"" functionality of Leptonic as well as providing global event-listening capabilities." + "You have to include it in your app once, and render all your content inside it. Many Leptonic components require the """" component to be present." +

+ +

+ "Using the component is as simple as this:" +

+ + + {indoc!(r#" + #[component] + pub fn App() -> impl IntoView { + view! { + + "Content goes here :)" + + } + } + "#)} + + +

"Need help?"

+ +

+ "If you get stuck at any point integrating or using Leptonic, these are things you may find helpful: " +

    +
  • "Look for help in the Leptos ""Discord"" server."
  • +
  • "If you think you encountered a bug, open a ticket in our " "repository"
  • +
  • "Compare the implementation of this book at ""GitHub"" with what you currently have."
  • +
+

+ +

"Contribute"

+ +

+ "If you have anything to say about Leptonic, be it a component you miss, a feature you feel missing, or a bug you encountered, " + "feel free to contribute back to the project by reaching the community or us through" +

    +
  • "the Leptos ""Discord"" server or "
  • +
  • "the ""Issues"" section of the Leptonic repository."
  • +
+

+ +

+ "Writing a component library is a big undertaking. Feel free to contribute by writing new or updating existing code yourself. " + "As more people are on board, more and better ideas, concepts and implementation details follow. Code contributions are always welcomed!" +

+ } +} diff --git a/src/pages/err404.rs b/src/pages/err404.rs new file mode 100644 index 0000000..811a556 --- /dev/null +++ b/src/pages/err404.rs @@ -0,0 +1,27 @@ +use leptonic::prelude::*; +use leptos::*; + +use crate::app::AppRoutes; + +#[component] +pub fn PageErr404() -> impl IntoView { + view! { + + + +

"404"

+

"Whoops, this page doesn't exist :-("

+ + + + + + + Ferris (Rust mascot, a crab) panicked + +
+
+ } +} diff --git a/src/pages/mod.rs b/src/pages/mod.rs new file mode 100644 index 0000000..cb0dd49 --- /dev/null +++ b/src/pages/mod.rs @@ -0,0 +1,3 @@ +pub mod documentation; +pub mod err404; +pub mod welcome; diff --git a/src/pages/welcome.rs b/src/pages/welcome.rs new file mode 100644 index 0000000..5c5e991 --- /dev/null +++ b/src/pages/welcome.rs @@ -0,0 +1,27 @@ +use leptonic::prelude::*; +use leptos::*; + +use crate::pages::documentation::doc_root::DocRoutes; + +#[component] +pub fn PageWelcome() -> impl IntoView { + view! { + + + +

+ "NIGIG LIMITED" +

+ +

+ "Build blazingly fast, ergonomic sites with an outstanding developer experience." + //"Elevate Web Development with Unleashed Performance and Ergonomic Design using Leptos!" +

+ + // TODO: Investigae: When using AppRoutes::Doc, browser navigation (back) does nothing... + + "Read the docs" + +
+ } +}