1#[derive(serde::Deserialize)]
2#[cfg_attr(
3 not(feature = "wasm"),
4 derive(serde::Serialize, PartialEq, Debug, Default, Clone)
5)]
6pub struct Node {
7 pub condition: Option<ftd_rt::Condition>,
8 pub events: Vec<ftd_rt::Event>,
9 pub classes: Vec<String>,
10 pub node: String,
11 pub attrs: ftd_rt::Map,
12 pub style: ftd_rt::Map,
13 pub children: Vec<Node>,
14 pub external_children: Vec<Node>,
15 pub open_id: Option<String>,
16 pub external_children_container: Vec<Vec<usize>>,
17 pub children_style: ftd_rt::Map,
18 pub text: Option<String>,
19 pub null: bool,
20 pub locals: ftd_rt::Map,
21}
22
23impl Node {
24 pub fn fixed_children_style(&self, index: usize) -> ftd_rt::Map {
25 if index == 1 {
26 let mut list: ftd_rt::Map = Default::default();
27 for (key, value) in self.children_style.iter() {
28 if key == "margin-left" || key == "margin-top" {
29 continue;
30 }
31 list.insert(key.clone(), value.clone());
32 }
33 list
34 } else {
35 self.children_style.clone()
36 }
37 }
38
39 pub fn is_visible(&self, data: &ftd_rt::DataDependenciesMap) -> bool {
40 if self.null {
41 return false;
42 }
43
44 match self.condition {
45 Some(ref v) => v.is_true(data),
46 None => true,
47 }
48 }
49
50 #[allow(clippy::too_many_arguments)]
51 pub fn to_dnode(
52 &self,
53 style: &ftd_rt::Map,
54 data: &ftd_rt::DataDependenciesMap,
55 external_children: &mut Option<Vec<Self>>,
56 external_open_id: &Option<String>,
57 external_children_container: &[Vec<usize>],
58 is_parent_visible: bool,
59 parent_id: &str,
60 is_last: bool,
61 ) -> ftd_rt::dnode::DNode {
62 let style = {
63 let mut s = self.style.clone();
64 s.extend(style.clone());
65 s
66 };
67
68 let all_children = {
69 let mut children: Vec<ftd_rt::Node> = self.children.to_vec();
70 #[allow(clippy::blocks_in_if_conditions)]
71 if let Some(ext_children) = external_children {
72 if *external_open_id
73 == self.attrs.get("data-id").map(|v| {
74 if v.contains(':') {
75 let mut part = v.splitn(2, ':');
76 part.next().unwrap().trim().to_string()
77 } else {
78 v.to_string()
79 }
80 })
81 && self.open_id.is_none()
82 && external_children_container.is_empty()
83 && ((self.is_visible(data) && is_parent_visible) || is_last)
84 {
85 for child in ext_children.iter() {
86 if let Some(data_id) = child.attrs.get("data-id") {
87 for child in child.children.iter() {
88 let mut child = child.clone();
89 child.attrs.insert(
90 "data-ext-id".to_string(),
91 format!("{}:{}", data_id, parent_id),
92 );
93 children.push(child);
94 }
95 }
96 }
97 *external_children = None;
98 }
99 }
100 children
101 };
102
103 let (open_id, external_children_container) =
104 if self.open_id.is_some() && external_children_container.is_empty() {
105 (&self.open_id, self.external_children_container.as_slice())
106 } else {
107 (external_open_id, external_children_container)
108 };
109
110 let mut ext_child = None;
111 let mut is_borrowed_ext_child = false;
112
113 let ext_child: &mut Option<Vec<Self>> = {
114 if external_children_container.is_empty() {
115 &mut ext_child
116 } else if self.open_id.is_some() && !self.external_children.is_empty() {
117 ext_child = Some(self.external_children.clone());
118 &mut ext_child
119 } else {
120 is_borrowed_ext_child = true;
121 external_children
122 }
123 };
124
125 let mut index = 0;
126 let mut index_of_visible_children = 0;
127
128 let children = {
129 let mut children: Vec<ftd_rt::dnode::DNode> = vec![];
130 for (i, v) in all_children.iter().enumerate() {
131 if v.node.is_empty() {
132 continue;
133 }
134
135 let (external_container, is_last) = {
136 let mut external_container = vec![];
137 while index < external_children_container.len() {
138 if let Some(container) = external_children_container[index].get(0) {
139 if container < &i {
140 index += 1;
141 continue;
142 }
143 let external_child_container =
144 external_children_container[index][1..].to_vec();
145 if container == &i {
146 if !external_child_container.is_empty() {
147 external_container.push(external_child_container)
148 }
149 } else {
150 break;
151 }
152 }
153 index += 1;
154 }
155 let is_last = {
156 let mut last = external_container.is_empty()
157 && (index >= external_children_container.len()
158 || !is_other_sibling_visible(
159 i,
160 &all_children,
161 index,
162 external_children_container,
163 ));
164 if is_borrowed_ext_child {
165 last = is_last && last;
166 }
167 last
168 };
169
170 (external_container, is_last)
171 };
172
173 if v.is_visible(data) {
174 index_of_visible_children += 1;
175 }
176
177 children.push(v.to_dnode(
178 &self.fixed_children_style(index_of_visible_children),
179 data,
180 ext_child,
181 open_id,
182 external_container.as_slice(),
183 is_parent_visible && self.is_visible(data),
184 parent_id,
185 is_last,
186 ));
187 }
188 children
189 };
190
191 let attrs = {
192 let mut attrs = self.attrs.to_owned();
193 let oid = if let Some(oid) = attrs.get("data-id") {
194 format!("{}:{}", oid, parent_id)
195 } else {
196 format!("{}:root", parent_id)
197 };
198 attrs.insert("data-id".to_string(), oid);
199 attrs
200 };
201
202 return ftd_rt::dnode::DNode {
203 classes: self.classes.to_owned(),
204 node: self.node.to_owned(),
205 attrs,
206 style,
207 children,
208 text: self.text.to_owned(),
209 null: self.null.to_owned(),
210 events: self.events.to_owned(),
211 visible: self.is_visible(data),
212 };
213
214 fn is_other_sibling_visible(
215 index: usize,
216 all_children: &[Node],
217 ext_child_container_index: usize,
218 external_children_container: &[Vec<usize>],
219 ) -> bool {
220 for external_child_container in external_children_container
221 .iter()
222 .skip(ext_child_container_index)
223 {
224 if let Some(container) = external_child_container.get(0) {
225 if container < &index {
226 continue;
227 }
228 if let Some(child) = all_children.get(*container) {
229 if !child.node.is_empty() {
230 return true;
231 }
232 }
233 }
234 }
235 false
236 }
237 }
238
239 pub fn to_html(
240 &self,
241 style: &ftd_rt::Map,
242 data: &ftd_rt::DataDependenciesMap,
243 id: &str,
244 ) -> String {
245 self.to_dnode(style, data, &mut None, &None, &[], true, id, false)
246 .to_html(id)
247 }
248
249 pub fn get_target_node(&mut self, container: Vec<usize>) -> &mut Self {
250 let mut current = self;
251 for i in container.iter() {
252 current = &mut current.children[*i];
253 }
254 current
255 }
256}
257
258impl ftd_rt::Element {
259 pub fn to_node(&self, doc_id: &str) -> Node {
260 match self {
261 Self::Row(i) => (i.to_node(doc_id)),
262 Self::Scene(i) => (i.to_node(doc_id)),
263 Self::Text(i) => (i.to_node(doc_id)),
264 Self::TextBlock(i) => (i.to_node(doc_id)),
265 Self::Code(i) => (i.to_node(doc_id)),
266 Self::Image(i) => (i.to_node(doc_id)),
267 Self::Column(i) => (i.to_node(doc_id)),
268 Self::IFrame(i) => (i.to_node(doc_id)),
269 Self::Input(i) => (i.to_node(doc_id)),
270 Self::Integer(i) => (i.to_node(doc_id)),
271 Self::Boolean(i) => (i.to_node(doc_id)),
272 Self::Decimal(i) => (i.to_node(doc_id)),
273 Self::Null => Node {
274 condition: None,
275 events: vec![],
276 classes: vec![],
277 node: "".to_string(),
278 attrs: Default::default(),
279 style: Default::default(),
280 children: vec![],
281 external_children: Default::default(),
282 open_id: None,
283 external_children_container: vec![],
284 children_style: Default::default(),
285 text: None,
286 null: true,
287 locals: Default::default(),
288 },
289 }
290 }
291
292 pub fn to_dom(&self, _id: &str) {
294 todo!()
295 }
296}
297
298impl Node {
299 fn from_common(node: &str, common: &ftd_rt::Common, doc_id: &str) -> Self {
300 Node {
301 condition: common.condition.clone(),
302 node: s(node),
303 attrs: common.attrs(),
304 style: common.style(doc_id),
305 children: vec![],
306 external_children: Default::default(),
307 open_id: None,
308 external_children_container: vec![],
309 children_style: common.children_style(),
310 text: None,
311 classes: common.add_class(),
312 null: false,
313 events: common.events.clone(),
314 locals: common.locals.clone(),
315 }
316 }
317
318 fn from_container(
319 common: &ftd_rt::Common,
320 container: &ftd_rt::Container,
321 doc_id: &str,
322 ) -> Self {
323 let mut attrs = common.attrs();
324 attrs.extend(container.attrs());
325 let mut style = common.style(doc_id);
326 style.extend(container.style());
327 let mut classes = common.add_class();
328 classes.extend(container.add_class());
329
330 let mut children_style = common.children_style();
331 children_style.extend(container.children_style());
332 let node = match common.link {
333 Some(_) => "a",
334 None => match common.submit {
335 Some(_) => "form",
336 None => "div",
337 },
338 };
339
340 let (id, external_children_container, external_children) = {
341 if let Some((id, external_children_container, child)) = &container.external_children {
342 (
343 Some(id.to_string()),
344 external_children_container.clone(),
345 child.iter().map(|v| v.to_node(doc_id)).collect(),
346 )
347 } else {
348 (None, vec![], vec![])
349 }
350 };
351
352 Node {
353 condition: common.condition.clone(),
354 node: s(node), attrs,
356 style,
357 classes,
358 children_style,
359 text: None,
360 children: container
361 .children
362 .iter()
363 .map(|v| v.to_node(doc_id))
364 .collect(),
365 external_children,
366 open_id: id,
367 external_children_container,
368 null: false,
369 events: common.events.clone(),
370 locals: common.locals.clone(),
371 }
372 }
373}
374
375impl ftd_rt::Scene {
376 pub fn to_node(&self, doc_id: &str) -> Node {
377 let node = {
378 let mut node = Node {
379 node: s("div"),
380 ..Default::default()
381 };
382 if let Some(ref data_id) = self.common.data_id {
383 node.attrs
384 .insert(s("data-id"), format!("{}:scene", data_id));
385 } else {
386 node.attrs.insert(s("data-id"), s("scene:root"));
387 }
388 node.style.insert(s("position"), s("relative"));
389 let children = {
390 let parent = {
391 let mut node = if let Some(ref img) = self.common.background_image {
392 let mut n = Node {
393 node: s("img"),
394 ..Default::default()
395 };
396 n.attrs.insert(s("src"), s(img));
397 n.attrs.insert(s("alt"), escape("Scene"));
398 n
399 } else {
400 Node {
401 node: s("div"),
402 ..Default::default()
403 }
404 };
405 node.style.insert(s("width"), s("100%"));
406 if !self.common.is_not_visible {
407 node.style.insert(s("display"), s("block"));
408 }
409 node.style.insert(s("height"), s("auto"));
410 if let Some(ref data_id) = self.common.data_id {
411 node.attrs
412 .insert(s("data-id"), format!("{}:scene-bg", data_id));
413 }
414 node
415 };
416 let mut children: Vec<Node> = self
417 .container
418 .children
419 .iter()
420 .map(|v| {
421 let mut n = v.to_node(doc_id);
422 n.style.insert(s("position"), s("absolute"));
423 n
424 })
425 .collect();
426 children.insert(0, parent);
427 children
428 };
429
430 let (id, external_children_container, external_children) = {
431 if let Some((id, external_children_container, child)) =
432 &self.container.external_children
433 {
434 (
435 Some(id.to_string()),
436 external_children_container.clone(),
437 child
438 .iter()
439 .map(|v| {
440 let mut n = v.to_node(doc_id);
441 n.style.insert(s("position"), s("absolute"));
442 n
443 })
444 .collect(),
445 )
446 } else {
447 (None, vec![], vec![])
448 }
449 };
450
451 node.children = children;
452 node.open_id = id;
453 node.external_children = external_children;
454 node.external_children_container = external_children_container;
455 node
456 };
457
458 let mut main_node = Node::from_common("div", &self.common, doc_id);
459 if self.common.width.is_none() {
460 main_node.style.insert(s("width"), s("1000px"));
461 }
462 if let Some(p) = self.container.spacing {
463 main_node
464 .children_style
465 .insert(s("margin-left"), format!("{}px", p));
466 }
467 main_node.children = vec![node];
468 main_node
469 }
470}
471
472impl ftd_rt::Row {
473 pub fn to_node(&self, doc_id: &str) -> Node {
474 let mut n = Node::from_container(&self.common, &self.container, doc_id);
475 if !self.common.is_not_visible {
476 n.style.insert(s("display"), s("flex"));
477 }
478 n.style.insert(s("flex-direction"), s("row"));
479 if self.container.wrap {
480 n.style.insert(s("flex-wrap"), s("wrap"));
481 } else {
482 n.style.insert(s("flex-wrap"), s("nowrap"));
483 }
484
485 n.style.insert(s("align-items"), s("flex-start"));
486
487 n.style.insert(s("justify-content"), s("flex-start"));
488
489 if let Some(p) = self.container.spacing {
490 n.children_style
491 .insert(s("margin-left"), format!("{}px", p));
492 n.attrs
493 .insert(s("data-spacing"), format!("margin-left:{}px", p));
494 }
495
496 n
497 }
498}
499
500impl ftd_rt::Column {
501 pub fn to_node(&self, doc_id: &str) -> Node {
502 let mut n = Node::from_container(&self.common, &self.container, doc_id);
503 if !self.common.is_not_visible {
504 n.style.insert(s("display"), s("flex"));
505 }
506 n.style.insert(s("flex-direction"), s("column"));
507 if self.container.wrap {
508 n.style.insert(s("flex-wrap"), s("wrap"));
509 } else {
510 n.style.insert(s("flex-wrap"), s("nowrap"));
511 }
512 n.style.insert(s("align-items"), s("flex-start"));
513
514 n.style.insert(s("justify-content"), s("flex-start"));
515
516 if let Some(p) = self.container.spacing {
517 n.children_style.insert(s("margin-top"), format!("{}px", p));
518 n.attrs
519 .insert(s("data-spacing"), format!("margin-top:{}px", p));
520 }
521
522 n
523 }
524}
525
526impl ftd_rt::Text {
527 pub fn to_node(&self, doc_id: &str) -> Node {
528 let node = match &self.common.link {
531 Some(_) => "a",
532 None => match &self.common.submit {
533 Some(_) => "form",
534 _ => "div",
535 },
536 };
537 let mut n = Node::from_common(node, &self.common, doc_id);
538 n.text = Some(self.text.rendered.clone());
539 let (key, value) = text_align(&self.text_align);
540 n.style.insert(s(key.as_str()), value);
541 if let Some(p) = self.size {
542 n.style.insert(s("font-size"), format!("{}px", p));
543 }
544 if let Some(p) = self.line_height {
545 n.style.insert(s("line-height"), format!("{}px", p));
546 } else if !&self.line {
547 n.style.insert(s("line-height"), s("26px"));
548 }
549
550 if self.style.italic {
551 n.style.insert(s("font-style"), s("italic"));
552 }
553 if self.style.underline {
554 n.style.insert(s("text-decoration"), s("underline"));
555 }
556 if self.style.strike {
557 n.style.insert(s("text-decoration"), s("line-through"));
558 }
559
560 if let Some(p) = &self.line_clamp {
561 n.style.insert(s("display"), "-webkit-box".to_string());
562 n.style.insert(s("overflow"), "hidden".to_string());
563 n.style.insert(s("-webkit-line-clamp"), format!("{}", p));
564 n.style
565 .insert(s("-webkit-box-orient"), "vertical".to_string());
566 }
567
568 let (key, value) = style(&self.style.weight);
569 n.style.insert(s(key.as_str()), value);
570
571 n
573 }
574}
575
576impl ftd_rt::TextBlock {
577 pub fn to_node(&self, doc_id: &str) -> Node {
578 let node = match &self.common.link {
581 Some(_) => "a",
582 None => match &self.common.submit {
583 Some(_) => "form",
584 _ => "div",
585 },
586 };
587 let mut n = Node::from_common(node, &self.common, doc_id);
588 n.text = Some(self.text.rendered.clone());
589 let (key, value) = text_align(&self.text_align);
590 n.style.insert(s(key.as_str()), value);
591 if let Some(p) = self.size {
592 n.style.insert(s("font-size"), format!("{}px", p));
593 }
594 if let Some(p) = self.line_height {
595 n.style.insert(s("line-height"), format!("{}px", p));
596 } else if !&self.line {
597 n.style.insert(s("line-height"), s("26px"));
598 }
599
600 if self.style.italic {
601 n.style.insert(s("font-style"), s("italic"));
602 }
603 if self.style.underline {
604 n.style.insert(s("text-decoration"), s("underline"));
605 }
606 if self.style.strike {
607 n.style.insert(s("text-decoration"), s("line-through"));
608 }
609
610 if let Some(p) = &self.line_clamp {
611 n.style.insert(s("display"), "-webkit-box".to_string());
612 n.style.insert(s("overflow"), "hidden".to_string());
613 n.style.insert(s("-webkit-line-clamp"), format!("{}", p));
614 n.style
615 .insert(s("-webkit-box-orient"), "vertical".to_string());
616 }
617
618 let (key, value) = style(&self.style.weight);
619 n.style.insert(s(key.as_str()), value);
620
621 n
623 }
624}
625
626impl ftd_rt::Code {
627 pub fn to_node(&self, doc_id: &str) -> Node {
628 let node = match &self.common.link {
629 Some(_) => "a",
630 None => match &self.common.submit {
631 Some(_) => "form",
632 _ => "div",
633 },
634 };
635 let mut n = Node::from_common(node, &self.common, doc_id);
636 n.text = Some(self.text.rendered.clone());
637 let (key, value) = text_align(&self.text_align);
638 n.style.insert(s(key.as_str()), value);
639 if let Some(p) = self.size {
640 n.style.insert(s("font-size"), format!("{}px", p));
641 }
642 if let Some(p) = self.line_height {
643 n.style.insert(s("line-height"), format!("{}px", p));
644 } else {
645 n.style.insert(s("line-height"), s("26px"));
646 }
647
648 if self.style.italic {
649 n.style.insert(s("font-style"), s("italic"));
650 }
651 if self.style.underline {
652 n.style.insert(s("text-decoration"), s("underline"));
653 }
654 if self.style.strike {
655 n.style.insert(s("text-decoration"), s("line-through"));
656 }
657
658 if let Some(p) = &self.line_clamp {
659 n.style.insert(s("display"), "-webkit-box".to_string());
660 n.style.insert(s("overflow"), "hidden".to_string());
661 n.style.insert(s("-webkit-line-clamp"), format!("{}", p));
662 n.style
663 .insert(s("-webkit-box-orient"), "vertical".to_string());
664 }
665
666 let (key, value) = style(&self.style.weight);
667 n.style.insert(s(key.as_str()), value);
668 n
669 }
670}
671
672impl ftd_rt::Image {
673 pub fn to_node(&self, doc_id: &str) -> Node {
674 let mut n = Node::from_common("img", &self.common, doc_id);
675 if self.common.link.is_some() {
676 n.node = s("a");
677 let mut img = Node {
678 condition: None,
679 events: vec![],
680 classes: vec![],
681 node: s("img"),
682 attrs: Default::default(),
683 style: Default::default(),
684 children: vec![],
685 external_children: vec![],
686 open_id: None,
687 external_children_container: vec![],
688 children_style: Default::default(),
689 text: None,
690 null: false,
691 locals: Default::default(),
692 };
693 img.style.insert(s("width"), s("100%"));
694 img.attrs.insert(s("src"), escape(self.src.as_str()));
695 img.attrs
696 .insert(s("alt"), escape(self.description.as_str()));
697 if self.crop {
698 img.style.insert(s("object-fit"), s("cover"));
699 img.style.insert(s("object-position"), s("0 0"));
700 }
701 n.children.push(img);
702 } else {
703 n.attrs.insert(s("src"), escape(self.src.as_str()));
704 n.attrs.insert(s("alt"), escape(self.description.as_str()));
705 if self.crop {
706 n.style.insert(s("object-fit"), s("cover"));
707 n.style.insert(s("object-position"), s("0 0"));
708 }
709 }
710 if self.common.width == None {
711 n.style.insert(s("width"), s("100%"));
712 }
713
714 n
715 }
716}
717
718impl ftd_rt::IFrame {
719 pub fn to_node(&self, doc_id: &str) -> Node {
720 let mut n = Node::from_common("iframe", &self.common, doc_id);
721 n.attrs.insert(s("src"), escape(self.src.as_str()));
722 n.attrs.insert(s("allow"), s("fullscreen"));
723 n.attrs.insert(s("allowfullscreen"), s("allowfullscreen"));
724 n
725 }
726}
727
728impl ftd_rt::Input {
729 pub fn to_node(&self, doc_id: &str) -> Node {
730 let mut n = Node::from_common("input", &self.common, doc_id);
731 if let Some(ref p) = self.placeholder {
732 n.attrs.insert(s("placeholder"), escape(p));
733 }
734 n
735 }
736}
737
738impl ftd_rt::Common {
739 fn add_class(&self) -> Vec<String> {
740 let d: Vec<String> = vec![s("ft_md")];
741 d
742 }
743 fn children_style(&self) -> ftd_rt::Map {
744 let d: ftd_rt::Map = Default::default();
745 d
746 }
747
748 fn style(&self, doc_id: &str) -> ftd_rt::Map {
749 let mut d: ftd_rt::Map = Default::default();
750
751 if !self.events.is_empty() && self.cursor.is_none() {
752 d.insert(s("cursor"), s("pointer"));
753 }
754 if self.is_not_visible {
755 d.insert(s("display"), s("none"));
756 }
757
758 if let Some(p) = self.padding {
759 d.insert(s("padding"), format!("{}px", p));
760 }
761 if let Some(p) = self.padding_left {
762 d.insert(s("padding-left"), format!("{}px", p));
763 }
764 if let Some(ref cursor) = self.cursor {
765 d.insert(s("cursor"), s(cursor));
766 }
767 if let Some(p) = self.padding_vertical {
768 d.insert(s("padding-top"), format!("{}px", p));
769 d.insert(s("padding-bottom"), format!("{}px", p));
770 }
771 if let Some(p) = self.padding_horizontal {
772 d.insert(s("padding-left"), format!("{}px", p));
773 d.insert(s("padding-right"), format!("{}px", p));
774 }
775 if let Some(p) = self.padding_right {
776 d.insert(s("padding-right"), format!("{}px", p));
777 }
778 if let Some(p) = self.padding_top {
779 d.insert(s("padding-top"), format!("{}px", p));
780 }
781 if let Some(p) = self.padding_bottom {
782 d.insert(s("padding-bottom"), format!("{}px", p));
783 }
784
785 if let Some(p) = self.border_top_radius {
786 d.insert(s("border-top-left-radius"), format!("{}px !important", p));
787 d.insert(s("border-top-right-radius"), format!("{}px !important", p));
788 }
789
790 if let Some(p) = self.border_left_radius {
791 d.insert(s("border-top-left-radius"), format!("{}px !important", p));
792 d.insert(
793 s("border-bottom-left-radius"),
794 format!("{}px !important", p),
795 );
796 }
797
798 if let Some(p) = self.border_right_radius {
799 d.insert(s("border-top-right-radius"), format!("{}px !important", p));
800 d.insert(
801 s("border-bottom-right-radius"),
802 format!("{}px !important", p),
803 );
804 }
805
806 if let Some(p) = self.border_bottom_radius {
807 d.insert(
808 s("border-bottom-right-radius"),
809 format!("{}px !important", p),
810 );
811 d.insert(
812 s("border-bottom-left-radius"),
813 format!("{}px !important", p),
814 );
815 }
816
817 if let Some(p) = &self.width {
818 let (key, value) = length(p, "width");
819 d.insert(s(key.as_str()), value);
820 }
821 if let Some(p) = &self.min_width {
822 let (key, value) = length(p, "min-width");
823 d.insert(s(key.as_str()), value);
824 }
825 if let Some(p) = &self.max_width {
826 let (key, value) = length(p, "max-width");
827 d.insert(s(key.as_str()), value);
828 }
829 if let Some(p) = &self.height {
830 let (key, value) = length(p, "height");
831 d.insert(s(key.as_str()), value);
832 }
833 if let Some(p) = &self.min_height {
834 let (key, value) = length(p, "min-height");
835 d.insert(s(key.as_str()), value);
836 }
837 if let Some(p) = &self.max_height {
838 let (key, value) = length(p, "max-height");
839 d.insert(s(key.as_str()), value);
840 }
841 if let Some(p) = self.border_left {
842 d.insert(s("border-left-width"), format!("{}px !important", p));
843 }
844 if let Some(p) = self.border_right {
845 d.insert(s("border-right-width"), format!("{}px !important", p));
846 }
847 if let Some(p) = self.border_top {
848 d.insert(s("border-top-width"), format!("{}px !important", p));
849 }
850 if let Some(p) = self.border_bottom {
851 d.insert(s("border-bottom-width"), format!("{}px !important", p));
852 }
853 if let Some(p) = self.margin_left {
854 d.insert(s("margin-left"), format!("{}px", p));
855 }
856 if let Some(p) = self.margin_right {
857 d.insert(s("margin-right"), format!("{}px", p));
858 }
859 if let Some(p) = self.margin_top {
860 d.insert(s("margin-top"), format!("{}px", p));
861 }
862 if let Some(p) = self.margin_bottom {
863 d.insert(s("margin-bottom"), format!("{}px", p));
864 }
865 if let Some(p) = &self.background_color {
866 d.insert(s("background-color"), color(p));
867 }
868 if let Some(p) = &self.color {
869 d.insert(s("color"), color(p));
870 }
871 if let Some(p) = &self.border_color {
872 d.insert(s("border-color"), color(p));
873 }
874 if let Some(p) = &self.overflow_x {
875 let (key, value) = overflow(p, "overflow-x");
876 d.insert(s(key.as_str()), value);
877 }
878 if let Some(p) = &self.overflow_y {
879 let (key, value) = overflow(p, "overflow-y");
880 d.insert(s(key.as_str()), value);
881 }
882 if self.sticky {
883 d.insert(s("position"), s("sticky"));
884 }
885 if let Some(p) = &self.top {
886 d.insert(s("top"), format!("{}px", p));
887 }
888 if let Some(p) = &self.bottom {
889 d.insert(s("bottom"), format!("{}px", p));
890 }
891 if let Some(p) = &self.left {
892 d.insert(s("left"), format!("{}px", p));
893 }
894 if let Some(p) = &self.right {
895 d.insert(s("right"), format!("{}px", p));
896 }
897 if self.submit.is_some() {
898 d.insert(s("cursor"), s("pointer"));
899 }
900 if self.link.is_some() {
901 d.insert(s("cursor"), s("pointer"));
902 }
903 if self.shadow_size.is_some()
904 || self.shadow_blur.is_some()
905 || self.shadow_offset_x.is_some()
906 || self.shadow_offset_y.is_some()
907 {
908 let shadow_color = match &self.shadow_color {
909 Some(p) => p,
910 None => &ftd_rt::Color {
911 r: 0,
912 g: 0,
913 b: 0,
914 alpha: 0.25,
915 },
916 };
917
918 d.insert(
919 s("box-shadow"),
920 format!(
921 "{}px {}px {}px {}px {}",
922 self.shadow_offset_x.unwrap_or(0),
923 self.shadow_offset_y.unwrap_or(0),
924 self.shadow_blur.unwrap_or(0),
925 self.shadow_size.unwrap_or(0),
926 color(shadow_color),
927 ),
928 );
929 }
930 if let Some(p) = &self.anchor {
931 d.insert(s("position"), p.to_postion());
932 }
933 if let Some(p) = &self.gradient_direction {
934 d.insert(s("background-image"), gradient(p, &self.gradient_colors));
935 }
936 if let Some(p) = &self.background_image {
937 d.insert(s("background-image"), format!("url({})", p));
938 if self.background_repeat {
939 d.insert(s("background-repeat"), s("repeat"));
940 } else {
941 d.insert(s("background-size"), s("cover"));
942 d.insert(s("background-position"), s("center"));
943 }
944 if self.background_parallax {
945 d.insert(s("background-attachment"), s("fixed"));
946 }
947 }
948
949 match &self.anchor {
950 Some(_) => {
951 for (key, value) in non_static_container_align(&self.position, self.inner) {
952 d.insert(s(key.as_str()), value);
953 }
954 }
955 None => {
956 for (key, value) in container_align(&self.position) {
957 d.insert(s(key.as_str()), value);
958 }
959 }
960 }
961
962 let translate = get_translate(
963 &self.move_left,
964 &self.move_right,
965 &self.move_up,
966 &self.move_down,
967 &self.scale,
968 &self.scale_x,
969 &self.scale_y,
970 &self.rotate,
971 doc_id,
972 )
973 .unwrap();
974
975 if let Some(p) = translate {
976 let data = if let Some(d) = d.get_mut("transform") {
977 format!("{} {}", d, p)
978 } else {
979 p
980 };
981 d.insert(s("transform"), data);
982 }
983
984 d.insert(s("border-style"), s("solid"));
985 d.insert(s("border-width"), format!("{}px", self.border_width));
986 d.insert(s("border-radius"), format!("{}px", self.border_radius));
987 d.insert(s("box-sizing"), s("border-box"));
988 d.insert(s("white-space"), s("initial"));
989
990 d
991 }
992
993 fn attrs(&self) -> ftd_rt::Map {
994 let mut d: ftd_rt::Map = Default::default();
995 if let Some(ref id) = self.data_id {
996 d.insert(s("data-id"), escape(id));
997 }
998 if let Some(ref id) = self.id {
999 d.insert(s("id"), escape(id));
1000 }
1001 if let Some(ref link) = self.link {
1003 d.insert(s("href"), link.to_string());
1004 }
1005 if self.open_in_new_tab {
1006 d.insert(s("target"), escape("_blank"));
1007 }
1008 if let Some(ref link) = self.submit {
1009 if cfg!(feature = "realm") {
1010 d.insert(
1011 s("onclick"),
1012 format!("window.REALM_SUBMIT('{}');", escape(link)),
1013 );
1014 } else {
1015 d.insert(s("onclick"), "this.submit()".to_string());
1016 }
1017 }
1018 d
1019 }
1020}
1021impl ftd_rt::Container {
1022 fn style(&self) -> ftd_rt::Map {
1023 let mut d: ftd_rt::Map = Default::default();
1024 let mut count = count_children_with_absolute_parent(&self.children);
1025 if let Some((_, _, ref ext_children)) = self.external_children {
1026 count += count_children_with_absolute_parent(ext_children);
1027 }
1028 if count != 0 {
1029 d.insert(s("position"), s("relative"));
1030 }
1031 return d;
1032
1033 fn count_children_with_absolute_parent(children: &[ftd_rt::Element]) -> usize {
1034 children
1035 .iter()
1036 .filter(|v| {
1037 let mut bool = false;
1038 if let Some(common) = v.get_common() {
1039 if Some(ftd_rt::Anchor::Parent) == common.anchor {
1040 bool = true;
1041 }
1042 }
1043 bool
1044 })
1045 .count()
1046 }
1047 }
1048 fn children_style(&self) -> ftd_rt::Map {
1049 let d: ftd_rt::Map = Default::default();
1050 d
1051 }
1052
1053 fn attrs(&self) -> ftd_rt::Map {
1054 let d: ftd_rt::Map = Default::default();
1055 d
1056 }
1057 fn add_class(&self) -> Vec<String> {
1058 let d: Vec<String> = Default::default();
1059 d
1060 }
1061}
1062
1063pub fn escape(s: &str) -> String {
1064 let s = s.replace('>', "\\u003E");
1065 let s = s.replace('<', "\\u003C");
1066 s.replace('&', "\\u0026")
1067}
1068
1069fn s(s: &str) -> String {
1070 s.to_string()
1071}
1072
1073pub fn color(c: &ftd_rt::Color) -> String {
1074 let ftd_rt::Color { r, g, b, alpha } = c;
1075 format!("rgba({},{},{},{})", r, g, b, alpha)
1076}
1077
1078pub fn length(l: &ftd_rt::Length, f: &str) -> (String, String) {
1079 let s = f.to_string();
1080 match l {
1081 ftd_rt::Length::Fill => (s, "100%".to_string()),
1082 ftd_rt::Length::Auto => (s, "auto".to_string()),
1083 ftd_rt::Length::Px { value } => (s, format!("{}px", value)),
1084 ftd_rt::Length::Portion { value } => ("flex-grow".to_string(), value.to_string()),
1085 ftd_rt::Length::Percent { value } => (s, format!("{}%", value)),
1086 ftd_rt::Length::FitContent => (s, "fit-content".to_string()),
1087 ftd_rt::Length::Calc { value } => (s, format!("calc({})", value)),
1088
1089 _ => (s, "100%".to_string()),
1090 }
1092}
1093
1094fn text_align(l: &ftd_rt::TextAlign) -> (String, String) {
1095 match l {
1096 ftd_rt::TextAlign::Center => ("text-align".to_string(), "center".to_string()),
1097 ftd_rt::TextAlign::Left => ("text-align".to_string(), "left".to_string()),
1098 ftd_rt::TextAlign::Right => ("text-align".to_string(), "right".to_string()),
1099 ftd_rt::TextAlign::Justify => ("text-align".to_string(), "justify".to_string()),
1100 }
1101}
1102
1103fn style(l: &ftd_rt::Weight) -> (String, String) {
1104 match l {
1105 ftd_rt::Weight::Heavy => ("font-weight".to_string(), "900".to_string()),
1106 ftd_rt::Weight::ExtraBold => ("font-weight".to_string(), "800".to_string()),
1107 ftd_rt::Weight::Bold => ("font-weight".to_string(), "700".to_string()),
1108 ftd_rt::Weight::SemiBold => ("font-weight".to_string(), "600".to_string()),
1109 ftd_rt::Weight::Medium => ("font-weight".to_string(), "500".to_string()),
1110 ftd_rt::Weight::Regular => ("font-weight".to_string(), "400".to_string()),
1111 ftd_rt::Weight::Light => ("font-weight".to_string(), "300".to_string()),
1112 ftd_rt::Weight::ExtraLight => ("font-weight".to_string(), "200".to_string()),
1113 ftd_rt::Weight::HairLine => ("font-weight".to_string(), "100".to_string()),
1114 }
1115}
1116
1117pub fn overflow(l: &ftd_rt::Overflow, f: &str) -> (String, String) {
1118 let s = f.to_string();
1119 match l {
1120 ftd_rt::Overflow::Auto => (s, "auto".to_string()),
1121 ftd_rt::Overflow::Hidden => (s, "hidden".to_string()),
1122 ftd_rt::Overflow::Scroll => (s, "scroll".to_string()),
1123 ftd_rt::Overflow::Visible => (s, "visible".to_string()),
1124 }
1125}
1126
1127fn gradient(d: &ftd_rt::GradientDirection, c: &[ftd_rt::Color]) -> String {
1128 let color = c
1129 .iter()
1130 .map(|v| color(v))
1131 .collect::<Vec<String>>()
1132 .join(",");
1133 let gradient_style = match d {
1134 ftd_rt::GradientDirection::BottomToTop => "linear-gradient(to top ".to_string(),
1135 ftd_rt::GradientDirection::TopToBottom => "linear-gradient(to bottom ".to_string(),
1136 ftd_rt::GradientDirection::LeftToRight => "linear-gradient(to right".to_string(),
1137 ftd_rt::GradientDirection::RightToLeft => "linear-gradient(to left".to_string(),
1138 ftd_rt::GradientDirection::BottomRightToTopLeft => {
1139 "linear-gradient(to top left".to_string()
1140 }
1141 ftd_rt::GradientDirection::TopLeftBottomRight => {
1142 "linear-gradient(to bottom right".to_string()
1143 }
1144 ftd_rt::GradientDirection::BottomLeftToTopRight => {
1145 "linear-gradient(to top right".to_string()
1146 }
1147 ftd_rt::GradientDirection::TopRightToBottomLeft => {
1148 "linear-gradient(to bottom left".to_string()
1149 }
1150 ftd_rt::GradientDirection::Center => "radial-gradient(circle ".to_string(),
1151 ftd_rt::GradientDirection::Angle { value } => format!("linear-gradient({}deg", value),
1152 };
1153 format!("{}, {} )", gradient_style, color)
1154}
1155
1156pub fn anchor(l: &ftd_rt::Anchor) -> String {
1157 match l {
1158 ftd_rt::Anchor::Parent => ("absolute".to_string()),
1159 ftd_rt::Anchor::Window => ("fixed".to_string()),
1160 }
1161}
1162
1163fn container_align(l: &ftd_rt::Position) -> Vec<(String, String)> {
1164 match l {
1165 ftd_rt::Position::Center => vec![
1166 ("align-self".to_string(), "center".to_string()),
1167 ("margin-bottom".to_string(), "auto".to_string()),
1168 ("margin-top".to_string(), "auto".to_string()),
1169 ],
1170 ftd_rt::Position::Top => vec![
1171 ("align-self".to_string(), "center".to_string()),
1172 ("margin-bottom".to_string(), "auto".to_string()),
1173 ],
1174 ftd_rt::Position::Left => vec![
1175 ("align-self".to_string(), "flex-start".to_string()),
1176 ("margin-bottom".to_string(), "auto".to_string()),
1177 ("margin-top".to_string(), "auto".to_string()),
1178 ],
1179 ftd_rt::Position::Right => vec![
1180 ("align-self".to_string(), "flex-end".to_string()),
1181 ("margin-bottom".to_string(), "auto".to_string()),
1182 ("margin-top".to_string(), "auto".to_string()),
1183 ("margin-left".to_string(), "auto".to_string()),
1184 ],
1185 ftd_rt::Position::Bottom => vec![
1186 ("align-self".to_string(), "center".to_string()),
1187 ("margin-bottom".to_string(), "0".to_string()),
1188 ("margin-top".to_string(), "auto".to_string()),
1189 ],
1190 ftd_rt::Position::TopLeft => vec![("align-self".to_string(), "flex-start".to_string())],
1191 ftd_rt::Position::TopRight => vec![
1192 ("align-self".to_string(), "flex-end".to_string()),
1193 ("margin-left".to_string(), "auto".to_string()),
1194 ],
1195 ftd_rt::Position::BottomLeft => vec![
1196 ("align-self".to_string(), "flex-start".to_string()),
1197 ("margin-bottom".to_string(), "0".to_string()),
1198 ("margin-top".to_string(), "auto".to_string()),
1199 ],
1200 ftd_rt::Position::BottomRight => vec![
1201 ("align-self".to_string(), "flex-end".to_string()),
1202 ("margin-bottom".to_string(), "0".to_string()),
1203 ("margin-top".to_string(), "auto".to_string()),
1204 ("margin-left".to_string(), "auto".to_string()),
1205 ],
1206 }
1207}
1208
1209fn non_static_container_align(l: &ftd_rt::Position, inner: bool) -> Vec<(String, String)> {
1210 match l {
1211 ftd_rt::Position::Center => vec![
1212 ("left".to_string(), "50%".to_string()),
1213 ("top".to_string(), "50%".to_string()),
1214 ("transform".to_string(), "translate(-50%,-50%)".to_string()),
1215 ],
1216 ftd_rt::Position::Top => {
1217 if inner {
1218 vec![
1219 ("top".to_string(), "0".to_string()),
1220 ("left".to_string(), "50%".to_string()),
1221 ("transform".to_string(), "translateX(-50%)".to_string()),
1222 ]
1223 } else {
1224 vec![
1225 ("bottom".to_string(), "100%".to_string()),
1226 ("left".to_string(), "50%".to_string()),
1227 ("transform".to_string(), "translateX(-50%)".to_string()),
1228 ]
1229 }
1230 }
1231 ftd_rt::Position::Left => {
1232 if inner {
1233 vec![
1234 ("left".to_string(), "0".to_string()),
1235 ("top".to_string(), "50%".to_string()),
1236 ("transform".to_string(), "translateY(-50%)".to_string()),
1237 ]
1238 } else {
1239 vec![
1240 ("right".to_string(), "100%".to_string()),
1241 ("top".to_string(), "50%".to_string()),
1242 ("transform".to_string(), "translateY(-50%)".to_string()),
1243 ]
1244 }
1245 }
1246 ftd_rt::Position::Right => {
1247 if inner {
1248 vec![
1249 ("right".to_string(), "0".to_string()),
1250 ("top".to_string(), "50%".to_string()),
1251 ("transform".to_string(), "translate(-50%)".to_string()),
1252 ]
1253 } else {
1254 vec![
1255 ("left".to_string(), "100%".to_string()),
1256 ("top".to_string(), "50%".to_string()),
1257 ("transform".to_string(), "translate(-50%)".to_string()),
1258 ]
1259 }
1260 }
1261 ftd_rt::Position::Bottom => {
1262 if inner {
1263 vec![
1264 ("bottom".to_string(), "0".to_string()),
1265 ("left".to_string(), "50".to_string()),
1266 ("transform".to_string(), "translateX(-50%)".to_string()),
1267 ]
1268 } else {
1269 vec![
1270 ("top".to_string(), "100%".to_string()),
1271 ("left".to_string(), "50".to_string()),
1272 ("transform".to_string(), "translateX(-50%)".to_string()),
1273 ]
1274 }
1275 }
1276 ftd_rt::Position::TopLeft => {
1277 if inner {
1278 vec![
1279 ("top".to_string(), "0".to_string()),
1280 ("left".to_string(), "0".to_string()),
1281 ]
1282 } else {
1283 vec![
1284 ("bottom".to_string(), "100%".to_string()),
1285 ("right".to_string(), "100%".to_string()),
1286 ]
1287 }
1288 }
1289 ftd_rt::Position::TopRight => {
1290 if inner {
1291 vec![
1292 ("top".to_string(), "0".to_string()),
1293 ("right".to_string(), "0".to_string()),
1294 ]
1295 } else {
1296 vec![
1297 ("bottom".to_string(), "100%".to_string()),
1298 ("left".to_string(), "100%".to_string()),
1299 ]
1300 }
1301 }
1302 ftd_rt::Position::BottomLeft => {
1303 if inner {
1304 vec![
1305 ("bottom".to_string(), "0".to_string()),
1306 ("left".to_string(), "0".to_string()),
1307 ]
1308 } else {
1309 vec![
1310 ("top".to_string(), "100%".to_string()),
1311 ("right".to_string(), "100%".to_string()),
1312 ]
1313 }
1314 }
1315 ftd_rt::Position::BottomRight => {
1316 if inner {
1317 vec![
1318 ("bottom".to_string(), "0".to_string()),
1319 ("right".to_string(), "0".to_string()),
1320 ]
1321 } else {
1322 vec![
1323 ("top".to_string(), "100%".to_string()),
1324 ("left".to_string(), "100%".to_string()),
1325 ]
1326 }
1327 }
1328 }
1329}
1330
1331#[allow(clippy::too_many_arguments)]
1332fn get_translate(
1333 left: &Option<i64>,
1334 right: &Option<i64>,
1335 up: &Option<i64>,
1336 down: &Option<i64>,
1337 scale: &Option<f64>,
1338 scale_x: &Option<f64>,
1339 scale_y: &Option<f64>,
1340 rotate: &Option<i64>,
1341 doc_id: &str,
1342) -> ftd_rt::Result<Option<String>> {
1343 let mut translate = match (left, right, up, down) {
1344 (Some(_), Some(_), Some(_), Some(_)) => {
1345 return ftd_rt::e(
1346 "move-up, move-down, move-left and move-right all 4 can't be used at once!",
1347 doc_id,
1348 )
1349 }
1350 (Some(_), Some(_), _, _) => {
1351 return ftd_rt::e("move-left, move-right both can't be used at once!", doc_id)
1352 }
1353 (_, _, Some(_), Some(_)) => {
1354 return ftd_rt::e("move-up, move-down both can't be used at once!", doc_id)
1355 }
1356 (Some(l), None, None, None) => Some(format!("translateX(-{}px) ", l)),
1357 (Some(l), None, Some(u), None) => Some(format!("translate(-{}px, -{}px) ", l, u)),
1358 (Some(l), None, None, Some(d)) => Some(format!("translate(-{}px, {}px) ", l, d)),
1359 (None, Some(r), None, None) => Some(format!("translateX({}px) ", r)),
1360 (None, Some(r), Some(u), None) => Some(format!("translate({}px, -{}px) ", r, u)),
1361 (None, Some(r), None, Some(d)) => Some(format!("translate({}px, {}px) ", r, d)),
1362 (None, None, Some(u), None) => Some(format!("translateY(-{}px) ", u)),
1363 (None, None, None, Some(d)) => Some(format!("translateY({}px) ", d)),
1364 _ => None,
1365 };
1366
1367 if let Some(ref scale) = scale {
1368 if let Some(d) = translate {
1369 translate = Some(format!("{} scale({})", d, scale));
1370 } else {
1371 translate = Some(format!("scale({})", scale));
1372 };
1373 }
1374 if let Some(ref scale) = scale_x {
1375 if let Some(d) = translate {
1376 translate = Some(format!("{} scaleX({})", d, scale));
1377 } else {
1378 translate = Some(format!("scaleX({})", scale));
1379 };
1380 }
1381 if let Some(ref scale) = scale_y {
1382 if let Some(d) = translate {
1383 translate = Some(format!("{} scaleY({})", d, scale));
1384 } else {
1385 translate = Some(format!("scaleY({})", scale));
1386 };
1387 }
1388 if let Some(ref rotate) = rotate {
1389 if let Some(d) = translate {
1390 translate = Some(format!("{} rotate({}deg)", d, rotate));
1391 } else {
1392 translate = Some(format!("rotate({}deg)", rotate));
1393 };
1394 }
1395 Ok(translate)
1396}