UX Spec

Design Pattern: Tree View

Amy TangJul 6 2026
Open Spec Changes
add-tree-view-component
tree-view-navigation (archived 2026-04-15)
Prototype
TreeViewList-demo.html(VORTEX-1.0 demo)

Visual basis: vortex-1.0 — verify behavior / copy / structure, not pixel style.

Context

Why we're building this

Multiple platform features — Site Management, Floor Plan, Device lists, Custom Views, Access Control — render hierarchical data as a tree, but each built its own expand/collapse and selection behaviour, so the experience is inconsistent. The immediate trigger: Site Management moved from three levels to five (Site > Area × 4), and the existing fixed-depth tree implementations cannot support it.

This pattern defines one reusable, depth-agnostic tree view: node behaviour is driven by node kind, not depth; selection and expansion are independent; "expand all" stops at a configurable kind. It ships first in the Site Management sidebar, then replaces the Floor Plan tree, and is available to other modules (device picker, views) later.

Section 01

節點結構與狀態

一個通用樹狀列表元件,顯示巢狀階層資料。元件本身不認得「Site / Area / Camera」,只認得「節點」與「種類(kind)」——icon、空狀態文案、業務語意都由使用端透過資料與設定傳入。

下圖為 Tree View 的預設狀態(以 Sites 為例):

tv-01-anatomy
Tree View — 預設狀態(280px sidebar)

節點類型 Node types

類型 外觀
可展開(無 icon) chevron(16×16)+ label
可展開(有 icon) chevron(16×16)+ icon(20×20)+ label
葉節點(無 icon) 無箭頭,label 左側 12px padding 對齊
葉節點(有 icon) 無箭頭,icon(20×20)+ label,左側 12px padding
空提示行 Empty Hint Row Regular 400 文字,無 icon/chevron,disabled 不可點;文字由使用端依 kind 傳入(如「No cameras」)

節點狀態 Node states

狀態 外觀(VORTEX 1.0 token)
預設 文字 @color-text01、icon @color-icon03、透明背景
Hover 文字 @color-text01、背景 @color-surface02(內層容器圓角 8px)
選取 Active 文字 @color-text01、背景 @color-surface04(內層容器圓角 8px)
空容器 Empty Container 文字 @color-text20 / icon @color-icon29(變淡);可展開、可選取;hover / selected 時恢復一般色
停用 Disabled 文字 @color-text06 / icon @color-icon01,不可點擊

輔助線 Indent guides

