├── examples ├── yew │ ├── .gitignore │ ├── _redirects │ ├── rustfmt.toml │ ├── src │ │ ├── api.rs │ │ ├── main.rs │ │ ├── pages.rs │ │ ├── components.rs │ │ ├── pages │ │ │ ├── error.rs │ │ │ ├── login_page_one.rs │ │ │ ├── login_page_two.rs │ │ │ ├── contact_page_one.rs │ │ │ ├── login_page_three.rs │ │ │ └── multi_step_page_one.rs │ │ ├── tailwind.css │ │ ├── app.rs │ │ ├── components │ │ │ ├── common.rs │ │ │ ├── login_form_one.rs │ │ │ ├── contact_form_one.rs │ │ │ ├── login_form_two.rs │ │ │ ├── login_form_three.rs │ │ │ └── multi_step_form_one.rs │ │ ├── router.rs │ │ └── api │ │ │ └── auth.rs │ ├── Trunk.toml │ ├── assets │ │ ├── form-one.png │ │ ├── form-two.png │ │ ├── form-three.png │ │ ├── contact-form-one.png │ │ └── multi-step-form-one.png │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── index.html │ ├── Cargo.toml │ ├── README.md │ └── LICENSE ├── leptos │ ├── Cargo.toml │ ├── index.html │ ├── README.md │ └── src │ │ └── main.rs └── dioxus │ ├── Cargo.toml │ ├── Dioxus.toml │ ├── README.md │ └── src │ └── main.rs ├── assets ├── favicon.png ├── logo-new.png ├── pass-demo.gif ├── tel-demo.gif ├── text-demo.gif └── textarea-demo.gif ├── .bumpversion.toml ├── .gitignore ├── .github ├── workflows │ └── rust.yml └── dependabot.yml ├── src ├── lib.rs ├── dioxus.rs └── leptos.rs ├── Cargo.toml ├── README.md ├── DIOXUS.md ├── YEW.md ├── LEPTOS.md └── LICENSE /examples/yew/.gitignore: -------------------------------------------------------------------------------- 1 | target/**/* -------------------------------------------------------------------------------- /examples/yew/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /examples/yew/rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" -------------------------------------------------------------------------------- /examples/yew/src/api.rs: -------------------------------------------------------------------------------- 1 | pub mod auth; 2 | -------------------------------------------------------------------------------- /examples/yew/Trunk.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | tailwindcss = "3.4.17" 3 | -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/logo-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/logo-new.png -------------------------------------------------------------------------------- /assets/pass-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/pass-demo.gif -------------------------------------------------------------------------------- /assets/tel-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/tel-demo.gif -------------------------------------------------------------------------------- /assets/text-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/text-demo.gif -------------------------------------------------------------------------------- /assets/textarea-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/assets/textarea-demo.gif -------------------------------------------------------------------------------- /examples/yew/assets/form-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/examples/yew/assets/form-one.png -------------------------------------------------------------------------------- /examples/yew/assets/form-two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/examples/yew/assets/form-two.png -------------------------------------------------------------------------------- /examples/yew/assets/form-three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/examples/yew/assets/form-three.png -------------------------------------------------------------------------------- /examples/yew/assets/contact-form-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/examples/yew/assets/contact-form-one.png -------------------------------------------------------------------------------- /examples/yew/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/yew/assets/multi-step-form-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensass/input-rs/HEAD/examples/yew/assets/multi-step-form-one.png -------------------------------------------------------------------------------- /.bumpversion.toml: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.2.5 3 | 4 | [bumpversion:file:Cargo.toml] 5 | search = version = "{current_version}" 6 | replace = version = "{new_version}" 7 | -------------------------------------------------------------------------------- /examples/yew/src/main.rs: -------------------------------------------------------------------------------- 1 | mod api; 2 | mod app; 3 | mod components; 4 | mod pages; 5 | mod router; 6 | 7 | fn main() { 8 | yew::Renderer::::new().render(); 9 | } 10 | -------------------------------------------------------------------------------- /examples/yew/src/pages.rs: -------------------------------------------------------------------------------- 1 | pub mod contact_page_one; 2 | pub mod error; 3 | pub mod login_page_one; 4 | pub mod login_page_three; 5 | pub mod login_page_two; 6 | pub mod multi_step_page_one; 7 | -------------------------------------------------------------------------------- /examples/yew/src/components.rs: -------------------------------------------------------------------------------- 1 | pub mod common; 2 | pub mod contact_form_one; 3 | pub mod login_form_one; 4 | pub mod login_form_three; 5 | pub mod login_form_two; 6 | pub mod multi_step_form_one; 7 | -------------------------------------------------------------------------------- /examples/yew/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{rs,html}" 6 | ], 7 | theme: {}, 8 | plugins: [], 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/yew/src/pages/error.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | #[function_component(Error)] 4 | pub fn error() -> Html { 5 | html! { 6 |
7 |

{ "Error Page." }

