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/// Pick the anchor that keeps a modal of `modal`'s size on-screen when opened
43/// against `target`, from those rects alone so repeated calls agree.
44pub fn calc_relative_position(target: &DomRect, modal: &DomRect) -> ModalAnchor {
45    let window = global::window();
46    let inner_width = window.inner_width().unwrap().as_f64().unwrap();
47    let inner_height = window.inner_height().unwrap().as_f64().unwrap();
48    let top = target.top();
49    let left = target.left();
50    let height = target.height();
51    let width = target.width();
52    let modal_height = modal.height();
53    let modal_width = modal.width();
54
55    let elem_over_y = inner_height < top + height - 1.0 + modal_height;
56    let elem_over_x = inner_width < left + modal_width;
57    let target_over_x = inner_width < left + width;
58    let target_over_y = inner_height < top + height;
59
60    // modal/target
61    match (elem_over_y, elem_over_x, target_over_x, target_over_y) {
62        (true, _, true, true) => ModalAnchor::BottomRightTopLeft,
63        (true, _, true, false) => ModalAnchor::BottomRightBottomLeft,
64        (true, true, false, _) => {
65            if left + width - modal_width > 0.0 {
66                ModalAnchor::BottomRightTopRight
67            } else {
68                ModalAnchor::BottomLeftTopLeft
69            }
70        },
71        (true, false, false, _) => ModalAnchor::BottomLeftTopLeft,
72        (false, true, true, _) => ModalAnchor::TopRightTopLeft,
73        (false, true, false, _) => {
74            if left + width - modal_width > 0.0 {
75                ModalAnchor::TopRightBottomRight
76            } else {
77                ModalAnchor::TopLeftBottomLeft
78            }
79        },
80        _ => ModalAnchor::TopLeftBottomLeft,
81    }
82}
83
84/// Calculate the (top, left) position for a modal element given an anchor
85/// point, target element bounding rect, and the modal element's own bounding
86/// rect.
87pub fn calc_anchor_position(anchor: ModalAnchor, target: &DomRect, modal: &DomRect) -> (f64, f64) {
88    let height = target.height();
89    let width = target.width();
90    let top = target.top();
91    let left = target.left();
92    let rect_height = modal.height();
93    let rect_width = modal.width();
94
95    match anchor {
96        ModalAnchor::BottomRightTopLeft => (top - rect_height, left - rect_width + 1.0),
97        ModalAnchor::BottomRightBottomLeft => (top - rect_height + height, left - rect_width + 1.0),
98        ModalAnchor::BottomRightTopRight => (top - rect_height + 1.0, left + width - rect_width),
99        ModalAnchor::BottomLeftTopLeft => (top - rect_height + 1.0, left),
100        ModalAnchor::TopRightTopLeft => (top, left - rect_width + 1.0),
101        ModalAnchor::TopRightBottomRight => (top + height - 1.0, left + width - rect_width),
102        ModalAnchor::TopLeftBottomLeft => (top + height - 1.0, left),
103    }
104}