├── preview
├── src
│ ├── components
│ │ ├── form
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── style.css
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── avatar
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── style.css
│ │ ├── button
│ │ │ ├── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── style.css
│ │ │ └── component.rs
│ │ ├── calendar
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ ├── simple
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── main
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── multi_month
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── range
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── internationalized
│ │ │ │ │ └── mod.rs
│ │ │ │ └── unavailable_dates
│ │ │ │ │ └── mod.rs
│ │ │ └── docs.md
│ │ ├── checkbox
│ │ │ ├── mod.rs
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── style.css
│ │ │ └── component.rs
│ │ ├── dialog
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── component.rs
│ │ ├── input
│ │ │ ├── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── style.css
│ │ ├── label
│ │ │ ├── mod.rs
│ │ │ ├── style.css
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── component.rs
│ │ ├── menubar
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── component.rs
│ │ ├── navbar
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ └── docs.md
│ │ ├── popover
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── progress
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── style.css
│ │ │ ├── component.rs
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── select
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── skeleton
│ │ │ ├── mod.rs
│ │ │ ├── component.rs
│ │ │ ├── style.css
│ │ │ ├── docs.md
│ │ │ ├── component.json
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── slider
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ └── style.css
│ │ ├── switch
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ └── style.css
│ │ ├── tabs
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── style.css
│ │ ├── toast
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── component.rs
│ │ │ ├── docs.md
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── toggle
│ │ │ ├── mod.rs
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.json
│ │ │ ├── style.css
│ │ │ └── component.rs
│ │ ├── toolbar
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── style.css
│ │ │ ├── docs.md
│ │ │ └── component.rs
│ │ ├── tooltip
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ └── component.rs
│ │ ├── accordion
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── style.css
│ │ ├── alert_dialog
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── docs.md
│ │ ├── aspect_ratio
│ │ │ ├── mod.rs
│ │ │ ├── component.rs
│ │ │ ├── style.css
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── collapsible
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── style.css
│ │ ├── context_menu
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── style.css
│ │ │ └── component.rs
│ │ ├── date_picker
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ ├── main
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── range
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── multi_month
│ │ │ │ │ └── mod.rs
│ │ │ │ ├── internationalized
│ │ │ │ │ └── mod.rs
│ │ │ │ └── unavailable_dates
│ │ │ │ │ └── mod.rs
│ │ │ └── style.css
│ │ ├── dropdown_menu
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ └── component.rs
│ │ ├── hover_card
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ └── component.rs
│ │ ├── radio_group
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ └── style.css
│ │ ├── scroll_area
│ │ │ ├── mod.rs
│ │ │ ├── style.css
│ │ │ ├── component.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── variants
│ │ │ │ └── main
│ │ │ │ └── mod.rs
│ │ ├── separator
│ │ │ ├── mod.rs
│ │ │ ├── style.css
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── component.json
│ │ │ ├── component.rs
│ │ │ └── docs.md
│ │ ├── sheet
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ └── docs.md
│ │ ├── textarea
│ │ │ ├── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.json
│ │ │ └── variants
│ │ │ │ ├── fade
│ │ │ │ └── mod.rs
│ │ │ │ ├── ghost
│ │ │ │ └── mod.rs
│ │ │ │ ├── main
│ │ │ │ └── mod.rs
│ │ │ │ └── outline
│ │ │ │ └── mod.rs
│ │ ├── toggle_group
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── variants
│ │ │ │ └── main
│ │ │ │ │ └── mod.rs
│ │ │ ├── docs.md
│ │ │ ├── component.rs
│ │ │ └── style.css
│ │ └── card
│ │ │ ├── mod.rs
│ │ │ ├── component.json
│ │ │ ├── docs.md
│ │ │ └── style.css
│ └── i18n
│ │ ├── de-DE.ftl
│ │ ├── fr-FR.ftl
│ │ ├── en-US.ftl
│ │ └── es-ES.ftl
├── .DS_Store
├── assets
│ ├── .DS_Store
│ ├── dioxus-logo.png
│ ├── github-mark
│ │ ├── .DS_Store
│ │ ├── github-mark-white.svg
│ │ └── github-mark.svg
│ └── language-select.css
├── .stylelintrc.json
├── index.html
├── package.json
└── Cargo.toml
├── test-harness
├── src
│ ├── components
│ │ └── mod.rs
│ └── main.rs
├── assets
│ ├── favicon.ico
│ └── main.css
├── .gitignore
└── Cargo.toml
├── primitives
├── src
│ ├── js
│ │ ├── hash.txt
│ │ └── focus-trap.js
│ ├── select
│ │ └── components
│ │ │ └── mod.rs
│ ├── label.rs
│ └── aspect_ratio.rs
├── build.rs
├── Cargo.toml
└── README.md
├── .DS_Store
├── playwright
├── package.json
├── input.spec.ts
├── radio-group.spec.ts
├── collapsible.spec.ts
├── avatar.spec.ts
├── checkbox.spec.ts
├── toggle.spec.ts
├── switch.spec.ts
├── toast.spec.ts
├── tooltip.spec.ts
├── hover-card.spec.ts
├── accordion.spec.ts
├── toolbar.spec.ts
├── popover.spec.ts
├── dialog.spec.ts
├── toggle_group.spec.ts
├── slider.spec.ts
├── tabs.spec.ts
├── preview.spec.ts
└── alert-dialog.spec.ts
├── .gitignore
├── Cargo.toml
├── .github
├── actions
│ └── free-disk-space
│ │ └── action.yaml
└── workflows
│ ├── clean-up-preview.yml
│ └── stylelint.yml
├── LICENSE-MIT
└── component.json
/preview/src/components/form/docs.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test-harness/src/components/mod.rs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/preview/src/components/form/component.rs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/primitives/src/js/hash.txt:
--------------------------------------------------------------------------------
1 | [11982134631128731377]
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/.DS_Store
--------------------------------------------------------------------------------
/preview/src/components/avatar/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/button/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/calendar/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/checkbox/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/dialog/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/form/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/input/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/label/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/menubar/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/navbar/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/popover/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/progress/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/select/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/skeleton/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/slider/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/switch/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/tabs/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/toast/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/toggle/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/toolbar/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/tooltip/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/accordion/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/alert_dialog/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/collapsible/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/context_menu/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/date_picker/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/dropdown_menu/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/hover_card/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/radio_group/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/separator/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/sheet/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
3 |
--------------------------------------------------------------------------------
/preview/src/components/textarea/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
3 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
--------------------------------------------------------------------------------
/preview/src/components/card/mod.rs:
--------------------------------------------------------------------------------
1 | mod component;
2 | pub use component::*;
3 |
4 |
--------------------------------------------------------------------------------
/preview/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/preview/.DS_Store
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/style.css:
--------------------------------------------------------------------------------
1 | /* Scroll area doesn't require any additional styles */
--------------------------------------------------------------------------------
/preview/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/preview/assets/.DS_Store
--------------------------------------------------------------------------------
/preview/assets/dioxus-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/preview/assets/dioxus-logo.png
--------------------------------------------------------------------------------
/test-harness/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/test-harness/assets/favicon.ico
--------------------------------------------------------------------------------
/preview/assets/github-mark/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DioxusLabs/components/HEAD/preview/assets/github-mark/.DS_Store
--------------------------------------------------------------------------------
/test-harness/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target
4 | .DS_Store
5 |
6 | # These are backup files generated by rustfmt
7 | **/*.rs.bk
8 |
--------------------------------------------------------------------------------
/preview/src/components/label/style.css:
--------------------------------------------------------------------------------
1 | /* Label Styles */
2 | .label {
3 | display: flex;
4 | align-items: center;
5 | color: var(--secondary-color-4);
6 | font-size: 0.8rem;
7 | line-height: 1;
8 | }
9 |
--------------------------------------------------------------------------------
/playwright/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "@axe-core/playwright": "^4.10.2",
4 | "@playwright/test": "^1.53.0",
5 | "axe-playwright": "^2.1.0",
6 | "playwright": "^1.53.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
3 | **/.claude/settings.local.json
4 | node_modules
5 | package-lock.json
6 |
7 | # Playwright
8 | /test-results/
9 | /playwright/playwright-report/
10 | /playwright/test-results/
11 | /blob-report/
12 | /playwright/.cache/
13 |
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::scroll_area::{self, ScrollAreaProps};
3 |
4 | #[component]
5 | pub fn ScrollArea(props: ScrollAreaProps) -> Element {
6 | scroll_area::ScrollArea(props)
7 | }
8 |
--------------------------------------------------------------------------------
/preview/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard", "stylelint-config-idiomatic-order"],
3 | "rules": {
4 | "number-max-precision": null,
5 | "no-descending-specificity": null
6 | },
7 | "ignoreFiles": ["assets/prism.css", "**/*.js"]
8 | }
--------------------------------------------------------------------------------
/preview/src/components/checkbox/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | Checkbox { name: "tos-check", aria_label: "Demo Checkbox" }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::aspect_ratio::AspectRatioProps;
3 |
4 | #[component]
5 | pub fn AspectRatio(props: AspectRatioProps) -> Element {
6 | dioxus_primitives::aspect_ratio::AspectRatio(props)
7 | }
8 |
--------------------------------------------------------------------------------
/primitives/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | // If any TS files change, re-run the build script
3 | lazy_js_bundle::LazyTypeScriptBindings::new()
4 | .with_watching("./src/ts")
5 | .with_binding("./src/ts/focus-trap.ts", "./src/js/focus-trap.js")
6 | .run();
7 | }
8 |
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/style.css:
--------------------------------------------------------------------------------
1 | .aspect-ratio-container {
2 | overflow: hidden;
3 | box-sizing: border-box;
4 | padding: 1rem;
5 | border-radius: .5rem;
6 | }
7 |
8 | .aspect-ratio-image {
9 | width: 100%;
10 | height: 100%;
11 | object-fit: cover;
12 | }
13 |
--------------------------------------------------------------------------------
/preview/src/components/toggle/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | Toggle { width: "2rem", height: "2rem",
8 | em { "B" }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/preview/src/components/separator/style.css:
--------------------------------------------------------------------------------
1 | .separator {
2 | background-color: var(--primary-color-6);
3 | }
4 |
5 | .separator[data-orientation="horizontal"] {
6 | width: 100%;
7 | height: 1px;
8 | }
9 |
10 | .separator[data-orientation="vertical"] {
11 | width: 1px;
12 | height: 100%;
13 | }
--------------------------------------------------------------------------------
/preview/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dioxus Components
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/preview/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "create-stylelint": "^0.5.0"
4 | },
5 | "devDependencies": {
6 | "stylelint": "^16.20.0",
7 | "stylelint-config-idiomatic-order": "^10.0.0",
8 | "stylelint-config-standard": "^38.0.0",
9 | "stylelint-order": "^6.0.4"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/preview/src/components/button/docs.md:
--------------------------------------------------------------------------------
1 | The button element is used to trigger actions or events in a user interface.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | button {
7 | // Global html attributes
8 | class: "button",
9 | "data-style": "outline",
10 | // Children
11 | {children}
12 | }
13 | ```
14 |
--------------------------------------------------------------------------------
/preview/src/components/skeleton/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 |
3 | #[component]
4 | pub fn Skeleton(#[props(extends=GlobalAttributes)] attributes: Vec) -> Element {
5 | rsx! {
6 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
7 | div { class: "skeleton", ..attributes }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/preview/src/components/textarea/docs.md:
--------------------------------------------------------------------------------
1 | The textarea element is used to allow users to enter multi-line text input in a user interface.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | textarea {
7 | // Global html attributes
8 | class: "textarea",
9 | "data-style": "default",
10 | // Children
11 | {children}
12 | }
13 | ```
14 |
15 |
--------------------------------------------------------------------------------
/preview/src/components/input/docs.md:
--------------------------------------------------------------------------------
1 | The input element is used to accept a text input from the user. It can be styled and configured to fit various use cases, such as forms or search bars.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | input {
7 | // Global html attributes
8 | class: "input",
9 | // Children
10 | {children}
11 | }
12 | ```
13 |
--------------------------------------------------------------------------------
/preview/src/components/skeleton/style.css:
--------------------------------------------------------------------------------
1 | .skeleton {
2 | border-radius: 0.375rem;
3 | animation: skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
4 | background-color: var(--primary-color-5);
5 | }
6 |
7 | @keyframes skeleton-pulse {
8 | 0%,
9 | 100% {
10 | opacity: 1;
11 | }
12 |
13 | 61.8% {
14 | opacity: 0.5;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | resolver = "3"
3 | members = ["primitives", "preview", "test-harness"]
4 |
5 | [workspace.dependencies]
6 | dioxus-primitives = { path = "primitives" }
7 |
8 | dioxus = "0.7.0"
9 | tracing = { version = "0.1", features = ["std"] }
10 |
11 | [profile.release]
12 | opt-level = "z"
13 | debug = false
14 | lto = true
15 | codegen-units = 1
16 | strip = true
17 | incremental = false
18 |
--------------------------------------------------------------------------------
/preview/src/components/separator/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | "One thing"
8 | Separator {
9 | style: "margin: 15px 0; width: 50%;",
10 | horizontal: true,
11 | decorative: true,
12 | }
13 | "Another thing"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/preview/src/components/navbar/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "navbar",
3 | "description": "A navbar component for navigation between pages.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components",
10 | "features": ["router"]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/playwright/input.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=input&", {
5 | timeout: 20 * 60 * 1000,
6 | }); // Increase timeout to 20 minutes
7 |
8 | await page.getByRole('textbox', { name: 'Enter your name' }).fill('name');
9 | await expect(page.locator('#input-greeting')).toContainText('Hello, name!');
10 | });
11 |
--------------------------------------------------------------------------------
/preview/src/components/tabs/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tabs",
3 | "description": "A tabbed interface component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/toggle/docs.md:
--------------------------------------------------------------------------------
1 | The Toggle component is used to create a toggle button that can be pressed or unpressed. It is useful for creating a binary option, such as enabling or disabling a feature.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Toggle component is used to create a toggle button that can be pressed or unpressed.
7 | Toggle {
8 | // The contents of the toggle button.
9 | "Bold",
10 | }
11 | ```
12 |
--------------------------------------------------------------------------------
/preview/src/components/slider/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slider",
3 | "description": "An accessable slider component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/switch/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "switch",
3 | "description": "A togglable switch component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/toast/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toast",
3 | "description": "A toast notification component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/checkbox/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "checkbox",
3 | "description": "A togglable checkbox component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/toggle/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toggle",
3 | "description": "A simple toggle button component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/.github/actions/free-disk-space/action.yaml:
--------------------------------------------------------------------------------
1 | name: Free Disk Space
2 | description: Free up disk space on the runner
3 | runs:
4 | using: composite
5 | steps:
6 | - name: Free Disk Space (Ubuntu)
7 | if: runner.os == 'Linux'
8 | shell: bash
9 | run: |
10 | echo "Freeing up disk space..."
11 | sudo rm -rf /opt/ghc
12 | sudo rm -rf /usr/share/dotnet
13 | sudo rm -rf /usr/local/lib/android
14 | sudo rm -rf /usr/share/swift
--------------------------------------------------------------------------------
/preview/src/components/form/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "form",
3 | "description": "A form component for collecting user input.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/progress/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "progress",
3 | "description": "An accessable progress bar indicator.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scroll_area",
3 | "description": "A scrollable area component.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/skeleton/docs.md:
--------------------------------------------------------------------------------
1 | The Skeleton component is used to display a placeholder preview of content before the data gets loaded. This helps improve perceived performance and provides users with a visual indication that content is being loaded.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | Skeleton {
7 | // Accepts all GlobalAttributes, commonly used with style for sizing
8 | style: "width: 15rem; height: 1rem;",
9 | }
10 | ```
11 |
--------------------------------------------------------------------------------
/preview/src/components/input/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "input",
3 | "description": "An input field component for user text entry.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/label/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "label",
3 | "description": "An accessible label component for form elements.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/label/docs.md:
--------------------------------------------------------------------------------
1 | The Label component is used to provide a label for form elements, enhancing accessibility and usability. It is typically used in conjunction with form controls like inputs, selects, and textareas.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | Label {
7 | html_for: "id", // The ID of the form control this label is associated with
8 | }
9 | button {
10 | id: "id", // The ID of the labeled element
11 | }
12 | ```
13 |
--------------------------------------------------------------------------------
/preview/src/components/popover/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "popover",
3 | "description": "A popover component for colapsible content.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "calendar",
3 | "description": "A calendar grid component for selecting dates.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/dialog/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dialog",
3 | "description": "A dialog component for displaying modal content.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/select/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "select",
3 | "description": "A select dropdown component with typeahead support.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/toolbar/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toolbar",
3 | "description": "A toolbar component for grouping related inputs.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/label/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::components::input::component::Input;
2 |
3 | use super::super::component::*;
4 | use dioxus::prelude::*;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | rsx! {
9 | div { display: "flex", flex_direction: "column", gap: ".5rem",
10 | Label { html_for: "name", "Name" }
11 |
12 | Input { id: "name", placeholder: "Enter your name" }
13 | }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/preview/src/components/menubar/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "menubar",
3 | "description": "A menubar component for a collection of menu items.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/button/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "button",
3 | "description": "A button component for triggering actions or events when clicked.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/separator/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "separator",
3 | "description": "A visual separator between different sections of the page.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/avatar/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "avatar",
3 | "description": "An avatar component for displaying user profile images or initials.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/radio_group/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "radio_group",
3 | "description": "A group of radio buttons for selecting one option from a set.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/tooltip/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tooltip",
3 | "description": "A tooltip component for additional information on hover or focus.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/i18n/de-DE.ftl:
--------------------------------------------------------------------------------
1 | ## Months
2 | January = Januar
3 | February = Februar
4 | March = März
5 | April = April
6 | May = Mai
7 | June = Juni
8 | July = Juli
9 | August = August
10 | September = September
11 | October = Oktober
12 | November = November
13 | December = Dezember
14 |
15 | ## Weekdays
16 | Monday = Mo
17 | Tuesday = Di
18 | Wednesday = Mi
19 | Thursday = Do
20 | Friday = Fr
21 | Saturday = Sa
22 | Sunday = So
23 |
24 | ## Date
25 | D_Abbr = T
26 | M_Abbr = M
27 | Y_Abbr = J
--------------------------------------------------------------------------------
/preview/src/i18n/fr-FR.ftl:
--------------------------------------------------------------------------------
1 | ## Months
2 | January = Janvier
3 | February = Février
4 | March = Mars
5 | April = Avril
6 | May = Mai
7 | June = Juin
8 | July = Juillet
9 | August = Août
10 | September = Septembre
11 | October = Octobre
12 | November = Novembre
13 | December = Décembre
14 |
15 | ## Weekdays
16 | Monday = Lu
17 | Tuesday = Ma
18 | Wednesday = Me
19 | Thursday = Je
20 | Friday = Ve
21 | Saturday = Sa
22 | Sunday = Di
23 |
24 | ## Date
25 | D_Abbr = J
26 | M_Abbr = M
27 | Y_Abbr = A
--------------------------------------------------------------------------------
/preview/src/components/accordion/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "accordion",
3 | "description": "An accordion component for displaying collapsible content sections.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/collapsible/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "collapsible",
3 | "description": "A collapsible component for showing and hiding content sections.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/dropdown_menu/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropdown_menu",
3 | "description": "A dropdown menu component for selecting options from a list.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/label/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::label::{self, LabelProps};
3 |
4 | #[component]
5 | pub fn Label(props: LabelProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | label::Label {
9 | class: "label",
10 | html_for: props.html_for,
11 | attributes: props.attributes,
12 | {props.children}
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/preview/src/i18n/en-US.ftl:
--------------------------------------------------------------------------------
1 | ## Months
2 | January = January
3 | February = February
4 | March = March
5 | April = April
6 | May = May
7 | June = June
8 | July = July
9 | August = August
10 | September = September
11 | October = October
12 | November = November
13 | December = December
14 |
15 | ## Weekdays
16 | Monday = Mo
17 | Tuesday = Tu
18 | Wednesday = We
19 | Thursday = Th
20 | Friday = Fr
21 | Saturday = Sa
22 | Sunday = Su
23 |
24 | ## Date
25 | D_Abbr = D
26 | M_Abbr = M
27 | Y_Abbr = Y
--------------------------------------------------------------------------------
/preview/src/i18n/es-ES.ftl:
--------------------------------------------------------------------------------
1 | ## Months
2 | January = Enero
3 | February = Febrero
4 | March = Marzo
5 | April = Abril
6 | May = Mayo
7 | June = Junio
8 | July = Julio
9 | August = Agosto
10 | September = Septiembre
11 | October = Octubre
12 | November = Noviembre
13 | December = Diciembre
14 |
15 | ## Weekdays
16 | Monday = Lu
17 | Tuesday = Ma
18 | Wednesday = Mi
19 | Thursday = Ju
20 | Friday = Vi
21 | Saturday = Sa
22 | Sunday = Do
23 |
24 | ## Date
25 | D_Abbr = D
26 | M_Abbr = M
27 | Y_Abbr = Y
--------------------------------------------------------------------------------
/preview/src/components/checkbox/docs.md:
--------------------------------------------------------------------------------
1 | The Checkbox component is used to create an accessible checkbox input.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The checkbox component creates a checkbox button
7 | Checkbox {
8 | // The checkbox indicator is a child component which will only be visible when the checkbox is checked.
9 | CheckboxIndicator {
10 | // The content of the checkbox indicator, typically an icon or image.
11 | {children}
12 | }
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/preview/src/components/hover_card/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hover_card",
3 | "description": "A hover card component for displaying additional information on hover.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/radio_group/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | RadioGroup {
8 | RadioItem { value: "option1".to_string(), index: 0usize, "Blue" }
9 | RadioItem { value: "option2".to_string(), index: 1usize, "Red" }
10 | RadioItem { value: "option3".to_string(), index: 2usize, disabled: true, "Green" }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toggle_group",
3 | "description": "A group of toggle buttons for selecting one or more options from a set.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/card/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "card",
3 | "description": "A simple card component",
4 | "authors": [
5 | "zhiyanzhaijie"
6 | ],
7 | "exclude": [
8 | "variants",
9 | "docs.md",
10 | "component.json"
11 | ],
12 | "cargoDependencies": [
13 | {
14 | "name": "dioxus-primitives",
15 | "git": "https://github.com/DioxusLabs/components"
16 | }
17 | ],
18 | "globalAssets": [
19 | "../../../assets/dx-components-theme.css"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/alert_dialog/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alert_dialog",
3 | "description": "An alert dialog component for displaying important messages and requiring user confirmation.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aspect_ratio",
3 | "description": "An aspect ratio component for maintaining a consistent width-to-height ratio of an element.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/context_menu/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "context_menu",
3 | "description": "A context menu component for displaying a list of actions or option after right clicking an area.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "globalAssets": ["../../../assets/dx-components-theme.css"]
13 | }
14 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "date_picker",
3 | "description": "A date picker component to select or input dates.",
4 | "authors": ["Evan Almloff"],
5 | "exclude": ["variants", "docs.md", "component.json"],
6 | "cargoDependencies": [
7 | {
8 | "name": "dioxus-primitives",
9 | "git": "https://github.com/DioxusLabs/components"
10 | }
11 | ],
12 | "componentDependencies": ["calendar", "popover"],
13 | "globalAssets": ["../../../assets/dx-components-theme.css"]
14 | }
15 |
--------------------------------------------------------------------------------
/preview/src/components/progress/docs.md:
--------------------------------------------------------------------------------
1 | The Progress component is used to display the progress of a task or operation. It can be used to indicate loading states, file uploads, or any other process that takes time to complete.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | Progress {
7 | // The current progress value (0 to max
8 | value: 0.5,
9 | // The maximum value of the progress (default is 100.0)
10 | max: 1.0,
11 | // Elements that will be displayed inside the progress bar
12 | {children}
13 | }
14 | ```
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/docs.md:
--------------------------------------------------------------------------------
1 | The AspectRatio component is used to maintain a specific aspect ratio for its children. This is particularly useful for responsive designs where you want to ensure that an element retains its proportions regardless of the screen size.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | AspectRatio {
7 | // The aspect ratio to maintain (width / height)
8 | ratio: 4. / 3.,
9 | // The children of the AspectRatio component will be rendered within it.
10 | {children}
11 | }
12 | ```
13 |
--------------------------------------------------------------------------------
/preview/src/components/input/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | let mut name = use_signal(String::new);
7 | rsx! {
8 | Input {
9 | oninput: move |e: FormEvent| name.set(e.value()),
10 | placeholder: "Enter your name",
11 | value: name,
12 | }
13 | if !name.read().is_empty() {
14 | p { id: "input-greeting", "Hello, {name}!" }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/preview/src/components/skeleton/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "skeleton",
3 | "description": "A placeholder component for all loading elements.",
4 | "authors": [
5 | "zhiyanzhaijie"
6 | ],
7 | "exclude": [
8 | "variants",
9 | "docs.md",
10 | "component.json"
11 | ],
12 | "cargoDependencies": [
13 | {
14 | "name": "dioxus-primitives",
15 | "git": "https://github.com/DioxusLabs/components"
16 | }
17 | ],
18 | "globalAssets": [
19 | "../../../assets/dx-components-theme.css"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/sheet/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sheet",
3 | "description": "A sheet component as an edge panel that complements the main content",
4 | "authors": [
5 | "zhiyanzhaijie"
6 | ],
7 | "exclude": [
8 | "variants",
9 | "docs.md",
10 | "component.json"
11 | ],
12 | "cargoDependencies": [
13 | {
14 | "name": "dioxus-primitives",
15 | "git": "https://github.com/DioxusLabs/components"
16 | }
17 | ],
18 | "globalAssets": [
19 | "../../../assets/dx-components-theme.css"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/toast/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::toast::{self, ToastProviderProps};
3 |
4 | #[component]
5 | pub fn ToastProvider(props: ToastProviderProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | toast::ToastProvider {
9 | default_duration: props.default_duration,
10 | max_toasts: props.max_toasts,
11 | render_toast: props.render_toast,
12 | {props.children}
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/docs.md:
--------------------------------------------------------------------------------
1 | The ScrollArea component is used to create a scrollable container for its children. It can be used to enable scrolling for content that overflows the available space.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The ScrollArea component wraps all scrollable content.
7 | ScrollArea {
8 | // The direction in which the scroll area can scroll. Can be one of Horizontal, Vertical, or Both.
9 | scroll_direction: ScrollDirection::Vertical,
10 | // The content of the scrollable area
11 | {children}
12 | }
13 | ```
--------------------------------------------------------------------------------
/preview/src/components/textarea/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "textarea",
3 | "description": "a textarea component used to allow users to enter multi-line text input",
4 | "authors": [
5 | "zhiyanzhaijie"
6 | ],
7 | "exclude": [
8 | "variants",
9 | "docs.md",
10 | "component.json"
11 | ],
12 | "cargoDependencies": [
13 | {
14 | "name": "dioxus-primitives",
15 | "git": "https://github.com/DioxusLabs/components"
16 | }
17 | ],
18 | "globalAssets": [
19 | "../../../assets/dx-components-theme.css"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/playwright/radio-group.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=radio_group&", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | await page.getByRole('radio', { name: 'Blue' }).click();
6 | await page.keyboard.press('ArrowDown');
7 | await expect(page.getByRole('radio', { name: 'Red' })).toBeFocused();
8 | await page.keyboard.press('ArrowDown');
9 | await expect(page.getByRole('radio', { name: 'Blue' })).toBeFocused();
10 | });
11 |
--------------------------------------------------------------------------------
/preview/src/components/separator/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::separator::{self, SeparatorProps};
3 |
4 | #[component]
5 | pub fn Separator(props: SeparatorProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | separator::Separator {
9 | class: "separator",
10 | horizontal: props.horizontal,
11 | decorative: props.decorative,
12 | attributes: props.attributes,
13 | {props.children}
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 |
8 | ToggleGroup { horizontal: true, allow_multiple_pressed: true,
9 | ToggleItem { index: 0usize,
10 | b { "B" }
11 | }
12 | ToggleItem { index: 1usize,
13 | i { "I" }
14 | }
15 | ToggleItem { index: 2usize,
16 | u { "U" }
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/preview/src/components/separator/docs.md:
--------------------------------------------------------------------------------
1 | The Separator component is a simple horizontal or vertical line that can be used to visually separate content in your application.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Separator component can be oriented horizontally or vertically.
7 | Separator {
8 | // The orientation of the separator line. true for horizontal, false for vertical.
9 | horizontal: true,
10 | // The decorative property controls if the separator is decorative and should not be visible to screen readers.
11 | decorative: false,
12 | }
13 | ```
14 |
--------------------------------------------------------------------------------
/preview/src/components/switch/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | let mut checked = use_signal(|| false);
7 | rsx! {
8 |
9 | div { class: "switch-example",
10 | Switch {
11 | checked: checked(),
12 | aria_label: "Switch Demo",
13 | on_checked_change: move |new_checked| {
14 | checked.set(new_checked);
15 | },
16 | SwitchThumb {}
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/primitives/src/select/components/mod.rs:
--------------------------------------------------------------------------------
1 | //! Component definitions for the select primitive.
2 |
3 | pub mod group;
4 | pub mod list;
5 | pub mod option;
6 | pub mod select;
7 | pub mod trigger;
8 | pub mod value;
9 |
10 | pub use group::{SelectGroup, SelectGroupLabel, SelectGroupLabelProps, SelectGroupProps};
11 | pub use list::{SelectList, SelectListProps};
12 | pub use option::{SelectItemIndicator, SelectItemIndicatorProps, SelectOption, SelectOptionProps};
13 | pub use select::{Select, SelectProps};
14 | pub use trigger::{SelectTrigger, SelectTriggerProps};
15 | pub use value::{SelectValue, SelectValueProps};
16 |
--------------------------------------------------------------------------------
/preview/src/components/collapsible/docs.md:
--------------------------------------------------------------------------------
1 | The `Collapsible` component allows users to create expandable sections that can be toggled open or closed. This is useful for displaying content in a compact manner, such as FAQs, menus, or any content that benefits from being hidden until needed.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The collapsible component must wrap all collapsible items.
7 | Collapsible {
8 | // The trigger is used to expand or collapse the item.
9 | CollapsibleTrigger {}
10 | // The content that is shown when the item is expanded.
11 | CollapsibleContent {}
12 | }
13 | ```
14 |
--------------------------------------------------------------------------------
/preview/src/components/tooltip/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use dioxus_primitives::ContentSide;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | rsx! {
8 |
9 | Tooltip {
10 | TooltipTrigger { "Rich content" }
11 | TooltipContent { side: ContentSide::Left, style: "width: 200px;",
12 | h4 { style: "margin-top: 0; margin-bottom: 8px;", "Tooltip title" }
13 | p { style: "margin: 0;", "This tooltip contains rich HTML content with styling." }
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test-harness/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "test-harness"
3 | version = "0.1.0"
4 | authors = ["Evan Almloff "]
5 | edition = "2021"
6 |
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 |
9 | [dependencies]
10 | dioxus = { version = "0.7.0-rc.1", features = [] }
11 | dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false, features = ["router"] }
12 |
13 | [features]
14 | default = ["web"]
15 | web = ["dioxus/web"]
16 | desktop = ["dioxus/desktop"]
17 | mobile = ["dioxus/mobile"]
18 |
--------------------------------------------------------------------------------
/preview/src/components/form/style.css:
--------------------------------------------------------------------------------
1 | #tos-check {
2 | width: 50px;
3 | height: 50px;
4 | border: 1px solid var(--primary-color-6);
5 | border-radius: 4px;
6 | margin: 8px 16px;
7 | background-color: var(--primary-color-1);
8 | color: var(--secondary-color-4);
9 | cursor: pointer;
10 | font-size: 14px;
11 | }
12 |
13 | .form-example button {
14 | padding: 8px 16px;
15 | border: 1px solid var(--primary-color-6);
16 | border-radius: 4px;
17 | background-color: var(--primary-color-1);
18 | color: var(--secondary-color-4);
19 | cursor: pointer;
20 | font-size: 14px;
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/button/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | // demo.rs
2 | use super::super::component::*;
3 | use dioxus::prelude::*;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | rsx! {
8 | div { display: "flex", flex_direction: "column", gap: "0.5rem",
9 | Button { "Primary" }
10 |
11 | Button { variant: ButtonVariant::Secondary, "Secondary" }
12 |
13 | Button { variant: ButtonVariant::Destructive, "Destructive" }
14 |
15 | Button { variant: ButtonVariant::Outline, "Outline" }
16 |
17 | Button { variant: ButtonVariant::Ghost, "Ghost" }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | use time::Date;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut selected_date = use_signal(|| None::);
9 |
10 | rsx! {
11 | div {
12 | DatePicker {
13 | selected_date: selected_date(),
14 | on_value_change: move |v| {
15 | tracing::info!("Selected date changed: {:?}", v);
16 | selected_date.set(v);
17 | },
18 | DatePickerInput {}
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/preview/src/components/toggle/style.css:
--------------------------------------------------------------------------------
1 | .toggle {
2 | display: inline-flex;
3 | width: fit-content;
4 | min-width: 2rem;
5 | flex-direction: row;
6 | align-items: center;
7 | justify-content: center;
8 | padding-right: 0.5rem;
9 | padding-left: 0.5rem;
10 | border: none;
11 | border-radius: 0.5rem;
12 | background-color: transparent;
13 | color: var(--secondary-color-4);
14 | font-size: 14px;
15 | outline: none;
16 | }
17 |
18 | .toggle:hover, .toggle:focus-visible {
19 | background-color: var(--primary-color-4);
20 | cursor: pointer;
21 | }
22 |
23 | .toggle[data-state="on"] {
24 | background-color: var(--primary-color-7);
25 | color: var(--secondary-color-1);
26 | }
27 |
--------------------------------------------------------------------------------
/playwright/collapsible.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=collapsible&", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | // Get the .collapsible-content
6 | const collapsibleContent = page.locator(".collapsible-content");
7 | // Click on the .collapsible-trigger
8 | const firstCollapsibleTrigger = page.locator(".collapsible-trigger");
9 | await firstCollapsibleTrigger.click();
10 | // Verify that the first .collapsible-content is expanded (data-open="true")
11 | await expect(collapsibleContent.first()).toHaveAttribute("data-open", "true");
12 | });
13 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/variants/range/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | use dioxus_primitives::calendar::DateRange;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut selected_range = use_signal(|| None::);
9 |
10 | rsx! {
11 | div {
12 | DateRangePicker {
13 | selected_range: selected_range(),
14 | on_range_change: move |range| {
15 | tracing::info!("Selected range: {:?}", range);
16 | selected_range.set(range);
17 | },
18 | DateRangePickerInput {}
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/preview/src/components/switch/docs.md:
--------------------------------------------------------------------------------
1 | The Switch component allows users to toggle between two states, such as on and off.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Switch component wraps the switch thumb
7 | Switch {
8 | // The current state of the switch, true for on and false for off.
9 | checked: true,
10 | // Callback function triggered when the switch state changes.
11 | on_checked_change: |checked: bool| {
12 | // Handle the change in switch state.
13 | },
14 | // The switch thumb represents the draggable handle that the user moves to toggle the switch.
15 | SwitchThumb {
16 | // The content of the thumb
17 | {children}
18 | }
19 | }
20 | ```
--------------------------------------------------------------------------------
/preview/src/components/collapsible/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | Collapsible {
8 | CollapsibleTrigger {
9 | b { "Recent Activity" }
10 | }
11 | CollapsibleList {
12 | CollapsibleItem { "Added a new feature to the collapsible component" }
13 | CollapsibleContent {
14 | CollapsibleItem { "Fixed a bug in the collapsible component" }
15 | CollapsibleItem { "Updated the documentation for the collapsible component" }
16 | }
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/variants/multi_month/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | use dioxus_primitives::calendar::DateRange;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut selected_range = use_signal(|| None::);
9 |
10 | rsx! {
11 | div {
12 | DateRangePicker {
13 | selected_range: selected_range(),
14 | on_range_change: move |range| {
15 | tracing::info!("Selected range: {:?}", range);
16 | selected_range.set(range);
17 | },
18 | month_count: 2,
19 | DateRangePickerInput {}
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/playwright/avatar.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=avatar&", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | // Get the avatar element
6 | const avatar = page.locator(".avatar-item").nth(0);
7 | // Verify the avatar has a loaded image
8 | let image = avatar.locator("img");
9 | await expect(image).toHaveAttribute("src", "https://avatars.githubusercontent.com/u/66571940?s=96&v=4");
10 |
11 | // Get the second avatar element
12 | const secondAvatar = page.locator(".avatar-item").nth(1);
13 | // Verify the second avatar has fallback text
14 | await expect(secondAvatar).toContainText("JK");
15 | });
16 |
--------------------------------------------------------------------------------
/preview/src/components/aspect_ratio/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::AspectRatio;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("../../style.css") }
8 | div {
9 | class: "aspect-ratio-container",
10 | width: "20rem",
11 | max_width: "30vw",
12 | AspectRatio { ratio: 4.0 / 3.0,
13 | div {
14 | background: "linear-gradient(to bottom right, var(--primary-color-4), var(--primary-color-3))",
15 | width: "100%",
16 | height: "100%",
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/dialog/docs.md:
--------------------------------------------------------------------------------
1 | The dialog component can be used to display additional information or actions related to an item. It is a simple modal dialog that can be opened and closed by the user.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The dialog component must wrap all dialog elements.
7 | Dialog {
8 | // The open prop determines if the dialog is currently open or closed.
9 | open: open(),
10 | // The dialog title defines the heading of the dialog.
11 | DialogTitle {
12 | "Item information"
13 | }
14 | // The dialog description provides additional information about the dialog.
15 | DialogDescription {
16 | "Here is some additional information about the item."
17 | }
18 | }
19 | ```
20 |
--------------------------------------------------------------------------------
/preview/src/components/form/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::checkbox::{Checkbox, CheckboxIndicator};
3 | #[component]
4 | pub fn Demo() -> Element {
5 | rsx! {
6 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
7 | form {
8 | class: "form-example",
9 | onsubmit: move |e| {
10 | tracing::info!("{:?}", e.values());
11 | },
12 | Checkbox { id: "tos-check", name: "tos-check",
13 | CheckboxIndicator { "+" }
14 | }
15 | label { r#for: "tos-check", "I agree to the terms presented." }
16 | br {}
17 | button { r#type: "submit", "Submit" }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/preview/src/components/scroll_area/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use dioxus_primitives::scroll_area::ScrollDirection;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | rsx! {
8 |
9 | ScrollArea {
10 | width: "10em",
11 | height: "10em",
12 | border: "1px solid var(--primary-color-6)",
13 | border_radius: "0.5em",
14 | padding: "0 1em 1em 1em",
15 | direction: ScrollDirection::Vertical,
16 | tabindex: "0",
17 | div { class: "scroll-content",
18 | for i in 1..=20 {
19 | p { "Scrollable content item {i}" }
20 | }
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/preview/src/components/toggle/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::toggle::{self, ToggleProps};
3 |
4 | #[component]
5 | pub fn Toggle(props: ToggleProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | toggle::Toggle {
9 | class: "toggle",
10 | pressed: props.pressed,
11 | default_pressed: props.default_pressed,
12 | disabled: props.disabled,
13 | on_pressed_change: props.on_pressed_change,
14 | onmounted: props.onmounted,
15 | onfocus: props.onfocus,
16 | onkeydown: props.onkeydown,
17 | attributes: props.attributes,
18 | {props.children}
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/preview/src/components/progress/style.css:
--------------------------------------------------------------------------------
1 | .progress {
2 | position: relative;
3 | overflow: hidden;
4 | width: 200px;
5 | height: .5rem;
6 | box-sizing: border-box;
7 | border-radius: 9999px;
8 | background: var(--primary-color-5);
9 | }
10 |
11 | .progress[data-state='indeterminate'] .progress-indicator {
12 | width: 50%;
13 | animation: indeterminate 1s infinite linear;
14 | }
15 |
16 | .progress-indicator {
17 | width: var(--progress-value, 0%);
18 | height: 100%;
19 | background-color: var(--secondary-color-1);
20 | transition: width 250ms ease;
21 | }
22 |
23 | @keyframes indeterminate {
24 | 0% {
25 | transform: translateX(-100%);
26 | }
27 |
28 | 100% {
29 | transform: translateX(200%);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/docs.md:
--------------------------------------------------------------------------------
1 | The Toggle Group component is used to create a group of toggle buttons that allows the user to select one or more options from a set. It is useful for creating a set of options.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The ToggleGroup component wraps all toggle items in the group.
7 | ToggleGroup {
8 | // The orientation of the toggle group, true for horizontal and false for vertical.
9 | horizontal: true,
10 | // The toggle item represents each individual toggle button in the group.
11 | ToggleItem {
12 | // The index of the toggle item, used to determine the order in which items are focused.
13 | index: 0,
14 | // The contents of the toggle item button
15 | {children}
16 | }
17 | }
18 | ```
19 |
--------------------------------------------------------------------------------
/preview/src/components/progress/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::progress::{self, ProgressIndicatorProps, ProgressProps};
3 |
4 | #[component]
5 | pub fn Progress(props: ProgressProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | progress::Progress {
9 | class: "progress",
10 | value: props.value,
11 | max: props.max,
12 | attributes: props.attributes,
13 | {props.children}
14 | }
15 | }
16 | }
17 |
18 | #[component]
19 | pub fn ProgressIndicator(props: ProgressIndicatorProps) -> Element {
20 | rsx! {
21 | progress::ProgressIndicator { class: "progress-indicator", attributes: props.attributes, {props.children} }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/primitives/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dioxus-primitives"
3 | version = "0.0.1"
4 | edition = "2021"
5 | rust-version = "1.80.0"
6 | readme = "../README.md"
7 | keywords = ["gui", "dioxus", "components", "primitives"]
8 | categories = ["gui", "wasm"]
9 | authors = ["Dioxus Labs", "DogeDark"]
10 | license = "MIT OR Apache-2.0"
11 | homepage = "https://dioxuslabs.com"
12 | repository = "https://github.com/DioxusLabs/components"
13 |
14 | [dependencies]
15 | dioxus.workspace = true
16 | dioxus-sdk-time = "0.7.0"
17 | time = { version = "0.3.44", features = ["std", "macros", "parsing"] }
18 | num-integer = "0.1.46"
19 | tracing.workspace = true
20 |
21 | [build-dependencies]
22 | lazy-js-bundle = "0.6.2"
23 |
24 | [features]
25 | default = ["router"]
26 | router = ["dioxus/router"]
27 | web = ["time/wasm-bindgen"]
28 |
--------------------------------------------------------------------------------
/preview/src/components/accordion/docs.md:
--------------------------------------------------------------------------------
1 | The accordion component is used to display collapsible content panels for presenting information in a limited amount of space. It allows users to expand and collapse sections to view more or less content.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The accordion component must wrap all accordion items.
7 | Accordion {
8 | // Each accordion item contains both a trigger the expanded contents of the item.
9 | AccordionItem {
10 | // Each item must have an index starting from 0 to control where the item is placed.
11 | index: 0,
12 | // The trigger is used to expand or collapse the item.
13 | AccordionTrigger {}
14 | // The content that is shown when the item is expanded.
15 | AccordionContent {}
16 | }
17 | }
18 | ```
--------------------------------------------------------------------------------
/playwright/checkbox.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=checkbox&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | let checkbox = page.getByRole('checkbox', { name: 'Demo Checkbox' });
6 | await expect(checkbox).toBeVisible();
7 | // The checkbox should not be checked initially
8 | await expect(checkbox).toHaveAttribute('data-state', 'unchecked');
9 | // Clicking the checkbox should check it
10 | await checkbox.click();
11 | await expect(checkbox).toHaveAttribute('data-state', 'checked');
12 | // Pressing space should also toggle the checkbox
13 | await page.keyboard.press('Space');
14 | await expect(checkbox).toHaveAttribute('data-state', 'unchecked');
15 | });
16 |
--------------------------------------------------------------------------------
/preview/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "preview"
3 | version = "0.1.0"
4 | edition = "2021"
5 | rust-version = "1.80.0"
6 |
7 | [dependencies]
8 | dioxus = { workspace = true, features = ["router"] }
9 | dioxus-primitives.workspace = true
10 | dioxus-i18n = { git = "https://github.com/ealmloff/dioxus-i18n", branch = "bump-dioxus" }
11 | unic-langid = { version = "0.9", features = ["macros"] }
12 | strum = { version = "0.27.2", features = ["derive"] }
13 | tracing.workspace = true
14 | time = { version = "0.3.44", features = ["std", "macros"] }
15 |
16 | [build-dependencies]
17 | syntect = "5.2"
18 | pulldown-cmark = "0.13.0"
19 |
20 | [features]
21 | web = ["dioxus/web", "dioxus-primitives/web"]
22 | desktop = ["dioxus/desktop"]
23 | fullstack = ["dioxus/fullstack"]
24 | server = ["dioxus/server"]
25 | native = ["dioxus/native"]
26 |
--------------------------------------------------------------------------------
/playwright/toggle.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=toggle&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | let toggleElement = page.getByRole('button', { name: 'B', exact: true });
6 | await expect(toggleElement).toBeVisible();
7 | // The toggle should not be checked initially
8 | await expect(toggleElement).toHaveAttribute('data-state', 'off');
9 | // // Clicking the toggle should check it
10 | await toggleElement.click();
11 | await expect(toggleElement).toHaveAttribute('data-state', 'on');
12 | // // Pressing space should also toggle the toggle
13 | await page.keyboard.press('Space');
14 | await expect(toggleElement).toHaveAttribute('data-state', 'off');
15 | });
16 |
--------------------------------------------------------------------------------
/.github/workflows/clean-up-preview.yml:
--------------------------------------------------------------------------------
1 | name: Clean Up Preview
2 |
3 | on:
4 | pull_request_target:
5 | types:
6 | - closed
7 |
8 | permissions:
9 | contents: write
10 | pages: write
11 |
12 | jobs:
13 | merge_job:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Create empty directory and store it in a variable
18 | run: echo "empty_dir=$(mktemp -d)" >> $GITHUB_ENV
19 | - name: Deploy 🚀
20 | uses: JamesIves/github-pages-deploy-action@v4.2.3
21 | with:
22 | branch: gh-pages # The branch the action should deploy to.
23 | folder: "${{ env.empty_dir }}" # The folder the action should deploy.
24 | target-folder: "pr-preview/pr-${{ github.event.pull_request.number }}/"
25 | clean: true
26 | single-commit: true
27 |
--------------------------------------------------------------------------------
/playwright/switch.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=switch&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | let switchElement = page.getByRole('switch', { name: 'Switch Demo' });
6 | await expect(switchElement).toBeVisible();
7 | // The switch should not be checked initially
8 | await expect(switchElement).toHaveAttribute('data-state', 'unchecked');
9 | // Clicking the switch should check it
10 | await switchElement.click();
11 | await expect(switchElement).toHaveAttribute('data-state', 'checked');
12 | // Pressing space should also toggle the switch
13 | await page.keyboard.press('Space');
14 | await expect(switchElement).toHaveAttribute('data-state', 'unchecked');
15 | });
16 |
--------------------------------------------------------------------------------
/preview/src/components/progress/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | let mut progress = use_signal(|| 0);
7 |
8 | use_effect(move || {
9 | let mut timer = document::eval(
10 | "setInterval(() => {
11 | dioxus.send(Math.floor(Math.random() * 30));
12 | }, 1000);",
13 | );
14 | spawn(async move {
15 | while let Ok(new_progress) = timer.recv::().await {
16 | let mut progress = progress.write();
17 | *progress = (*progress + new_progress) % 101;
18 | }
19 | });
20 | });
21 |
22 | rsx! {
23 | Progress { aria_label: "Progressbar Demo", value: progress() as f64, ProgressIndicator {} }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/playwright/toast.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=toast&');
5 | // Create a toast
6 | await page.getByRole('button', { name: 'Info (60s)' }).click();
7 | // Create another toast
8 | await page.getByRole('button', { name: 'Info (60s)' }).click();
9 | const toast_close_buttons = page.getByRole('button', { name: 'close' });
10 | // Hover and close the first toast
11 | await toast_close_buttons.first().hover();
12 | await toast_close_buttons.first().click();
13 | await expect(toast_close_buttons).toHaveCount(1);
14 |
15 | // Hover and close the second toast
16 | await toast_close_buttons.first().hover();
17 | await toast_close_buttons.first().click();
18 | await expect(toast_close_buttons).toHaveCount(0);
19 | });
20 |
--------------------------------------------------------------------------------
/preview/assets/language-select.css:
--------------------------------------------------------------------------------
1 | /* Language */
2 | .language-container {
3 | position: relative;
4 | display: flex;
5 | align-items: center;
6 | }
7 |
8 | .language-select-container {
9 | position: relative;
10 | }
11 |
12 | .language-select-container:has(:focus-visible) {
13 | border-radius: 0.5rem;
14 | outline: 2px solid var(--focused-border-color);
15 | }
16 |
17 | .language-select {
18 | position: absolute;
19 | width: 100%;
20 | height: 100%;
21 | padding: .25rem;
22 | margin: 0;
23 | inset: 0;
24 | opacity: 0;
25 | }
26 |
27 | .language-select-value {
28 | display: inline-flex;
29 | align-items: center;
30 | justify-content: center;
31 | padding: .25rem;
32 | border: none;
33 | background-color: transparent;
34 | color: var(--secondary-color-4);
35 | cursor: pointer;
36 | font-size: 1.5rem;
37 | transition: background-color 0.2s ease, color 0.2s ease;
38 | }
--------------------------------------------------------------------------------
/preview/src/components/date_picker/variants/internationalized/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | use dioxus_i18n::tid;
5 | use time::Date;
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_date = use_signal(|| None::);
10 |
11 | rsx! {
12 | div {
13 | DatePicker {
14 | selected_date: selected_date(),
15 | on_value_change: move |v| {
16 | tracing::info!("Selected date changed: {:?}", v);
17 | selected_date.set(v);
18 | },
19 | DatePickerInput {
20 | on_format_day_placeholder: || tid!("D_Abbr"),
21 | on_format_month_placeholder: || tid!("M_Abbr"),
22 | on_format_year_placeholder: || tid!("Y_Abbr"),
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/playwright/tooltip.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=tooltip&");
5 | let tooltip = page.getByRole("tooltip");
6 | // tabbing to the trigger element should show the tooltip
7 | await page.locator("#component-preview-frame").focus();
8 | await page.keyboard.press("Tab");
9 | await expect(tooltip).toBeVisible();
10 | // tabbing out of the trigger element should hide the tooltip
11 | await page.keyboard.press("Tab");
12 | await expect(tooltip).toHaveCount(0);
13 |
14 | // hovering over the trigger element should show the tooltip
15 | await page.locator(".tooltip-trigger").hover();
16 | await expect(tooltip).toBeVisible();
17 |
18 | // moving the mouse away from the trigger element should hide the tooltip
19 | await page.mouse.move(0, 0);
20 | await expect(tooltip).toHaveCount(0);
21 | });
22 |
--------------------------------------------------------------------------------
/preview/src/components/accordion/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::{Accordion, AccordionContent, AccordionItem, AccordionTrigger};
2 | use dioxus::prelude::*;
3 | #[component]
4 | pub fn Demo() -> Element {
5 | rsx! {
6 | Accordion { allow_multiple_open: false, horizontal: false,
7 | for i in 0..4 {
8 | AccordionItem { index: i,
9 | AccordionTrigger { "the quick brown fox" }
10 | AccordionContent {
11 | div { padding_bottom: "1rem",
12 | p { padding: "0",
13 | "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum"
14 | }
15 | }
16 | }
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test-harness/assets/main.css:
--------------------------------------------------------------------------------
1 | /* App-wide styling */
2 | body {
3 | background-color: #0f1116;
4 | color: #ffffff;
5 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
6 | margin: 20px;
7 | }
8 |
9 | #hero {
10 | margin: 0;
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: center;
14 | align-items: center;
15 | }
16 |
17 | #links {
18 | width: 400px;
19 | text-align: left;
20 | font-size: x-large;
21 | color: white;
22 | display: flex;
23 | flex-direction: column;
24 | }
25 |
26 | #links a {
27 | color: white;
28 | text-decoration: none;
29 | margin-top: 20px;
30 | margin: 10px 0px;
31 | border: white 1px solid;
32 | border-radius: 5px;
33 | padding: 10px;
34 | }
35 |
36 | #links a:hover {
37 | background-color: #1f1f1f;
38 | cursor: pointer;
39 | }
40 |
41 | #header {
42 | max-width: 1200px;
43 | }
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/playwright/hover-card.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=hover_card&");
5 | let tooltip = page.getByRole("tooltip");
6 | // tabbing to the trigger element should show the tooltip
7 | await page.locator("#component-preview-frame").focus();
8 | await page.keyboard.press("Tab");
9 | await expect(tooltip).toBeVisible();
10 | // tabbing out of the trigger element should hide the tooltip
11 | await page.keyboard.press("Tab");
12 | await expect(tooltip).toHaveCount(0);
13 |
14 | // hovering over the trigger element should show the tooltip
15 | await page.getByRole("button", { name: "Dioxus" }).hover();
16 | await expect(tooltip).toBeVisible();
17 |
18 | // moving the mouse away from the trigger element should hide the tooltip
19 | await page.mouse.move(0, 0);
20 | await expect(tooltip).toHaveCount(0);
21 | });
22 |
--------------------------------------------------------------------------------
/preview/src/components/hover_card/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use dioxus_primitives::ContentSide;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | rsx! {
8 | div { style: "padding: 50px; display: flex; flex-direction: row; flex-wrap: wrap; gap: 40px; justify-content: center; align-items: center;",
9 | HoverCard {
10 | HoverCardTrigger {
11 | i { "Dioxus" }
12 | }
13 | HoverCardContent { side: ContentSide::Bottom,
14 | div { padding: "1rem",
15 | "Dioxus is"
16 | i { " the " }
17 | "Rust framework for building fullstack web, desktop, and mobile apps. Iterate with live hotreloading, add server functions, and deploy in record time."
18 | }
19 | }
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/preview/assets/github-mark/github-mark-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/preview/assets/github-mark/github-mark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/preview/src/components/collapsible/style.css:
--------------------------------------------------------------------------------
1 | .collapsible-trigger {
2 | display: flex;
3 | overflow: hidden;
4 | width: 100%;
5 | box-sizing: border-box;
6 | flex-direction: row;
7 | align-items: center;
8 | justify-content: space-between;
9 | padding: 0;
10 | padding-top: 1rem;
11 | padding-bottom: 1rem;
12 | border: none;
13 | background-color: transparent;
14 | color: var(--secondary-color-4);
15 | outline: none;
16 | text-align: left;
17 | }
18 |
19 | .collapsible-trigger:focus-visible {
20 | box-shadow: inset 0 0 0 2px var(--focused-border-color);
21 | }
22 |
23 | .collapsible-trigger:hover {
24 | cursor: pointer;
25 | text-decoration-line: underline;
26 | }
27 |
28 | .collapsible-content {
29 | display: contents;
30 | }
31 |
32 | .collapsible-expand-icon {
33 | width: 1rem;
34 | height: 1rem;
35 | fill: none;
36 | stroke: var(--secondary-color-3);
37 | stroke-linecap: round;
38 | stroke-linejoin: round;
39 | stroke-width: 2;
40 | }
41 |
--------------------------------------------------------------------------------
/preview/src/components/switch/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::switch::{self, SwitchProps, SwitchThumbProps};
3 |
4 | #[component]
5 | pub fn Switch(props: SwitchProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | switch::Switch {
9 | class: "switch",
10 | checked: props.checked,
11 | default_checked: props.default_checked,
12 | disabled: props.disabled,
13 | required: props.required,
14 | name: props.name,
15 | value: props.value,
16 | on_checked_change: props.on_checked_change,
17 | attributes: props.attributes,
18 | {props.children}
19 | }
20 | }
21 | }
22 |
23 | #[component]
24 | pub fn SwitchThumb(props: SwitchThumbProps) -> Element {
25 | rsx! {
26 | switch::SwitchThumb { class: "switch-thumb", attributes: props.attributes, {props.children} }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/preview/src/components/checkbox/style.css:
--------------------------------------------------------------------------------
1 | .checkbox {
2 | width: 1rem;
3 | height: 1rem;
4 | box-sizing: border-box;
5 | padding: 0;
6 | border: none;
7 | border-radius: 4px;
8 | margin: 0;
9 | background-color: var(--primary-color-3);
10 | box-shadow: inset 0 0 0 1px var(--primary-color-7);
11 | color: var(--secondary-color-4);
12 | cursor: pointer;
13 | }
14 |
15 | .checkbox-indicator {
16 | display: flex;
17 | align-items: center;
18 | justify-content: center;
19 | }
20 |
21 | .checkbox[data-state="checked"] {
22 | background-color: var(--secondary-color-2);
23 | box-shadow: none;
24 | color: var(--primary-color);
25 | }
26 |
27 | .checkbox:focus-visible {
28 | box-shadow: 0 0 0 2px var(--focused-border-color);
29 | }
30 |
31 | .checkbox-check-icon {
32 | width: 1rem;
33 | height: 1rem;
34 | fill: none;
35 | stroke: currentcolor;
36 | stroke-linecap: round;
37 | stroke-linejoin: round;
38 | stroke-width: 2;
39 | }
40 |
--------------------------------------------------------------------------------
/preview/src/components/slider/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use dioxus_primitives::slider::SliderValue;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut current_value = use_signal(|| 50.0);
8 |
9 | rsx! {
10 | // Display the current value
11 | div { style: "margin-bottom: 15px; font-size: 16px; font-weight: bold;", "{current_value:.0}%" }
12 |
13 | Slider {
14 | label: "Demo Slider",
15 | horizontal: true,
16 | min: 0.0,
17 | max: 100.0,
18 | step: 1.0,
19 | default_value: SliderValue::Single(50.0),
20 | on_value_change: move |value: SliderValue| {
21 | // Extract the f64 value from SliderValue::Single
22 | let SliderValue::Single(v) = value;
23 | current_value.set(v);
24 | },
25 | SliderTrack {
26 | SliderRange {}
27 | SliderThumb {}
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/preview/src/components/toast/docs.md:
--------------------------------------------------------------------------------
1 | The Toast component is used to display brief messages to the user, typically for notifications or alerts. The toast messages can be focused with the keyboard with the `f6` key.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Toast provider provides the toast context to its children and handler rendering any toasts that are sent.
7 | ToastProvider {
8 | // Any child component can consume the toast context and send a toast to be rendered.
9 | button {
10 | onclick: |event: MouseEvent| {
11 | // Consume the toast context to send a toast.
12 | let toast_api = consume_toast();
13 | toast_api
14 | .error(
15 | "Critical Error".to_string(),
16 | ToastOptions::new()
17 | .description("Some info you need")
18 | .duration(Duration::from_secs(60))
19 | .permanent(false),
20 | );
21 | },
22 | "Show Toast"
23 | }
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/preview/src/components/toast/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::components::button::component::Button;
2 |
3 | use super::super::component::*;
4 | use dioxus::prelude::*;
5 | use dioxus_primitives::toast::{use_toast, ToastOptions};
6 | use std::time::Duration;
7 |
8 | #[component]
9 | pub fn Demo() -> Element {
10 | rsx! {
11 | ToastProvider { ToastButton {} }
12 | }
13 | }
14 |
15 | #[component]
16 | fn ToastButton() -> Element {
17 | let toast_api = use_toast();
18 |
19 | rsx! {
20 | Button {
21 | r#type: "button",
22 | "data-style": "outline",
23 | onclick: move |_| {
24 | toast_api
25 | .info(
26 | "Custom Toast".to_string(),
27 | ToastOptions::new()
28 | .description("Some info you need")
29 | .duration(Duration::from_secs(60))
30 | .permanent(false),
31 | );
32 | },
33 | "Info (60s)"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/preview/src/components/checkbox/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::checkbox::{self, CheckboxProps};
3 |
4 | #[component]
5 | pub fn Checkbox(props: CheckboxProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | checkbox::Checkbox {
9 | class: "checkbox",
10 | checked: props.checked,
11 | default_checked: props.default_checked,
12 | required: props.required,
13 | disabled: props.disabled,
14 | name: props.name,
15 | value: props.value,
16 | on_checked_change: props.on_checked_change,
17 | attributes: props.attributes,
18 | checkbox::CheckboxIndicator { class: "checkbox-indicator",
19 | svg {
20 | class: "checkbox-check-icon",
21 | view_box: "0 0 24 24",
22 | xmlns: "http://www.w3.org/2000/svg",
23 | path { d: "M5 13l4 4L19 7" }
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/preview/src/components/textarea/variants/fade/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use crate::components::label::Label;
3 | use dioxus::prelude::*;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut description = use_signal(String::new);
8 | rsx! {
9 | div {
10 | display: "flex",
11 | flex_direction: "column",
12 | gap: "1.5rem",
13 |
14 | p { id: "textarea-message", "Description here: {description}" }
15 |
16 | div {
17 | display: "flex",
18 | flex_direction: "column",
19 | gap: ".5rem",
20 | justify_content: "center",
21 |
22 | Label { html_for: "fade", "Fade" }
23 | Textarea {
24 | id: "fade",
25 | variant: TextareaVariant::Fade,
26 | placeholder: "Enter your description",
27 | value: description,
28 | oninput: move |e: FormEvent| description.set(e.value()),
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/preview/src/components/textarea/variants/ghost/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use crate::components::label::Label;
3 | use dioxus::prelude::*;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut description = use_signal(String::new);
8 | rsx! {
9 | div {
10 | display: "flex",
11 | flex_direction: "column",
12 | gap: "1.5rem",
13 |
14 | p { id: "textarea-message", "Description here: {description}" }
15 |
16 | div {
17 | display: "flex",
18 | flex_direction: "column",
19 | gap: ".5rem",
20 | justify_content: "center",
21 |
22 | Label { html_for: "ghost", "Ghost" }
23 | Textarea {
24 | id: "ghost",
25 | variant: TextareaVariant::Ghost,
26 | placeholder: "Enter your description",
27 | value: description,
28 | oninput: move |e: FormEvent| description.set(e.value()),
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/preview/src/components/textarea/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use crate::components::label::Label;
3 | use dioxus::prelude::*;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut description = use_signal(String::new);
8 | rsx! {
9 | div {
10 | display: "flex",
11 | flex_direction: "column",
12 | gap: "1.5rem",
13 |
14 | p { id: "textarea-message", "Description here: {description}" }
15 |
16 | div {
17 | display: "flex",
18 | flex_direction: "column",
19 | gap: ".5rem",
20 | justify_content: "center",
21 |
22 | Label { html_for: "default", "Default" }
23 | Textarea {
24 | id: "default",
25 | variant: TextareaVariant::Default,
26 | placeholder: "Enter your description",
27 | value: description,
28 | oninput: move |e: FormEvent| description.set(e.value()),
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/preview/src/components/textarea/variants/outline/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use crate::components::label::Label;
3 | use dioxus::prelude::*;
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut description = use_signal(String::new);
8 | rsx! {
9 | div {
10 | display: "flex",
11 | flex_direction: "column",
12 | gap: "1.5rem",
13 |
14 | p { id: "textarea-message", "Description here: {description}" }
15 |
16 | div {
17 | display: "flex",
18 | flex_direction: "column",
19 | gap: ".5rem",
20 | justify_content: "center",
21 | Label { html_for: "outline", "Outline" }
22 | Textarea {
23 | id: "outline",
24 | variant: TextareaVariant::Outline,
25 | placeholder: "Enter your description",
26 | value: description,
27 | oninput: move |e: FormEvent| description.set(e.value()),
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/playwright/accordion.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=accordion&", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | // Get the first .accordion-item
6 | const accordionItem = page.locator(".accordion-item");
7 | // Click on the first .accordion-item
8 | const firstAccordionItem = accordionItem.first();
9 | await firstAccordionItem.locator("button").click();
10 | // Verify that the first .accordion-item is expanded (data-open="true")
11 | await expect(firstAccordionItem).toHaveAttribute("data-open", "true");
12 |
13 | // Click on the second .accordion-item
14 | const secondAccordionItem = accordionItem.nth(1);
15 | await secondAccordionItem.locator("button").click();
16 | // Verify that the second .accordion-item is expanded (data-open="true")
17 | await expect(secondAccordionItem).toHaveAttribute("data-open", "true");
18 | // Verify the first .accordion-item is collapsed (data-open="false")
19 | await expect(firstAccordionItem).toHaveAttribute("data-open", "false");
20 | });
21 |
--------------------------------------------------------------------------------
/preview/src/components/tooltip/docs.md:
--------------------------------------------------------------------------------
1 | The Tooltip component is used to display additional information when a user hovers over an element.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Tooltip component wraps the trigger element and the content that will be displayed on hover.
7 | Tooltip {
8 | // The TooltipTrigger contains the elements that will trigger the tooltip to display when hovered over.
9 | TooltipTrigger {
10 | // The elements that will trigger the tooltip when hovered over.
11 | {children}
12 | }
13 | // The TooltipContent contains the content that will be displayed when the user hovers over the trigger.
14 | TooltipContent {
15 | // The side of the TooltipTrigger where the content will be displayed. Can be one of Top, Right, Bottom, or Left.
16 | side: ContentSide::Top,
17 | // The alignment of the TooltipContent relative to the TooltipTrigger. Can be one of Start, Center, or End.
18 | align: ContentAlign::Center,
19 | // The content of the tooltip, which can include text, images, or any other elements.
20 | {children}
21 | }
22 | }
23 | ```
--------------------------------------------------------------------------------
/preview/src/components/radio_group/docs.md:
--------------------------------------------------------------------------------
1 | The RadioGroup component is used to create a group of radio buttons that allows the user to select one option from a set.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The RadioGroup component wraps all radio items in the group.
7 | RadioGroup {
8 | // The value property represents the currently selected radio button in the group.
9 | value: "option1",
10 | on_value_change: |value: String| {
11 | // This callback is triggered when the selected radio button changes.
12 | // The value parameter contains the value of the newly selected radio button.
13 | },
14 | // The RadioItem component represents each individual radio button in the group.
15 | RadioItem {
16 | // The index of the radio item, used to determine the order in which items are displayed.
17 | index: 0,
18 | // The value of the radio button, which is used to identify the selected option and will be passed to the on_value_change callback when selected.
19 | value: "option1",
20 | // The contents of the radio item button
21 | {children}
22 | }
23 | }
24 | ```
--------------------------------------------------------------------------------
/preview/src/components/popover/docs.md:
--------------------------------------------------------------------------------
1 | The Popover primitive provides an interactive modal that is positioned relative to the target element. It can be used to display additional options, or actions related to the trigger.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The PopoverRoot is the root component that contains the trigger and content.
7 | PopoverRoot {
8 | // The PopoverTrigger contains the elements that will trigger the popover to display when clicked.
9 | PopoverTrigger {
10 | "Show Popover"
11 | }
12 | // The PopoverContent contains the content that will be displayed when the user clicks on the trigger.
13 | PopoverContent {
14 | // The side of the PopoverTrigger where the content will be displayed. Can be one of Top, Right, Bottom, or Left.
15 | side: ContentSide::Top,
16 | // The alignment of the PopoverContent relative to the PopoverTrigger. Can be one of Start, Center, or End.
17 | align: ContentAlign::Center,
18 | // The interactive content of the popover. This content will trap the focus until the popover is closed.
19 | {children}
20 | }
21 | }
22 | ```
23 |
--------------------------------------------------------------------------------
/preview/src/components/avatar/docs.md:
--------------------------------------------------------------------------------
1 | The Avatar component is used to display a user's profile picture or an icon representing the user. It handles the loading state of the image and can display a fallback icon if the image fails to load.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // All avatar contents must be wrapped in the Avatar component.
7 | Avatar {
8 | on_state_change: |state: AvatarState| {
9 | // This callback is triggered when the avatar's state changes. The state can be used to determine if the image is loading, loaded, or failed to load.
10 | },
11 | // The avatar image component is used to display the user's profile picture.
12 | AvatarImage {
13 | // The source URL of the image to be displayed.
14 | src: "",
15 | // The alt text for the image, used for accessibility.
16 | alt: "",
17 | }
18 | // The avatar fallback component is used to display an icon or text when the image fails to load.
19 | AvatarFallback {
20 | // The content to display when the image fails to load.
21 | // This can be an icon or text representing the user.
22 | {children}
23 | }
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/preview/src/components/input/style.css:
--------------------------------------------------------------------------------
1 | /* Input Styles */
2 | .input {
3 | position: relative;
4 | display: flex;
5 | box-sizing: border-box;
6 | flex-direction: row;
7 | align-items: center;
8 | justify-content: space-between;
9 | padding: 0.25rem;
10 | padding: 8px 12px;
11 | border: none;
12 | border-radius: 0.5rem;
13 | border-radius: calc(0.5rem);
14 | background: none;
15 | background-color: var(--light, var(--primary-color)) var(--dark, color-mix(in oklab, #FFFFFF26 30%, transparent));
16 | box-shadow: inset 0 0 0 1px var(--light, var(--primary-color-6))
17 | var(--dark, var(--primary-color-7));
18 | color: var(--secondary-color-4);
19 | cursor: pointer;
20 | gap: 0.25rem;
21 | transition: background-color 100ms ease-out;
22 | }
23 |
24 | .input::placeholder {
25 | color: var(--secondary-color-5);
26 | }
27 |
28 | .input:disabled {
29 | color: var(--secondary-color-5);
30 | cursor: not-allowed;
31 | }
32 |
33 | .input:hover:not(:disabled),
34 | .input:focus-visible {
35 | background: var(--light, var(--primary-color-4))
36 | var(--dark, color-mix(in oklab, #FFFFFF26 50%, transparent));
37 | color: var(--secondary-color-1);
38 | outline: none;
39 | }
40 |
--------------------------------------------------------------------------------
/preview/src/components/card/docs.md:
--------------------------------------------------------------------------------
1 | The card component is a flexible container for grouping related content and actions. It provides a structured layout with optional header, content, and footer sections.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Card component must wrap all card elements.
7 | Card {
8 | // CardHeader contains the title, description, and optional action.
9 | CardHeader {
10 | // CardTitle displays the main heading.
11 | CardTitle { "Card Title" }
12 | // CardDescription provides supporting text.
13 | CardDescription { "Card description goes here." }
14 | // CardAction positions action elements (e.g., buttons) in the header.
15 | CardAction {
16 | Button { "Action" }
17 | }
18 | }
19 | // CardContent holds the main body content.
20 | CardContent {
21 | p { "Main content of the card." }
22 | }
23 | // CardFooter contains footer actions or information.
24 | CardFooter {
25 | Button { "Submit" }
26 | }
27 | }
28 | ```
29 |
30 | ## Layout Notes
31 |
32 | - When `CardAction` is present inside `CardHeader`, the header automatically switches to a two-column grid layout.
33 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/variants/unavailable_dates/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | use dioxus_primitives::calendar::DateRange;
5 | use time::{ext::NumericalDuration, UtcDateTime};
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_range = use_signal(|| None::);
10 |
11 | let now = UtcDateTime::now().date();
12 | let disabled_ranges = use_signal(|| {
13 | vec![
14 | DateRange::new(now, now.saturating_add(3.days())),
15 | DateRange::new(now.saturating_add(15.days()), now.saturating_add(18.days())),
16 | DateRange::new(now.saturating_add(22.days()), now.saturating_add(23.days())),
17 | ]
18 | });
19 |
20 | rsx! {
21 | div {
22 | DateRangePicker {
23 | selected_range: selected_range(),
24 | on_range_change: move |range| {
25 | tracing::info!("Selected range: {:?}", range);
26 | selected_range.set(range);
27 | },
28 | disabled_ranges: disabled_ranges,
29 | DateRangePickerInput {}
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/preview/src/components/dialog/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::components::button::component::Button;
2 |
3 | use super::super::component::{DialogContent, DialogDescription, DialogRoot, DialogTitle};
4 | use dioxus::prelude::*;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut open = use_signal(|| false);
9 |
10 | rsx! {
11 | Button {
12 | r#type: "button",
13 | "data-style": "outline",
14 | style: "margin-bottom: 1.5rem;",
15 | onclick: move |_| open.set(true),
16 | "Show Dialog"
17 | }
18 | DialogRoot { open: open(), on_open_change: move |v| open.set(v),
19 | DialogContent {
20 | button {
21 | class: "dialog-close",
22 | r#type: "button",
23 | aria_label: "Close",
24 | tabindex: if open() { "0" } else { "-1" },
25 | onclick: move |_| open.set(false),
26 | "×"
27 | }
28 | DialogTitle { "Item information" }
29 | DialogDescription { "Here is some additional information about the item." }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/preview/src/components/switch/style.css:
--------------------------------------------------------------------------------
1 | .switch-example {
2 | display: flex;
3 | align-items: center;
4 | padding: 20px;
5 | gap: 15px;
6 | }
7 |
8 | .switch {
9 | all: unset;
10 | position: relative;
11 | width: 2rem;
12 | height: 1.15rem;
13 | border-radius: 9999px;
14 | background-color: var(--primary-color-6);
15 | cursor: pointer;
16 | transition: background-color 150ms;
17 | }
18 |
19 | .switch[data-state="checked"] {
20 | background-color: var(--secondary-color-2);
21 | }
22 |
23 | .switch-thumb {
24 | display: block;
25 | width: calc(1.15rem - 2px);
26 | height: calc(1.15rem - 2px);
27 | border-radius: 9999px;
28 | background-color: var(--light, var(--primary-color)) var(--dark, var(--secondary-color-2));
29 | transform: translateX(1px);
30 | transition: transform 150ms;
31 | will-change: transform;
32 | }
33 |
34 | .switch[data-state="checked"] .switch-thumb {
35 | background-color: var(--light, var(--primary-color)) var(--dark, var(--primary-color-3));
36 | transform: translateX(calc(2rem - 1px - (1.15rem - 2px)));
37 | }
38 |
39 | /* Only apply disabled styles when data-disabled is "true" */
40 | .switch[data-disabled="true"] {
41 | cursor: not-allowed;
42 | opacity: 0.5;
43 | }
44 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::toggle_group::{self, ToggleGroupProps, ToggleItemProps};
3 |
4 | #[component]
5 | pub fn ToggleGroup(props: ToggleGroupProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | toggle_group::ToggleGroup {
9 | class: "toggle-group",
10 | default_pressed: props.default_pressed,
11 | pressed: props.pressed,
12 | on_pressed_change: props.on_pressed_change,
13 | disabled: props.disabled,
14 | allow_multiple_pressed: props.allow_multiple_pressed,
15 | horizontal: props.horizontal,
16 | roving_loop: props.roving_loop,
17 | attributes: props.attributes,
18 | {props.children}
19 | }
20 | }
21 | }
22 |
23 | #[component]
24 | pub fn ToggleItem(props: ToggleItemProps) -> Element {
25 | rsx! {
26 | toggle_group::ToggleItem {
27 | class: "toggle-item",
28 | index: props.index,
29 | disabled: props.disabled,
30 | attributes: props.attributes,
31 | {props.children}
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/preview/src/components/toolbar/style.css:
--------------------------------------------------------------------------------
1 | .toolbar {
2 | display: flex;
3 | flex-flow: row wrap;
4 | align-items: center;
5 | justify-content: space-between;
6 | padding: .25rem;
7 | border: none;
8 | border-radius: .5rem;
9 | box-shadow: inset 0 0 0 1px var(--primary-color-6);
10 | gap: .25rem;
11 | }
12 |
13 | .toolbar button {
14 | padding: 8px 12px;
15 | border: none;
16 | border-radius: calc(.5rem - .25rem);
17 | background: none;
18 | color: var(--secondary-color-4);
19 | cursor: pointer;
20 | }
21 |
22 | .toolbar button:hover:not([disabled]),
23 | .toolbar button:focus-visible {
24 | background: var(--light, var(--primary-color-4))
25 | var(--dark, var(--primary-color-7));
26 | color: var(--secondary-color-1);
27 | }
28 |
29 | .toolbar button:disabled {
30 | color: var(--secondary-color-5);
31 | cursor: not-allowed;
32 | }
33 |
34 | .toolbar-group {
35 | display: flex;
36 | flex-direction: row;
37 | gap: 5px;
38 | }
39 |
40 | .toolbar-separator {
41 | width: 1px;
42 | height: 24px;
43 | margin: 0 5px;
44 | background-color: var(--primary-color-6);
45 | }
46 |
47 | .toolbar-content p {
48 | padding: 0;
49 | margin: 0;
50 | }
51 |
--------------------------------------------------------------------------------
/preview/src/components/radio_group/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::radio_group::{self, RadioGroupProps, RadioItemProps};
3 |
4 | #[component]
5 | pub fn RadioGroup(props: RadioGroupProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | radio_group::RadioGroup {
9 | class: "radio-group",
10 | value: props.value,
11 | default_value: props.default_value,
12 | on_value_change: props.on_value_change,
13 | disabled: props.disabled,
14 | required: props.required,
15 | name: props.name,
16 | horizontal: props.horizontal,
17 | roving_loop: props.roving_loop,
18 | attributes: props.attributes,
19 | {props.children}
20 | }
21 | }
22 | }
23 |
24 | #[component]
25 | pub fn RadioItem(props: RadioItemProps) -> Element {
26 | rsx! {
27 | radio_group::RadioItem {
28 | class: "radio-item",
29 | value: props.value,
30 | index: props.index,
31 | disabled: props.disabled,
32 | attributes: props.attributes,
33 | {props.children}
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/playwright/toolbar.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=toolbar&");
5 | let bold = page.getByRole("button", { name: "Bold" });
6 | let italic = page.getByRole("button", { name: "Italic" });
7 | let underline = page.getByRole("button", { name: "Underline" });
8 | let alignLeft = page.getByRole("button", { name: "Align Left" });
9 | let alignCenter = page.getByRole("button", { name: "Align Center" });
10 | let alignRight = page.getByRole("button", { name: "Align Right" });
11 | await page.locator("#component-preview-frame").focus();
12 | // Tabbing to the first button should focus it
13 | await page.keyboard.press("Tab");
14 | await expect(bold).toBeFocused();
15 | await page.keyboard.press("ArrowRight");
16 | await expect(italic).toBeFocused();
17 | await page.keyboard.press("ArrowRight");
18 | await expect(underline).toBeFocused();
19 | await page.keyboard.press("ArrowRight");
20 | await expect(alignLeft).toBeFocused();
21 | await page.keyboard.press("ArrowRight");
22 | await expect(alignCenter).toBeFocused();
23 | await page.keyboard.press("ArrowRight");
24 | await expect(alignRight).toBeFocused();
25 | });
26 |
--------------------------------------------------------------------------------
/preview/src/components/card/style.css:
--------------------------------------------------------------------------------
1 | .card {
2 | display: flex;
3 | flex-direction: column;
4 | padding: 1.5rem 0;
5 | border: 1px solid var(--light, var(--primary-color-6)) var(--dark, var(--primary-color-5));
6 | border-radius: 1rem;
7 | background-color: var(--light, var(--primary-color-2)) var(--dark, var(--primary-color-3));
8 | box-shadow: 0 2px 10px rgb(0 0 0 / 10%);
9 | color: var(--secondary-color-4);
10 | gap: 1.5rem;
11 | }
12 |
13 | .card-header {
14 | display: grid;
15 | align-items: start;
16 | padding: 0 1.5rem;
17 | gap: 0.5rem;
18 | grid-auto-rows: min-content;
19 | grid-template-rows: auto auto;
20 | }
21 |
22 | .card-header:has([data-slot="card-action"]) {
23 | grid-template-columns: 1fr auto;
24 | }
25 |
26 | .card-title {
27 | font-size: 1rem;
28 | font-weight: 600;
29 | line-height: 1;
30 | }
31 |
32 | .card-description {
33 | color: var(--secondary-color-5);
34 | font-size: 0.875rem;
35 | line-height: 1.25rem;
36 | }
37 |
38 | .card-action {
39 | grid-column-start: 2;
40 | grid-row: 1 / span 2;
41 | place-self: start end;
42 | }
43 |
44 | .card-content {
45 | padding: 0 1.5rem;
46 | }
47 |
48 | .card-footer {
49 | display: flex;
50 | align-items: center;
51 | padding: 0 1.5rem;
52 | }
53 |
--------------------------------------------------------------------------------
/preview/src/components/hover_card/docs.md:
--------------------------------------------------------------------------------
1 | The HoverCard component can be used to display additional information when a user hovers over an element. It is useful for showing tooltips, additional details, or any other content that should be revealed on hover.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The HoverCard component wraps the trigger element and the content that will be displayed on hover.
7 | HoverCard {
8 | // The HoverCardTrigger contains the elements that will trigger the hover card to display when hovered.
9 | HoverCardTrigger {
10 | // The elements that will trigger the hover card when hovered over.
11 | {children}
12 | }
13 | // The HoverCardContent contains the content that will be displayed when the user hovers over the trigger.
14 | HoverCardContent {
15 | // The side of the HoverCardTrigger where the content will be displayed. Can be one Top, Right, Bottom, or Left.
16 | side: ContentSide::Bottom,
17 | // The alignment of the HoverCardContent relative to the HoverCardTrigger. Can be one of Start, Center, or End.
18 | align: ContentAlign::Start,
19 | // The content of the hover card, which can include text, images, or any other elements.
20 | {children}
21 | }
22 | }
23 | ```
--------------------------------------------------------------------------------
/preview/src/components/radio_group/style.css:
--------------------------------------------------------------------------------
1 | .radio-group {
2 | display: flex;
3 | flex-direction: column;
4 | gap: .75rem;
5 | }
6 |
7 | .radio-item {
8 | display: flex;
9 | flex-direction: row;
10 | align-items: center;
11 | padding: 0;
12 | border: none;
13 | background-color: transparent;
14 | color: var(--secondary-color-4);
15 | font-size: 14px;
16 | gap: .75rem;
17 |
18 | &::before {
19 | display: block;
20 | width: 1rem;
21 | height: 1rem;
22 | box-sizing: border-box;
23 | border-radius: 1.5rem;
24 | background: var(--light, var(--primary-color)) var(--dark, var(--primary-color-3));
25 | box-shadow: 0 0 0 1px var(--light, var(--primary-color-6))
26 | var(--dark, var(--primary-color-7));
27 | content: "";
28 | cursor: pointer;
29 | }
30 |
31 | &:focus-visible {
32 | outline: none;
33 | }
34 |
35 | &:focus-visible::before {
36 | box-shadow: 0 0 0 2px var(--focused-border-color);
37 | }
38 |
39 | &[data-state="checked"]::before {
40 | border: 0.25rem solid var(--light, var(--primary-color)) var(--dark, var(--primary-color-3));
41 | background: var(--secondary-color-4);
42 | }
43 |
44 | &[data-disabled="true"]::before {
45 | cursor: not-allowed;
46 | opacity: 0.5;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/playwright/popover.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=popover&");
5 | const popoverButton = page.getByRole("button", { name: "Show Popover" });
6 | await expect(popoverButton).toBeVisible();
7 | await popoverButton.click();
8 | // pressing the first input should be focused
9 | const confirm = page.getByRole("button", { name: "Confirm" });
10 | const cancel = page.getByRole("button", { name: "Cancel" });
11 | await expect(confirm).toBeFocused();
12 | // pressing tab again should focus the cancel button
13 | await page.keyboard.press("Tab");
14 | await expect(cancel).toBeFocused();
15 | // pressing tab again should focus the confirm button again
16 | await page.keyboard.press("Tab");
17 | await expect(confirm).toBeFocused();
18 | // pressing enter should close the popover
19 | await page.keyboard.press("Enter");
20 | // the item should show deleted under component-preview-frame
21 | await expect(page.locator("#component-preview-frame")).toContainText(
22 | "Item deleted!",
23 | );
24 |
25 | // Open the popover again
26 | await popoverButton.click();
27 | // pressing escape should close the popover
28 | await page.keyboard.press("Escape");
29 | });
30 |
--------------------------------------------------------------------------------
/playwright/dialog.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=dialog&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | await page.getByRole('button', { name: 'Show Dialog' }).click();
6 | // Assert the dialog is open
7 | const dialog = page.locator('.dialog-backdrop');
8 | await expect(dialog).toHaveAttribute('data-state', 'open');
9 | // Assert the close button is focused
10 | const closeButton = dialog.getByRole('button');
11 | await expect(closeButton).toBeFocused();
12 | // Hitting tab should keep focus on the close button
13 | await page.keyboard.press('Tab');
14 | await expect(closeButton).toBeFocused();
15 | // Hitting escape should close the dialog
16 | await page.keyboard.press('Escape');
17 | // Assert the dialog can no longer be found
18 | await expect(dialog).toHaveCount(0);
19 |
20 | // Reopen the dialog
21 | await page.getByRole('button', { name: 'Show Dialog' }).click();
22 | // Assert the dialog is open again
23 | await expect(dialog).toHaveAttribute('data-state', 'open');
24 | // Click the close button
25 | await closeButton.click();
26 | // Assert the dialog is closed after clicking close
27 | await expect(dialog).toHaveCount(0);
28 | });
29 |
--------------------------------------------------------------------------------
/preview/src/components/alert_dialog/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::components::button::component::Button;
2 |
3 | use super::super::component::*;
4 | use dioxus::prelude::*;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut open = use_signal(|| false);
9 | let mut confirmed = use_signal(|| false);
10 |
11 | rsx! {
12 | Button {
13 | r#type: "button",
14 | "data-style": "outline",
15 | style: "margin-bottom: 1.5rem;",
16 | onclick: move |_| open.set(true),
17 | "Show Alert Dialog"
18 | }
19 | AlertDialogRoot { open: open(), on_open_change: move |v| open.set(v),
20 | AlertDialogContent {
21 | AlertDialogTitle { "Delete item" }
22 | AlertDialogDescription { "Are you sure you want to delete this item? This action cannot be undone." }
23 | AlertDialogActions {
24 | AlertDialogCancel { "Cancel" }
25 | AlertDialogAction { on_click: move |_| confirmed.set(true), "Delete" }
26 | }
27 | }
28 | }
29 | if confirmed() {
30 | p { style: "color: var(--contrast-error-color); margin-top: 16px; font-weight: 600;",
31 | "Item deleted!"
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/preview/src/components/toolbar/docs.md:
--------------------------------------------------------------------------------
1 | The toolbar component is a flexible and customizable component that can be used to create a variety of toolbars. It can be used to create a simple toolbar with a title, or a more complex toolbar with multiple buttons and actions.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Toolbar component wraps all toolbar items.
7 | Toolbar {
8 | // The aria_label of the toolbar, used for accessibility purposes.
9 | aria_label: "Toolbar Title",
10 | // The ToolbarButton component represents each individual button in the toolbar.
11 | ToolbarButton {
12 | // The index of the toolbar button, used to determine the order in which buttons are focused.
13 | index: 0,
14 | on_click: |_: ()| {
15 | // This callback is triggered when the button is clicked.
16 | },
17 | // The contents of the toolbar button
18 | {children}
19 | }
20 | // The ToolbarSeparator component represents a separator line in the toolbar.
21 | ToolbarSeparator {
22 | // The orientation of the separator, true for horizontal and false for vertical.
23 | horizontal: true,
24 | // The decorative property controls if the separator is decorative and should not be visible to screen readers.
25 | decorative: false,
26 | }
27 | }
28 | ```
--------------------------------------------------------------------------------
/preview/src/components/popover/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::popover::{
3 | self, PopoverContentProps, PopoverRootProps, PopoverTriggerProps,
4 | };
5 |
6 | #[component]
7 | pub fn PopoverRoot(props: PopoverRootProps) -> Element {
8 | rsx! {
9 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
10 | popover::PopoverRoot {
11 | class: "popover",
12 | is_modal: props.is_modal,
13 | open: props.open,
14 | default_open: props.default_open,
15 | on_open_change: props.on_open_change,
16 | attributes: props.attributes,
17 | {props.children}
18 | }
19 | }
20 | }
21 |
22 | #[component]
23 | pub fn PopoverTrigger(props: PopoverTriggerProps) -> Element {
24 | rsx! {
25 | popover::PopoverTrigger { class: "popover-trigger", attributes: props.attributes, {props.children} }
26 | }
27 | }
28 |
29 | #[component]
30 | pub fn PopoverContent(props: PopoverContentProps) -> Element {
31 | rsx! {
32 | popover::PopoverContent {
33 | class: "popover-content",
34 | id: props.id,
35 | side: props.side,
36 | align: props.align,
37 | attributes: props.attributes,
38 | {props.children}
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/preview/src/components/slider/docs.md:
--------------------------------------------------------------------------------
1 | The slider component allows users to select a value from a range by sliding a handle along a track.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The slider component wraps all slider-related elements.
7 | Slider {
8 | // The current value of the slider, which should be updated as the user interacts with the slider.
9 | value: SliderValue::Single(0.0),
10 | // The orientation of the slider, true for horizontal and false for vertical.
11 | horizontal: true,
12 | // Callback function triggered when the slider value changes.
13 | on_value_change: |value: u32| {
14 | // Handle the change in slider value.
15 | },
16 | // The track represents the visual track along which the handle moves.
17 | SliderTrack {
18 | // The slider range represents the filled portion of the track
19 | SliderRange {
20 | // The content of the range
21 | {children}
22 | }
23 | // The slider thumb represents the draggable handle that the user moves along the track.
24 | SliderThumb {
25 | // An optional index which can be either 0 or 1 to indicate if this is the first or second thumb in a range slider.
26 | index: 0,
27 | // The content of the thumb button
28 | {children}
29 | }
30 | }
31 | }
32 | ```
--------------------------------------------------------------------------------
/playwright/toggle_group.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=toggle_group&");
5 | const b_button = page.getByRole("button", { name: "B", exact: true });
6 | const i_button = page.getByRole("button", { name: "I", exact: true });
7 | const u_button = page.getByRole("button", { name: "U", exact: true });
8 |
9 | // The buttons should not be selected initially
10 | await expect(b_button).toHaveAttribute("data-state", "off");
11 | await expect(i_button).toHaveAttribute("data-state", "off");
12 | await expect(u_button).toHaveAttribute("data-state", "off");
13 |
14 | // Click the "B" button and check its state
15 | await b_button.click();
16 | await expect(b_button).toHaveAttribute("data-state", "on");
17 |
18 | // Pressing right arrow should select the "I" button
19 | await page.keyboard.press("ArrowRight");
20 | await expect(i_button).toBeFocused();
21 |
22 | // Pressing enter should focus the "I" button
23 | await page.keyboard.press("Enter");
24 | await expect(i_button).toHaveAttribute("data-state", "on");
25 |
26 | // Pressing right two more times should bring us back to the "B" button
27 | await page.keyboard.press("ArrowRight");
28 | await page.keyboard.press("ArrowRight");
29 | await expect(b_button).toBeFocused();
30 | });
31 |
--------------------------------------------------------------------------------
/test-harness/src/main.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 |
3 | const FAVICON: Asset = asset!("/assets/favicon.ico");
4 | const MAIN_CSS: Asset = asset!("/assets/main.css");
5 | const HEADER_SVG: Asset = asset!("/assets/header.svg");
6 |
7 | mod components;
8 |
9 | fn main() {
10 | dioxus::launch(App);
11 | }
12 |
13 | #[component]
14 | fn App() -> Element {
15 | rsx! {
16 | document::Link { rel: "icon", href: FAVICON }
17 | document::Link { rel: "stylesheet", href: MAIN_CSS }
18 | Hero {}
19 |
20 | }
21 | }
22 |
23 | #[component]
24 | pub fn Hero() -> Element {
25 | rsx! {
26 | div { id: "hero",
27 | img { src: HEADER_SVG, id: "header" }
28 | div { id: "links",
29 | a { href: "https://dioxuslabs.com/learn/0.6/", "📚 Learn Dioxus" }
30 | a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
31 | a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
32 | a { href: "https://github.com/DioxusLabs/sdk", "⚙️ Dioxus Development Kit" }
33 | a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus",
34 | "💫 VSCode Extension"
35 | }
36 | a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/playwright/slider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=slider&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | const slider = await page.locator('.slider');
6 | const thumb = await page.locator('.slider-thumb');
7 | // The initial aria-valuenow should be 50
8 | await expect(thumb).toHaveAttribute('aria-valuenow', '50');
9 | await thumb.focus();
10 | // The aria-valuenow should be 60 after pressing Shift+ArrowRight
11 | await page.keyboard.press('Shift+ArrowRight');
12 | await expect(thumb).toHaveAttribute('aria-valuenow', '60');
13 | await page.keyboard.press('Shift+ArrowRight');
14 | // The aria-valuenow should be 70 after pressing Shift+ArrowRight again
15 | await expect(thumb).toHaveAttribute('aria-valuenow', '70');
16 | // Pressing Shift+ArrowLeft should decrease the value by 10
17 | await page.keyboard.press('Shift+ArrowLeft');
18 | await expect(thumb).toHaveAttribute('aria-valuenow', '60');
19 | // Pressing ArrowLeft should decrease the value by 1
20 | await page.keyboard.press('ArrowLeft');
21 | await expect(thumb).toHaveAttribute('aria-valuenow', '59');
22 | // Pressing ArrowRight should increase the value by 1
23 | await page.keyboard.press('ArrowRight');
24 | await expect(thumb).toHaveAttribute('aria-valuenow', '60');
25 | });
--------------------------------------------------------------------------------
/preview/src/components/tooltip/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::tooltip::{self, TooltipContentProps, TooltipProps, TooltipTriggerProps};
3 |
4 | #[component]
5 | pub fn Tooltip(props: TooltipProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | tooltip::Tooltip {
9 | class: "tooltip",
10 | disabled: props.disabled,
11 | open: props.open,
12 | default_open: props.default_open,
13 | on_open_change: props.on_open_change,
14 | attributes: props.attributes,
15 | {props.children}
16 | }
17 | }
18 | }
19 |
20 | #[component]
21 | pub fn TooltipTrigger(props: TooltipTriggerProps) -> Element {
22 | rsx! {
23 | tooltip::TooltipTrigger {
24 | class: "tooltip-trigger",
25 | id: props.id,
26 | attributes: props.attributes,
27 | {props.children}
28 | }
29 | }
30 | }
31 |
32 | #[component]
33 | pub fn TooltipContent(props: TooltipContentProps) -> Element {
34 | rsx! {
35 | tooltip::TooltipContent {
36 | class: "tooltip-content",
37 | id: props.id,
38 | side: props.side,
39 | align: props.align,
40 | attributes: props.attributes,
41 | {props.children}
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/preview/src/components/context_menu/docs.md:
--------------------------------------------------------------------------------
1 | The context menu component can be used to define a context menu that is displayed when the user right-clicks on an element. It can contain various menu items that the user can interact with.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The context menu component must wrap all context menu items.
7 | ContextMenu {
8 | // The context menu trigger is the element that will display the context menu when right-clicked.
9 | ContextMenuTrigger {
10 | // The content of the trigger
11 | {children}
12 | }
13 | // The context menu content contains all the items that will be displayed in the context menu.
14 | ContextMenuContent {
15 | // Each context menu item represents an individual action in the context menu. Items are displayed in order based on the order of the index property.
16 | ContextMenuItem {
17 | // The index of the item, used to determine the order in which items are displayed.
18 | index: 0,
19 | // The value of the item which will be passed to the on_select callback when the item is selected.
20 | value: "",
21 | on_select: |value: String| {
22 | // This callback is triggered when the item is selected.
23 | // The value parameter contains the value of the selected item.
24 | },
25 | }
26 | }
27 | }
28 | ```
--------------------------------------------------------------------------------
/preview/src/components/dropdown_menu/docs.md:
--------------------------------------------------------------------------------
1 | The DropdownMenu component is used to create a dropdown menu that can be triggered by a button click. It allows users to select an option from a list of items.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The dropdown menu component must wrap all dropdown items.
7 | DropdownMenu {
8 | // The dropdown menu trigger is the button that will display the dropdown menu when clicked.
9 | DropdownMenuTrigger {
10 | // The content of the trigger to display inside the button.
11 | {children}
12 | }
13 | // The dropdown menu content contains all the items that will be displayed in the dropdown menu.
14 | DropdownMenuContent {
15 | // Each dropdown menu item represents an individual option in the dropdown menu. Items are displayed in order based on the order of the index property.
16 | DropdownMenuItem {
17 | // The index of the item, used to determine the order in which items are displayed.
18 | index: 0,
19 | // The value of the item which will be passed to the on_select callback when the item is selected.
20 | value: "",
21 | on_select: |value: String| {
22 | // This callback is triggered when the item is selected.
23 | // The value parameter contains the value of the selected item.
24 | },
25 | }
26 | }
27 | }
28 | ```
--------------------------------------------------------------------------------
/preview/src/components/skeleton/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | div { style: "display: flex; flex-direction: column; align-items: center; gap: 2rem;",
8 | SkeletonInfoDemo {}
9 | SkeletonCardDemo {}
10 | }
11 | }
12 | }
13 |
14 | #[component]
15 | fn SkeletonInfoDemo() -> Element {
16 | rsx! {
17 | div { style: "display: flex; align-items: center; gap: 1rem;",
18 | Skeleton { style: "width: 3rem; height: 3rem; border-radius: 50%;" }
19 | div { style: "display: flex; flex-direction: column; gap: 0.5rem;",
20 | Skeleton { style: "width: 11.625rem; height: 1rem;" }
21 | Skeleton { style: "width: 8.5rem; height: 1rem;" }
22 | }
23 | }
24 | }
25 | }
26 |
27 | #[component]
28 | fn SkeletonCardDemo() -> Element {
29 | rsx! {
30 | div { style: "display: flex; flex-direction: column; gap: 0.75rem;",
31 | Skeleton { style: "width: 15rem; height: 8rem; border-radius: 0.75rem;" }
32 | div { style: "display: flex; flex-direction: column; gap: 0.5rem;",
33 | Skeleton { style: "width: 15.625rem; height: 1rem;" }
34 | Skeleton { style: "width: 12.5rem; height: 1rem;" }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/preview/src/components/menubar/docs.md:
--------------------------------------------------------------------------------
1 | The Menubar component can be used to display a menu bar with collapsible menus.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Menubar component wraps the entire menu bar and contains the individual menus in the order of their index.
7 | Menubar {
8 | // The MenubarMenu contains the individual menus that can be opened.
9 | MenubarMenu {
10 | // The index of the menu, used to determine the order in which menus are displayed.
11 | index: 0,
12 | // The menubar trigger is the element that will display the menu when activated.
13 | MenubarTrigger {
14 | // The content of the trigger button
15 | {children}
16 | }
17 | // The menubar content contains all the items that will be displayed in the menu when it is opened.
18 | MenubarContent {
19 | // Each menubar item represents an individual items in the menu.
20 | MenubarItem {
21 | // The value of the item which will be passed to the on_select callback when the item is selected.
22 | value: "",
23 | on_select: |value: String| {
24 | // This callback is triggered when the item is selected.
25 | // The value parameter contains the value of the selected item.
26 | },
27 | }
28 | }
29 | }
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/preview/src/components/navbar/docs.md:
--------------------------------------------------------------------------------
1 | The Navbar component can be used to display a navigation bar with collapsible sections.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Navbar component wraps the entire menu bar and contains the individual menus in the order of their index.
7 | Navbar {
8 | // The NavbarNav contains the individual menus that can be opened.
9 | NavbarNav {
10 | // The index of the menu, used to determine the order in which menus are displayed.
11 | index: 0,
12 | // The menubar trigger is the element that will display the menu when activated.
13 | NavbarTrigger {
14 | // The content of the trigger button
15 | {children}
16 | }
17 | // The menubar content contains all the items that will be displayed in the menu when it is opened.
18 | NavbarContent {
19 | // Each menubar item represents an individual items in the menu.
20 | NavbarItem {
21 | // The value of the item which will be passed to the on_select callback when the item is selected.
22 | value: "",
23 | on_select: |value: String| {
24 | // This callback is triggered when the item is selected.
25 | // The value parameter contains the value of the selected item.
26 | },
27 | }
28 | }
29 | }
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/preview/src/components/tabs/docs.md:
--------------------------------------------------------------------------------
1 | The Tabs component is used to create a tabbed interface, allowing users to switch between different views or sections of content.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Tabs component wraps all tab triggers and contents and orders them based on their index.
7 | Tabs {
8 | // The TabList component contains all the tab triggers
9 | TabList {
10 | // The TabTrigger component is used to create a clickable tab button that switches the active tab.
11 | TabTrigger {
12 | // The index of the tab trigger, used to determine the focus order of the tabs.
13 | index: 0,
14 | // The value of the tab trigger, which must be unique and is used to identify the active tab.
15 | value: "tab1",
16 | // The contents of the tab trigger button
17 | {children}
18 | }
19 | }
20 | // The TabContent component contains the content that is displayed when the corresponding tab is active.
21 | TabContent {
22 | // The index of the tab content, used to determine the focus order of the tabs.
23 | index: 0,
24 | // The value of the tab content, which must match the value of the corresponding TabTrigger to be displayed.
25 | value: "tab1",
26 | // The content of the tab, which is displayed when the tab is active.
27 | {children}
28 | }
29 | }
30 | ```
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/simple/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use time::{Date, UtcDateTime};
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut selected_date = use_signal(|| None::);
8 | let mut view_date = use_signal(|| UtcDateTime::now().date());
9 | rsx! {
10 | div { class: "calendar-example", style: "padding: 20px;",
11 | Calendar {
12 | selected_date: selected_date(),
13 | on_date_change: move |date| {
14 | tracing::info!("Selected date: {:?}", date);
15 | selected_date.set(date);
16 | },
17 | view_date: view_date(),
18 | on_view_change: move |new_view: Date| {
19 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
20 | view_date.set(new_view);
21 | },
22 | CalendarView {
23 | CalendarHeader {
24 | CalendarNavigation {
25 | CalendarPreviousMonthButton {}
26 | CalendarMonthTitle {}
27 | CalendarNextMonthButton {}
28 | }
29 | }
30 | CalendarGrid {}
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/preview/src/components/dropdown_menu/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::{
2 | DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
3 | };
4 | use dioxus::prelude::*;
5 | use strum::IntoEnumIterator;
6 |
7 | #[derive(Clone, Copy, strum::Display, strum::EnumIter, PartialEq)]
8 | enum Operation {
9 | Edit,
10 | Undo,
11 | Duplicate,
12 | Delete,
13 | }
14 |
15 | #[component]
16 | pub fn Demo() -> Element {
17 | let mut selected_operation = use_signal(|| None);
18 |
19 | let operations = Operation::iter().enumerate().map(|(i, o)| {
20 | rsx! {
21 | DropdownMenuItem:: {
22 | class: "dropdown-menu-item",
23 | value: o,
24 | index: i,
25 | disabled: matches!(o, Operation::Undo),
26 | on_select: move |value| {
27 | selected_operation.set(Some(value));
28 | },
29 | {o.to_string()}
30 | }
31 | }
32 | });
33 |
34 | rsx! {
35 | DropdownMenu { class: "dropdown-menu", default_open: false,
36 | DropdownMenuTrigger { class: "dropdown-menu-trigger", "Open Menu" }
37 | DropdownMenuContent { class: "dropdown-menu-content", {operations} }
38 | }
39 | if let Some(op) = selected_operation() {
40 | "Selected: {op}"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/primitives/src/js/focus-trap.js:
--------------------------------------------------------------------------------
1 | var focusable=function(element){if(element.tabIndex<0||element.getAttribute("disabled"))return!1;switch(element.tagName){case"A":return!!element.href&&element.rel!=="ignore";case"INPUT":return element.type!=="hidden";case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}};class FocusTrap{container;restoreFocusElement;nodeWalker;constructor(container){this.container=container,this.restoreFocusElement=document.activeElement,this.nodeWalker=document.createTreeWalker(this.container,NodeFilter.SHOW_ELEMENT,{acceptNode:(node)=>{if(node instanceof HTMLElement&&focusable(node))return NodeFilter.FILTER_ACCEPT;return NodeFilter.FILTER_SKIP}}),this.focusNext(),this.container.addEventListener("keydown",(event)=>{if(event.key==="Tab"){if(event.shiftKey)this.focusPrevious();else this.focusNext();event.preventDefault()}})}remove(){this.restoreFocusElement.focus()}focusChild(child){child.focus()}focusNext(){const nextNode=this.nodeWalker.nextNode();if(nextNode)this.focusChild(nextNode);else{this.nodeWalker.currentNode=this.container;const nextNode2=this.nodeWalker.nextNode();if(nextNode2)this.focusChild(nextNode2)}}focusPrevious(){const previousNode=this.nodeWalker.previousNode();if(previousNode)this.focusChild(previousNode);else{this.nodeWalker.currentNode=this.container;const lastNode=this.nodeWalker.lastChild();if(lastNode)this.focusChild(lastNode)}}}window.createFocusTrap=(container)=>{return new FocusTrap(container)};
--------------------------------------------------------------------------------
/.github/workflows/stylelint.yml:
--------------------------------------------------------------------------------
1 | name: Stylelint Checks
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - /**
9 | - preview/**/*.rs
10 | - preview/**/Cargo.toml
11 | - primitives/**/*.rs
12 | - primitives/**/Cargo.toml
13 | - .github/**
14 | - Cargo.toml
15 |
16 | pull_request:
17 | types: [opened, synchronize, reopened, ready_for_review]
18 | branches:
19 | - main
20 | paths:
21 | - /**
22 | - preview/**/*.rs
23 | - preview/**/Cargo.toml
24 | - primitives/**/*.rs
25 | - primitives/**/Cargo.toml
26 | - .github/**
27 | - Cargo.toml
28 |
29 | concurrency:
30 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
31 | cancel-in-progress: true
32 |
33 | jobs:
34 | stylelint:
35 | if: github.event.pull_request.draft == false
36 | timeout-minutes: 30
37 | runs-on: ubuntu-latest
38 | steps:
39 | # Do our best to cache the toolchain and node install steps
40 | - uses: actions/checkout@v4
41 | - uses: actions/setup-node@v4
42 | with:
43 | node-version: lts/*
44 | - name: Install dependencies
45 | run: |
46 | npm install --save-dev stylelint-config-idiomatic-order
47 | npm install -g stylelint stylelint-config-standard
48 | - name: Stylelint
49 | run: cd ./preview && npx stylelint "**/*.css" --max-warnings=0
--------------------------------------------------------------------------------
/playwright/tabs.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("test", async ({ page }) => {
4 | await page.goto("http://127.0.0.1:8080/component/?name=tabs&");
5 | let activeTab = page.locator(
6 | "#component-preview-frame > .tabs > .tabs-content[data-state='active']",
7 | );
8 | let tab1Button = page.getByRole("tab", { name: "Tab 1" });
9 | let tab2Button = page.getByRole("tab", { name: "Tab 2" });
10 | let tab3Button = page.getByRole("tab", { name: "Tab 3" });
11 | // Clicking the right arrow should focus the next tab trigger
12 | await tab1Button.click();
13 | await page.keyboard.press("ArrowRight");
14 | await expect(tab2Button).toBeFocused();
15 |
16 | // Clicking enter should activate the focused tab
17 | await page.keyboard.press("Enter");
18 | await expect(activeTab).toContainText("Tab 2 Content");
19 |
20 | // Clicking right twice more should bring us back to the first tab
21 | await page.keyboard.press("ArrowRight");
22 | await expect(tab3Button).toBeFocused();
23 | await page.keyboard.press("ArrowRight");
24 | await expect(tab1Button).toBeFocused();
25 |
26 | // Clicking each tab should activate it
27 | await tab3Button.click();
28 | await expect(activeTab).toContainText("Tab 3 Content");
29 | await tab2Button.click();
30 | await expect(activeTab).toContainText("Tab 2 Content");
31 | await tab1Button.click();
32 | await expect(activeTab).toContainText("Tab 1 Content");
33 | });
34 |
--------------------------------------------------------------------------------
/playwright/preview.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 | import AxeBuilder from "@axe-core/playwright";
3 |
4 | test.describe("homepage", () => {
5 | test("should not have any automatically detectable accessibility issues", async ({
6 | page,
7 | }) => {
8 | await page.goto("http://127.0.0.1:8080/", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
9 |
10 | // Wait for the page to fully load
11 | let heroSection = page.locator("#hero");
12 | await heroSection.waitFor({ state: "visible" });
13 |
14 | const accessibilityScanResults = await new AxeBuilder({ page })
15 | .disableRules("color-contrast")
16 | .analyze();
17 |
18 | expect(accessibilityScanResults.violations).toEqual([]);
19 | });
20 | });
21 |
22 |
23 | test.describe("details", () => {
24 | test("should not have any automatically detectable accessibility issues", async ({
25 | page,
26 | }) => {
27 | await page.goto("http://127.0.0.1:8080/component/?name=calendar", { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
28 |
29 | // Wait for the page to fully load
30 | let componentSection = page.locator(".component-demo");
31 | await componentSection.waitFor({ state: "visible" });
32 |
33 | const accessibilityScanResults = await new AxeBuilder({ page })
34 | .disableRules("color-contrast")
35 | .analyze();
36 |
37 | expect(accessibilityScanResults.violations).toEqual([]);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/preview/src/components/hover_card/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::hover_card::{
3 | self, HoverCardContentProps, HoverCardProps, HoverCardTriggerProps,
4 | };
5 |
6 | #[component]
7 | pub fn HoverCard(props: HoverCardProps) -> Element {
8 | rsx! {
9 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
10 | hover_card::HoverCard {
11 | class: "hover-card",
12 | open: props.open,
13 | default_open: props.default_open,
14 | on_open_change: props.on_open_change,
15 | disabled: props.disabled,
16 | attributes: props.attributes,
17 | {props.children}
18 | }
19 | }
20 | }
21 |
22 | #[component]
23 | pub fn HoverCardTrigger(props: HoverCardTriggerProps) -> Element {
24 | rsx! {
25 | hover_card::HoverCardTrigger {
26 | class: "hover-card-trigger",
27 | id: props.id,
28 | attributes: props.attributes,
29 | {props.children}
30 | }
31 | }
32 | }
33 |
34 | #[component]
35 | pub fn HoverCardContent(props: HoverCardContentProps) -> Element {
36 | rsx! {
37 | hover_card::HoverCardContent {
38 | class: "hover-card-content",
39 | side: props.side,
40 | align: props.align,
41 | id: props.id,
42 | force_mount: props.force_mount,
43 | attributes: props.attributes,
44 | {props.children}
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/playwright/alert-dialog.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://127.0.0.1:8080/component/?name=alert_dialog&', { timeout: 20 * 60 * 1000 }); // Increase timeout to 20 minutes
5 | await page.getByRole('button', { name: 'Show Alert Dialog' }).click();
6 | // Assert the dialog is open
7 | const dialog = page.locator('.alert-dialog-backdrop');
8 | await expect(dialog).toHaveAttribute('data-state', 'open');
9 | // Assert the cancel button is focused
10 | const cancelButton = page.getByRole('button', { name: 'Cancel' });
11 | await expect(cancelButton).toBeFocused();
12 | // Hitting tab should move to the confirm button
13 | await page.keyboard.press('Tab');
14 | // Hitting tab again should move focus back to the cancel button
15 | await page.keyboard.press('Tab');
16 | await expect(cancelButton).toBeFocused();
17 | // Hitting escape should close the dialog
18 | await page.keyboard.press('Escape');
19 | // Assert the dialog is closed
20 | await expect(dialog).toHaveCount(0);
21 |
22 | // Reopen the dialog
23 | await page.getByRole('button', { name: 'Show Alert Dialog' }).click();
24 | // Assert the dialog is open again
25 | await expect(dialog).toHaveAttribute('data-state', 'open');
26 | // Click the confirm button
27 | const confirmButton = page.getByRole('button', { name: 'Delete' });
28 | await confirmButton.click();
29 | // Assert the dialog is closed after confirming
30 | await expect(dialog).toHaveCount(0);
31 | });
32 |
--------------------------------------------------------------------------------
/primitives/src/label.rs:
--------------------------------------------------------------------------------
1 | //! Defines the [`Label`] component
2 |
3 | use dioxus::prelude::*;
4 |
5 | /// The props for the [`Label`] component
6 | #[derive(Props, Clone, PartialEq)]
7 | pub struct LabelProps {
8 | /// The id of the element that this label is associated with
9 | pub html_for: ReadSignal,
10 |
11 | /// Additional attributes to apply to the label element
12 | #[props(extends = GlobalAttributes)]
13 | pub attributes: Vec,
14 |
15 | /// The children of the label element
16 | pub children: Element,
17 | }
18 |
19 | /// # Label
20 | ///
21 | /// The `Label` component is used to create a label for form elements. It must be associated with an element using the [`LabelProps::html_for`] attribute.
22 | ///
23 | /// ```rust
24 | /// use dioxus::prelude::*;
25 | /// use dioxus_primitives::label::Label;
26 | ///
27 | /// #[component]
28 | /// fn Demo() -> Element {
29 | /// rsx! {
30 | /// Label {
31 | /// html_for: "name",
32 | /// "Name"
33 | /// }
34 | ///
35 | /// input {
36 | /// id: "name",
37 | /// placeholder: "Enter your name",
38 | /// }
39 | /// }
40 | /// }
41 | /// ```
42 | #[component]
43 | pub fn Label(props: LabelProps) -> Element {
44 | // TODO: (?) the Radix primitive prevents selection on double click (but not intentional highlighting)
45 | rsx! {
46 | label {
47 | for: props.html_for,
48 | ..props.attributes,
49 |
50 | {props.children}
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use time::{macros::date, Date, UtcDateTime};
4 |
5 | #[component]
6 | pub fn Demo() -> Element {
7 | let mut selected_date = use_signal(|| None::);
8 | let mut view_date = use_signal(|| UtcDateTime::now().date());
9 | rsx! {
10 | div { class: "calendar-example", style: "padding: 20px;",
11 | Calendar {
12 | selected_date: selected_date(),
13 | on_date_change: move |date| {
14 | tracing::info!("Selected date: {:?}", date);
15 | selected_date.set(date);
16 | },
17 | view_date: view_date(),
18 | on_view_change: move |new_view: Date| {
19 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
20 | view_date.set(new_view);
21 | },
22 | min_date: date!(1995 - 07 - 21),
23 | max_date: date!(2035 - 09 - 11),
24 | CalendarView {
25 | CalendarHeader {
26 | CalendarNavigation {
27 | CalendarPreviousMonthButton {}
28 | CalendarSelectMonth {}
29 | CalendarSelectYear {}
30 | CalendarNextMonthButton {}
31 | }
32 | }
33 | CalendarGrid {}
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dioxus-components",
3 | "description": "",
4 | "members": [
5 | "preview/src/components/tabs",
6 | "preview/src/components/dropdown_menu",
7 | "preview/src/components/navbar",
8 | "preview/src/components/form",
9 | "preview/src/components/tooltip",
10 | "preview/src/components/calendar",
11 | "preview/src/components/menubar",
12 | "preview/src/components/progress",
13 | "preview/src/components/hover_card",
14 | "preview/src/components/input",
15 | "preview/src/components/accordion",
16 | "preview/src/components/separator",
17 | "preview/src/components/toast",
18 | "preview/src/components/toolbar",
19 | "preview/src/components/checkbox",
20 | "preview/src/components/label",
21 | "preview/src/components/slider",
22 | "preview/src/components/toggle",
23 | "preview/src/components/dialog",
24 | "preview/src/components/alert_dialog",
25 | "preview/src/components/popover",
26 | "preview/src/components/radio_group",
27 | "preview/src/components/button",
28 | "preview/src/components/collapsible",
29 | "preview/src/components/avatar",
30 | "preview/src/components/switch",
31 | "preview/src/components/select",
32 | "preview/src/components/toggle_group",
33 | "preview/src/components/context_menu",
34 | "preview/src/components/aspect_ratio",
35 | "preview/src/components/scroll_area",
36 | "preview/src/components/date_picker",
37 | "preview/src/components/textarea",
38 | "preview/src/components/skeleton",
39 | "preview/src/components/card",
40 | "preview/src/components/sheet"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/preview/src/components/select/docs.md:
--------------------------------------------------------------------------------
1 | The Select component is used to create a dropdown menu that allows users to select one or more options from the select groups.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The Select component wraps all select items in the dropdown.
7 | Select:: {
8 | // The currently selected value(s) in the dropdown.
9 | value: "option1",
10 | // Callback function triggered when the selected value changes.
11 | on_value_change: |value: String| {
12 | // Handle the change in selected value.
13 | },
14 | // The select trigger is the button that opens the dropdown.
15 | SelectTrigger {
16 | // The (optional) select value displays the currently selected text value.
17 | SelectValue {}
18 | }
19 | // All groups must be wrapped in the select list.
20 | SelectList {
21 | // An group within the select dropdown which may contain multiple items.
22 | SelectGroup {
23 | // The label for the group
24 | SelectGroupLabel {
25 | "Other"
26 | }
27 | // Each select option represents an individual option in the dropdown. The type must match the type of the select.
28 | SelectOption:: {
29 | // The value of the item, which will be passed to the on_value_change callback when selected.
30 | value: "option1",
31 | // Select item indicator is only rendered if the item is selected.
32 | SelectItemIndicator {
33 | "✔️"
34 | }
35 | }
36 | }
37 | }
38 | }
39 | ```
--------------------------------------------------------------------------------
/preview/src/components/dialog/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::dialog::{
3 | self, DialogContentProps, DialogDescriptionProps, DialogRootProps, DialogTitleProps,
4 | };
5 |
6 | #[component]
7 | pub fn DialogRoot(props: DialogRootProps) -> Element {
8 | rsx! {
9 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
10 | dialog::DialogRoot {
11 | class: "dialog-backdrop",
12 | id: props.id,
13 | is_modal: props.is_modal,
14 | open: props.open,
15 | default_open: props.default_open,
16 | on_open_change: props.on_open_change,
17 | attributes: props.attributes,
18 | {props.children}
19 | }
20 | }
21 | }
22 |
23 | #[component]
24 | pub fn DialogContent(props: DialogContentProps) -> Element {
25 | rsx! {
26 | dialog::DialogContent { class: "dialog", id: props.id, attributes: props.attributes, {props.children} }
27 | }
28 | }
29 |
30 | #[component]
31 | pub fn DialogTitle(props: DialogTitleProps) -> Element {
32 | rsx! {
33 | dialog::DialogTitle {
34 | class: "dialog-title",
35 | id: props.id,
36 | attributes: props.attributes,
37 | {props.children}
38 | }
39 | }
40 | }
41 |
42 | #[component]
43 | pub fn DialogDescription(props: DialogDescriptionProps) -> Element {
44 | rsx! {
45 | dialog::DialogDescription {
46 | class: "dialog-description",
47 | id: props.id,
48 | attributes: props.attributes,
49 | {props.children}
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/multi_month/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use time::{macros::date, Date, UtcDateTime};
4 |
5 | use dioxus_primitives::calendar::DateRange;
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_range = use_signal(|| None::);
10 | let mut view_date = use_signal(|| UtcDateTime::now().date());
11 | rsx! {
12 | div { class: "calendar-example", style: "padding: 20px;",
13 | RangeCalendar {
14 | selected_range: selected_range(),
15 | on_range_change: move |range| {
16 | tracing::info!("Selected range: {:?}", range);
17 | selected_range.set(range);
18 | },
19 | view_date: view_date(),
20 | on_view_change: move |new_view: Date| {
21 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
22 | view_date.set(new_view);
23 | },
24 | min_date: date!(1995 - 07 - 21),
25 | max_date: date!(2035 - 09 - 11),
26 | month_count: 3,
27 | CalendarView {
28 | CalendarHeader {
29 | CalendarNavigation {
30 | CalendarPreviousMonthButton {}
31 | CalendarMonthTitle {}
32 | CalendarNextMonthButton {}
33 | }
34 | }
35 | CalendarGrid {}
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/range/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use time::{macros::date, Date, UtcDateTime};
4 |
5 | use dioxus_primitives::calendar::DateRange;
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_range = use_signal(|| None::);
10 | let mut view_date = use_signal(|| UtcDateTime::now().date());
11 | rsx! {
12 | div { class: "calendar-example", style: "padding: 20px;",
13 | RangeCalendar {
14 | selected_range: selected_range(),
15 | on_range_change: move |range| {
16 | tracing::info!("Selected range: {:?}", range);
17 | selected_range.set(range);
18 | },
19 | view_date: view_date(),
20 | on_view_change: move |new_view: Date| {
21 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
22 | view_date.set(new_view);
23 | },
24 | min_date: date!(1995 - 07 - 21),
25 | max_date: date!(2035 - 09 - 11),
26 | CalendarView {
27 | CalendarHeader {
28 | CalendarNavigation {
29 | CalendarPreviousMonthButton {}
30 | CalendarSelectMonth {}
31 | CalendarSelectYear {}
32 | CalendarNextMonthButton {}
33 | }
34 | }
35 | CalendarGrid {}
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/preview/src/components/button/style.css:
--------------------------------------------------------------------------------
1 | .button {
2 | padding: 8px 18px;
3 | border: none;
4 | border-radius: 0.5rem;
5 | cursor: pointer;
6 | font-size: 1rem;
7 | transition: background-color 0.2s ease, color 0.2s ease;
8 | }
9 |
10 | .button:focus-visible {
11 | box-shadow: 0 0 0 2px var(--focused-border-color);
12 | }
13 |
14 | .button[data-style="primary"] {
15 | background-color: var(--secondary-color-2);
16 | color: var(--primary-color);
17 | }
18 |
19 | .button[data-style="primary"]:hover {
20 | background-color: var(--secondary-color-1);
21 | }
22 |
23 | .button[data-style="secondary"] {
24 | background-color: var(--primary-color-5);
25 | color: var(--secondary-color-1);
26 | }
27 |
28 | .button[data-style="secondary"]:hover {
29 | background-color: var(--primary-color-4);
30 | }
31 |
32 | .button[data-style="ghost"] {
33 | background-color: transparent;
34 | color: var(--secondary-color-4);
35 | }
36 |
37 | .button[data-style="ghost"]:hover {
38 | background-color: var(--primary-color-5);
39 | color: var(--secondary-color-1);
40 | }
41 |
42 | .button[data-style="outline"] {
43 | border: 1px solid var(--primary-color-6);
44 | background-color: var(--light, var(--primary-color))
45 | var(--dark, var(--primary-color-3));
46 | color: var(--secondary-color-4);
47 | }
48 |
49 | .button[data-style="outline"]:hover {
50 | background-color: var(--primary-color-4);
51 | }
52 |
53 | .button[data-style="destructive"] {
54 | background-color: var(--primary-error-color);
55 | color: var(--contrast-error-color);
56 | }
57 |
58 | .button[data-style="destructive"]:hover {
59 | background-color: var(--secondary-error-color);
60 | }
61 |
--------------------------------------------------------------------------------
/preview/src/components/popover/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::components::button::component::Button;
2 |
3 | use super::super::component::*;
4 | use dioxus::prelude::*;
5 |
6 | #[component]
7 | pub fn Demo() -> Element {
8 | let mut open = use_signal(|| false);
9 | let mut confirmed = use_signal(|| false);
10 |
11 | rsx! {
12 | PopoverRoot { open: open(), on_open_change: move |v| open.set(v),
13 | PopoverTrigger { "Show Popover" }
14 | PopoverContent { gap: "0.25rem",
15 | h3 {
16 | padding_top: "0.25rem",
17 | padding_bottom: "0.25rem",
18 | width: "100%",
19 | text_align: "center",
20 | margin: 0,
21 | "Delete Item?"
22 | }
23 | Button {
24 | r#type: "button",
25 | "data-style": "outline",
26 | onclick: move |_| {
27 | open.set(false);
28 | confirmed.set(true);
29 | },
30 | "Confirm"
31 | }
32 | Button {
33 | r#type: "button",
34 | "data-style": "outline",
35 | onclick: move |_| {
36 | open.set(false);
37 | },
38 | "Cancel"
39 | }
40 | }
41 | }
42 | if confirmed() {
43 | p { style: "color: var(--contrast-error-color); margin-top: 16px; font-weight: 600;",
44 | "Item deleted!"
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/preview/src/components/slider/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::slider::{
3 | self, SliderProps, SliderRangeProps, SliderThumbProps, SliderTrackProps,
4 | };
5 |
6 | #[component]
7 | pub fn Slider(props: SliderProps) -> Element {
8 | rsx! {
9 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
10 | slider::Slider {
11 | class: "slider",
12 | value: props.value,
13 | default_value: props.default_value,
14 | min: props.min,
15 | max: props.max,
16 | step: props.step,
17 | disabled: props.disabled,
18 | horizontal: props.horizontal,
19 | inverted: props.inverted,
20 | on_value_change: props.on_value_change,
21 | label: props.label,
22 | attributes: props.attributes,
23 | {props.children}
24 | }
25 | }
26 | }
27 |
28 | #[component]
29 | pub fn SliderTrack(props: SliderTrackProps) -> Element {
30 | rsx! {
31 | slider::SliderTrack { class: "slider-track", attributes: props.attributes, {props.children} }
32 | }
33 | }
34 |
35 | #[component]
36 | pub fn SliderRange(props: SliderRangeProps) -> Element {
37 | rsx! {
38 | slider::SliderRange { class: "slider-range", attributes: props.attributes, {props.children} }
39 | }
40 | }
41 |
42 | #[component]
43 | pub fn SliderThumb(props: SliderThumbProps) -> Element {
44 | rsx! {
45 | slider::SliderThumb {
46 | class: "slider-thumb",
47 | index: props.index,
48 | attributes: props.attributes,
49 | {props.children}
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/primitives/src/aspect_ratio.rs:
--------------------------------------------------------------------------------
1 | //! Defines the [`AspectRatio`] component, which maintains a specific aspect ratio for its children.
2 |
3 | use dioxus::prelude::*;
4 |
5 | /// The props for the [`AspectRatio`] component.
6 | #[derive(Props, Clone, PartialEq)]
7 | pub struct AspectRatioProps {
8 | /// The desired ratio. E.g. 16.0 / 9.0
9 | #[props(default = 1.0)]
10 | pub ratio: f64,
11 |
12 | /// Additional attributes to apply to the aspect ratio container.
13 | #[props(extends = GlobalAttributes)]
14 | pub attributes: Vec,
15 |
16 | /// The children to render inside the aspect ratio container.
17 | pub children: Element,
18 | }
19 |
20 | /// # AspectRatio
21 | ///
22 | /// A component that maintains a specific aspect ratio for its children.
23 | ///
24 | /// ## Example
25 | ///
26 | /// ```rust
27 | /// use dioxus::prelude::*;
28 | /// use dioxus_primitives::aspect_ratio::AspectRatio;
29 | /// fn App() -> Element {
30 | /// rsx! {
31 | /// AspectRatio { ratio: 16.0 / 9.0,
32 | /// div { style: "background-color: lightblue; width: 100%; height: 100%;",
33 | /// "This div maintains a 16:9 aspect ratio."
34 | /// }
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | #[component]
40 | pub fn AspectRatio(props: AspectRatioProps) -> Element {
41 | let ratio = 100.0 / (props.ratio);
42 |
43 | rsx! {
44 | div {
45 | style: "position: relative; width: 100%; padding-bottom: {ratio}%;",
46 | div {
47 | style: "position: absolute; inset: 0;",
48 | ..props.attributes,
49 |
50 | {props.children}
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/preview/src/components/toolbar/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::toolbar::{self, ToolbarButtonProps, ToolbarProps, ToolbarSeparatorProps};
3 |
4 | #[component]
5 | pub fn Toolbar(props: ToolbarProps) -> Element {
6 | rsx! {
7 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
8 | toolbar::Toolbar {
9 | class: "toolbar",
10 | aria_label: props.aria_label,
11 | disabled: props.disabled,
12 | horizontal: props.horizontal,
13 | attributes: props.attributes,
14 | {props.children}
15 | }
16 | }
17 | }
18 |
19 | #[component]
20 | pub fn ToolbarButton(props: ToolbarButtonProps) -> Element {
21 | rsx! {
22 | toolbar::ToolbarButton {
23 | class: "toolbar-button",
24 | index: props.index,
25 | disabled: props.disabled,
26 | on_click: props.on_click,
27 | attributes: props.attributes,
28 | {props.children}
29 | }
30 | }
31 | }
32 |
33 | #[component]
34 | pub fn ToolbarSeparator(props: ToolbarSeparatorProps) -> Element {
35 | rsx! {
36 | toolbar::ToolbarSeparator {
37 | class: "toolbar-separator",
38 | decorative: props.decorative,
39 | horizontal: props.horizontal,
40 | attributes: props.attributes,
41 | }
42 | }
43 | }
44 |
45 | #[component]
46 | pub fn ToolbarGroup(
47 | #[props(extends = GlobalAttributes)]
48 | #[props(extends = div)]
49 | attributes: Vec,
50 | children: Element,
51 | ) -> Element {
52 | rsx! {
53 | div { class: "toolbar-group", ..attributes, {children} }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/preview/src/components/toggle_group/style.css:
--------------------------------------------------------------------------------
1 | .toggle-group {
2 | width: fit-content;
3 | }
4 |
5 | .toggle-item {
6 | min-width: 35px;
7 | padding: 10px;
8 | border: none;
9 | border-radius: 0;
10 | background-color: transparent;
11 | color: var(--secondary-color-4);
12 | font-size: 14px;
13 | outline: none;
14 | transition: background-color 200ms ease, border 200ms ease;
15 | }
16 |
17 | .toggle-group[data-allow-multiple-pressed="true"]
18 | .toggle-item {
19 | border-top: 1px solid var(--primary-color-6);
20 | border-right: 1px solid var(--primary-color-6);
21 | border-bottom: 1px solid var(--primary-color-6);
22 | }
23 |
24 | .toggle-item:hover,
25 | .toggle-item:focus-visible {
26 | background-color: var(--primary-color-4);
27 | cursor: pointer;
28 | }
29 |
30 | .toggle-item[data-state="on"] {
31 | background-color: var(--primary-color-7);
32 | color: var(--secondary-color-1);
33 | }
34 |
35 | .toggle-group[data-allow-multiple-pressed="true"]
36 | .toggle-item[data-state="on"] {
37 | border-top: 1px solid var(--secondary-color-6);
38 | border-right: 1px solid var(--secondary-color-6);
39 | border-bottom: 1px solid var(--secondary-color-6);
40 | }
41 |
42 | .toggle-group[data-allow-multiple-pressed="true"]
43 | .toggle-item:first-child[data-state="on"] {
44 | border: 1px solid var(--secondary-color-6);
45 | }
46 |
47 | .toggle-item:first-child {
48 | border-bottom-left-radius: 0.5rem;
49 | border-top-left-radius: 0.5rem;
50 | }
51 |
52 | .toggle-group[data-allow-multiple-pressed="true"] .toggle-item:first-child {
53 | border: 1px solid var(--primary-color-6);
54 | }
55 |
56 | .toggle-item:last-child {
57 | border-bottom-right-radius: 0.5rem;
58 | border-top-right-radius: 0.5rem;
59 | }
60 |
--------------------------------------------------------------------------------
/primitives/README.md:
--------------------------------------------------------------------------------
1 |
2 |
🎲 Dioxus Primitives 🧱
3 |
Accessible, unstyled, foundational components for Dioxus.
4 |
5 |
6 |
23 |
24 | ---
25 |
26 |
27 |
28 | Dioxus primitives is an ARIA-accessible, unstyled, foundational component library for Dioxus based on Radix Primitives. It defines the structure and behavior of components without imposing any specific styles. You can bring your own design patterns and styles to create a consistent look and feel for your application.
29 |
30 | If you are looking for a starting point to develop a styled component library for your application, you can use our shadcn-style [dioxus components](https://dioxuslabs.github.io/components) or a community library.
31 |
32 | ## License
33 |
34 | This project is dual licensed under the [MIT](./LICENSE-MIT) and [Apache 2.0](./LICENSE-APACHE) licenses.
35 |
36 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this repository, by you, shall be licensed as MIT or Apache 2.0, without any additional terms or conditions.
37 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/docs.md:
--------------------------------------------------------------------------------
1 | The Calendar component is used to display a calendar interface, allowing users to select dates. It provides a grid layout of days for a specific month and year.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | Calendar {
7 | // The currently selected date in the calendar (if any).
8 | selected_date,
9 | on_date_change: |date: Option| {
10 | // This callback is triggered when a date is selected in the calendar.
11 | // The date parameter contains the selected date.
12 | },
13 | // The current view date of the calendar, which determines the month and year displayed.
14 | view_date,
15 | on_view_change: |date: CalendarDate| {
16 | // This callback is triggered when the view date changes.
17 | // The date parameter contains the new view date.
18 | },
19 | // The calendar view will get rendered once for each month in the multi-month view.
20 | CalendarView {
21 | // The calendar header should contain the navigation controls and the title for the calendar.
22 | CalendarHeader {
23 | // The calendar navigation handles switching between months and years within the calendar view.
24 | CalendarNavigation {
25 | // The previous month button allows users to navigate to the previous month.
26 | PreviousMonthButton {}
27 | // The title displays the current month and year of the calendar view.
28 | CalendarTitle {}
29 | // The next month button allows users to navigate to the next month.
30 | NextMonthButton {}
31 | }
32 | }
33 | // The calendar grid displays the days of the month in a grid layout.
34 | CalendarGrid {}
35 | }
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/internationalized/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use dioxus_i18n::tid;
4 |
5 | use time::{macros::date, Date, Month, UtcDateTime, Weekday};
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_date = use_signal(|| None::);
10 | let mut view_date = use_signal(|| UtcDateTime::now().date());
11 | rsx! {
12 | div { class: "calendar-example", style: "padding: 20px;",
13 | Calendar {
14 | selected_date: selected_date(),
15 | on_date_change: move |date| {
16 | tracing::info!("Selected date: {:?}", date);
17 | selected_date.set(date);
18 | },
19 | view_date: view_date(),
20 | on_view_change: move |new_view: Date| {
21 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
22 | view_date.set(new_view);
23 | },
24 | on_format_weekday: |weekday: Weekday| tid!(& weekday.to_string()),
25 | on_format_month: |month: Month| tid!(& month.to_string()),
26 | min_date: date!(1995 - 07 - 21),
27 | max_date: date!(2035 - 09 - 11),
28 | CalendarView {
29 | CalendarHeader {
30 | CalendarNavigation {
31 | CalendarPreviousMonthButton {}
32 | CalendarSelectMonth {}
33 | CalendarSelectYear {}
34 | CalendarNextMonthButton {}
35 | }
36 | }
37 | CalendarGrid {}
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/preview/src/components/context_menu/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | #[component]
4 | pub fn Demo() -> Element {
5 | let mut selected_item = use_signal(|| None);
6 |
7 | rsx! {
8 | ContextMenu {
9 | ContextMenuTrigger { "right click here" }
10 | ContextMenuContent {
11 | ContextMenuItem {
12 | value: "edit".to_string(),
13 | index: 0usize,
14 | on_select: move |value| {
15 | selected_item.set(Some(value));
16 | },
17 | "Edit"
18 | }
19 | ContextMenuItem {
20 | value: "undo".to_string(),
21 | index: 1usize,
22 | disabled: true,
23 | on_select: move |value| {
24 | selected_item.set(Some(value));
25 | },
26 | "Undo"
27 | }
28 | ContextMenuItem {
29 | value: "duplicate".to_string(),
30 | index: 2usize,
31 | on_select: move |value| {
32 | selected_item.set(Some(value));
33 | },
34 | "Duplicate"
35 | }
36 | ContextMenuItem {
37 | value: "delete".to_string(),
38 | index: 3usize,
39 | on_select: move |value| {
40 | selected_item.set(Some(value));
41 | },
42 | "Delete"
43 | }
44 | }
45 | }
46 |
47 | if let Some(item) = selected_item() {
48 | "Selected: {item}"
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/preview/src/components/sheet/docs.md:
--------------------------------------------------------------------------------
1 | The sheet component is a panel that slides in from the edge of the screen. It can be used to display additional content, forms, or navigation menus without leaving the current page.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // The sheet component must wrap all sheet elements.
7 | Sheet {
8 | // The open prop determines if the sheet is currently open or closed.
9 | open: open(),
10 | // SheetContent wraps the content and defines the side from which the sheet slides in.
11 | // Available sides: Top, Right (default), Bottom, Left.
12 | SheetContent {
13 | side: SheetSide::Right,
14 | // SheetHeader groups the title and description at the top.
15 | SheetHeader {
16 | // The sheet title defines the heading of the sheet.
17 | SheetTitle {
18 | "Edit Profile"
19 | }
20 | // The sheet description provides additional information about the sheet.
21 | SheetDescription {
22 | "Make changes to your profile here."
23 | }
24 | }
25 | // Add your main content here.
26 | // SheetFooter groups actions at the bottom.
27 | SheetFooter {
28 | // SheetClose can be used to close the sheet.
29 | SheetClose {
30 | "Close"
31 | }
32 | }
33 | }
34 | }
35 | ```
36 |
37 | ## SheetClose with `as` prop
38 |
39 | The `as` prop allows you to render a custom element while preserving the close behavior, similar to shadcn/ui's `asChild` pattern.
40 |
41 | ```rust
42 | // Default: renders as
43 | SheetClose { "Close" }
44 |
45 | // Custom element: attributes include the preset onclick handler
46 | SheetClose {
47 | r#as: |attributes| rsx! {
48 | a { href: "#", ..attributes, "Go back" }
49 | }
50 | }
51 | ```
52 |
--------------------------------------------------------------------------------
/preview/src/components/button/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 |
3 | #[derive(Copy, Clone, PartialEq, Default)]
4 | #[non_exhaustive]
5 | pub enum ButtonVariant {
6 | #[default]
7 | Primary,
8 | Secondary,
9 | Destructive,
10 | Outline,
11 | Ghost,
12 | }
13 |
14 | impl ButtonVariant {
15 | pub fn class(&self) -> &'static str {
16 | match self {
17 | ButtonVariant::Primary => "primary",
18 | ButtonVariant::Secondary => "secondary",
19 | ButtonVariant::Destructive => "destructive",
20 | ButtonVariant::Outline => "outline",
21 | ButtonVariant::Ghost => "ghost",
22 | }
23 | }
24 | }
25 |
26 | #[component]
27 | pub fn Button(
28 | #[props(default)] variant: ButtonVariant,
29 | #[props(extends=GlobalAttributes)]
30 | #[props(extends=button)]
31 | attributes: Vec,
32 | onclick: Option>,
33 | onmousedown: Option>,
34 | onmouseup: Option>,
35 | children: Element,
36 | ) -> Element {
37 | rsx! {
38 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
39 |
40 | button {
41 | class: "button",
42 | "data-style": variant.class(),
43 | onclick: move |event| {
44 | if let Some(f) = &onclick {
45 | f.call(event);
46 | }
47 | },
48 | onmousedown: move |event| {
49 | if let Some(f) = &onmousedown {
50 | f.call(event);
51 | }
52 | },
53 | onmouseup: move |event| {
54 | if let Some(f) = &onmouseup {
55 | f.call(event);
56 | }
57 | },
58 | ..attributes,
59 | {children}
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/preview/src/components/select/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use strum::{EnumCount, IntoEnumIterator};
4 |
5 | #[derive(Debug, Clone, Copy, PartialEq, strum::EnumCount, strum::EnumIter, strum::Display)]
6 | enum Fruit {
7 | Apple,
8 | Banana,
9 | Orange,
10 | Strawberry,
11 | Watermelon,
12 | }
13 |
14 | impl Fruit {
15 | const fn emoji(&self) -> &'static str {
16 | match self {
17 | Fruit::Apple => "🍎",
18 | Fruit::Banana => "🍌",
19 | Fruit::Orange => "🍊",
20 | Fruit::Strawberry => "🍓",
21 | Fruit::Watermelon => "🍉",
22 | }
23 | }
24 | }
25 |
26 | #[component]
27 | pub fn Demo() -> Element {
28 | let fruits = Fruit::iter().enumerate().map(|(i, f)| {
29 | rsx! {
30 | SelectOption::> { index: i, value: f, text_value: "{f}",
31 | {format!("{} {f}", f.emoji())}
32 | SelectItemIndicator {}
33 | }
34 | }
35 | });
36 |
37 | rsx! {
38 |
39 | Select:: > { placeholder: "Select a fruit...",
40 | SelectTrigger { aria_label: "Select Trigger", width: "12rem", SelectValue {} }
41 | SelectList { aria_label: "Select Demo",
42 | SelectGroup {
43 | SelectGroupLabel { "Fruits" }
44 | {fruits}
45 | }
46 | SelectGroup {
47 | SelectGroupLabel { "Other" }
48 | SelectOption:: > {
49 | index: Fruit::COUNT,
50 | value: None,
51 | text_value: "Other",
52 | "Other"
53 | SelectItemIndicator {}
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/preview/src/components/tabs/variants/main/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 |
4 | #[component]
5 | pub fn Demo() -> Element {
6 | rsx! {
7 | Tabs {
8 | default_value: "tab1".to_string(),
9 | horizontal: true,
10 | max_width: "16rem",
11 | TabList {
12 | TabTrigger { value: "tab1".to_string(), index: 0usize, "Tab 1" }
13 | TabTrigger { value: "tab2".to_string(), index: 1usize, "Tab 2" }
14 | TabTrigger { value: "tab3".to_string(), index: 2usize, "Tab 3" }
15 | }
16 | TabContent { index: 0usize, value: "tab1".to_string(),
17 | div {
18 | width: "100%",
19 | height: "5rem",
20 | display: "flex",
21 | align_items: "center",
22 | justify_content: "center",
23 | "Tab 1 Content"
24 | }
25 | }
26 | TabContent {
27 | index: 1usize,
28 | class: "tabs-content",
29 | value: "tab2".to_string(),
30 | div {
31 | width: "100%",
32 | height: "5rem",
33 | display: "flex",
34 | align_items: "center",
35 | justify_content: "center",
36 | "Tab 2 Content"
37 | }
38 | }
39 | TabContent { index: 2usize, value: "tab3".to_string(),
40 | div {
41 | width: "100%",
42 | height: "5rem",
43 | display: "flex",
44 | align_items: "center",
45 | justify_content: "center",
46 | "Tab 3 Content"
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/preview/src/components/slider/style.css:
--------------------------------------------------------------------------------
1 | .slider {
2 | position: relative;
3 | display: flex;
4 | width: 200px;
5 | align-items: center;
6 | padding: 0.5rem 0;
7 | touch-action: none;
8 | }
9 |
10 | .slider[data-orientation="vertical"] {
11 | width: auto;
12 | height: 200px;
13 | flex-direction: column;
14 | }
15 |
16 | .slider-track {
17 | position: relative;
18 | height: 0.5rem;
19 | box-sizing: border-box;
20 | flex-grow: 1;
21 | border-radius: 9999px;
22 | background: var(--primary-color-5);
23 | }
24 |
25 | .slider[data-orientation="vertical"] .slider-track {
26 | width: 4px;
27 | height: 100%;
28 | }
29 |
30 | .slider-range {
31 | position: absolute;
32 | height: 100%;
33 | border-radius: 9999px;
34 | background-color: var(--secondary-color-2);
35 | }
36 |
37 | .slider[data-orientation="vertical"] .slider-range {
38 | width: 100%;
39 | }
40 |
41 | .slider-thumb {
42 | all: unset;
43 | position: absolute;
44 | top: 50%;
45 | display: block;
46 | width: 16px;
47 | height: 16px;
48 | border: 1px solid var(--secondary-color-2);
49 | border-radius: 50%;
50 | background-color: var(--primary-color-1);
51 | cursor: pointer;
52 | transform: translate(-50%, -50%);
53 | transition: border-color 150ms;
54 | }
55 |
56 | .slider[data-orientation="vertical"] .slider-thumb {
57 | left: 50%;
58 | transform: translate(-50%, 50%);
59 | }
60 |
61 | .slider-thumb:focus-visible[data-dragging="true"],
62 | .slider-thumb:focus-visible,
63 | .slider-thumb:hover {
64 | box-shadow: 0 0 0 4px
65 | color-mix(in oklab, var(--primary-color-7) 50%, transparent);
66 | transition: box-shadow 150ms;
67 | }
68 |
69 | .slider[data-disabled="true"] {
70 | cursor: not-allowed;
71 | opacity: 0.5;
72 | }
73 |
74 | .slider[data-disabled="true"] .slider-thumb {
75 | cursor: not-allowed;
76 | }
77 |
--------------------------------------------------------------------------------
/preview/src/components/context_menu/style.css:
--------------------------------------------------------------------------------
1 | .context-menu-content {
2 | z-index: 1000;
3 | min-width: 220px;
4 | padding: 0.25rem;
5 | border-radius: 0.5rem;
6 | background: var(--dark, var(--primary-color-5))
7 | var(--light, var(--primary-color));
8 | box-shadow: inset 0 0 0 1px var(--dark, var(--primary-color-7))
9 | var(--light, var(--primary-color-6));
10 | opacity: 0;
11 | pointer-events: none;
12 | will-change: transform, opacity;
13 | }
14 |
15 | .context-menu-content[data-state="closed"] {
16 | animation: context-menu-animate-out 150ms ease-in forwards;
17 | }
18 |
19 | @keyframes context-menu-animate-out {
20 | 0% {
21 | opacity: 1;
22 | transform: scale(1) translateY(0);
23 | }
24 |
25 | 100% {
26 | opacity: 0;
27 | transform: scale(0.95) translateY(-2px);
28 | }
29 | }
30 |
31 | .context-menu-content[data-state="open"] {
32 | animation: context-menu-animate-in 150ms ease-out forwards;
33 | }
34 |
35 | @keyframes context-menu-animate-in {
36 | 0% {
37 | opacity: 0;
38 | transform: scale(0.95) translateY(-2px);
39 | }
40 |
41 | 100% {
42 | opacity: 1;
43 | transform: scale(1) translateY(0);
44 | }
45 | }
46 |
47 | .context-menu-item {
48 | display: flex;
49 | align-items: center;
50 | padding: 8px 12px;
51 | border-radius: calc(0.5rem - 0.25rem);
52 | color: var(--secondary-color-4);
53 | cursor: pointer;
54 | font-size: 14px;
55 | outline: none;
56 | transition: background-color 100ms ease-out;
57 | user-select: none;
58 | }
59 |
60 | .context-menu-item[data-disabled="true"] {
61 | color: var(--secondary-color-5);
62 | cursor: not-allowed;
63 | }
64 |
65 | .context-menu-item:hover:not([data-disabled="true"]),
66 | .context-menu-item:focus-visible {
67 | background: var(--light, var(--primary-color-4))
68 | var(--dark, var(--primary-color-7));
69 | color: var(--light, var(--secondary-color-1))
70 | var(--dark, var(--secondary-color-4));
71 | }
72 |
--------------------------------------------------------------------------------
/preview/src/components/dropdown_menu/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::dropdown_menu::{
3 | self, DropdownMenuContentProps, DropdownMenuItemProps, DropdownMenuProps,
4 | DropdownMenuTriggerProps,
5 | };
6 |
7 | #[component]
8 | pub fn DropdownMenu(props: DropdownMenuProps) -> Element {
9 | rsx! {
10 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
11 | dropdown_menu::DropdownMenu {
12 | class: "dropdown-menu",
13 | open: props.open,
14 | default_open: props.default_open,
15 | on_open_change: props.on_open_change,
16 | disabled: props.disabled,
17 | roving_loop: props.roving_loop,
18 | attributes: props.attributes,
19 | {props.children}
20 | }
21 | }
22 | }
23 |
24 | #[component]
25 | pub fn DropdownMenuTrigger(props: DropdownMenuTriggerProps) -> Element {
26 | rsx! {
27 | dropdown_menu::DropdownMenuTrigger { class: "dropdown-menu-trigger", attributes: props.attributes, {props.children} }
28 | }
29 | }
30 |
31 | #[component]
32 | pub fn DropdownMenuContent(props: DropdownMenuContentProps) -> Element {
33 | rsx! {
34 | dropdown_menu::DropdownMenuContent {
35 | class: "dropdown-menu-content",
36 | id: props.id,
37 | attributes: props.attributes,
38 | {props.children}
39 | }
40 | }
41 | }
42 |
43 | #[component]
44 | pub fn DropdownMenuItem(
45 | props: DropdownMenuItemProps,
46 | ) -> Element {
47 | rsx! {
48 | dropdown_menu::DropdownMenuItem {
49 | class: "dropdown-menu-item",
50 | disabled: props.disabled,
51 | value: props.value,
52 | index: props.index,
53 | on_select: props.on_select,
54 | attributes: props.attributes,
55 | {props.children}
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/preview/src/components/avatar/style.css:
--------------------------------------------------------------------------------
1 | .avatar-item {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | gap: 0.5rem;
6 | }
7 |
8 | .avatar-label {
9 | margin: 0;
10 | color: var(--secondary-color-4);
11 | font-size: 0.875rem;
12 | }
13 |
14 | /* Avatar Component Styles */
15 | .avatar {
16 | position: relative;
17 | display: inline-flex;
18 | overflow: hidden;
19 | width: 64px;
20 | height: 64px;
21 | flex-shrink: 0;
22 | align-items: center;
23 | justify-content: center;
24 | border-radius: 3.40282e+38px;
25 | color: var(--secondary-color-4);
26 | cursor: pointer;
27 | font-weight: 500;
28 | }
29 |
30 | .avatar-image {
31 | width: 100%;
32 | height: 100%;
33 | aspect-ratio: 1;
34 | }
35 |
36 | /* Avatar sizes */
37 | .avatar-sm {
38 | width: 2rem;
39 | height: 2rem;
40 | font-size: 0.875rem;
41 | }
42 |
43 | .avatar-md {
44 | width: 3rem;
45 | height: 3rem;
46 | font-size: 1.25rem;
47 | }
48 |
49 | .avatar-lg {
50 | width: 4rem;
51 | height: 4rem;
52 | font-size: 1.75rem;
53 | }
54 |
55 | /* State-specific styles */
56 | .avatar[data-state="loading"] {
57 | animation: pulse 1.5s infinite ease-in-out;
58 | }
59 |
60 | .avatar[data-state="empty"] {
61 | background: var(--primary-color-2);
62 | }
63 |
64 | @keyframes pulse {
65 | 0% {
66 | opacity: 1;
67 | }
68 |
69 | 50% {
70 | opacity: 0.7;
71 | }
72 |
73 | 100% {
74 | opacity: 1;
75 | }
76 | }
77 |
78 | .avatar-fallback {
79 | display: flex;
80 | width: 100%;
81 | height: 100%;
82 | align-items: center;
83 | justify-content: center;
84 | background: var(--primary-color);
85 | color: var(--secondary-color-4);
86 | font-size: 1.5rem;
87 | }
88 |
89 | .avatar[data-state="error"] .avatar-fallback {
90 | background: var(--primary-color-3);
91 | color: var(--secondary-color-4);
92 | }
93 |
--------------------------------------------------------------------------------
/preview/src/components/menubar/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::menubar::{
3 | self, MenubarContentProps, MenubarItemProps, MenubarMenuProps, MenubarProps,
4 | MenubarTriggerProps,
5 | };
6 |
7 | #[component]
8 | pub fn Menubar(props: MenubarProps) -> Element {
9 | rsx! {
10 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
11 | menubar::Menubar {
12 | class: "menubar",
13 | disabled: props.disabled,
14 | roving_loop: props.roving_loop,
15 | attributes: props.attributes,
16 | {props.children}
17 | }
18 | }
19 | }
20 |
21 | #[component]
22 | pub fn MenubarMenu(props: MenubarMenuProps) -> Element {
23 | rsx! {
24 | menubar::MenubarMenu {
25 | class: "menubar-menu",
26 | index: props.index,
27 | disabled: props.disabled,
28 | attributes: props.attributes,
29 | {props.children}
30 | }
31 | }
32 | }
33 |
34 | #[component]
35 | pub fn MenubarTrigger(props: MenubarTriggerProps) -> Element {
36 | rsx! {
37 | menubar::MenubarTrigger { class: "menubar-trigger", attributes: props.attributes, {props.children} }
38 | }
39 | }
40 |
41 | #[component]
42 | pub fn MenubarContent(props: MenubarContentProps) -> Element {
43 | rsx! {
44 | menubar::MenubarContent {
45 | class: "menubar-content",
46 | id: props.id,
47 | attributes: props.attributes,
48 | {props.children}
49 | }
50 | }
51 | }
52 |
53 | #[component]
54 | pub fn MenubarItem(props: MenubarItemProps) -> Element {
55 | rsx! {
56 | menubar::MenubarItem {
57 | class: "menubar-item",
58 | index: props.index,
59 | value: props.value,
60 | disabled: props.disabled,
61 | on_select: props.on_select,
62 | attributes: props.attributes,
63 | {props.children}
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/preview/src/components/tabs/style.css:
--------------------------------------------------------------------------------
1 | .tabs {
2 | display: flex;
3 | width: 100%;
4 | flex-direction: column;
5 | gap: 0.5rem;
6 | }
7 |
8 | .tabs-list {
9 | display: flex;
10 | width: fit-content;
11 | box-sizing: border-box;
12 | flex: 1;
13 | flex-direction: row;
14 | padding: 0.25rem;
15 | border: none;
16 | border-radius: 0.5rem;
17 | gap: 0.25rem;
18 | }
19 |
20 | [data-variant="default"] .tabs-list {
21 | background: var(--light, var(--primary-color-3))
22 | var(--dark, var(--primary-color-5));
23 | }
24 |
25 | .tabs-trigger {
26 | padding: 4px 8px;
27 | border: none;
28 | border-radius: calc(0.5rem - 0.25rem);
29 | background: none;
30 | color: var(--secondary-color-5);
31 | cursor: pointer;
32 | }
33 |
34 | [data-variant="default"] .tabs-trigger[data-state="active"] {
35 | background-color: var(--light, var(--primary-color))
36 | var(--dark, var(--primary-color-6));
37 | box-shadow: var(--dark, inset 0 0 0 1px var(--primary-color-7))
38 | var(--light, 0 1px 2px rgb(0 0 0 / 18%));
39 | }
40 |
41 | .tabs-trigger[data-state="active"] {
42 | color: var(--secondary-color-1);
43 | }
44 |
45 | .tabs-trigger[data-disabled="true"] {
46 | color: var(--secondary-color-5);
47 | cursor: not-allowed;
48 | }
49 |
50 | .tabs-trigger:hover:not([data-disabled="true"]),
51 | .tabs-trigger:focus-visible {
52 | color: var(--secondary-color-3);
53 | }
54 |
55 | .tabs-content {
56 | width: 100%;
57 | box-sizing: border-box;
58 | padding: 0.25rem;
59 | }
60 |
61 | [data-variant="default"] .tabs-content-themed {
62 | border: 1px solid var(--light, var(--primary-color-6))
63 | var(--dark, var(--primary-color-7));
64 | border-radius: 0.5rem;
65 | background: var(--light, var(--primary-color))
66 | var(--dark, var(--primary-color-3));
67 | box-shadow: var(--light, 0 1px 2px rgb(0 0 0 / 18%)) var(--dark, none);
68 | }
69 |
70 | .tabs-content[data-state="inactive"] {
71 | display: none;
72 | }
73 |
--------------------------------------------------------------------------------
/preview/src/components/accordion/style.css:
--------------------------------------------------------------------------------
1 | .accordion-trigger {
2 | display: flex;
3 | width: 100%;
4 | box-sizing: border-box;
5 | flex-direction: row;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 0;
9 | padding-top: 1rem;
10 | padding-bottom: 1rem;
11 | border: none;
12 | background-color: transparent;
13 | color: var(--secondary-color-4);
14 | outline: none;
15 | text-align: left;
16 | }
17 |
18 | .accordion-trigger:focus-visible {
19 | border: none;
20 | box-shadow: inset 0 0 0 2px var(--focused-border-color);
21 | }
22 |
23 | .accordion-trigger:hover {
24 | cursor: pointer;
25 | text-decoration-line: underline;
26 | }
27 |
28 | .accordion-content {
29 | display: grid;
30 | height: 0;
31 | }
32 |
33 | .accordion-content[data-open="false"] {
34 | animation: accordion-slide-down 300ms cubic-bezier(0.87, 0, 0.13, 1) forwards;
35 | }
36 |
37 | .accordion-content[data-open="true"] {
38 | animation: accordion-slide-up 300ms cubic-bezier(0.87, 0, 0.13, 1) forwards;
39 | }
40 |
41 | @keyframes accordion-slide-down {
42 | from {
43 | height: var(--collapsible-content-width);
44 | }
45 |
46 | to {
47 | height: 0;
48 | }
49 | }
50 |
51 | @keyframes accordion-slide-up {
52 | from {
53 | height: 0;
54 | }
55 |
56 | to {
57 | height: var(--collapsible-content-width);
58 | }
59 | }
60 |
61 | .accordion-item {
62 | overflow: hidden;
63 | box-sizing: border-box;
64 | border-bottom: 1px solid var(--primary-color-6);
65 | margin-top: 1px;
66 | }
67 |
68 | .accordion-item:first-child {
69 | margin-top: 0;
70 | }
71 |
72 | .accordion-item:last-child {
73 | border-bottom: none;
74 | }
75 |
76 | .accordion-expand-icon {
77 | width: 20px;
78 | height: 20px;
79 | fill: none;
80 | stroke: var(--secondary-color-4);
81 | stroke-linecap: round;
82 | stroke-linejoin: round;
83 | stroke-width: 2;
84 | transition: rotate 150ms cubic-bezier(0.4, 0, 0.2, 1);
85 | }
86 |
87 | .accordion-item[data-open="true"] .accordion-expand-icon {
88 | rotate: 180deg;
89 | }
90 |
--------------------------------------------------------------------------------
/preview/src/components/date_picker/style.css:
--------------------------------------------------------------------------------
1 | .date-picker {
2 | position: relative;
3 | display: inline-flex;
4 | align-items: center;
5 | }
6 |
7 | .date-picker-group .popover-trigger {
8 | display: inline-flex;
9 | align-items: center;
10 | justify-content: center;
11 | padding: 0;
12 | border: none;
13 | background-color: transparent;
14 | cursor: pointer;
15 | transition: rotate 150ms cubic-bezier(0.4, 0, 0.2, 1);
16 | }
17 |
18 | .popover[data-state="open"] div .date-picker-trigger {
19 | rotate: 180deg;
20 | }
21 |
22 | .date-picker-expand-icon {
23 | width: 20px;
24 | height: 20px;
25 | fill: none;
26 | stroke: var(--primary-color-7);
27 | stroke-linecap: round;
28 | stroke-linejoin: round;
29 | stroke-width: 2;
30 | }
31 |
32 | [data-disabled="true"] {
33 | cursor: not-allowed;
34 | opacity: 0.5;
35 | }
36 |
37 | .date-picker-group {
38 | display: flex;
39 | width: fit-content;
40 | min-width: 150px;
41 | flex-direction: row;
42 | align-items: center;
43 | justify-content: space-between;
44 | padding: 0.5rem;
45 | border: none;
46 | border-radius: 0.5rem;
47 | background: none;
48 | background: var(--light, var(--primary-color))
49 | var(--dark, var(--primary-color-3));
50 | box-shadow: inset 0 0 0 1px var(--light, var(--primary-color-6))
51 | var(--dark, var(--primary-color-7));
52 | color: var(--secondary-color-4);
53 | gap: 0.25rem;
54 | transition: background-color 100ms ease-out;
55 | }
56 |
57 | .date-picker-group .popover-content {
58 | max-width: unset;
59 | padding: 0;
60 | }
61 |
62 | .date-segment {
63 | caret-color: transparent;
64 | }
65 |
66 | .date-segment[no-date="true"] {
67 | color: var(--secondary-color-5);
68 | }
69 |
70 | .date-segment[is-separator="true"] {
71 | padding: 0;
72 | }
73 |
74 | .date-segment:focus-visible {
75 | border-radius: 0.25rem;
76 | background: var(--secondary-color-3);
77 | color: var(--primary-color);
78 | outline: none;
79 | }
80 |
--------------------------------------------------------------------------------
/preview/src/components/alert_dialog/docs.md:
--------------------------------------------------------------------------------
1 | The AlertDialog primitive provides an accessible, composable modal dialog for critical user confirmations (such as destructive actions). It is unstyled by default except for minimal centering/stacking, and can be fully themed by the consumer.
2 |
3 | ## Component Structure
4 |
5 | ```rust
6 | // Usage example:
7 | let open = use_signal(|| false);
8 | rsx! {
9 | button {
10 | onclick: move |_| open.set(true),
11 | type: "button",
12 | "Show Alert Dialog"
13 | }
14 | AlertDialogRoot { open: Some(open), on_open_change: move |v| open.set(v),
15 | AlertDialogContent {
16 | // You may pass class/style for custom appearance
17 | AlertDialogTitle { "Title" }
18 | AlertDialogDescription { "Description" }
19 | AlertDialogActions {
20 | AlertDialogCancel { "Cancel" }
21 | AlertDialogAction { "Confirm" }
22 | }
23 | }
24 | }
25 | }
26 | ```
27 |
28 | ### Components
29 | - **AlertDialogRoot**: Provides context and manages open state.
30 | - **AlertDialogContent**: The dialog container. Handles accessibility and focus trap. Applies only minimal inline style for centering/stacking if no style is provided.
31 | - **AlertDialogTitle**: The dialog's heading.
32 | - **AlertDialogDescription**: Additional description for the dialog.
33 | - **AlertDialogActions**: Container for action buttons.
34 | - **AlertDialogAction**: Main action button (e.g., confirm/delete). Closes dialog and calls optional `on_click`.
35 | - **AlertDialogCancel**: Cancel/close button. Closes dialog and calls optional `on_click`.
36 |
37 | ### Notes
38 | - By default, only minimal centering/positioning styles are applied to `AlertDialogContent` (position, top, left, transform, z-index). All appearance is controlled by your CSS.
39 | - The dialog is accessible and closes on Escape, backdrop click, or Cancel/Action.
40 | - You can pass custom `on_click`, `class`, and `style` props to all subcomponents for full control.
41 | - Focus trap is not fully implemented; focus may escape the dialog in some cases.
42 |
--------------------------------------------------------------------------------
/preview/src/components/calendar/variants/unavailable_dates/mod.rs:
--------------------------------------------------------------------------------
1 | use super::super::component::*;
2 | use dioxus::prelude::*;
3 | use time::{ext::NumericalDuration, macros::date, Date, UtcDateTime};
4 |
5 | use dioxus_primitives::calendar::DateRange;
6 |
7 | #[component]
8 | pub fn Demo() -> Element {
9 | let mut selected_range = use_signal(|| None::);
10 |
11 | let now = UtcDateTime::now().date();
12 | let mut view_date = use_signal(|| now);
13 |
14 | let disabled_ranges = use_signal(|| {
15 | vec![
16 | DateRange::new(now, now.saturating_add(3.days())),
17 | DateRange::new(now.saturating_add(15.days()), now.saturating_add(18.days())),
18 | DateRange::new(now.saturating_add(22.days()), now.saturating_add(23.days())),
19 | ]
20 | });
21 |
22 | rsx! {
23 | div { class: "calendar-example", style: "padding: 20px;",
24 | RangeCalendar {
25 | selected_range: selected_range(),
26 | on_range_change: move |range| {
27 | tracing::info!("Selected range: {:?}", range);
28 | selected_range.set(range);
29 | },
30 | view_date: view_date(),
31 | on_view_change: move |new_view: Date| {
32 | tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
33 | view_date.set(new_view);
34 | },
35 | min_date: date!(1995 - 07 - 21),
36 | max_date: date!(2035 - 09 - 11),
37 | disabled_ranges: disabled_ranges(),
38 | CalendarView {
39 | CalendarHeader {
40 | CalendarNavigation {
41 | CalendarPreviousMonthButton {}
42 | CalendarSelectMonth {}
43 | CalendarSelectYear {}
44 | CalendarNextMonthButton {}
45 | }
46 | }
47 | CalendarGrid {}
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/preview/src/components/context_menu/component.rs:
--------------------------------------------------------------------------------
1 | use dioxus::prelude::*;
2 | use dioxus_primitives::context_menu::{
3 | self, ContextMenuContentProps, ContextMenuItemProps, ContextMenuProps, ContextMenuTriggerProps,
4 | };
5 |
6 | #[component]
7 | pub fn ContextMenu(props: ContextMenuProps) -> Element {
8 | rsx! {
9 | document::Link { rel: "stylesheet", href: asset!("./style.css") }
10 | context_menu::ContextMenu {
11 | disabled: props.disabled,
12 | open: props.open,
13 | default_open: props.default_open,
14 | on_open_change: props.on_open_change,
15 | roving_loop: props.roving_loop,
16 | attributes: props.attributes,
17 | {props.children}
18 | }
19 | }
20 | }
21 |
22 | #[component]
23 | pub fn ContextMenuTrigger(props: ContextMenuTriggerProps) -> Element {
24 | rsx! {
25 | context_menu::ContextMenuTrigger {
26 | padding: "20px",
27 | background: "var(--primary-color)",
28 | border: "1px dashed var(--primary-color-6)",
29 | border_radius: ".5rem",
30 | cursor: "context-menu",
31 | user_select: "none",
32 | text_align: "center",
33 | attributes: props.attributes,
34 | {props.children}
35 | }
36 | }
37 | }
38 |
39 | #[component]
40 | pub fn ContextMenuContent(props: ContextMenuContentProps) -> Element {
41 | rsx! {
42 | context_menu::ContextMenuContent {
43 | class: "context-menu-content",
44 | id: props.id,
45 | attributes: props.attributes,
46 | {props.children}
47 | }
48 | }
49 | }
50 |
51 | #[component]
52 | pub fn ContextMenuItem(props: ContextMenuItemProps) -> Element {
53 | rsx! {
54 | context_menu::ContextMenuItem {
55 | class: "context-menu-item",
56 | disabled: props.disabled,
57 | value: props.value,
58 | index: props.index,
59 | on_select: props.on_select,
60 | attributes: props.attributes,
61 | {props.children}
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------