Skip to main content

perspective_viewer/utils/
modal_position.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use perspective_js::utils::global;
14use web_sys::*;
15
16/// Anchor point enum, `ModalCornerTargetCorner`
17#[derive(Clone, Copy, Debug, Default)]
18pub enum ModalAnchor {
19    BottomRightTopLeft,
20    BottomRightBottomLeft,
21    BottomRightTopRight,
22    BottomLeftTopLeft,
23    TopRightTopLeft,
24    TopRightBottomRight,
25
26    #[default]
27    TopLeftBottomLeft,
28}
29
30impl ModalAnchor {
31    pub const fn is_rev_vert(&self) -> bool {
32        matches!(
33            self,
34            Self::BottomLeftTopLeft
35                | Self::BottomRightBottomLeft
36                | Self::BottomRightTopLeft
37                | Self::BottomRightTopRight
38        )
39    }
40}
41
42/// Given the bounds of the target element as previously computed, as well as
43/// the browser's viewport and the bounds of the already-connected modal element
44/// itself, determine the best anchor point to keep the element on-screen.
45pub fn calc_relative_position(
46    elem: &HtmlElement,
47    _top: f64,
48    left: f64,
49    height: f64,
50    width: f64,
51) -> ModalAnchor {
52    let window = global::window();
53    let rect = elem.get_bounding_client_rect();
54    let inner_width = window.inner_width().unwrap().as_f64().unwrap();
55    let inner_height = window.inner_height().unwrap().as_f64().unwrap();
56    let rect_top = rect.top();
57    let rect_height = rect.height();
58    let rect_width = rect.width();
59    let rect_left = rect.left();
60
61    let elem_over_y = inner_height < rect_top + rect_height;
62    let elem_over_x = inner_width < rect_left + rect_width;
63    let target_over_x = inner_width < rect_left + width;
64    let target_over_y = inner_height < rect_top + height;
65
66    // modal/target
67    match (elem_over_y, elem_over_x, target_over_x, target_over_y) {
68        (true, _, true, true) => ModalAnchor::BottomRightTopLeft,
69        (true, _, true, false) => ModalAnchor::BottomRightBottomLeft,
70        (true, true, false, _) => {
71            if left + width - rect_width > 0.0 {
72                ModalAnchor::BottomRightTopRight
73            } else {
74                ModalAnchor::BottomLeftTopLeft
75            }
76        },
77        (true, false, false, _) => ModalAnchor::BottomLeftTopLeft,
78        (false, true, true, _) => ModalAnchor::TopRightTopLeft,
79        (false, true, false, _) => {
80            if left + width - rect_width > 0.0 {
81                ModalAnchor::TopRightBottomRight
82            } else {
83                ModalAnchor::TopLeftBottomLeft
84            }
85        },
86        _ => ModalAnchor::TopLeftBottomLeft,
87    }
88}
89
90/// Calculate the (top, left) position for a modal element given an anchor
91/// point, target element bounding rect, and the modal element's own bounding
92/// rect.
93pub fn calc_anchor_position(anchor: ModalAnchor, target: &DomRect, modal: &DomRect) -> (f64, f64) {
94    let height = target.height();
95    let width = target.width();
96    let top = target.top();
97    let left = target.left();
98    let rect_height = modal.height();
99    let rect_width = modal.width();
100
101    match anchor {
102        ModalAnchor::BottomRightTopLeft => (top - rect_height, left - rect_width + 1.0),
103        ModalAnchor::BottomRightBottomLeft => (top - rect_height + height, left - rect_width + 1.0),
104        ModalAnchor::BottomRightTopRight => (top - rect_height + 1.0, left + width - rect_width),
105        ModalAnchor::BottomLeftTopLeft => (top - rect_height + 1.0, left),
106        ModalAnchor::TopRightTopLeft => (top, left - rect_width + 1.0),
107        ModalAnchor::TopRightBottomRight => (top + height - 1.0, left + width - rect_width),
108        ModalAnchor::TopLeftBottomLeft => (top + height - 1.0, left),
109    }
110}