-
Notifications
You must be signed in to change notification settings - Fork 42
Badge component #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Badge component #178
Changes from all commits
316241e
f6edf7d
7c209e5
d23e70a
8bfd2e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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"] | ||
| } |
| 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} | ||
| } | ||
| } | ||
| } |
| 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} | ||
| } | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| mod component; | ||
| pub use component::*; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| .badge-example { | ||
| display: flex; | ||
| flex-direction: row; | ||
| align-items: center; | ||
| 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; | ||
| min-width: 20px; | ||
| height: 20px; | ||
| align-items: center; | ||
| justify-content: center; | ||
| border-radius: 10px; | ||
| background-color: var(--badge-color); | ||
| box-shadow: 0 0 0 1px var(--primary-color-2); | ||
| font-size: 12px; | ||
| transform: translate(-50%, -50%); | ||
| } | ||
|
|
||
| .badge[padding="true"] { | ||
| padding: 0 8px; | ||
| } | ||
|
|
||
| .badge[dot="true"] { | ||
| min-width: 8px; | ||
| height: 8px; | ||
| } |
| 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", | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| 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"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the default color should be defined in css as an existing or new css variable here: https://github.com/DioxusLabs/components/blob/main/preview/assets/dx-components-theme.css#L31 |
||
|
|
||
| /// The props for the [`Badge`] component. | ||
| #[derive(Props, Clone, PartialEq)] | ||
| pub struct BadgeProps { | ||
| /// Number to show in badge | ||
| pub count: u32, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While the current version is useful with notifications, I would like to generalize the component outside of that. Badges should also be usable standalone with whatever text the user passes in as a child. For example this github UI should be expressible as a badge component with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What name is recommended for the notification component then? If I understand correctly, we can't reuse the same component for two cases.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could re-use most of it. The only part that isn't applicable is the positioning, but that can be configured from the props. In shadcn, notifications can be just a specific application of badges. An example from https://shadcnstudio.com/docs/components/badge: import { ShoppingCartIcon } from 'lucide-react'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
const BadgeCartDemo = () => {
return (
<div className='relative w-fit'>
<Avatar className='size-9 rounded-sm'>
<AvatarFallback className='rounded-sm'>
<ShoppingCartIcon className='size-5' />
</AvatarFallback>
</Avatar>
<Badge className='absolute -top-2.5 -right-2.5 h-5 min-w-5 px-1 tabular-nums'>8</Badge>
</div>
)
}
export default BadgeCartDemo |
||
|
|
||
| /// Max count to show | ||
| #[props(default = u32::MAX)] | ||
| pub overflow_count: u32, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another notification specific prop |
||
|
|
||
| /// 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, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another notification specific prop |
||
|
|
||
| /// 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() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are other valid syntaxes for color strings in css. We don't need to parse it here and quote it with |
||
| 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} | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since badge doesn't have any special accessibility behavior (it is just a span), the component should be defined only in the component.rs in the preview instead of in the primitives