跳到內容

元素

元素是詞彙在瀏覽器端的一半:每種區塊一個不依賴框架的自訂元素。宿主把剖析好的 data 交給每個元素,並監聽一個事件,pbs-answer。

import "pi-better-show/elements"; // defines every pbs-* tag (a no-op without a DOM)
import { configure } from "pi-better-show/elements";
el.data = segment.data; // or <pbs-form data-json='{...}'>
el.addEventListener("pbs-answer", (event) => send(encodeAnswer(event.detail)));
el.answered = event.detail.fields; // once delivered: locks the element and shows what was sent

宿主把區塊剖析後的 data 交給元素,並監聽 pbs-answer(會冒泡、composed、可取消),它的 detail 是 core 的 Answer。元素從不讀寫 HTML:文字以文字節點進入頁面,連結只允許 http(s),並帶 rel="noopener noreferrer" 與 target="_blank"。每個元素是帶 constructed stylesheet(adoptedStyleSheets)的 shadow root,所以能在 default-src 'self' 下執行:沒有 <style> 元素,也沒有 style 屬性。examples/ 就是這樣的頁面;用 bun run demo 執行(loopback,連接埠 4177)。

標籤 區塊 data 答案 部件
pbs-callout alert AlertData callout header icon title body
pbs-cards cards CardsData grid card title icon body
pbs-options options OptionsData pick grid option title badge body pros cons actions pick status
pbs-form form FormData form form title question label hint choices choice badge other textarea error actions submit status sent
pbs-review review ReviewData review review title items item item-title pick media image enlarge frame unavailable body comment actions submit status lightbox close sent
pbs-mindmap mindmap MindmapData mindmap tree item node toggle label group
pbs-suggest suggest SuggestData suggest suggest chips chip status
pbs-checklist checklist ChecklistData checklist checklist title items item progress actions submit status sent
pbs-table table TableData table bar filter count scroll header head sort body row cell empty
pbs-steps steps StepsData steps step rail marker content title badge when body
pbs-stats stats StatsData grid stat label value delta note
pbs-diff diff DiffData diff scroll line
pbs-diagram mermaid MermaidData diagram image source note
pbs-tree tree TreeData treeview bar expand-all collapse-all tree item node toggle name count tags tag note more group

TAGS 把區塊種類對應到它的標籤。沒有整份文件的元素:extension 在自己的頁面伺服器上渲染 Markdown,其他宿主用自己的渲染器。

每個元素都有這些。

名稱 種類 意義
data / data-json property / attribute 區塊的資料;JSON 有誤或元素畫不了的形狀,會顯示錯誤面板並觸發 pbs-error。
answered / data-answered property / attribute 已送出的 [name, value][]:鎖定元素並顯示它們。undefined 會重新開啟。
ref,或 doc 與 block 屬性 property / attributes 區塊的位址,會複製進每份答案的 ref。
density attribute 與 property comfortable(預設)或 compact。
scheme attribute 與 property light 或 dark,用來強制配色;否則元素跟隨宿主的 color-scheme。
lang attribute(或祖先的、文件的) zh* 顯示繁體中文,其他顯示英文。
refresh() method 重新繪製。宿主改變 color-scheme 或文件的 lang 之後要呼叫它,元素觀察不到這些變化。

挑選(pbs-options)或 chip(pbs-suggest)會立刻送出,並在監聽者接下事件後鎖定自己;呼叫 preventDefault() 的監聽者會拒絕它,元素維持開啟。表單、review 或 checklist 送出後仍可編輯(狀態列寫「Sent.」),所以使用者可以再送一份修改過的答案;宿主用設定 answered 來鎖定它。表單的必填問題在送出前先檢查。在文字框按 Ctrl/Cmd+Enter 即可送出。

元素本身不該做的事由宿主負責:圖、review 圖片與原型從哪裡來、用字,以及圖能不能被指點。

