diff --git a/Cargo.toml b/Cargo.toml index c30f92d..81e3235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,11 @@ exclude = ["/assets", "/examples"] yew = { version = "0.21.0", default-features = false, optional = true } dioxus = { version = "0.6.3", optional = true } leptos = { version = "0.7.7", optional = true } -web-sys = { version = "0.3.77", features = ["Window"]} -gloo-timers = "0.3.0" +web-sys = { version = "0.3.77", features = ["Window", "UrlSearchParams", "Url"]} +gloo-timers = { version = "0.3.0", optional = true } [features] -yew = ["dep:yew"] +yew = ["dep:yew", "gloo-timers"] dio = ["dioxus"] lep = ["leptos"] diff --git a/DIOXUS.md b/DIOXUS.md new file mode 100644 index 0000000..5a06cf8 --- /dev/null +++ b/DIOXUS.md @@ -0,0 +1,159 @@ +# 🧬 Table RS Dioxus Usage + +Adding Table RS to your project is simple: + +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. + +1. Add the **table-rs** library to your dependencies by including it in your `Cargo.toml` file: + + ```sh + cargo add table-rs --features=dio + ``` + +1. Import the `Table` component into your Dioxus application. + +## 🛠️ Usage + +Incorporating Table RS into your Dioxus app involves just a few steps: + +1. Import the `Table` component and types: + + ```rust + use dioxus::prelude::*; + use table_rs::dioxus::table::Table; + use table_rs::dioxus::types::Column; + use maplit::hashmap; + ``` + +1. Use the `Table` component in your Dioxus app: + + ```rust + use dioxus::prelude::*; + use table_rs::dioxus::table::Table; + use table_rs::dioxus::types::Column; + use maplit::hashmap; + + + fn App() -> Element { + let data = vec![ + hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, + hashmap! { "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, + hashmap! { "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }, + ]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: false, + ..Default::default() + }, + ]; + + rsx! { + Table { + data: data, + columns: columns, + } + } + } + ``` + +## 🔧 Props + +### `Table` Component Props + +| Prop | Type | Description | Default | +| ----------- | ------------------------------------- | --------------------------------- | ------- | +| `data` | `Vec>` | The row data to render. | `[]` | +| `columns` | `Vec` | Column definitions. | `[]` | +| `page_size` | `usize` | Number of rows per page. | `10` | +| `loading` | `bool` | Show loading state if true. | `false` | +| `paginate` | `bool` | Enable pagination. | `false` | +| `search` | `bool` | Enable global search input. | `false` | +| `classes` | `TableClasses` | CSS class overrides. | Default | +| `styles` | `HashMap<&'static str, &'static str>` | Inline style overrides. | `{}` | +| `texts` | `TableTexts` | Text customization for UI labels. | Default | + +### `Column` Props + +| Prop | Type | Description | Default | +| ---------- | ---------------------- | ----------------------------------------- | --------------------------------------------------------- | +| `id` | `&'static str` | Column key (used to fetch from row data). | `""` | +| `header` | `&'static str` | Display name in the table header. | `""` | +| `sortable` | `bool` | Allow sorting on this column. | `false` | +| `style` | `Option<&'static str>` | Inline CSS for the header. | Some("padding: 8px; font-weight: 600; text-align: left;") | +| `class` | `Option<&'static str>` | Optional class name for this column. | Some("table-header-cell") | + +### `TableClasses` + +| Prop | Type | Description | Default | +| ------------------- | -------------- | ------------------------------------ | ----------------------- | +| `container` | `&'static str` | Outer container class. | `"table-container"` | +| `table` | `&'static str` | Main table class. | `"table"` | +| `thead` | `&'static str` | Table head (``) class. | `"thead"` | +| `tbody` | `&'static str` | Table body (``) class. | `"tbody"` | +| `row` | `&'static str` | Row (``) class. | `"tr"` | +| `header_cell` | `&'static str` | Header cell (``) class. | `"th"` | +| `body_cell` | `&'static str` | Body cell (``) class. | `"td"` | +| `loading_row` | `&'static str` | Row shown when loading. | `"loading-row"` | +| `empty_row` | `&'static str` | Row shown when no data is available. | `"empty-row"` | +| `search_input` | `&'static str` | Search input field class. | `"search-input"` | +| `pagination` | `&'static str` | Pagination controls wrapper. | `"pagination-controls"` | +| `pagination_button` | `&'static str` | Pagination buttons. | `"pagination-button"` | + +### `TableTexts` + +| Prop | Type | Description | Default | +| -------------------- | -------------- | --------------------------------- | ----------------------------- | +| `loading` | `&'static str` | Text shown when loading. | `"Loading..."` | +| `empty` | `&'static str` | Text when no data is present. | `"No results found"` | +| `search_placeholder` | `&'static str` | Placeholder for search input. | `"Search..."` | +| `previous_button` | `&'static str` | Label for previous page button. | `"Previous"` | +| `next_button` | `&'static str` | Label for next page button. | `"Next"` | +| `page_indicator` | `&'static str` | Format string for page indicator. | `"Page {current} of {total}"` | + +### 🧱 Style/Layout Structure + +```sh ++-------------------------------------------------------------+ +| [container] | <-- class: "table-container" +| | +| +-----------------------------------------------------+ | +| | [search_input] | | <-- class: "search-input" +| | (optional search element) | | +| +-----------------------------------------------------+ | +| | +| +-----------------------------------------------------+ | +| | [table] | | <-- class: "table" +| | +--------------------[thead]--------------------+ | | <-- class: "thead" +| | | Column Headers (e.g., Name, Email) | | | +| | +-----------------------------------------------+ | | +| | +--------------------[tbody]--------------------+ | | <-- class: "tbody" +| | | Data rows (from `data` prop, each row = )| | | +| | +-----------------------------------------------+ | | +| +-----------------------------------------------------+ | +| | +| +-----------------------------------------------------+ | +| | [pagination] | | <-- class: "pagination-controls" +| | Page selector / next-prev buttons (if enabled) | | +| +-----------------------------------------------------+ | ++-------------------------------------------------------------+ +``` + +## 💡 Notes + +- The `data` must match the `id` values defined in each `Column`. +- The `search` prop enables input-based filtering across all columns. +- Pagination is controlled using the `page_size` and `paginate` props. +- Sorting is column-specific via `sortable = true` and `on_sort_column`. +- All style classes can be customized via `TableClasses`. +- All texts are configurable via `TableTexts`. +- The component handles loading and empty states out-of-the-box. +- You can inject additional per-column styling via `Column.style` and `Column.class`. diff --git a/README.md b/README.md index 56293e6..4f64aa1 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ | Framework | Live Demo | | --------- | ------------------------------------------------------------------------------------------------------------------------ | | Yew | [![Netlify Status](https://api.netlify.com/api/v1/badges/4e1494d6-c19a-4a4c-b2d3-47d964214a71/deploy-status)](https://table-rs.netlify.app) | -| Dioxus | TODO | +| Dioxus | [![Netlify Status](https://api.netlify.com/api/v1/badges/4e1494d6-c19a-4a4c-b2d3-47d964214a71/deploy-status)](https://table-dio.netlify.app) | | Leptos | TODO | ## 📜 Intro @@ -44,7 +44,7 @@ The following are some of the reasons why **Table RS** should be your go-to tabl Refer to [our guide](https://github.com/opensass/table-rs/blob/main/YEW.md) to integrate this component into your Yew app. -## 🧬 Dioxus Usage (TODO) +## 🧬 Dioxus Usage Refer to [our guide](https://github.com/opensass/table-rs/blob/main/DIOXUS.md) to integrate this component into your Dioxus app. @@ -54,35 +54,6 @@ Refer to [our guide](https://github.com/opensass/table-rs/blob/main/DIOXUS.md) t Refer to [our guide](https://github.com/opensass/table-rs/blob/main/LEPTOS.md) to integrate this component into your Leptos app. -## 📊 Benchmark: TanStack Table vs Table RS - -| Metric | TanStack Table (React) | Table RS (Yew + WASM) | -|--------------------------------|-----------------------------|----------------------------| -| **Page Load Time (1M rows)** | ~10 seconds | ~2 seconds | -| **Memory Heap Usage** | >3 GB (heap overflow) | ~1.1 GB (stable) | -| **Initial Rendering** | Heavy blocking, slow DOM paint | Efficient, lightweight rendering | -| **Browser Responsiveness** | Delayed interactivity | Smooth after hydration | -| **Sorting Performance** | 2-4s for large columns | Sub-1s due to WASM speed | -| **Search Performance** | Acceptable, but slower | Instantaneous, even at scale | -| **Lighthouse Performance Score** | 49/100 | 60/100 | -| **Scalability** | Limited due to memory and VDOM | Near-native scalability | - -### 🟨 TanStack Table (React) -- Uses Virtual DOM and JS heap to manage massive data. -- Runtime bottlenecks emerge with >100k rows. -- Memory allocation during sorting and filtering can spike to **3GB+**, often leading to **heap overflow** during intensive usage. -- Lighthouse audit shows poor TTI and CPU blocking. - -### 🟩 Table RS (Yew + WASM) -- WASM-compiled logic is highly memory-efficient and deterministic. -- DOM rendering is direct, bypassing React's reconciliation. -- ~1.1 GB of memory heap used even with **1 million rows**. -- Built-in support for search/sort with stable paging. -- No hydration issues (client-only generation). -- Lighthouse performance significantly better, especially in CPU/Memory metrics. - -For large-data UI benchmarks like tables with millions of rows, **`table-rs` in Yew/WASM is a superior choice** compared to React + TanStack. - ## 🤝 Contributions Contributions are welcome! Whether it's bug fixes, feature requests, or examples, we would love your help to make **Table RS** even better. diff --git a/YEW.md b/YEW.md index dcc0291..29ae572 100644 --- a/YEW.md +++ b/YEW.md @@ -69,28 +69,59 @@ Incorporating Table RS into your Yew application is easy. Follow these steps: #### Main Props -| Property | Type | Description | Default | -| ----------- | ------------------------------------- | ------------------------------------------- | -------- | -| `data` | `Vec>` | The row data to be rendered in the table. | `[]` | -| `columns` | `Vec` | List of column definitions. | `[]` | -| `page_size` | `usize` | Number of rows per page. | `10` | -| `loading` | `bool` | Whether to show a loading state. | `false` | -| `paginate` | `bool` | Enables pagination UI. | `false` | -| `search` | `bool` | Enables search input field. | `false` | -| `classes` | `TableClasses` | CSS class names for customization. | Defaults | -| `styles` | `HashMap<&'static str, &'static str>` | Inline styles for different parts of table. | `{}` | - -#### Column Props - -| Property | Type | Description | Default | -| ---------- | ---------------------- | ------------------------------------ | ------- | -| `id` | `&'static str` | Key used to fetch data for the cell. | `""` | -| `header` | `&'static str` | Column header text. | `""` | -| `sortable` | `bool` | Whether the column is sortable. | `false` | -| `class` | `Option<&'static str>` | Optional CSS class for the column. | `None` | -| `style` | `Option<&'static str>` | Optional inline style. | `None` | - -#### Style Props +| Property | Type | Description | Default | +| ----------- | ------------------------------------- | ----------------------------------------------- | --------- | +| `data` | `Vec>` | The row data to be rendered in the table. | `[]` | +| `columns` | `Vec` | List of column definitions. | `[]` | +| `page_size` | `usize` | Number of rows per page. | `10` | +| `loading` | `bool` | Whether to show a loading state. | `false` | +| `paginate` | `bool` | Enables pagination UI. | `false` | +| `search` | `bool` | Enables search input field. | `false` | +| `classes` | `TableClasses` | CSS class names for customization. | See below | +| `styles` | `HashMap<&'static str, &'static str>` | Inline styles for different parts of the table. | `{}` | +| `texts` | `TableTexts` | Customizable text labels for UI elements. | See below | + +### `Column` Props + +| Property | Type | Description | Default | +| ----------- | ---------------------- | -------------------------------------------------------- | ----------------------------------------------------------- | +| `id` | `&'static str` | Key used to fetch data from row objects. | `""` | +| `header` | `&'static str` | Text shown in the table header. | `""` | +| `accessor` | `Callback<()>` | Optional callback for custom rendering or cell behavior. | `Callback::noop()` | +| `sortable` | `bool` | Whether this column can be sorted. | `false` | +| `min_width` | `u32` | Minimum width for the column in pixels. | `100` | +| `style` | `Option<&'static str>` | Optional inline styles for the column header. | `Some("padding: 8px; font-weight: 600; text-align: left;")` | +| `class` | `Option<&'static str>` | Optional CSS class for the column header. | `Some("table-header-cell")` | + +### `TableClasses` (Class Name Overrides) + +| Property | Type | Description | Default | +| ------------------- | -------------- | -------------------------------------- | ----------------------- | +| `container` | `&'static str` | Wrapper container for the whole table. | `"table-container"` | +| `table` | `&'static str` | The `` element. | `"table"` | +| `thead` | `&'static str` | The `` element. | `"thead"` | +| `tbody` | `&'static str` | The `` element. | `"tbody"` | +| `pagination` | `&'static str` | Pagination controls wrapper. | `"pagination-controls"` | +| `search_input` | `&'static str` | Class for the search input element. | `"search-input"` | +| `header_cell` | `&'static str` | Class for table header cells (``). | `"tr"` | +| `loading_row` | `&'static str` | Row shown during loading state. | `"loading-row"` | +| `empty_row` | `&'static str` | Row shown when there's no data. | `"empty-row"` | +| `pagination_button` | `&'static str` | Class for pagination buttons. | `"pagination-button"` | + +### `TableTexts` (UI Labels) + +| Property | Type | Description | Default | +| -------------------- | -------------- | ------------------------------------------- | ----------------------------- | +| `loading` | `&'static str` | Text shown during loading state. | `"Loading..."` | +| `empty` | `&'static str` | Text shown when no data matches the filter. | `"No results found"` | +| `search_placeholder` | `&'static str` | Placeholder text for search input. | `"Search..."` | +| `previous_button` | `&'static str` | Label for the previous page button. | `"Previous"` | +| `next_button` | `&'static str` | Label for the next page button. | `"Next"` | +| `page_indicator` | `&'static str` | Format string for pagination text. | `"Page {current} of {total}"` | + +### 🧱 Style/Layout Structure ```sh +-------------------------------------------------------------+ @@ -106,9 +137,9 @@ Incorporating Table RS into your Yew application is easy. Follow these steps: | | +--------------------[thead]--------------------+ | | <-- class: "thead" | | | Column Headers (e.g., Name, Email) | | | | | +-----------------------------------------------+ | | -| | +--------------------[tbody]-------------------0+ | | <-- class: "tbody" +| | +--------------------[tbody]--------------------+ | | <-- class: "tbody" | | | Data rows (from `data` prop, each row = )| | | -| | +---------------------------------------------- + | | +| | +-----------------------------------------------+ | | | +-----------------------------------------------------+ | | | | +-----------------------------------------------------+ | @@ -118,23 +149,42 @@ Incorporating Table RS into your Yew application is easy. Follow these steps: +-------------------------------------------------------------+ ``` -| Property | Type | Description | Default | -| -------------- | -------------- | --------------------------------------- | ----------------------- | -| `container` | `&'static str` | Wrapper container class. | `"table-container"` | -| `table` | `&'static str` | The `
`). | `"th"` | +| `body_cell` | `&'static str` | Class for table body cells (``). | `"td"` | +| `row` | `&'static str` | Class for rows (`
` element class. | `"table"` | -| `thead` | `&'static str` | The `` class. | `"thead"` | -| `tbody` | `&'static str` | The `` class. | `"tbody"` | -| `pagination` | `&'static str` | Pagination controls wrapper class. | `"pagination-controls"` | -| `search_input` | `&'static str` | Class for the search `` element. | `"search-input"` | - ## 💡 Notes - The `data` must match the `id` values defined in each `Column`. -- The `search` prop enables URL-synced filtering across all columns. +- The `search` prop enables input-based filtering across all columns. - Pagination is controlled using the `page_size` and `paginate` props. -- Column sorting is supported with `sortable = true` and visually indicated via `aria-sort`. -- You can override any table styles or classes via the `styles` and `classes` props. -- The component supports custom loading and empty states out of the box. -- Each column can define its own style and class for deeper customization. -- URL search parameters like `?search=term` can be used for search input hydration. -- The table body will show "Loading..." or "No results found" automatically. +- Sorting is column-specific via `sortable = true` and `on_sort_column`. +- All style classes can be customized via `TableClasses`. +- All texts are configurable via `TableTexts`. +- The component handles loading and empty states out-of-the-box. +- You can inject additional per-column styling via `Column.style` and `Column.class`. + +## 📊 Benchmark: TanStack Table vs Table RS + +| Metric | TanStack Table (React) | Table RS (Yew + WASM) | +|--------------------------------|-----------------------------|----------------------------| +| **Page Load Time (1M rows)** | ~10 seconds | ~2 seconds | +| **Memory Heap Usage** | >3 GB (heap overflow) | ~1.1 GB (stable) | +| **Initial Rendering** | Heavy blocking, slow DOM paint | Efficient, lightweight rendering | +| **Browser Responsiveness** | Delayed interactivity | Smooth after hydration | +| **Sorting Performance** | 2-4s for large columns | Sub-1s due to WASM speed | +| **Search Performance** | Acceptable, but slower | Instantaneous, even at scale | +| **Lighthouse Performance Score** | 49/100 | 60/100 | +| **Scalability** | Limited due to memory and VDOM | Near-native scalability | + +### 🟨 TanStack Table (React) +- Uses Virtual DOM and JS heap to manage massive data. +- Runtime bottlenecks emerge with >100k rows. +- Memory allocation during sorting and filtering can spike to **3GB+**, often leading to **heap overflow** during intensive usage. +- Lighthouse audit shows poor TTI and CPU blocking. + +### 🟩 Table RS (Yew + WASM) +- WASM-compiled logic is highly memory-efficient and deterministic. +- DOM rendering is direct, bypassing React's reconciliation. +- ~1.1 GB of memory heap used even with **1 million rows**. +- Built-in support for search/sort with stable paging. +- No hydration issues (client-only generation). +- Lighthouse performance significantly better, especially in CPU/Memory metrics. + +For large-data UI benchmarks like tables with millions of rows, **`table-rs` in Yew/WASM is a superior choice** compared to React + TanStack. diff --git a/examples/dioxus/Cargo.lock b/examples/dioxus/Cargo.lock new file mode 100644 index 0000000..681d778 --- /dev/null +++ b/examples/dioxus/Cargo.lock @@ -0,0 +1,1962 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-serialize" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08259976d62c715c4826cb4a3d64a3a9e5c5f68f964ff6087319857f569f93a6" +dependencies = [ + "const-serialize-macro", + "serde", +] + +[[package]] +name = "const-serialize-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04382d0d9df7434af6b1b49ea1a026ef39df1b0738b1cc373368cf175354f6eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dioxus" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a247114500f1a78e87022defa8173de847accfada8e8809dfae23a118a580c" +dependencies = [ + "dioxus-cli-config", + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-devtools", + "dioxus-document", + "dioxus-fullstack", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-logger", + "dioxus-signals", + "dioxus-web", + "manganis", + "warnings", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd16948f1ffdb068dd9a64812158073a4250e2af4e98ea31fdac0312e6bce86" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cbf582fbb1c32d34a1042ea675469065574109c95154468710a4d73ee98b49" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c03f451a119e47433c16e2d8eb5b15bf7d6e6734eb1a4c47574e6711dadff8d" +dependencies = [ + "const_format", + "dioxus-core-types", + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash", + "rustversion", + "serde", + "slab", + "slotmap", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "105c954caaaedf8cd10f3d1ba576b01e18aa8d33ad435182125eefe488cf0064" +dependencies = [ + "convert_case", + "dioxus-rsx", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dioxus-core-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91a82fccfa48574eb7aa183e297769540904694844598433a9eb55896ad9f93b" +dependencies = [ + "once_cell", +] + +[[package]] +name = "dioxus-devtools" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a7300f1e8181218187b03502044157eef04e0a25b518117c5ef9ae1096880" +dependencies = [ + "dioxus-core", + "dioxus-devtools-types", + "dioxus-signals", + "serde", + "serde_json", + "tracing", + "tungstenite", + "warnings", +] + +[[package]] +name = "dioxus-devtools-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f62434973c0c9c5a3bc42e9cd5e7070401c2062a437fb5528f318c3e42ebf4ff" +dependencies = [ + "dioxus-core", + "serde", +] + +[[package]] +name = "dioxus-document" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "802a2014d1662b6615eec0a275745822ee4fc66aacd9d0f2fb33d6c8da79b8f2" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-html", + "futures-channel", + "futures-util", + "generational-box", + "lazy-js-bundle", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe99b48a1348eec385b5c4bd3e80fd863b0d3b47257d34e2ddc58754dec5d128" +dependencies = [ + "base64", + "bytes", + "ciborium", + "dioxus-devtools", + "dioxus-history", + "dioxus-lib", + "dioxus-web", + "dioxus_server_macro", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "serde", + "server_fn", + "tracing", + "web-sys", +] + +[[package]] +name = "dioxus-history" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ae4e22616c698f35b60727313134955d885de2d32e83689258e586ebc9b7909" +dependencies = [ + "dioxus-core", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "948e2b3f20d9d4b2c300aaa60281b1755f3298684448920b27106da5841896d0" +dependencies = [ + "dioxus-core", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "rustversion", + "slab", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-html" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c9a40e6fee20ce7990095492dedb6a753eebe05e67d28271a249de74dc796d" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-hooks", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "lazy-js-bundle", + "rustversion", + "tracing", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ba87b53688a2c9f619ecdf4b3b955bc1f08bd0570a80a0d626c405f6d14a76" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330707b10ca75cb0eb05f9e5f8d80217cd0d7e62116a8277ae363c1a09b57a22" +dependencies = [ + "js-sys", + "lazy-js-bundle", + "rustc-hash", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-lib" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5405b71aa9b8b0c3e0d22728f12f34217ca5277792bd315878cc6ecab7301b72" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-document", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", + "warnings", +] + +[[package]] +name = "dioxus-logger" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545961e752f6c8bf59c274951b3c8b18a106db6ad2f9e2035b29e1f2a3e899b1" +dependencies = [ + "console_error_panic_hook", + "dioxus-cli-config", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "dioxus-rsx" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb588e05800b5a7eb90b2f40fca5bbd7626e823fb5e1ba21e011de649b45aa1" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "dioxus-signals" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10e032dbb3a2c0386ec8b8ee59bc20b5aeb67038147c855801237b45b13d72ac" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-web" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7c12475c3d360058b8afe1b68eb6dfc9cbb7dcd760aed37c5f85c561c83ed1" +dependencies = [ + "async-trait", + "ciborium", + "dioxus-cli-config", + "dioxus-core", + "dioxus-core-types", + "dioxus-devtools", + "dioxus-document", + "dioxus-history", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "js-sys", + "lazy-js-bundle", + "rustc-hash", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "371a5b21989a06b53c5092e977b3f75d0e60a65a4c15a2aa1d07014c3b2dda97" +dependencies = [ + "proc-macro2", + "quote", + "server_fn_macro", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generational-box" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a673cf4fb0ea6a91aa86c08695756dfe875277a912cdbf33db9a9f62d47ed82b" +dependencies = [ + "parking_lot", + "tracing", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "half" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "lazy-js-bundle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49596223b9d9d4947a14a25c142a6e7d8ab3f27eb3ade269d238bb8b5c267e2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "manganis" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317af44b15e7605b85f04525449a3bb631753040156c9b318e6cba8a3ea4ef73" +dependencies = [ + "const-serialize", + "manganis-core", + "manganis-macro", +] + +[[package]] +name = "manganis-core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38bee65cc725b2bba23b5dbb290f57c8be8fadbe2043fb7e2ce73022ea06519" +dependencies = [ + "const-serialize", + "dioxus-cli-config", + "dioxus-core-types", + "serde", +] + +[[package]] +name = "manganis-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f4f71310913c40174d9f0cfcbcb127dad0329ecdb3945678a120db22d3d065" +dependencies = [ + "dunce", + "manganis-core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "server_fn" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fae7a3038a32e5a34ba32c6c45eb4852f8affaf8b794ebfcd4b1099e2d62ebe" +dependencies = [ + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http", + "js-sys", + "once_cell", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaaf648c6967aef78177c0610478abb5a3455811f401f3c62d10ae9bd3901a1" +dependencies = [ + "const_format", + "convert_case", + "proc-macro2", + "quote", + "syn", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2aa8119b558a17992e0ac1fd07f080099564f24532858811ce04f742542440" +dependencies = [ + "server_fn_macro", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e83e178d176459c92bc129cfd0958afac3ced925471b889b3a75546cfc4133" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a1b4f13e2bbf2f5b29d09dfebc9de69229ffee245aed80e3b70f9b5fd28c06" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdd4b83524961983cea3c55383b3910fd2f24fd13a188f5b091d2d504a61ae" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "table-rs" +version = "0.0.2" +dependencies = [ + "dioxus", + "web-sys", +] + +[[package]] +name = "table-rs-dioxus-example" +version = "0.1.0" +dependencies = [ + "dioxus", + "dioxus-logger", + "maplit", + "table-rs", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "warnings" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f68998838dab65727c9b30465595c6f7c953313559371ca8bf31759b3680ad" +dependencies = [ + "pin-project", + "tracing", + "warnings-macro", +] + +[[package]] +name = "warnings-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59195a1db0e95b920366d949ba5e0d3fc0e70b67c09be15ce5abb790106b0571" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/examples/dioxus/Cargo.toml b/examples/dioxus/Cargo.toml new file mode 100755 index 0000000..8ab21b5 --- /dev/null +++ b/examples/dioxus/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "table-rs-dioxus-example" +version = "0.1.0" +edition = "2021" + +[dependencies] +dioxus = { version = "0.6.3", features = ["web"] } +table-rs = { path = "../../", features = ["dio"] } +dioxus-logger = "0.6.2" +maplit = "1.0.2" + +[profile] + +[profile.wasm-dev] +inherits = "dev" +opt-level = 1 + +[profile.server-dev] +inherits = "dev" + +[profile.android-dev] +inherits = "dev" diff --git a/examples/dioxus/Dioxus.toml b/examples/dioxus/Dioxus.toml new file mode 100755 index 0000000..86e8d1c --- /dev/null +++ b/examples/dioxus/Dioxus.toml @@ -0,0 +1,39 @@ +[application] + +# App (Project) Name +name = "table-rs" + +# Dioxus App Default Platform +# desktop, web +default_platform = "web" + +# resource (assets) file folder +asset_dir = "assets" + +[web.app] + +# HTML title tag content +title = "table-rs" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "assets"] + +# include `assets` in web platform +[web.resource] + +# CSS style file +style = [] + +# Javascript code file +script = [] + +[web.resource.dev] + +# Javascript code file +# serve: [dev-server] only +script = [] diff --git a/examples/dioxus/README.md b/examples/dioxus/README.md new file mode 100644 index 0000000..ca13433 --- /dev/null +++ b/examples/dioxus/README.md @@ -0,0 +1,69 @@ +# 📚 Table RS Dioxus Tailwind Components + +## 🛠️ Pre-requisites: + +### 🐧 **Linux Users** + +1. **Install [`rustup`](https://www.rust-lang.org/tools/install)**: + + ```sh + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` + +1. Install [`Dioxus CLI`](https://dioxuslabs.com/learn/0.5/getting_started): + + ```sh + cargo install dioxus-cli + ``` + +### 🪟 **Windows Users** + +1. **Download and install `rustup`**: Follow the installation instructions [here](https://www.rust-lang.org/tools/install). + +1. **Install [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)**: Open PowerShell as administrator and run: + + ```sh + wsl --install + ``` + +1. **Reset Network Stack**: In PowerShell (administrator mode), run: + + ```sh + netsh int ip reset all + netsh winsock reset + ``` + +1. **Install Linux packages in WSL**: Once inside your WSL terminal, update and install required dependencies: + + ```sh + sudo apt update + sudo apt install build-essential pkg-config libudev-dev + ``` + +1. Install [`Dioxus CLI`](https://dioxuslabs.com/learn/0.5/getting_started): + + ```sh + cargo install dioxus-cli + ``` + +## 🚀 Building and Running + +1. Fork/Clone the GitHub repository. + + ```sh + git clone https://github.com/opensass/table-rs + ``` + +1. Navigate to the application directory. + + ```sh + cd table-rs/examples/dioxus + ``` + +1. Run the client: + + ```sh + dx serve --port 3000 + ``` + +Navigate to http://localhost:3000 to explore the landing page. diff --git a/examples/dioxus/assets/favicon.ico b/examples/dioxus/assets/favicon.ico new file mode 100644 index 0000000..eed0c09 Binary files /dev/null and b/examples/dioxus/assets/favicon.ico differ diff --git a/examples/dioxus/assets/header.svg b/examples/dioxus/assets/header.svg new file mode 100644 index 0000000..59c96f2 --- /dev/null +++ b/examples/dioxus/assets/header.svg @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/examples/dioxus/assets/styles.css b/examples/dioxus/assets/styles.css new file mode 100644 index 0000000..0137e2d --- /dev/null +++ b/examples/dioxus/assets/styles.css @@ -0,0 +1,15 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap"); + +@tailwind base; +@tailwind components; +@tailwind utilities; +@tailwind variants; + +body { + color: #5e5c7f; + background-color: #303030; + font-family: "Rubik", sans-serif; + font-size: 16px; + line-height: 1.7; + overflow-x: hidden; +} diff --git a/examples/dioxus/src/main.rs b/examples/dioxus/src/main.rs new file mode 100755 index 0000000..b226177 --- /dev/null +++ b/examples/dioxus/src/main.rs @@ -0,0 +1,700 @@ +use dioxus::prelude::*; +use dioxus_logger::tracing; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{Column, TableClasses, TableTexts}; + +const FAVICON: Asset = asset!("/assets/favicon.ico"); +const MAIN_CSS: Asset = asset!("/assets/styles.css"); + +fn main() { + dioxus_logger::init(tracing::Level::INFO).expect("failed to init logger"); + tracing::info!("starting app"); + launch(app); +} + +fn app() -> Element { + rsx! { + document::Script { src: "https://kit.fontawesome.com/8f223ead6e.js" }, + document::Stylesheet { href: "https://unpkg.com/tailwindcss@2.2.19/dist/tailwind.min.css" }, + document::Link { rel: "icon", href: FAVICON } + document::Link { rel: "stylesheet", href: MAIN_CSS } + Examples {} + } +} + +#[component] +fn Examples() -> Element { + rsx! { + div { class: "m-6 min-h-screen flex flex-col items-center justify-center", + h1 { class: "text-3xl font-bold mb-8 text-white", "Table RS Dioxus Examples" } + div { class: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8", + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Basic Table" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example1() -> Element {{ + let data = vec![ + hashmap! {{ "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }}, + hashmap! {{ "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }}, + hashmap! {{ "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }}, + ]; + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: false, ..Default::default() }}, + ]; + + rsx! {{ + Table {{ data: data, columns: columns }} + }} +}}"## + } + Example1 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Paginated Searchable Table" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example2() -> Element {{ + let data = (1..=50).map(|i| hashmap! {{ + "name" => format!("Ferris {{i}}"), + "email" => format!("ferris{{i}}@opensass.org") + }}).collect::>(); + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: true, ..Default::default() }}, + ]; + + rsx! {{ + Table {{ + data: data, + columns: columns, + page_size: 5, + paginate: true, + search: true + }} + }} +}}"## + } + Example2 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Custom Classes" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example3() -> Element {{ + let data = vec![ + hashmap! {{ "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }}, + hashmap! {{ "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }}, + ]; + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: false, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: false, ..Default::default() }}, + ]; + + let custom_classes = TableClasses {{ + container: "custom-container", + table: "custom-table", + thead: "custom-thead", + tbody: "custom-tbody", + pagination: "custom-pagination", + search_input: "custom-search-input", + ..Default::default() + }}; + + rsx! {{ + Table {{ + data: data, + columns: columns, + classes: custom_classes + }} + }} +}}"## + } + Example3 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Loading State" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example4() -> Element {{ + let data = vec![]; + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: true, ..Default::default() }}, + ]; + + rsx! {{ + Table {{ + data: data, + columns: columns, + loading: true + }} + }} +}}"## + } + Example4 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Styled Columns" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example5() -> Element {{ + let data = (0..=20).map(|i| hashmap! {{ + "name" => format!("Ferris {{i}}"), + "email" => format!("ferris{{i}}@opensass.org") + }}).collect::>(); + + let columns = vec![ + Column {{ + id: "name", header: "Name", sortable: true, + class: Some("text-blue-500"), style: Some("min-width: 200px;"), + ..Default::default() + }}, + Column {{ + id: "email", header: "Email", sortable: true, + class: Some("text-red-500"), style: Some("min-width: 300px;"), + ..Default::default() + }}, + ]; + + rsx! {{ + Table {{ data: data, columns: columns }} + }} +}}"## + } + Example5 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Striped Rows" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example6() -> Element {{ + let data = (1..=10).map(|i| hashmap! {{ + "name" => format!("Ferris {{i}}"), + "email" => format!("ferris{{i}}@opensass.org") + }}).collect::>(); + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: true, ..Default::default() }}, + ]; + + let custom_classes = TableClasses {{ + tbody: "divide-y divide-gray-200 odd:bg-gray-100 even:bg-white hover:bg-gray-200", + ..Default::default() + }}; + + rsx! {{ + Table {{ + data: data, + columns: columns, + classes: custom_classes + }} + }} +}}"## + } + Example6 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Sticky Header" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example7() -> Element {{ + let data = (1..=10).map(|i| hashmap! {{ + "name" => format!("Ferris {{i}}"), + "email" => format!("ferris{{i}}@opensass.org") + }}).collect::>(); + + let columns = vec![ + Column {{ id: "name", header: "Sticky Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Sticky Email", sortable: false, ..Default::default() }}, + ]; + + let custom_classes = TableClasses {{ + thead: "bg-white sticky top-0 shadow", + ..Default::default() + }}; + + rsx! {{ + div {{ class: "h-64 overflow-y-auto", + Table {{ + data: data, + columns: columns, + classes: custom_classes + }} + }} + }} +}}"## + } + Example7 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Search Only" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example8() -> Element {{ + let data = (1..=5).map(|i| hashmap! {{ + "name" => format!("Ferris {{i}}"), + "email" => format!("ferris{{i}}@opensass.org") + }}).collect::>(); + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: false, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: false, ..Default::default() }}, + ]; + + rsx! {{ + Table {{ data: data, columns: columns, search: true }} + }} +}}"## + } + Example8 {} + } + + div { + class: "flex flex-col items-center bg-gray-50 p-6 rounded-lg shadow-lg", + h2 { class: "text-xl font-semibold mb-4 text-gray-800", "Custom Text & Pagination" } + pre { + class: "font-mono text-xs text-white p-4 bg-gray-800 mb-8 rounded-md w-full overflow-x-auto", + r##"use dioxus::prelude::*; +use maplit::hashmap; +use table_rs::dioxus::table::Table; +use table_rs::dioxus::types::{{Column, TableClasses, TableTexts}}; + + +#[component] +fn Example9() -> Element {{ + let data = (1..=3).map(|i| hashmap! {{ + "name" => format!("Crab {{i}}"), + "email" => format!("crab{{i}}@shell.org") + }}).collect::>(); + + let columns = vec![ + Column {{ id: "name", header: "Name", sortable: true, ..Default::default() }}, + Column {{ id: "email", header: "Email", sortable: true, ..Default::default() }}, + ]; + + let texts = TableTexts {{ + search_placeholder: "Filter by name or email...", + ..Default::default() + }}; + + rsx! {{ + Table {{ + data: data, + columns: columns, + page_size: 2, + paginate: true, + search: true, + texts: texts + }} + }} +}}"## + } + Example9 {} + } + } + } + } +} + +#[component] +fn Example1() -> Element { + let data = vec![ + hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, + hashmap! { "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, + hashmap! { "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }, + ]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: false, + ..Default::default() + }, + ]; + + rsx! { + Table { data: data, columns: columns } + } +} + +#[component] +fn Example2() -> Element { + let data = (1..=50) + .map(|i| { + hashmap! { + "name" => format!("Ferris {i}"), + "email" => format!("ferris{i}@opensass.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + rsx! { + Table { + data: data, + columns: columns, + page_size: 5, + paginate: true, + search: true + } + } +} + +#[component] +fn Example3() -> Element { + let data = vec![ + hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, + hashmap! { "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, + ]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: false, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: false, + ..Default::default() + }, + ]; + + let custom_classes = TableClasses { + container: "custom-container", + table: "custom-table", + thead: "custom-thead", + tbody: "custom-tbody", + pagination: "custom-pagination", + search_input: "custom-search-input", + ..Default::default() + }; + + rsx! { + Table { + data: data, + columns: columns, + classes: custom_classes + } + } +} + +#[component] +fn Example4() -> Element { + let data = vec![]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + rsx! { + Table { + data: data, + columns: columns, + loading: true + } + } +} + +#[component] +fn Example5() -> Element { + let data = (0..=20) + .map(|i| { + hashmap! { + "name" => format!("Ferris {i}"), + "email" => format!("ferris{i}@opensass.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + class: Some("text-blue-500"), + style: Some("min-width: 200px;"), + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + class: Some("text-red-500"), + style: Some("min-width: 300px;"), + ..Default::default() + }, + ]; + + rsx! { + Table { data: data, columns: columns } + } +} + +#[component] +fn Example6() -> Element { + let data = (1..=10) + .map(|i| { + hashmap! { + "name" => format!("Ferris {i}"), + "email" => format!("ferris{i}@opensass.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + let custom_classes = TableClasses { + tbody: "divide-y divide-gray-200 odd:bg-gray-100 even:bg-white hover:bg-gray-200", + ..Default::default() + }; + + rsx! { + Table { + data: data, + columns: columns, + classes: custom_classes + } + } +} + +#[component] +fn Example7() -> Element { + let data = (1..=10) + .map(|i| { + hashmap! { + "name" => format!("Ferris {i}"), + "email" => format!("ferris{i}@opensass.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Sticky Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Sticky Email", + sortable: false, + ..Default::default() + }, + ]; + + let custom_classes = TableClasses { + thead: "bg-white sticky top-0 shadow", + ..Default::default() + }; + + rsx! { + div { class: "h-64 overflow-y-auto", + Table { + data: data, + columns: columns, + classes: custom_classes + } + } + } +} + +#[component] +fn Example8() -> Element { + let data = (1..=5) + .map(|i| { + hashmap! { + "name" => format!("Ferris {i}"), + "email" => format!("ferris{i}@opensass.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: false, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: false, + ..Default::default() + }, + ]; + + rsx! { + Table { data: data, columns: columns, search: true } + } +} + +#[component] +fn Example9() -> Element { + let data = (1..=3) + .map(|i| { + hashmap! { + "name" => format!("Crab {i}"), + "email" => format!("crab{i}@shell.org") + } + }) + .collect::>(); + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + let texts = TableTexts { + search_placeholder: "Filter by name or email...", + ..Default::default() + }; + + rsx! { + Table { + data: data, + columns: columns, + page_size: 2, + paginate: true, + search: true, + texts: texts + } + } +} diff --git a/examples/yew/src/pages/landing.rs b/examples/yew/src/pages/landing.rs index 2aa4193..45520f6 100644 --- a/examples/yew/src/pages/landing.rs +++ b/examples/yew/src/pages/landing.rs @@ -1,6 +1,6 @@ use maplit::hashmap; use table_rs::yew::table::Table; -use table_rs::yew::types::{Column, TableClasses}; +use table_rs::yew::types::{Column, TableClasses, TableTexts}; use yew::prelude::*; #[derive(Properties, PartialEq)] @@ -110,6 +110,7 @@ pub fn example3() -> Html { tbody: "custom-tbody", pagination: "custom-pagination", search_input: "custom-search-input", + ..Default::default() }; html! {
} @@ -420,6 +421,74 @@ pub fn example12() -> Html { } } +#[function_component(Example13)] +pub fn example13() -> Html { + let data = vec![ + hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, + hashmap! { "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, + hashmap! { "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }, + hashmap! { "name" => "CrabFerris".to_string(), "email" => "crabferris@opensass.org".to_string() }, + ]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + let classes = TableClasses { + container: "w-full max-w-5xl mx-auto mt-10", + table: "min-w-full bg-white shadow-md rounded-lg overflow-hidden", + thead: "bg-gray-100 text-gray-700 uppercase text-sm", + tbody: "divide-y divide-gray-200", + search_input: "mb-4 p-2 border rounded w-full max-w-xs", + row: "hover:bg-gray-50", + header_cell: "px-4 py-3", + body_cell: "px-4 py-3 text-gray-900", + loading_row: "text-center py-4", + empty_row: "text-center py-4 text-gray-500", + pagination: "flex justify-between items-center mt-4", + pagination_button: + "px-4 py-2 text-sm text-white bg-blue-500 rounded hover:bg-blue-600 disabled:opacity-50", + }; + + let styles = hashmap! { + "table" => "border-collapse: collapse; width: 100%;", + }; + + let texts = TableTexts { + loading: "Loading data...", + empty: "No entries match your search.", + search_placeholder: "Search by name or email...", + previous_button: "← Previous", + next_button: "Next →", + page_indicator: "Page {current} of {total}", + }; + + html! { +
+ } +} + #[function_component(LandingPage)] pub fn landing_page() -> Html { html! { @@ -945,6 +1014,84 @@ pub fn example12() -> Html { > + Html { + let data = vec![ + hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, + hashmap! { "name" => "Ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, + hashmap! { "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }, + hashmap! { "name" => "CrabFerris".to_string(), "email" => "crabferris@opensass.org".to_string() }, + ]; + + let columns = vec![ + Column { + id: "name", + header: "Name", + sortable: true, + ..Default::default() + }, + Column { + id: "email", + header: "Email", + sortable: true, + ..Default::default() + }, + ]; + + let classes = TableClasses { + container: "w-full max-w-5xl mx-auto mt-10", + table: "min-w-full bg-white shadow-md rounded-lg overflow-hidden", + thead: "bg-gray-100 text-gray-700 uppercase text-sm", + tbody: "divide-y divide-gray-200", + search_input: "mb-4 p-2 border rounded w-full max-w-xs", + row: "hover:bg-gray-50", + header_cell: "px-4 py-3", + body_cell: "px-4 py-3 text-gray-900", + loading_row: "text-center py-4", + empty_row: "text-center py-4 text-gray-500", + pagination: "flex justify-between items-center mt-4", + pagination_button: "px-4 py-2 text-sm text-white bg-blue-500 rounded hover:bg-blue-600 disabled:opacity-50", + }; + + let styles = hashmap! { + "table" => "border-collapse: collapse; width: 100%;", + }; + + let texts = TableTexts { + loading: "Loading data...", + empty: "No entries match your search.", + search_placeholder: "Search by name or email...", + previous_button: "← Previous", + next_button: "Next →", + page_indicator: "Page {current} of {total}", + }; + + html! { +
+ } +}"# + > + + } diff --git a/src/dioxus.rs b/src/dioxus.rs index 8b13789..45277f1 100644 --- a/src/dioxus.rs +++ b/src/dioxus.rs @@ -1 +1,7 @@ +#![doc = include_str!("../DIOXUS.md")] +pub mod body; +pub mod controls; +pub mod header; +pub mod table; +pub mod types; diff --git a/src/dioxus/body.rs b/src/dioxus/body.rs new file mode 100644 index 0000000..4a8e50b --- /dev/null +++ b/src/dioxus/body.rs @@ -0,0 +1,106 @@ +use crate::dioxus::types::Column; +use crate::dioxus::types::TableClasses; +use crate::dioxus::types::TableTexts; +use dioxus::prelude::*; +use std::collections::HashMap; + +/// A table body component that renders rows of data, along with loading and empty states. +/// +/// This component is responsible for rendering the `` section of a table in a Dioxus application. +/// It dynamically displays row data, a loading message, or an empty state message based on the provided props. +/// +/// # Props +/// - `columns`: A `Vec` defining which fields to render in each table row. Each column corresponds to a key in the row data. +/// - `rows`: A `Vec>` representing the data for each row, where keys match column IDs. +/// - `loading`: A `bool` flag that, when true, displays a loading message instead of data rows. +/// - `classes`: A `TableClasses` struct for customizing the CSS class names of the body, rows, and cells. +/// - `texts`: A `TableTexts` struct that provides custom text for the loading and empty states. +/// +/// # Behavior +/// - If `loading` is `true`, a single row with a loading message is shown spanning all columns. +/// - If `rows` is empty and not loading, an empty message row is displayed. +/// - Otherwise, each data row is rendered in a ``, with one `` of a table, with dynamic row content. +/// +/// # Example +/// ```rust +/// use dioxus::prelude::*; +/// use maplit::hashmap; +/// use table_rs::dioxus::table::Table; +/// use table_rs::dioxus::types::{Column, TableClasses, TableTexts}; +/// use table_rs::dioxus::body::TableBody; +/// +/// +/// fn App() -> Element { +/// let rows = vec![ +/// hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, +/// hashmap! { "name" => "Rustacean".to_string(), "email" => "rust@opensass.org".to_string() }, +/// ]; +/// +/// let columns = vec![ +/// Column { id: "name", header: "Name", ..Default::default() }, +/// Column { id: "email", header: "Email", ..Default::default() }, +/// ]; +/// +/// rsx! { +/// TableBody { +/// columns: columns, +/// rows: rows, +/// loading: false, +/// classes: TableClasses::default(), +/// texts: TableTexts::default(), +/// } +/// } +/// } +/// ``` +/// +/// # See Also +/// - [MDN `` Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/tbody) +#[component] +pub fn TableBody( + columns: Vec, + rows: Vec>, + loading: bool, + classes: TableClasses, + texts: TableTexts, +) -> Element { + let content = if loading { + rsx! { + tr { class: "{classes.loading_row}", + td { + colspan: "{columns.len()}", + "{texts.loading}" + } + } + } + } else if rows.is_empty() { + rsx! { + tr { class: "{classes.empty_row}", + td { + colspan: "{columns.len()}", + "{texts.empty}" + } + } + } + } else { + rsx! { + for row in rows.iter() { + tr { class: "{classes.row}", role: "row", + for col in columns.iter() { + td { class: "{classes.body_cell}", role: "cell", + "{row.get(col.id).unwrap_or(&String::new())}" + } + } + } + } + } + }; + + rsx! { + tbody { class: "{classes.tbody}", + {content} + } + } +} diff --git a/src/dioxus/controls.rs b/src/dioxus/controls.rs new file mode 100644 index 0000000..996aa79 --- /dev/null +++ b/src/dioxus/controls.rs @@ -0,0 +1,47 @@ +use crate::dioxus::types::TableClasses; +use crate::dioxus::types::TableTexts; +use dioxus::prelude::*; + +#[component] +pub fn PaginationControls( + page: Signal, + total_pages: usize, + classes: TableClasses, + texts: TableTexts, +) -> Element { + let on_prev = move |_| { + if page() > 0 { + page.set(page() - 1); + } + }; + + let on_next = move |_| { + if page() + 1 < total_pages { + page.set(page() + 1); + } + }; + + rsx! { + div { class: classes.pagination, + button { + class: classes.pagination_button, + onclick: on_prev, + disabled: page() == 0, + "{texts.previous_button}" + } + span { + { + texts.page_indicator + .replace("{current}", &(page() + 1).to_string()) + .replace("{total}", &total_pages.to_string()) + } + } + button { + class: classes.pagination_button, + onclick: on_next, + disabled: page() + 1 >= total_pages, + "{texts.next_button}" + } + } + } +} diff --git a/src/dioxus/header.rs b/src/dioxus/header.rs new file mode 100644 index 0000000..adc3a1e --- /dev/null +++ b/src/dioxus/header.rs @@ -0,0 +1,108 @@ +use crate::dioxus::types::Column; +use crate::dioxus::types::SortOrder; +use crate::dioxus::types::TableClasses; +use dioxus::prelude::*; + +/// A table header component that renders sortable column headers for use within the `Table` component. +/// +/// This component produces the `` section of a table using the provided column definitions, +/// handling rendering, sorting indicators (`aria-sort`), and user interaction to trigger sort changes. +/// +/// # Props +/// - `columns`: A `Vec` defining the columns to display in the header. Each `Column` may be sortable and have optional styles or class overrides. +/// - `sort_column`: A `Signal>` indicating which column (if any) is currently being sorted. +/// - `sort_order`: A `Signal` indicating the current sort direction (`Asc` or `Desc`). +/// - `on_sort_column`: An `EventHandler<&'static str>` triggered when a sortable header cell is clicked. The column ID is passed as the event payload. +/// - `classes`: A `TableClasses` struct allowing custom class names for ``, ``, and `` with all column headers rendered as `` Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/thead) +#[component] +pub fn TableHeader( + columns: Vec, + sort_column: Signal>, + sort_order: Signal, + on_sort_column: EventHandler<&'static str>, + classes: TableClasses, +) -> Element { + let header_cells = columns.iter().map(|col| { + let col_id = col.id; + let is_sorted = sort_column() == Some(col_id); + let aria_sort = if is_sorted { + match sort_order() { + SortOrder::Asc => "ascending", + SortOrder::Desc => "descending", + } + } else { + "none" + }; + + let class = format!("{} {}", classes.header_cell, col.class.unwrap_or_default()); + let style = col.style.unwrap_or_default(); + let header = col.header; + + let onclick = if col.sortable { + Callback::new(move |_| on_sort_column.call(col_id)) + } else { + Callback::new(|_| {}) + }; + + rsx! { + th { + key: "{col_id}", + role: "columnheader", + class: "{class}", + style: "{style}", + aria_sort: "{aria_sort}", + onclick: onclick, + "{header}" + } + } + }); + + rsx! { + thead { class: "{classes.thead}", + tr { class: "{classes.row}", role: "row", + {header_cells} + } + } + } +} diff --git a/src/dioxus/table.rs b/src/dioxus/table.rs new file mode 100644 index 0000000..48a6bf9 --- /dev/null +++ b/src/dioxus/table.rs @@ -0,0 +1,209 @@ +use dioxus::prelude::*; +use web_sys::UrlSearchParams; +use web_sys::wasm_bindgen::JsValue; + +use crate::dioxus::body::TableBody; +use crate::dioxus::controls::PaginationControls; +use crate::dioxus::header::TableHeader; +use crate::dioxus::types::SortOrder; +use crate::dioxus::types::TableProps; + +/// A fully featured table component with sorting, pagination, and search functionality in Dioxus. +/// +/// This component renders an interactive HTML `
` per column. +/// +/// # Returns +/// A Dioxus `Element` representing the `
` elements. +/// +/// # Behavior +/// - Sortable columns show proper `aria-sort` attributes for accessibility (`ascending`, `descending`, or `none`). +/// - Clicking a sortable column emits an event to update sort state. +/// - Each column can override default styles and classes via `Column::style` and `Column::class`. +/// +/// # Returns +/// Returns a `Dioxus` `Element` containing the `
` elements. +/// +/// # Example +/// ```rust +/// use dioxus::prelude::*; +/// use maplit::hashmap; +/// use table_rs::dioxus::table::Table; +/// use table_rs::dioxus::types::{Column, TableClasses, SortOrder}; +/// use table_rs::dioxus::header::TableHeader; +/// +/// +/// fn App() -> Element { +/// let columns = vec![ +/// Column { id: "name", header: "Name", sortable: true, ..Default::default() }, +/// Column { id: "email", header: "Email", sortable: false, ..Default::default() }, +/// ]; +/// +/// let sort_column = use_signal(|| Some("name")); +/// let sort_order = use_signal(|| SortOrder::Asc); +/// +/// rsx! { +/// TableHeader { +/// columns: columns, +/// sort_column: sort_column, +/// sort_order: sort_order, +/// on_sort_column: move |col_id| println!("Sort column changed: {}", col_id), +/// classes: TableClasses::default(), +/// } +/// } +/// } +/// ``` +/// +/// # See Also +/// - [MDN `
` with customizable columns, data, +/// class names, and labels. It supports client-side sorting, search with URL hydration, +/// and pagination. +/// +/// # Props +/// `TableProps` defines the configuration for this component: +/// - `data`: A `Vec>` representing row data. +/// - `columns`: A `Vec` describing each column's ID, header text, and behavior. +/// - `page_size`: Number of rows to display per page (default: `10`). +/// - `loading`: When `true`, displays a loading indicator (default: `false`). +/// - `paginate`: Enables pagination controls (default: `false`). +/// - `search`: Enables a search input for client-side filtering (default: `false`). +/// - `texts`: Customizable text labels for UI strings (default: `TableTexts::default()`). +/// - `classes`: Customizable CSS class names for each table part (default: `TableClasses::default()`). +/// +/// # Features +/// - **Search**: Filters rows client-side using a text input; the query is persisted in the URL via `?search=`. +/// - **Sorting**: Clickable headers allow sorting columns ascending or descending. +/// - **Pagination**: Navigate between pages using prev/next buttons, with an indicator showing current page. +/// - **Custom Classes**: All elements are styled via `TableClasses` for full customization. +/// - **Text Overrides**: All UI strings (e.g., empty state, loading, buttons) can be customized using `TableTexts`. +/// +/// # Returns +/// Returns a `Dioxus` `Element` that renders a complete table with the above features. +/// +/// # Example +/// ```rust +/// use dioxus::prelude::*; +/// use maplit::hashmap; +/// use table_rs::dioxus::table::Table; +/// use table_rs::dioxus::types::Column; +/// +/// +/// fn App() -> Element { +/// let data = vec![ +/// hashmap! { "name" => "ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, +/// hashmap! { "name" => "ferros".to_string(), "email" => "ferros@opensass.org".to_string() }, +/// ]; +/// +/// let columns = vec![ +/// Column { id: "name", header: "Name", sortable: true, ..Default::default() }, +/// Column { id: "email", header: "Email", ..Default::default() }, +/// ]; +/// +/// rsx! { +/// Table { +/// data: data, +/// columns: columns, +/// paginate: true, +/// search: true, +/// } +/// } +/// } +/// ``` +/// +/// # See Also +/// - [MDN `
` Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/table) +#[component] +pub fn Table(props: TableProps) -> Element { + let TableProps { + data, + columns, + page_size, + loading, + paginate, + search, + texts, + classes, + } = props; + + let mut page = use_signal(|| 0_usize); + let mut sort_column = use_signal(|| None::<&'static str>); + let mut sort_order = use_signal(SortOrder::default); + let mut search_query = use_signal(String::new); + + use_effect(move || { + let window = web_sys::window().unwrap(); + let location = window.location(); + let search = location.search().unwrap_or_default(); + let params = UrlSearchParams::new_with_str(&search).unwrap(); + if let Some(search_val) = params.get("search") { + search_query.set(search_val); + } + }); + + let update_search_param = move |query: &str| { + let window = web_sys::window().unwrap(); + let href = window.location().href().unwrap(); + let url = web_sys::Url::new(&href).unwrap(); + let params = url.search_params(); + params.set("search", query); + url.set_search(¶ms.to_string().as_string().unwrap_or_default()); + + window + .history() + .unwrap() + .replace_state_with_url(&JsValue::NULL, "", Some(&url.href())) + .unwrap(); + }; + + let filtered_rows = { + let mut rows = data.clone(); + if !search_query().is_empty() { + rows.retain(|row| { + columns.iter().any(|col| { + row.get(col.id) + .map(|v| v.to_lowercase().contains(&search_query().to_lowercase())) + .unwrap_or(false) + }) + }); + } + + if let Some(col_id) = sort_column() { + if let Some(col) = columns.iter().find(|c| c.id == col_id) { + rows.sort_by(|a, b| { + let val = "".to_string(); + let a_val = a.get(col.id).unwrap_or(&val); + let b_val = b.get(col.id).unwrap_or(&val); + match sort_order() { + SortOrder::Asc => a_val.cmp(b_val), + SortOrder::Desc => b_val.cmp(a_val), + } + }); + } + } + + rows + }; + + let total_pages = (filtered_rows.len() as f64 / page_size as f64).ceil() as usize; + let start = page() * page_size; + let end = ((page() + 1) * page_size).min(filtered_rows.len()); + let page_rows = &filtered_rows[start..end]; + + let on_sort_column = move |id: &'static str| { + if Some(id) == sort_column() { + sort_order.set(match sort_order() { + SortOrder::Asc => SortOrder::Desc, + SortOrder::Desc => SortOrder::Asc, + }); + } else { + sort_column.set(Some(id)); + sort_order.set(SortOrder::Asc); + } + }; + + let pagination_controls = if paginate { + rsx! { + PaginationControls { + page: page, + total_pages: total_pages, + classes: classes.clone(), + texts: texts.clone(), + } + } + } else { + rsx! {} + }; + + rsx! { + div { + class: "{classes.container}", + if search { + input { + class: "{classes.search_input}", + r#type: "text", + value: "{search_query()}", + placeholder: "{texts.search_placeholder}", + oninput: move |e| { + let val = e.value(); + search_query.set(val.clone()); + page.set(0); + update_search_param(&val); + } + } + } + table { + class: "{classes.table}", + TableHeader { + columns: columns.clone(), + sort_column: sort_column, + sort_order: sort_order, + on_sort_column: on_sort_column, + classes: classes.clone(), + } + TableBody { + columns: columns.clone(), + rows: page_rows.to_vec(), + loading: loading, + classes: classes.clone(), + texts: texts.clone(), + } + } + {pagination_controls} + } + } +} diff --git a/src/dioxus/types.rs b/src/dioxus/types.rs new file mode 100644 index 0000000..946b2d7 --- /dev/null +++ b/src/dioxus/types.rs @@ -0,0 +1,174 @@ +use dioxus::prelude::*; +use std::collections::HashMap; + +/// Represents a column definition for the table. +#[derive(PartialEq, Props, Clone, Default)] +pub struct Column { + /// Unique identifier for the column. + pub id: &'static str, + + /// Header text displayed in the column. + pub header: &'static str, + + /// Whether this column is sortable. + #[props(default)] + pub sortable: bool, + + /// Minimum width of the column (default is 100). + #[props(default = 100)] + pub min_width: u32, + + /// Optional inline styles for the column header. + #[props(default)] + pub style: Option<&'static str>, + + /// Optional CSS classes for the column header. + #[props(default)] + pub class: Option<&'static str>, +} + +/// Text labels for table UI elements. +#[derive(PartialEq, Props, Clone)] +pub struct TableTexts { + /// Text shown when data is loading. + #[props(default = "Loading...")] + pub loading: &'static str, + + /// Text shown when no data is available. + #[props(default = "No results found")] + pub empty: &'static str, + + /// Placeholder text for the search input. + #[props(default = "Search...")] + pub search_placeholder: &'static str, + + /// Label for the previous page button. + #[props(default = "Previous")] + pub previous_button: &'static str, + + /// Label for the next page button. + #[props(default = "Next")] + pub next_button: &'static str, + + /// Page indicator text with placeholders `{current}` and `{total}`. + #[props(default = "Page {current} of {total}")] + pub page_indicator: &'static str, +} + +impl Default for TableTexts { + fn default() -> Self { + Self { + loading: "Loading...", + empty: "No results found", + search_placeholder: "Search...", + previous_button: "Previous", + next_button: "Next", + page_indicator: "Page {current} of {total}", + } + } +} + +/// Defines the styling classes for each part of the table. +#[derive(Clone, PartialEq)] +pub struct TableClasses { + /// Wrapper around the entire table. + pub container: &'static str, + + /// Class for the `
` element. + pub table: &'static str, + + /// Class for the `` element. + pub thead: &'static str, + + /// Class for the `` element. + pub tbody: &'static str, + + /// Wrapper for pagination controls. + pub pagination: &'static str, + + /// Class for the search input field. + pub search_input: &'static str, + + /// Class for header cells (`` section of a table, based on the provided data and configuration. +/// +/// # Arguments +/// * `props` - The properties passed to the component. +/// - `columns` - A list of column definitions (`Vec`) specifying which fields to render. +/// - `rows` - A vector of row data (`Vec>`) to display. +/// - `loading` - A boolean flag indicating whether the table is in a loading state. +/// - `classes` - A `TableClasses` object defining CSS class names for customization. +/// - `texts` - A `TableTexts` object defining UI text like loading or empty messages. +/// +/// # Returns +/// (Html): A rendered `` element, containing: +/// - A loading row if `loading` is `true`. +/// - An empty state row if `rows` is empty. +/// - The list of rows otherwise. +/// +/// # Examples +/// ```rust +/// use table_rs::yew::body::TableBody; +/// use table_rs::yew::types::{TableBodyProps, Column, TableClasses, TableTexts}; +/// use yew::prelude::*; +/// use maplit::hashmap; +/// +/// #[function_component(App)] +/// pub fn app() -> Html { +/// let rows = vec![ +/// hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() }, +/// hashmap! { "name" => "Crab".to_string(), "email" => "crab@opensass.org".to_string() }, +/// ]; +/// +/// let columns = vec![ +/// Column { id: "name", header: "Name", ..Default::default() }, +/// Column { id: "email", header: "Email", ..Default::default() }, +/// ]; +/// +/// let props = TableBodyProps { +/// columns, +/// rows, +/// loading: false, +/// classes: Default::default(), +/// texts: Default::default(), +/// }; +/// +/// html! { +/// +/// } +/// } +/// ``` +/// +/// # See Also +/// - [MDN tbody Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/tbody) #[function_component(TableBody)] pub fn body(props: &TableBodyProps) -> Html { let TableBodyProps { @@ -17,26 +62,35 @@ pub fn body(props: &TableBodyProps) -> Html { rows, loading, classes, + texts, } = props; html! { { if *loading { - html! { } + html! { + + } } else if rows.is_empty() { - html! { } + html! { + + } } else { - html! { for rows.iter().map(|row| { - html! { - - { for columns.iter().map(|col| { - html! { - + html! { + for rows.iter().map(|row| { + html! { + + { + for columns.iter().map(|col| { + html! { + + } + }) } - }) } - - } - }) } + + } + }) + } } } } diff --git a/src/yew/controls.rs b/src/yew/controls.rs index d758a39..c729fd1 100644 --- a/src/yew/controls.rs +++ b/src/yew/controls.rs @@ -3,7 +3,12 @@ use yew::prelude::*; #[function_component(PaginationControls)] pub fn pagination_controls(props: &PaginationControlsProps) -> Html { - let PaginationControlsProps { page, total_pages } = props; + let PaginationControlsProps { + page, + total_pages, + classes, + texts, + } = props; let page_val = **page; let on_prev = { @@ -23,10 +28,20 @@ pub fn pagination_controls(props: &PaginationControlsProps) -> Html { }; html! { -
- - { format!(" Page {} of {} ", page_val + 1, total_pages) } - +
+ + + { texts.page_indicator.replace("{current}", &(page_val + 1).to_string()).replace("{total}", &total_pages.to_string()) } + +
} } diff --git a/src/yew/header.rs b/src/yew/header.rs index 5385210..bd46d85 100644 --- a/src/yew/header.rs +++ b/src/yew/header.rs @@ -1,6 +1,55 @@ use crate::yew::types::{SortOrder, TableHeaderProps}; use yew::prelude::*; +/// A table header component that renders column headers with optional sorting functionality. +/// +/// This component is part of the `table_rs` Yew integration and is responsible for rendering +/// the `
` section of a table. It supports sortable columns and emits sort events when +/// a sortable header is clicked. +/// +/// # Arguments +/// * `props` - The properties passed to the component. +/// - `columns` - A list of column definitions (`Vec`) specifying the headers to render. +/// - `sort_column` - An `Option<&'static str>` indicating the currently sorted column, if any. +/// - `sort_order` - A `SortOrder` indicating whether the sort is ascending or descending. +/// - `on_sort_column` - A `Callback<&'static str>` triggered when a sortable column is clicked. +/// - `classes` - A `TableClasses` object defining CSS class names for customization. +/// +/// # Returns +/// (Html): A rendered `` element containing the table header row and interactive sorting logic. +/// +/// # Examples +/// ```rust +/// use table_rs::yew::header::TableHeader; +/// use table_rs::yew::types::{TableHeaderProps, Column, SortOrder, TableClasses}; +/// use yew::prelude::*; +/// +/// #[function_component(App)] +/// pub fn app() -> Html { +/// let columns = vec![ +/// Column { id: "name", header: "Name", sortable: true, ..Default::default() }, +/// Column { id: "email", header: "Email", sortable: false, ..Default::default() }, +/// ]; +/// +/// let sort_order = use_state(|| SortOrder::Asc); +/// let sort_column = use_state(|| Some("name")); +/// +/// let props = TableHeaderProps { +/// columns, +/// sort_column: sort_column, +/// sort_order: sort_order, +/// on_sort_column: Callback::from(|col_id| web_sys::console::log_1(&format!("Sort: {}", col_id).into())), +/// classes: Default::default(), +/// }; +/// +/// html! { +/// +/// } +/// }; +/// ``` +/// +/// # See Also +/// - [MDN thead Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/thead) #[function_component(TableHeader)] pub fn header(props: &TableHeaderProps) -> Html { let TableHeaderProps { @@ -13,22 +62,19 @@ pub fn header(props: &TableHeaderProps) -> Html { html! { - + { for columns.iter().map(|col| { let col_id = col.id; - let is_sortable = col.sortable; - let onclick = if is_sortable { + let onclick = if col.sortable { let on_sort_column = on_sort_column.clone(); Some(Callback::from(move |_| on_sort_column.emit(col_id))) - } else { - None - }; + } else { None }; html! { `), body (``), +/// and optional features such as client-side sorting, pagination, and search input. +/// It is built using Yew and supports flexible styling and customization. +/// +/// # Arguments +/// * `props` - The properties passed to the component. +/// - `data` - A `Vec>` representing the table's row data. +/// - `columns` - A `Vec` defining the structure and behavior of each column. +/// - `page_size` - A `usize` defining how many rows to show per page. +/// - `loading` - A `bool` indicating whether the table is in a loading state. +/// - `classes` - A `TableClasses` struct for customizing class names of elements. +/// - `styles` - A `HashMap<&'static str, &'static str>` for inline style overrides. +/// - `paginate` - A `bool` controlling whether pagination controls are displayed. +/// - `search` - A `bool` enabling a search input above the table. +/// - `texts` - A `TableTexts` struct for customizing placeholder and fallback texts. +/// +/// # Features +/// - **Client-side search** with URL hydration via `?search=` +/// - **Column sorting** (ascending/descending toggle) +/// - **Pagination controls** +/// - **Custom class and inline style support** +/// - Displays a loading row or empty state message when appropriate +/// +/// # Returns +/// (Html): A complete, styled and interactive table component rendered in Yew. +/// +/// # Examples +/// ```rust +/// use yew::prelude::*; +/// use maplit::hashmap; +/// use table_rs::yew::table::Table; +/// use table_rs::yew::types::{Column, TableClasses, TableTexts}; +/// +/// #[function_component(App)] +/// pub fn app() -> Html { +/// let data = vec![ +/// hashmap! { "name" => "Ferris".into(), "email" => "ferris@opensass.org".into() }, +/// hashmap! { "name" => "Ferros".into(), "email" => "ferros@opensass.org".into() }, +/// ]; +/// +/// let columns = vec![ +/// Column { id: "name", header: "Name", sortable: true, ..Default::default() }, +/// Column { id: "email", header: "Email", sortable: false, ..Default::default() }, +/// ]; +/// +/// html! { +///
`). + pub header_cell: &'static str, + + /// Class for body cells (``). + pub body_cell: &'static str, + + /// Class for each table row. + pub row: &'static str, + + /// Class for the row shown while loading. + pub loading_row: &'static str, + + /// Class for the row shown when no data is found. + pub empty_row: &'static str, + + /// Class for pagination buttons. + pub pagination_button: &'static str, +} + +impl Default for TableClasses { + fn default() -> Self { + Self { + container: "table-container", + table: "table", + thead: "thead", + tbody: "tbody", + pagination: "pagination-controls", + search_input: "search-input", + header_cell: "th", + body_cell: "td", + row: "tr", + loading_row: "loading-row", + empty_row: "empty-row", + pagination_button: "pagination-button", + } + } +} + +/// Main props for the table component. +#[derive(PartialEq, Props, Clone)] +pub struct TableProps { + /// Data rows, where each row is a key-value map. + #[props(default)] + pub data: Vec>, + + /// Definitions of columns to display. + #[props(default)] + pub columns: Vec, + + /// Number of rows per page (default is 10). + #[props(default = 10)] + pub page_size: usize, + + /// Indicates whether the table is loading. + #[props(default)] + pub loading: bool, + + /// Enables pagination controls. + #[props(default = false)] + pub paginate: bool, + + /// Enables the search input field. + #[props(default = false)] + pub search: bool, + + /// Texts for various table UI messages. + #[props(default)] + pub texts: TableTexts, + + /// CSS classes for styling different parts of the table. + #[props(default)] + pub classes: TableClasses, +} + +/// Sort direction (ascending or descending). +#[derive(PartialEq, Clone, Copy, Default)] +pub enum SortOrder { + /// Ascending (default). + #[default] + Asc, + /// Descending. + Desc, +} diff --git a/src/yew/body.rs b/src/yew/body.rs index a574156..c3299be 100644 --- a/src/yew/body.rs +++ b/src/yew/body.rs @@ -1,15 +1,60 @@ -use crate::yew::types::{Column, TableClasses}; -use std::collections::HashMap; +use crate::yew::types::TableBodyProps; use yew::prelude::*; -#[derive(Properties, PartialEq)] -pub struct TableBodyProps { - pub columns: Vec, - pub rows: Vec>, - pub loading: bool, - pub classes: TableClasses, -} - +/// A table body component that handles rendering of table rows, empty state, and loading state. +/// +/// This component is part of the `table_rs` Yew integration and is responsible for rendering +/// the `
{ "Loading..." }
{ texts.loading }
{ "No results found" }
{ texts.empty }
{ row.get(col.id).unwrap_or(&"".to_string()) }
{ row.get(col.id).unwrap_or(&"".to_string()) }
` element, including headers (`
+/// } +/// } +/// ``` +/// +/// # See Also +/// - [MDN table Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/table) #[function_component(Table)] pub fn table(props: &TableProps) -> Html { let TableProps { @@ -20,6 +84,7 @@ pub fn table(props: &TableProps) -> Html { styles, paginate, search, + texts, } = props; let page = use_state(|| 0); @@ -132,7 +197,7 @@ pub fn table(props: &TableProps) -> Html { class={classes.search_input} type="text" value={(*search_query).clone()} - placeholder="Search..." + placeholder={texts.search_placeholder} aria-label="Search table" oninput={on_search_change} /> diff --git a/src/yew/types.rs b/src/yew/types.rs index 6f50e13..5a5d3a4 100644 --- a/src/yew/types.rs +++ b/src/yew/types.rs @@ -1,80 +1,284 @@ use std::collections::HashMap; use yew::prelude::*; +/// Represents a column in the table with customization options. #[derive(Properties, PartialEq, Clone, Default)] pub struct Column { + /// Unique identifier for the column. + #[prop_or("")] pub id: &'static str, + + /// Header text displayed at the top of the column. + #[prop_or("")] pub header: &'static str, + + /// Callback triggered when accessing column data; can be used for custom rendering. + #[prop_or(Callback::noop())] pub accessor: Callback<()>, + + /// Determines if the column is sortable. + #[prop_or(false)] pub sortable: bool, + + /// Minimum width of the column in pixels. #[prop_or(100)] pub min_width: u32, - #[prop_or_default] - pub class: Option<&'static str>, - #[prop_or_default] + + /// Optional inline style string for the column header. + #[prop_or(Some("padding: 8px; font-weight: 600; text-align: left;"))] pub style: Option<&'static str>, + + /// Optional class name(s) for the column header. + #[prop_or(Some("table-header-cell"))] + pub class: Option<&'static str>, } -#[derive(Clone, PartialEq)] +/// Sort direction for a column: ascending or descending. +#[derive(Clone, PartialEq, Default)] pub enum SortOrder { + /// Ascending order (default). + #[default] Asc, + + /// Descending order. Desc, } -#[derive(Properties, PartialEq, Clone, Default)] +/// Class names used to style various parts of the table. +#[derive(Properties, PartialEq, Clone)] pub struct TableClasses { + /// Class name for the container wrapping the table. #[prop_or("table-container")] pub container: &'static str, + + /// Class name for the table element. #[prop_or("table")] pub table: &'static str, + + /// Class name for the `` element. #[prop_or("thead")] pub thead: &'static str, + + /// Class name for the `` element. #[prop_or("tbody")] pub tbody: &'static str, + + /// Class name for pagination controls wrapper. #[prop_or("pagination-controls")] pub pagination: &'static str, + + /// Class name for the search input. #[prop_or("search-input")] pub search_input: &'static str, + + /// Class name for header cells (``). + #[prop_or("tr")] + pub row: &'static str, + + /// Class name for the loading row. + #[prop_or("loading-row")] + pub loading_row: &'static str, + + /// Class name for the empty row. + #[prop_or("empty-row")] + pub empty_row: &'static str, + + /// Class name for pagination buttons. + #[prop_or("pagination-button")] + pub pagination_button: &'static str, +} + +impl Default for TableClasses { + fn default() -> Self { + Self { + container: "table-container", + table: "table", + thead: "thead", + tbody: "tbody", + pagination: "pagination-controls", + search_input: "search-input", + header_cell: "th", + body_cell: "td", + row: "tr", + loading_row: "loading-row", + empty_row: "empty-row", + pagination_button: "pagination-button", + } + } } -#[derive(Properties, PartialEq)] +/// Texts used in various parts of the table UI. +#[derive(Properties, PartialEq, Clone)] +pub struct TableTexts { + /// Text shown while data is loading. + #[prop_or("Loading...")] + pub loading: &'static str, + + /// Text shown when no rows are found. + #[prop_or("No results found")] + pub empty: &'static str, + + /// Placeholder text for the search input. + #[prop_or("Search...")] + pub search_placeholder: &'static str, + + /// Label for the "Previous" pagination button. + #[prop_or("Previous")] + pub previous_button: &'static str, + + /// Label for the "Next" pagination button. + #[prop_or("Next")] + pub next_button: &'static str, + + /// Format string for the page indicator, e.g., "Page 1 of 5". + #[prop_or("Page {current} of {total}")] + pub page_indicator: &'static str, +} + +impl Default for TableTexts { + fn default() -> Self { + Self { + loading: "Loading...", + empty: "No results found", + search_placeholder: "Search...", + previous_button: "Previous", + next_button: "Next", + page_indicator: "Page {current} of {total}", + } + } +} + +/// Props for the main table component. +#[derive(Properties, PartialEq, Clone)] pub struct TableProps { + /// Vector of row data as key-value pairs. + #[prop_or_default] pub data: Vec>, + + /// List of column definitions. + #[prop_or_default] pub columns: Vec, + + /// Number of rows per page. #[prop_or(10)] pub page_size: usize, + + /// Whether the table is currently in a loading state. #[prop_or(false)] pub loading: bool, + + /// Class names used to style the table. #[prop_or_default] pub classes: TableClasses, + + /// Optional inline styles mapped by component part. #[prop_or_default] pub styles: HashMap<&'static str, &'static str>, + + /// Whether to enable pagination. #[prop_or(false)] pub paginate: bool, + + /// Whether to enable search functionality. #[prop_or(false)] pub search: bool, + + /// Text labels for the table UI. + #[prop_or_default] + pub texts: TableTexts, } -#[derive(Properties, PartialEq)] +/// Props for the table header including sorting logic. +#[derive(Properties, PartialEq, Clone)] pub struct HeaderProps { + /// Column definitions for the header. + #[prop_or_default] pub columns: Vec, + + /// Current column used for sorting. pub sort_column: UseStateHandle>, + + /// Current sort order (ascending/descending). pub sort_order: UseStateHandle, + + /// Callback triggered when a column header is clicked for sorting. + #[prop_or(Callback::noop())] pub on_sort_column: Callback<&'static str>, + + /// CSS classes used to style the header. + #[prop_or_default] pub classes: TableClasses, } -#[derive(Properties, PartialEq)] +/// Alias for `HeaderProps` to be used explicitly in header components. +#[derive(Properties, PartialEq, Clone)] pub struct TableHeaderProps { + /// Column definitions for the header. + #[prop_or_default] pub columns: Vec, + + /// Current column used for sorting. pub sort_column: UseStateHandle>, + + /// Current sort order (ascending/descending). pub sort_order: UseStateHandle, + + /// Callback triggered when a column is sorted. + #[prop_or(Callback::noop())] pub on_sort_column: Callback<&'static str>, + + /// CSS classes used to style the table header. + #[prop_or_default] pub classes: TableClasses, } -#[derive(Properties, PartialEq)] +/// Props for the pagination controls component. +#[derive(Properties, PartialEq, Clone)] pub struct PaginationControlsProps { + /// Current page index. pub page: UseStateHandle, + + /// Total number of pages. + #[prop_or(1)] pub total_pages: usize, + + /// Class names used to style pagination elements. + #[prop_or_default] + pub classes: TableClasses, + + /// Texts used in pagination controls. + #[prop_or_default] + pub texts: TableTexts, +} + +/// Props for rendering the body of the table. +#[derive(Properties, PartialEq, Clone)] +pub struct TableBodyProps { + /// Column definitions. + #[prop_or_default] + pub columns: Vec, + + /// List of row data to render. + #[prop_or_default] + pub rows: Vec>, + + /// Indicates if the body is in a loading state. + #[prop_or(false)] + pub loading: bool, + + /// Class names used to style the table body. + #[prop_or_default] + pub classes: TableClasses, + + /// Text labels used in the body (e.g., loading, empty). + #[prop_or_default] + pub texts: TableTexts, }
`). + #[prop_or("th")] + pub header_cell: &'static str, + + /// Class name for body cells (``). + #[prop_or("td")] + pub body_cell: &'static str, + + /// Class name for table rows (`