狀態 外觀
預設 不顯示
Hover 該節點所屬最頂層群組底下所有展開層級出現微弱垂直線(@color-outline19
Active 選取節點展開後,直接子層出現較明顯垂直線(@color-outline04);收合時父層出現
Active + Hover 較明顯的線優先

尺寸 Dimensions

項目
Node height 40px
Node horizontal padding 8px
Node internal gap 4px
Chevron 16×16px
Node icon 20×20px
Indent per level 16px
Leaf 左側 padding 12px
選取內層容器圓角 8px
Level-1 群組間距 8px
Container padding 8px
輔助線寬度 1px

字體:節點文字 Segoe UI 16px Semibold(600) line-height 1;空提示行 Segoe UI 16px Regular(400) line-height 1(不用 italic)。

邊界 Boundaries

情境 行為
沒有任何資料 不顯示樹,由使用端處理 empty state
展開的節點沒有子項目 顯示空提示行(Empty Hint Row),文字由使用端依 kind 提供

Token gap:@color-icon29(空容器 icon 變淡)尚未加入 theme-color/index.less,以 OpenSpec add-tree-view-component 為準。

TODO / 未解問題

  • @color-icon29 token 尚未落地,需與前端確認補上。
Section 02

選取

規則 Behaviour

  • 點擊節點 label(或 icon)= 選取;一次只能選一個(single-select)。
  • 為了讓被選節點露出,選取只會**往上展開它的祖先(上層)**中被收合的節點;節點自己與往下的子節點展開狀態一律不動(詳見下方「不會做的事」)。
  • 若節點不在捲動可見區域,平滑捲動到可見位置。
  • 選取 emit 事件通知使用端(select / update:selectedId)。
  • 點擊 chevron 只切換展開/收合、不影響選取(見 Section 03)。

下面兩張圖分別為「選取態」與「選取 + hover 同時」(hover 為微暗背景、選取為微亮背景):

tv-selected
選取態 Selected
tv-selected-hover
選取 + Hover 同時

選取「不會」做的事 釐清邊界

選取只「往上」展開祖先讓節點可見,除此之外的展開/收合狀態都不碰:

  • 不改變被選取節點本身的展開/收合狀態(原本收合就維持收合)。
  • 不展開被選取節點的子節點
  • 不收合其他已展開的節點。

驗收條件 QA · Given-When-Then

  • Given 點擊某節點 label,Then 該節點成為唯一選取(前一個取消)。
  • Given 選取一個位於收合祖先下的節點,Then 自動展開其所有祖先使其可見。
  • Given 被選取節點不在可見區,Then 平滑捲動到可見位置。
  • Given 點擊 chevron,Then 只切換展開/收合、選取不變。

元件與 Token 決策

  • 選取態:文字 @color-text01、背景 @color-surface04(內層容器圓角 8px)。
  • Emits:select {nodeId, node}update:selectedId(支援 v-model)。
Section 03

展開與收合

點擊分離 Click separation

[chevron] [icon] [label] [counter]
 ↑ 展開/收合         ↑ 選取
  • chevron 區:只切換展開/收合,不影響選取。
  • label / icon / counter 區:只選取,不影響展開/收合。
  • 整列 hover:整列背景變化。

全部展開/全部收合

頂部節點計數列(「N Sites」)右側的單顆按鈕,即為「全部展開/全部收合」切換:

tv-expand-all-btn
節點計數列右側的全部展開/全部收合按鈕
控制 行為
展開按鈕 icon 全收合時顯示 chevrons-down,tooltip「Expand All」
收合按鈕 icon 有展開節點時顯示 chevrons-up,tooltip「Collapse All」
Expand ceiling 「全部展開」停在 expandCeilingKind 指定的種類(如展開到 Floorplan,Camera 維持收合)
初始展開 defaultExpandToKind 決定初次載入展開到哪一種類

驗收條件 QA · Given-When-Then

  • Given 點擊 chevron,Then 切換該節點展開/收合、選取不變。
  • Given 點「全部展開」,Then 展開到 expandCeilingKind 指定的種類為止。
  • Given 已有任一節點展開,Then 全部按鈕顯示為 Collapse All(chevrons-up)。

元件與 Token 決策

  • Props:expandedIds(v-model)、defaultExpandToKindexpandCeilingKind
  • Emits:toggle {nodeId, expanded}update:expandedIds
  • Methods:expandAll()(受 ceiling 限制)、collapseAll()
Section 04

搜尋

搜尋過濾、highlight、No Matches Found 由使用端處理,不是元件本身的責任。此處定義互動規則,供使用端實作出一致行為。

搜尋流程 Flow

  1. 使用者輸入 → 首次輸入時快照當前展開狀態
  2. 即時過濾(不分大小寫)。
  3. 自動展開命中節點的所有祖先。
  4. 命中文字以 highlight 顯示;無關分支隱藏。
tv-search
搜尋命中:祖先提供 context、命中結果標示(device picker modal 內的 tree)

過濾規則 Filter rules

節點 行為
命中 名稱含關鍵字(不分大小寫),正常可選
命中的祖先 顯示且可選(正常樣式、不灰化),提供階層 context
命中的子節點 預設不展開;父層也命中才展開
完全無關分支 隱藏

清除搜尋 Clear

操作 行為
點清除(X) 清除搜尋文字
展開狀態 恢復搜尋前的快照
選取狀態 搜尋前有選取則恢復;搜尋中有新選取則保留新選取
highlight 全部移除

狀態與邊界

情境 行為
無命中 No matches 於 tree 區顯示「No matches found」訊息,隱藏所有節點
disabled 節點被命中 顯示但維持 disabled、不可選
選取節點仍在結果中 維持該節點的選取態
選取節點被過濾掉 選取在邏輯上保留、但不可見;清除搜尋後恢復顯示
搜尋中選了新節點 清除後保留新選取
搜尋中外部觸發 reveal 先清除搜尋、恢復展開,再 reveal(見 Section 05)

驗收條件 QA · Given-When-Then

  • Given 輸入關鍵字,Then 只顯示命中節點及其祖先,其餘隱藏。
  • Given 命中節點,Then 自動展開其祖先,且命中文字 highlight。
  • Given 清除搜尋,Then 展開狀態恢復成搜尋前的快照。
  • Given disabled 節點被命中,Then 顯示但維持不可選。
  • Given 搜尋無任何命中,Then 顯示「No matches found」並隱藏所有節點。
  • Given 選取的節點被搜尋過濾掉,Then 選取邏輯上保留,清除搜尋後恢復可見。
Section 05

外部觸發選取

外部事件(例如點擊地圖上的 camera marker)可觸發 tree 選取特定節點。

流程 Flow

  1. 外部呼叫 revealAndSelect(nodeId)
  2. 若在搜尋中,先清除搜尋、恢復展開狀態
  3. 找到目標節點 → 展開所有祖先 → 選取 → 平滑捲動到可見區。

狀態與邊界

情境 行為
目標已可見且已選取 不做任何事
目標已可見但未選取 選取,必要時捲動
目標不存在 靜默失敗,不做任何事
搜尋中觸發 先清除搜尋、恢復展開,再 reveal

驗收條件 QA · Given-When-Then

  • Given 對一個收合的深層節點呼叫 revealAndSelect,Then 展開其祖先、選取、捲動到可見。
  • Given 目標節點不存在,Then 靜默失敗(無錯誤、畫面不變)。
  • Given 搜尋中觸發 reveal,Then 先清除搜尋並恢復展開,再執行 reveal。

元件與 Token 決策

  • Exposed method:revealAndSelect(nodeId)

TODO / 未解問題

  • 鍵盤導航(Arrow / Enter / Space)目前不實作,元件結構預留擴充空間(interaction-spec §5)。
Section 06

使用場景

同一個元件、不同資料與設定,撐起平台上數種樹狀情境。元件不綁定業務語意——icon 由使用端透過 node-icon slot 依 node.kind 傳入。

Icon 規則Site / Area 節點一律無 icon;icon 只用來區分不同「種類」的節點。因此:Site Management 只有 site/area → 完全無 icon;View 多了 Camera 種類 → Camera 有 icon;Floorplans 有 Floor plan + Camera 兩種 → 兩者各有 icon。

Site Management Sidebar 五層、無 icon

Site > Area × 4,純文字節點(無 icon)。這是 tree-view 的首發場景

View Sidebar 最多六層、Camera 有 icon

Site > Area × 4 > Camera——Camera 直接掛在最深的 Area 底下(無 Floor plan 層);Camera 由使用端依 node.kind 傳入 icon。

tv-view
View — 最多六層、Camera 有 icon

Floorplans Sidebar 最多七層、Floor plan / Camera 有 icon

Site > Area × 4 > Floor plan > Camera——比 View 多一層 Floor plan;Floor plan 與 Camera 由使用端依 node.kind 傳入對應 icon。

tv-floorplans
Floorplans — 最多七層、Floor plan / Camera 有 icon

狀態與邊界

情境 說明
層數不固定 節點行為由 kind 決定,不由深度決定;同一 kind 可出現在不同深度
未來取代 之後取代 Floor Plan 專案現有寫死層數的 tree 實作

驗收條件 QA · Given-When-Then

  • Given Site Management 資料(最多五層、無 icon),Then 正確渲染且無 icon。
  • Given View 資料(最多六層,Camera 有 icon),Then 依 node.kind 顯示 Camera icon。
  • Given Floorplans 資料(最多七層,Floor plan / Camera 有 icon),Then 依 node.kind 顯示對應 icon。