configure({
strings: { "zh-Hant": { send: "送出" } }, // per locale, per word (see Strings)
diagramUrl: (source, scheme) => `/diagram.svg?...`, // an image URL for pbs-diagram; without it the source is shown
imageUrl: (ref) => `/files/${ref}`, // a review item's `image`; default admits http(s) and raster data URLs only
pageUrl: (ref) => `/pages/${ref}`, // a review item's `page`, shown in a sandboxed frame; without it, its name
diagramMap: async (url) => readTargets(url), // makes a diagram's nodes and edges clickable (below); without it the diagram is an image
});

請從 pi-better-show/server 提供圖片(mermaidSvg(source, { scheme, transparent: true }));圖的標記不會進入頁面。resetConfiguration() 會還原預設值。

設了 diagramMap 後,圖的節點與邊會變成使用者可以留言的按鈕。

宿主要提供什麼

設了 diagramMap 後,pbs-diagram 在圖片的每個節點、邊(它的線與標籤框)與 subgraph 標題上蓋一個透明按鈕。diagramMap(url) 收到 diagramUrl 回傳的 URL,並解析成 DiagramMap:{ viewBox, targets: [{ kind: "node" | "edge" | "group", text, shapes: [{ tag, attrs }] }] },形狀用圖片自己的座標,而且只帶幾何屬性。讀取並剖析繪圖的是宿主,因為元素從不剖析標記;extension 的頁面為 mermaidSvg 畫的 SVG 做這件事(g.node、.edge、g.edge-label、g.subgraph 與它們的 data-* 標籤:流程圖與狀態圖;其他圖種沒有目標,維持圖片)。目標依閱讀順序排列,也就是方向鍵走的順序;不論順序如何,群組畫在邊的下面,邊畫在節點的下面。點擊,或在有焦點的按鈕上按 Enter 或空白鍵,會從按鈕本身觸發 pbs-diagram-target(冒泡、composed),帶 detail: { kind, text },所以 composedPath()[0] 就是宿主要把焦點還回去的地方。整張圖是一個 Tab 停靠點,落在最後取得焦點的按鈕上(一開始是第一個);方向鍵移到下一個或上一個按鈕,Home 與 End 移到第一個或最後一個。按鈕放在一個 role="group" 裡,它的描述(aria-describedby,一個 SVG desc)告訴螢幕閱讀器用方向鍵在按鈕之間移動;DOM 維持繪製的圖層順序,而不是閱讀順序。每個按鈕都帶 aria-label(「Comment on edge: …」),並在滑過與取得焦點時顯示蓋在圖上的色調,所以不會有東西移動。

在元素或任何祖先上設定 --pbs-* 屬性;每個都有跟隨宿主 color-scheme 的 light-dark() 預設值。

Token 意義
--pbs-bg --pbs-surface 面板背景;有底色的表面(表頭、程式碼)
--pbs-fg --pbs-muted 文字;次要文字
--pbs-line --pbs-control 面板與分隔線;輸入框的邊(與其表面 3:1 對比)
--pbs-accent --pbs-on-accent 按鈕、連結、選取;其上的文字
--pbs-note --pbs-ok --pbs-warn --pbs-risk 語氣
--pbs-radius --pbs-gap 圓角;區塊之間與內部的間距
--pbs-font --pbs-mono --pbs-font-size 字體
--pbs-font-display 標題(預設為 --pbs-font)

結構帶有 part 名稱,可用 ::part() 重新設計樣式。面板四邊的框線相同,或完全沒有;語氣以色調、徽章、圖示與標題顏色呈現,絕不用有顏色或較粗的單邊框線。examples/demo.css 把 Roundtable 與 Straw boss 的 token 對應起來當範例。

每個控制項都是有標籤的原生控制項。pbs-mindmap 遵循 ARIA tree 模式(方向鍵移動,右鍵與左鍵展開與收合,Enter 與空白鍵切換,Home 與 End 跳轉);pbs-tree 也遵循,並把資料夾展開到第 2 層(更深、含有帶標籤節點的資料夾會展開到那條路徑),附「expand all」與「collapse all」按鈕;pbs-table 從表頭按鈕排序(aria-sort),並從有標籤的搜尋框篩選。