Spaces:
Sleeping
Sleeping
File size: 3,352 Bytes
e66cfb4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | """
layout/page/sections.py
------------------------
Section(分段)相關的資料邏輯與 UI 元件。
只依賴 Dash / DMC,不含任何 @callback。
公開 API:
split_blocks_into_sections(blocks) → List[section_dict]
get_section_blocks(sections, section_id) → List[block_dict]
make_section_control(sections, active_id) → html.Div | None
"""
from __future__ import annotations
from typing import Any
from dash import html
import dash_mantine_components as dmc
# ---------------------------------------------------------------------------
# 資料邏輯
# ---------------------------------------------------------------------------
def split_blocks_into_sections(blocks: list[dict]) -> list[dict]:
"""
把平坦的 block 清單按 H2 heading 切割成 section 清單。
回傳格式:
[
{"id": "sec-all", "title": "全部", "blocks": [...]}, # 永遠第一筆
{"id": "sec-1", "title": "標題文字", "blocks": [...]},
...
]
"""
sections: list[dict] = [
{"id": "sec-all", "title": "全部", "blocks": blocks[:]}
]
current: dict | None = None
sec_idx = 0
for b in blocks:
if b.get("type") == "heading" and int(b.get("level", 0)) == 2:
if current:
sections.append(current)
sec_idx += 1
current = {
"id": f"sec-{sec_idx}",
"title": (b.get("content") or f"段落 {sec_idx}").strip(),
"blocks": [b],
}
else:
if current is not None:
current["blocks"].append(b)
if current:
sections.append(current)
return sections
def get_section_blocks(
sections: list[dict], section_id: str | None
) -> list[dict]:
"""
取得指定 section_id 的 block 清單。
找不到時退回第一個 section;sections 為空時回傳空清單。
"""
if not sections:
return []
if not section_id:
return sections[0].get("blocks") or []
for s in sections:
if (s.get("id") or "") == section_id:
return s.get("blocks") or []
return sections[0].get("blocks") or []
# ---------------------------------------------------------------------------
# UI 元件
# ---------------------------------------------------------------------------
def make_section_control(
sections: list[dict], active_id: str | None
) -> html.Div | None:
"""
產生水平捲動的 section 切換按鈕列。
若 sections 為空,回傳 None。
"""
if not sections:
return None
buttons: list[Any] = []
for i, s in enumerate(sections):
sid = s.get("id") or f"sec-{i + 1}"
title = s.get("title") or f"段落 {i + 1}"
buttons.append(
dmc.Button(
title,
id={"type": "section-btn", "sid": sid},
variant="filled" if sid == active_id else "light",
size="sm",
style={"flex": "0 0 auto"},
)
)
return html.Div(
dmc.Group(buttons, gap="sm", wrap="nowrap"),
style={
"overflowX": "auto",
"overflowY": "hidden",
"whiteSpace": "nowrap",
"paddingBottom": "6px",
},
)
|