1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fmt::Display;
4use std::rc::Rc;
5
6use wasm_bindgen::JsCast;
7
8use crate::events::*;
9use avalanche::renderer::{HasChildrenMarker, NativeType};
10use avalanche::{Component, Context, View};
11
12#[derive(Clone, PartialEq)]
14pub struct Text {
15 pub(crate) text: String,
16 updated: bool,
17 location: (u32, u32),
18 key: Option<String>,
19}
20#[derive(Default)]
21pub struct TextBuilder {
22 text: Option<String>,
23 updated: bool,
24 key: Option<String>,
25}
26
27impl TextBuilder {
28 pub fn new() -> Self {
29 Default::default()
30 }
31
32 pub fn text<T: ToString>(mut self, text: T, updated: bool) -> Self {
33 self.text = Some(text.to_string());
34 self.updated = updated;
35 self
36 }
37
38 pub fn __last<T: ToString>(mut self, text: T, updated: bool) -> Self {
39 self.text = Some(text.to_string());
40 self.updated = updated;
41 self
42 }
43
44 pub fn key<T: ToString>(mut self, key: T, _updated: bool) -> Self {
45 self.key = Some(key.to_string());
46 self
47 }
48
49 pub fn build(self, location: (u32, u32)) -> Text {
50 Text {
51 text: self.text.unwrap(),
52 updated: self.updated,
53 key: self.key,
54 location,
55 }
56 }
57}
58
59impl Component for Text {
60 type Builder = TextBuilder;
61
62 fn render(&self, _: Context) -> View {
63 ().into()
64 }
65 fn native_type(&self) -> Option<NativeType> {
66 let action = NativeType {
67 handler: "avalanche_web_text",
68 name: "",
69 };
70
71 Some(action)
72 }
73
74 fn updated(&self) -> bool {
75 self.updated
76 }
77
78 fn location(&self) -> Option<(u32, u32)> {
79 Some(self.location)
80 }
81
82 fn key(&self) -> Option<&str> {
83 self.key.as_deref()
84 }
85}
86
87pub(crate) enum Attr {
88 Prop(Option<Cow<'static, str>>),
89 Handler(Rc<dyn Fn(Event)>),
90}
91#[derive(Default)]
92#[doc(hidden)]
93pub struct RawElement {
94 pub(crate) attrs: HashMap<&'static str, (Attr, bool)>,
96 pub(crate) attrs_updated: bool,
97 pub(crate) children: Vec<View>,
98 pub(crate) children_updated: bool,
99 pub(crate) value_controlled: bool,
100 pub(crate) checked_controlled: bool,
101 pub(crate) key: Option<String>,
102 pub(crate) location: (u32, u32),
103 pub(crate) tag: &'static str,
104}
105
106impl RawElement {
107 fn attr(&mut self, name: &'static str, attr: Attr, updated: bool) {
108 self.attrs.insert(name, (attr, updated));
109 self.attrs_updated |= updated;
110 }
111
112 fn children(&mut self, children: Vec<View>, updated: bool) {
113 self.children = children;
114 self.children_updated = updated;
115 }
116}
117
118impl Component for RawElement {
119 type Builder = ();
120
121 fn render(&self, _: Context) -> View {
122 HasChildrenMarker {
123 children: self.children.clone(),
124 }
125 .into()
126 }
127
128 fn updated(&self) -> bool {
129 self.attrs_updated || self.children_updated
130 }
131
132 fn native_type(&self) -> Option<NativeType> {
133 Some(NativeType {
134 handler: "avalanche_web",
135 name: self.tag,
136 })
137 }
138
139 fn location(&self) -> Option<(u32, u32)> {
140 Some(self.location)
141 }
142
143 fn key(&self) -> Option<&str> {
144 self.key.as_deref()
145 }
146}
147
148#[derive(Debug, Copy, Clone)]
149pub enum Dir {
150 Ltr,
151 Rtl,
152 Auto,
153}
154
155impl std::fmt::Display for Dir {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(
158 f,
159 "{}",
160 match self {
161 Dir::Ltr => "ltr",
162 Dir::Rtl => "rtl",
163 Dir::Auto => "auto",
164 }
165 )
166 }
167}
168
169#[derive(Debug, Copy, Clone)]
170#[non_exhaustive]
171pub enum Translate {
172 Yes,
173 No,
174}
175
176impl std::fmt::Display for Translate {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 write!(
179 f,
180 "{}",
181 match self {
182 Translate::Yes => "yes",
183 Translate::No => "no",
184 }
185 )
186 }
187}
188
189#[doc(hidden)]
193pub trait AssociatedNativeElement {
194 type NativeElement: JsCast;
195}
196
197macro_rules! def_component {
198 (
199 $native_tag:expr;
200 $native_element:path;
201 $tag:ident;
202 $tag_builder:ident;
203 ) => {
204 pub struct $tag;
205
206 impl ::avalanche::Component for $tag {
209 type Builder = $tag_builder;
210
211 fn render(&self, _: Context) -> View {
212 unreachable!()
213 }
214
215 fn updated(&self) -> bool {
216 unreachable!()
217 }
218 }
219
220 pub struct $tag_builder {
221 raw: RawElement,
222 }
223
224 impl $tag_builder {
225 pub fn new() -> Self {
226 Default::default()
227 }
228
229 pub fn build(mut self, location: (u32, u32)) -> RawElement {
230 self.raw.location = location;
231 self.raw.tag = $native_tag;
232 self.raw
233 }
234
235 pub fn key<S: ToString>(mut self, key: S, _updated: bool) -> Self {
236 self.raw.key = Some(key.to_string());
237 self
238 }
239
240 pub fn child(mut self, child: View, updated: bool) -> Self {
241 self.raw.children(vec![child], updated);
242 self
243 }
244
245 pub fn children<T: Into<Vec<View>>>(mut self, children: T, updated: bool) -> Self {
246 self.raw.children(children.into(), updated);
247 self
248 }
249
250 pub fn __last<T: Into<Vec<View>>>(mut self, children: T, updated: bool) -> Self {
251 self.raw.children(children.into(), updated);
252 self
253 }
254 }
255
256 impl Default for $tag_builder {
257 fn default() -> Self {
258 Self {
259 raw: std::default::Default::default(),
260 }
261 }
262 }
263
264 impl AssociatedNativeElement for $tag_builder {
265 type NativeElement = $native_element;
266 }
267
268 add_global_attrs! {$tag_builder}
269 };
270}
271
272macro_rules! def_component_attrs {
273 (
274 $mac:ident;
275 props: $($propnative:expr => $propident:ident : $proptype:ty),*;
276 $(bool_props: $($boolpropnative: expr => $boolpropident:ident),*;)?
277 $(listeners: $($listennative:expr => $listenident:ident : $listentype:ty),*;)?
278 ) => {
279 macro_rules! $mac {
280 ($builder:ty) => {
281 impl $builder {
282 $(
283 pub fn $propident<T>(mut self, val: T, updated: bool) -> Self where T : Into<$proptype> {
284 self.raw.attr(
285 $propnative,
286 Attr::Prop(Some(Cow::Owned(Into::<$proptype>::into(val).to_string()))),
287 updated
288 );
289 self
290 }
291 )*
292
293 $(
294 $(
295 pub fn $boolpropident(mut self, val: bool, updated: bool) -> Self {
296 self.raw.attr(
297 $boolpropnative,
298 Attr::Prop(val.then(|| Cow::Borrowed($boolpropnative))),
299 updated
300 );
301 self
302 }
303 )*
304 )?
305
306 $(
307 $(
308 pub fn $listenident(mut self, f: impl Fn(TypedEvent::<$listentype, <$builder as AssociatedNativeElement>::NativeElement>) + 'static, updated: bool) -> Self {
309 self.raw.attr(
310 $listennative,
311 Attr::Handler(std::rc::Rc::new(move |e: Event| f(
312 TypedEvent::<$listentype, <$builder as AssociatedNativeElement>::NativeElement>::new(e.dyn_into::<$listentype>().unwrap())
313 ))),
314 updated
315 );
316 self
317 }
318 )*
319 )?
320 }
321 }
322 }
323 }
324}
325
326def_component_attrs! {
327 add_global_attrs;
328 props:
329 "accesskey" => access_key: String,
330 "class" => class: String,
331 "contenteditable" => content_editable: bool,
334 "dir" => dir: Dir,
335 "draggable" => draggable: bool,
336 "id" => id: String,
337 "lang" => lang: String,
338 "placeholder" => placeholder: String,
339 "slot" => slot: String,
340 "spellcheck" => spell_check: bool,
341 "style" => style: String,
342 "tabindex" => tab_index: i16,
343 "title" => title: String,
344 "translate" => translate: Translate;
345 bool_props:
346 "hidden" => hidden;
347 listeners:
348 "blur" => on_blur: FocusEvent,
350 "focus" => on_focus: FocusEvent,
351 "compositionstart" => on_composition_start: CompositionEvent,
359 "compositionupdate" => on_composition_update: CompositionEvent,
360 "compositionend" => on_composition_end: CompositionEvent,
361
362 "change" => on_change: Event,
364 "input" => on_input: Event,
365 "reset" => on_reset: Event,
367 "submit" => on_submit: Event,
368 "invalid" => on_invalid: Event,
369
370 "load" => on_load: Event,
372 "error" => on_error: Event,
373
374 "keydown" => on_key_down: KeyboardEvent,
376 "keyup" => on_key_up: KeyboardEvent,
377
378 "canplay" => on_can_play: Event,
380 "canplaythrough" => on_can_play_through: Event,
381 "durationchange" => on_duration_change: Event,
382 "emptied" => on_emptied: Event,
383 "ended" => on_ended: Event,
384 "loadeddata" => on_loaded_data: Event,
385 "loadedmetadata" => on_loaded_metadata: Event,
386 "pause" => on_pause: Event,
387 "play" => on_play: Event,
388 "playing" => on_playing: Event,
389 "ratechange" => on_rate_change: Event,
390 "seeked" => on_seeked: Event,
391 "seeking" => on_seeking: Event,
392 "stalled" => on_stalled: Event,
393 "suspend" => on_suspend: Event,
394 "timeupdate" => on_time_update: Event,
395 "volumechange" => on_volume_change: Event,
396 "waiting" => on_waiting: Event,
397
398 "auxclick" => on_aux_click: MouseEvent,
400 "click" => on_click: MouseEvent,
401 "contextmenu" => on_context_menu: MouseEvent,
402 "dblclick" => on_double_click: MouseEvent,
403 "mousedown" => on_mouse_down: MouseEvent,
404 "mouseenter" => on_mouse_enter: MouseEvent,
405 "mouseleave" => on_mouse_leave: MouseEvent,
406 "mousemove" => on_mouse_move: MouseEvent,
407 "mouseover" => on_mouse_over: MouseEvent,
408 "mouseout" => on_mouse_out: MouseEvent,
409 "mouseup" => on_mouse_up: MouseEvent,
410 "pointerlockchange" => on_pointer_lock_change: Event,
411 "pointerlockerror" => on_pointer_lock_error: Event,
412 "select" => on_select: Event,
413
414 "wheel" => on_wheel: WheelEvent,
416
417 "drag" => on_drag: DragEvent,
419 "dragend" => on_drag_end: DragEvent,
420 "dragenter" => on_drag_enter: DragEvent,
421 "dragstart" => on_drag_start: DragEvent,
422 "dragleave" => on_drag_leave: DragEvent,
423 "dragover" => on_drag_over: DragEvent,
424 "drop" => on_drop: DragEvent,
425
426 "touchcancel" => on_touch_cancel: TouchEvent,
428 "touchend" => on_touch_end: TouchEvent,
429 "touchmove" => on_touch_move: TouchEvent,
430 "touchstart" => on_touch_start: TouchEvent,
431
432 "pointerover" => on_pointer_over: PointerEvent,
434 "pointerenter" => on_pointer_enter: PointerEvent,
435 "pointerdown" => on_pointer_down: PointerEvent,
436 "pointermove" => on_pointer_move: PointerEvent,
437 "pointerup" => on_pointer_up: PointerEvent,
438 "pointercancel" => on_pointer_cancel: PointerEvent,
439 "pointerout" => on_pointer_out: PointerEvent,
440 "pointerleave" => on_pointer_leave: PointerEvent,
441 "gotpointercapture" => on_got_pointer_capture: PointerEvent,
442 "lostpointercapture" => on_lost_pointer_capture: PointerEvent,
443
444 "scroll" => on_scroll: Event,
446
447 "animationstart" => on_animation_start: AnimationEvent,
449 "animationcancel" => on_animation_cancel: AnimationEvent,
450 "animationend" => on_animation_end: AnimationEvent,
451 "animationinteraction" => on_animation_interaction: AnimationEvent,
452
453 "transitionstart" => on_transition_start: TransitionEvent,
455 "transitioncancel" => on_transition_cancel: TransitionEvent,
456 "transitionend" => on_transition_end: TransitionEvent,
457 "transitionrun" => on_transition_run: TransitionEvent,
458
459 "abort" => on_abort: Event,
461 "loadstart" => on_load_start: ProgressEvent,
462 "progress" => on_progress: ProgressEvent;
463}
464
465def_component_attrs! {
466 add_cite_attr;
467 props:
468 "cite" => cite: String;
469}
470
471def_component_attrs! {
472 add_datetime_attr;
473 props:
474 "datetime" => date_time: String;
475}
476
477def_component_attrs! {
478 add_string_value_attr;
479 props:
480 "value" => value: String;
481}
482
483def_component_attrs! {
484 add_name_attr;
485 props:
486 "name" => name: String;
487}
488
489def_component! {
490 "div";
491 web_sys::HtmlDivElement;
492 Div;
493 DivBuilder;
494}
495
496def_component! {
497 "h1";
498 web_sys::HtmlHeadingElement;
499 H1;
500 H1Builder;
501}
502
503def_component! {
504 "h2";
505 web_sys::HtmlHeadingElement;
506 H2;
507 H2Builder;
508}
509
510def_component! {
511 "h3";
512 web_sys::HtmlHeadingElement;
513 H3;
514 H3Builder;
515}
516
517def_component! {
518 "h4";
519 web_sys::HtmlHeadingElement;
520 H4;
521 H4Builder;
522}
523
524def_component! {
525 "h5";
526 web_sys::HtmlHeadingElement;
527 H5;
528 H5Builder;
529}
530
531def_component! {
532 "h6";
533 web_sys::HtmlHeadingElement;
534 H6;
535 H6Builder;
536}
537
538def_component! {
541 "body";
542 web_sys::HtmlBodyElement;
543 Body;
544 BodyBuilder;
545}
546
547def_component! {
548 "address";
549 web_sys::HtmlSpanElement;
550 Address;
551 AddressBuilder;
552}
553
554def_component! {
555 "article";
556 web_sys::HtmlElement;
557 Article;
558 ArticleBuilder;
559}
560
561def_component! {
562 "aside";
563 web_sys::HtmlElement;
564 Aside;
565 AsideBuilder;
566}
567
568def_component! {
569 "footer";
570 web_sys::HtmlElement;
571 Footer;
572 FooterBuilder;
573}
574
575def_component! {
576 "header";
577 web_sys::HtmlElement;
578 Header;
579 HeaderBuilder;
580}
581
582def_component! {
583 "hgroup";
584 web_sys::HtmlElement;
585 HGroup;
586 HGroupBuilder;
587}
588
589def_component! {
590 "main";
591 web_sys::HtmlElement;
592 Main;
593 MainBuilder;
594}
595
596def_component! {
597 "nav";
598 web_sys::HtmlElement;
599 Nav;
600 NavBuilder;
601}
602
603def_component! {
604 "section";
605 web_sys::HtmlElement;
606 Section;
607 SectionBuilder;
608}
609
610def_component! {
611 "blockquote";
612 web_sys::HtmlQuoteElement;
613 BlockQuote;
614 BlockQuoteBuilder;
615}
616
617add_cite_attr! {BlockQuoteBuilder}
618
619def_component! {
620 "dd";
621 web_sys::HtmlElement;
622 Dd;
623 DdBuilder;
624}
625
626def_component! {
627 "dl";
628 web_sys::HtmlElement;
629 Dl;
630 DlBuilder;
631}
632
633def_component! {
634 "dt";
635 web_sys::HtmlElement;
636 Dt;
637 DtBuilder;
638}
639
640def_component! {
641 "figcaption";
642 web_sys::HtmlElement;
643 FigCaption;
644 FigCaptionBuilder;
645}
646
647def_component! {
648 "figure";
649 web_sys::HtmlElement;
650 Figure;
651 FigureBuilder;
652}
653
654def_component! {
655 "hr";
656 web_sys::HtmlHrElement;
657 Hr;
658 HrBuilder;
659}
660
661def_component! {
662 "li";
663 web_sys::HtmlLiElement;
664 Li;
665 LiBuilder;
666}
667
668def_component_attrs! {
669 add_li_attrs;
670 props:
671 "value" => value: u32;
672}
673add_li_attrs! {LiBuilder}
674
675def_component! {
676 "ol";
677 web_sys::HtmlOListElement;
678 Ol;
679 OlBuilder;
680}
681
682def_component_attrs! {
683 add_ol_attrs;
684 props:
685 "start" => start: i32,
686 "type" => type_: String;
687 bool_props:
688 "reversed" => reversed;
689}
690add_ol_attrs! {OlBuilder}
691
692def_component! {
693 "p";
694 web_sys::HtmlParagraphElement;
695 P;
696 PBuilder;
697}
698
699def_component! {
700 "pre";
701 web_sys::HtmlPreElement;
702 Pre;
703 PreBuilder;
704}
705
706def_component! {
707 "ul";
708 web_sys::HtmlUListElement;
709 Ul;
710 UlBuilder;
711}
712
713def_component! {
714 "a";
715 web_sys::HtmlAnchorElement;
716 A;
717 ABuilder;
718}
719
720def_component_attrs! {
721 add_a_attrs;
722 props:
723 "download" => download: String,
724 "href" => href: String,
725 "hreflanf" => href_lang: String,
726 "ping" => ping: String,
727 "referrerpolicy" => referrer_policy: String,
728 "rel" => rel: String,
729 "target" => target: String,
730 "type" => type_: String;
731}
732add_a_attrs! {ABuilder}
733
734def_component! {
735 "abbr";
736 web_sys::HtmlElement;
737 Abbr;
738 AbbrBuilder;
739}
740
741def_component! {
742 "b";
743 web_sys::HtmlElement;
744 B;
745 BBuilder;
746}
747
748def_component! {
749 "bdi";
750 web_sys::HtmlElement;
751 Bdi;
752 BdiBuilder;
753}
754
755def_component! {
756 "bdo";
757 web_sys::HtmlElement;
758 Bdo;
759 BdoBuilder;
760}
761
762def_component! {
763 "br";
764 web_sys::HtmlBrElement;
765 Br;
766 BrBuilder;
767}
768
769def_component! {
770 "cite";
771 web_sys::HtmlSpanElement;
772 Cite;
773 CiteBuilder;
774}
775
776def_component! {
777 "code";
778 web_sys::HtmlSpanElement;
779 Code;
780 CodeBuilder;
781}
782
783def_component! {
784 "data";
785 web_sys::HtmlDataElement;
786 Data;
787 DataBuilder;
788}
789add_string_value_attr! {DataBuilder}
790
791def_component! {
792 "dfn";
793 web_sys::HtmlElement;
794 Dfn;
795 DfnBuilder;
796}
797
798def_component! {
799 "em";
800 web_sys::HtmlSpanElement;
801 Em;
802 EmBuilder;
803}
804
805def_component! {
806 "i";
807 web_sys::HtmlElement;
808 I;
809 IBuilder;
810}
811
812def_component! {
813 "kbd";
814 web_sys::HtmlElement;
815 Kbd;
816 KbdBuilder;
817}
818
819def_component! {
820 "mark";
821 web_sys::HtmlElement;
822 Mark;
823 MarkBuilder;
824}
825
826def_component! {
827 "q";
828 web_sys::HtmlQuoteElement;
829 Q;
830 QBuilder;
831}
832add_cite_attr! {QBuilder}
833
834def_component! {
835 "rp";
836 web_sys::HtmlElement;
837 Rp;
838 RpBuilder;
839}
840
841def_component! {
842 "rt";
843 web_sys::HtmlElement;
844 Rt;
845 RtBuilder;
846}
847
848def_component! {
849 "rtc";
850 web_sys::HtmlElement;
851 Rtc;
852 RtcBuilder;
853}
854
855def_component! {
856 "ruby";
857 web_sys::HtmlElement;
858 Ruby;
859 RubyBuilder;
860}
861
862def_component! {
863 "s";
864 web_sys::HtmlElement;
865 S;
866 SBuilder;
867}
868
869def_component! {
870 "samp";
871 web_sys::HtmlElement;
872 Samp;
873 SampBuilder;
874}
875
876def_component! {
877 "small";
878 web_sys::HtmlElement;
879 Small;
880 SmallBuilder;
881}
882
883def_component! {
884 "span";
885 web_sys::HtmlSpanElement;
886 Span;
887 SpanBuilder;
888}
889
890def_component! {
891 "strong";
892 web_sys::HtmlElement;
893 Strong;
894 StrongBuilder;
895}
896
897def_component! {
898 "sub";
899 web_sys::HtmlElement;
900 Sub;
901 SubBuilder;
902}
903
904def_component! {
905 "sup";
906 web_sys::HtmlElement;
907 Sup;
908 SupBuilder;
909}
910
911def_component! {
912 "time";
913 web_sys::HtmlTimeElement;
914 Time;
915 TimeBuilder;
916}
917
918add_datetime_attr! {TimeBuilder}
919
920def_component! {
921 "u";
922 web_sys::HtmlElement;
923 U;
924 UBuilder;
925}
926
927def_component! {
928 "var";
929 web_sys::HtmlElement;
930 Var;
931 VarBuilder;
932}
933
934def_component! {
935 "wbr";
936 web_sys::HtmlElement;
937 Wbr;
938 WbrBuilder;
939}
940
941def_component! {
942 "area";
943 web_sys::HtmlAreaElement;
944 Area;
945 AreaBuilder;
946}
947add_a_attrs! {AreaBuilder}
948
949def_component_attrs! {
950 add_area_attrs;
951 props:
952 "coords" => coords: String,
953 "shape" => shape: String;
954}
955add_area_attrs! {AreaBuilder}
956
957pub enum CrossOrigin {
958 Anonymous,
959 UseCredentials,
960}
961
962impl Display for CrossOrigin {
963 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
964 write!(
965 f,
966 "{}",
967 match self {
968 CrossOrigin::Anonymous => "anonymous",
969 CrossOrigin::UseCredentials => "use-credentials",
970 }
971 )
972 }
973}
974
975pub enum Preload {
976 None,
977 Metadata,
978 Auto,
979}
980
981impl Display for Preload {
982 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
983 write!(
984 f,
985 "{}",
986 match self {
987 Preload::None => "none",
988 Preload::Metadata => "metadata",
989 Preload::Auto => "auto",
990 }
991 )
992 }
993}
994
995def_component_attrs! {
996 add_media_attrs;
997 props:
998 "crossorigin" => cross_origin: CrossOrigin,
999 "preload" => preload: Preload,
1000 "src" => src: String;
1001 bool_props:
1002 "autoplay" => autoplay,
1003 "controls" => controls,
1004 "disableRemotePlayback" => disable_remote_playback,
1005 "loop" => loop_,
1006 "muted" => muted;
1007}
1008
1009def_component! {
1010 "audio";
1011 web_sys::HtmlAudioElement;
1012 Audio;
1013 AudioBuilder;
1014}
1015add_media_attrs! {AudioBuilder}
1016
1017def_component_attrs! {
1018 add_width_height_attrs;
1019 props:
1020 "width" => width: f64,
1021 "height" => height: f64;
1022}
1023
1024def_component! {
1025 "video";
1026 web_sys::HtmlVideoElement;
1027 Video;
1028 VideoBuilder;
1029}
1030add_media_attrs! {VideoBuilder}
1031add_width_height_attrs! {VideoBuilder}
1032
1033def_component_attrs! {
1034 add_video_attrs;
1035 props:
1036 "poster" => poster: String;
1037 bool_props:
1038 "autoPictureInPicture" => auto_picture_in_picture,
1039 "disablePictureInPicture" => disable_picture_in_picture,
1040 "playsinline" => plays_inline;
1041}
1042add_video_attrs! {VideoBuilder}
1043
1044def_component! {
1045 "img";
1046 web_sys::HtmlImageElement;
1047 Img;
1048 ImgBuilder;
1049}
1050add_width_height_attrs! {ImgBuilder}
1051
1052pub enum Decoding {
1053 Sync,
1054 Async,
1055 Auto,
1056}
1057
1058impl Display for Decoding {
1059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1060 write!(
1061 f,
1062 "{}",
1063 match self {
1064 Decoding::Sync => "sync",
1065 Decoding::Async => "async",
1066 Decoding::Auto => "auto",
1067 }
1068 )
1069 }
1070}
1071
1072pub enum Loading {
1073 Eager,
1074 Lazy,
1075}
1076
1077impl Display for Loading {
1078 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1079 write!(
1080 f,
1081 "{}",
1082 match self {
1083 Loading::Eager => "eager",
1084 Loading::Lazy => "lazy",
1085 }
1086 )
1087 }
1088}
1089
1090def_component_attrs! {
1091 add_img_attrs;
1092 props:
1093 "alt" => alt: String,
1094 "crossorigin" => cross_origin: CrossOrigin,
1095 "decoding" => decoding: Decoding,
1096 "loading" => loading: Loading,
1097 "referrerpolicy" => referrer_policy: String,
1098 "sizes" => sizes: String,
1099 "src" => src: String,
1100 "srcset" => src_set: String,
1101 "usemap" => use_map: String;
1102 bool_props:
1103 "ismap" => is_map;
1104}
1105add_img_attrs! {ImgBuilder}
1106
1107def_component! {
1108 "map";
1109 web_sys::HtmlMapElement;
1110 Map;
1111 MapBuilder;
1112}
1113add_name_attr! {MapBuilder}
1114
1115def_component! {
1116 "track";
1117 web_sys::HtmlTrackElement;
1118 Track;
1119 TrackBuilder;
1120}
1121
1122pub enum TrackKind {
1123 Subtitles,
1124 Captions,
1125 Descriptions,
1126 Chapters,
1127 Metadata,
1128}
1129
1130impl Display for TrackKind {
1131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1132 write!(
1133 f,
1134 "{}",
1135 match self {
1136 TrackKind::Subtitles => "subtitles",
1137 TrackKind::Captions => "captions",
1138 TrackKind::Descriptions => "descriptions",
1139 TrackKind::Chapters => "chapters",
1140 TrackKind::Metadata => "metadata",
1141 }
1142 )
1143 }
1144}
1145
1146def_component_attrs! {
1147 add_track_attrs;
1148 props:
1149 "kind" => kind: TrackKind,
1150 "label" => label: String,
1151 "src" => src: String,
1152 "srclang" => src_lang: String;
1153 bool_props:
1154 "default" => default;
1155}
1156add_track_attrs! {TrackBuilder}
1157
1158def_component_attrs! {
1159 add_base_img_attrs;
1160 props:
1161 "alt" => alt: String,
1162 "height" => height: f64,
1163 "src" => src: String,
1164 "width" => width: f64;
1165}
1166
1167def_component! {
1168 "embed";
1169 web_sys::HtmlEmbedElement;
1170 Embed;
1171 EmbedBuilder;
1172}
1173add_width_height_attrs! {EmbedBuilder}
1174
1175def_component_attrs! {
1176 add_embed_attrs;
1177 props:
1178 "src" => src: String,
1179 "type" => type_: String;
1180}
1181add_embed_attrs! {EmbedBuilder}
1182
1183def_component! {
1184 "iframe";
1185 web_sys::HtmlIFrameElement;
1186 IFrame;
1187 IFrameBuilder;
1188}
1189add_width_height_attrs! {IFrameBuilder}
1190
1191def_component_attrs! {
1192 add_iframe_attrs;
1193 props:
1194 "allow" => allow: String,
1195 "csp" => csp: String,
1196 "loading" => loading: Loading,
1197 "name" => name: String,
1198 "referrerpolicy" => referrer_policy: String,
1199 "sandbox" => sandbox: String,
1200 "src" => src: String,
1201 "srcdoc" => src_doc: String;
1202 bool_props:
1203 "allowfullscreen" => allow_full_screen,
1204 "allowpaymentrequest" => allow_payment_request;
1205}
1206add_iframe_attrs! {IFrameBuilder}
1207
1208def_component! {
1209 "object";
1210 web_sys::HtmlObjectElement;
1211 Object;
1212 ObjectBuilder;
1213}
1214add_width_height_attrs! {ObjectBuilder}
1215add_name_attr! {ObjectBuilder}
1216
1217def_component_attrs! {
1218 add_object_attrs;
1219 props:
1220 "data" => data: String,
1221 "form" => form: String,
1222 "type" => type_: String,
1223 "usemap" => use_map: String;
1224 bool_props:
1225 "typemustmatch" => type_must_match;
1226}
1227add_object_attrs! {ObjectBuilder}
1228
1229def_component! {
1230 "param";
1231 web_sys::HtmlParamElement;
1232 Param;
1233 ParamBuilder;
1234}
1235add_string_value_attr! {ParamBuilder}
1236add_name_attr! {ParamBuilder}
1237
1238def_component! {
1239 "picture";
1240 web_sys::HtmlPictureElement;
1241 Picture;
1242 PictureBuilder;
1243}
1244
1245def_component! {
1246 "ins";
1247 web_sys::HtmlModElement;
1248 Ins;
1249 InsBuilder;
1250}
1251add_cite_attr! {InsBuilder}
1252add_datetime_attr! {InsBuilder}
1253
1254def_component! {
1255 "del";
1256 web_sys::HtmlModElement;
1257 Del;
1258 DelBuilder;
1259}
1260add_cite_attr! {DelBuilder}
1261add_datetime_attr! {DelBuilder}
1262
1263def_component! {
1264 "caption";
1265 web_sys::HtmlTableCaptionElement;
1266 Caption;
1267 CaptionBuilder;
1268}
1269
1270def_component! {
1271 "col";
1272 web_sys::HtmlTableColElement;
1273 Col;
1274 ColBuilder;
1275}
1276
1277def_component_attrs! {
1278 add_col_attrs;
1279 props:
1280 "span" => span: u32;
1281}
1282add_col_attrs! {ColBuilder}
1283
1284def_component! {
1286 "colgroup";
1287 web_sys::HtmlTableColElement;
1288 ColGroup;
1289 ColGroupBuilder;
1290}
1291
1292def_component! {
1293 "table";
1294 web_sys::HtmlTableElement;
1295 Table;
1296 TableBuilder;
1297}
1298
1299def_component! {
1300 "tbody";
1301 web_sys::HtmlTableSectionElement;
1302 TBody;
1303 TBodyBuilder;
1304}
1305
1306def_component! {
1307 "td";
1308 web_sys::HtmlTableCellElement;
1309 Td;
1310 TdBuilder;
1311}
1312
1313def_component_attrs! {
1314 add_td_th_attrs;
1315 props:
1316 "colspan" => col_span: u32,
1317 "rowspan" => row_span: u32,
1318 "headers" => headers: String;
1319}
1320add_td_th_attrs! {TdBuilder}
1321
1322def_component! {
1323 "tfoot";
1324 web_sys::HtmlTableSectionElement;
1325 TFoot;
1326 TFootBuilder;
1327}
1328
1329def_component! {
1331 "th";
1332 web_sys::HtmlTableCellElement;
1333 Th;
1334 ThBuilder;
1335}
1336add_td_th_attrs! {ThBuilder}
1337
1338pub enum Scope {
1339 Row,
1340 Col,
1341 RowGroup,
1342 ColGroup,
1343 Auto,
1344}
1345
1346impl Display for Scope {
1347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1348 write!(
1349 f,
1350 "{}",
1351 match self {
1352 Scope::Row => "row",
1353 Scope::Col => "col",
1354 Scope::RowGroup => "rowgroup",
1355 Scope::ColGroup => "colgroup",
1356 Scope::Auto => "auto",
1357 }
1358 )
1359 }
1360}
1361
1362def_component_attrs! {
1363 add_th_attrs;
1364 props:
1365 "abbr" => abbr: String,
1366 "scope" => scope: Scope;
1367}
1368add_th_attrs! {ThBuilder}
1369
1370def_component! {
1371 "thead";
1372 web_sys::HtmlTableSectionElement;
1373 THead;
1374 THeadBuilder;
1375}
1376
1377def_component! {
1378 "tr";
1379 web_sys::HtmlTableRowElement;
1380 Tr;
1381 TrBuilder;
1382}
1383
1384def_component_attrs! {
1385 add_form_field_attrs;
1386 props:
1387 "form" => form: String,
1388 "name" => name: String;
1389 bool_props:
1390 "autofocus" => auto_focus,
1391 "disabled" => disabled;
1392}
1393
1394def_component_attrs! {
1395 add_form_submit_attrs;
1396 props:
1397 "formaction" => form_ation: String,
1398 "formenctype" => form_enc_type: String,
1399 "formmethod" => form_method: String,
1400 "formtarget" => form_target: String;
1401 bool_props:
1402 "formnovalidate" => form_no_validate;
1403}
1404
1405def_component_attrs! {
1406 add_type_attr;
1407 props:
1408 "type" => type_: String;
1409}
1410
1411def_component! {
1412 "button";
1413 web_sys::HtmlButtonElement;
1414 Button;
1415 ButtonBuilder;
1416}
1417add_type_attr! {ButtonBuilder}
1418add_form_field_attrs! {ButtonBuilder}
1419add_form_submit_attrs! {ButtonBuilder}
1420add_string_value_attr! {ButtonBuilder}
1421
1422def_component! {
1423 "datalist";
1424 web_sys::HtmlDataListElement;
1425 DataList;
1426 DataListBuilder;
1427}
1428
1429def_component! {
1430 "fieldset";
1431 web_sys::HtmlFieldSetElement;
1432 FieldSet;
1433 FieldSetBuilder;
1434}
1435
1436def_component_attrs! {
1437 add_field_set_attrs;
1438 props:
1439 "form" => form: String;
1440 bool_props:
1441 "disabled" => disabled;
1442}
1443add_field_set_attrs! {FieldSetBuilder}
1444add_name_attr! {FieldSetBuilder}
1445
1446def_component! {
1447 "form";
1448 web_sys::HtmlFormElement;
1449 Form;
1450 FormBuilder;
1451}
1452add_name_attr! {FormBuilder}
1453
1454def_component_attrs! {
1455 add_form_attrs;
1456 props:
1457 "accept-charset" => accept_charset: String,
1458 "autocomplete" => auto_complete: String,
1459 "rel" => rel: String,
1460 "action" => action: String,
1461 "enctype" => enc_type: String,
1462 "method" => method: String,
1463 "target" => target: String;
1464 bool_props:
1465 "novalidate" => no_validate;
1466}
1467add_form_attrs! {FormBuilder}
1468
1469def_component_attrs! {
1470 add_textinput_attrs;
1471 props:
1472 "autocomplete" => auto_complete: String,
1473 "maxlength" => max_length: u32,
1474 "minlength" => min_length: u32;
1475 bool_props:
1476 "readonly" => read_only,
1477 "required" => required;
1478}
1479
1480def_component! {
1481 "input";
1482 web_sys::HtmlInputElement;
1483 Input;
1484 InputBuilder;
1485}
1486add_form_field_attrs! {InputBuilder}
1487add_textinput_attrs! {InputBuilder}
1488add_form_submit_attrs! {InputBuilder}
1489add_base_img_attrs! {InputBuilder}
1490add_type_attr! {InputBuilder}
1491
1492def_component_attrs! {
1493 add_input_attrs;
1494 props:
1495 "capture" => capture: String,
1496 "dirname" => dir_name: String,
1497 "inputmode" => input_mode: String,
1498 "list" => list: String,
1499 "min" => min: String,
1500 "max" => max: String;
1501 bool_props:
1502 "multiple" => multiple;
1503}
1504add_input_attrs! {InputBuilder}
1505
1506impl InputBuilder {
1507 pub fn value<S: Into<String>>(mut self, val: S, updated: bool) -> Self {
1508 self.raw.value_controlled = true;
1509 self.raw
1510 .attr("value", Attr::Prop(Some(Cow::Owned(val.into()))), updated);
1511 self
1512 }
1513
1514 pub fn checked(mut self, val: bool, updated: bool) -> Self {
1515 self.raw.checked_controlled = true;
1516 self.raw.attr(
1517 "checked",
1518 Attr::Prop(val.then(|| Cow::Borrowed("checked"))),
1519 updated,
1520 );
1521 self
1522 }
1523}
1524
1525def_component! {
1526 "textarea";
1527 web_sys::HtmlTextAreaElement;
1528 TextArea;
1529 TextAreaBuilder;
1530}
1531add_form_field_attrs! {TextAreaBuilder}
1532add_textinput_attrs! {TextAreaBuilder}
1533
1534impl TextAreaBuilder {
1535 pub fn value<S: ToString>(mut self, val: S, updated: bool) -> Self {
1536 self.raw.value_controlled = true;
1537 self.raw
1538 .attr("value", Attr::Prop(Some(Cow::Owned(val.to_string()))), updated);
1539 self
1540 }
1541}
1542
1543pub enum Wrap {
1544 Soft,
1545 Hard,
1546 Off,
1547}
1548
1549impl Display for Wrap {
1550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1551 write!(
1552 f,
1553 "{}",
1554 match self {
1555 Wrap::Soft => "soft",
1556 Wrap::Hard => "hard",
1557 Wrap::Off => "off",
1558 }
1559 )
1560 }
1561}
1562
1563def_component_attrs! {
1564 add_textarea_attrs;
1565 props:
1566 "cols" => cols: u32,
1567 "rows" => rows: u32,
1568 "wrap" => wrap: Wrap;
1569}
1570add_textarea_attrs! {TextAreaBuilder}
1571
1572def_component_attrs! {
1573 add_label_attrs;
1574 props:
1575 "for" => for_: String;
1576}
1577
1578def_component! {
1579 "label";
1580 web_sys::HtmlLabelElement;
1581 Label;
1582 LabelBuilder;
1583}
1584add_label_attrs! {LabelBuilder}
1585
1586def_component! {
1587 "legend";
1588 web_sys::HtmlLegendElement;
1589 Legend;
1590 LegendBuilder;
1591}
1592
1593def_component_attrs! {
1594 add_meter_attrs;
1595 props:
1596 "min" => min: f64,
1597 "max" => max: f64,
1598 "low" => low: f64,
1599 "high" => high: f64,
1600 "optimum" => optimum: f64,
1601 "form" => form: String,
1602 "value" => value: String;
1603}
1604
1605def_component! {
1606 "meter";
1607 web_sys::HtmlMeterElement;
1608 Meter;
1609 MeterBuilder;
1610}
1611add_meter_attrs! {MeterBuilder}
1612
1613def_component! {
1614 "optgroup";
1615 web_sys::HtmlOptGroupElement;
1616 OptGroup;
1617 OptGroupBuilder;
1618}
1619
1620def_component_attrs! {
1621 add_label_value_attrs;
1622 props:
1623 "value" => value: String,
1624 "label" => label: String;
1625}
1626add_label_value_attrs! {OptGroupBuilder}
1627
1628def_component! {
1630 "option";
1631 web_sys::HtmlOptionElement;
1632 Opt;
1633 OptBuilder;
1634}
1635add_label_value_attrs! {OptBuilder}
1636
1637def_component_attrs! {
1638 add_opt_attrs;
1639 props:
1640 ;
1641 bool_props:
1642 "disabled" => disabled,
1643 "selected" => selected;
1644}
1645add_opt_attrs! {OptBuilder}
1646
1647def_component! {
1648 "output";
1649 web_sys::HtmlOutputElement;
1650 Output;
1651 OutputBuilder;
1652}
1653
1654def_component_attrs! {
1655 add_output_attrs;
1656 props:
1657 "for" => for_: String,
1658 "form" => form: String,
1659 "name" => name: String;
1660}
1661add_output_attrs! {OutputBuilder}
1662
1663def_component! {
1664 "progress";
1665 web_sys::HtmlProgressElement;
1666 Progress;
1667 ProgressBuilder;
1668}
1669
1670def_component_attrs! {
1671 add_progress_attrs;
1672 props:
1673 "max" => max: f64,
1674 "value" => value: f64;
1675}
1676
1677add_progress_attrs! {ProgressBuilder}
1678
1679def_component! {
1681 "select";
1682 web_sys::HtmlSelectElement;
1683 Select;
1684 SelectBuilder;
1685}
1686
1687add_form_field_attrs! {SelectBuilder}
1688
1689def_component_attrs! {
1690 add_select_attrs;
1691 props:
1692 "autocomplete" => auto_complete: String,
1693 "size" => size: u32;
1694 bool_props:
1695 "multiple" => multiple,
1696 "required" => required;
1697}
1698add_select_attrs! {SelectBuilder}
1699
1700def_component_attrs! {
1701 add_open_attr;
1702 props:
1703 ;
1704 bool_props:
1705 "open" => open;
1706}
1707
1708def_component! {
1709 "details";
1710 web_sys::HtmlDetailsElement;
1711 Details;
1712 DetailsBuilder;
1713}
1714add_open_attr! {DetailsBuilder}
1715
1716def_component! {
1717 "dialog";
1718 web_sys::HtmlDialogElement;
1719 Dialog;
1720 DialogBuilder;
1721}
1722add_open_attr! {DialogBuilder}
1723
1724def_component! {
1725 "summary";
1726 web_sys::HtmlElement;
1727 Summary;
1728 SummaryBuilder;
1729}