8 |
9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/yew/src/pages/login_page_one.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | use crate::components::login_form_one::LoginFormOne; 4 | 5 | #[function_component(LoginPageOne)] 6 | pub fn login_page_one() -> Html { 7 | html! { } 8 | } 9 | -------------------------------------------------------------------------------- /examples/yew/src/pages/login_page_two.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | use crate::components::login_form_two::LoginFormTwo; 4 | 5 | #[function_component(LoginPageTwo)] 6 | pub fn login_page_two() -> Html { 7 | html! { } 8 | } 9 | -------------------------------------------------------------------------------- /examples/yew/src/pages/contact_page_one.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | use crate::components::contact_form_one::ContactFormOne; 4 | 5 | #[function_component(ContactPageOne)] 6 | pub fn contact_page_one() -> Html { 7 | html! { } 8 | } 9 | -------------------------------------------------------------------------------- /examples/yew/src/pages/login_page_three.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | use crate::components::login_form_three::LoginFormThree; 4 | 5 | #[function_component(LoginPageThree)] 6 | pub fn login_page_three() -> Html { 7 | html! { } 8 | } 9 | -------------------------------------------------------------------------------- /examples/yew/src/pages/multi_step_page_one.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | use crate::components::multi_step_form_one::MultiStepFormOne; 4 | 5 | #[function_component(MultiStepPageOne)] 6 | pub fn multi_step_page_one() -> Html { 7 | html! { } 8 | } 9 | -------------------------------------------------------------------------------- /examples/yew/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap'); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | @tailwind variants; 7 | 8 | .telephone-input select { 9 | max-width: 55px; 10 | font-size: 14px; 11 | padding: 10px; 12 | } -------------------------------------------------------------------------------- /examples/yew/src/app.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | use yew_router::prelude::*; 3 | 4 | use crate::router::{switch, Route}; 5 | 6 | #[function_component(App)] 7 | pub fn app() -> Html { 8 | html! { 9 | 10 | render={switch} /> 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/leptos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leptos-input-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | console_error_panic_hook = "0.1.7" 8 | input-rs = { path = "../../", features = ["lep"] } 9 | leptos = { version = "0.7.2", features = ["csr"] } 10 | regex = "1.11.1" 11 | serde = "1.0.215" 12 | wasm-logger = "0.2.0" 13 | -------------------------------------------------------------------------------- /examples/leptos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/** 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | **/dist/* 12 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --all-features --verbose 23 | -------------------------------------------------------------------------------- /examples/yew/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | Yew Tailwind Forms 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/dioxus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "input-rs-dioxus-example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | dioxus = { version = "0.6.3", features = ["web"] } 8 | input-rs = { path = "../../", features = ["dio"] } 9 | dioxus-logger = "0.6.2" 10 | regex = "1.11.1" 11 | 12 | [profile] 13 | 14 | [profile.wasm-dev] 15 | inherits = "dev" 16 | opt-level = 1 17 | 18 | [profile.server-dev] 19 | inherits = "dev" 20 | 21 | [profile.android-dev] 22 | inherits = "dev" 23 | -------------------------------------------------------------------------------- /examples/yew/src/components/common.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct LoginUserSchema { 6 | email: String, 7 | password: String, 8 | } 9 | 10 | pub fn validate_email(email: String) -> bool { 11 | let pattern = Regex::new(r"^[^ ]+@[^ ]+\.[a-z]{2,3}$").unwrap(); 12 | pattern.is_match(&email) 13 | } 14 | 15 | pub fn validate_input(field: String) -> bool { 16 | !&field.is_empty() 17 | } 18 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc( 2 | html_logo_url = "https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/logo-new.png", 3 | html_favicon_url = "https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/favicon.png" 4 | )] 5 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 6 | #![doc = include_str!("../README.md")] 7 | 8 | pub(crate) mod countries; 9 | #[cfg(feature = "dio")] 10 | pub mod dioxus; 11 | #[cfg(feature = "lep")] 12 | pub mod leptos; 13 | #[cfg(feature = "yew")] 14 | pub mod yew; 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /examples/dioxus/Dioxus.toml: -------------------------------------------------------------------------------- 1 | [application] 2 | 3 | # App (Project) Name 4 | name = "input-rs" 5 | 6 | # Dioxus App Default Platform 7 | # desktop, web 8 | default_platform = "web" 9 | 10 | # resource (assets) file folder 11 | asset_dir = "assets" 12 | 13 | [web.app] 14 | 15 | # HTML title tag content 16 | title = "input-rs" 17 | 18 | [web.watcher] 19 | 20 | # when watcher trigger, regenerate the `index.html` 21 | reload_html = true 22 | 23 | # which files or dirs will be watcher monitoring 24 | watch_path = ["src", "assets"] 25 | 26 | # include `assets` in web platform 27 | [web.resource] 28 | 29 | # CSS style file 30 | style = [] 31 | 32 | # Javascript code file 33 | script = [] 34 | 35 | [web.resource.dev] 36 | 37 | # Javascript code file 38 | # serve: [dev-server] only 39 | script = [] 40 | -------------------------------------------------------------------------------- /examples/yew/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yew-input-rs" 3 | version = "0.1.0" 4 | authors = ["Mahmoud Harmouch "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | input-rs = { path = "../..", features = ["yew"] } 11 | regex = { version = "1.9.1", default-features = false } 12 | reqwasm = { version = "0.5.0", default-features = false } 13 | serde = { version = "1.0.178", default-features = false } 14 | serde_json = { version = "1.0.104", default-features = false } 15 | wasm-bindgen = { version = "0.2.87", default-features = false } 16 | wasm-bindgen-futures = { version = "0.4.37", default-features = false } 17 | web-sys = { version = "0.3.64", default-features = false } 18 | yew = { version = "0.21.0", features = ["csr"], default-features = false } 19 | yew-router = { version = "0.18.0", default-features = false } 20 | 21 | [profile.release] 22 | codegen-units = 1 23 | opt-level = "z" 24 | lto = "thin" 25 | strip = "symbols" 26 | -------------------------------------------------------------------------------- /examples/yew/src/router.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | use yew_router::prelude::*; 3 | 4 | use crate::pages::contact_page_one::ContactPageOne; 5 | use crate::pages::error::Error; 6 | use crate::pages::login_page_one::LoginPageOne; 7 | use crate::pages::login_page_three::LoginPageThree; 8 | use crate::pages::login_page_two::LoginPageTwo; 9 | use crate::pages::multi_step_page_one::MultiStepPageOne; 10 | 11 | #[derive(Clone, Routable, PartialEq)] 12 | pub enum Route { 13 | #[at("/error")] 14 | Error, 15 | #[at("/login/1")] 16 | LoginPageOne, 17 | #[at("/login/2")] 18 | LoginPageTwo, 19 | #[at("/login/3")] 20 | LoginPageThree, 21 | #[at("/contact/1")] 22 | ContactPageOne, 23 | #[at("/multi-step/1")] 24 | MultiStepPageOne, 25 | } 26 | 27 | pub fn switch(routes: Route) -> Html { 28 | match routes { 29 | Route::LoginPageOne => html! { }, 30 | Route::LoginPageTwo => html! { }, 31 | Route::LoginPageThree => html! { }, 32 | Route::ContactPageOne => html! { }, 33 | Route::MultiStepPageOne => html! { }, 34 | Route::Error => html! { }, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "input-rs" 3 | version = "0.2.5" 4 | edition = "2021" 5 | rust-version = "1.82" 6 | description = "🔤 A highly customizable input component for WASM frameworks like Yew, Dioxus, and Leptos." 7 | license = "Apache-2.0" 8 | keywords = ["input", "yew", "dioxus", "leptos", "input-rs"] 9 | categories = ["web-programming", "science"] 10 | repository = "https://github.com/opensass/input-rs" 11 | documentation = "https://docs.rs/input-rs/" 12 | authors = ["Mahmoud Harmouch "] 13 | exclude = ["assets", "examples"] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | web-sys = { version = "0.3", default-features = false } 19 | yew = { version = "0.21.0", default-features = false, optional = true } 20 | dioxus = { version = "0.6.3", optional = true } 21 | leptos = { version = "0.7.7", optional = true } 22 | 23 | [dev-dependencies] 24 | bump2version = "0.1.4" 25 | regex = "1.10.2" 26 | serde = { version = "1.0.193", features = ["derive"] } 27 | 28 | [features] 29 | yew = ["dep:yew", ] 30 | dio = ["dioxus", ] 31 | lep = ["leptos", ] 32 | 33 | [profile.release] 34 | opt-level = "z" 35 | debug = false 36 | lto = "thin" 37 | codegen-units = 1 38 | panic = "abort" 39 | strip = "symbols" 40 | incremental = false 41 | 42 | [package.metadata.docs.rs] 43 | all-features = true 44 | rustdoc-args = ["--cfg", "docsrs"] 45 | 46 | [badges] 47 | maintenance = { status = "actively-developed" } 48 | -------------------------------------------------------------------------------- /examples/yew/src/api/auth.rs: -------------------------------------------------------------------------------- 1 | use reqwasm::http::{Request, RequestCredentials}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json::json; 4 | 5 | const BASE_URL: &str = "http://localhost:8080/api/v1"; 6 | 7 | #[derive(Serialize, Deserialize, Debug)] 8 | pub struct UserLoginResponse { 9 | pub status: String, 10 | pub token: String, 11 | } 12 | 13 | #[derive(Serialize, Deserialize, Debug)] 14 | pub struct ErrorResponse { 15 | pub status: String, 16 | pub message: String, 17 | } 18 | 19 | pub async fn login_user(username: String, password: String) -> Result { 20 | let response = match Request::post(&format!("{}/auth/login", BASE_URL)) 21 | .header("Content-Type", "application/json") 22 | .credentials(RequestCredentials::Include) 23 | .body( 24 | json!({ 25 | "username": username, 26 | "password": password 27 | }) 28 | .to_string(), 29 | ) 30 | .send() 31 | .await 32 | { 33 | Ok(res) => res, 34 | Err(_) => { 35 | return Err("Network Error!".to_string()); 36 | } 37 | }; 38 | 39 | if response.status() != 200 { 40 | let error_response = response.json::().await; 41 | if let Ok(error_response) = error_response { 42 | return Err(error_response.message); 43 | } else { 44 | return Err(format!("Network Error: {}", response.status())); 45 | } 46 | } 47 | 48 | let res_json = response.json::().await; 49 | match res_json { 50 | Ok(data) => Ok(data), 51 | Err(_) => Err("Failed to parse response".to_string()), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/dioxus/README.md: -------------------------------------------------------------------------------- 1 | # 📚 Input RS Dioxus Tailwind Components 2 | 3 | ## 🛠️ Pre-requisites: 4 | 5 | ### 🐧 **Linux Users** 6 | 7 | 1. **Install [`rustup`](https://www.rust-lang.org/tools/install)**: 8 | 9 | ```sh 10 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | ``` 12 | 13 | 1. Install [`Dioxus CLI`](https://dioxuslabs.com/learn/0.5/getting_started): 14 | 15 | ```sh 16 | cargo install dioxus-cli 17 | ``` 18 | 19 | ### 🪟 **Windows Users** 20 | 21 | 1. **Download and install `rustup`**: Follow the installation instructions [here](https://www.rust-lang.org/tools/install). 22 | 23 | 1. **Install [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)**: Open PowerShell as administrator and run: 24 | 25 | ```sh 26 | wsl --install 27 | ``` 28 | 29 | 1. **Reset Network Stack**: In PowerShell (administrator mode), run: 30 | 31 | ```sh 32 | netsh int ip reset all 33 | netsh winsock reset 34 | ``` 35 | 36 | 1. **Install Linux packages in WSL**: Once inside your WSL terminal, update and install required dependencies: 37 | 38 | ```sh 39 | sudo apt update 40 | sudo apt install build-essential pkg-config libudev-dev 41 | ``` 42 | 43 | 1. Install [`Dioxus CLI`](https://dioxuslabs.com/learn/0.5/getting_started): 44 | 45 | ```sh 46 | cargo install dioxus-cli 47 | ``` 48 | 49 | ## 🚀 Building and Running 50 | 51 | 1. Fork/Clone the GitHub repository. 52 | 53 | ```sh 54 | git clone https://github.com/opensass/input-rs 55 | ``` 56 | 57 | 1. Navigate to the application directory. 58 | 59 | ```sh 60 | cd input-rs/examples/dioxus 61 | ``` 62 | 63 | 1. Run the client: 64 | 65 | ```sh 66 | dx serve --port 3000 67 | ``` 68 | 69 | Navigate to http://localhost:3000 to explore the landing page. 70 | -------------------------------------------------------------------------------- /examples/leptos/README.md: -------------------------------------------------------------------------------- 1 | # 📚 Input RS Leptos Example 2 | 3 | ## 🛠️ Pre-requisites: 4 | 5 | ### 🐧 **Linux Users** 6 | 7 | 1. **Install [`rustup`](https://www.rust-lang.org/tools/install)**: 8 | 9 | ```sh 10 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | ``` 12 | 13 | 1. **Install [`trunk`](https://trunkrs.dev/)**: 14 | 15 | ```sh 16 | cargo install --locked trunk 17 | ``` 18 | 19 | 1. **Add the Wasm target**: 20 | 21 | ```sh 22 | rustup target add wasm32-unknown-unknown 23 | ``` 24 | 25 | ### 🪟 **Windows Users** 26 | 27 | 1. **Download and install `rustup`**: Follow the installation instructions [here](https://www.rust-lang.org/tools/install). 28 | 29 | 1. **Install [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)**: Open PowerShell as administrator and run: 30 | 31 | ```sh 32 | wsl --install 33 | ``` 34 | 35 | 1. **Reset Network Stack**: In PowerShell (administrator mode), run: 36 | 37 | ```sh 38 | netsh int ip reset all 39 | netsh winsock reset 40 | ``` 41 | 42 | 1. **Install Linux packages in WSL**: Once inside your WSL terminal, update and install required dependencies: 43 | 44 | ```sh 45 | sudo apt update 46 | sudo apt install build-essential pkg-config libudev-dev 47 | ``` 48 | 49 | 1. **Install `trunk`**: 50 | 51 | ```sh 52 | cargo install --locked trunk 53 | ``` 54 | 55 | 1. **Add the Wasm target**: 56 | 57 | ```sh 58 | rustup target add wasm32-unknown-unknown 59 | ``` 60 | 61 | ## 🚀 Building and Running 62 | 63 | 1. Fork/Clone the GitHub repository. 64 | 65 | ```bash 66 | git clone https://github.com/opensass/input-rs 67 | ``` 68 | 69 | 1. Navigate to the application directory. 70 | 71 | ```bash 72 | cd input-rs/examples/leptos 73 | ``` 74 | 75 | 1. Run the client: 76 | 77 | ```sh 78 | trunk serve --port 3000 79 | ``` 80 | 81 | Navigate to http://localhost:3000 to explore all available components. 82 | -------------------------------------------------------------------------------- /examples/yew/README.md: -------------------------------------------------------------------------------- 1 | # 📚 Input RS Yew Tailwind Components 2 | 3 | ## 🛠️ Pre-requisites: 4 | 5 | ### 🐧 **Linux Users** 6 | 7 | 1. **Install [`rustup`](https://www.rust-lang.org/tools/install)**: 8 | 9 | ```sh 10 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 11 | ``` 12 | 13 | 1. **Install [`trunk`](https://trunkrs.dev/)**: 14 | 15 | ```sh 16 | cargo install --locked trunk 17 | ``` 18 | 19 | 1. **Add the Wasm target**: 20 | 21 | ```sh 22 | rustup target add wasm32-unknown-unknown 23 | ``` 24 | 25 | ### 🪟 **Windows Users** 26 | 27 | 1. **Download and install `rustup`**: Follow the installation instructions [here](https://www.rust-lang.org/tools/install). 28 | 29 | 1. **Install [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)**: Open PowerShell as administrator and run: 30 | 31 | ```sh 32 | wsl --install 33 | ``` 34 | 35 | 1. **Reset Network Stack**: In PowerShell (administrator mode), run: 36 | 37 | ```sh 38 | netsh int ip reset all 39 | netsh winsock reset 40 | ``` 41 | 42 | 1. **Install Linux packages in WSL**: Once inside your WSL terminal, update and install required dependencies: 43 | 44 | ```sh 45 | sudo apt update 46 | sudo apt install build-essential pkg-config libudev-dev 47 | ``` 48 | 49 | 1. **Install `trunk`**: 50 | 51 | ```sh 52 | cargo install --locked trunk 53 | ``` 54 | 55 | 1. **Add the Wasm target**: 56 | 57 | ```sh 58 | rustup target add wasm32-unknown-unknown 59 | ``` 60 | 61 | ## 🚀 Building and Running 62 | 63 | 1. Fork/Clone the GitHub repository. 64 | 65 | ```bash 66 | git clone https://github.com/opensass/input-rs 67 | ``` 68 | 69 | 1. Navigate to the application directory. 70 | 71 | ```bash 72 | cd input-rs/examples/yew 73 | ``` 74 | 75 | 1. Run the client: 76 | 77 | ```sh 78 | trunk serve --port 3000 79 | ``` 80 | 81 | Navigate to http://localhost:3000 to explore all available components. 82 | 83 | ## 🌀 Tailwind CSS Components 84 | 85 | This section lists components implemented using the [Tailwind CSS](https://tailwindcss.com/) framework. 86 | 87 | ### 🔐 Login Forms 88 | 89 | | ID | Preview | Demo | Localhost | 90 | |---|---|---|---| 91 | | 1 | ![Component 1](./assets/form-one.png) | [![Netlify Status](https://api.netlify.com/api/v1/badges/68d1469e-05ee-4acd-9368-b67d9e53bc2e/deploy-status)](https://tailwind-login-form-1.netlify.app/) | [Localhost](http://localhost:3000/login/1) | 92 | | 2 | ![Component 2](./assets/form-two.png) | [![Netlify Status](https://api.netlify.com/api/v1/badges/68d1469e-05ee-4acd-9368-b67d9e53bc2e/deploy-status)](https://tailwind-login-form-2.netlify.app/) | [Localhost](http://localhost:3000/login/2) | 93 | | 3 | ![Component 3](./assets/form-three.png) | [![Netlify Status](https://api.netlify.com/api/v1/badges/68d1469e-05ee-4acd-9368-b67d9e53bc2e/deploy-status)](https://tailwind-login-form-3.netlify.app/) | [Localhost](http://localhost:3000/login/3) | 94 | 95 | ### 📬 Contact Forms 96 | 97 | | ID | Preview | Demo | Localhost | 98 | |---|---|---|---| 99 | | 1 | ![Component 1](./assets/contact-form-one.png) | [![Netlify Status](https://api.netlify.com/api/v1/badges/68d1469e-05ee-4acd-9368-b67d9e53bc2e/deploy-status)](https://tailwind-contact-form-1.netlify.app/) | [Localhost](http://localhost:3000/contact/1) | 100 | 101 | ### 🔢 Multi-Steps Forms 102 | 103 | | ID | Preview | Demo | Localhost | 104 | |---|---|---|---| 105 | | 1 | ![Component 1](./assets/multi-step-form-one.png) | [![Netlify Status](https://api.netlify.com/api/v1/badges/68d1469e-05ee-4acd-9368-b67d9e53bc2e/deploy-status)](https://tailwind-multi-step-form-1.netlify.app/) | [Localhost](http://localhost:3000/multi-step/1) | 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # 🔤 Input RS 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/input-rs)](https://crates.io/crates/input-rs) 6 | [![Crates.io Downloads](https://img.shields.io/crates/d/input-rs)](https://crates.io/crates/input-rs) 7 | ![Crates.io License](https://img.shields.io/crates/l/input-rs) 8 | [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg?logo=rust&logoColor=white)](https://www.rust-lang.org/) 9 | [![Rust](https://img.shields.io/badge/Rust-1.79%2B-blue.svg)](https://www.rust-lang.org) 10 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/wiseaidev) 11 | 12 | [![Open SASS Discord](https://dcbadge.limes.pink/api/server/b5JbvHW5nv)](https://discord.gg/b5JbvHW5nv) 13 | 14 | 15 | ![logo](https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/logo-new.png) 16 | 17 |
18 | 19 | ## 🎬 Demo 20 | 21 | | Input Type | Demo | 22 | | ---------- | -------------------------------------------- | 23 | | Text | ![text-demo](https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/text-demo.gif) | 24 | | Password | ![pass-demo](https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/pass-demo.gif) | 25 | | Textarea | ![textarea-demo](https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/textarea-demo.gif) | 26 | | Telephone | ![tel-demo](https://raw.githubusercontent.com/opensass/input-rs/refs/heads/main/assets/tel-demo.gif) | 27 | 28 | | Framework | Live Demo | 29 | | --- | --- | 30 | | Yew | [![Netlify Status](https://api.netlify.com/api/v1/badges/45d208ab-8b1b-4608-bcb1-f7d3d049cac5/deploy-status)](https://input-rs.netlify.app) | 31 | | Dioxus | [![Netlify Status](https://api.netlify.com/api/v1/badges/b94e6586-192b-4db8-bc09-b287366c9731/deploy-status)](https://input-rs-dioxus.netlify.app) | 32 | | Leptos | [![Netlify Status](https://api.netlify.com/api/v1/badges/7b6175fa-892b-4a05-9fd5-b425b399e48f/deploy-status)](https://input-rs-leptos.netlify.app) | 33 | 34 | ### 📜 Intro 35 | 36 | A reusable input component built for WASM frameworks like Yew, Dioxus, and Leptos. It's customizable, accessible, and designed to simplify creating dynamic input fields in your applications. 37 | 38 | ## 🤔 Why Use Input RS? 39 | 40 | The following features make Input RS a must-have for your WASM-based projects: 41 | 42 | 1. **🎨 Advanced Customization**: Style inputs with custom classes, inline styles, and themes to suit your app's design. 43 | 1. **🔑 Flexible Input Types**: Supports text, password, phone number, and more with built-in validation. 44 | 1. **⚡ Interactive Callbacks**: Efficiently handle value changes and validity checks with customizable callback functions. 45 | 1. **🧩 Accessibility**: Built-in ARIA attributes for screen readers and other assistive technologies. 46 | 1. **📞 Phone Number Validation**: Dynamic phone number parsing with country code support. 47 | 48 | ## Y Yew Usage 49 | 50 | 51 | Refer to [our guide](https://github.com/opensass/input-rs/blob/main/YEW.md) to integrate this component into your Yew app. 52 | 53 | ## 🧬 Dioxus Usage 54 | 55 | 56 | Refer to [our guide](https://github.com/opensass/input-rs/blob/main/DIOXUS.md) to integrate this component into your Dioxus app. 57 | 58 | ## 🌱 Leptos Usage 59 | 60 | 61 | Refer to [our guide](https://github.com/opensass/input-rs/blob/main/LEPTOS.md) to integrate this component into your Leptos app. 62 | 63 | ## 🤝 Contributions 64 | 65 | Contributions are welcome! Whether it's bug fixes, feature requests, or examples, we would love your help to make Input RS better. 66 | 67 | 1. Fork the repository. 68 | 1. Create a new branch for your feature/bugfix. 69 | 1. Submit a pull request for review. 70 | 71 | ## 📜 License 72 | 73 | Input RS is licensed under the [Apache License](LICENSE). You are free to use, modify, and distribute this library in your projects. 74 | -------------------------------------------------------------------------------- /DIOXUS.md: -------------------------------------------------------------------------------- 1 | # 🧬 Input RS Dioxus Usage 2 | 3 | Adding Input RS to your project is simple: 4 | 5 | 1. Make sure your project is set up with **Dioxus**. Refer to the [Dioxus Getting Started Guide](https://dioxuslabs.com/learn/0.6/getting_started) for setup instructions. 6 | 7 | 1. Add the Input component to your dependencies by including it in your `Cargo.toml` file. 8 | 9 | ```sh 10 | cargo add input-rs --features=dio 11 | ``` 12 | 13 | 1. Import the `Input` components into your Dioxus component and start using it in your app. 14 | 15 | ## 🛠️ Usage 16 | 17 | Incorporating the Input component into your application is easy. Follow these steps: 18 | 19 | 1. Import `Input` into your component: 20 | 21 | ```rust 22 | use dioxus::prelude::*; 23 | use input_rs::dioxus::Input; 24 | ``` 25 | 26 | 1. Use the `Input` component in your application: 27 | 28 | ```rust 29 | use dioxus::prelude::*; 30 | use input_rs::dioxus::Input; 31 | 32 | #[component] 33 | pub fn App() -> Element { 34 | let input_value = use_signal(|| String::new()); 35 | let is_valid = use_signal(|| true); 36 | 37 | fn validate_input(value: String) -> bool { 38 | !value.trim().is_empty() 39 | } 40 | 41 | rsx! { 42 | div { 43 | class: "app-container", 44 | h1 { "Custom Input Example" } 45 | Input { 46 | r#type: "text", 47 | label: "Enter your name:", 48 | id: "name-input", 49 | handle: input_value.clone(), 50 | valid_handle: is_valid.clone(), 51 | validate_function: validate_input, 52 | placeholder: "Type here...", 53 | class: "custom-input", 54 | label_class: "input-label", 55 | field_class: "input-field", 56 | error_message: "This field cannot be empty", 57 | error_class: "input-error", 58 | } 59 | if !is_valid() { 60 | p { class: "error-message", "Please correct the input." } 61 | } 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | ## 🔧 Props 68 | 69 | ### Main Props 70 | 71 | | Property | Type | Description | Default | 72 | | ------------------- | -------------------- | ------------------------------------------------------------------------------------ | -------- | 73 | | `r#type` | `&'static str` | Type of the input (e.g., `text`, `password`, `email`, etc.). | `"text"` | 74 | | `label` | `&'static str` | Label text for the input field. | `""` | 75 | | `id` | `&'static str` | Unique ID for the input element. | `""` | 76 | | `placeholder` | `&'static str` | Placeholder text for the input. | `""` | 77 | | `handle` | `Signal` | Signal handle for the input value. | None | 78 | | `valid_handle` | `Signal` | Signal handle for the validity of the input value. | None | 79 | | `validate_function` | `fn(String) -> bool` | Validation function for the input value. Returns `true` if valid, `false` otherwise. | None | 80 | | `required` | `bool` | Indicates whether the input is required. | `false` | 81 | | `error_message` | `&'static str` | Error message to display if the input is invalid. | `""` | 82 | 83 | ### Styling Props 84 | 85 | ```sh 86 | +-----------------------------+ <-- `class` 87 | | | 88 | | +-----------------------+ | <-- `label_class` 89 | | | Label | | 90 | | +-----------------------+ | 91 | | | 92 | | +-----------------------+ | <-- `field_class` 93 | | | +-------+ +--------+ | | 94 | | | | Input | | Icon | | | <-- `input_class` and `icon_class` 95 | | | +-------+ +--------+ | | 96 | | +-----------------------+ | 97 | | | 98 | | +-----------------------+ | <-- `error_class` (if invalid) 99 | | | Error Message | | 100 | | +-----------------------+ | 101 | +-----------------------------+ 102 | ``` 103 | 104 | | Property | Type | Description | Default | 105 | | ------------- | -------------- | ------------------------------------------ | ------- | 106 | | `class` | `&'static str` | CSS class for the input container. | `""` | 107 | | `label_class` | `&'static str` | CSS class for the label element. | `""` | 108 | | `input_class` | `&'static str` | CSS class applied to the input element. | `""` | 109 | | `field_class` | `&'static str` | CSS class for the input field container. | `""` | 110 | | `error_class` | `&'static str` | CSS class for the error message container. | `""` | 111 | 112 | ### Accessibility Props 113 | 114 | | Property | Type | Description | Default | 115 | | ------------------ | -------------- | ----------------------------------------------- | -------- | 116 | | `aria_label` | `&'static str` | Label for accessibility. | `""` | 117 | | `aria_required` | `&'static str` | Accessibility hint for required status. | `"true"` | 118 | | `aria_invalid` | `&'static str` | Accessibility hint for invalid input. | `"true"` | 119 | | `aria_describedby` | `&'static str` | Links the input to a description (e.g., error). | `""` | 120 | 121 | ## 💡 Notes 122 | 123 | - The `Input` component can be used for various input types like text, password, etc. 124 | - You can bind the component to state hooks for two-way data binding. 125 | - Utilize `validate_function` to validate user input and display error messages. 126 | - The `eye_active` and `eye_disabled` props allow for password visibility toggling with FontAwesome icons. 127 | - Customize the appearance with CSS classes for better integration into your app's design. 128 | -------------------------------------------------------------------------------- /examples/leptos/src/main.rs: -------------------------------------------------------------------------------- 1 | use input_rs::leptos::Input; 2 | use leptos::{prelude::*, task::spawn_local}; 3 | use regex::Regex; 4 | use serde::{Deserialize, Serialize}; 5 | use leptos::logging::log; 6 | 7 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 8 | struct LoginUserSchema { 9 | email: String, 10 | password: String, 11 | } 12 | 13 | fn validate_email(email: String) -> bool { 14 | let pattern = Regex::new(r"^[^ ]+@[^ ]+\.[a-z]{2,3}$").unwrap(); 15 | pattern.is_match(&email) 16 | } 17 | 18 | fn validate_password(password: String) -> bool { 19 | !&password.is_empty() 20 | } 21 | 22 | #[component] 23 | pub fn App() -> impl IntoView { 24 | view! { 25 | 26 | } 27 | } 28 | 29 | #[component] 30 | fn LoginForm() -> impl IntoView { 31 | let error_handle = signal(String::default()); 32 | let error = error_handle.0.get(); 33 | 34 | let email_valid_handle = signal(true); 35 | let email_valid = email_valid_handle.0.get(); 36 | 37 | let password_valid_handle = signal(true); 38 | let password_valid = password_valid_handle.0.get(); 39 | 40 | let email_handle = signal(String::default()); 41 | let email = email_handle.0.get(); 42 | 43 | let password_handle = signal(String::default()); 44 | let password = password_handle.0.get(); 45 | 46 | let onsubmit = move |ev: leptos::ev::SubmitEvent| { 47 | ev.prevent_default(); 48 | 49 | let email_ref = email.clone(); 50 | let password_ref = password.clone(); 51 | let error_handle = error_handle.clone(); 52 | 53 | spawn_local(async move { 54 | if email_valid && password_valid { 55 | // API call 56 | log!( 57 | "Logged in with Email: {}, Password: {}", 58 | email_ref, 59 | password_ref 60 | ); 61 | } else { 62 | error_handle 63 | .1 64 | .set("Please provide a valid email and password!".to_string()); 65 | } 66 | }); 67 | }; 68 | 69 | view! { 70 |
73 | // TODO: Why the flex styling is not applied? 74 | //
75 | //

