Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion component.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"preview/src/components/textarea",
"preview/src/components/skeleton",
"preview/src/components/card",
"preview/src/components/sheet"
"preview/src/components/sheet",
"preview/src/components/badge"
]
}
21 changes: 20 additions & 1 deletion preview/src/components/avatar/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ impl AvatarImageSize {
}
}

#[derive(Clone, Copy, PartialEq, Default)]
pub enum AvatarShape {
#[default]
Circle,
Rounded,
}

impl AvatarShape {
fn to_class(self) -> &'static str {
match self {
AvatarShape::Circle => "avatar-circle",
AvatarShape::Rounded => "avatar-rounded",
}
}
}

/// The props for the [`Avatar`] component.
#[derive(Props, Clone, PartialEq)]
pub struct AvatarProps {
Expand All @@ -37,6 +53,9 @@ pub struct AvatarProps {
#[props(default)]
pub size: AvatarImageSize,

#[props(default)]
pub shape: AvatarShape,

/// Additional attributes for the avatar element
#[props(extends = GlobalAttributes)]
pub attributes: Vec<Attribute>,
Expand All @@ -51,7 +70,7 @@ pub fn Avatar(props: AvatarProps) -> Element {
document::Link { rel: "stylesheet", href: asset!("./style.css") }

avatar::Avatar {
class: "avatar {props.size.to_class()}",
class: "avatar {props.size.to_class()} {props.shape.to_class()}",
on_load: props.on_load,
on_error: props.on_error,
on_state_change: props.on_state_change,
Expand Down
12 changes: 10 additions & 2 deletions preview/src/components/avatar/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
flex-shrink: 0;
align-items: center;
justify-content: center;
border-radius: 3.40282e+38px;
color: var(--secondary-color-4);
cursor: pointer;
font-weight: 500;
Expand Down Expand Up @@ -52,13 +51,22 @@
font-size: 1.75rem;
}

/* Avatar shape */
.avatar-circle {
border-radius: 50%;
}

.avatar-rounded {
border-radius: 8px;
}

/* State-specific styles */
.avatar[data-state="loading"] {
animation: pulse 1.5s infinite ease-in-out;
}

.avatar[data-state="empty"] {
background: var(--primary-color-2);
background: var(--primary-color-7);
}

@keyframes pulse {
Expand Down
13 changes: 13 additions & 0 deletions preview/src/components/badge/component.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "Badge",
"description": "Show notifications, counts or status information on its children",
"authors": ["Evan Almloff"],
"exclude": ["variants", "docs.md", "component.json"],
"cargoDependencies": [
{
"name": "dioxus-primitives",
"git": "https://github.com/DioxusLabs/components"
}
],
"globalAssets": ["../../../assets/dx-components-theme.css"]
}
19 changes: 19 additions & 0 deletions preview/src/components/badge/component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use dioxus::prelude::*;
use dioxus_primitives::badge::{self, BadgeProps};

#[component]
pub fn Badge(props: BadgeProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("./style.css") }

badge::Badge {
count: props.count,
overflow_count: props.overflow_count,
dot: props.dot,
show_zero: props.show_zero,
color: props.color,
attributes: props.attributes,
{props.children}
}
}
}
10 changes: 10 additions & 0 deletions preview/src/components/badge/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Badges are used as a small numerical value or status descriptor for its children elements.
Badge will be hidden when count is 0, but we can use show_zero to show it.

## Component Structure

```rust
Badge {
{children}
}
```
2 changes: 2 additions & 0 deletions preview/src/components/badge/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod component;
pub use component::*;
42 changes: 42 additions & 0 deletions preview/src/components/badge/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.badge-example {
display: flex;
flex-direction: row;
align-items: center;
justify-content: between;
gap: 1rem;
}

.badge-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}

.badge-label {
color: var(--secondary-color-4);
font-size: 0.875rem;
}

.badge {
position: absolute;
display: inline-flex;
justify-content: center;
align-items: center;
min-width: 20px;
height: 20px;
font-size: 12px;
background-color: var(--badge-color);
border-radius: 10px;
box-shadow: 0 0 0 1px var(--primary-color-2);
transform: translate(-50%, -50%);
}

