bp-tools.js 擴充補丁

bp-tools-patch.js
六個元件,一個補丁

只需兩個 script 標籤,為任何網頁加入 InfoRegion、DualCell、WordFlip、bp-slide、bp-notice、BpNote 六大元件,並透過 BPTools 事件匯流排串接所有互動。

<!-- 1. Bootstrap Icons(部分元件需要)-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

<!-- 2. 主工具庫(永遠不動)-->
<script src="bp-tools.js"></script>

<!-- 3. 擴充補丁(所有新功能)-->
<script src="bp-tools-patch.js"></script>
BPTools 匯流排 InfoRegion DualCell WordFlip ir-challenge bp-slide bp-notice BpNote

快速開始

頁面裡使用 BPTools 訂閱事件時,必須用輪詢寫法,確保 BPTools 建立完成後才執行。

頁面自訂訂閱的正確寫法
// ✓ 正確:輪詢等待
(function wait() {
  if (typeof BPTools === 'undefined') { setTimeout(wait, 50); return; }
  BPTools.on('my:event', () => { /* 你的邏輯 */ });
})();

// ✗ 錯誤:DOMContentLoaded 不保證 BPTools 已就緒
document.addEventListener('DOMContentLoaded', () => {
  BPTools.on('my:event', ...); // 可能噴 BPTools is not defined
});
全域設定:BpNote 和 bp-slide 支援在 patch 載入前定義 window.StickyNotesConfig 覆蓋預設值。各元件的設定方式詳見各自章節。

BPTools 事件匯流排

六個元件的通訊中樞。發布端不知道誰在聽,訂閱端不知道誰觸發,靠事件名稱字串鬆散耦合。_fired 紀錄歷史,後來載入的元件也能知道事件是否已發過並立即解鎖。

BPTools.on(ev, fn)

訂閱事件。同一事件可訂閱多次,全部觸發。

BPTools.emit(ev, data)

發布事件。同時記入 _fired 歷史。

BPTools.off(ev, fn)

取消特定訂閱。

BPTools._fired

Set<string>,記錄所有曾發過的事件名稱。

一個事件同時觸發多件事
BPTools.on('lesson:done', () => badge.style.display = 'block');
BPTools.on('lesson:done', () => fetch('/api/progress', { method: 'POST' }));
BPTools.on('lesson:done', () => localStorage.setItem('done', '1'));
事件命名建議:用 主體:動作 格式,例如 quiz:passlesson:doneslide:revealed。描述「發生了什麼」而不是「要做什麼」。

品牌色系

所有元件共用同一套色系,透過 BrandColors 統一管理。支援深色/淺色雙主題切換。

shell
lavender
special
warning
salmon
attention
sky
safe
brown
info
pink
orange
BrandColors API
BrandColors.applyTheme('light');        // 切換主題,全頁自動更新
BrandColors.get('sky');               // '#04b5a3'
BrandColors.get('sky', 'light');      // '#027a6c'(淺色主題值)
BrandColors.resolve('sky');           // 同 get,找不到時原值回傳

InfoRegion 步驟引導

逐步動畫顯示的說明區塊,支援自動播放、倒數、手動確認、全域進度條。patch 新增分支路徑與 link 事件發布。

基本串接
<info-region-group global-progress progress-color="sky" show-percent
  start-label="▶ 開始" start-color="sky">

  <info-region id="s1" color="sky" next="s2" countdown="2000">
    第一步,2 秒後自動前進。
  </info-region>

  <info-region id="s2" color="lavender"
    manual manual-label="下一步 →" link="lesson:done">
    第二步,手動確認,完成時發出 lesson:done 事件。
  </info-region>

</info-region-group>
分支路徑(patch 新增)
<info-region id="q1" color="lavender" choice-align="left">
  <p>問題文字</p>
  <ir-choice target="ans-ok"  color="safe"    icon="check-circle">正確選項</ir-choice>
  <ir-choice target="ans-no"  color="warning" icon="x-circle">錯誤選項</ir-choice>
</info-region>

