Dash-math_Database / layout /page /page_callbacks.py
Mochi0622's picture
initial deploy
e66cfb4
Raw
History Blame Contribute Delete
3.82 kB
"""
layout/page/page_callbacks.py
------------------------------
頁面級 Dash callback。
callback 定義在 module 層級,import 即生效。
register_page_callbacks() 保留為空殼供相容呼叫。
"""
from __future__ import annotations
import hashlib, json
from dash import callback, Input, Output, State, ALL, ctx, no_update
from ..renderer.renderer import render_doc
from .sections import get_section_blocks
from .loader import build_toc_titles
from ..toc import make_toc
# render_doc 結果快取(key = section blocks 的 hash)
_render_cache: dict[str, object] = {}
def _cached_render(blocks: list) -> object:
"""同樣的 blocks 只渲染一次。"""
try:
key = hashlib.md5(
json.dumps(blocks, ensure_ascii=False, sort_keys=True).encode()
).hexdigest()
except Exception:
return render_doc(blocks)
if key not in _render_cache:
_render_cache[key] = render_doc(blocks)
return _render_cache[key]
# ------------------------------------------------------------------
# Callback 1:切換 section → 更新內容 + TOC + Grid 欄寬
# ------------------------------------------------------------------
@callback(
Output("section-content-wrapper", "children"),
Output("section-toc-wrapper", "children"),
Output("main-content-col", "span"),
Output("toc-col", "span"),
Output("toc-col", "style"),
Output("page-active-section-store", "data"),
Input({"type": "section-btn", "sid": ALL}, "n_clicks"),
State("page-sections-store", "data"),
State("page-raw-toc-store", "data"),
prevent_initial_call=True,
)
def _cb_switch_section(_, sections, raw_toc):
if not sections:
return (no_update,) * 6
trig = ctx.triggered_id
if not trig:
return (no_update,) * 6
section_id = trig.get("sid")
current_blocks = get_section_blocks(sections, section_id)
content = _cached_render(current_blocks)
if section_id == "sec-all":
toc_titles = build_toc_titles(current_blocks, raw_toc or [])
toc_component = (
make_toc(
titles=toc_titles,
top_offset=90,
col_span={"xs": 0, "sm": 0, "md": 3, "lg": 3, "xl": 3},
visible_from="md",
)
if toc_titles
else None
)
return (
content,
toc_component,
{"base": 12, "md": 9},
{"base": 12, "md": 3},
{"display": "block"},
section_id,
)
return (
content,
None,
{"base": 12, "md": 12},
{"base": 12, "md": 0},
{"display": "none"},
section_id,
)
# ------------------------------------------------------------------
# Callback 2:同步 section 按鈕的 active 樣式
# ------------------------------------------------------------------
@callback(
Output({"type": "section-btn", "sid": ALL}, "variant"),
Output({"type": "section-btn", "sid": ALL}, "color"),
Input("page-active-section-store", "data"),
State("page-sections-store", "data"),
)
def _cb_highlight_section_buttons(active_sid, sections):
if not sections:
return [], []
variants, colors = [], []
for s in sections:
sid = s.get("id") or ""
is_active = sid == active_sid
variants.append("filled" if is_active else "light")
colors.append("blue" if is_active else "gray")
return variants, colors
# ------------------------------------------------------------------
# 相容 shim:讓舊的 register_page_callbacks() 呼叫不報錯
# ------------------------------------------------------------------
def register_page_callbacks() -> None:
"""Callbacks 在 import 時已全部掛載,此函式保留供舊版呼叫。"""
pass