Skip to content

Commit

Permalink
Add support for arbitrary node ref elements
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielleHuisman committed Apr 14, 2024
1 parent 571af01 commit 6f57e8b
Show file tree
Hide file tree
Showing 6 changed files with 362 additions and 86 deletions.
1 change: 1 addition & 0 deletions packages/leptos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ version.workspace = true
cfg-if = "1.0.0"
floating-ui-dom = { path = "../dom", version = "0.0.4" }
leptos = { version = "0.6.9", features = ["nightly"] }
paste = "1.0.0"
web-sys.workspace = true

[dev-dependencies]
Expand Down
52 changes: 28 additions & 24 deletions packages/leptos/src/arrow.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
use std::ops::Deref;
use std::marker::PhantomData;

use floating_ui_dom::{
Arrow as CoreArrow, ArrowOptions as CoreArrowOptions, Middleware, MiddlewareReturn,
MiddlewareState, Padding, ARROW_NAME,
};
use leptos::{html::ElementDescriptor, NodeRef};
use leptos::html::ElementDescriptor;

use crate::node_ref::NodeRefAsElement;

/// Options for [`Arrow`].
#[derive(Clone)]
pub struct ArrowOptions<Descriptor, Element>
pub struct ArrowOptions<Ref, RefEl>
where
Descriptor: ElementDescriptor + Deref<Target = Element> + Clone + 'static,
Element: Deref<Target = web_sys::HtmlElement>,
Ref: NodeRefAsElement<RefEl> + Copy + 'static,
RefEl: ElementDescriptor + Clone + 'static,
{
/// The arrow element to be positioned.
pub element: NodeRef<Descriptor>,
pub element: Ref,

/// The padding between the arrow element and the floating element edges.
/// Useful when the floating element has rounded corners.
///
/// Defaults to `0` on all sides.
pub padding: Option<Padding>,

phantom: PhantomData<RefEl>,
}