{"Sign In"}

76 | // { move || if !error.is_empty() { 77 | // Some(view! {
error
}) 78 | // } 79 | // else {None} 80 | // } 81 | //
82 |
83 | 98 | 115 |
116 | 120 |
121 |
122 | {"Not a member?"} 123 | {"Sign Up Now"} 124 |
125 |
126 |
127 | {"Or Sign In With"} 128 |
129 |
130 |
131 |
132 |
133 | 136 | 139 | 142 | 145 |
146 |
147 |
148 |
149 | } 150 | } 151 | 152 | fn main() { 153 | console_error_panic_hook::set_once(); 154 | wasm_logger::init(wasm_logger::Config::default()); 155 | leptos::mount::mount_to_body(|| view! { }) 156 | } 157 | -------------------------------------------------------------------------------- /examples/yew/src/components/login_form_one.rs: -------------------------------------------------------------------------------- 1 | use crate::components::common::{validate_email, validate_input, LoginUserSchema}; 2 | use input_rs::yew::Input; 3 | use regex::Regex; 4 | use wasm_bindgen_futures::spawn_local; 5 | use web_sys::{console, HtmlInputElement, Window}; 6 | use yew::prelude::*; 7 | 8 | use crate::api::auth::login_user; 9 | 10 | #[function_component(LoginFormOne)] 11 | pub fn login_form_one() -> Html { 12 | let error_handle = use_state(String::default); 13 | let error = (*error_handle).clone(); 14 | 15 | let email_valid_handle = use_state(|| true); 16 | let email_valid = (*email_valid_handle).clone(); 17 | 18 | let password_valid_handle = use_state(|| true); 19 | let password_valid = (*password_valid_handle).clone(); 20 | 21 | let email_ref = use_node_ref(); 22 | let email_handle = use_state(|| "sad".to_string()); 23 | let email = (*email_handle).clone(); 24 | 25 | let password_ref = use_node_ref(); 26 | let password_handle = use_state(|| "sad".to_string()); 27 | let password = (*password_handle).clone(); 28 | 29 | let onsubmit = Callback::from(move |event: SubmitEvent| { 30 | event.prevent_default(); 31 | 32 | let email_ref = password.clone(); 33 | let password_ref = password.clone(); 34 | let error_handle = error_handle.clone(); 35 | console::log_1(&format!("Email: {}, Password: {}", email, password).into()); 36 | 37 | spawn_local(async move { 38 | let email_val = email_ref.clone(); 39 | let password_val = password_ref.clone(); 40 | let error_handle = error_handle.clone(); 41 | if email_valid && password_valid { 42 | let response = login_user(email_val.to_string(), password_val.to_string()).await; 43 | match response { 44 | Ok(_) => { 45 | console::log_1(&"success".into()); 46 | let window: Window = web_sys::window().expect("window not available"); 47 | let location = window.location(); 48 | let _ = location.set_href("/error"); 49 | } 50 | Err(err) => { 51 | error_handle.set(err); 52 | } 53 | } 54 | } else { 55 | error_handle.set("Please provide a valid email and password!".into()); 56 | } 57 | }); 58 | }); 59 | 60 | html! { 61 |
64 |
65 |
66 | if !error.is_empty() { 67 |
{error}
68 | } 69 | {"Login"} 70 | 86 | 104 |
105 | 112 |
113 |
114 | {"Not a member?"} 115 | {"Sign Up Now"} 116 |
117 |
118 |
119 | {"Or Sign In With"} 120 |
121 |
122 |
123 |
124 |
125 | 131 | 137 | 143 | 149 |
150 |
151 |
152 |
153 |
154 | } 155 | } 156 | -------------------------------------------------------------------------------- /YEW.md: -------------------------------------------------------------------------------- 1 | # Y Input RS Yew Usage 2 | 3 | Adding Input RS to your project is simple: 4 | 5 | 1. Make sure your project is set up with **Yew**. Follow their [Getting Started Guide](https://yew.rs/docs/getting-started/introduction) for setup instructions. 6 | 7 | 1. Add the Input RS component to your dependencies by including it in your `Cargo.toml` file: 8 | 9 | ```sh 10 | cargo add input-rs --features=yew 11 | ``` 12 | 13 | 1. Import the `Input` component into your Yew component and start using it in your app. 14 | 15 | ## 🛠️ Usage 16 | 17 | Incorporating Yew Input RS into your application is easy. Follow these steps: 18 | 19 | 1. Import the `Input` component into your Yew project: 20 | 21 | ```rust 22 | use yew::prelude::*; 23 | use input_rs::yew::Input; 24 | use regex::Regex; 25 | ``` 26 | 27 | 1. Use the `Input` component within your Yew application: 28 | 29 | ```rust 30 | use yew::prelude::*; 31 | use regex::Regex; 32 | use input_rs::yew::Input; 33 | 34 | fn validate_email(email: String) -> bool { 35 | let pattern = Regex::new(r"^[^ ]+@[^ ]+\.[a-z]{2,3}$").unwrap(); 36 | pattern.is_match(&email) 37 | } 38 | 39 | #[function_component(App)] 40 | pub fn app() -> Html { 41 | let input_email_ref = use_node_ref(); 42 | let input_email_handle = use_state(String::default); 43 | let email_valid_handle = use_state(|| true); 44 | 45 | html! { 46 |
47 | 63 |
64 | } 65 | } 66 | ``` 67 | 68 | ## 🔧 Props 69 | 70 | ### Main Props 71 | 72 | | Property | Type | Description | Default | 73 | | ------------------- | ------------------------ | ---------------------------------------------------------------------- | -------- | 74 | | `type` | `&'static str` | Input type, e.g., `"text"`, `"email"`, `"password"`, `"textarea"`. | `"text"` | 75 | | `name` | `&'static str` | Name attribute for the input element. | `""` | 76 | | `label` | `&'static str` | Text label displayed above the input. | `""` | 77 | | `placeholder` | `&'static str` | Placeholder text inside the input field. | `""` | 78 | | `id` | `&'static str` | ID attribute for the input element. | `""` | 79 | | `required` | `bool` | Indicates whether the field is required. | `false` | 80 | | `handle` | `UseStateHandle` | State handle for managing the value of the input. | None | 81 | | `valid_handle` | `UseStateHandle` | State handle for managing the validity of the input value. | None | 82 | | `validate_function` | `Callback` | Validation function that checks the input value and returns a boolean. | None | 83 | | `error_message` | `&'static str` | Message displayed when the input value is invalid. | `""` | 84 | 85 | ### Styling Props 86 | 87 | ```sh 88 | +-----------------------------+ <-- `class` 89 | | | 90 | | +-----------------------+ | <-- `label_class` 91 | | | Label | | 92 | | +-----------------------+ | 93 | | | 94 | | +-----------------------+ | <-- `field_class` 95 | | | +-------+ +--------+ | | 96 | | | | Input | | Icon | | | <-- `input_class` and `icon_class` 97 | | | +-------+ +--------+ | | 98 | | +-----------------------+ | 99 | | | 100 | | +-----------------------+ | <-- `error_class` (if invalid) 101 | | | Error Message | | 102 | | +-----------------------+ | 103 | +-----------------------------+ 104 | ``` 105 | 106 | | Property | Type | Description | Default | 107 | | ------------- | -------------- | ----------------------------------------------------------------------- | ------- | 108 | | `class` | `&'static str` | CSS class applied to the wrapper container. | `""` | 109 | | `label_class` | `&'static str` | CSS class applied to the label element. | `""` | 110 | | `input_class` | `&'static str` | CSS class applied to the input element. | `""` | 111 | | `field_class` | `&'static str` | CSS class applied to the input wrapper element (includes icons, etc.). | `""` | 112 | | `error_class` | `&'static str` | CSS class applied to the error message container when validation fails. | `""` | 113 | | `icon_class` | `&'static str` | CSS class applied to the optional icon (if specified). | `""` | 114 | 115 | ### Password Icon Props 116 | 117 | | Property | Type | Description | Default | 118 | | -------------- | -------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | 119 | | `eye_active` | `&'static str` | Icon CSS class for showing the "visible" state in password fields. | `"cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye"` | 120 | | `eye_disabled` | `&'static str` | Icon CSS class for showing the "hidden" state in password fields. | `"cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye-slash"` | 121 | 122 | ### Accessibility Props 123 | 124 | | Property | Type | Description | Default | 125 | | ------------------ | -------------- | --------------------------------------------------------------------------- | --------- | 126 | | `aria_label` | `&'static str` | Aria-label for the input element for screen reader users. | `""` | 127 | | `aria_required` | `&'static str` | Specifies whether the input is required for screen readers. | `"true"` | 128 | | `aria_invalid` | `&'static str` | Indicates whether the input value is invalid for screen readers. | `"false"` | 129 | | `aria_describedby` | `&'static str` | ID of the element that describes the input (e.g., error message container). | `""` | 130 | 131 | ## 💡 Notes 132 | 133 | - The `Input` component can be used for various input types like text, password, etc. 134 | - You can bind the component to state hooks for two-way data binding. 135 | - Utilize `validate_function` to validate user input and display error messages. 136 | - The `eye_active` and `eye_disabled` props allow for password visibility toggling with FontAwesome icons. 137 | - Customize the appearance with CSS classes for better integration into your app's design. 138 | -------------------------------------------------------------------------------- /examples/yew/src/components/contact_form_one.rs: -------------------------------------------------------------------------------- 1 | use crate::components::common::{validate_email, validate_input, LoginUserSchema}; 2 | use input_rs::yew::Input; 3 | use serde::{Deserialize, Serialize}; 4 | use wasm_bindgen_futures::spawn_local; 5 | use web_sys::{console, HtmlInputElement, Window}; 6 | use yew::prelude::*; 7 | 8 | use crate::api::auth::login_user; 9 | 10 | #[function_component(ContactFormOne)] 11 | pub fn contact_form_one() -> Html { 12 | let error_handle = use_state(String::default); 13 | let error = (*error_handle).clone(); 14 | 15 | let email_valid_handle = use_state(|| true); 16 | let email_valid = (*email_valid_handle).clone(); 17 | 18 | let name_valid_handle = use_state(|| true); 19 | let name_valid = (*name_valid_handle).clone(); 20 | 21 | let subject_valid_handle = use_state(|| true); 22 | let subject_valid = (*subject_valid_handle).clone(); 23 | 24 | let message_valid_handle = use_state(|| true); 25 | let message_valid = (*message_valid_handle).clone(); 26 | 27 | let input_email_ref = use_node_ref(); 28 | let input_email_handle = use_state(String::default); 29 | let input_email = (*input_email_handle).clone(); 30 | 31 | let input_name_ref = use_node_ref(); 32 | let input_name_handle = use_state(|| "afasfasf".to_string()); 33 | let input_name = (*input_name_handle).clone(); 34 | 35 | let input_subject_ref = use_node_ref(); 36 | let input_subject_handle = use_state(String::default); 37 | let input_subject = (*input_subject_handle).clone(); 38 | 39 | let input_message_ref = use_node_ref(); 40 | let input_message_handle = use_state(String::default); 41 | let input_message = (*input_message_handle).clone(); 42 | 43 | let onsubmit = Callback::from(move |event: SubmitEvent| { 44 | event.prevent_default(); 45 | 46 | let email_ref = input_email.clone(); 47 | let name_ref = input_name.clone(); 48 | let subject_ref = input_subject.clone(); 49 | let message_ref = input_message.clone(); 50 | let error_handle = error_handle.clone(); 51 | console::log_1( 52 | &format!( 53 | "Email: {}, Name: {}, Subject: {}, Message: {}", 54 | input_email, input_name, input_subject, input_message 55 | ) 56 | .into(), 57 | ); 58 | 59 | spawn_local(async move { 60 | let email_val = email_ref.clone(); 61 | let _name_val = name_ref.clone(); 62 | let subject_val = subject_ref.clone(); 63 | let _message_val = message_ref.clone(); 64 | 65 | let error_handle = error_handle.clone(); 66 | if email_valid && name_valid && subject_valid && message_valid { 67 | // TODO: create a contact us endpoint 68 | let response = login_user(email_val.to_string(), subject_val.to_string()).await; 69 | match response { 70 | Ok(_) => { 71 | console::log_1(&"success".into()); 72 | let window: Window = web_sys::window().expect("window not available"); 73 | let location = window.location(); 74 | let _ = location.set_href("/home"); 75 | } 76 | Err(err) => { 77 | error_handle.set(err); 78 | } 79 | } 80 | } else { 81 | error_handle.set("Please provide valid contact information!".into()); 82 | } 83 | }); 84 | }); 85 | 86 | html! { 87 |
88 |
89 |
92 | 103 |
104 | if !error.is_empty() { 105 |
108 | { error } 109 |
110 | } 111 | 112 | { "Contact US" } 113 | 114 | 128 | 142 | 156 | 170 |
171 |