floating_ui_core/middleware/
hide.rs1use floating_ui_utils::{ALL_SIDES, Rect, SideObject};
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 detect_overflow::{DetectOverflowOptions, detect_overflow},
6 types::{
7 Derivable, DerivableFn, ElementContext, Middleware, MiddlewareReturn, MiddlewareState,
8 MiddlewareWithOptions,
9 },
10};
11
12fn get_side_offsets(overflow: SideObject, rect: &Rect) -> SideObject {
13 SideObject {
14 top: overflow.top - rect.height,
15 right: overflow.right - rect.width,
16 bottom: overflow.bottom - rect.height,
17 left: overflow.left - rect.width,
18 }
19}
20
21fn is_any_side_fully_clipped(overflow: &SideObject) -> bool {
22 ALL_SIDES.into_iter().any(|side| overflow.side(side) >= 0.0)
23}
24
25pub const HIDE_NAME: &str = "hide";
27
28#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
30pub enum HideStrategy {
31 #[default]
32 ReferenceHidden,
33 Escaped,
34}
35
36#[derive(Clone, Debug, PartialEq)]
38pub struct HideOptions<Element: Clone> {
39 pub detect_overflow: Option<DetectOverflowOptions<Element>>,
43
44 pub strategy: Option<HideStrategy>,
48}
49
50impl<Element: Clone> HideOptions<Element> {
51 pub fn detect_overflow(mut self, value: DetectOverflowOptions<Element>) -> Self {
53 self.detect_overflow = Some(value);
54 self
55 }
56
57 pub fn strategy(mut self, value: HideStrategy) -> Self {
59 self.strategy = Some(value);
60 self
61 }
62}
63
64impl<Element: Clone> Default for HideOptions<Element> {
65 fn default() -> Self {
66 Self {
67 detect_overflow: Default::default(),
68 strategy: Default::default(),
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
75pub struct HideData {
76 pub reference_hidden: Option<bool>,
77 pub reference_hidden_offsets: Option<SideObject>,
78 pub escaped: Option<bool>,
79 pub escaped_offsets: Option<SideObject>,
80}
81
82#[derive(PartialEq)]
89pub struct Hide<'a, Element: Clone + 'static, Window: Clone> {
90 options: Derivable<'a, Element, Window, HideOptions<Element>>,
91}
92
93impl<'a, Element: Clone, Window: Clone> Hide<'a, Element, Window> {
94 pub fn new(options: HideOptions<Element>) -> Self {
96 Hide {
97 options: options.into(),
98 }
99 }
100
101 pub fn new_derivable(options: Derivable<'a, Element, Window, HideOptions<Element>>) -> Self {
103 Hide { options }
104 }
105
106 pub fn new_derivable_fn(
108 options: DerivableFn<'a, Element, Window, HideOptions<Element>>,
109 ) -> Self {
110 Hide {
111 options: options.into(),
112 }
113 }
114}
115
116impl<Element: Clone + 'static, Window: Clone> Clone for Hide<'_, Element, Window> {
117 fn clone(&self) -> Self {
118 Self {
119 options: self.options.clone(),
120 }
121 }
122}
123
124impl<Element: Clone + PartialEq, Window: Clone + PartialEq> Middleware<Element, Window>
125 for Hide<'static, Element, Window>
126{
127 fn name(&self) -> &'static str {
128 HIDE_NAME
129 }
130
131 fn compute(&self, state: MiddlewareState<Element, Window>) -> MiddlewareReturn {
132 let options = self.options.evaluate(state.clone());
133
134 let MiddlewareState {
135 elements, rects, ..
136 } = state;
137
138 let strategy = options.strategy.unwrap_or_default();
139
140 match strategy {
141 HideStrategy::ReferenceHidden => {
142 let overflow = detect_overflow(
143 MiddlewareState {
144 elements: elements.clone(),
145 ..state
146 },
147 options
148 .detect_overflow
149 .unwrap_or_default()
150 .element_context(ElementContext::Reference),
151 );
152
153 let offsets = get_side_offsets(overflow, &rects.reference);
154
155 MiddlewareReturn {
156 x: None,
157 y: None,
158 data: Some(
159 serde_json::to_value(HideData {
160 reference_hidden: Some(is_any_side_fully_clipped(&offsets)),
161 reference_hidden_offsets: Some(offsets),
162 escaped: None,
163 escaped_offsets: None,
164 })
165 .expect("Data should be valid JSON."),
166 ),
167 reset: None,
168 }
169 }
170 HideStrategy::Escaped => {
171 let overflow = detect_overflow(
172 MiddlewareState {
173 elements: elements.clone(),
174 ..state
175 },
176 options
177 .detect_overflow
178 .unwrap_or_default()
179 .alt_boundary(true),
180 );
181
182 let offsets = get_side_offsets(overflow, &rects.floating);
183
184 MiddlewareReturn {
185 x: None,
186 y: None,
187 data: Some(
188 serde_json::to_value(HideData {
189 reference_hidden: None,
190 reference_hidden_offsets: None,
191 escaped: Some(is_any_side_fully_clipped(&offsets)),
192 escaped_offsets: Some(offsets),
193 })
194 .expect("Data should be valid JSON."),
195 ),
196 reset: None,
197 }
198 }
199 }
200 }
201}
202
203impl<Element: Clone, Window: Clone> MiddlewareWithOptions<Element, Window, HideOptions<Element>>
204 for Hide<'_, Element, Window>
205{
206 fn options(&self) -> &Derivable<'_, Element, Window, HideOptions<Element>> {
207 &self.options
208 }
209}