Skip to main content

detaxine_ui/
lib.rs

1//! # detaxine-ui
2//!
3//! `detaxine-ui` is a Leptos + Tailwind CSS component library compiled to WebAssembly.
4//! It provides a full set of accessible, themeable UI components for building modern
5//! web applications with [Leptos](https://leptos.dev).
6//!
7//! ## Live Demo
8//!
9//! [https://elonaire.github.io/detaxine-ui/](https://elonaire.github.io/detaxine-ui/)
10//!
11//! ## Installation
12//!
13//! Install the CLI and let it scaffold everything for you:
14//!
15//! ```bash
16//! cargo install detaxine-ui-cli
17//! dtx init my-app
18//! cd my-app
19//! trunk serve
20//! ```
21//!
22//! `dtx init` creates a ready-to-run Leptos + Trunk project with `detaxine-ui`
23//! as a dependency, `input.css` configured, and a Tailwind binary in place.
24//!
25//! ## Available Components
26//!
27//! ### Actions
28//!
29//! | Component | Description |
30//! |---|---|
31//! | [`BasicButton`] | Button with optional icon, disabled state, and custom styles |
32//! | [`ButtonGroup`] | Inline group of buttons with shared styles and rounded ends |
33//! | [`Carousel`] | Sliding panel carousel with navigation and dot indicators |
34//!
35//! ### Data Display
36//!
37//! | Component | Description |
38//! |---|---|
39//! | [`Badge`] | Overlaid count or status indicator anchored to a child element |
40//! | [`Chip`] | Removable tag with color temperature variants |
41//! | [`LabelTag`] | Static color-coded label |
42//! | [`Timeline`] | Vertical list of timestamped steps with icon/image/circle heads |
43//! | [`DataTable`] | Sortable, paginated table with optional row actions |
44//! | [`Pagination`] | Page navigation control |
45//!
46//! ### Feedback
47//!
48//! | Component | Description |
49//! |---|---|
50//! | [`BasicModal`] | Portal-based dialog with contextual icons and footer actions |
51//! | [`Popover`] | Viewport-aware floating panel with auto-alignment |
52//! | [`ProgressBar`] | Horizontal progress bar with determinate and indeterminate modes |
53//! | [`CircularProgress`] | Circular progress ring with optional percentage label |
54//! | [`Spinner`] | Animated SVG loading spinner with optional backdrop |
55//!
56//! ### Forms
57//!
58//! | Component | Description |
59//! |---|---|
60//! | [`InputField`] | Text, email, password, number, file and all other HTML input types |
61//! | [`CustomFileInput`] | Styled file picker with chip-based selected file list |
62//! | [`Textarea`] | Multi-line text input |
63//! | [`SelectInput`] | Native `<select>` dropdown |
64//! | [`CustomSelectInput`] | Searchable, chip-based single or multi-select |
65//! | [`CheckboxInputField`] | Single checkbox input |
66//! | [`CheckboxGroup`] | Grouped checkboxes with shared selection state |
67//! | [`RadioInputField`] | Single radio input |
68//! | [`RadioInputGroup`] | Grouped radio inputs with shared selection state |
69//! | [`ToggleSwitch`] | Boolean toggle built on a hidden checkbox |
70//! | [`DatePicker`] | Calendar date picker with min/max and disabled date support |
71//! | [`RichTextEditor`] | Contenteditable rich text editor with formatting toolbar |
72//! | [`ReactiveForm`] | Form wrapper that auto-submits on valid input/change |
73//!
74//! ### Navigation
75//!
76//! | Component | Description |
77//! |---|---|
78//! | [`Breadcrumbs`] | Route-derived breadcrumb trail |
79//! | [`Panel`] | Collapsible panel with clickable title |
80//! | [`Collapse`] | Accordion group of panels |
81//! | [`Tabs`] | Scrollable tabbed view with slot-based content |
82//! | [`Stepper`] | Multi-step form wizard with per-step validation |
83//!
84//! ## Quick Start
85//!
86//! ```rust
87//! use leptos::prelude::*;
88//! use detaxine_ui::components::actions::button::BasicButton;
89//!
90//! #[component]
91//! fn App() -> impl IntoView {
92//!     view! {
93//!         <BasicButton
94//!             button_text="Click me"
95//!             style_ext="bg-primary text-white hover:bg-secondary"
96//!             onclick=Callback::new(move |_| leptos::logging::log!("clicked"))
97//!         />
98//!     }
99//! }
100//! ```
101//!
102//! ## Component Examples
103//!
104//! ### [`BasicButton`] and [`ButtonGroup`]
105//!
106//! ```rust
107//! use leptos::prelude::*;
108//! use detaxine_ui::components::actions::button::{BasicButton, ButtonGroup};
109//!
110//! #[component]
111//! fn Example() -> impl IntoView {
112//!     view! {
113//!         <ButtonGroup style_ext="bg-primary text-white hover:bg-secondary">
114//!             <BasicButton button_text="Save" />
115//!             <BasicButton button_text="Cancel" />
116//!         </ButtonGroup>
117//!     }
118//! }
119//! ```
120//!
121//! ### [`BasicModal`]
122//!
123//! ```rust
124//! use leptos::prelude::*;
125//! use detaxine_ui::components::feedback::modal::modal::{BasicModal, UseCase};
126//!
127//! #[component]
128//! fn Example() -> impl IntoView {
129//!     let is_open = RwSignal::new(false);
130//!     view! {
131//!         <BasicModal
132//!             title="Confirm"
133//!             is_open=is_open
134//!             use_case=UseCase::Confirmation
135//!             on_click_primary=Callback::new(|_| {})
136//!             on_cancel=Callback::new(|_| {})
137//!         >
138//!             <p>"Are you sure?"</p>
139//!         </BasicModal>
140//!     }
141//! }
142//! ```
143//!
144//! ### [`DataTable`]
145//!
146//! ```rust
147//! use leptos::prelude::*;
148//! use std::collections::HashMap;
149//! use detaxine_ui::components::data_display::table::data_table::{Column, DataTable, TableCellData};
150//!
151//! #[component]
152//! fn Example() -> impl IntoView {
153//!     let columns = vec![
154//!         Column::new("Name", true),
155//!         Column::new("Role", false),
156//!     ];
157//!     let rows = vec![{
158//!         let mut row = HashMap::new();
159//!         row.insert("id".to_string(),   TableCellData::String("1".into()));
160//!         row.insert("Name".to_string(), TableCellData::String("Alice".into()));
161//!         row.insert("Role".to_string(), TableCellData::String("Engineer".into()));
162//!         row
163//!     }];
164//!     let data = RwSignal::new((columns, rows));
165//!     view! {
166//!         <DataTable data=data editable=true deletable=true />
167//!     }
168//! }
169//! ```
170//!
171//! ### [`Stepper`]
172//!
173//! ```rust
174//! use leptos::prelude::*;
175//! use leptos::html::Form;
176//! use detaxine_ui::components::navigation::stepper::{Step, Stepper, StepInfo};
177//! use detaxine_ui::components::forms::input::{InputField, InputFieldType};
178//!
179//! #[component]
180//! fn Example() -> impl IntoView {
181//!     view! {
182//!         <Stepper
183//!             step_labels=RwSignal::new(vec![
184//!                 StepInfo::new("Account", None),
185//!                 StepInfo::new("Confirm", None),
186//!             ])
187//!             final_button_text="Finish"
188//!             send_all_form_refs=Callback::new(|_| {})
189//!             is_linear=true
190//!         >
191//!             <Step>
192//!                 <InputField
193//!                     field_type=InputFieldType::Email
194//!                     label="Email"
195//!                     name="email"
196//!                     id_attr="email"
197//!                     required=true
198//!                 />
199//!             </Step>
200//!             <Step>
201//!                 <p>"Review and submit."</p>
202//!             </Step>
203//!         </Stepper>
204//!     }
205//! }
206//! ```
207//!
208//! ### [`Tabs`]
209//!
210//! ```rust
211//! use leptos::prelude::*;
212//! use detaxine_ui::components::navigation::tabs::{Tab, TabLabel, Tabs};
213//!
214//! #[component]
215//! fn Example() -> impl IntoView {
216//!     let labels = RwSignal::new(vec![
217//!         TabLabel::new(ViewFn::from(|| view! { <span>"First"</span> })),
218//!         TabLabel::new(ViewFn::from(|| view! { <span>"Second"</span> })),
219//!     ]);
220//!     view! {
221//!         <Tabs tab_labels=labels>
222//!             <Tab slot><p>"First tab content"</p></Tab>
223//!             <Tab slot><p>"Second tab content"</p></Tab>
224//!         </Tabs>
225//!     }
226//! }
227//! ```
228//!
229//! ### [`Form Handling`]
230//!
231//! `ReactiveForm` wraps your fields and fires a native `submit` event
232//! automatically whenever `checkValidity()` returns true, on every
233//! `input` or `change` event. Read form data with the standard
234//! `FormData` API:
235//!
236//! ```rust
237//! use leptos::prelude::*;
238//! use web_sys::{HtmlFormElement, SubmitEvent};
239//! use serde::{Deserialize, Serialize};
240//! use detaxine_ui::{
241//!     components::forms::{
242//!         checkbox::{CheckboxGroup, CheckboxOption},
243//!         reactive_form::ReactiveForm,
244//!         input::{InputField, InputFieldType}
245//!     },
246//!     utils::forms::deserialize_form,
247//! };
248//! use leptos::wasm_bindgen::JsCast;
249//! use std::collections::HashSet;
250//!
251//! #[derive(Debug, Serialize, Deserialize)]
252//! struct RegistrationForm {
253//!     interests: Vec<String>, // note that this matches the form field name attribute
254//!     email: String, // note that this matches the form field name attribute
255//! }
256//!
257//! #[component]
258//! fn Example() -> impl IntoView {
259//!     let selected = RwSignal::new(HashSet::new());
260//!     let form_ref = NodeRef::new();
261//!     let (registration_form_is_valid, set_registration_form_is_valid) = signal(false);
262//!
263//!     let handle_submit = move |ev: SubmitEvent| {
264//!         ev.prevent_default();
265//!
266//!         let target = ev.target()
267//!             .and_then(|t| t.dyn_into::<HtmlFormElement>().ok());
268//!
269//!         if let Some(form) = target {
270//!             set_registration_form_is_valid.set(form.check_validity());
271//!
272//!             // You can use this in case you want to perform an action
273//!             // based on the condition that the submit event was triggered
274//!             // by, let's say a submit button.
275//!             if let Some(_submitter) = ev.submitter() {
276//!
277//!             }
278//!         }
279//!
280//!         // You might also put this into its own Effect for guaranteed reactivity.
281//!         if registration_form_is_valid.get() {
282//!             // This is how you deserialize a form's value.
283//!             let deserialized_registration_form = deserialize_form::<RegistrationForm>(
284//!                 &form_ref,
285//!                 false,
286//!                 Some(&["interests"]), // note that this matches the form field name
287//!             );
288//!         }
289//!     };
290//!
291//!     view! {
292//!         <ReactiveForm form_ref=form_ref on:submit=handle_submit>
293//!             <CheckboxGroup
294//!                 legend="Interests"
295//!                 name="interests"
296//!                 options=RwSignal::new(vec![
297//!                     CheckboxOption::new("rust", "Rust", None),
298//!                     CheckboxOption::new("leptos", "Leptos", None),
299//!                 ])
300//!                 selected_values=selected
301//!             />
302//!             <InputField
303//!                 field_type=InputFieldType::Email
304//!                 name="email"
305//!                 label="Email"
306//!                 required=true
307//!             />
308//!         </ReactiveForm>
309//!     }
310//! }
311//! ```
312//!
313//! ### [`RichTextEditor`]
314//!
315//! ```rust
316//! use leptos::prelude::*;
317//! use detaxine_ui::components::content::richtext_editor::{ExtraFormatingOption, RichTextEditor};
318//!
319//! #[component]
320//! fn Example() -> impl IntoView {
321//!     let content = RwSignal::new("<p><br></p>".to_string());
322//!     view! {
323//!         <RichTextEditor
324//!             initial_content=content
325//!             id_attr="editor"
326//!             name="body"
327//!             extra_formating_options=vec![
328//!                 ExtraFormatingOption::Heading,
329//!                 ExtraFormatingOption::CodeBlock,
330//!                 ExtraFormatingOption::Lists,
331//!                 ExtraFormatingOption::ImageUpload,
332//!             ]
333//!         />
334//!     }
335//! }
336//! ```
337//!
338//! ## Design Notes
339//!
340//! - **WebAssembly-first** — compiled to WASM via `wasm-pack` / `trunk`, with full
341//!   access to `web_sys` APIs for DOM interaction.
342//! - **Tailwind CSS v4** — styles are distributed via `input.css` using `@apply`
343//!   and `@source inline()` so the library's classes are always included in the
344//!   consumer's build regardless of scanning boundaries.
345//! - **Reactive by default** — all stateful props accept `Signal`, `RwSignal`, or
346//!   `MaybeProp` so components integrate naturally into any Leptos reactive graph.
347//! - **Accessible** — form components forward `id`, `name`, `required`, `readonly`,
348//!   and `aria-*` attributes to the underlying HTML elements.
349//! - **Zero opinion on layout** — components expose `style_ext`, `ext_input_styles`,
350//!   `ext_wrapper_styles`, and similar props so consumers can apply any Tailwind
351//!   utility without forking the library.
352
353pub mod components;
354pub mod utils;
355
356// Re-exports
357pub use icondata;