Skip to content

Commit

Permalink
feat: Checkbox adds size prop
Browse files Browse the repository at this point in the history
  • Loading branch information
luoxiaozero committed Dec 11, 2024
1 parent cc21e25 commit 0233d7f
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
cursor: inherit;
}

.thaw-checkbox--large > .thaw-checkbox__input {
width: calc(20px + 2 * var(--spacingHorizontalS));
}

.thaw-checkbox__indicator {
align-self: flex-start;
flex-shrink: 0;
Expand All @@ -94,6 +98,12 @@
overflow: hidden;
}

.thaw-checkbox--large > .thaw-checkbox__indicator {
font-size: 16px;
height: 20px;
width: 20px;
}

.thaw-checkbox__label {
align-self: center;
margin-bottom: calc((16px - var(--lineHeightBase300)) / 2);
Expand All @@ -108,3 +118,8 @@
color: inherit;
cursor: inherit;
}

.thaw-checkbox--large > .thaw-checkbox__label {
margin-top: calc((20px - var(--lineHeightBase300)) / 2);
margin-bottom: calc((20px - var(--lineHeightBase300)) / 2);
}
124 changes: 124 additions & 0 deletions thaw/src/checkbox/checkbox/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
mod types;

pub use types::*;

use super::checkbox_group::CheckboxGroupInjection;
use crate::{Checkmark12FilledIcon, Checkmark16FilledIcon};
use leptos::{either::Either, html, prelude::*};
use thaw_utils::{class_list, mount_style, Model};

#[component]
pub fn Checkbox(
#[prop(optional, into)] class: MaybeProp<String>,
/// The controlled value for the checkbox.
#[prop(optional, into)]
checked: Model<bool>,
/// The value of the checkbox to be used in a checkbox group.us
#[prop(optional, into)]
value: Option<String>,
/// The Checkbox's label.
#[prop(optional, into)]
label: MaybeProp<String>,
/// The size of the checkbox indicator.
#[prop(optional, into)]
size: Signal<CheckboxSize>,
) -> impl IntoView {
mount_style("checkbox", include_str!("./checkbox.css"));

let id = uuid::Uuid::new_v4().to_string();
let input_ref = NodeRef::<html::Input>::new();
let group = CheckboxGroupInjection::use_context();
let item_value = StoredValue::new(value);

let group_checked = Memo::new(move |_| {
let Some(group) = group.as_ref() else {
return None;
};
group.value.with(|group_value| {
item_value.with_value(|value| {
let Some(value) = value else {
return None;
};
Some(group_value.contains(value))
})
})
});

let on_change = move |_| {
let input = input_ref.get_untracked().unwrap();
if group_checked.get_untracked().is_some() {
if input.checked() {
group.as_ref().unwrap().value.update(move |group_value| {
group_value.insert(item_value.get_value().unwrap());
});
} else {
group.as_ref().unwrap().value.update(move |group_value| {
item_value.with_value(|value| {
group_value.remove(value.as_ref().unwrap());
});
});
}
} else {
checked.set(input.checked())
}
};

let checked = move || group_checked.get().unwrap_or_else(|| checked.get());

view! {
<span class=class_list![
"thaw-checkbox",
("thaw-checkbox--checked", checked),
move || format!("thaw-checkbox--{}", size.get().as_str()),
class
]>
<input
class="thaw-checkbox__input"
type="checkbox"
id=id.clone()
name=move || group.map(|g| g.name.get()).flatten()
value=item_value.get_value()
checked=checked
node_ref=input_ref
on:change=on_change
/>
<div aria-hidden="true" class="thaw-checkbox__indicator">
{move || {
if checked() {
match size.get() {
CheckboxSize::Medium => {
Either::Left(
view! {
<Checkmark12FilledIcon attr:style="display: inline;line-height: 0" />
},
)
}
CheckboxSize::Large => {
Either::Right(
view! {
<Checkmark16FilledIcon attr:style="display: inline;line-height: 0" />
},
)
}
}
.into()
} else {
None
}
}}
</div>
{move || {
if let Some(label) = label.get() {
view! {
<label class="thaw-checkbox__label" for=id.clone()>
{label}
</label>
}
.into()
} else {
None
}
}}
</span>
}
}
15 changes: 15 additions & 0 deletions thaw/src/checkbox/checkbox/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum CheckboxSize {
#[default]
Medium,
Large,
}

