/ libs / ui / src / components / label.rs
label.rs
  1  use crate::{use_id_or, use_unique_id};
  2  use dioxus::prelude::*;
  3  
  4  /// Label size options
  5  #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
  6  pub enum LabelSize {
  7      Small,
  8      #[default]
  9      Medium,
 10      Large,
 11  }
 12  
 13  #[derive(Props, Clone, PartialEq)]
 14  pub struct LabelProps {
 15      /// The HTML for attribute that associates the label with a form control
 16      #[props(default)]
 17      for_id: ReadSignal<Option<String>>,
 18  
 19      /// The size of the label
 20      #[props(default)]
 21      size: ReadSignal<LabelSize>,
 22  
 23      /// Whether the label is for a required field
 24      #[props(default)]
 25      required: ReadSignal<bool>,
 26  
 27      /// Optional ID for the label element
 28      #[props(default)]
 29      id: ReadSignal<Option<String>>,
 30  
 31      /// Optional additional classes for the label
 32      #[props(default)]
 33      class: Option<String>,
 34  
 35      /// Whether to display the label as disabled
 36      #[props(default)]
 37      disabled: ReadSignal<bool>,
 38  
 39      #[props(extends = GlobalAttributes)]
 40      attributes: Vec<Attribute>,
 41  
 42      children: Element,
 43  }
 44  
 45  #[component]
 46  pub fn Label(props: LabelProps) -> Element {
 47      // Generate unique ID if not provided
 48      let label_id = use_unique_id();
 49      let id_value = use_id_or(label_id, props.id);
 50  
 51      // Determine size classes
 52      let size_classes = match (props.size)() {
 53          LabelSize::Small => "text-xs",
 54          LabelSize::Medium => "text-sm",
 55          LabelSize::Large => "text-base",
 56      };
 57  
 58      // Determine state classes
 59      let state_class = if (props.disabled)() {
 60          "text-muted-foreground cursor-not-allowed opacity-70"
 61      } else {
 62          "text-foreground"
 63      };
 64  
 65      // Generate all the classes
 66      let label_classes = vec![
 67          // Base classes
 68          "font-medium mb-1.5 block",
 69          // Size-specific classes
 70          size_classes,
 71          // State class
 72          state_class,
 73          // Additional classes passed by the user
 74          props.class.as_deref().unwrap_or(""),
 75      ]
 76      .into_iter()
 77      .filter(|s| !s.is_empty())
 78      .collect::<Vec<_>>()
 79      .join(" ");
 80  
 81      rsx! {
 82          label {
 83              id: id_value,
 84              class: label_classes,
 85              for: (props.for_id)(),
 86  
 87              // Pass through other attributes
 88              ..props.attributes,
 89  
 90              // Label content
 91              {props.children}
 92  
 93              // Required indicator
 94              if (props.required)() {
 95                  span {
 96                      class: "ml-1 text-destructive",
 97                      aria_hidden: "true",
 98                      "*"
 99                  }
100              }
101          }
102      }
103  }