gioser-web: draggable graph nodes, fixed controls, history.pushState routing
- Graph: nodes are draggable via pointer events. Snap back with spring transition on release (cubic-bezier 0.34,1.56,0.64,1) - Removed hover bounce animation (was distracting) - Page controls (minimize/close): now fixed position in viewport (top-right, z-index 100), not inside the deck-page scroll area. Created once in sync_page_controls() on show/hide deck. Controls detect active page when data-minimize/close-page is empty. - Hash routing → history.pushState: URLs are /estudio/aire etc. popstate listener handles back/forward. Initial path read on boot. - Added PointerEvent feature to gioser-graph-web Cargo.toml - Added History feature to gioser-web Cargo.toml
This commit is contained in:
@@ -40,4 +40,5 @@ features = [
|
||||
"Performance",
|
||||
"console",
|
||||
"Location",
|
||||
"History",
|
||||
]
|
||||
|
||||
+9
-8
@@ -8,14 +8,15 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly boot: () => void;
|
||||
readonly __wasm_bindgen_func_elem_229: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_1437: (a: number, b: number, c: number, d: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_228: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_228_3: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_510: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_620: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_302: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_303: (a: number, b: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_234: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_1460: (a: number, b: number, c: number, d: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_233: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_233_3: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_515: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_635: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_515_6: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_307: (a: number, b: number, c: number) => void;
|
||||
readonly __wasm_bindgen_func_elem_308: (a: number, b: number) => void;
|
||||
readonly __wbindgen_export: (a: number, b: number) => number;
|
||||
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_export3: (a: number) => void;
|
||||
|
||||
@@ -228,13 +228,6 @@ function __wbg_get_imports() {
|
||||
const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
},
|
||||
__wbg_hash_db43ea0a219f3045: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg1).hash;
|
||||
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||
}, arguments); },
|
||||
__wbg_height_110f80d27112b6b3: function(arg0) {
|
||||
const ret = getObject(arg0).height;
|
||||
return ret;
|
||||
@@ -243,6 +236,10 @@ function __wbg_get_imports() {
|
||||
const ret = getObject(arg0).height;
|
||||
return ret;
|
||||
},
|
||||
__wbg_history_4fea091a79b506f0: function() { return handleError(function (arg0) {
|
||||
const ret = getObject(arg0).history;
|
||||
return addHeapObject(ret);
|
||||
}, arguments); },
|
||||
__wbg_id_e2fe0a117fc8156c: function(arg0, arg1) {
|
||||
const ret = getObject(arg1).id;
|
||||
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
||||
@@ -426,6 +423,13 @@ function __wbg_get_imports() {
|
||||
const ret = getObject(arg0).ok;
|
||||
return ret;
|
||||
},
|
||||
__wbg_pathname_4da19d3e179041e2: function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg1).pathname;
|
||||
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||
}, arguments); },
|
||||
__wbg_pointerId_b61ce7aca1eab0cc: function(arg0) {
|
||||
const ret = getObject(arg0).pointerId;
|
||||
return ret;
|
||||
@@ -433,6 +437,9 @@ function __wbg_get_imports() {
|
||||
__wbg_preventDefault_077a15ca7e97dc5a: function(arg0) {
|
||||
getObject(arg0).preventDefault();
|
||||
},
|
||||
__wbg_pushState_c8f418e428c76877: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5));
|
||||
}, arguments); },
|
||||
__wbg_querySelectorAll_0981bdbbafa5bf17: function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).querySelectorAll(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
@@ -478,9 +485,6 @@ function __wbg_get_imports() {
|
||||
__wbg_setProperty_ee784b2651f9ff8d: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setProperty(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments); },
|
||||
__wbg_set_hash_62c4dbdd31cfb200: function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).hash = getStringFromWasm0(arg1, arg2);
|
||||
}, arguments); },
|
||||
__wbg_set_height_bdd58e6b04e88cca: function(arg0, arg1) {
|
||||
getObject(arg0).height = arg1 >>> 0;
|
||||
},
|
||||
@@ -592,51 +596,56 @@ function __wbg_get_imports() {
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 187, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1437);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 196, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1460);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [F64], shim_idx: 2, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_229);
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_234);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 6, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_228);
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_233);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 6, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_228_3);
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_233_3);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_510);
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_515);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 181, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_620);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MouseEvent")], shim_idx: 190, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_635);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 74, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_302);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_515_6);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000008: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 76, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_303);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 74, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_307);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 76, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_308);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_000000000000000a: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(Slice(F32)) -> NamedExternref("Float32Array")`.
|
||||
const ret = getArrayF32FromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_cast_000000000000000a: function(arg0, arg1) {
|
||||
__wbindgen_cast_000000000000000b: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(String) -> Externref`.
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
@@ -655,34 +664,38 @@ function __wbg_get_imports() {
|
||||
};
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_303(arg0, arg1) {
|
||||
wasm.__wasm_bindgen_func_elem_303(arg0, arg1);
|
||||
function __wasm_bindgen_func_elem_308(arg0, arg1) {
|
||||
wasm.__wasm_bindgen_func_elem_308(arg0, arg1);
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_228(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_228(arg0, arg1, addHeapObject(arg2));
|
||||
function __wasm_bindgen_func_elem_233(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_233(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_228_3(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_228_3(arg0, arg1, addHeapObject(arg2));
|
||||
function __wasm_bindgen_func_elem_233_3(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_233_3(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_510(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_510(arg0, arg1, addHeapObject(arg2));
|
||||
function __wasm_bindgen_func_elem_515(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_515(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_620(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_620(arg0, arg1, addHeapObject(arg2));
|
||||
function __wasm_bindgen_func_elem_635(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_635(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_302(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_302(arg0, arg1, addHeapObject(arg2));
|
||||
function __wasm_bindgen_func_elem_515_6(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_515_6(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_1437(arg0, arg1, arg2) {
|
||||
function __wasm_bindgen_func_elem_307(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_307(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_1460(arg0, arg1, arg2) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.__wasm_bindgen_func_elem_1437(retptr, arg0, arg1, addHeapObject(arg2));
|
||||
wasm.__wasm_bindgen_func_elem_1460(retptr, arg0, arg1, addHeapObject(arg2));
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
if (r1) {
|
||||
@@ -693,8 +706,8 @@ function __wasm_bindgen_func_elem_1437(arg0, arg1, arg2) {
|
||||
}
|
||||
}
|
||||
|
||||
function __wasm_bindgen_func_elem_229(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_229(arg0, arg1, arg2);
|
||||
function __wasm_bindgen_func_elem_234(arg0, arg1, arg2) {
|
||||
wasm.__wasm_bindgen_func_elem_234(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
|
||||
Binary file not shown.
+9
-8
@@ -2,14 +2,15 @@
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const boot: () => void;
|
||||
export const __wasm_bindgen_func_elem_229: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_1437: (a: number, b: number, c: number, d: number) => void;
|
||||
export const __wasm_bindgen_func_elem_228: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_228_3: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_510: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_620: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_302: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_303: (a: number, b: number) => void;
|
||||
export const __wasm_bindgen_func_elem_234: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_1460: (a: number, b: number, c: number, d: number) => void;
|
||||
export const __wasm_bindgen_func_elem_233: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_233_3: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_515: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_635: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_515_6: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_307: (a: number, b: number, c: number) => void;
|
||||
export const __wasm_bindgen_func_elem_308: (a: number, b: number) => void;
|
||||
export const __wbindgen_export: (a: number, b: number) => number;
|
||||
export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
||||
export const __wbindgen_export3: (a: number) => void;
|
||||
|
||||
@@ -79,9 +79,16 @@ impl AppState {
|
||||
self.sync_active_class();
|
||||
self.sync_taskbar();
|
||||
self.load_md_if_empty(element, md_url);
|
||||
// Actualizar hash sin disparar evento (evitar loop)
|
||||
// Actualizar URL con history.pushState (sin #)
|
||||
if let Some(win) = web_sys::window() {
|
||||
let _ = win.location().set_hash(&format!("/{}", element));
|
||||
if let Ok(hist) = win.history() {
|
||||
let path = format!("/estudio/{}", element);
|
||||
let _ = hist.push_state_with_url(
|
||||
&wasm_bindgen::JsValue::NULL,
|
||||
"",
|
||||
Some(&path),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,9 +117,15 @@ impl AppState {
|
||||
self.sync_active_class();
|
||||
self.sync_taskbar();
|
||||
self.hide_deck(origin_x, origin_y);
|
||||
// Limpiar hash
|
||||
// Restaurar URL
|
||||
if let Some(win) = web_sys::window() {
|
||||
let _ = win.location().set_hash("");
|
||||
if let Ok(hist) = win.history() {
|
||||
let _ = hist.push_state_with_url(
|
||||
&wasm_bindgen::JsValue::NULL,
|
||||
"",
|
||||
Some("/"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +177,7 @@ impl AppState {
|
||||
if let Some(body) = self.document.body() {
|
||||
let _ = body.class_list().add_1("deck-visible");
|
||||
}
|
||||
self.sync_page_controls();
|
||||
}
|
||||
|
||||
fn hide_deck(&self, x: f64, y: f64) {
|
||||
@@ -175,6 +189,35 @@ impl AppState {
|
||||
if let Some(body) = self.document.body() {
|
||||
let _ = body.class_list().remove_1("deck-visible");
|
||||
}
|
||||
self.sync_page_controls();
|
||||
}
|
||||
|
||||
fn sync_page_controls(&self) {
|
||||
let exists = self.document.get_element_by_id("global-page-controls");
|
||||
let is_visible = self.state.borrow().active.is_some();
|
||||
if let Some(ctl) = exists {
|
||||
ctl.set_attribute("style", if is_visible {
|
||||
"opacity:1;pointer-events:auto;"
|
||||
} else {
|
||||
"opacity:0;pointer-events:none;"
|
||||
}).ok();
|
||||
} else if is_visible {
|
||||
let Some(body) = self.document.body() else { return };
|
||||
let div: HtmlElement = self.document
|
||||
.create_element("div")
|
||||
.ok()
|
||||
.and_then(|e| e.dyn_into().ok())
|
||||
.unwrap();
|
||||
div.set_id("global-page-controls");
|
||||
div.set_attribute("class", "page-controls").ok();
|
||||
div.set_inner_html(
|
||||
"<button class=\"page-control-btn page-minimize\" data-minimize=\"\" type=\"button\" aria-label=\"Minimizar\">\
|
||||
<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M5 19 H19\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/></svg>\
|
||||
</button>\
|
||||
<button class=\"page-control-btn page-close\" data-close-page=\"\" type=\"button\" aria-label=\"Cerrar\">×</button>"
|
||||
);
|
||||
body.append_child(&div).ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn deck_el(&self) -> Option<HtmlElement> {
|
||||
@@ -243,12 +286,6 @@ impl AppState {
|
||||
};
|
||||
let html = format!(
|
||||
"<article class=\"deck-page\" data-element=\"{el}\" id=\"deck-page-{el}\">\
|
||||
<div class=\"page-controls\">\
|
||||
<button class=\"page-control-btn page-minimize\" data-minimize=\"{el}\" type=\"button\" aria-label=\"Minimizar {title}\">\
|
||||
<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M5 19 H19\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/></svg>\
|
||||
</button>\
|
||||
<button class=\"page-control-btn page-close\" data-close-page=\"{el}\" type=\"button\" aria-label=\"Cerrar {title}\">×</button>\
|
||||
</div>\
|
||||
<div class=\"page-ambience\" aria-hidden=\"true\"></div>\
|
||||
<header class=\"page-head\">\
|
||||
<span class=\"page-mark\">{el}</span>\
|
||||
@@ -448,13 +485,13 @@ pub fn boot() -> Result<(), JsValue> {
|
||||
install_deck_delegation(&document, &app)?;
|
||||
install_taskbar(&document, &app)?;
|
||||
install_keyboard(&document, &app)?;
|
||||
install_hash_listener(&window, &app)?;
|
||||
install_popstate_listener(&window, &app)?;
|
||||
install_raf(&window, &document, &canvas, &renderer);
|
||||
|
||||
// Leer hash inicial para abrir página directa
|
||||
if let Ok(hash) = window.location().hash() {
|
||||
let clean = hash.trim_start_matches('#').trim_start_matches('/');
|
||||
if !clean.is_empty() && clean != "" {
|
||||
// Leer ruta inicial para abrir página directa
|
||||
if let Ok(pathname) = window.location().pathname() {
|
||||
let clean = pathname.trim_start_matches('/').trim_start_matches("estudio/");
|
||||
if !clean.is_empty() {
|
||||
if let Some(el) = document.query_selector(&format!(".tip[data-md][id='tip-{}']", clean)).ok().flatten() {
|
||||
let rect = el.get_bounding_client_rect();
|
||||
let cx = rect.left() + rect.width() / 2.0;
|
||||
@@ -469,14 +506,14 @@ pub fn boot() -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_hash_listener(window: &Window, app: &Rc<AppState>) -> Result<(), JsValue> {
|
||||
fn install_popstate_listener(window: &Window, app: &Rc<AppState>) -> Result<(), JsValue> {
|
||||
let app2 = app.clone();
|
||||
let doc = app.document.clone();
|
||||
let win2 = window.clone();
|
||||
let cb = Closure::<dyn FnMut(Event)>::new(move |_e: Event| {
|
||||
if let Ok(hash) = win2.location().hash() {
|
||||
let clean = hash.trim_start_matches('#').trim_start_matches('/');
|
||||
if clean.is_empty() {
|
||||
if let Ok(pathname) = win2.location().pathname() {
|
||||
let clean = pathname.trim_start_matches('/').trim_start_matches("estudio/");
|
||||
if clean.is_empty() || clean == "/" {
|
||||
app2.home();
|
||||
} else if let Some(el) = doc.query_selector(&format!(".tip[data-md][id='tip-{}']", clean)).ok().flatten() {
|
||||
let rect = el.get_bounding_client_rect();
|
||||
@@ -488,7 +525,7 @@ fn install_hash_listener(window: &Window, app: &Rc<AppState>) -> Result<(), JsVa
|
||||
}
|
||||
}
|
||||
});
|
||||
window.add_event_listener_with_callback("hashchange", cb.as_ref().unchecked_ref())?;
|
||||
window.add_event_listener_with_callback("popstate", cb.as_ref().unchecked_ref())?;
|
||||
cb.forget();
|
||||
Ok(())
|
||||
}
|
||||
@@ -615,22 +652,35 @@ fn install_deck_delegation(document: &Document, app: &Rc<AppState>) -> Result<()
|
||||
if let Ok(Some(btn)) = target_el.closest("[data-minimize]") {
|
||||
e.stop_propagation();
|
||||
let element = btn.get_attribute("data-minimize").unwrap_or_default();
|
||||
// Origin = la cajita correspondiente en la taskbar (efecto
|
||||
// visual: la página se "encoge" hacia su entrada del taskbar).
|
||||
let origin = app2
|
||||
.taskbar_item_center(&element)
|
||||
.unwrap_or_else(|| center_of_element(&btn));
|
||||
app2.minimize(origin.0, origin.1);
|
||||
// Si el data-minimize está vacío, usar el elemento activo
|
||||
let el = if element.is_empty() {
|
||||
app2.state.borrow().active.clone().unwrap_or_default()
|
||||
} else {
|
||||
element
|
||||
};
|
||||
if !el.is_empty() {
|
||||
let origin = app2
|
||||
.taskbar_item_center(&el)
|
||||
.unwrap_or_else(|| center_of_element(&btn));
|
||||
app2.minimize(origin.0, origin.1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Close
|
||||
if let Ok(Some(btn)) = target_el.closest("[data-close-page]") {
|
||||
e.stop_propagation();
|
||||
let element = btn.get_attribute("data-close-page").unwrap_or_default();
|
||||
let origin = app2
|
||||
.taskbar_item_center(&element)
|
||||
.unwrap_or_else(|| center_of_element(&btn));
|
||||
app2.close(&element, origin.0, origin.1);
|
||||
let el = if element.is_empty() {
|
||||
app2.state.borrow().active.clone().unwrap_or_default()
|
||||
} else {
|
||||
element
|
||||
};
|
||||
if !el.is_empty() {
|
||||
let origin = app2
|
||||
.taskbar_item_center(&el)
|
||||
.unwrap_or_else(|| center_of_element(&btn));
|
||||
app2.close(&el, origin.0, origin.1);
|
||||
}
|
||||
}
|
||||
});
|
||||
deck_el.add_event_listener_with_callback("click", cb.as_ref().unchecked_ref())?;
|
||||
|
||||
@@ -254,14 +254,21 @@ body.deck-active-tierra .deck { --deck-glow: rgba(212, 152, 115, 0.24); }
|
||||
to { opacity: 0.80; }
|
||||
}
|
||||
|
||||
/* Head + controls */
|
||||
/* Head + controls — fijos en el deck, no dentro de la página */
|
||||
.page-controls {
|
||||
position: absolute;
|
||||
top: 1.8vh;
|
||||
right: 1.8vw;
|
||||
z-index: 5;
|
||||
position: fixed;
|
||||
top: 1.2rem;
|
||||
right: 1.2rem;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.deck--visible .page-controls {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.page-control-btn {
|
||||
width: 40px;
|
||||
|
||||
Reference in New Issue
Block a user