1#[cfg(feature = "dom")]
10pub mod dom;
11
12use std::rc::Rc;
13
14use dyn_derive::dyn_trait;
15use serde::{Deserialize, Serialize};
16
17#[derive(Copy, Clone, Debug, PartialEq)]
18pub enum Alignment {
19 Start,
20 End,
21}
22
23#[derive(Copy, Clone, Debug, PartialEq)]
24pub enum Side {
25 Top,
26 Right,
27 Bottom,
28 Left,
29}
30
31impl Side {
32 pub fn opposite(&self) -> Side {
33 match self {
34 Side::Top => Side::Bottom,
35 Side::Right => Side::Left,
36 Side::Bottom => Side::Top,
37 Side::Left => Side::Right,
38 }
39 }
40
41 pub fn axis(&self) -> Axis {
42 match self {
43 Side::Top => Axis::Y,
44 Side::Right => Axis::X,
45 Side::Bottom => Axis::Y,
46 Side::Left => Axis::X,
47 }
48 }
49}
50
51#[derive(Copy, Clone, Debug, PartialEq)]
52pub enum AlignedPlacement {
53 TopStart,
54 TopEnd,
55 RightStart,
56 RightEnd,
57 BottomStart,
58 BottomEnd,
59 LeftStart,
60 LeftEnd,
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
64pub enum Placement {
65 Top,
66 TopStart,
67 TopEnd,
68 Right,
69 RightStart,
70 RightEnd,
71 Bottom,
72 BottomStart,
73 BottomEnd,
74 Left,
75 LeftStart,
76 LeftEnd,
77}
78
79impl Placement {
80 pub fn alignment(&self) -> Option<Alignment> {
81 match self {
82 Placement::Top => None,
83 Placement::TopStart => Some(Alignment::Start),
84 Placement::TopEnd => Some(Alignment::End),
85 Placement::Right => None,
86 Placement::RightStart => Some(Alignment::Start),
87 Placement::RightEnd => Some(Alignment::End),
88 Placement::Bottom => None,
89 Placement::BottomStart => Some(Alignment::Start),
90 Placement::BottomEnd => Some(Alignment::End),
91 Placement::Left => None,
92 Placement::LeftStart => Some(Alignment::Start),
93 Placement::LeftEnd => Some(Alignment::End),
94 }
95 }
96
97 pub fn side(&self) -> Side {
98 match self {
99 Placement::Top => Side::Top,
100 Placement::TopStart => Side::Top,
101 Placement::TopEnd => Side::Top,
102 Placement::Right => Side::Right,
103 Placement::RightStart => Side::Right,
104 Placement::RightEnd => Side::Right,
105 Placement::Bottom => Side::Bottom,
106 Placement::BottomStart => Side::Bottom,
107 Placement::BottomEnd => Side::Bottom,
108 Placement::Left => Side::Left,
109 Placement::LeftStart => Side::Left,
110 Placement::LeftEnd => Side::Left,
111 }
112 }
113
114 pub fn opposite(&self) -> Placement {
115 match self {
116 Placement::Top => Placement::Bottom,
117 Placement::TopStart => Placement::BottomStart,
118 Placement::TopEnd => Placement::BottomEnd,
119 Placement::Right => Placement::Left,
120 Placement::RightStart => Placement::LeftStart,
121 Placement::RightEnd => Placement::LeftEnd,
122 Placement::Bottom => Placement::Top,
123 Placement::BottomStart => Placement::TopStart,
124 Placement::BottomEnd => Placement::TopEnd,
125 Placement::Left => Placement::Right,
126 Placement::LeftStart => Placement::RightStart,
127 Placement::LeftEnd => Placement::RightEnd,
128 }
129 }
130
131 pub fn opposite_alignment(&self) -> Placement {
132 match self {
133 Placement::Top => Placement::Top,
134 Placement::TopStart => Placement::TopEnd,
135 Placement::TopEnd => Placement::TopStart,
136 Placement::Right => Placement::Right,
137 Placement::RightStart => Placement::RightEnd,
138 Placement::RightEnd => Placement::RightStart,
139 Placement::Bottom => Placement::Bottom,
140 Placement::BottomStart => Placement::BottomEnd,
141 Placement::BottomEnd => Placement::BottomStart,
142 Placement::Left => Placement::Left,
143 Placement::LeftStart => Placement::LeftEnd,
144 Placement::LeftEnd => Placement::LeftStart,
145 }
146 }
147}
148
149impl From<(Side, Option<Alignment>)> for Placement {
150 fn from(value: (Side, Option<Alignment>)) -> Self {
151 match value {
152 (Side::Top, None) => Placement::Top,
153 (Side::Top, Some(Alignment::Start)) => Placement::TopStart,
154 (Side::Top, Some(Alignment::End)) => Placement::TopEnd,
155 (Side::Right, None) => Placement::Right,
156 (Side::Right, Some(Alignment::Start)) => Placement::RightStart,
157 (Side::Right, Some(Alignment::End)) => Placement::RightEnd,
158 (Side::Bottom, None) => Placement::Bottom,
159 (Side::Bottom, Some(Alignment::Start)) => Placement::BottomStart,
160 (Side::Bottom, Some(Alignment::End)) => Placement::BottomEnd,
161 (Side::Left, None) => Placement::Left,
162 (Side::Left, Some(Alignment::Start)) => Placement::LeftStart,
163 (Side::Left, Some(Alignment::End)) => Placement::LeftEnd,
164 }
165 }
166}
167
168#[derive(Copy, Clone, Debug, PartialEq)]
169pub enum Strategy {
170 Absolute,
171 Fixed,
172}
173
174#[derive(Copy, Clone, Debug, PartialEq)]
175pub enum Axis {
176 X,
177 Y,
178}
179
180impl Axis {
181 pub fn opposite(&self) -> Axis {
182 match self {
183 Axis::X => Axis::Y,
184 Axis::Y => Axis::X,
185 }
186 }
187
188 pub fn length(&self) -> Length {
189 match self {
190 Axis::X => Length::Width,
191 Axis::Y => Length::Height,
192 }
193 }
194}
195
196#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
197pub struct Coords {
198 pub x: f64,
199 pub y: f64,
200}
201
202impl Coords {
203 pub fn new(value: f64) -> Self {
204 Self { x: value, y: value }
205 }
206
207 pub fn axis(&self, axis: Axis) -> f64 {
208 match axis {
209 Axis::X => self.x,
210 Axis::Y => self.y,
211 }
212 }
213
214 pub fn update_axis<F>(&mut self, axis: Axis, update: F)
215 where
216 F: Fn(f64) -> f64,
217 {
218 match axis {
219 Axis::X => {
220 self.x = update(self.x);
221 }
222 Axis::Y => {
223 self.y = update(self.y);
224 }
225 }
226 }
227}
228
229#[derive(Copy, Clone, Debug, PartialEq)]
230pub enum Length {
231 Width,
232 Height,
233}
234
235#[derive(Clone, Debug)]
236pub struct Dimensions {
237 pub width: f64,
238 pub height: f64,
239}
240
241impl Dimensions {
242 pub fn length(&self, length: Length) -> f64 {
243 match length {
244 Length::Width => self.width,
245 Length::Height => self.height,
246 }
247 }
248}
249
250#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
251pub struct SideObject {
252 pub top: f64,
253 pub right: f64,
254 pub bottom: f64,
255 pub left: f64,
256}
257
258impl SideObject {
259 pub fn side(&self, side: Side) -> f64 {
260 match side {
261 Side::Top => self.top,
262 Side::Right => self.right,
263 Side::Bottom => self.bottom,
264 Side::Left => self.left,
265 }
266 }
267}
268
269#[derive(Clone, Debug, PartialEq)]
270pub struct PartialSideObject {
271 pub top: Option<f64>,
272 pub right: Option<f64>,
273 pub bottom: Option<f64>,
274 pub left: Option<f64>,
275}
276
277#[derive(Clone, Debug, PartialEq)]
278pub struct Rect {
279 pub x: f64,
280 pub y: f64,
281 pub width: f64,
282 pub height: f64,
283}
284
285impl Rect {
286 pub fn axis(&self, axis: Axis) -> f64 {
287 match axis {
288 Axis::X => self.x,
289 Axis::Y => self.y,
290 }
291 }
292
293 pub fn length(&self, length: Length) -> f64 {
294 match length {
295 Length::Width => self.width,
296 Length::Height => self.height,
297 }
298 }
299}
300
301#[derive(Clone, Debug, PartialEq)]
302pub enum Padding {
303 All(f64),
304 PerSide(PartialSideObject),
305}
306
307#[derive(Clone, Debug, PartialEq)]
308pub struct ClientRectObject {
309 pub x: f64,
310 pub y: f64,
311 pub width: f64,
312 pub height: f64,
313 pub top: f64,
314 pub right: f64,
315 pub bottom: f64,
316 pub left: f64,
317}
318
319impl From<Rect> for ClientRectObject {
320 fn from(value: Rect) -> Self {
321 ClientRectObject {
322 x: value.x,
323 y: value.y,
324 width: value.width,
325 height: value.height,
326 top: value.y,
327 right: value.x + value.width,
328 bottom: value.y + value.height,
329 left: value.x,
330 }
331 }
332}
333
334cfg_if::cfg_if! {
335 if #[cfg(feature = "dom")] {
336 impl ClientRectObject {
337 pub fn from_dom_rect_list(value: web_sys::DomRectList) -> Vec<Self> {
338 (0..value.length())
339 .filter_map(|i| value.item(i).map(ClientRectObject::from))
340 .collect()
341 }
342 }
343
344 impl From<web_sys::DomRect> for ClientRectObject {
345 fn from(value: web_sys::DomRect) -> Self {
346 Self {
347 x: value.x(),
348 y: value.y(),
349 width: value.width(),
350 height: value.height(),
351 top: value.top(),
352 right: value.right(),
353 bottom: value.bottom(),
354 left: value.left(),
355 }
356 }
357 }
358 }
359}
360
361#[derive(Clone, Debug, PartialEq)]
362pub struct ElementRects {
363 pub reference: Rect,
364 pub floating: Rect,
365}
366
367#[dyn_trait]
371pub trait VirtualElement<Element: 'static>: Clone + PartialEq {
372 fn get_bounding_client_rect(&self) -> ClientRectObject;
373
374 fn get_client_rects(&self) -> Option<Vec<ClientRectObject>>;
375
376 fn context_element(&self) -> Option<Element>;
377}
378
379#[dyn_trait]
380pub trait GetBoundingClientRectCloneable: Clone {
381 fn call(&self) -> ClientRectObject;
382}
383
384impl<F> GetBoundingClientRectCloneable for F
385where
386 F: Fn() -> ClientRectObject + Clone + 'static,
387{
388 fn call(&self) -> ClientRectObject {
389 self()
390 }
391}
392
393#[dyn_trait]
394pub trait GetClientRectsCloneable: Clone {
395 fn call(&self) -> Vec<ClientRectObject>;
396}
397
398impl<F> GetClientRectsCloneable for F
399where
400 F: Fn() -> Vec<ClientRectObject> + Clone + 'static,
401{
402 fn call(&self) -> Vec<ClientRectObject> {
403 self()
404 }
405}
406
407#[derive(Clone)]
408pub struct DefaultVirtualElement<Element: Clone> {
409 pub get_bounding_client_rect: Rc<dyn GetBoundingClientRectCloneable>,
410 pub get_client_rects: Option<Rc<dyn GetClientRectsCloneable>>,
411 pub context_element: Option<Element>,
412}
413
414impl<Element: Clone> DefaultVirtualElement<Element> {
415 pub fn new(get_bounding_client_rect: Rc<dyn GetBoundingClientRectCloneable>) -> Self {
416 DefaultVirtualElement {
417 get_bounding_client_rect,
418 get_client_rects: None,
419 context_element: None,
420 }
421 }
422
423 pub fn get_bounding_client_rect(
424 mut self,
425 get_bounding_client_rect: Rc<dyn GetBoundingClientRectCloneable>,
426 ) -> Self {
427 self.get_bounding_client_rect = get_bounding_client_rect;
428 self
429 }
430
431 pub fn get_client_rects(mut self, get_client_rects: Rc<dyn GetClientRectsCloneable>) -> Self {
432 self.get_client_rects = Some(get_client_rects);
433 self
434 }
435
436 pub fn context_element(mut self, context_element: Element) -> Self {
437 self.context_element = Some(context_element);
438 self
439 }
440}
441
442impl<Element: Clone + PartialEq + 'static> VirtualElement<Element>
443 for DefaultVirtualElement<Element>
444{
445 fn get_bounding_client_rect(&self) -> ClientRectObject {
446 (self.get_bounding_client_rect).call()
447 }
448
449 fn get_client_rects(&self) -> Option<Vec<ClientRectObject>> {
450 self.get_client_rects
451 .as_ref()
452 .map(|get_client_rects| get_client_rects.call())
453 }
454
455 fn context_element(&self) -> Option<Element> {
456 self.context_element.clone()
457 }
458}
459
460impl<Element: Clone + PartialEq + 'static> PartialEq for DefaultVirtualElement<Element> {
461 fn eq(&self, other: &Self) -> bool {
462 Rc::ptr_eq(
463 &self.get_bounding_client_rect,
464 &other.get_bounding_client_rect,
465 ) && match (
466 self.get_client_rects.as_ref(),
467 other.get_client_rects.as_ref(),
468 ) {
469 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
470 (None, None) => true,
471 _ => false,
472 } && self.context_element == other.context_element
473 }
474}
475
476#[derive(Clone)]
477pub enum ElementOrVirtual<'a, Element: Clone + 'static> {
478 Element(&'a Element),
479 VirtualElement(Box<dyn VirtualElement<Element>>),
480}
481
482impl<Element: Clone + 'static> ElementOrVirtual<'_, Element> {
483 pub fn resolve(self) -> Option<Element> {
484 match self {
485 ElementOrVirtual::Element(element) => Some(element.clone()),
486 ElementOrVirtual::VirtualElement(virtal_element) => virtal_element.context_element(),
487 }
488 }
489}
490
491impl<'a, Element: Clone> From<&'a Element> for ElementOrVirtual<'a, Element> {
492 fn from(value: &'a Element) -> Self {
493 ElementOrVirtual::Element(value)
494 }
495}
496
497impl<Element: Clone> From<Box<dyn VirtualElement<Element>>> for ElementOrVirtual<'_, Element> {
498 fn from(value: Box<dyn VirtualElement<Element>>) -> Self {
499 ElementOrVirtual::VirtualElement(value)
500 }
501}
502
503impl<'a, Element: Clone> From<&'a OwnedElementOrVirtual<Element>>
504 for ElementOrVirtual<'a, Element>
505{
506 fn from(value: &'a OwnedElementOrVirtual<Element>) -> Self {
507 match value {
508 OwnedElementOrVirtual::Element(element) => ElementOrVirtual::Element(element),
509 OwnedElementOrVirtual::VirtualElement(virtual_element) => {
510 ElementOrVirtual::VirtualElement(virtual_element.clone())
511 }
512 }
513 }
514}
515
516#[derive(Clone)]
517pub enum OwnedElementOrVirtual<Element: 'static> {
518 Element(Element),
519 VirtualElement(Box<dyn VirtualElement<Element>>),
520}
521
522impl<Element: 'static> OwnedElementOrVirtual<Element> {
523 pub fn resolve(self) -> Option<Element> {
524 match self {
525 OwnedElementOrVirtual::Element(element) => Some(element),
526 OwnedElementOrVirtual::VirtualElement(virtal_element) => {
527 virtal_element.context_element()
528 }
529 }
530 }
531}
532
533impl<Element> From<Element> for OwnedElementOrVirtual<Element> {
534 fn from(value: Element) -> Self {
535 OwnedElementOrVirtual::Element(value)
536 }
537}
538
539impl<Element> From<Box<dyn VirtualElement<Element>>> for OwnedElementOrVirtual<Element> {
540 fn from(value: Box<dyn VirtualElement<Element>>) -> Self {
541 OwnedElementOrVirtual::VirtualElement(value)
542 }
543}
544
545#[derive(Clone, Debug, PartialEq)]
546pub enum ElementOrWindow<'a, Element, Window> {
547 Element(&'a Element),
548 Window(&'a Window),
549}
550
551impl<'a, Element, Window> From<&'a OwnedElementOrWindow<Element, Window>>
552 for ElementOrWindow<'a, Element, Window>
553{
554 fn from(value: &'a OwnedElementOrWindow<Element, Window>) -> Self {
555 match value {
556 OwnedElementOrWindow::Element(element) => ElementOrWindow::Element(element),
557 OwnedElementOrWindow::Window(window) => ElementOrWindow::Window(window),
558 }
559 }
560}
561
562#[derive(Clone, Debug, PartialEq)]
563pub enum OwnedElementOrWindow<Element, Window> {
564 Element(Element),
565 Window(Window),
566}
567
568pub const ALL_PLACEMENTS: [Placement; 12] = [
569 Placement::Top,
570 Placement::TopStart,
571 Placement::TopEnd,
572 Placement::Right,
573 Placement::RightStart,
574 Placement::RightEnd,
575 Placement::Bottom,
576 Placement::BottomStart,
577 Placement::BottomEnd,
578 Placement::Left,
579 Placement::LeftStart,
580 Placement::LeftEnd,
581];
582
583pub const ALL_SIDES: [Side; 4] = [Side::Top, Side::Right, Side::Bottom, Side::Left];
584
585pub fn clamp(start: f64, value: f64, end: f64) -> f64 {
586 value.min(end).max(start)
587}
588
589pub fn get_side(placement: Placement) -> Side {
590 placement.side()
591}
592
593pub fn get_alignment(placement: Placement) -> Option<Alignment> {
594 placement.alignment()
595}
596
597pub fn get_placement(side: Side, alignment: Option<Alignment>) -> Placement {
598 (side, alignment).into()
599}
600
601pub fn get_opposite_axis(axis: Axis) -> Axis {
602 axis.opposite()
603}
604
605pub fn get_axis_length(axis: Axis) -> Length {
606 axis.length()
607}
608
609pub fn get_side_axis(placement: Placement) -> Axis {
610 placement.side().axis()
611}
612
613pub fn get_alignment_axis(placement: Placement) -> Axis {
614 get_opposite_axis(get_side_axis(placement))
615}
616
617pub fn get_alignment_sides(
618 placement: Placement,
619 rects: &ElementRects,
620 rtl: Option<bool>,
621) -> (Side, Side) {
622 let alignment = get_alignment(placement);
623 let alignment_axis = get_alignment_axis(placement);
624 let length = get_axis_length(alignment_axis);
625
626 let mut main_alignment_side = match (alignment_axis, alignment) {
627 (Axis::X, Some(Alignment::Start)) => match rtl {
628 Some(true) => Side::Left,
629 _ => Side::Right,
630 },
631 (Axis::X, _) => match rtl {
632 Some(true) => Side::Right,
633 _ => Side::Left,
634 },
635 (Axis::Y, Some(Alignment::Start)) => Side::Bottom,
636 (Axis::Y, _) => Side::Top,
637 };
638
639 if rects.reference.length(length) > rects.floating.length(length) {
640 main_alignment_side = get_opposite_side(main_alignment_side);
641 }
642
643 (main_alignment_side, get_opposite_side(main_alignment_side))
644}
645
646pub fn get_expanded_placements(placement: Placement) -> Vec<Placement> {
647 let opposite_placement = get_opposite_placement(placement);
648
649 vec![
650 get_opposite_alignment_placement(placement),
651 opposite_placement,
652 get_opposite_alignment_placement(opposite_placement),
653 ]
654}
655
656pub fn get_opposite_alignment_placement(placement: Placement) -> Placement {
657 placement.opposite_alignment()
658}
659
660pub fn get_side_list(side: Side, is_start: bool, rtl: Option<bool>) -> Vec<Side> {
661 match side {
662 Side::Top | Side::Bottom => match rtl {
663 Some(true) => {
664 if is_start {
665 vec![Side::Right, Side::Left]
666 } else {
667 vec![Side::Left, Side::Right]
668 }
669 }
670 _ => {
671 if is_start {
672 vec![Side::Left, Side::Right]
673 } else {
674 vec![Side::Right, Side::Left]
675 }
676 }
677 },
678 Side::Right | Side::Left => {
679 if is_start {
680 vec![Side::Top, Side::Bottom]
681 } else {
682 vec![Side::Bottom, Side::Top]
683 }
684 }
685 }
686}
687
688pub fn get_opposite_side(side: Side) -> Side {
689 side.opposite()
690}
691
692pub fn get_opposite_axis_placements(
693 placement: Placement,
694 flip_alignment: bool,
695 direction: Option<Alignment>,
696 rtl: Option<bool>,
697) -> Vec<Placement> {
698 let alignment = get_alignment(placement);
699 let side_list = get_side_list(
700 get_side(placement),
701 direction.is_some_and(|d| d == Alignment::Start),
702 rtl,
703 );
704
705 let mut list: Vec<Placement> = side_list
706 .into_iter()
707 .map(|side| get_placement(side, alignment))
708 .collect();
709
710 if flip_alignment {
711 let mut opposite_list: Vec<Placement> = list
712 .clone()
713 .into_iter()
714 .map(get_opposite_alignment_placement)
715 .collect();
716
717 list.append(&mut opposite_list);
718 }
719
720 list
721}
722
723pub fn get_opposite_placement(placement: Placement) -> Placement {
724 placement.opposite()
725}
726
727pub fn expand_padding_object(padding: PartialSideObject) -> SideObject {
728 SideObject {
729 top: padding.top.unwrap_or(0.0),
730 right: padding.right.unwrap_or(0.0),
731 bottom: padding.bottom.unwrap_or(0.0),
732 left: padding.left.unwrap_or(0.0),
733 }
734}
735
736pub fn get_padding_object(padding: Padding) -> SideObject {
737 match padding {
738 Padding::All(padding) => SideObject {
739 top: padding,
740 right: padding,
741 bottom: padding,
742 left: padding,
743 },
744 Padding::PerSide(padding) => expand_padding_object(padding),
745 }
746}
747
748pub fn rect_to_client_rect(rect: Rect) -> ClientRectObject {
749 ClientRectObject {
750 x: rect.x,
751 y: rect.y,
752 width: rect.width,
753 height: rect.height,
754 top: rect.y,
755 right: rect.x + rect.width,
756 bottom: rect.y + rect.height,
757 left: rect.x,
758 }
759}