Skip to main content

dioxus_nox_select/
lib.rs

1//! # dioxus-nox-select
2//!
3//! **⚠️ Disclaimer:** This crate was entirely generated by AI (Claude) as part of a
4//! personal learning project. It has not been battle-tested in production and may
5//! contain bugs or unsound abstractions. Use at your own risk and exercise extreme
6//! caution before depending on it in anything that matters.
7//!
8//! Headless Select, Combobox, and Multiselect primitives for Dioxus applications.
9//!
10//! Follows the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)
11//! and [Listbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/)
12//! with proper ARIA roles, keyboard navigation, and data attributes for
13//! styling — shipping **zero visual styles**.
14//!
15//! ## Layers
16//!
17//! | Layer | Items | Dioxus dependency? |
18//! |-------|-------|--------------------|
19//! | **Types** | [`AutoComplete`], [`ItemEntry`], [`GroupEntry`], [`ScoredItem`], [`CustomFilter`] | No |
20//! | **Context** | [`SelectContext`] | Yes (Signals, Memos) |
21//! | **Components** | [`select::Root`], [`select::Trigger`], [`select::Value`], [`select::Input`], [`select::Content`], [`select::Item`], etc. | Yes (RSX, context) |
22//! | **Navigation** | Pure navigation functions (internal) | No |
23//! | **Filter** | Nucleo-powered fuzzy filtering (internal) | No (only nucleo) |
24//!
25//! ## Quick start — Select-only
26//!
27//! ```rust,ignore
28//! use dioxus::prelude::*;
29//! use dioxus_nox_select::*;
30//!
31//! #[component]
32//! fn App() -> Element {
33//!     rsx! {
34//!         select::Root {
35//!             default_value: "apple",
36//!             select::Trigger {
37//!                 select::Value { placeholder: "Choose a fruit…" }
38//!             }
39//!             select::Content {
40//!                 select::Item { value: "apple", label: "Apple",
41//!                     select::ItemText { "Apple" }
42//!                 }
43//!                 select::Item { value: "banana", label: "Banana",
44//!                     select::ItemText { "Banana" }
45//!                 }
46//!                 select::Item { value: "cherry", label: "Cherry",
47//!                     select::ItemText { "Cherry" }
48//!                 }
49//!             }
50//!         }
51//!     }
52//! }
53//! ```
54//!
55//! ## Quick start — Combobox with search
56//!
57//! ```rust,ignore
58//! use dioxus::prelude::*;
59//! use dioxus_nox_select::*;
60//!
61//! #[component]
62//! fn App() -> Element {
63//!     rsx! {
64//!         select::Root {
65//!             autocomplete: AutoComplete::List,
66//!             select::Input { placeholder: "Search fruits…" }
67//!             select::Content {
68//!                 select::Empty { "No results found." }
69//!                 select::Item { value: "apple", label: "Apple", "Apple" }
70//!                 select::Item { value: "banana", label: "Banana", "Banana" }
71//!                 select::Item { value: "cherry", label: "Cherry", "Cherry" }
72//!             }
73//!         }
74//!     }
75//! }
76//! ```
77//!
78//! ## Quick start — Multiselect
79//!
80//! ```rust,ignore
81//! use dioxus::prelude::*;
82//! use dioxus_nox_select::*;
83//!
84//! #[component]
85//! fn App() -> Element {
86//!     rsx! {
87//!         select::Root {
88//!             multiple: true,
89//!             select::Trigger {
90//!                 select::Value { placeholder: "Select tags…" }
91//!             }
92//!             select::Content {
93//!                 select::Item { value: "rust", label: "Rust",
94//!                     select::ItemText { "Rust" }
95//!                     select::ItemIndicator { "✓" }
96//!                 }
97//!                 select::Item { value: "python", label: "Python",
98//!                     select::ItemText { "Python" }
99//!                     select::ItemIndicator { "✓" }
100//!                 }
101//!             }
102//!         }
103//!     }
104//! }
105//! ```
106//!
107//! ## Data attributes
108//!
109//! | Attribute | Component | Values |
110//! |-----------|-----------|--------|
111//! | `data-select-state` | Root | `"open"` \| `"closed"` |
112//! | `data-select-disabled` | Root | `"true"` (when disabled) |
113//! | `data-state` | Trigger, Item | `"open"/"closed"` or `"checked"/"unchecked"` |
114//! | `data-highlighted` | Item | `"true"` (when keyboard-focused) |
115//! | `data-disabled` | Trigger, Item | `"true"` (when disabled) |
116//! | `data-select-placeholder` | Value | `"true"` (when showing placeholder) |
117//! | `data-select-input` | Input | `"true"` |
118//! | `data-select-content` | Content | `"true"` |
119//! | `data-select-item-text` | ItemText | `"true"` |
120//! | `data-select-item-indicator` | ItemIndicator | `"true"` |
121//! | `data-select-group` | Group | `"true"` |
122//! | `data-select-label` | Label | `"true"` |
123//! | `data-select-separator` | Separator | `"true"` |
124//! | `data-select-empty` | Empty | `"true"` |
125//! | `data-select-clear` | ClearButton | `"true"` |
126//!
127//! ## Keyboard navigation — Select-only
128//!
129//! | Key | Popup Closed | Popup Open |
130//! |-----|-------------|------------|
131//! | Space / Enter | Open popup | Select highlighted, close |
132//! | Arrow Down | Open, next | Highlight next |
133//! | Arrow Up | Open, prev | Highlight prev |
134//! | Home | Open, first | Highlight first |
135//! | End | Open, last | Highlight last |
136//! | Escape | — | Close popup |
137//! | Printable char | Type-ahead | Type-ahead |
138//!
139//! ## Keyboard navigation — Combobox
140//!
141//! | Key | Popup Closed | Popup Open |
142//! |-----|-------------|------------|
143//! | Arrow Down | Open, first | Highlight next |
144//! | Arrow Up | — | Highlight prev |
145//! | Alt+Arrow Down | Open | — |
146//! | Enter | — | Select highlighted |
147//! | Escape | — | Close popup |
148//! | Home / End | Cursor | Cursor |
149
150mod components;
151mod context;
152mod filter;
153mod navigation;
154mod types;
155
156#[cfg(test)]
157mod tests;
158
159// Re-export types at crate root for `use dioxus_nox_select::*`.
160pub use context::SelectContext;
161pub use types::{AutoComplete, CustomFilter, GroupEntry, ItemEntry, ScoredItem};
162
163/// Compound component namespace.
164///
165/// ```rust,ignore
166/// select::Root {
167///     select::Trigger { select::Value { placeholder: "Pick one…" } }
168///     select::Content {
169///         select::Item { value: "a", label: "A", "A" }
170///     }
171/// }
172/// ```
173pub mod select {
174    pub use crate::components::{
175        ClearButton, Content, Empty, Group, Input, Item, ItemIndicator, ItemProps, ItemText, Label,
176        Root, Separator, Trigger, Value,
177    };
178}