feat: arbitrary attributes

This commit is contained in:
Jonathan Kelley 2022-05-05 16:50:33 -04:00
parent f2c48c5d90
commit 356f37e9ee
7 changed files with 298 additions and 9 deletions

View file

@ -0,0 +1,265 @@
use std::fmt::Formatter;
// trying to keep values at 3 bytes
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum AttributeValue<'a> {
Text(&'a str),
Float32(f32),
Float64(f64),
Int32(i32),
Int64(i64),
Uint32(u32),
Uint64(u64),
Bool(bool),
ColorRGB(u8, u8, u8),
ColorRGBA(u8, u8, u8, u8),
ColorHex(u32),
ColorHexAlpha(u32, u8),
Vec3Float(f32, f32, f32),
Vec3Int(i32, i32, i32),
Vec3Uint(u32, u32, u32),
Vec4Float(f32, f32, f32, f32),
Vec4Int(i32, i32, i32, i32),
Vec4Uint(u32, u32, u32, u32),
Bytes(&'a [u8]),
Any(ArbitraryAttributeValue<'a>),
}
impl<'a> AttributeValue<'a> {
pub fn is_truthy(&self) -> bool {
match self {
AttributeValue::Text(t) => *t == "true",
AttributeValue::Bool(t) => *t,
_ => false,
}
}
pub fn is_falsy(&self) -> bool {
match self {
AttributeValue::Text(t) => *t == "false",
AttributeValue::Bool(t) => !(*t),
_ => false,
}
}
}
impl<'a> std::fmt::Display for AttributeValue<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AttributeValue::Text(a) => write!(f, "{}", a),
AttributeValue::Float32(a) => write!(f, "{}", a),
AttributeValue::Float64(a) => write!(f, "{}", a),
AttributeValue::Int32(a) => write!(f, "{}", a),
AttributeValue::Int64(a) => write!(f, "{}", a),
AttributeValue::Uint32(a) => write!(f, "{}", a),
AttributeValue::Uint64(a) => write!(f, "{}", a),
AttributeValue::Bool(a) => write!(f, "{}", a),
AttributeValue::ColorRGB(_, _, _) => todo!(),
AttributeValue::ColorRGBA(_, _, _, _) => todo!(),
AttributeValue::ColorHex(_) => todo!(),
AttributeValue::ColorHexAlpha(_, _) => todo!(),
AttributeValue::Vec3Float(_, _, _) => todo!(),
AttributeValue::Vec3Int(_, _, _) => todo!(),
AttributeValue::Vec3Uint(_, _, _) => todo!(),
AttributeValue::Vec4Float(_, _, _, _) => todo!(),
AttributeValue::Vec4Int(_, _, _, _) => todo!(),
AttributeValue::Vec4Uint(_, _, _, _) => todo!(),
AttributeValue::Bytes(_) => todo!(),
AttributeValue::Any(_) => todo!(),
}
}
}
#[derive(Clone, Copy)]
pub struct ArbitraryAttributeValue<'a> {
pub value: &'a dyn std::any::Any,
pub cmp: fn(&'a dyn std::any::Any, &'a dyn std::any::Any) -> bool,
}
impl PartialEq for ArbitraryAttributeValue<'_> {
fn eq(&self, other: &Self) -> bool {
(self.cmp)(self.value, other.value)
}
}
impl std::fmt::Debug for ArbitraryAttributeValue<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArbitraryAttributeValue").finish()
}
}
#[cfg(feature = "serialize")]
impl<'a> serde::Serialize for ArbitraryAttributeValue<'a> {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
panic!("ArbitraryAttributeValue should not be serialized")
}
}
#[cfg(feature = "serialize")]
impl<'de, 'a> serde::Deserialize<'de> for &'a ArbitraryAttributeValue<'a> {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
panic!("ArbitraryAttributeValue is not deserializable!")
}
}
#[cfg(feature = "serialize")]
impl<'de, 'a> serde::Deserialize<'de> for ArbitraryAttributeValue<'a> {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
panic!("ArbitraryAttributeValue is not deserializable!")
}
}
impl<'a> AttributeValue<'a> {
pub fn as_text(&self) -> Option<&'a str> {
match self {
AttributeValue::Text(s) => Some(s),
_ => None,
}
}
pub fn as_float32(&self) -> Option<f32> {
match self {
AttributeValue::Float32(f) => Some(*f),
_ => None,
}
}
pub fn as_float64(&self) -> Option<f64> {
match self {
AttributeValue::Float64(f) => Some(*f),
_ => None,
}
}
pub fn as_int32(&self) -> Option<i32> {
match self {
AttributeValue::Int32(i) => Some(*i),
_ => None,
}
}
pub fn as_int64(&self) -> Option<i64> {
match self {
AttributeValue::Int64(i) => Some(*i),
_ => None,
}
}
pub fn as_uint32(&self) -> Option<u32> {
match self {
AttributeValue::Uint32(i) => Some(*i),
_ => None,
}
}
pub fn as_uint64(&self) -> Option<u64> {
match self {
AttributeValue::Uint64(i) => Some(*i),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
AttributeValue::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_color_rgb(&self) -> Option<(u8, u8, u8)> {
match self {
AttributeValue::ColorRGB(r, g, b) => Some((*r, *g, *b)),
_ => None,
}
}
pub fn as_color_rgba(&self) -> Option<(u8, u8, u8, u8)> {
match self {
AttributeValue::ColorRGBA(r, g, b, a) => Some((*r, *g, *b, *a)),
_ => None,
}
}
pub fn as_color_hex(&self) -> Option<u32> {
match self {
AttributeValue::ColorHex(c) => Some(*c),
_ => None,
}
}
pub fn as_color_hex_alpha(&self) -> Option<(u32, u8)> {
match self {
AttributeValue::ColorHexAlpha(c, a) => Some((*c, *a)),
_ => None,
}
}
pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> {
match self {
AttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)),
_ => None,
}
}
pub fn as_vec3_int(&self) -> Option<(i32, i32, i32)> {
match self {
AttributeValue::Vec3Int(x, y, z) => Some((*x, *y, *z)),
_ => None,
}
}
pub fn as_vec3_uint(&self) -> Option<(u32, u32, u32)> {
match self {
AttributeValue::Vec3Uint(x, y, z) => Some((*x, *y, *z)),
_ => None,
}
}
pub fn as_vec4_float(&self) -> Option<(f32, f32, f32, f32)> {
match self {
AttributeValue::Vec4Float(x, y, z, w) => Some((*x, *y, *z, *w)),
_ => None,
}
}
pub fn as_vec4_int(&self) -> Option<(i32, i32, i32, i32)> {
match self {
AttributeValue::Vec4Int(x, y, z, w) => Some((*x, *y, *z, *w)),
_ => None,
}
}
pub fn as_vec4_uint(&self) -> Option<(u32, u32, u32, u32)> {
match self {
AttributeValue::Vec4Uint(x, y, z, w) => Some((*x, *y, *z, *w)),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
AttributeValue::Bytes(b) => Some(b),
_ => None,
}
}
pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
match self {
AttributeValue::Any(a) => Some(a),
_ => None,
}
}
}
#[test]
fn test_attribute_value_size() {
assert_eq!(std::mem::size_of::<AttributeValue<'_>>(), 24);
}

View file

@ -2,6 +2,7 @@
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
pub(crate) mod arbitrary_value;
pub(crate) mod diff;
pub(crate) mod events;
pub(crate) mod lazynodes;
@ -13,6 +14,7 @@ pub(crate) mod util;
pub(crate) mod virtual_dom;
pub(crate) mod innerlude {
pub use crate::arbitrary_value::*;
pub use crate::events::*;
pub use crate::lazynodes::*;
pub use crate::mutations::*;

View file

@ -167,8 +167,9 @@ pub enum DomEdit<'bump> {
field: &'static str,
/// The value of the attribute.
value: &'bump str,
value: AttributeValue<'bump>,
// value: &'bump str,
/// The (optional) namespace of the attribute.
/// For instance, "style" is in the "style" namespace.
ns: Option<&'bump str>,
@ -286,7 +287,7 @@ impl<'a> Mutations<'a> {
self.edits.push(SetText { text, root });
}
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: u64) {
let Attribute {
name,
value,
@ -296,7 +297,7 @@ impl<'a> Mutations<'a> {
self.edits.push(SetAttribute {
field: name,
value,
value: value.clone(),
ns: *namespace,
root,
});

View file

@ -4,7 +4,7 @@
//! cheap and *very* fast to construct - building a full tree should be quick.
use crate::{
innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState, AttributeValue},
lazynodes::LazyNodes,
AnyEvent, Component,
};
@ -339,7 +339,7 @@ pub struct Attribute<'a> {
pub name: &'static str,
/// The value of the attribute.
pub value: &'a str,
pub value: AttributeValue<'a>,
/// An indication if this attribute can be ignored during diffing
///
@ -357,6 +357,7 @@ pub struct Attribute<'a> {
pub namespace: Option<&'static str>,
}
/// An event listener.
/// IE onclick, onkeydown, etc
pub struct Listener<'bump> {
@ -610,6 +611,24 @@ impl<'a> NodeFactory<'a> {
is_volatile: bool,
) -> Attribute<'a> {
let (value, is_static) = self.raw_text(val);
Attribute {
name,
value: AttributeValue::Text(value),
is_static,
namespace,
is_volatile,
}
}
/// Create a new [`Attribute`] using non-arguments
pub fn custom_attr(
&self,
name: &'static str,
value: AttributeValue<'a>,
namespace: Option<&'static str>,
is_volatile: bool,
is_static: bool,
) -> Attribute<'a> {
Attribute {
name,
value,

View file

@ -186,7 +186,9 @@ impl<'a> TextRenderer<'a, '_> {
while let Some(attr) = attr_iter.next() {
match attr.namespace {
None => match attr.name {
"dangerous_inner_html" => inner_html = Some(attr.value),
"dangerous_inner_html" => {
inner_html = Some(attr.value.as_text().unwrap())
}
"allowfullscreen"
| "allowpaymentrequest"
| "async"
@ -213,7 +215,7 @@ impl<'a> TextRenderer<'a, '_> {
| "reversed"
| "selected"
| "truespeed" => {
if attr.value != "false" {
if attr.value.is_truthy() {
write!(f, " {}=\"{}\"", attr.name, attr.value)?
}
}

View file

@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
dioxus-core = { path = "../core", version = "^0.2.1" }
dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
"web",
"web"
] }
js-sys = "0.3.56"

View file

@ -146,7 +146,7 @@ impl WebsysDom {
value,
ns,
} => {
let value = serde_wasm_bindgen::to_value(value).unwrap();
let value = serde_wasm_bindgen::to_value(&value).unwrap();
self.interpreter.SetAttribute(root, field, value, ns)
}
}