biji_ui/utils/
positioning.rs

1#[derive(Copy, Clone, Debug)]
2pub enum Positioning {
3    Top,
4    TopStart,
5    TopEnd,
6    Right,
7    RightStart,
8    RightEnd,
9    Bottom,
10    BottomStart,
11    BottomEnd,
12    Left,
13    LeftStart,
14    LeftEnd,
15}
16
17impl Default for Positioning {
18    fn default() -> Self {
19        Positioning::Top
20    }
21}
22
23impl Positioning {
24    /// Calculate the position of content relative to a trigger element
25    /// Returns (top, left) coordinates in pixels
26    pub fn calculate_position(
27        self,
28        trigger_top: f64,
29        trigger_left: f64,
30        trigger_width: f64,
31        trigger_height: f64,
32        content_height: f64,
33        content_width: f64,
34        offset: f64,
35    ) -> (f64, f64) {
36        match self {
37            Positioning::Top => {
38                let top = trigger_top - content_height - offset;
39                let left = trigger_left + (trigger_width / 2.0) - (content_width / 2.0);
40                (top, left)
41            }
42            Positioning::TopStart => {
43                let top = trigger_top - content_height - offset;
44                (top, trigger_left)
45            }
46            Positioning::TopEnd => {
47                let top = trigger_top - content_height - offset;
48                let left = trigger_left + trigger_width - content_width;
49                (top, left)
50            }
51            Positioning::Right => {
52                let top = trigger_top + (trigger_height / 2.0) - (content_height / 2.0);
53                let left = trigger_left + trigger_width + offset;
54                (top, left)
55            }
56            Positioning::RightStart => {
57                let left = trigger_left + trigger_width + offset;
58                (trigger_top, left)
59            }
60            Positioning::RightEnd => {
61                let top = trigger_top + trigger_height - content_height;
62                let left = trigger_left + trigger_width + offset;
63                (top, left)
64            }
65            Positioning::Bottom => {
66                let top = trigger_top + trigger_height + offset;
67                let left = trigger_left + (trigger_width / 2.0) - (content_width / 2.0);
68                (top, left)
69            }
70            Positioning::BottomStart => {
71                let top = trigger_top + trigger_height + offset;
72                (top, trigger_left)
73            }
74            Positioning::BottomEnd => {
75                let top = trigger_top + trigger_height + offset;
76                let left = trigger_left + trigger_width - content_width;
77                (top, left)
78            }
79            Positioning::Left => {
80                let top = trigger_top + (trigger_height / 2.0) - (content_height / 2.0);
81                let left = trigger_left - content_width - offset;
82                (top, left)
83            }
84            Positioning::LeftStart => {
85                let left = trigger_left - content_width - offset;
86                (trigger_top, left)
87            }
88            Positioning::LeftEnd => {
89                let left = trigger_left - content_width - offset;
90                let top = trigger_top + trigger_height - content_height;
91                (top, left)
92            }
93        }
94    }
95
96    /// Calculate the position and rotation for an arrow indicator
97    /// Returns (top, left, rotation) where rotation is in degrees
98    pub fn calculate_arrow_position(
99        self,
100        trigger_top: f64,
101        trigger_left: f64,
102        trigger_width: f64,
103        trigger_height: f64,
104        arrow_size: f64,
105    ) -> (f64, f64, i32) {
106        match self {
107            Positioning::Top | Positioning::TopStart | Positioning::TopEnd => {
108                let top = trigger_top - arrow_size - (arrow_size / 2.0);
109                let left = trigger_left + (trigger_width / 2.0) - (arrow_size / 2.0);
110                (top, left, 225)
111            }
112            Positioning::Right | Positioning::RightStart | Positioning::RightEnd => {
113                let top = trigger_top + (trigger_height / 2.0) - (arrow_size / 2.0);
114                let left = trigger_left + trigger_width + (arrow_size / 2.0);
115                (top, left, 315)
116            }
117            Positioning::Bottom | Positioning::BottomStart | Positioning::BottomEnd => {
118                let top = trigger_top + trigger_height + arrow_size - (arrow_size / 2.0);
119                let left = trigger_left + (trigger_width / 2.0) - (arrow_size / 2.0);
120                (top, left, 45)
121            }
122            Positioning::Left | Positioning::LeftStart | Positioning::LeftEnd => {
123                let top = trigger_top + (trigger_height / 2.0) - (arrow_size / 2.0);
124                let left = trigger_left - arrow_size - (arrow_size / 2.0);
125                (top, left, 135)
126            }
127        }
128    }
129
130    /// Calculate position as a CSS style string with arrow CSS variables
131    pub fn calculate_position_style(
132        self,
133        trigger_top: f64,
134        trigger_left: f64,
135        trigger_width: f64,
136        trigger_height: f64,
137        content_height: f64,
138        content_width: f64,
139        offset: f64,
140        arrow_size: f64,
141    ) -> String {
142        let position = self.calculate_position(
143            trigger_top,
144            trigger_left,
145            trigger_width,
146            trigger_height,
147            content_height,
148            content_width,
149            offset,
150        );
151        let arrow_position = self.calculate_arrow_position(
152            trigger_top,
153            trigger_left,
154            trigger_width,
155            trigger_height,
156            arrow_size,
157        );
158        format!(
159            "position: fixed; top: {}px; left: {}px; --biji-tooltip-arrow-top: {}px; --biji-tooltip-arrow-left: {}px; --biji-tooltip-arrow-rotation: {}deg;",
160            position.0, position.1, arrow_position.0, arrow_position.1, arrow_position.2
161        )
162    }
163
164    /// Calculate position as a simple CSS style string without arrow variables
165    pub fn calculate_position_style_simple(
166        self,
167        trigger_top: f64,
168        trigger_left: f64,
169        trigger_width: f64,
170        trigger_height: f64,
171        content_height: f64,
172        content_width: f64,
173        offset: f64,
174    ) -> String {
175        let position = self.calculate_position(
176            trigger_top,
177            trigger_left,
178            trigger_width,
179            trigger_height,
180            content_height,
181            content_width,
182            offset,
183        );
184        format!(
185            "position: fixed; top: {}px; left: {}px;",
186            position.0, position.1
187        )
188    }
189}