impl CheckboxSize {
pub fn as_str(&self) -> &'static str {
match self {
Self::Medium => "medium",
Self::Large => "large",
}
}
}
22 changes: 16 additions & 6 deletions thaw/src/checkbox/docs/mod.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ view! {
}
```

### Size

```rust demo
view! {
<Checkbox label="Medium"/>
<Checkbox size=CheckboxSize::Large label="Large"/>
}
```

### Group

```rust demo
Expand All @@ -28,12 +37,13 @@ view! {

### Checkbox Props

| Name | Type | Default | Description |
| ------- | ------------------- | -------------------- | --------------------------------------------------------- |
| class | `MaybeProp<String>` | `Default::default()` | |
| checked | `Model<bool>` | `false` | The controlled value for the checkbox. |
| value | `Option<String>` | `None` | The value of the checkbox to be used in a checkbox group. |
| label | `MaybeProp<String>` | `Default::default()` | The Checkbox's label. |
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| class | `MaybeProp<String>` | `Default::default()` | |
| checked | `Model<bool>` | `false` | The controlled value for the checkbox. |
| value | `Option<String>` | `None` | The value of the checkbox to be used in a checkbox group. |
| label | `MaybeProp<String>` | `Default::default()` | The Checkbox's label. |
| size | `Signal<CheckboxSize>` | `CheckboxSize::Medium` | The size of the checkbox indicator. |

### CheckboxGroup Props

Expand Down
117 changes: 2 additions & 115 deletions thaw/src/checkbox/mod.rs
Original file line number Diff line number Diff line change
@@ -1,118 +1,5 @@
mod checkbox_group;
mod checkbox;

pub use checkbox_group::{CheckboxGroup, CheckboxGroupRule, CheckboxGroupRuleTrigger};

use checkbox_group::CheckboxGroupInjection;
use leptos::{html, prelude::*};
use thaw_utils::{class_list, mount_style, Model};

#[component]
pub fn Checkbox(
#[prop(optional, into)] class: MaybeProp<String>,
/// The controlled value for the checkbox.
#[prop(optional, into)]
checked: Model<bool>,
/// The value of the checkbox to be used in a checkbox group.
#[prop(optional, into)]
value: Option<String>,
/// The Checkbox's label.
#[prop(optional, into)]
label: MaybeProp<String>,
) -> impl IntoView {
mount_style("checkbox", include_str!("./checkbox.css"));

let id = uuid::Uuid::new_v4().to_string();
let input_ref = NodeRef::<html::Input>::new();
let group = CheckboxGroupInjection::use_context();
let item_value = StoredValue::new(value);

let group_checked = Memo::new(move |_| {
let Some(group) = group.as_ref() else {
return None;
};
group.value.with(|group_value| {
item_value.with_value(|value| {
let Some(value) = value else {
return None;
};
Some(group_value.contains(value))
})
})
});

let on_change = move |_| {
let input = input_ref.get_untracked().unwrap();
if group_checked.get_untracked().is_some() {
if input.checked() {
group.as_ref().unwrap().value.update(move |group_value| {
group_value.insert(item_value.get_value().unwrap());
});
} else {
group.as_ref().unwrap().value.update(move |group_value| {
item_value.with_value(|value| {
group_value.remove(value.as_ref().unwrap());
});
});
}
} else {
checked.set(input.checked())
}
};

let checked = move || group_checked.get().unwrap_or_else(|| checked.get());

view! {
<span class=class_list![
"thaw-checkbox",
("thaw-checkbox--checked", checked),
class
]>
<input
class="thaw-checkbox__input"
type="checkbox"
id=id.clone()
name=move || group.map(|g| g.name.get()).flatten()
value=item_value.get_value()
checked=checked
node_ref=input_ref
on:change=on_change
/>
<div aria-hidden="true" class="thaw-checkbox__indicator">
{move || {
if checked() {
view! {
<svg
fill="currentColor"
aria-hidden="true"
width="12"
height="12"
viewBox="0 0 12 12"
style="display: inline;line-height: 0"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
></path>
</svg>
}
.into()
} else {
None
}
}}
</div>
{move || {
if let Some(label) = label.get() {
view! {
<label class="thaw-checkbox__label" for=id.clone()>
{label}
</label>
}
.into()
} else {
None
}
}}
</span>
}
}
pub use checkbox::*;
38 changes: 38 additions & 0 deletions thaw/src/icon/icons/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,41 @@ pub fn InfoRegularIcon() -> impl IntoView {
</svg>
}
}

#[component]
pub fn Checkmark12FilledIcon() -> impl IntoView {
view! {
<svg
fill="currentColor"
aria-hidden="true"
width="12"
height="12"
viewBox="0 0 12 12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z"
fill="currentColor"
></path>
</svg>
}
}

#[component]
pub fn Checkmark16FilledIcon() -> impl IntoView {
view! {
<svg
fill="currentColor"
aria-hidden="true"
width="16"
height="16"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.05 3.49c.28.3.27.77-.04 1.06l-7.93 7.47A.85.85 0 0 1 4.9 12L2.22 9.28a.75.75 0 1 1 1.06-1.06l2.24 2.27 7.47-7.04a.75.75 0 0 1 1.06.04Z"
fill="currentColor"
></path>
</svg>
}
}

0 comments on commit 0233d7f

Please sign in to comment.