<!-- 答錯:next 指回題目,自動重置 -->
<info-region id="ans-no" color="warning" next="q1">
  ✗ 不對,再試一次。
</info-region>

patch 新增屬性

屬性(info-region)預設說明
link啟動時向匯流排發出事件
屬性(ir-choice)
target必填點擊後跳往的 info-region id
color父層色按鈕品牌色
iconBootstrap Icon 類名(不含 bi-)
屬性(info-region,choice-align)
choice-alignleft選項按鈕對齊:left / center / right

DualCell 多欄表格

語義化多欄表格,支援主題色、遮罩揭示、群組折疊、輪播等。patch 新增 on-link,訂閱事件後自動移除遮罩。

on-link 解鎖遮罩(patch 新增)
<div id="t1" data-dual-cell data-cols="2" data-theme="sky"
     data-show-menu-button="false">

  <div data-col>問題</div>

  <!-- on-link:監聽事件,事件發生後移除遮罩 -->
  <div data-col
       data-on-link="quiz:pass"
       data-overlay-1-text="答對後解鎖">
    答案內容
  </div>

</div>

patch 新增屬性

屬性(data-col)說明
data-on-link事件名稱。事件發生時移除此格的所有遮罩,取消 blur。
主題色:data-theme 支援所有品牌色名稱:sky / lavender / special / safe / info / warning / salmon / attention / pink / orange / brown / default

WordFlip 行內互動詞彙

夾在段落中的可點擊詞彙,點擊後展開說明。patch 新增四項擴充,並支援 12 種視覺樣式。

patch 擴充屬性
<!-- link:翻開時發出事件 -->
<word-flip data-color="sky" link="term:opened"
           data-content="說明文字">詞彙</word-flip>

<!-- unlock-on:預設鎖定,事件發生後才可點擊 -->
<word-flip data-color="safe" unlock-on="quiz:pass"
           data-content="解鎖後的說明">鎖定中</word-flip>

<!-- answer-src:從外部元素讀取 HTML 內容 -->
<div id="my-detail" style="display:none"><strong>完整 HTML 說明</strong></div>
<word-flip data-color="lavender" answer-src="my-detail">詞彙</word-flip>

<!-- group:同組互斥,同時只展開一個 -->
<word-flip data-color="sky"     group="terms" data-content="A 的說明">詞 A</word-flip>
<word-flip data-color="lavender" group="terms" data-content="B 的說明">詞 B</word-flip>

全部 style-type 樣式

badgepillhighlight tagbuttonicon underlinedotted card ★ glow ★ neon ★ chip ★ ribbon ★ announce ★

★ = patch 新增樣式

注意:bp-tools.js 原始元件的展開內容屬性是 data-content,不是 answer

ir-challenge 輸入驗證

答對才向匯流排發出事件,可連動解鎖任意訂閱同一事件的元件。支援多個正確答案。

完整範例
<ir-challenge
  answer="Cookie|cookie|COOKIE"  <!-- | 分隔多個答案 -->
  link="q:correct"             <!-- 答對後發出的事件 -->
  color="sky"
  placeholder="輸入答案…"
  hint="提示文字"
  btn-label="確認"
  font-size="1rem"           <!-- 同時設定輸入框與按鈕 -->
  btn-font-size="0.9rem"    <!-- 單獨設定按鈕 -->
  input-font-size="1rem"   <!-- 單獨設定輸入框 -->
  btn-padding="8px 24px"
  case-sensitive>           <!-- 加上此屬性則區分大小寫 -->
</ir-challenge>
屬性預設說明
answer必填正確答案,| 分隔多個
link答對後向匯流排發出的事件名稱
colorsky品牌色(影響邊框、按鈕、focus 光暈)
placeholder輸入答案…輸入框提示
hint輸入框下方灰色提示文字
btn-label確認按鈕文字
font-size同時設定輸入框與按鈕字體
input-font-size單獨設定輸入框字體
btn-font-size單獨設定按鈕字體
btn-padding7px 20px按鈕 padding
case-sensitive布林屬性,加上後區分大小寫