.badge[padding="true"] {
padding: 0 8px;
}

.badge[dot="true"] {
min-width: 8px;
height: 8px;
}
86 changes: 86 additions & 0 deletions preview/src/components/badge/variants/main/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use dioxus::prelude::*;

use super::super::component::*;
use crate::components::avatar::*;

#[component]
pub fn Demo() -> Element {
rsx! {
div {
class: "badge-example",

div {
class: "badge-item",
p { class: "badge-label", "Basic" }
Badge {
count: 5,
Avatar {
size: AvatarImageSize::Medium,
shape: AvatarShape::Rounded,
aria_label: "Space item",
}
}
}

div {
class: "badge-item",
p { class: "badge-label", "Show Zero" }

Badge {
count: 0,
show_zero: true,
Avatar {
size: AvatarImageSize::Medium,
shape: AvatarShape::Rounded,
aria_label: "Space item",
}
}
}

div {
class: "badge-item",
p { class: "badge-label", "Overflow" }

Badge {
count: 100,
overflow_count: 99,
Avatar {
size: AvatarImageSize::Medium,
shape: AvatarShape::Rounded,
aria_label: "Space item",
}
}
}

div {
class: "badge-item",
p { class: "badge-label", "Colorful" }

Badge {
count: 7,
color: String::from("52c41a"),
Avatar {
size: AvatarImageSize::Medium,
shape: AvatarShape::Rounded,
aria_label: "Space item",
}
}
}

div {
class: "badge-item",
p { class: "badge-label", "As Dot" }

Badge {
count: 5,
dot: true,
Avatar {
size: AvatarImageSize::Medium,
shape: AvatarShape::Rounded,
aria_label: "Space item",
}
}
}
}
}
}
1 change: 1 addition & 0 deletions preview/src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ examples!(
alert_dialog,
aspect_ratio,
avatar,
badge,
button,
calendar[simple, internationalized, range, multi_month, unavailable_dates],
checkbox,
Expand Down
92 changes: 92 additions & 0 deletions primitives/src/badge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Defines the [`Badge`] component

use dioxus::prelude::*;

const DEF_COLOR: &str = "EB5160";

/// The props for the [`Badge`] component.
#[derive(Props, Clone, PartialEq)]
pub struct BadgeProps {
/// Number to show in badge
pub count: u32,

/// Max count to show
#[props(default = u32::MAX)]
pub overflow_count: u32,

/// Whether to display a dot instead of count
#[props(default = false)]
pub dot: bool,

/// Whether to show badge when count is zero
#[props(default = false)]
pub show_zero: bool,

/// Customize Badge color (as HEX)
#[props(default = String::from(DEF_COLOR))]
pub color: String,

/// Additional attributes to extend the badge element
#[props(extends = GlobalAttributes)]
pub attributes: Vec<Attribute>,

/// The children of the badge element
pub children: Element,
}

/// # Badge
///
/// The [`Badge`] component displays a small badge to the top-right of its child(ren).
///
/// ## Example
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_primitives::badge::Badge;
/// use dioxus_primitives::avatar::*;
/// #[component]
/// fn Demo() -> Element {
/// rsx! {
/// Badge {
/// count: 100,
/// overflow_count: 99,
/// Avatar {
/// aria_label: "Space item",
/// }
/// }
/// }
/// }
/// ```
#[component]
pub fn Badge(props: BadgeProps) -> Element {
let text = if props.dot {
String::default()
} else if props.overflow_count < props.count {
format!("{}+", props.overflow_count)
} else {
format!("{}", props.count)
};

let add_padding = text.chars().count() > 1;
let color = if u32::from_str_radix(&props.color, 16).is_ok() {
props.color
} else {
DEF_COLOR.to_string()
};

rsx! {
span {
{props.children}

if props.count > 0 || props.show_zero {
span {
class: "badge",
style: "--badge-color: #{color}",
"padding": if add_padding { true },
"dot": if props.dot { true },
..props.attributes,
{text}
}
}
}
}
}
Loading