Skip to content

Commit

Permalink
Merge #585
Browse files Browse the repository at this point in the history
585: Add own Classes struct implementation r=DenisKolodin a=DenisKolodin

Hide details of Classes set implementation and add a feature to produce that struct from variety of sources like `&str`, `String` and `Vec<T> where T: AsRef<str>`.

I need this to have an effective way to construct complex classes set for https://github.com/yewstack/facade. With this PR we can do something like:

```rust
let mut classes = vec!["container"];
classes.push("fluid");
// or
let mut classes = Classes::new();
classes.append("container");
if fluid_flag {
    container.append("fluid");
}

html! {
    <div classes=classes />
}
```

In the future we can implement `impl Info<Classes> for (tuple)` and  simplify macro by delegating **tuples to classes** conversion to type system (instead of macro).

Let's give a chance to CI to check it 🤞 

Also I applied `cargo fmt` to the root crate sources.

Co-authored-by: Denis Kolodin <deniskolodin@gmail.com>
  • Loading branch information
bors[bot] and therustmonk committed Aug 13, 2019
2 parents 76336b3 + 46ae317 commit 84db98c
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 21 deletions.
2 changes: 1 addition & 1 deletion crates/macro/src/html_tree/html_tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl ToTokens for HtmlTag {
#vtag.add_classes(vec![#(&(#classes)),*]);
},
ClassesForm::Single(classes) => quote! {
#vtag.set_classes(&(#classes));
#vtag.set_classes(#classes);
},
});

Expand Down
6 changes: 4 additions & 2 deletions src/services/resize.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! This module contains the implementation of a service that listens for browser window resize events.
use stdweb::Value;
use stdweb::{
js,
web::{window, Window},
};
use yew::callback::Callback;
use stdweb::{js, web::{window, Window}};


/// A service that fires events when the browser window resizes.
#[derive(Default)]
Expand Down
59 changes: 57 additions & 2 deletions src/virtual_dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ pub mod vnode;
pub mod vtag;
pub mod vtext;

use indexmap::set::IndexSet;
use std::collections::HashMap;
use std::fmt;
use stdweb::web::{Element, EventListenerHandle, Node};
use indexmap::set::IndexSet;

pub use self::vcomp::VComp;
pub use self::vlist::VList;
Expand Down Expand Up @@ -41,7 +41,62 @@ type Listeners<COMP> = Vec<Box<dyn Listener<COMP>>>;
type Attributes = HashMap<String, String>;

/// A set of classes.
type Classes = IndexSet<String>;
#[derive(Debug)]
pub struct Classes {
set: IndexSet<String>,
}

impl Classes {
/// Creates empty set of classes.
pub fn new() -> Self {
Self {
set: IndexSet::new(),
}
}

/// Adds a class to a set.
pub fn push(&mut self, class: &str) {
self.set.insert(class.into());
}

/// Check the set contains a class.
pub fn contains(&self, class: &str) -> bool {
self.set.contains(class)
}
}

impl ToString for Classes {
fn to_string(&self) -> String {
let mut buf = String::new();
for class in &self.set {
buf.push_str(class);
buf.push(' ');
}
buf.pop();
buf
}
}

impl From<&str> for Classes {
fn from(t: &str) -> Self {
let set = t.split_whitespace().map(String::from).collect();
Self { set }
}
}

impl From<String> for Classes {
fn from(t: String) -> Self {
let set = t.split_whitespace().map(String::from).collect();
Self { set }
}
}

impl<T: AsRef<str>> From<Vec<T>> for Classes {
fn from(t: Vec<T>) -> Self {
let set = t.iter().map(|x| x.as_ref().to_string()).collect();
Self { set }
}
}

/// Patch for DOM node modification.
enum Patch<ID, T> {
Expand Down
17 changes: 10 additions & 7 deletions src/virtual_dom/vtag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl<COMP: Component> VTag<COMP> {
pub fn add_class(&mut self, class: &str) {
let class = class.trim();
if !class.is_empty() {
self.classes.insert(class.into());
self.classes.push(class);
}
}

Expand All @@ -107,16 +107,16 @@ impl<COMP: Component> VTag<COMP> {
for class in classes {
let class = class.trim();
if !class.is_empty() {
self.classes.insert(class.into());
self.classes.push(class);
}
}
}

/// Add classes to this virtual node. Actually it will set by
/// [Element.classList.add](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
/// call later.
pub fn set_classes(&mut self, classes: &str) {
self.classes = classes.split_whitespace().map(String::from).collect();
pub fn set_classes(&mut self, classes: impl Into<Classes>) {
self.classes = classes.into();
}

/// Sets `value` for an
Expand Down Expand Up @@ -187,18 +187,21 @@ impl<COMP: Component> VTag<COMP> {
// Only change what is necessary.
let to_add = self
.classes
.difference(&ancestor.classes)
.set
.difference(&ancestor.classes.set)
.map(|class| Patch::Add(class.to_owned(), ()));
changes.extend(to_add);
let to_remove = ancestor
.classes
.difference(&self.classes)
.set
.difference(&self.classes.set)
.map(|class| Patch::Remove(class.to_owned()));
changes.extend(to_remove);
} else {
// Add everything
let to_add = self
.classes
.set
.iter()
.map(|class| Patch::Add(class.to_owned(), ()));
changes.extend(to_add);
Expand Down Expand Up @@ -558,7 +561,7 @@ impl<COMP: Component> PartialEq for VTag<COMP> {
return false;
}

if self.classes.iter().ne(other.classes.iter()) {
if self.classes.set.iter().ne(other.classes.set.iter()) {
return false;
}

Expand Down
29 changes: 20 additions & 9 deletions tests/vtag_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,24 @@ fn supports_multiple_classes_string() {
}
}

#[test]
fn supports_multiple_classes_vec() {
let mut classes = vec!["class-1"];
classes.push("class-2");
let a: VNode<Comp> = html! {
<div class=classes></div>
};

if let VNode::VTag(vtag) = a {
println!("{:?}", vtag.classes);
assert!(vtag.classes.contains("class-1"));
assert!(vtag.classes.contains("class-2"));
assert!(!vtag.classes.contains("class-3"));
} else {
panic!("vtag expected");
}
}

fn assert_vtag(node: &mut VNode<Comp>) -> &mut VTag<Comp> {
if let VNode::VTag(vtag) = node {
return vtag;
Expand Down Expand Up @@ -261,16 +279,9 @@ fn keeps_order_of_classes() {
<div class="class-1 class-2 class-3",></div>
};

if let VNode::VTag(mut vtag) = a {
if let VNode::VTag(vtag) = a {
println!("{:?}", vtag.classes);
assert_eq!(
vtag.classes.drain(..).collect::<Vec<String>>(),
vec![
String::from("class-1"),
String::from("class-2"),
String::from("class-3")
]
)
assert_eq!(vtag.classes.to_string(), "class-1 class-2 class-3");
}
}

Expand Down

0 comments on commit 84db98c

Please sign in to comment.