bp-slide 投影片

原 slider-show.js 已整合進 patch,標籤改為 <bp-slide>。完整保留原有功能,新增四個匯流排連動屬性。

連動屬性範例
<bp-slide
  theme="orange"
  height="240px"
  link="slider:done"          <!-- 按完成按鈕時發事件 -->
  unlock-on="quiz:pass"     <!-- 等事件才解鎖整個導航 -->
  auto-show-part-buttons="true"
  finish-btn-text="完成課程 →">

  <div slide part="1">第一頁</div>

  <!-- 切換到此頁時發出事件 -->
  <div slide part="1" link="slide:p2">第二頁</div>

  <!-- 停在此頁時鎖住下一頁,等 quiz:pass 才放行 -->
  <div slide part="1" unlock-on="quiz:pass">第三頁</div>

</bp-slide>

patch 新增連動屬性

屬性位置屬性說明
<bp-slide>link按「完成」按鈕時發出事件
<bp-slide>unlock-on等事件才解鎖整個 slider 的導航
[slide]link切換到此張時發出事件
[slide]unlock-on停在此張時鎖住下一頁,等事件才放行

bp-notice 公告元件

區塊級公告,支援燃燒引線倒數動畫、可關閉、置頂固定、事件解鎖。內文支援完整 HTML 與 Bootstrap Icons。

完整範例
<!-- 燃燒引線 + 可關閉 -->
<bp-notice
  color="sky"
  icon="info-circle-fill"    <!-- Bootstrap Icon 類名 -->
  font-size="1rem"
  icon-size="1.4rem"
  padding="16px 20px"
  dismissible                <!-- 顯示關閉按鈕 -->
  auto-hide="5000"           <!-- 5 秒後燃盡消失 -->
  sticky                     <!-- 置頂固定 -->
  unlock-on="lesson:done"  <!-- 等事件才顯示,顯示後才倒數 -->
  link="notice:closed"      <!-- 關閉時發出事件 -->>
  <!-- 完整 HTML + Bootstrap Icons -->
  <strong>公告標題</strong>
  <ul>
    <li><i class="bi bi-check2"></i> 項目一</li>
  </ul>
</bp-notice>
屬性預設說明
colorsky品牌色
icon左側 Bootstrap Icon 類名(不含 bi-)
font-size.92rem整體字體大小
icon-size1.2rem左側圖示大小
padding14px 18px內距
dismissible布林屬性,顯示關閉按鈕
auto-hide毫秒數,到時燃燒引線燃盡後淡出
sticky布林屬性,position:sticky 置頂
unlock-on等匯流排事件才顯示,顯示後才開始倒數
link關閉或 auto-hide 消失時發出的事件
燃燒引線:加上 auto-hide 屬性後底部自動出現引線動畫,火花顏色跟隨 color 屬性,無需額外設定。

BpNote 便利貼

雙擊頁面任意空白處建立便利貼。整合 BrandColors 品牌色,支援拖曳、調色、收合、localStorage 自動儲存,並可透過 BPTools 匯流排通知其他元件。

全域設定(在 patch 載入前定義)
<script>
  window.StickyNotesConfig = {
    defaultColor : 'lavender',  // 品牌色名稱或 hex
    defaultWidth : 280,
    position     : 'fixed',      // 'fixed' | 'absolute'
    storageKey   : 'my_notes',  // localStorage 鍵名
    excludeClass : 'no-sticky', // 掛此 class 的元素不觸發
    fontSize     : '1rem',       // 便利貼字體大小
    lineHeight   : '1.8',        // 便利貼行高
    link         : 'note:saved', // 操作時向匯流排發出的事件
  };
</script>
BpNote.save()

手動觸發 localStorage 儲存。

BpNote.clearAll()

清除所有便利貼。

BpNote.export()

回傳 JSON 陣列,可存入資料庫。

BpNote.import(arr)

批次匯入便利貼資料(疊加)。

BpNote.disable()

暫停雙擊建立功能。

BpNote.enable()

恢復雙擊建立功能。

