use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use super::dom;
const STATE_FLAG: &str = "islandsNav";
const STATE_SCROLL_X: &str = "islandsScrollX";
const STATE_SCROLL_Y: &str = "islandsScrollY";
pub(crate) fn set_manual_scroll_restoration() -> Result<(), JsValue> {
let history = dom::window()?.history()?;
let _ = history.set_scroll_restoration(web_sys::ScrollRestoration::Manual);
Ok(())
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ScrollPosition {
pub x: f64,
pub y: f64,
}
fn current_scroll() -> ScrollPosition {
let window = match dom::window() {
Ok(window) => window,
Err(_) => return ScrollPosition::default(),
};
ScrollPosition {
x: window.scroll_x().unwrap_or(0.0),
y: window.scroll_y().unwrap_or(0.0),
}
}
fn build_state(scroll: ScrollPosition) -> JsValue {
let state = js_sys::Object::new();
let _ = js_sys::Reflect::set(&state, &JsValue::from_str(STATE_FLAG), &JsValue::TRUE);
let _ = js_sys::Reflect::set(
&state,
&JsValue::from_str(STATE_SCROLL_X),
&JsValue::from_f64(scroll.x),
);
let _ = js_sys::Reflect::set(
&state,
&JsValue::from_str(STATE_SCROLL_Y),
&JsValue::from_f64(scroll.y),
);
state.into()
}
pub(crate) fn scroll_from_state(state: &JsValue) -> ScrollPosition {
let read = |key: &str| -> f64 {
js_sys::Reflect::get(state, &JsValue::from_str(key))
.ok()
.and_then(|value| value.as_f64())
.unwrap_or(0.0)
};
ScrollPosition {
x: read(STATE_SCROLL_X),
y: read(STATE_SCROLL_Y),
}
}
pub(crate) fn is_nav_state(state: &JsValue) -> bool {
js_sys::Reflect::get(state, &JsValue::from_str(STATE_FLAG))
.ok()
.and_then(|value| value.as_bool())
.unwrap_or(false)
}
pub(crate) fn capture_scroll_and_push(url: &str) -> Result<(), JsValue> {
let history = dom::window()?.history()?;
let outgoing = build_state(current_scroll());
let _ = history.replace_state_with_url(&outgoing, "", None);
let incoming = build_state(ScrollPosition::default());
history.push_state_with_url(&incoming, "", Some(url))?;
Ok(())
}
pub(crate) fn restore_scroll_next_frame(target: ScrollPosition) -> Result<(), JsValue> {
let window = dom::window()?;
let window_for_callback = window.clone();
let callback = Closure::once_into_js(move || {
window_for_callback.scroll_to_with_x_and_y(target.x, target.y);
});
window.request_animation_frame(callback.as_ref().unchecked_ref())?;
Ok(())
}
pub(crate) fn focus_main() -> Result<(), JsValue> {
let document = dom::document()?;
let main = match document.query_selector("main")? {
Some(main) => main,
None => return Ok(()),
};
let main_element: web_sys::HtmlElement = match main.dyn_into() {
Ok(element) => element,
Err(_) => return Ok(()),
};
if !main_element.has_attribute("tabindex") {
let _ = main_element.set_attribute("tabindex", "-1");
}
let _ = main_element.focus();
Ok(())
}