Skip to main content

dominator2/
dom.rs

1use std::borrow::BorrowMut;
2use std::convert::AsRef;
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use discard::{Discard, DiscardOnDrop};
8use futures_channel::oneshot;
9use futures_signals::signal::{not, Signal};
10use futures_signals::signal_vec::SignalVec;
11use futures_util::FutureExt;
12use once_cell::sync::Lazy;
13use wasm_bindgen::{intern, JsCast, JsValue, UnwrapThrowExt};
14use web_sys::{
15    CssStyleDeclaration, CssStyleSheet, Element, EventTarget, HtmlElement, Node, ShadowRoot,
16    ShadowRootInit, ShadowRootMode, Text,
17};
18
19use crate::bindings;
20use crate::callbacks::Callbacks;
21use crate::operations;
22use crate::operations::{for_each, spawn_future};
23use crate::traits::*;
24use crate::utils::{on, EventListener, FnDiscard, ValueDiscard};
25
26pub struct RefFn<A, B, C>
27where
28    B: ?Sized,
29    C: Fn(&A) -> &B,
30{
31    value: A,
32    callback: C,
33}
34
35impl<A, B, C> RefFn<A, B, C>
36where
37    B: ?Sized,
38    C: Fn(&A) -> &B,
39{
40    #[inline]
41    pub fn new(value: A, callback: C) -> Self {
42        Self { value, callback }
43    }
44
45    #[inline]
46    pub fn call_ref(&self) -> &B {
47        (self.callback)(&self.value)
48    }
49
50    /*pub fn map<D, E>(self, callback: E) -> RefFn<A, impl Fn(&A) -> &D>
51        where D: ?Sized,
52              E: Fn(&B) -> &D {
53
54        let old_callback = self.callback;
55
56        RefFn {
57            value: self.value,
58            callback: move |value| callback(old_callback(value)),
59        }
60    }*/
61}
62
63/*impl<A, B, C> Deref for RefFn<A, C> where B: ?Sized, C: Fn(&A) -> &B {
64    type Target = B;
65
66    #[inline]
67    fn deref(&self) -> &Self::Target {
68        self.call_ref()
69    }
70}*/
71
72/*impl<A, B, C> AsRef<B> for RefFn<A, C> where B: ?Sized, C: Fn(&A) -> &B {
73    #[inline]
74    fn as_ref(&self) -> &B {
75        self.call_ref()
76    }
77}*/
78
79// https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#Valid%20Namespace%20URIs
80const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
81
82// 32-bit signed int
83pub const HIGHEST_ZINDEX: &str = "2147483647";
84
85static HIDDEN_CLASS: Lazy<String> = Lazy::new(|| {
86    class! {
87        .style_important("display", "none")
88    }
89});
90
91// TODO should return HtmlBodyElement ?
92pub fn body() -> HtmlElement {
93    bindings::body()
94}
95
96pub fn get_id(id: &str) -> Element {
97    // TODO intern ?
98    bindings::get_element_by_id(id)
99}
100
101pub struct DomHandle {
102    parent: Node,
103    dom: Dom,
104}
105
106impl Discard for DomHandle {
107    #[inline]
108    fn discard(self) {
109        bindings::remove_child(&self.parent, &self.dom.element);
110        self.dom.callbacks.discard();
111    }
112}
113
114#[inline]
115pub fn append_dom(parent: &Node, mut dom: Dom) -> DomHandle {
116    bindings::append_child(&parent, &dom.element);
117
118    dom.callbacks.trigger_after_insert();
119
120    // This prevents it from triggering after_remove
121    dom.callbacks.leak();
122
123    DomHandle {
124        parent: parent.clone(),
125        dom,
126    }
127}
128
129// TODO use must_use ?
130enum IsWindowLoaded {
131    Initial {},
132    Pending {
133        receiver: oneshot::Receiver<Option<bool>>,
134        _event: EventListener,
135    },
136    Done {},
137}
138
139impl Signal for IsWindowLoaded {
140    type Item = bool;
141
142    fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
143        let result = match *self {
144            IsWindowLoaded::Initial {} => {
145                let is_ready = bindings::ready_state() == "complete";
146
147                if is_ready {
148                    Poll::Ready(Some(true))
149                } else {
150                    let (sender, receiver) = oneshot::channel();
151
152                    *self = IsWindowLoaded::Pending {
153                        receiver,
154                        _event: EventListener::once(
155                            bindings::window_event_target(),
156                            "load",
157                            move |_| {
158                                // TODO test this
159                                sender.send(Some(true)).unwrap_throw();
160                            },
161                        ),
162                    };
163
164                    Poll::Ready(Some(false))
165                }
166            }
167            IsWindowLoaded::Pending {
168                ref mut receiver, ..
169            } => receiver.poll_unpin(cx).map(|x| x.unwrap_throw()),
170            IsWindowLoaded::Done {} => Poll::Ready(None),
171        };
172
173        if let Poll::Ready(Some(true)) = result {
174            *self = IsWindowLoaded::Done {};
175        }
176
177        result
178    }
179}
180
181// TODO this should be moved into gloo
182#[inline]
183pub fn is_window_loaded() -> impl Signal<Item = bool> {
184    IsWindowLoaded::Initial {}
185}
186
187// TODO should this intern ?
188#[inline]
189pub fn text(value: &str) -> Dom {
190    Dom::new(bindings::create_text_node(value).into())
191}
192
193fn make_text_signal<A, B>(callbacks: &mut Callbacks, value: B) -> Text
194where
195    A: AsStr,
196    B: Signal<Item = A> + 'static,
197{
198    let element = bindings::create_text_node(intern(""));
199
200    {
201        let element = element.clone();
202
203        callbacks.after_remove(for_each(value, move |value| {
204            value.with_str(|value| {
205                // TODO maybe this should intern ?
206                bindings::set_text(&element, value);
207            });
208        }));
209    }
210
211    element
212}
213
214// TODO should this inline ?
215pub fn text_signal<A, B>(value: B) -> Dom
216where
217    A: AsStr,
218    B: Signal<Item = A> + 'static,
219{
220    let mut callbacks = Callbacks::new();
221
222    let element = make_text_signal(&mut callbacks, value);
223
224    Dom {
225        element: element.into(),
226        callbacks: callbacks,
227    }
228}
229
230// TODO better warning message for must_use
231#[must_use]
232#[derive(Debug)]
233pub struct Dom {
234    pub(crate) element: Node,
235    pub(crate) callbacks: Callbacks,
236}
237
238impl Dom {
239    #[inline]
240    pub fn new(element: Node) -> Self {
241        Self {
242            element,
243            callbacks: Callbacks::new(),
244        }
245    }
246
247    #[inline]
248    pub fn empty() -> Self {
249        Self::new(bindings::create_empty_node())
250    }
251
252    #[deprecated(
253        since = "0.5.15",
254        note = "Store the data explicitly in a component struct instead"
255    )]
256    #[inline]
257    pub fn with_state<A, F>(mut state: A, initializer: F) -> Dom
258    where
259        A: 'static,
260        F: FnOnce(&mut A) -> Dom,
261    {
262        let mut dom = initializer(&mut state);
263
264        dom.callbacks.after_remove(ValueDiscard::new(state));
265
266        dom
267    }
268}
269
270#[inline]
271fn create_element<A>(name: &str) -> A
272where
273    A: JsCast,
274{
275    // TODO use unchecked_into in release mode ?
276    bindings::create_element(intern(name))
277        .dyn_into()
278        .unwrap_throw()
279}
280
281#[inline]
282fn create_element_ns<A>(name: &str, namespace: &str) -> A
283where
284    A: JsCast,
285{
286    // TODO use unchecked_into in release mode ?
287    bindings::create_element_ns(intern(namespace), intern(name))
288        .dyn_into()
289        .unwrap_throw()
290}
291
292// TODO should this inline ?
293fn set_option<A, B, C, D, F>(element: A, callbacks: &mut Callbacks, value: D, mut f: F)
294where
295    A: 'static,
296    C: OptionStr<Output = B>,
297    D: Signal<Item = C> + 'static,
298    F: FnMut(&A, Option<B>) + 'static,
299{
300    let mut is_set = false;
301
302    callbacks.after_remove(for_each(value, move |value| {
303        let value = value.into_option();
304
305        if value.is_some() {
306            is_set = true;
307        } else if is_set {
308            is_set = false;
309        } else {
310            return;
311        }
312
313        f(&element, value);
314    }));
315}
316
317// TODO should this inline ?
318fn set_style<A, B>(style: &CssStyleDeclaration, name: &A, value: B, important: bool)
319where
320    A: MultiStr,
321    B: MultiStr,
322{
323    let mut names = vec![];
324    let mut values = vec![];
325
326    fn try_set_style(
327        style: &CssStyleDeclaration,
328        names: &mut Vec<String>,
329        values: &mut Vec<String>,
330        name: &str,
331        value: &str,
332        important: bool,
333    ) -> Option<()> {
334        assert!(value != "");
335
336        // TODO handle browser prefixes ?
337        bindings::remove_style(style, name);
338
339        bindings::set_style(style, name, value, important);
340
341        let is_changed = bindings::get_style(style, name) != "";
342
343        if is_changed {
344            Some(())
345        } else {
346            names.push(String::from(name));
347            values.push(String::from(value));
348            None
349        }
350    }
351
352    let okay = name.find_map(|name| {
353        let name: &str = intern(name);
354
355        value.find_map(|value| {
356            // TODO should this intern ?
357            try_set_style(style, &mut names, &mut values, &name, &value, important)
358        })
359    });
360
361    if let None = okay {
362        if cfg!(debug_assertions) {
363            // TODO maybe make this configurable
364            panic!(
365                "style is incorrect:\n  names: {}\n  values: {}",
366                names.join(", "),
367                values.join(", ")
368            );
369        }
370    }
371}
372
373// TODO should this inline ?
374fn set_style_signal<A, B, C, D>(
375    style: CssStyleDeclaration,
376    callbacks: &mut Callbacks,
377    name: A,
378    value: D,
379    important: bool,
380) where
381    A: MultiStr + 'static,
382    B: MultiStr,
383    C: OptionStr<Output = B>,
384    D: Signal<Item = C> + 'static,
385{
386    set_option(style, callbacks, value, move |style, value| {
387        match value {
388            Some(value) => {
389                // TODO should this intern or not ?
390                set_style(style, &name, value, important);
391            }
392            None => {
393                name.each(|name| {
394                    // TODO handle browser prefixes ?
395                    bindings::remove_style(style, intern(name));
396                });
397            }
398        }
399    });
400}
401
402// TODO should this inline ?
403fn set_style_unchecked_signal<A, B, C, D>(
404    style: CssStyleDeclaration,
405    callbacks: &mut Callbacks,
406    name: A,
407    value: D,
408    important: bool,
409) where
410    A: AsStr + 'static,
411    B: AsStr,
412    C: OptionStr<Output = B>,
413    D: Signal<Item = C> + 'static,
414{
415    set_option(style, callbacks, value, move |style, value| match value {
416        Some(value) => {
417            name.with_str(|name| {
418                let name: &str = intern(name);
419
420                value.with_str(|value| {
421                    bindings::set_style(style, name, value, important);
422                });
423            });
424        }
425        None => {
426            name.with_str(|name| {
427                bindings::remove_style(style, intern(name));
428            });
429        }
430    });
431}
432
433// TODO check that the property *actually* was changed ?
434// TODO maybe use AsRef<Object> ?
435// TODO should this inline ?
436fn set_property<A, B, C>(element: &A, name: &B, value: C)
437where
438    A: AsRef<JsValue>,
439    B: MultiStr,
440    C: Into<JsValue>,
441{
442    let element = element.as_ref();
443    let value = value.into();
444
445    name.each(|name| {
446        bindings::set_property(element, intern(name), &value);
447    });
448}
449
450#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
451pub struct EventOptions {
452    pub bubbles: bool,
453    pub preventable: bool,
454}
455
456impl EventOptions {
457    pub fn bubbles() -> Self {
458        Self {
459            bubbles: true,
460            preventable: false,
461        }
462    }
463
464    pub fn preventable() -> Self {
465        Self {
466            bubbles: false,
467            preventable: true,
468        }
469    }
470}
471
472impl Default for EventOptions {
473    fn default() -> Self {
474        Self {
475            bubbles: false,
476            preventable: false,
477        }
478    }
479}
480
481// TODO better warning message for must_use
482#[must_use]
483pub struct DomBuilder<A> {
484    element: A,
485    callbacks: Callbacks,
486}
487
488impl<A> DomBuilder<A>
489where
490    A: JsCast,
491{
492    #[inline]
493    pub fn new_html(name: &str) -> Self {
494        Self::new(create_element(name))
495    }
496
497    #[inline]
498    pub fn new_svg(name: &str) -> Self {
499        Self::new(create_element_ns(name, SVG_NAMESPACE))
500    }
501}
502
503impl<A> DomBuilder<A> {
504    #[inline]
505    #[doc(hidden)]
506    pub fn __internal_transfer_callbacks<B>(mut self, mut shadow: DomBuilder<B>) -> Self {
507        self.callbacks
508            .after_insert
509            .append(&mut shadow.callbacks.after_insert);
510        self.callbacks
511            .after_remove
512            .append(&mut shadow.callbacks.after_remove);
513        self
514    }
515
516    #[inline]
517    pub fn new(value: A) -> Self {
518        Self {
519            element: value,
520            callbacks: Callbacks::new(),
521        }
522    }
523
524    #[inline]
525    fn _event<T, F>(&mut self, element: EventTarget, options: &EventOptions, listener: F)
526    where
527        T: StaticEvent,
528        F: FnMut(T) + 'static,
529    {
530        self.callbacks.after_remove(on(element, options, listener));
531    }
532
533    // TODO add this to the StylesheetBuilder and ClassBuilder too
534    #[inline]
535    pub fn global_event_with_options<T, F>(mut self, options: &EventOptions, listener: F) -> Self
536    where
537        T: StaticEvent,
538        F: FnMut(T) + 'static,
539    {
540        self._event(bindings::window_event_target(), options, listener);
541        self
542    }
543
544    // TODO add this to the StylesheetBuilder and ClassBuilder too
545    #[inline]
546    pub fn global_event<T, F>(self, listener: F) -> Self
547    where
548        T: StaticEvent,
549        F: FnMut(T) + 'static,
550    {
551        self.global_event_with_options(&EventOptions::default(), listener)
552    }
553
554    // TODO add this to the StylesheetBuilder and ClassBuilder too
555    #[deprecated(since = "0.5.21", note = "Use global_event_with_options instead")]
556    #[inline]
557    pub fn global_event_preventable<T, F>(self, listener: F) -> Self
558    where
559        T: StaticEvent,
560        F: FnMut(T) + 'static,
561    {
562        self.global_event_with_options(&EventOptions::preventable(), listener)
563    }
564
565    #[inline]
566    pub fn future<F>(mut self, future: F) -> Self
567    where
568        F: Future<Output = ()> + 'static,
569    {
570        self.callbacks
571            .after_remove(DiscardOnDrop::leak(spawn_future(future)));
572        self
573    }
574
575    #[inline]
576    pub fn apply<F>(self, f: F) -> Self
577    where
578        F: FnOnce(Self) -> Self,
579    {
580        f(self)
581    }
582
583    #[inline]
584    pub fn apply_if<F>(self, test: bool, f: F) -> Self
585    where
586        F: FnOnce(Self) -> Self,
587    {
588        if test {
589            f(self)
590        } else {
591            self
592        }
593    }
594}
595
596impl<A> DomBuilder<A>
597where
598    A: Clone,
599{
600    #[inline]
601    #[doc(hidden)]
602    pub fn __internal_element(&self) -> A {
603        self.element.clone()
604    }
605
606    #[deprecated(since = "0.5.1", note = "Use the with_node macro instead")]
607    #[inline]
608    pub fn with_element<B, F>(self, f: F) -> B
609    where
610        F: FnOnce(Self, A) -> B,
611    {
612        let element = self.element.clone();
613        f(self, element)
614    }
615
616    #[deprecated(since = "0.5.20", note = "Use the with_node macro instead")]
617    #[inline]
618    pub fn before_inserted<F>(self, f: F) -> Self
619    where
620        F: FnOnce(A),
621    {
622        let element = self.element.clone();
623        f(element);
624        self
625    }
626}
627
628impl<A> DomBuilder<A>
629where
630    A: Clone + 'static,
631{
632    #[inline]
633    pub fn after_inserted<F>(mut self, f: F) -> Self
634    where
635        F: FnOnce(A) + 'static,
636    {
637        let element = self.element.clone();
638        self.callbacks.after_insert(move |_| f(element));
639        self
640    }
641
642    #[inline]
643    pub fn after_removed<F>(mut self, f: F) -> Self
644    where
645        F: FnOnce(A) + 'static,
646    {
647        let element = self.element.clone();
648        self.callbacks
649            .after_remove(FnDiscard::new(move || f(element)));
650        self
651    }
652}
653
654impl<A> DomBuilder<A>
655where
656    A: Into<Node>,
657{
658    #[inline]
659    pub fn into_dom(self) -> Dom {
660        Dom {
661            element: self.element.into(),
662            callbacks: self.callbacks,
663        }
664    }
665}
666
667impl<A> DomBuilder<A>
668where
669    A: AsRef<JsValue>,
670{
671    #[inline]
672    pub fn prop<B, C>(self, name: B, value: C) -> Self
673    where
674        B: MultiStr,
675        C: Into<JsValue>,
676    {
677        self.property(name, value)
678    }
679
680    /// The same as the [`prop`](#method.prop) method.
681    #[inline]
682    pub fn property<B, C>(self, name: B, value: C) -> Self
683    where
684        B: MultiStr,
685        C: Into<JsValue>,
686    {
687        set_property(&self.element, &name, value);
688        self
689    }
690}
691
692impl<A> DomBuilder<A>
693where
694    A: AsRef<JsValue>,
695{
696    // TODO should this inline ?
697    fn set_property_signal<B, C, D>(&mut self, name: B, value: D)
698    where
699        B: MultiStr + 'static,
700        C: Into<JsValue>,
701        D: Signal<Item = C> + 'static,
702    {
703        let element = self.element.as_ref().clone();
704
705        self.callbacks.after_remove(for_each(value, move |value| {
706            set_property(&element, &name, value);
707        }));
708    }
709
710    #[inline]
711    pub fn prop_signal<B, C, D>(self, name: B, value: D) -> Self
712    where
713        B: MultiStr + 'static,
714        C: Into<JsValue>,
715        D: Signal<Item = C> + 'static,
716    {
717        self.property_signal(name, value)
718    }
719
720    /// The same as the [`prop_signal`](#method.prop_signal) method.
721    #[inline]
722    pub fn property_signal<B, C, D>(mut self, name: B, value: D) -> Self
723    where
724        B: MultiStr + 'static,
725        C: Into<JsValue>,
726        D: Signal<Item = C> + 'static,
727    {
728        self.set_property_signal(name, value);
729        self
730    }
731}
732
733impl<A> DomBuilder<A>
734where
735    A: AsRef<EventTarget>,
736{
737    #[inline]
738    pub fn event_with_options<T, F>(mut self, options: &EventOptions, listener: F) -> Self
739    where
740        T: StaticEvent,
741        F: FnMut(T) + 'static,
742    {
743        // TODO can this clone be avoided ?
744        self._event(self.element.as_ref().clone(), options, listener);
745        self
746    }
747
748    #[inline]
749    pub fn event<T, F>(self, listener: F) -> Self
750    where
751        T: StaticEvent,
752        F: FnMut(T) + 'static,
753    {
754        self.event_with_options(&EventOptions::default(), listener)
755    }
756
757    #[deprecated(since = "0.5.21", note = "Use event_with_options instead")]
758    #[inline]
759    pub fn event_preventable<T, F>(self, listener: F) -> Self
760    where
761        T: StaticEvent,
762        F: FnMut(T) + 'static,
763    {
764        self.event_with_options(&EventOptions::preventable(), listener)
765    }
766}
767
768impl<A> DomBuilder<A>
769where
770    A: AsRef<Node>,
771{
772    #[inline]
773    pub fn text(self, value: &str) -> Self {
774        // TODO should this intern ?
775        bindings::append_child(self.element.as_ref(), &bindings::create_text_node(value));
776        self
777    }
778
779    #[inline]
780    pub fn text_signal<B, C>(mut self, value: C) -> Self
781    where
782        B: AsStr,
783        C: Signal<Item = B> + 'static,
784    {
785        let element = make_text_signal(&mut self.callbacks, value);
786        bindings::append_child(self.element.as_ref(), &element);
787        self
788    }
789
790    #[inline]
791    pub fn child<B: BorrowMut<Dom>>(mut self, mut child: B) -> Self {
792        operations::insert_children_one(
793            self.element.as_ref(),
794            &mut self.callbacks,
795            child.borrow_mut(),
796        );
797        self
798    }
799
800    #[inline]
801    pub fn child_signal<B>(mut self, child: B) -> Self
802    where
803        B: Signal<Item = Option<Dom>> + 'static,
804    {
805        operations::insert_child_signal(self.element.as_ref().clone(), &mut self.callbacks, child);
806        self
807    }
808
809    // TODO figure out how to make this owned rather than &mut
810    #[inline]
811    pub fn children<B: BorrowMut<Dom>, C: IntoIterator<Item = B>>(mut self, children: C) -> Self {
812        operations::insert_children_iter(self.element.as_ref(), &mut self.callbacks, children);
813        self
814    }
815
816    #[inline]
817    pub fn children_signal_vec<B>(mut self, children: B) -> Self
818    where
819        B: SignalVec<Item = Dom> + 'static,
820    {
821        operations::insert_children_signal_vec(
822            self.element.as_ref().clone(),
823            &mut self.callbacks,
824            children,
825        );
826        self
827    }
828}
829
830impl<A> DomBuilder<A>
831where
832    A: AsRef<Element>,
833{
834    #[inline]
835    #[doc(hidden)]
836    pub fn __internal_shadow_root(&self, mode: ShadowRootMode) -> DomBuilder<ShadowRoot> {
837        let shadow = self
838            .element
839            .as_ref()
840            .attach_shadow(&ShadowRootInit::new(mode))
841            .unwrap_throw();
842        DomBuilder::new(shadow)
843    }
844
845    #[inline]
846    pub fn attr<B>(self, name: B, value: &str) -> Self
847    where
848        B: MultiStr,
849    {
850        self.attribute(name, value)
851    }
852
853    /// The same as the [`attr`](#method.attr) method.
854    #[inline]
855    pub fn attribute<B>(self, name: B, value: &str) -> Self
856    where
857        B: MultiStr,
858    {
859        let element = self.element.as_ref();
860        // TODO should this intern the value ?
861        let value: &str = intern(value);
862
863        name.each(|name| {
864            bindings::set_attribute(element, intern(name), &value);
865        });
866
867        self
868    }
869
870    #[inline]
871    pub fn attr_ns<B>(self, namespace: &str, name: B, value: &str) -> Self
872    where
873        B: MultiStr,
874    {
875        self.attribute_namespace(namespace, name, value)
876    }
877
878    /// The same as the [`attr_ns`](#method.attr_ns) method.
879    #[inline]
880    pub fn attribute_namespace<B>(self, namespace: &str, name: B, value: &str) -> Self
881    where
882        B: MultiStr,
883    {
884        let element = self.element.as_ref();
885        let namespace: &str = intern(namespace);
886        // TODO should this intern the value ?
887        let value: &str = intern(value);
888
889        name.each(|name| {
890            bindings::set_attribute_ns(element, &namespace, intern(name), &value);
891        });
892
893        self
894    }
895
896    #[inline]
897    pub fn class<B>(self, name: B) -> Self
898    where
899        B: MultiStr,
900    {
901        let classes = self.element.as_ref().class_list();
902
903        name.each(|name| {
904            bindings::add_class(&classes, intern(name));
905        });
906
907        self
908    }
909
910    // TODO make this more efficient ?
911    #[inline]
912    pub fn visible(self, value: bool) -> Self {
913        if value {
914            // TODO remove the class somehow ?
915            self
916        } else {
917            self.class(&*HIDDEN_CLASS)
918        }
919    }
920}
921
922impl<A> DomBuilder<A>
923where
924    A: AsRef<Element>,
925{
926    // TODO should this inline ?
927    fn set_attribute_signal<B, C, D, E>(&mut self, name: B, value: E)
928    where
929        B: MultiStr + 'static,
930        C: AsStr,
931        D: OptionStr<Output = C>,
932        E: Signal<Item = D> + 'static,
933    {
934        set_option(
935            self.element.as_ref().clone(),
936            &mut self.callbacks,
937            value,
938            move |element, value| {
939                match value {
940                    Some(value) => {
941                        value.with_str(|value| {
942                            name.each(|name| {
943                                // TODO should this intern the value ?
944                                bindings::set_attribute(element, intern(name), &value);
945                            });
946                        });
947                    }
948                    None => {
949                        name.each(|name| {
950                            bindings::remove_attribute(element, intern(name));
951                        });
952                    }
953                }
954            },
955        );
956    }
957
958    #[inline]
959    pub fn attr_signal<B, C, D, E>(self, name: B, value: E) -> Self
960    where
961        B: MultiStr + 'static,
962        C: AsStr,
963        D: OptionStr<Output = C>,
964        E: Signal<Item = D> + 'static,
965    {
966        self.attribute_signal(name, value)
967    }
968
969    /// The same as the [`attr_signal`](#method.attr_signal) method.
970    #[inline]
971    pub fn attribute_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
972    where
973        B: MultiStr + 'static,
974        C: AsStr,
975        D: OptionStr<Output = C>,
976        E: Signal<Item = D> + 'static,
977    {
978        self.set_attribute_signal(name, value);
979        self
980    }
981
982    // TODO should this inline ?
983    fn set_attribute_namespace_signal<B, C, D, E>(&mut self, namespace: &str, name: B, value: E)
984    where
985        B: MultiStr + 'static,
986        C: AsStr,
987        D: OptionStr<Output = C>,
988        E: Signal<Item = D> + 'static,
989    {
990        // TODO avoid this to_owned by using Into<Cow<'static str>>
991        let namespace: String = intern(namespace).to_owned();
992
993        set_option(
994            self.element.as_ref().clone(),
995            &mut self.callbacks,
996            value,
997            move |element, value| {
998                match value {
999                    Some(value) => {
1000                        value.with_str(|value| {
1001                            name.each(|name| {
1002                                // TODO should this intern the value ?
1003                                bindings::set_attribute_ns(
1004                                    element,
1005                                    &namespace,
1006                                    intern(name),
1007                                    &value,
1008                                );
1009                            });
1010                        });
1011                    }
1012                    None => {
1013                        name.each(|name| {
1014                            bindings::remove_attribute_ns(element, &namespace, intern(name));
1015                        });
1016                    }
1017                }
1018            },
1019        );
1020    }
1021
1022    #[inline]
1023    pub fn attr_ns_signal<B, C, D, E>(self, namespace: &str, name: B, value: E) -> Self
1024    where
1025        B: MultiStr + 'static,
1026        C: AsStr,
1027        D: OptionStr<Output = C>,
1028        E: Signal<Item = D> + 'static,
1029    {
1030        self.attribute_namespace_signal(namespace, name, value)
1031    }
1032
1033    /// The same as the [`attr_ns_signal`](#method.attr_ns_signal) method.
1034    #[inline]
1035    pub fn attribute_namespace_signal<B, C, D, E>(
1036        mut self,
1037        namespace: &str,
1038        name: B,
1039        value: E,
1040    ) -> Self
1041    where
1042        B: MultiStr + 'static,
1043        C: AsStr,
1044        D: OptionStr<Output = C>,
1045        E: Signal<Item = D> + 'static,
1046    {
1047        self.set_attribute_namespace_signal(namespace, name, value);
1048        self
1049    }
1050
1051    // TODO should this inline ?
1052    fn set_class_signal<B, C>(&mut self, name: B, value: C)
1053    where
1054        B: MultiStr + 'static,
1055        C: Signal<Item = bool> + 'static,
1056    {
1057        let element = self.element.as_ref().class_list();
1058
1059        let mut is_set = false;
1060
1061        self.callbacks.after_remove(for_each(value, move |value| {
1062            if value {
1063                if !is_set {
1064                    is_set = true;
1065
1066                    name.each(|name| {
1067                        bindings::add_class(&element, intern(name));
1068                    });
1069                }
1070            } else {
1071                if is_set {
1072                    is_set = false;
1073
1074                    name.each(|name| {
1075                        bindings::remove_class(&element, intern(name));
1076                    });
1077                }
1078            }
1079        }));
1080    }
1081
1082    #[inline]
1083    pub fn class_signal<B, C>(mut self, name: B, value: C) -> Self
1084    where
1085        B: MultiStr + 'static,
1086        C: Signal<Item = bool> + 'static,
1087    {
1088        self.set_class_signal(name, value);
1089        self
1090    }
1091
1092    // TODO make this more efficient ?
1093    #[inline]
1094    pub fn visible_signal<B>(self, value: B) -> Self
1095    where
1096        B: Signal<Item = bool> + 'static,
1097    {
1098        self.class_signal(&*HIDDEN_CLASS, not(value))
1099    }
1100
1101    // TODO use OptionStr ?
1102    // TODO should this inline ?
1103    fn set_scroll_signal<B, F>(&mut self, signal: B, mut f: F)
1104    where
1105        B: Signal<Item = Option<i32>> + 'static,
1106        F: FnMut(&Element, i32) + 'static,
1107    {
1108        let element: Element = self.element.as_ref().clone();
1109
1110        // This needs to use `after_insert` because scrolling an element before it is in the DOM has no effect
1111        self.callbacks.after_insert(move |callbacks| {
1112            callbacks.after_remove(for_each(signal, move |value| {
1113                if let Some(value) = value {
1114                    f(&element, value);
1115                }
1116            }));
1117        });
1118    }
1119
1120    // TODO rename to scroll_x_signal ?
1121    #[inline]
1122    pub fn scroll_left_signal<B>(mut self, signal: B) -> Self
1123    where
1124        B: Signal<Item = Option<i32>> + 'static,
1125    {
1126        // TODO bindings function for this ?
1127        self.set_scroll_signal(signal, Element::set_scroll_left);
1128        self
1129    }
1130
1131    // TODO rename to scroll_y_signal ?
1132    #[inline]
1133    pub fn scroll_top_signal<B>(mut self, signal: B) -> Self
1134    where
1135        B: Signal<Item = Option<i32>> + 'static,
1136    {
1137        // TODO bindings function for this ?
1138        self.set_scroll_signal(signal, Element::set_scroll_top);
1139        self
1140    }
1141}
1142
1143impl<A> DomBuilder<A>
1144where
1145    A: AsRef<HtmlElement>,
1146{
1147    #[inline]
1148    pub fn style<B, C>(self, name: B, value: C) -> Self
1149    where
1150        B: MultiStr,
1151        C: MultiStr,
1152    {
1153        set_style(&self.element.as_ref().style(), &name, value, false);
1154        self
1155    }
1156
1157    #[inline]
1158    pub fn style_important<B, C>(self, name: B, value: C) -> Self
1159    where
1160        B: MultiStr,
1161        C: MultiStr,
1162    {
1163        set_style(&self.element.as_ref().style(), &name, value, true);
1164        self
1165    }
1166
1167    #[inline]
1168    pub fn style_unchecked<B, C>(self, name: B, value: C) -> Self
1169    where
1170        B: AsStr,
1171        C: AsStr,
1172    {
1173        name.with_str(|name| {
1174            value.with_str(|value| {
1175                bindings::set_style(&self.element.as_ref().style(), intern(name), value, false);
1176            });
1177        });
1178        self
1179    }
1180}
1181
1182impl<A> DomBuilder<A>
1183where
1184    A: AsRef<HtmlElement>,
1185{
1186    #[inline]
1187    pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1188    where
1189        B: MultiStr + 'static,
1190        C: MultiStr,
1191        D: OptionStr<Output = C>,
1192        E: Signal<Item = D> + 'static,
1193    {
1194        set_style_signal(
1195            self.element.as_ref().style(),
1196            &mut self.callbacks,
1197            name,
1198            value,
1199            false,
1200        );
1201        self
1202    }
1203
1204    #[inline]
1205    pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1206    where
1207        B: MultiStr + 'static,
1208        C: MultiStr,
1209        D: OptionStr<Output = C>,
1210        E: Signal<Item = D> + 'static,
1211    {
1212        set_style_signal(
1213            self.element.as_ref().style(),
1214            &mut self.callbacks,
1215            name,
1216            value,
1217            true,
1218        );
1219        self
1220    }
1221
1222    #[inline]
1223    pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1224    where
1225        B: AsStr + 'static,
1226        C: AsStr,
1227        D: OptionStr<Output = C>,
1228        E: Signal<Item = D> + 'static,
1229    {
1230        set_style_unchecked_signal(
1231            self.element.as_ref().style(),
1232            &mut self.callbacks,
1233            name,
1234            value,
1235            false,
1236        );
1237        self
1238    }
1239
1240    // TODO remove the `value` argument ?
1241    #[inline]
1242    pub fn focused(mut self, value: bool) -> Self {
1243        let element = self.element.as_ref().clone();
1244
1245        // This needs to use `after_insert` because calling `.focus()` on an element before it is in the DOM has no effect
1246        self.callbacks.after_insert(move |_| {
1247            // TODO avoid updating if the focused state hasn't changed ?
1248            if value {
1249                bindings::focus(&element);
1250            } else {
1251                bindings::blur(&element);
1252            }
1253        });
1254
1255        self
1256    }
1257
1258    // TODO should this inline ?
1259    fn set_focused_signal<B>(&mut self, value: B)
1260    where
1261        B: Signal<Item = bool> + 'static,
1262    {
1263        let element = self.element.as_ref().clone();
1264
1265        // This needs to use `after_insert` because calling `.focus()` on an element before it is in the DOM has no effect
1266        self.callbacks.after_insert(move |callbacks| {
1267            // TODO verify that this is correct under all circumstances
1268            callbacks.after_remove(for_each(value, move |value| {
1269                // TODO avoid updating if the focused state hasn't changed ?
1270                if value {
1271                    bindings::focus(&element);
1272                } else {
1273                    bindings::blur(&element);
1274                }
1275            }));
1276        });
1277    }
1278
1279    #[inline]
1280    pub fn focused_signal<B>(mut self, value: B) -> Self
1281    where
1282        B: Signal<Item = bool> + 'static,
1283    {
1284        self.set_focused_signal(value);
1285        self
1286    }
1287}
1288
1289// TODO better warning message for must_use
1290#[must_use]
1291pub struct StylesheetBuilder {
1292    element: CssStyleDeclaration,
1293    callbacks: Callbacks,
1294}
1295
1296// TODO remove the CssStyleRule when this is discarded
1297impl StylesheetBuilder {
1298    // TODO should this inline ?
1299    #[doc(hidden)]
1300    #[inline]
1301    pub fn __internal_new<A>(selector: A) -> Self
1302    where
1303        A: MultiStr,
1304    {
1305        // TODO can this be made faster ?
1306        // TODO somehow share this safely between threads ?
1307        thread_local! {
1308            static STYLESHEET: CssStyleSheet = bindings::create_stylesheet();
1309        }
1310
1311        fn try_make(
1312            stylesheet: &CssStyleSheet,
1313            selector: &str,
1314            selectors: &mut Vec<String>,
1315        ) -> Option<CssStyleDeclaration> {
1316            // TODO maybe intern the selector ?
1317            if let Ok(declaration) = bindings::make_style_rule(stylesheet, selector) {
1318                Some(declaration.style())
1319            } else {
1320                selectors.push(String::from(selector));
1321                None
1322            }
1323        }
1324
1325        let element = STYLESHEET.with(move |stylesheet| {
1326            let mut selectors = vec![];
1327
1328            let okay = selector.find_map(|selector| try_make(stylesheet, selector, &mut selectors));
1329
1330            if let Some(okay) = okay {
1331                okay
1332            } else {
1333                // TODO maybe make this configurable
1334                panic!("selectors are incorrect:\n  {}", selectors.join("\n  "));
1335            }
1336        });
1337
1338        Self {
1339            element,
1340            callbacks: Callbacks::new(),
1341        }
1342    }
1343
1344    #[inline]
1345    pub fn style<B, C>(self, name: B, value: C) -> Self
1346    where
1347        B: MultiStr,
1348        C: MultiStr,
1349    {
1350        set_style(&self.element, &name, value, false);
1351        self
1352    }
1353
1354    #[inline]
1355    pub fn style_important<B, C>(self, name: B, value: C) -> Self
1356    where
1357        B: MultiStr,
1358        C: MultiStr,
1359    {
1360        set_style(&self.element, &name, value, true);
1361        self
1362    }
1363
1364    #[inline]
1365    pub fn style_unchecked<B, C>(self, name: B, value: C) -> Self
1366    where
1367        B: AsStr,
1368        C: AsStr,
1369    {
1370        name.with_str(|name| {
1371            value.with_str(|value| {
1372                bindings::set_style(&self.element, intern(name), value, false);
1373            });
1374        });
1375        self
1376    }
1377
1378    #[inline]
1379    pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1380    where
1381        B: MultiStr + 'static,
1382        C: MultiStr,
1383        D: OptionStr<Output = C>,
1384        E: Signal<Item = D> + 'static,
1385    {
1386        set_style_signal(
1387            self.element.clone(),
1388            &mut self.callbacks,
1389            name,
1390            value,
1391            false,
1392        );
1393        self
1394    }
1395
1396    #[inline]
1397    pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1398    where
1399        B: MultiStr + 'static,
1400        C: MultiStr,
1401        D: OptionStr<Output = C>,
1402        E: Signal<Item = D> + 'static,
1403    {
1404        set_style_signal(self.element.clone(), &mut self.callbacks, name, value, true);
1405        self
1406    }
1407
1408    #[inline]
1409    pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1410    where
1411        B: AsStr + 'static,
1412        C: AsStr,
1413        D: OptionStr<Output = C>,
1414        E: Signal<Item = D> + 'static,
1415    {
1416        set_style_unchecked_signal(
1417            self.element.clone(),
1418            &mut self.callbacks,
1419            name,
1420            value,
1421            false,
1422        );
1423        self
1424    }
1425
1426    // TODO return a Handle
1427    #[inline]
1428    #[doc(hidden)]
1429    pub fn __internal_done(mut self) {
1430        self.callbacks.trigger_after_insert();
1431
1432        // This prevents it from triggering after_remove
1433        self.callbacks.leak();
1434    }
1435}
1436
1437// TODO better warning message for must_use
1438#[must_use]
1439pub struct ClassBuilder {
1440    stylesheet: StylesheetBuilder,
1441    class_name: String,
1442}
1443
1444impl ClassBuilder {
1445    #[doc(hidden)]
1446    #[inline]
1447    pub fn __internal_new() -> Self {
1448        let class_name = __internal::make_class_id();
1449
1450        Self {
1451            // TODO make this more efficient ?
1452            stylesheet: StylesheetBuilder::__internal_new(&format!(".{}", class_name)),
1453            class_name,
1454        }
1455    }
1456
1457    #[doc(hidden)]
1458    #[inline]
1459    pub fn __internal_class_name(&self) -> &str {
1460        &self.class_name
1461    }
1462
1463    #[inline]
1464    pub fn style<B, C>(mut self, name: B, value: C) -> Self
1465    where
1466        B: MultiStr,
1467        C: MultiStr,
1468    {
1469        self.stylesheet = self.stylesheet.style(name, value);
1470        self
1471    }
1472
1473    #[inline]
1474    pub fn style_important<B, C>(mut self, name: B, value: C) -> Self
1475    where
1476        B: MultiStr,
1477        C: MultiStr,
1478    {
1479        self.stylesheet = self.stylesheet.style_important(name, value);
1480        self
1481    }
1482
1483    #[inline]
1484    pub fn style_unchecked<B, C>(mut self, name: B, value: C) -> Self
1485    where
1486        B: AsStr,
1487        C: AsStr,
1488    {
1489        self.stylesheet = self.stylesheet.style_unchecked(name, value);
1490        self
1491    }
1492
1493    #[inline]
1494    pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1495    where
1496        B: MultiStr + 'static,
1497        C: MultiStr,
1498        D: OptionStr<Output = C>,
1499        E: Signal<Item = D> + 'static,
1500    {
1501        self.stylesheet = self.stylesheet.style_signal(name, value);
1502        self
1503    }
1504
1505    #[inline]
1506    pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1507    where
1508        B: MultiStr + 'static,
1509        C: MultiStr,
1510        D: OptionStr<Output = C>,
1511        E: Signal<Item = D> + 'static,
1512    {
1513        self.stylesheet = self.stylesheet.style_important_signal(name, value);
1514        self
1515    }
1516
1517    #[inline]
1518    pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1519    where
1520        B: AsStr + 'static,
1521        C: AsStr,
1522        D: OptionStr<Output = C>,
1523        E: Signal<Item = D> + 'static,
1524    {
1525        self.stylesheet = self.stylesheet.style_unchecked_signal(name, value);
1526        self
1527    }
1528
1529    // TODO return a Handle ?
1530    #[doc(hidden)]
1531    #[inline]
1532    pub fn __internal_done(self) -> String {
1533        self.stylesheet.__internal_done();
1534        self.class_name
1535    }
1536}
1537
1538#[doc(hidden)]
1539pub mod __internal {
1540    use crate::traits::MultiStr;
1541    use std::sync::atomic::{AtomicU32, Ordering};
1542
1543    pub use web_sys::HtmlElement;
1544    pub use web_sys::SvgElement;
1545
1546    pub fn make_class_id() -> String {
1547        // TODO replace this with a global counter in JavaScript ?
1548        // TODO can this be made more efficient ?
1549        static CLASS_ID: AtomicU32 = AtomicU32::new(0);
1550
1551        // TODO check for overflow ?
1552        // TODO should this be SeqCst ?
1553        let id = CLASS_ID.fetch_add(1, Ordering::Relaxed);
1554
1555        // TODO make this more efficient ?
1556        format!("__class_{}__", id)
1557    }
1558
1559    pub struct Pseudo<'a, A> {
1560        class_name: &'a str,
1561        pseudos: A,
1562    }
1563
1564    impl<'a, A> Pseudo<'a, A>
1565    where
1566        A: MultiStr,
1567    {
1568        #[inline]
1569        pub fn new(class_name: &'a str, pseudos: A) -> Self {
1570            Self {
1571                class_name,
1572                pseudos,
1573            }
1574        }
1575    }
1576
1577    impl<'a, A> MultiStr for Pseudo<'a, A>
1578    where
1579        A: MultiStr,
1580    {
1581        #[inline]
1582        fn find_map<B, F>(&self, mut f: F) -> Option<B>
1583        where
1584            F: FnMut(&str) -> Option<B>,
1585        {
1586            self.pseudos
1587                .find_map(|x| f(&format!(".{}{}", self.class_name, x)))
1588        }
1589    }
1590}
1591
1592#[cfg(test)]
1593mod tests {
1594    use super::{text_signal, DomBuilder, RefFn};
1595    use crate::{html, shadow_root, with_cfg, ShadowRootMode};
1596    use futures_signals::signal::{always, SignalExt};
1597    use once_cell::sync::Lazy;
1598    use web_sys::HtmlElement;
1599
1600    #[test]
1601    fn apply() {
1602        let a: DomBuilder<HtmlElement> = DomBuilder::new_html("div");
1603
1604        fn my_mixin<A: AsRef<HtmlElement>>(builder: DomBuilder<A>) -> DomBuilder<A> {
1605            builder.style("foo", "bar")
1606        }
1607
1608        let _ = a.apply(my_mixin);
1609    }
1610
1611    #[test]
1612    fn children_mut() {
1613        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div").children(&mut [
1614            DomBuilder::<HtmlElement>::new_html("div").into_dom(),
1615            DomBuilder::<HtmlElement>::new_html("div").into_dom(),
1616            DomBuilder::<HtmlElement>::new_html("div").into_dom(),
1617        ]);
1618    }
1619
1620    #[test]
1621    fn children_value() {
1622        let v: Vec<u32> = vec![];
1623
1624        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div").children(
1625            v.iter()
1626                .map(|_| DomBuilder::<HtmlElement>::new_html("div").into_dom()),
1627        );
1628    }
1629
1630    #[test]
1631    fn text_signal_types() {
1632        let _ = text_signal(always("foo"));
1633        let _ = text_signal(always("foo".to_owned()));
1634        let _ = text_signal(always("foo".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())));
1635        //text_signal(always(Arc::new("foo")));
1636        //text_signal(always(Arc::new("foo".to_owned())));
1637        //text_signal(always(Rc::new("foo")));
1638        //text_signal(always(Rc::new("foo".to_owned())));
1639        //text_signal(always(Box::new("foo")));
1640        //text_signal(always(Box::new("foo".to_owned())));
1641        //text_signal(always(Cow::Borrowed(&"foo")));
1642        //text_signal(always(Cow::Owned::<String>("foo".to_owned())));
1643    }
1644
1645    #[test]
1646    fn property_signal_types() {
1647        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
1648            .property("foo", "hi")
1649            .property("foo", 5)
1650            .property(["foo", "-webkit-foo", "-ms-foo"], "hi")
1651            .property_signal("foo", always("hi"))
1652            .property_signal("foo", always(5))
1653            .property_signal("foo", always(Some("hi")))
1654            .property_signal(["foo", "-webkit-foo", "-ms-foo"], always("hi"))
1655            .property_signal(["foo", "-webkit-foo", "-ms-foo"], always(5))
1656            .property_signal(["foo", "-webkit-foo", "-ms-foo"], always(Some("hi")));
1657    }
1658
1659    #[test]
1660    fn attribute_signal_types() {
1661        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
1662            .attribute("foo", "hi")
1663            .attribute(["foo", "-webkit-foo", "-ms-foo"], "hi")
1664            .attribute_signal("foo", always("hi"))
1665            .attribute_signal("foo", always(Some("hi")))
1666            .attribute_signal(["foo", "-webkit-foo", "-ms-foo"], always("hi"))
1667            .attribute_signal(["foo", "-webkit-foo", "-ms-foo"], always(Some("hi")));
1668    }
1669
1670    #[test]
1671    fn class_signal_types() {
1672        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
1673            .class("foo")
1674            .class(["foo", "-webkit-foo", "-ms-foo"])
1675            .class_signal("foo", always(true))
1676            .class_signal(["foo", "-webkit-foo", "-ms-foo"], always(true));
1677    }
1678
1679    #[test]
1680    fn style_signal_types() {
1681        static FOO: Lazy<String> = Lazy::new(|| "foo".to_owned());
1682
1683        let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
1684            .style_signal("foo", always("bar"))
1685            .style_signal("foo", always("bar".to_owned()))
1686            .style_signal(
1687                "foo",
1688                always("bar".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())),
1689            )
1690            .style("foo".to_owned(), "bar".to_owned())
1691            .style_signal("foo".to_owned(), always("bar".to_owned()))
1692            .style(&"foo".to_owned(), &"bar".to_owned())
1693            //.style(Box::new("foo".to_owned()), Box::new("bar".to_owned()))
1694            //.style_signal(Box::new("foo".to_owned()), always(Box::new("bar".to_owned())))
1695            .style_signal(&*FOO, always(&*FOO))
1696            //.style(vec!["-moz-foo", "-webkit-foo", "foo"].as_slice(), vec!["bar"].as_slice())
1697            .style_signal(
1698                RefFn::new(vec!["-moz-foo", "-webkit-foo", "foo"], |x| x.as_slice()),
1699                always(RefFn::new(vec!["bar"], |x| x.as_slice())),
1700            )
1701            .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar"))
1702            .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar".to_owned()))
1703            .style_signal(
1704                ["-moz-foo", "-webkit-foo", "foo"],
1705                always("bar".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())),
1706            )
1707            .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(["bar", "qux"]))
1708            .style_signal(
1709                ["-moz-foo", "-webkit-foo", "foo"],
1710                always(["bar".to_owned(), "qux".to_owned()]),
1711            )
1712            //.style_signal(["-moz-foo", "-webkit-foo", "foo"], always(AsSlice::new(["foo", "bar"])))
1713            //.style_signal(["-moz-foo", "-webkit-foo", "foo"], always(("bar".to_owned(), "qux".to_owned())).map(|x| RefFn::new(x, |x| AsSlice::new([x.0.as_str(), x.1.as_str()]))))
1714            .style_signal("foo", always(Some("bar")))
1715            .style_signal("foo", always(Some("bar".to_owned())))
1716            .style_signal(
1717                "foo",
1718                always("bar".to_owned()).map(|x| Some(RefFn::new(x, |x| x.as_str()))),
1719            )
1720            .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(Some("bar")))
1721            .style_signal(
1722                ["-moz-foo", "-webkit-foo", "foo"],
1723                always(Some("bar".to_owned())),
1724            )
1725            .style_signal(
1726                ["-moz-foo", "-webkit-foo", "foo"],
1727                always("bar".to_owned()).map(|x| Some(RefFn::new(x, |x| x.as_str()))),
1728            );
1729    }
1730
1731    #[test]
1732    fn shadow_root() {
1733        let _a = html!("div", {
1734            .shadow_root!(ShadowRootMode::Closed => {
1735                .children(&mut [
1736                    html!("span")
1737                ])
1738            })
1739        });
1740    }
1741
1742    #[test]
1743    fn with_cfg() {
1744        let _a = html!("div", {
1745            .with_cfg!(target_arch = "wasm32", {
1746                .attribute("foo", "bar")
1747            })
1748
1749            .with_cfg!(all(not(foo), bar = "test", feature = "hi"), {
1750                .attribute("foo", "bar")
1751            })
1752        });
1753    }
1754}