1use std::pin::Pin;
2use std::borrow::BorrowMut;
3use std::convert::AsRef;
4use std::future::Future;
5use std::task::{Context, Poll};
6
7use once_cell::sync::Lazy;
8use futures_signals::signal::{Signal, Mutable, MutableSignal, not};
9use futures_signals::signal_vec::SignalVec;
10use futures_util::FutureExt;
11use futures_channel::oneshot;
12use discard::{Discard, DiscardOnDrop};
13use wasm_bindgen::{JsValue, UnwrapThrowExt, JsCast, intern};
14use web_sys::{HtmlElement, Node, EventTarget, Element, CssRule, CssStyleRule, CssStyleSheet, CssStyleDeclaration, ShadowRoot, ShadowRootMode, ShadowRootInit, Text};
15
16use crate::bindings;
17use crate::bindings::WINDOW;
18use crate::callbacks::Callbacks;
19use crate::traits::*;
20use crate::fragment::{Fragment, FragmentBuilder};
21use crate::operations;
22use crate::operations::{for_each, spawn_future};
23use crate::utils::{EventListener, on, RefCounter, MutableListener, UnwrapJsExt, ValueDiscard, FnDiscard};
24
25#[cfg(doc)]
26use crate::fragment;
27
28
29pub struct RefFn<A, B, C> where B: ?Sized, C: Fn(&A) -> &B {
30 value: A,
31 callback: C,
32}
33
34impl<A, B, C> RefFn<A, B, C> where B: ?Sized, C: Fn(&A) -> &B {
35 #[inline]
36 pub fn new(value: A, callback: C) -> Self {
37 Self {
38 value,
39 callback,
40 }
41 }
42
43 #[inline]
44 pub fn call_ref(&self) -> &B {
45 (self.callback)(&self.value)
46 }
47
48 }
60
61const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
80
81pub const HIGHEST_ZINDEX: &str = "2147483647";
83
84
85static HIDDEN_CLASS: Lazy<String> = Lazy::new(|| class! {
86 .style_important("display", "none")
87});
88
89
90pub fn body() -> HtmlElement {
92 bindings::body()
93}
94
95
96pub fn get_id(id: &str) -> Element {
97 bindings::get_element_by_id(id)
99}
100
101
102pub struct DomHandle {
103 parent: Node,
104 dom: Dom,
105}
106
107impl DomHandle {
108 #[inline]
115 pub(crate) fn new(parent: &Node, mut dom: Dom) -> Self {
116 dom.callbacks.trigger_after_insert();
117
118 dom.callbacks.leak();
120
121 Self {
122 parent: parent.clone(),
123 dom,
124 }
125 }
126}
127
128impl Discard for DomHandle {
129 #[inline]
130 #[track_caller]
131 fn discard(self) {
132 bindings::remove_child(&self.parent, &self.dom.element);
133 self.dom.callbacks.discard();
134 }
135}
136
137
138#[inline]
147#[track_caller]
148pub fn append_dom(parent: &Node, dom: Dom) -> DomHandle {
149 bindings::append_child(&parent, &dom.element);
150 DomHandle::new(parent, dom)
151}
152
153#[inline]
155#[track_caller]
156pub fn replace_dom(parent: &Node, old_node: &Node, dom: Dom) -> DomHandle {
157 bindings::replace_child(&parent, &dom.element, old_node);
158 DomHandle::new(parent, dom)
159}
160
161
162#[must_use = "Signals do nothing unless polled"]
163enum IsWindowLoaded {
164 Initial {},
165 Pending {
166 receiver: oneshot::Receiver<Option<bool>>,
167 _event: DiscardOnDrop<EventListener>,
168 },
169 Done {},
170}
171
172impl Signal for IsWindowLoaded {
173 type Item = bool;
174
175 fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
176 let result = match *self {
177 IsWindowLoaded::Initial {} => {
178 let is_ready = bindings::ready_state() == "complete";
179
180 if is_ready {
181 Poll::Ready(Some(true))
182
183 } else {
184 let (sender, receiver) = oneshot::channel();
185
186 *self = IsWindowLoaded::Pending {
187 receiver,
188 _event: DiscardOnDrop::new(WINDOW.with(|window| {
189 EventListener::once(window, "load", move |_| {
190 crate::__unwrap!(
192 sender.send(Some(true)),
193 _e => panic!("Invalid is_window_loaded() state"),
194 )
195 })
196 })),
197 };
198
199 Poll::Ready(Some(false))
200 }
201 },
202 IsWindowLoaded::Pending { ref mut receiver, .. } => {
203 receiver.poll_unpin(cx).map(|x| x.unwrap_throw())
204 },
205 IsWindowLoaded::Done {} => {
206 Poll::Ready(None)
207 },
208 };
209
210 if let Poll::Ready(Some(true)) = result {
211 *self = IsWindowLoaded::Done {};
212 }
213
214 result
215 }
216}
217
218#[inline]
223pub fn is_window_loaded() -> impl Signal<Item = bool> {
224 IsWindowLoaded::Initial {}
225}
226
227
228#[derive(Debug, Clone, Copy, PartialEq)]
230pub struct WindowSize {
231 pub width: f64,
232 pub height: f64,
233}
234
235impl WindowSize {
236 fn new() -> Self {
237 WINDOW.with(|window| {
238 let width = window.inner_width().unwrap_throw().as_f64().unwrap_throw();
239 let height = window.inner_height().unwrap_throw().as_f64().unwrap_throw();
240
241 Self { width, height }
242 })
243 }
244}
245
246
247thread_local! {
248 static WINDOW_SIZE: RefCounter<MutableListener<WindowSize>> = RefCounter::new();
249}
250
251
252#[derive(Debug)]
253#[must_use = "Signals do nothing unless polled"]
254struct WindowSizeSignal {
255 signal: MutableSignal<WindowSize>,
256}
257
258impl Signal for WindowSizeSignal {
259 type Item = WindowSize;
260
261 #[inline]
262 fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
263 Pin::new(&mut self.signal).poll_change(cx)
264 }
265}
266
267impl Drop for WindowSizeSignal {
268 fn drop(&mut self) {
269 WINDOW_SIZE.with(|size| {
270 size.decrement();
271 });
272 }
273}
274
275
276pub fn window_size() -> impl Signal<Item = WindowSize> {
280 let signal = WINDOW_SIZE.with(|size| {
281 let size = size.increment(|| {
282 let size = Mutable::new(WindowSize::new());
283
284 let listener = {
285 let size = size.clone();
286
287 WINDOW.with(move |window| {
288 on(window, &EventOptions::default(), move |_: crate::events::Resize| {
289 size.set_neq(WindowSize::new());
290 })
291 })
292 };
293
294 MutableListener::new(size, listener)
295 });
296
297 size.as_mutable().signal()
298 });
299
300 WindowSizeSignal { signal }
301}
302
303
304fn media_query_raw<A, F>(query: &str, mut f: F) -> (Mutable<A>, EventListener)
305 where A: PartialEq + 'static,
306 F: FnMut(bool) -> A + 'static {
307
308 let query = WINDOW.with(|window| window.match_media(query).unwrap().unwrap());
309
310 let mutable = Mutable::new(f(query.matches()));
311
312 let listener = on(&query, &EventOptions::default(), {
313 let mutable = mutable.clone();
314 let query = query.clone();
315
316 move |_: crate::events::Change| {
317 mutable.set_neq(f(query.matches()));
318 }
319 });
320
321 (mutable, listener)
322}
323
324
325#[derive(Debug)]
326#[must_use = "Signals do nothing unless polled"]
327struct MediaQuerySignal {
328 signal: MutableSignal<bool>,
329 _listener: EventListener,
330}
331
332impl Signal for MediaQuerySignal {
333 type Item = bool;
334
335 #[inline]
336 fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
337 Pin::new(&mut self.signal).poll_change(cx)
338 }
339}
340
341pub fn media_query(query: &str) -> impl Signal<Item = bool> {
383 let (mutable, listener) = media_query_raw(query, |value| value);
384
385 MediaQuerySignal {
386 signal: mutable.signal(),
387 _listener: listener,
388 }
389}
390
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
396pub enum ColorScheme {
397 Light,
398 Dark,
399}
400
401impl ColorScheme {
402 #[inline]
403 fn from_bool(matches: bool) -> Self {
404 if matches {
405 Self::Dark
406 } else {
407 Self::Light
408 }
409 }
410
411 #[inline]
413 pub fn is_light(self) -> bool {
414 matches!(self, Self::Light)
415 }
416
417 #[inline]
419 pub fn is_dark(self) -> bool {
420 matches!(self, Self::Dark)
421 }
422
423 #[inline]
433 pub fn choose<A>(self, light: A, dark: A) -> A {
434 match self {
435 Self::Light => light,
436 Self::Dark => dark,
437 }
438 }
439}
440
441
442thread_local! {
443 static COLOR_SCHEME: RefCounter<MutableListener<ColorScheme>> = RefCounter::new();
444}
445
446
447#[derive(Debug)]
448#[must_use = "Signals do nothing unless polled"]
449struct ColorSchemeSignal {
450 signal: MutableSignal<ColorScheme>,
451}
452
453impl Signal for ColorSchemeSignal {
454 type Item = ColorScheme;
455
456 #[inline]
457 fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
458 Pin::new(&mut self.signal).poll_change(cx)
459 }
460}
461
462impl Drop for ColorSchemeSignal {
463 fn drop(&mut self) {
464 COLOR_SCHEME.with(|x| x.decrement());
465 }
466}
467
468
469pub fn color_scheme() -> impl Signal<Item = ColorScheme> {
476 let signal = COLOR_SCHEME.with(|counter| {
477 let counter = counter.increment(|| {
478 let (mutable, listener) = media_query_raw("(prefers-color-scheme: dark)", ColorScheme::from_bool);
479
480 MutableListener::new(mutable, listener)
481 });
482
483 counter.as_mutable().signal()
484 });
485
486 ColorSchemeSignal { signal }
487}
488
489
490#[inline]
492pub fn text(value: &str) -> Dom {
493 Dom::new(bindings::create_text_node(value).into())
494}
495
496
497fn make_text_signal<A, B>(callbacks: &mut Callbacks, value: B) -> Text
498 where A: AsStr,
499 B: Signal<Item = A> + 'static {
500
501 let element = bindings::create_text_node(intern(""));
502
503 {
504 let element = element.clone();
505
506 callbacks.after_remove(for_each(value, move |value| {
507 value.with_str(|value| {
508 bindings::set_text(&element, value);
510 });
511 }));
512 }
513
514 element
515}
516
517pub fn text_signal<A, B>(value: B) -> Dom
519 where A: AsStr,
520 B: Signal<Item = A> + 'static {
521
522 let mut callbacks = Callbacks::new();
523
524 let element = make_text_signal(&mut callbacks, value);
525
526 Dom {
527 element: element.into(),
528 callbacks: callbacks,
529 }
530}
531
532
533#[must_use]
535#[derive(Debug)]
536pub struct Dom {
537 pub(crate) element: Node,
538 pub(crate) callbacks: Callbacks,
539}
540
541impl Dom {
542 #[inline]
543 pub fn new(element: Node) -> Self {
544 Self {
545 element,
546 callbacks: Callbacks::new(),
547 }
548 }
549
550 #[inline]
551 #[track_caller]
552 pub fn empty() -> Self {
553 Self::new(bindings::create_empty_node())
554 }
555
556 #[deprecated(since = "0.5.15", note = "Store the data explicitly in a component struct instead")]
557 #[inline]
558 pub fn with_state<A, F>(mut state: A, initializer: F) -> Dom
559 where A: 'static,
560 F: FnOnce(&mut A) -> Dom {
561
562 let mut dom = initializer(&mut state);
563
564 dom.callbacks.after_remove(ValueDiscard::new(state));
565
566 dom
567 }
568}
569
570
571#[inline]
572#[track_caller]
573fn create_element<A>(name: &str) -> A where A: JsCast {
574 crate::__unwrap!(
576 bindings::create_element(intern(name)).dyn_into(),
577 e => panic!("Invalid DOM type: \"{}\" => {:?}", name, JsValue::as_ref(&e)),
578 )
579}
580
581#[inline]
582#[track_caller]
583fn create_element_ns<A>(name: &str, namespace: &str) -> A where A: JsCast {
584 crate::__unwrap!(
586 bindings::create_element_ns(intern(namespace), intern(name)).dyn_into(),
587 e => panic!("Invalid DOM type: \"{}\" => {:?}", name, JsValue::as_ref(&e)),
588 )
589}
590
591
592fn set_option<A, B, C, D, F>(element: A, callbacks: &mut Callbacks, value: D, mut f: F)
594 where A: 'static,
595 C: OptionStr<Output = B>,
596 D: Signal<Item = C> + 'static,
597 F: FnMut(&A, Option<B>) + 'static {
598
599 let mut is_set = false;
600
601 callbacks.after_remove(for_each(value, move |value| {
602 let value = value.into_option();
603
604 if value.is_some() {
605 is_set = true;
606
607 } else if is_set {
608 is_set = false;
609
610 } else {
611 return;
612 }
613
614 f(&element, value);
615 }));
616}
617
618fn set_style<A, B>(style: &CssStyleDeclaration, name: &A, value: B, important: bool)
621 where A: MultiStr,
622 B: MultiStr {
623
624 let mut names = vec![];
625 let mut values = vec![];
626
627 fn try_set_style(style: &CssStyleDeclaration, names: &mut Vec<String>, values: &mut Vec<String>, name: &str, value: &str, important: bool) -> Option<()> {
629 assert!(value != "");
630
631 bindings::remove_style(style, name);
633
634 bindings::set_style(style, name, value, important);
635
636 let is_changed = bindings::get_style(style, name) != "";
637
638 if is_changed {
639 Some(())
640
641 } else {
642 names.push(String::from(name));
643 values.push(String::from(value));
644 None
645 }
646 }
647
648 let okay = name.find_map(|name| {
649 let name: &str = intern(name);
650
651 value.find_map(|value| {
652 try_set_style(style, &mut names, &mut values, &name, &value, important)
654 })
655 });
656
657 if let None = okay {
658 if cfg!(debug_assertions) {
659 panic!("style is incorrect:\n names: {}\n values: {}", names.join(", "), values.join(", "));
661 }
662 }
663}
664
665fn set_style_signal<A, B, C, D>(style: CssStyleDeclaration, callbacks: &mut Callbacks, name: A, value: D, important: bool)
668 where A: MultiStr + 'static,
669 B: MultiStr,
670 C: OptionStr<Output = B>,
671 D: Signal<Item = C> + 'static {
672
673 set_option(style, callbacks, value, move |style, value| {
674 match value {
675 Some(value) => {
676 set_style(style, &name, value, important);
678 },
679 None => {
680 name.each(|name| {
681 bindings::remove_style(style, intern(name));
683 });
684 },
685 }
686 });
687}
688
689fn set_style_unchecked_signal<A, B, C, D>(style: CssStyleDeclaration, callbacks: &mut Callbacks, name: A, value: D, important: bool)
692 where A: AsStr + 'static,
693 B: AsStr,
694 C: OptionStr<Output = B>,
695 D: Signal<Item = C> + 'static {
696
697 set_option(style, callbacks, value, move |style, value| {
698 match value {
699 Some(value) => {
700 name.with_str(|name| {
701 let name: &str = intern(name);
702
703 value.with_str(|value| {
704 bindings::set_style(style, name, value, important);
705 });
706 });
707 },
708 None => {
709 name.with_str(|name| {
710 bindings::remove_style(style, intern(name));
711 });
712 },
713 }
714 });
715}
716
717#[track_caller]
721fn set_property<A, B, C>(element: &A, name: &B, value: C) where A: AsRef<JsValue>, B: MultiStr, C: Into<JsValue> {
722 let element = element.as_ref();
723 let value = value.into();
724
725 name.each(|name| {
726 bindings::set_property(element, intern(name), &value);
727 });
728}
729
730
731#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
732pub struct EventOptions {
733 pub bubbles: bool,
734 pub preventable: bool,
735}
736
737impl EventOptions {
738 pub fn bubbles() -> Self {
739 Self {
740 bubbles: true,
741 preventable: false,
742 }
743 }
744
745 pub fn preventable() -> Self {
746 Self {
747 bubbles: false,
748 preventable: true,
749 }
750 }
751
752 pub(crate) fn into_gloo(self) -> gloo_events::EventListenerOptions {
753 gloo_events::EventListenerOptions {
754 phase: if self.bubbles {
755 gloo_events::EventListenerPhase::Bubble
756 } else {
757 gloo_events::EventListenerPhase::Capture
758 },
759 passive: !self.preventable,
760 }
761 }
762}
763
764impl Default for EventOptions {
765 fn default() -> Self {
766 Self {
767 bubbles: false,
768 preventable: false,
769 }
770 }
771}
772
773
774#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
776pub enum ScrollBehavior {
777 Auto,
781
782 Instant,
784
785 Smooth,
787}
788
789impl ScrollBehavior {
790 fn into_js(self) -> web_sys::ScrollBehavior {
791 match self {
792 Self::Auto => web_sys::ScrollBehavior::Auto,
793 Self::Instant => web_sys::ScrollBehavior::Instant,
794 Self::Smooth => web_sys::ScrollBehavior::Smooth,
795 }
796 }
797}
798
799impl Default for ScrollBehavior {
800 fn default() -> Self {
801 Self::Auto
802 }
803}
804
805
806#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
808pub enum ScrollAlign {
809 Start,
811
812 Center,
814
815 End,
817
818 Nearest,
822}
823
824impl ScrollAlign {
825 fn into_js(self) -> web_sys::ScrollLogicalPosition {
826 match self {
827 Self::Start => web_sys::ScrollLogicalPosition::Start,
828 Self::Center => web_sys::ScrollLogicalPosition::Center,
829 Self::End => web_sys::ScrollLogicalPosition::End,
830 Self::Nearest => web_sys::ScrollLogicalPosition::Nearest,
831 }
832 }
833}
834
835
836#[derive(Debug, Clone, Hash, PartialEq)]
838pub struct ScrollIntoView {
839 pub behavior: ScrollBehavior,
841
842 pub align_x: ScrollAlign,
844
845 pub align_y: ScrollAlign,
847}
848
849impl ScrollIntoView {
850 pub fn smooth_center() -> Self {
852 Self {
853 behavior: ScrollBehavior::Smooth,
854 align_x: ScrollAlign::Center,
855 align_y: ScrollAlign::Center,
856 }
857 }
858
859 pub fn smooth_nearest() -> Self {
861 Self {
862 behavior: ScrollBehavior::Smooth,
863 align_x: ScrollAlign::Nearest,
864 align_y: ScrollAlign::Nearest,
865 }
866 }
867
868 fn into_js(&self) -> web_sys::ScrollIntoViewOptions {
869 let output = web_sys::ScrollIntoViewOptions::new();
870 output.set_inline(self.align_x.into_js());
871 output.set_block(self.align_y.into_js());
872 output.set_behavior(self.behavior.into_js());
873 output
874 }
875}
876
877impl Default for ScrollIntoView {
878 fn default() -> Self {
879 Self {
880 behavior: ScrollBehavior::default(),
881 align_x: ScrollAlign::Start,
882 align_y: ScrollAlign::Nearest,
883 }
884 }
885}
886
887
888#[must_use]
890#[derive(Debug)]
891pub struct DomBuilder<A> {
892 element: A,
893 callbacks: Callbacks,
894}
895
896impl<A> DomBuilder<A> where A: JsCast {
897 #[track_caller]
898 #[inline]
899 pub fn new_html(name: &str) -> Self {
900 Self::new(create_element(name))
901 }
902
903 #[track_caller]
904 #[inline]
905 pub fn new_svg(name: &str) -> Self {
906 Self::new(create_element_ns(name, SVG_NAMESPACE))
907 }
908}
909
910impl<A> DomBuilder<A> {
911 #[inline]
912 #[doc(hidden)]
913 pub fn __internal_transfer_callbacks<B>(mut self, mut shadow: DomBuilder<B>) -> Self {
914 self.callbacks.after_insert.append(&mut shadow.callbacks.after_insert);
915 self.callbacks.after_remove.append(&mut shadow.callbacks.after_remove);
916 self
917 }
918
919 #[inline]
920 pub fn new(value: A) -> Self {
921 Self {
922 element: value,
923 callbacks: Callbacks::new(),
924 }
925 }
926
927 #[inline]
928 #[track_caller]
929 fn _event<T, F>(callbacks: &mut Callbacks, element: &EventTarget, options: &EventOptions, listener: F)
930 where T: StaticEvent,
931 F: FnMut(T) + 'static {
932 callbacks.after_remove(on(element, options, listener));
933 }
934
935 #[inline]
937 #[track_caller]
938 pub fn global_event_with_options<T, F>(mut self, options: &EventOptions, listener: F) -> Self
939 where T: StaticEvent,
940 F: FnMut(T) + 'static {
941 WINDOW.with(|window| {
942 Self::_event(&mut self.callbacks, window, options, listener);
943 });
944 self
945 }
946
947 #[inline]
949 #[track_caller]
950 pub fn global_event<T, F>(self, listener: F) -> Self
951 where T: StaticEvent,
952 F: FnMut(T) + 'static {
953 self.global_event_with_options(&T::default_options(false), listener)
954 }
955
956 #[deprecated(since = "0.5.21", note = "Use global_event_with_options instead")]
957 #[inline]
958 #[track_caller]
959 pub fn global_event_preventable<T, F>(self, listener: F) -> Self
960 where T: StaticEvent,
961 F: FnMut(T) + 'static {
962 self.global_event_with_options(&T::default_options(true), listener)
963 }
964
965 #[inline]
966 pub fn future<F>(mut self, future: F) -> Self where F: Future<Output = ()> + 'static {
967 self.callbacks.after_remove(DiscardOnDrop::leak(spawn_future(future)));
968 self
969 }
970
971
972 #[inline]
974 pub fn apply<F>(self, f: F) -> Self where F: FnOnce(Self) -> Self {
975 f(self)
976 }
977
978 #[inline]
979 pub fn apply_if<F>(self, test: bool, f: F) -> Self where F: FnOnce(Self) -> Self {
980 if test {
981 f(self)
982
983 } else {
984 self
985 }
986 }
987}
988
989impl<A> DomBuilder<A> where A: Clone {
990 #[inline]
991 #[doc(hidden)]
992 pub fn __internal_element(&self) -> A {
993 self.element.clone()
994 }
995
996 #[deprecated(since = "0.5.1", note = "Use the with_node macro instead")]
997 #[inline]
998 pub fn with_element<B, F>(self, f: F) -> B where F: FnOnce(Self, A) -> B {
999 let element = self.element.clone();
1000 f(self, element)
1001 }
1002
1003 #[deprecated(since = "0.5.20", note = "Use the with_node macro instead")]
1004 #[inline]
1005 pub fn before_inserted<F>(self, f: F) -> Self where F: FnOnce(A) {
1006 let element = self.element.clone();
1007 f(element);
1008 self
1009 }
1010}
1011
1012impl<A> DomBuilder<A> where A: Clone + 'static {
1013 #[inline]
1014 pub fn after_inserted<F>(mut self, f: F) -> Self where F: FnOnce(A) + 'static {
1015 let element = self.element.clone();
1016 self.callbacks.after_insert(move |_| f(element));
1017 self
1018 }
1019
1020 #[inline]
1021 pub fn after_removed<F>(mut self, f: F) -> Self where F: FnOnce(A) + 'static {
1022 let element = self.element.clone();
1023 self.callbacks.after_remove(FnDiscard::new(move || f(element)));
1024 self
1025 }
1026}
1027
1028impl<A> DomBuilder<A> where A: Into<Node> {
1029 #[inline]
1030 pub fn into_dom(self) -> Dom {
1031 Dom {
1032 element: self.element.into(),
1033 callbacks: self.callbacks,
1034 }
1035 }
1036}
1037
1038impl<A> DomBuilder<A> where A: AsRef<JsValue> {
1039 #[inline]
1040 #[track_caller]
1041 pub fn prop<B, C>(self, name: B, value: C) -> Self where B: MultiStr, C: Into<JsValue> {
1042 set_property(&self.element, &name, value);
1043 self
1044 }
1045
1046 #[deprecated(since = "0.5.24", note = "Use the `prop` method instead")]
1047 #[inline]
1048 #[track_caller]
1049 pub fn property<B, C>(self, name: B, value: C) -> Self where B: MultiStr, C: Into<JsValue> {
1050 self.prop(name, value)
1051 }
1052}
1053
1054impl<A> DomBuilder<A> where A: AsRef<JsValue> {
1055 fn set_property_signal<B, C, D>(&mut self, name: B, value: D)
1058 where B: MultiStr + 'static,
1059 C: Into<JsValue>,
1060 D: Signal<Item = C> + 'static {
1061
1062 let element = self.element.as_ref().clone();
1063
1064 self.callbacks.after_remove(for_each(value, move |value| {
1065 set_property(&element, &name, value);
1066 }));
1067 }
1068
1069 #[inline]
1070 #[track_caller]
1071 pub fn prop_signal<B, C, D>(mut self, name: B, value: D) -> Self
1072 where B: MultiStr + 'static,
1073 C: Into<JsValue>,
1074 D: Signal<Item = C> + 'static {
1075
1076 self.set_property_signal(name, value);
1077 self
1078 }
1079
1080 #[deprecated(since = "0.5.24", note = "Use the `prop_signal` method instead")]
1081 #[inline]
1082 #[track_caller]
1083 pub fn property_signal<B, C, D>(self, name: B, value: D) -> Self
1084 where B: MultiStr + 'static,
1085 C: Into<JsValue>,
1086 D: Signal<Item = C> + 'static {
1087
1088 self.prop_signal(name, value)
1089 }
1090}
1091
1092impl<A> DomBuilder<A> where A: AsRef<EventTarget> {
1093 #[inline]
1094 #[track_caller]
1095 pub fn event_with_options<T, F>(mut self, options: &EventOptions, listener: F) -> Self
1096 where T: StaticEvent,
1097 F: FnMut(T) + 'static {
1098 Self::_event(&mut self.callbacks, &self.element.as_ref(), options, listener);
1099 self
1100 }
1101
1102 #[inline]
1103 #[track_caller]
1104 pub fn event<T, F>(self, listener: F) -> Self
1105 where T: StaticEvent,
1106 F: FnMut(T) + 'static {
1107 self.event_with_options(&T::default_options(false), listener)
1108 }
1109
1110 #[deprecated(since = "0.5.21", note = "Use event_with_options instead")]
1111 #[inline]
1112 #[track_caller]
1113 pub fn event_preventable<T, F>(self, listener: F) -> Self
1114 where T: StaticEvent,
1115 F: FnMut(T) + 'static {
1116 self.event_with_options(&T::default_options(true), listener)
1117 }
1118}
1119
1120impl<A> DomBuilder<A> where A: AsRef<Node> {
1121 #[inline]
1125 #[track_caller]
1126 pub fn fragment<F>(self, fragment: &F) -> Self where F: Fragment {
1127 let FragmentBuilder(DomBuilder { callbacks, .. }) = {
1128 let element: &Node = self.element.as_ref();
1129
1130 fragment.apply(FragmentBuilder(DomBuilder {
1131 element,
1132 callbacks: self.callbacks,
1133 }))
1134 };
1135
1136 Self {
1137 element: self.element,
1138 callbacks,
1139 }
1140 }
1141
1142 #[inline]
1143 #[track_caller]
1144 pub fn text(self, value: &str) -> Self {
1145 bindings::append_child(self.element.as_ref(), &bindings::create_text_node(value));
1147 self
1148 }
1149
1150 #[inline]
1151 #[track_caller]
1152 pub fn text_signal<B, C>(mut self, value: C) -> Self
1153 where B: AsStr,
1154 C: Signal<Item = B> + 'static {
1155
1156 let element = make_text_signal(&mut self.callbacks, value);
1157 bindings::append_child(self.element.as_ref(), &element);
1158 self
1159 }
1160
1161 #[inline]
1162 #[track_caller]
1163 pub fn child<B: BorrowMut<Dom>>(mut self, mut child: B) -> Self {
1164 operations::insert_children_one(self.element.as_ref(), &mut self.callbacks, child.borrow_mut());
1165 self
1166 }
1167
1168 #[inline]
1169 #[track_caller]
1170 pub fn child_signal<B>(mut self, child: B) -> Self
1171 where B: Signal<Item = Option<Dom>> + 'static {
1172
1173 operations::insert_child_signal(self.element.as_ref().clone(), &mut self.callbacks, child);
1174 self
1175 }
1176
1177 #[inline]
1179 #[track_caller]
1180 pub fn children<B: BorrowMut<Dom>, C: IntoIterator<Item = B>>(mut self, children: C) -> Self {
1181 operations::insert_children_iter(self.element.as_ref(), &mut self.callbacks, children);
1182 self
1183 }
1184
1185 #[inline]
1186 #[track_caller]
1187 pub fn children_signal_vec<B>(mut self, children: B) -> Self
1188 where B: SignalVec<Item = Dom> + 'static {
1189
1190 operations::insert_children_signal_vec(self.element.as_ref().clone(), &mut self.callbacks, children);
1191 self
1192 }
1193}
1194
1195impl<A> DomBuilder<A> where A: AsRef<Element> {
1196 #[inline]
1197 #[doc(hidden)]
1198 #[track_caller]
1199 pub fn __internal_shadow_root(&self, mode: ShadowRootMode) -> DomBuilder<ShadowRoot> {
1200 let shadow = self.element.as_ref().attach_shadow(&ShadowRootInit::new(mode)).unwrap_js();
1201 DomBuilder::new(shadow)
1202 }
1203
1204 #[inline]
1205 #[track_caller]
1206 pub fn attr<B>(self, name: B, value: &str) -> Self where B: MultiStr {
1207 let element = self.element.as_ref();
1208
1209 name.each(|name| {
1210 bindings::set_attribute(element, intern(name), &value);
1211 });
1212
1213 self
1214 }
1215
1216 #[deprecated(since = "0.5.24", note = "Use the `attr` method instead")]
1217 #[inline]
1218 #[track_caller]
1219 pub fn attribute<B>(self, name: B, value: &str) -> Self where B: MultiStr {
1220 self.attr(name, value)
1221 }
1222
1223 #[inline]
1224 #[track_caller]
1225 pub fn attr_ns<B>(self, namespace: &str, name: B, value: &str) -> Self where B: MultiStr {
1226 let element = self.element.as_ref();
1227 let namespace: &str = intern(namespace);
1228
1229 name.each(|name| {
1230 bindings::set_attribute_ns(element, &namespace, intern(name), &value);
1231 });
1232
1233 self
1234 }
1235
1236 #[deprecated(since = "0.5.24", note = "Use the `attr_ns` method instead")]
1237 #[inline]
1238 #[track_caller]
1239 pub fn attribute_namespace<B>(self, namespace: &str, name: B, value: &str) -> Self where B: MultiStr {
1240 self.attr_ns(namespace, name, value)
1241 }
1242
1243 #[inline]
1244 #[track_caller]
1245 pub fn class<B>(self, name: B) -> Self where B: MultiStr {
1246 let classes = self.element.as_ref().class_list();
1247
1248 name.each(|name| {
1249 bindings::add_class(&classes, intern(name));
1250 });
1251
1252 self
1253 }
1254
1255 #[inline]
1257 #[track_caller]
1258 pub fn visible(self, value: bool) -> Self {
1259 if value {
1260 self
1262
1263 } else {
1264 self.class(&*HIDDEN_CLASS)
1265 }
1266 }
1267}
1268
1269impl<A> DomBuilder<A> where A: AsRef<Element> {
1270 fn set_attribute_signal<B, C, D, E>(&mut self, name: B, value: E)
1273 where B: MultiStr + 'static,
1274 C: AsStr,
1275 D: OptionStr<Output = C>,
1276 E: Signal<Item = D> + 'static {
1277
1278 set_option(self.element.as_ref().clone(), &mut self.callbacks, value, move |element, value| {
1279 match value {
1280 Some(value) => {
1281 value.with_str(|value| {
1282 name.each(|name| {
1283 bindings::set_attribute(element, intern(name), &value);
1284 });
1285 });
1286 },
1287 None => {
1288 name.each(|name| {
1289 bindings::remove_attribute(element, intern(name));
1290 });
1291 },
1292 }
1293 });
1294 }
1295
1296 #[inline]
1297 #[track_caller]
1298 pub fn attr_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1299 where B: MultiStr + 'static,
1300 C: AsStr,
1301 D: OptionStr<Output = C>,
1302 E: Signal<Item = D> + 'static {
1303
1304 self.set_attribute_signal(name, value);
1305 self
1306 }
1307
1308 #[deprecated(since = "0.5.24", note = "Use the `attr_signal` method instead")]
1309 #[inline]
1310 #[track_caller]
1311 pub fn attribute_signal<B, C, D, E>(self, name: B, value: E) -> Self
1312 where B: MultiStr + 'static,
1313 C: AsStr,
1314 D: OptionStr<Output = C>,
1315 E: Signal<Item = D> + 'static {
1316
1317 self.attr_signal(name, value)
1318 }
1319
1320
1321 fn set_attribute_namespace_signal<B, C, D, E>(&mut self, namespace: &str, name: B, value: E)
1324 where B: MultiStr + 'static,
1325 C: AsStr,
1326 D: OptionStr<Output = C>,
1327 E: Signal<Item = D> + 'static {
1328
1329 let namespace: String = intern(namespace).to_owned();
1331
1332 set_option(self.element.as_ref().clone(), &mut self.callbacks, value, move |element, value| {
1333 match value {
1334 Some(value) => {
1335 value.with_str(|value| {
1336 name.each(|name| {
1337 bindings::set_attribute_ns(element, &namespace, intern(name), &value);
1339 });
1340 });
1341 },
1342 None => {
1343 name.each(|name| {
1344 bindings::remove_attribute_ns(element, &namespace, intern(name));
1345 });
1346 },
1347 }
1348 });
1349 }
1350
1351 #[inline]
1352 #[track_caller]
1353 pub fn attr_ns_signal<B, C, D, E>(mut self, namespace: &str, name: B, value: E) -> Self
1354 where B: MultiStr + 'static,
1355 C: AsStr,
1356 D: OptionStr<Output = C>,
1357 E: Signal<Item = D> + 'static {
1358
1359 self.set_attribute_namespace_signal(namespace, name, value);
1360 self
1361 }
1362
1363 #[deprecated(since = "0.5.24", note = "Use the `attr_ns_signal` method instead")]
1364 #[inline]
1365 #[track_caller]
1366 pub fn attribute_namespace_signal<B, C, D, E>(self, namespace: &str, name: B, value: E) -> Self
1367 where B: MultiStr + 'static,
1368 C: AsStr,
1369 D: OptionStr<Output = C>,
1370 E: Signal<Item = D> + 'static {
1371
1372 self.attr_ns_signal(namespace, name, value)
1373 }
1374
1375
1376 fn set_class_signal<B, C>(&mut self, name: B, value: C)
1379 where B: MultiStr + 'static,
1380 C: Signal<Item = bool> + 'static {
1381
1382 let element = self.element.as_ref().class_list();
1383
1384 let mut is_set = false;
1385
1386 self.callbacks.after_remove(for_each(value, move |value| {
1387 if value {
1388 if !is_set {
1389 is_set = true;
1390
1391 name.each(|name| {
1392 bindings::add_class(&element, intern(name));
1393 });
1394 }
1395
1396 } else {
1397 if is_set {
1398 is_set = false;
1399
1400 name.each(|name| {
1401 bindings::remove_class(&element, intern(name));
1402 });
1403 }
1404 }
1405 }));
1406 }
1407
1408 #[inline]
1409 #[track_caller]
1410 pub fn class_signal<B, C>(mut self, name: B, value: C) -> Self
1411 where B: MultiStr + 'static,
1412 C: Signal<Item = bool> + 'static {
1413
1414 self.set_class_signal(name, value);
1415 self
1416 }
1417
1418 #[inline]
1420 #[track_caller]
1421 pub fn visible_signal<B>(self, value: B) -> Self where B: Signal<Item = bool> + 'static {
1422 self.class_signal(&*HIDDEN_CLASS, not(value))
1423 }
1424
1425
1426 fn set_scroll_signal<B, F>(&mut self, signal: B, mut f: F)
1430 where B: Signal<Item = Option<i32>> + 'static,
1431 F: FnMut(&Element, i32) + 'static {
1432
1433 let element: Element = self.element.as_ref().clone();
1434
1435 self.callbacks.after_insert(move |callbacks| {
1437 callbacks.after_remove(for_each(signal, move |value| {
1438 if let Some(value) = value {
1439 f(&element, value);
1440 }
1441 }));
1442 });
1443 }
1444
1445 #[inline]
1447 #[track_caller]
1448 pub fn scroll_left_signal<B>(mut self, signal: B) -> Self where B: Signal<Item = Option<i32>> + 'static {
1449 self.set_scroll_signal(signal, Element::set_scroll_left);
1451 self
1452 }
1453
1454 #[inline]
1456 #[track_caller]
1457 pub fn scroll_top_signal<B>(mut self, signal: B) -> Self where B: Signal<Item = Option<i32>> + 'static {
1458 self.set_scroll_signal(signal, Element::set_scroll_top);
1460 self
1461 }
1462
1463
1464 fn set_scroll_into_view_signal<B>(&mut self, signal: B)
1467 where B: Signal<Item = Option<ScrollIntoView>> + 'static {
1468
1469 let element: Element = self.element.as_ref().clone();
1470
1471 self.callbacks.after_insert(move |callbacks| {
1473 callbacks.after_remove(for_each(signal, move |options| {
1474 if let Some(options) = options {
1475 element.scroll_into_view_with_scroll_into_view_options(&options.into_js());
1476 }
1477 }));
1478 });
1479 }
1480
1481 #[inline]
1487 #[track_caller]
1488 pub fn scroll_into_view_signal<B>(mut self, signal: B) -> Self where B: Signal<Item = Option<ScrollIntoView>> + 'static {
1489 self.set_scroll_into_view_signal(signal);
1490 self
1491 }
1492}
1493
1494impl<A> DomBuilder<A> where A: AsRef<HtmlElement> {
1495 #[inline]
1496 #[track_caller]
1497 pub fn style<B, C>(self, name: B, value: C) -> Self
1498 where B: MultiStr,
1499 C: MultiStr {
1500 set_style(&self.element.as_ref().style(), &name, value, false);
1501 self
1502 }
1503
1504 #[inline]
1505 #[track_caller]
1506 pub fn style_important<B, C>(self, name: B, value: C) -> Self
1507 where B: MultiStr,
1508 C: MultiStr {
1509 set_style(&self.element.as_ref().style(), &name, value, true);
1510 self
1511 }
1512
1513 #[inline]
1514 #[track_caller]
1515 pub fn style_unchecked<B, C>(self, name: B, value: C) -> Self
1516 where B: AsStr,
1517 C: AsStr {
1518 name.with_str(|name| {
1519 value.with_str(|value| {
1520 bindings::set_style(&self.element.as_ref().style(), intern(name), value, false);
1521 });
1522 });
1523 self
1524 }
1525}
1526
1527impl<A> DomBuilder<A> where A: AsRef<HtmlElement> {
1528 #[inline]
1529 #[track_caller]
1530 pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1531 where B: MultiStr + 'static,
1532 C: MultiStr,
1533 D: OptionStr<Output = C>,
1534 E: Signal<Item = D> + 'static {
1535
1536 set_style_signal(self.element.as_ref().style(), &mut self.callbacks, name, value, false);
1537 self
1538 }
1539
1540 #[inline]
1541 #[track_caller]
1542 pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1543 where B: MultiStr + 'static,
1544 C: MultiStr,
1545 D: OptionStr<Output = C>,
1546 E: Signal<Item = D> + 'static {
1547
1548 set_style_signal(self.element.as_ref().style(), &mut self.callbacks, name, value, true);
1549 self
1550 }
1551
1552 #[inline]
1553 #[track_caller]
1554 pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1555 where B: AsStr + 'static,
1556 C: AsStr,
1557 D: OptionStr<Output = C>,
1558 E: Signal<Item = D> + 'static {
1559
1560 set_style_unchecked_signal(self.element.as_ref().style(), &mut self.callbacks, name, value, false);
1561 self
1562 }
1563
1564
1565 #[inline]
1567 #[track_caller]
1568 pub fn focused(mut self, value: bool) -> Self {
1569 let element = self.element.as_ref().clone();
1570
1571 self.callbacks.after_insert(move |_| {
1573 if value {
1575 bindings::focus(&element);
1576
1577 } else {
1578 bindings::blur(&element);
1579 }
1580 });
1581
1582 self
1583 }
1584
1585
1586 fn set_focused_signal<B>(&mut self, value: B)
1589 where B: Signal<Item = bool> + 'static {
1590
1591 let element = self.element.as_ref().clone();
1592
1593 self.callbacks.after_insert(move |callbacks| {
1595 callbacks.after_remove(for_each(value, move |value| {
1597 if value {
1599 bindings::focus(&element);
1600
1601 } else {
1602 bindings::blur(&element);
1603 }
1604 }));
1605 });
1606 }
1607
1608 #[inline]
1609 #[track_caller]
1610 pub fn focused_signal<B>(mut self, value: B) -> Self
1611 where B: Signal<Item = bool> + 'static {
1612
1613 self.set_focused_signal(value);
1614 self
1615 }
1616}
1617
1618
1619#[inline]
1649pub fn stylesheet_raw<A>(css: A) where A: AsStr {
1650 css.with_str(|css| {
1651 bindings::create_stylesheet(Some(css));
1652 });
1653}
1654
1655
1656#[must_use]
1658pub struct StylesheetBuilder {
1659 element: CssStyleDeclaration,
1660 callbacks: Callbacks,
1661}
1662
1663impl StylesheetBuilder {
1665 fn __internal_rules<A>(rules: &A) -> CssRule where A: MultiStr {
1666 thread_local! {
1669 static STYLESHEET: CssStyleSheet = bindings::create_stylesheet(None);
1670 }
1671
1672 STYLESHEET.with(move |stylesheet| {
1673 let mut failed = vec![];
1674
1675 let okay = rules.find_map(|rule| {
1676 if let Ok(declaration) = bindings::make_rule(stylesheet, rule) {
1678 Some(declaration)
1679
1680 } else {
1681 failed.push(String::from(rule));
1682 None
1683 }
1684 });
1685
1686 if let Some(okay) = okay {
1687 okay
1688
1689 } else {
1690 panic!("selectors are incorrect:\n {}", failed.join("\n "));
1692 }
1693 })
1694 }
1695
1696 #[doc(hidden)]
1697 #[inline]
1698 pub fn __internal_stylesheet<A>(rules: A) -> Self where A: MultiStr {
1699 let element = Self::__internal_rules(&rules).unchecked_into::<CssStyleRule>();
1700
1701 Self {
1702 element: element.style(),
1703 callbacks: Callbacks::new(),
1704 }
1705 }
1706
1707 #[doc(hidden)]
1708 #[inline]
1709 pub fn __internal_new<A>(rules: A) -> Self where A: MultiStr {
1710 Self::__internal_stylesheet(MapMultiStr::new(rules, |rule| format!("{} {{}}", rule)))
1711 }
1712
1713 #[inline]
1714 #[track_caller]
1715 pub fn style<B, C>(self, name: B, value: C) -> Self
1716 where B: MultiStr,
1717 C: MultiStr {
1718 set_style(&self.element, &name, value, false);
1719 self
1720 }
1721
1722 #[inline]
1723 #[track_caller]
1724 pub fn style_important<B, C>(self, name: B, value: C) -> Self
1725 where B: MultiStr,
1726 C: MultiStr {
1727 set_style(&self.element, &name, value, true);
1728 self
1729 }
1730
1731 #[inline]
1732 #[track_caller]
1733 pub fn style_unchecked<B, C>(self, name: B, value: C) -> Self
1734 where B: AsStr,
1735 C: AsStr {
1736 name.with_str(|name| {
1737 value.with_str(|value| {
1738 bindings::set_style(&self.element, intern(name), value, false);
1739 });
1740 });
1741 self
1742 }
1743
1744 #[inline]
1745 #[track_caller]
1746 pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1747 where B: MultiStr + 'static,
1748 C: MultiStr,
1749 D: OptionStr<Output = C>,
1750 E: Signal<Item = D> + 'static {
1751
1752 set_style_signal(self.element.clone(), &mut self.callbacks, name, value, false);
1753 self
1754 }
1755
1756 #[inline]
1757 #[track_caller]
1758 pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1759 where B: MultiStr + 'static,
1760 C: MultiStr,
1761 D: OptionStr<Output = C>,
1762 E: Signal<Item = D> + 'static {
1763
1764 set_style_signal(self.element.clone(), &mut self.callbacks, name, value, true);
1765 self
1766 }
1767
1768 #[inline]
1769 #[track_caller]
1770 pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1771 where B: AsStr + 'static,
1772 C: AsStr,
1773 D: OptionStr<Output = C>,
1774 E: Signal<Item = D> + 'static {
1775
1776 set_style_unchecked_signal(self.element.clone(), &mut self.callbacks, name, value, false);
1777 self
1778 }
1779
1780 #[inline]
1799 #[track_caller]
1800 pub fn raw<B>(self, css: B) -> Self where B: AsStr {
1801 css.with_str(|css| {
1802 bindings::append_raw(&self.element, css);
1803 });
1804
1805 self
1806 }
1807
1808 #[inline]
1810 #[track_caller]
1811 #[doc(hidden)]
1812 pub fn __internal_done(mut self) {
1813 self.callbacks.trigger_after_insert();
1814
1815 self.callbacks.leak();
1817 }
1818}
1819
1820
1821#[must_use]
1823pub struct ClassBuilder {
1824 stylesheet: StylesheetBuilder,
1825 class_name: String,
1826}
1827
1828impl ClassBuilder {
1829 #[doc(hidden)]
1830 #[inline]
1831 #[track_caller]
1832 pub fn __internal_new(name: Option<&str>) -> Self {
1833 let class_name = __internal::make_class_id(name);
1834
1835 Self {
1836 stylesheet: StylesheetBuilder::__internal_stylesheet(&format!(".{} {{}}", class_name)),
1838 class_name,
1839 }
1840 }
1841
1842 #[doc(hidden)]
1843 #[inline]
1844 #[track_caller]
1845 pub fn __internal_class_name(&self) -> &str {
1846 &self.class_name
1847 }
1848
1849 #[inline]
1850 #[track_caller]
1851 pub fn style<B, C>(mut self, name: B, value: C) -> Self
1852 where B: MultiStr,
1853 C: MultiStr {
1854 self.stylesheet = self.stylesheet.style(name, value);
1855 self
1856 }
1857
1858 #[inline]
1859 #[track_caller]
1860 pub fn style_important<B, C>(mut self, name: B, value: C) -> Self
1861 where B: MultiStr,
1862 C: MultiStr {
1863 self.stylesheet = self.stylesheet.style_important(name, value);
1864 self
1865 }
1866
1867 #[inline]
1868 #[track_caller]
1869 pub fn style_unchecked<B, C>(mut self, name: B, value: C) -> Self
1870 where B: AsStr,
1871 C: AsStr {
1872 self.stylesheet = self.stylesheet.style_unchecked(name, value);
1873 self
1874 }
1875
1876 #[inline]
1877 #[track_caller]
1878 pub fn style_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1879 where B: MultiStr + 'static,
1880 C: MultiStr,
1881 D: OptionStr<Output = C>,
1882 E: Signal<Item = D> + 'static {
1883
1884 self.stylesheet = self.stylesheet.style_signal(name, value);
1885 self
1886 }
1887
1888 #[inline]
1889 #[track_caller]
1890 pub fn style_important_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1891 where B: MultiStr + 'static,
1892 C: MultiStr,
1893 D: OptionStr<Output = C>,
1894 E: Signal<Item = D> + 'static {
1895
1896 self.stylesheet = self.stylesheet.style_important_signal(name, value);
1897 self
1898 }
1899
1900 #[inline]
1901 #[track_caller]
1902 pub fn style_unchecked_signal<B, C, D, E>(mut self, name: B, value: E) -> Self
1903 where B: AsStr + 'static,
1904 C: AsStr,
1905 D: OptionStr<Output = C>,
1906 E: Signal<Item = D> + 'static {
1907
1908 self.stylesheet = self.stylesheet.style_unchecked_signal(name, value);
1909 self
1910 }
1911
1912 #[inline]
1931 #[track_caller]
1932 pub fn raw<B>(mut self, css: B) -> Self where B: AsStr {
1933 self.stylesheet = self.stylesheet.raw(css);
1934 self
1935 }
1936
1937 #[doc(hidden)]
1939 #[inline]
1940 #[track_caller]
1941 pub fn __internal_done(self) -> String {
1942 self.stylesheet.__internal_done();
1943 self.class_name
1944 }
1945}
1946
1947
1948#[doc(hidden)]
1949pub mod __internal {
1950 use std::sync::atomic::{AtomicU32, Ordering};
1951 use crate::fragment::{Fragment, FragmentBuilder, BoxFragment};
1952 use crate::traits::MultiStr;
1953
1954
1955 pub use web_sys::HtmlElement;
1956 pub use web_sys::SvgElement;
1957
1958
1959 pub fn make_class_id(name: Option<&str>) -> String {
1960 static CLASS_ID: AtomicU32 = AtomicU32::new(0);
1963
1964 let id = CLASS_ID.fetch_add(1, Ordering::Relaxed);
1967
1968 let name = name.unwrap_or("__class_");
1969 format!("{}_{}", name, id)
1971 }
1972
1973
1974 pub struct Pseudo<'a, A> {
1975 class_name: &'a str,
1976 pseudos: A,
1977 }
1978
1979 impl<'a, A> Pseudo<'a, A> where A: MultiStr {
1980 #[inline]
1981 pub fn new(class_name: &'a str, pseudos: A) -> Self {
1982 Self { class_name, pseudos }
1983 }
1984 }
1985
1986 impl<'a, A> MultiStr for Pseudo<'a, A> where A: MultiStr {
1987 #[inline]
1988 fn find_map<B, F>(&self, mut f: F) -> Option<B> where F: FnMut(&str) -> Option<B> {
1989 self.pseudos.find_map(|x| {
1990 f(&format!(".{}{}", self.class_name, x))
1991 })
1992 }
1993 }
1994
1995
1996 #[derive(Debug)]
1997 struct FnFragment<F>(F);
1998
1999 impl<F> Fragment for FnFragment<F> where F: Fn(FragmentBuilder<'_>) -> FragmentBuilder<'_> {
2000 #[inline]
2001 fn apply<'a>(&self, dom: FragmentBuilder<'a>) -> FragmentBuilder<'a> {
2002 (self.0)(dom)
2003 }
2004 }
2005
2006 #[inline]
2007 pub fn fragment<F>(f: F) -> impl Fragment where F: Fn(FragmentBuilder<'_>) -> FragmentBuilder<'_> {
2008 FnFragment(f)
2009 }
2010
2011 #[inline]
2012 pub fn box_fragment<F>(f: F) -> BoxFragment where F: Fn(FragmentBuilder<'_>) -> FragmentBuilder<'_> + Send + Sync + 'static {
2013 Box::new(FnFragment(f))
2014 }
2015}
2016
2017
2018#[cfg(test)]
2019mod tests {
2020 use super::{DomBuilder, text_signal, RefFn};
2021 use crate::{html, shadow_root, ShadowRootMode, with_cfg};
2022 use futures_signals::signal::{always, SignalExt};
2023 use once_cell::sync::Lazy;
2024 use web_sys::HtmlElement;
2025
2026 #[test]
2027 fn apply() {
2028 let a: DomBuilder<HtmlElement> = DomBuilder::new_html("div");
2029
2030 fn my_mixin<A: AsRef<HtmlElement>>(builder: DomBuilder<A>) -> DomBuilder<A> {
2031 builder.style("foo", "bar")
2032 }
2033
2034 let _ = a.apply(my_mixin);
2035 }
2036
2037 #[test]
2038 fn children_mut() {
2039 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2040 .children(&mut [
2041 DomBuilder::<HtmlElement>::new_html("div").into_dom(),
2042 DomBuilder::<HtmlElement>::new_html("div").into_dom(),
2043 DomBuilder::<HtmlElement>::new_html("div").into_dom(),
2044 ]);
2045 }
2046
2047 #[test]
2048 fn children_value() {
2049 let v: Vec<u32> = vec![];
2050
2051 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2052 .children(v.iter().map(|_| {
2053 DomBuilder::<HtmlElement>::new_html("div").into_dom()
2054 }));
2055 }
2056
2057 #[test]
2058 fn text_signal_types() {
2059 let _ = text_signal(always("foo"));
2060 let _ = text_signal(always("foo".to_owned()));
2061 let _ = text_signal(always("foo".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())));
2062 }
2071
2072 #[test]
2073 fn property_signal_types() {
2074 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2075 .prop("foo", "hi")
2076 .prop("foo", 5)
2077 .prop(["foo", "-webkit-foo", "-ms-foo"], "hi")
2078
2079 .prop_signal("foo", always("hi"))
2080 .prop_signal("foo", always(5))
2081 .prop_signal("foo", always(Some("hi")))
2082
2083 .prop_signal(["foo", "-webkit-foo", "-ms-foo"], always("hi"))
2084 .prop_signal(["foo", "-webkit-foo", "-ms-foo"], always(5))
2085 .prop_signal(["foo", "-webkit-foo", "-ms-foo"], always(Some("hi")))
2086 ;
2087 }
2088
2089 #[test]
2090 fn attribute_signal_types() {
2091 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2092 .attr("foo", "hi")
2093 .attr(["foo", "-webkit-foo", "-ms-foo"], "hi")
2094
2095 .attr_signal("foo", always("hi"))
2096 .attr_signal("foo", always(Some("hi")))
2097
2098 .attr_signal(["foo", "-webkit-foo", "-ms-foo"], always("hi"))
2099 .attr_signal(["foo", "-webkit-foo", "-ms-foo"], always(Some("hi")))
2100 ;
2101 }
2102
2103 #[test]
2104 fn class_signal_types() {
2105 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2106 .class("foo")
2107 .class(["foo", "-webkit-foo", "-ms-foo"])
2108
2109 .class_signal("foo", always(true))
2110 .class_signal(["foo", "-webkit-foo", "-ms-foo"], always(true))
2111 ;
2112 }
2113
2114 #[test]
2115 fn style_signal_types() {
2116 static FOO: Lazy<String> = Lazy::new(|| "foo".to_owned());
2117
2118 let _a: DomBuilder<HtmlElement> = DomBuilder::new_html("div")
2119 .style_signal("foo", always("bar"))
2120 .style_signal("foo", always("bar".to_owned()))
2121 .style_signal("foo", always("bar".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())))
2122
2123 .style("foo".to_owned(), "bar".to_owned())
2124 .style_signal("foo".to_owned(), always("bar".to_owned()))
2125
2126 .style(&"foo".to_owned(), &"bar".to_owned())
2127 .style_signal(&*FOO, always(&*FOO))
2131
2132 .style_signal(RefFn::new(vec!["-moz-foo", "-webkit-foo", "foo"], |x| x.as_slice()), always(RefFn::new(vec!["bar"], |x| x.as_slice())))
2134
2135 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar"))
2136 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar".to_owned()))
2137 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar".to_owned()).map(|x| RefFn::new(x, |x| x.as_str())))
2138
2139 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(["bar", "qux"]))
2140 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(["bar".to_owned(), "qux".to_owned()]))
2141
2142 .style_signal("foo", always(Some("bar")))
2146 .style_signal("foo", always(Some("bar".to_owned())))
2147 .style_signal("foo", always("bar".to_owned()).map(|x| Some(RefFn::new(x, |x| x.as_str()))))
2148
2149 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(Some("bar")))
2150 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always(Some("bar".to_owned())))
2151 .style_signal(["-moz-foo", "-webkit-foo", "foo"], always("bar".to_owned()).map(|x| Some(RefFn::new(x, |x| x.as_str()))))
2152 ;
2153 }
2154
2155 #[test]
2156 fn shadow_root() {
2157 let _a = html!("div", {
2158 .shadow_root!(ShadowRootMode::Closed => {
2159 .children(&mut [
2160 html!("span")
2161 ])
2162 })
2163 });
2164 }
2165
2166 #[test]
2167 fn with_cfg() {
2168 let _a = html!("div", {
2169 .with_cfg!(target_arch = "wasm32", {
2170 .attr("foo", "bar")
2171 })
2172
2173 .with_cfg!(all(not(foo), bar = "test", feature = "hi"), {
2174 .attr("foo", "bar")
2175 })
2176 });
2177 }
2178}