skill_web/components/
button.rs1use yew::prelude::*;
4
5#[derive(Clone, PartialEq, Default)]
7pub enum ButtonVariant {
8 #[default]
9 Primary,
10 Secondary,
11 Ghost,
12 Danger,
13}
14
15#[derive(Clone, PartialEq, Default)]
17pub enum ButtonSize {
18 Small,
19 #[default]
20 Medium,
21 Large,
22}
23
24#[derive(Properties, PartialEq)]
26pub struct ButtonProps {
27 #[prop_or_default]
28 pub children: Children,
29 #[prop_or_default]
30 pub variant: ButtonVariant,
31 #[prop_or_default]
32 pub size: ButtonSize,
33 #[prop_or_default]
34 pub class: Classes,
35 #[prop_or_default]
36 pub disabled: bool,
37 #[prop_or_default]
38 pub loading: bool,
39 #[prop_or_default]
40 pub onclick: Callback<MouseEvent>,
41 #[prop_or_default]
42 pub r#type: Option<AttrValue>,
43}
44
45#[function_component(Button)]
47pub fn button(props: &ButtonProps) -> Html {
48 let variant_class = match props.variant {
49 ButtonVariant::Primary => "btn-primary",
50 ButtonVariant::Secondary => "btn-secondary",
51 ButtonVariant::Ghost => "btn-ghost",
52 ButtonVariant::Danger => "btn-danger",
53 };
54
55 let size_class = match props.size {
56 ButtonSize::Small => "px-3 py-1.5 text-xs",
57 ButtonSize::Medium => "px-4 py-2 text-sm",
58 ButtonSize::Large => "px-6 py-3 text-base",
59 };
60
61 let button_type = props.r#type.clone().unwrap_or_else(|| "button".into());
62
63 html! {
64 <button
65 type={button_type}
66 class={classes!("btn", variant_class, size_class, props.class.clone())}
67 disabled={props.disabled || props.loading}
68 onclick={props.onclick.clone()}
69 >
70 if props.loading {
71 <svg class="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
72 <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
73 <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
74 </svg>
75 }
76 { for props.children.iter() }
77 </button>
78 }
79}
80
81#[derive(Properties, PartialEq)]
83pub struct IconButtonProps {
84 pub children: Children,
85 #[prop_or_default]
86 pub class: Classes,
87 #[prop_or_default]
88 pub disabled: bool,
89 #[prop_or_default]
90 pub onclick: Callback<MouseEvent>,
91 #[prop_or_default]
92 pub title: Option<AttrValue>,
93}
94
95#[function_component(IconButton)]
96pub fn icon_button(props: &IconButtonProps) -> Html {
97 html! {
98 <button
99 type="button"
100 class={classes!(
101 "p-2", "rounded-lg", "text-gray-500", "hover:text-gray-700", "hover:bg-gray-100",
102 "dark:text-gray-400", "dark:hover:text-gray-200", "dark:hover:bg-gray-700",
103 "transition-colors", "disabled:opacity-50", "disabled:cursor-not-allowed",
104 props.class.clone()
105 )}
106 disabled={props.disabled}
107 onclick={props.onclick.clone()}
108 title={props.title.clone()}
109 >
110 { for props.children.iter() }
111 </button>
112 }
113}