use crate::{
DynamicAnchor, DynamicNodeSlot, Element, Event, Properties, ScopeId, Template, VirtualDom,
arena::ElementId,
events::ListenerCallback,
innerlude::{BoxedAnyProps, MountId, ScopeState, VProps},
properties::ComponentFunction,
view::ViewExt,
};
use dioxus_core_types::DioxusFormattable;
use std::ops::Deref;
use std::rc::Rc;
use std::{
any::{Any, TypeId},
fmt::{Arguments, Debug},
};
pub struct DynamicValues {
pub(crate) key: Option<String>,
pub(crate) dynamic_nodes: Vec<DynamicNode>,
pub(crate) dynamic_attrs: Vec<Box<[Attribute]>>,
}
impl Debug for DynamicValues {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynamicValues").finish_non_exhaustive()
}
}
impl DynamicValues {
#[inline]
pub fn from_parts(
key: Option<String>,
dynamic_nodes: Box<[DynamicNode]>,
dynamic_attrs: Box<[Box<[Attribute]>]>,
) -> Self {
let mut values = Self {
key,
dynamic_nodes: dynamic_nodes.into_vec(),
dynamic_attrs: dynamic_attrs.into_vec(),
};
values.normalize();
values
}
pub(crate) fn new() -> Self {
Self {
key: None,
dynamic_nodes: Vec::new(),
dynamic_attrs: Vec::new(),
}
}
pub(crate) fn set_key(&mut self, key: Option<String>) {
self.key = key;
}
pub(crate) fn push_node(&mut self, value: DynamicNode) {
self.dynamic_nodes.push(value);
}
pub(crate) fn push_attrs(&mut self, value: Box<[Attribute]>) {
self.dynamic_attrs.push(value);
}
pub(crate) fn normalize(&mut self) {
for slot in self.dynamic_attrs.iter_mut() {
slot.sort_by_key(|attribute| (attribute.name, attribute.namespace));
}
}
#[inline]
pub fn key(&self) -> Option<&str> {
self.key.as_deref()
}
#[inline]
pub fn dynamic_node_values(&self) -> &[DynamicNode] {
&self.dynamic_nodes
}
#[inline]
pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
&self.dynamic_attrs
}
}
pub(crate) struct VNodeInner {
pub template: Template,
pub view: DynamicValues,
}
impl Debug for VNodeInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VNodeInner").finish_non_exhaustive()
}
}
impl Deref for VNodeInner {
type Target = DynamicValues;
fn deref(&self) -> &Self::Target {
&self.view
}
}
#[derive(Clone)]
pub struct VNode {
vnode: Rc<VNodeInner>,
}
impl Debug for VNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VNode").finish_non_exhaustive()
}
}
impl Default for VNode {
fn default() -> Self {
Self::placeholder()
}
}
impl PartialEq for VNode {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.vnode, &other.vnode)
}
}
impl VNode {
pub fn template(&self) -> &Template {
&self.vnode.template
}
pub fn dynamic_values(&self) -> &DynamicValues {
&self.vnode.view
}
pub fn dynamic_node_values(&self) -> &[DynamicNode] {
self.dynamic_values().dynamic_node_values()
}
pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
self.dynamic_values().dynamic_attr_values()
}
pub fn key(&self) -> Option<&str> {
self.dynamic_values().key()
}
pub fn empty() -> Element {
Ok(Self::default())
}
pub fn placeholder() -> Self {
use std::cell::OnceCell;
thread_local! {
static EMPTY_VNODE: OnceCell<VNode> = const { OnceCell::new() };
}
EMPTY_VNODE.with(|cell| {
cell.get_or_init(move || crate::view::fragment().into_vnode())
.clone()
})
}
pub fn new(template: Template, mut values: DynamicValues) -> Self {
values.normalize();
debug_assert!(
values.dynamic_nodes.len()
== template
.anchors()
.iter()
.map(|anchor| anchor.nodes().end)
.max()
.unwrap_or_default(),
"bad dynamic node count"
);
debug_assert!(
values.dynamic_attrs.len()
== template
.anchors()
.iter()
.map(|anchor| anchor.attributes().end)
.max()
.unwrap_or_default(),
"bad dynamic attribute count"
);
Self {
vnode: Rc::new(VNodeInner {
template,
view: values,
}),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MountedVNode<'a> {
vnode: &'a VNode,
mount: MountId,
}
impl<'a> MountedVNode<'a> {
pub(crate) const fn new(vnode: &'a VNode, mount: MountId) -> Self {
Self { vnode, mount }
}
pub(crate) const fn mount(self) -> MountId {
self.mount
}
pub const fn vnode(self) -> &'a VNode {
self.vnode
}
pub fn mounted_dynamic_node(
self,
slot: DynamicNodeSlot<'a>,
dom: &VirtualDom,
) -> Option<ElementId> {
let dynamic_node_idx = slot.index();
match &self.vnode.dynamic_node_values()[dynamic_node_idx] {
DynamicNode::Text(_) => dom
.mounted_dynamic_text_node(self.mount, dynamic_node_idx)
.map(|id| id.element_id()),
_ => None,
}
}
pub fn mounted_anchor_node(
self,
anchor: DynamicAnchor<'a>,
dom: &VirtualDom,
) -> Option<ElementId> {
dom.mounted_anchor_node(self.mount, anchor.anchor_index())
.map(|id| id.element_id())
}
pub fn mounted_static_anchor(self, anchor_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
dom.mounted_anchor_node(self.mount, anchor_idx)
.map(|id| id.element_id())
}
pub fn mounted_fragment_children(
self,
slot: DynamicNodeSlot<'a>,
dom: &VirtualDom,
) -> Vec<MountedVNode<'a>> {
let dynamic_node_idx = slot.index();
let DynamicNode::Fragment(children) = &self.vnode.dynamic_node_values()[dynamic_node_idx]
else {
return Vec::new();
};
children
.iter()
.zip(dom.mounted_fragment_children(self.mount, dynamic_node_idx, children.len()))
.map(|(vnode, mount)| MountedVNode::new(vnode, mount))
.collect()
}
}
impl Deref for MountedVNode<'_> {
type Target = VNode;
fn deref(&self) -> &Self::Target {
self.vnode
}
}
#[derive(Debug, Clone)]
pub enum DynamicNode {
Component(VComponent),
Text(VText),
Fragment(Vec<VNode>),
}
impl DynamicNode {
pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
into.into_dyn_node()
}
}
impl Default for DynamicNode {
fn default() -> Self {
Self::Fragment(Vec::new())
}
}
pub struct VComponent {
pub name: &'static str,
pub(crate) render_fn: usize,
pub(crate) driver: Rc<dyn crate::render_driver::RenderDriver>,
pub(crate) props: BoxedAnyProps,
}
impl Clone for VComponent {
fn clone(&self) -> Self {
Self {
name: self.name,
render_fn: self.render_fn,
driver: self.driver.clone(),
props: self.props.duplicate(),
}
}
}
impl VComponent {
pub fn new<P, M: 'static>(
component: impl ComponentFunction<P, M>,
props: P,
fn_name: &'static str,
) -> Self
where
P: Properties + 'static,
{
let render_fn = component.fn_ptr();
let props = Box::new(VProps::new(
component,
<P as Properties>::memoize,
props,
fn_name,
));
Self::new_with_driver(
fn_name,
render_fn,
Rc::new(crate::render_driver::BodyDriver),
props,
)
}
pub(crate) fn new_with_driver(
fn_name: &'static str,
render_fn: usize,
driver: Rc<dyn crate::render_driver::RenderDriver>,
props: BoxedAnyProps,
) -> Self {
VComponent {
name: fn_name,
render_fn,
driver,
props,
}
}
pub fn mounted_scope_id(
&self,
slot: DynamicNodeSlot<'_>,
vnode: MountedVNode<'_>,
dom: &VirtualDom,
) -> Option<ScopeId> {
dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())
}
pub fn mounted_scope<'a>(
&self,
slot: DynamicNodeSlot<'_>,
vnode: MountedVNode<'_>,
dom: &'a VirtualDom,
) -> Option<&'a ScopeState> {
let scope_id = dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())?;
dom.scopes.get(scope_id.index())
}
}
impl std::fmt::Debug for VComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent")
.field("name", &self.name)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct VText {
pub value: String,
}
impl VText {
#[inline]
pub fn new(value: impl ToString) -> Self {
Self {
value: value.to_string(),
}
}
}
impl From<Arguments<'_>> for VText {
fn from(args: Arguments) -> Self {
Self::new(args.to_string())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
pub name: &'static str,
pub value: AttributeValue,
pub namespace: Option<&'static str>,
pub volatile: bool,
}
impl Attribute {
pub fn new<T>(
name: &'static str,
value: impl IntoAttributeValue<T>,
namespace: Option<&'static str>,
volatile: bool,
) -> Attribute {
Attribute {
name,
namespace,
volatile,
value: value.into_value(),
}
}
}
#[derive(Clone)]
pub enum AttributeValue {
Text(String),
Float(f64),
Int(i64),
Bool(bool),
Listener(ListenerCallback),
Any(Rc<dyn AnyValue>),
None,
}
impl AttributeValue {
pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
AttributeValue::Listener(ListenerCallback::new(callback).erase())
}
pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
AttributeValue::Any(Rc::new(value))
}
}
impl std::fmt::Debug for AttributeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Listener(_) => f.debug_tuple("Listener").finish(),
Self::Any(_) => f.debug_tuple("Any").finish(),
Self::None => write!(f, "None"),
}
}
}
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
(Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
(Self::None, Self::None) => true,
_ => false,
}
}
}
#[doc(hidden)]
pub trait AnyValue: 'static {
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId {
self.as_any().type_id()
}
}
impl<T: Any + PartialEq + 'static> AnyValue for T {
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
if let Some(other) = other.as_any().downcast_ref() {
self == other
} else {
false
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub trait IntoDynNode<A = ()> {
fn into_dyn_node(self) -> DynamicNode;
}
impl IntoDynNode for () {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::default()
}
}
impl IntoDynNode for VNode {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Fragment(vec![self])
}
}
impl IntoDynNode for DynamicNode {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
self
}
}
impl<T: IntoDynNode> IntoDynNode for Option<T> {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
match self {
Some(val) => val.into_dyn_node(),
None => DynamicNode::default(),
}
}
}
impl IntoDynNode for &Element {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
match self.as_ref() {
Ok(val) => val.into_dyn_node(),
_ => DynamicNode::default(),
}
}
}
impl IntoDynNode for Element {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
match self {
Ok(val) => val.into_dyn_node(),
_ => DynamicNode::default(),
}
}
}
impl IntoDynNode for &Option<VNode> {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
match self.as_ref() {
Some(val) => val.clone().into_dyn_node(),
_ => DynamicNode::default(),
}
}
}
impl IntoDynNode for &str {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText {
value: self.to_string(),
})
}
}
impl IntoDynNode for String {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText { value: self })
}
}
impl IntoDynNode for Arguments<'_> {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText {
value: self.to_string(),
})
}
}
impl IntoDynNode for &VNode {
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Fragment(vec![self.clone()])
}
}
pub trait IntoVNode {
fn into_vnode(self) -> VNode;
}
impl IntoVNode for VNode {
#[inline]
fn into_vnode(self) -> VNode {
self
}
}
impl IntoVNode for &VNode {
#[inline]
fn into_vnode(self) -> VNode {
self.clone()
}
}
impl IntoVNode for Element {
#[inline]
fn into_vnode(self) -> VNode {
match self {
Ok(val) => val.into_vnode(),
_ => VNode::default(),
}
}
}
impl IntoVNode for &Element {
#[inline]
fn into_vnode(self) -> VNode {
match self {
Ok(val) => val.into_vnode(),
_ => VNode::default(),
}
}
}
impl IntoVNode for Option<VNode> {
#[inline]
fn into_vnode(self) -> VNode {
match self {
Some(val) => val.into_vnode(),
_ => VNode::default(),
}
}
}
impl IntoVNode for &Option<VNode> {
#[inline]
fn into_vnode(self) -> VNode {
match self.as_ref() {
Some(val) => val.clone().into_vnode(),
_ => VNode::default(),
}
}
}
impl IntoVNode for Option<Element> {
#[inline]
fn into_vnode(self) -> VNode {
match self {
Some(val) => val.into_vnode(),
_ => VNode::default(),
}
}
}
impl IntoVNode for &Option<Element> {
#[inline]
fn into_vnode(self) -> VNode {
match self.as_ref() {
Some(val) => val.clone().into_vnode(),
_ => VNode::default(),
}
}
}
pub struct FromNodeIterator;
impl<T, I> IntoDynNode<FromNodeIterator> for T
where
T: Iterator<Item = I>,
I: IntoVNode,
{
#[inline]
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Fragment(self.into_iter().map(|node| node.into_vnode()).collect())
}
}
pub trait IntoAttributeValue<T = ()> {
fn into_value(self) -> AttributeValue;
}
impl IntoAttributeValue for AttributeValue {
fn into_value(self) -> AttributeValue {
self
}
}
impl IntoAttributeValue for &str {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self.to_string())
}
}
impl IntoAttributeValue for String {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self)
}
}
macro_rules! impl_float_attribute_value {
($($ty:ty),* $(,)?) => {
$(
impl IntoAttributeValue for $ty {
fn into_value(self) -> AttributeValue {
AttributeValue::Float(self as _)
}
}
)*
};
}
macro_rules! impl_int_attribute_value {
($($ty:ty),* $(,)?) => {
$(
impl IntoAttributeValue for $ty {
fn into_value(self) -> AttributeValue {
AttributeValue::Int(self as _)
}
}
)*
};
}
impl_float_attribute_value!(f32, f64);
impl_int_attribute_value!(i8, i16, i32, i64, isize, i128);
impl_int_attribute_value!(u8, u16, u32, u64, usize, u128);
impl IntoAttributeValue for bool {
fn into_value(self) -> AttributeValue {
AttributeValue::Bool(self)
}
}
impl IntoAttributeValue for Arguments<'_> {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self.to_string())
}
}
impl IntoAttributeValue for Rc<dyn AnyValue> {
fn into_value(self) -> AttributeValue {
AttributeValue::Any(self)
}
}
impl<T> IntoAttributeValue for ListenerCallback<T> {
fn into_value(self) -> AttributeValue {
AttributeValue::Listener(self.erase())
}
}
impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
fn into_value(self) -> AttributeValue {
match self {
Some(val) => val.into_value(),
None => AttributeValue::None,
}
}
}
impl<T: ToOwned<Owned = R>, R: IntoAttributeValue> IntoAttributeValue for &T {
fn into_value(self) -> AttributeValue {
self.to_owned().into_value()
}
}
#[doc(hidden)]
pub struct AnyFmtMarker;
impl<T> IntoAttributeValue<AnyFmtMarker> for T
where
T: DioxusFormattable,
{
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self.format().to_string())
}
}
pub trait HasAttributes {
fn push_attribute<T>(
self,
name: &'static str,
ns: Option<&'static str>,
attr: impl IntoAttributeValue<T>,
volatile: bool,
) -> Self;
}