impl<Descriptor, Element> ArrowOptions<Descriptor, Element>
impl<Ref, RefEl> ArrowOptions<Ref, RefEl>
where
Descriptor: ElementDescriptor + Deref<Target = Element> + Clone + 'static,
Element: Deref<Target = web_sys::HtmlElement>,
Ref: NodeRefAsElement<RefEl> + Copy + 'static,
RefEl: ElementDescriptor + Clone + 'static,
{
pub fn new(element: NodeRef<Descriptor>) -> Self {
pub fn new(element: Ref) -> Self {
ArrowOptions {
element,
padding: None,
phantom: PhantomData,
}
}

/// Set `element` option.
pub fn element(mut self, value: NodeRef<Descriptor>) -> Self {
pub fn element(mut self, value: Ref) -> Self {
self.element = value;
self
}
Expand All @@ -52,29 +57,28 @@ where
///
/// See <https://floating-ui.com/docs/arrow> for the original documentation.
#[derive(Clone)]
pub struct Arrow<Descriptor, Element>
pub struct Arrow<Ref, RefEl>
where
Descriptor: ElementDescriptor + Deref<Target = Element> + Clone + 'static,
Element: Deref<Target = web_sys::HtmlElement> + Clone,
Ref: NodeRefAsElement<RefEl> + Copy + 'static,
RefEl: ElementDescriptor + Clone + 'static,
{
options: ArrowOptions<Descriptor, Element>,
options: ArrowOptions<Ref, RefEl>,
}

impl<Descriptor, Element> Arrow<Descriptor, Element>
impl<Ref, RefEl> Arrow<Ref, RefEl>
where
Descriptor: ElementDescriptor + Deref<Target = Element> + Clone + 'static,
Element: Deref<Target = web_sys::HtmlElement> + Clone,
Ref: NodeRefAsElement<RefEl> + Copy + 'static,
RefEl: ElementDescriptor + Clone + 'static,
{
pub fn new(options: ArrowOptions<Descriptor, Element>) -> Self {
pub fn new(options: ArrowOptions<Ref, RefEl>) -> Self {
Arrow { options }
}
}

impl<Descriptor, Element> Middleware<web_sys::Element, web_sys::Window>
for Arrow<Descriptor, Element>
impl<Ref, RefEl> Middleware<web_sys::Element, web_sys::Window> for Arrow<Ref, RefEl>
where
Descriptor: ElementDescriptor + Deref<Target = Element> + Clone + 'static,
Element: Deref<Target = web_sys::HtmlElement> + Clone,
Ref: NodeRefAsElement<RefEl> + Copy + 'static,
RefEl: ElementDescriptor + Clone + 'static,
{
fn name(&self) -> &'static str {
ARROW_NAME
Expand All @@ -84,7 +88,7 @@ where
&self,
state: MiddlewareState<web_sys::Element, web_sys::Window>,
) -> MiddlewareReturn {
let element = self.options.element.get_untracked();
let element = self.options.element.get_untracked_as_element();

if let Some(element) = element {
let element: &web_sys::Element = &element;
Expand Down
1 change: 1 addition & 0 deletions packages/leptos/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod arrow;
mod node_ref;
mod types;
mod use_floating;
mod utils;
Expand Down
251 changes: 251 additions & 0 deletions packages/leptos/src/node_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
use std::ops::Deref;

use leptos::{html::ElementDescriptor, HtmlElement, NodeRef};
use web_sys::Element;

pub trait NodeRefAsElement<T: ElementDescriptor + Clone + 'static> {
fn get_as_element(&self) -> Option<Element>;

fn get_untracked_as_element(&self) -> Option<Element>;

fn on_load<F>(self, f: F)
where
F: FnOnce(HtmlElement<T>) + 'static;
}

impl NodeRefAsElement<leptos::html::AnyElement> for NodeRef<leptos::html::AnyElement> {
fn get_as_element(&self) -> Option<Element> {
self.get().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn get_untracked_as_element(&self) -> Option<Element> {
self.get_untracked().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn on_load<F>(self, f: F)
where
F: FnOnce(HtmlElement<leptos::html::AnyElement>) + 'static,
{
self.on_load(f)
}
}

macro_rules! generate_html_tags {
($(
$tag:ty
),* $(,)?) => {
paste::paste! {
$(
impl NodeRefAsElement<leptos::html::[<$tag>]> for NodeRef<leptos::html::[<$tag>]> {
fn get_as_element(&self) -> Option<Element> {
self.get().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn get_untracked_as_element(&self) -> Option<Element> {
self.get_untracked().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn on_load<F>(self, f: F)
where
F: FnOnce(HtmlElement<leptos::html::[<$tag>]>) + 'static,
{
self.on_load(f)
}
}
)*
}
}
}

macro_rules! generate_math_tags {
($(
$tag:ty
),* $(,)?) => {
paste::paste! {
$(
impl NodeRefAsElement<leptos::math::[<$tag>]> for NodeRef<leptos::math::[<$tag>]> {
fn get_as_element(&self) -> Option<Element> {
self.get().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn get_untracked_as_element(&self) -> Option<Element> {
self.get_untracked().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn on_load<F>(self, f: F)
where
F: FnOnce(HtmlElement<leptos::math::[<$tag>]>) + 'static,
{
self.on_load(f)
}
}
)*
}
}
}

macro_rules! generate_svg_tags {
($(
$tag:ty
),* $(,)?) => {
paste::paste! {
$(
impl NodeRefAsElement<leptos::svg::[<$tag>]> for NodeRef<leptos::svg::[<$tag>]> {
fn get_as_element(&self) -> Option<Element> {
self.get().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn get_untracked_as_element(&self) -> Option<Element> {
self.get_untracked().map(|html_element| {
let element: &web_sys::Element = html_element.deref();
element.clone()
})
}

fn on_load<F>(self, f: F)
where
F: FnOnce(HtmlElement<leptos::svg::[<$tag>]>) + 'static,
{
self.on_load(f)
}
}
)*
}
}
}

generate_html_tags![
Html, Base, Head, Link, Meta, Style, Title, Body, Address, Article, Aside, Footer, Header,
Hgroup, H1, H2, H3, H4, H5, H6, Main, Nav, Section, Blockquote, Dd, Div, Dl, Dt, Figcaption,
Figure, Hr, Li, Ol, P, Pre, Ul, A, Abbr, B, Bdi, Bdo, Br, Cite, Code, Data, Dfn, Em, I, Kbd,
Mark, Q, Rp, Rt, Ruby, S, Samp, Small, Span, Strong, Sub, Sup, Time, U, Var, Wbr, Area, Audio,
Img, Map, Track, Video, Embed, Iframe, Object, Param, Picture, Portal, Source, Svg, Math,
Canvas, Noscript, Script, Del, Ins, Caption, Col, Colgroup, Table, Tbody, Td, Tfoot, Th, Thead,
Tr, Button, Datalist, Fieldset, Form, Input, Label, Legend, Meter, Optgroup, Option_, Output,
Progress, Select, Textarea, Details, Dialog, Menu, Summary, Slot, Template,
];

generate_math_tags![
Math,
Mi,
Mn,
Mo,
Ms,
Mspace,
Mtext,
Menclose,
Merror,
Mfenced,
Mfrac,
Mpadded,
Mphantom,
Mroot,
Mrow,
Msqrt,
Mstyle,
Mmultiscripts,
Mover,
Mprescripts,
Msub,
Msubsup,
Msup,
Munder,
Munderover,
Mtable,
Mtd,
Mtr,
Maction,
Annotation,
AnnotationXml,
Semantics,
];

generate_svg_tags![
A,
Animate,
AnimateMotion,
AnimateTransform,
Circle,
ClipPath,
Defs,
Desc,
Discard,
Ellipse,
FeBlend,
FeColorMatrix,
FeComponentTransfer,
FeComposite,
FeConvolveMatrix,
FeDiffuseLighting,
FeDisplacementMap,
FeDistantLight,
FeDropShadow,
FeFlood,
FeFuncA,
FeFuncB,
FeFuncG,
FeFuncR,
FeGaussianBlur,
FeImage,
FeMerge,
FeMergeNode,
FeMorphology,
FeOffset,
FePointLight,
FeSpecularLighting,
FeSpotLight,
FeTile,
FeTurbulence,
Filter,
ForeignObject,
G,
Hatch,
Hatchpath,
Image,
Line,
LinearGradient,
Marker,
Mask,
Metadata,
Mpath,
Path,
Pattern,
Polygon,
Polyline,
RadialGradient,
Rect,
Script,
Set,
Stop,
Style,
Svg,
Switch,
Symbol,
Text,
TextPath,
Title,
Tspan,
Use,
View,
];
Loading

0 comments on commit 6f57e8b

Please sign in to comment.