向後相容window.StickyWallBpNote 的別名,原本使用 sticky-wall.js 的程式碼無需修改。
資料庫存取範例
// 存入資料庫
const data = BpNote.export();
fetch('/api/save-notes', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data),
});

// 從資料庫還原
fetch('/api/load-notes')
  .then(r => r.json())
  .then(arr => BpNote.import(arr));

連動流程範例

以雅思口說教學場景為例,展示五個元件如何透過一個事件名稱串接完整的學習流程。

bp-slide 第一頁 → 第二頁(link="slide:intro" 發出) WordFlip 解鎖(unlock-on="slide:intro"bp-slide 第三頁(unlock-on="quiz:pass" 鎖住) ↓ 需答題才能翻頁 ir-challenge 答對 → BPTools.emit("quiz:pass") ↓ 同時觸發 bp-slide 第三頁解鎖 DualCell 高分範例遮罩移除(on-link="quiz:pass" InfoRegion 正確回饋滑入(unlock-on="quiz:pass"bp-slide 按「完成」→ BPTools.emit("slider:done") bp-notice 完課公告浮現(unlock-on="slider:done" JS 顯示完課徽章 JS 打 API 記錄進度
完整連動 HTML 骨架
<!-- 投影片:答題通過才進第三頁,完成發事件 -->
<bp-slide theme="orange" link="slider:done" auto-show-part-buttons="true">
  <div slide part="1" link="slide:intro">第一頁</div>
  <div slide part="1" unlock-on="quiz:pass">第二頁(鎖住)</div>
</bp-slide>

<!-- 答題:答對後解鎖所有訂閱 quiz:pass 的元件 -->
<ir-challenge answer="Cookie" link="quiz:pass" color="lavender"></ir-challenge>

<!-- 答案表格:答對解鎖 -->
<div data-dual-cell data-cols="2" data-theme="lavender">
  <div data-col>問題</div>
  <div data-col data-on-link="quiz:pass" data-overlay-1-text="答對後解鎖">答案</div>
</div>

<!-- 完課公告:完成投影片才顯示,5 秒引線 -->
<bp-notice color="special" icon="stars"
  unlock-on="slider:done" auto-hide="5000" dismissible>
  🎓 課程完成!
</bp-notice>

所有公開 API

patch 載入後全域可用的所有物件與方法。

完整 API 一覽
/* BPTools 匯流排 */
BPTools.on(ev, fn)        // 訂閱
BPTools.emit(ev, data)   // 發布
BPTools.off(ev, fn)       // 取消訂閱
BPTools._fired             // Set,已發過的事件歷史

/* BrandColors */
BrandColors.applyTheme('dark' | 'light')
BrandColors.get(colorName)
BrandColors.resolve(val)
BrandColors.toCSSVars(themeName)

/* InfoRegion */
InfoRegion.activate(id)
InfoRegion.resetAll()

/* BpNote(便利貼)*/
BpNote.save()
BpNote.clearAll()
BpNote.export()          // → JSON 陣列
BpNote.import(arr)
BpNote.enable() / disable()
StickyWall                 // BpNote 的別名(向後相容)

相容性與注意事項

項目說明
瀏覽器支援Chrome 80+、Edge 80+、Firefox 75+、Safari 14+。使用 Custom Elements v1、CSS 變數、ResizeObserver。
相依檔案bp-tools.js 必須在 patch 之前載入。Bootstrap Icons CSS 為選填,但 icon 屬性需要它。
slider-show.js已整合進 patch,可以移除。<slider-show> 舊標籤須改為 <bp-slide>
sticky-wall.js已整合進 patch,可以移除。StickyWall 別名保留,舊程式碼不需修改。
word-flip 屬性展開內容用 data-content,不是 answer。patch 的 answer-src 功能不受影響。
BPTools 訂閱時機頁面 script 裡使用 BPTools 須用輪詢寫法,避免 BPTools is not defined 錯誤。
unlock-on + auto-hidebp-notice 的倒數從「公告出現後」才開始,不是頁面載入時。