AssetPriceTimeline
Production:src/components/AssetPrediction/source/AssetPriceTimeline/AssetPriceTimeline.tsx
Installation
import { AssetPriceTimeline } from "@/components/product/asset-price-timeline/asset-price-timeline";Overview
Trending 卡裡嵌的那張圖。一個資產的價格歷史,以及 today 右邊「開著的 market 認為它會走去哪」。 它不是一張走勢圖,是一排入口。
五個機制
- 入口歸入口 —— hover 只給唯讀的 chip, 什麼都不會導航。真的要跳走,得先開 preview、再點標題。
- hit area 是時間帶不是點 —— 每檔 market 擁有未來區的一段,切在相鄰兩檔的中點,整條 band 全高可點。 叫人瞄準一個 7px 的記號不是設計。
- 導航前先揭露 —— 點 band 先開 preview(market 名 / 價格 / 量 / traders), 離開頁面是第二個、刻意的動作。
- 兩半講不同的語言 —— 左半是資產自己的價格,連續的,所以不畫任何常駐記號, hover 到哪讀哪:實心圓點落在該日收盤上,chip 寫價格與日期。 右半是一排入口,本來就離散,所以維持空心記號+時間帶+虛線。
- 同時只有一個面板 —— 兩張 trending 卡並排,跨卡互斥(見
open-panel-store)。
兩個尺寸,一個組件(2026-08-18 裁決)
size="card"(預設)是 trending 卡裡的圖;size="hero" 是資產 dashboard 的 markets timeline。 在此之前 dashboard 那張是另一份 652 行的實作, 比例、軸、記號都自成一套。現在 hero 與 card 只差四件事,而且都是尺寸真的買得起的東西: plot 高 300、多一排圖例、歷史線下的漸層、today 虛線標Now。 幾何、記號、hover、顏色、軸一律是卡片版的,不變。
range(1M / 3M / 6M / 1Y)同時併了進來 —— dashboard 工具列那組按鈕直接傳進來,卡片版不傳、固定 3M, 所以卡片的行為完全沒動。
歷史那半邊為什麼沒有記號(2026-08-18 裁決)
2026-08-17 曾把已結算的 market 畫成實心圓放在歷史線上, 2026-08-18 由 user 撤回。實線那半是資產自己的價格, 線上每一點都是一個真實收盤;把結算記號疊上去等於在同一個位置畫第二種量 —— monthly 結算在該月均價, 所以那顆點看起來長在線上、其實不在線上。
互動上的代價更直接:歷史半邊只有四個點有反應,其餘 90% 是死的, 而讀者對一條價格線的預期是「滑到哪讀到哪」。 現在整個歷史半邊是一塊 hit area, 游標的 x 找最近的資料點,chip 寫 $65,224 · Jun 14 —— 只有日期與價格,沒有任何 market 語彙,因為這半邊沒有東西可以交易。
這件事讓本組件與 AssetDashboardTimeline 分家: 那張圖畫的是 market 隨時間的歷程,已結算的 market 是它的主題, 所以它保留空心/實心的時態記號。這張圖畫的是價格,所以不保留。
兩件從原型原封搬過來的判斷
- awaiting liquidity 是常態不是邊緣案例。market 開著、腿都在,但沒人成交也沒掛單 → 它沒有市場預期價。 2026-07-28 裁決:一律不畫、不進清單、不算進數量。畫一個 「有東西但讀不出數字」的記號,對掃視沒有幫助。
- x 軸是分段線性,today 固定落在寬度 40%。 未結算的 market 是可交易的東西,右邊 60% 是它們的;歷史只是脈絡。 兩側每天佔的 px 不同 —— 這張圖不是拿來量斜率的。 分段映射在資料層做完,圖表本身只看到一條 0–1 的線性軸。
顏色
線與記號走 brand,跟 sparkline 一致 —— 這是同一個資產的價格,價格在這裡沒有極性。 grid、today 線、band 覆色走中性的 --color-data-*。 全畫面唯一帶正負號的只有 market preview 裡的「vs spot」,那是真的有漲跌讀法的數字。 歷史 chip 不給任何漲跌色 —— 它報的是那天的價格本身,不是相對誰的位移。
Interactive Demo
換資產 —— market 數量只決定 weekly 有幾檔被定價(min(4, max(1, round(markets / 8))))
試:hover today 左邊任一處 → 實心圓點跟著游標落在該日收盤上、 chip 寫「價格 · 日期」,滑動時連續更新;hover today 右邊任一段時間帶 → 虛線落在該記號上 + chip 寫出是哪一檔,點下去開 preview。 Escape 或點空白處關閉。
導航 stub:點 preview 裡的 market 標題後顯示在這裡。
跨卡互斥 —— 兩張並排,同時只允許一個面板
在左卡開一個 preview,再去右卡開任何東西 —— 左邊會自己收掉。 規則住在 module store,不是 context,所以 timeline 放在頁面任何地方都遵守, 不需要記得包 provider。
Static State Matrix
尺寸 —— card(預設)/ hero(dashboard 用的放大版)
Range —— 1M / 3M / 6M / 1Y(hero 才有控制項,組件兩個尺寸都吃)
面板 —— 收合 / 開啟(開啟態由 store 常駐驅動,非截圖模擬)
Band 三階 —— rest / hover / 開啟中
band 的三階本身是 hover 態,靜態抓不到,所以這裡以同樣的覆色比例平鋪呈現。 覆色是 brand 的極低不透明度,不是另一個 token —— band 是「這一段屬於那條線」的提示, 不是獨立的資料墨水。
記號 —— 未來=空心(含開啟態)/ 歷史=游標實心點
分的是時態不是週期(2026-08-17 裁決): 空心的圈說「這裡有人在報價,但還沒發生」,實心的說「這件事已經發生了」。 weekly 與 monthly 共用形狀,是哪一種由 chip 用文字講。 兩者同色 —— 都是同一個資產的價格,換色會讓人以為是兩種東西。 實心刻意比空心小 0.4px:同半徑下實心圓看起來比圈重,這樣兩者才在同一個視覺級距上; 開啟態則是空心加粗邊、實心放大,兩邊給的是同一級的變化量。
定價數量 —— 4 檔 / 9 檔(weekly + monthly)/ 1 檔 / 0 檔
量級 —— 價格格式三個分支都要對
容器寬度 —— 280 / 420 / 620
Implementation Notes
來源:../opinion-labs-archive/prototypes/assets-forecasting/all-assets-5s-test-b.html 的 TL IIFE(約 383 行手刻 createElementNS)。
為什麼是 recharts
照 production 自己那張帶註記的折線圖的架構做的 —— MultiTopicChart/MultiLineChart(OpinionLabs/frontend):ComposedChart 負責尺度,所有有表達力的東西都是 custom render prop。production 自己寫了 EndPoint 這個 SVG dot、 疊 TradeMarkersSection、hover 走外部 store 而不是圖表 state。 原型那 383 行正好塌進這個形狀:庫留住尺度,記號留給我們。
兩個記號沒有走庫,理由
- hover chip 是唯讀的浮動提示(
pointer-events: none), 不是有 trigger 的 tooltip。 - market preview 錨在計算出來的圖表座標上, 而 @olab
Popover是對著真的 DOM trigger 定位的。 「釘在 SVG 內任意一點的面板」沒有對應的 @olab 元件,所以用 token 手刻。 系列清單有真的按鈕可以掛,就走 @olabPopover。
缺口
- 歷史 chip 的日期與月份軸標都是從
TODAY_DATE(寫死 2026-07-27)往前推的天數, 不是真的時間。接真資料時整條 series 應該自帶時間戳。 - 歷史 hover 取的是最近的資料點,不是插值, 所以密度低的 series(sparkline 只有幾十點)在兩點之間會跳。 真資料是日 K 之後這件事自然消失。
AssetDashboardTimeline仍保留空心/實心的時態記號, 本組件 2026-08-18 起不再有實心常駐記號 —— 兩張圖的文法刻意分家: 那張畫 market 的歷程,這張畫價格。若日後要重新統一,要先決定哪一邊改。- weekly 的
priced目前用 market 數當流動性代理 (min(4, max(1, round(markets / 8))), monthly 不受它管、一律畫)。接真資料時換成「該 parent 的腿是否有成交 / 掛單」。 - 未來偏移的幅度由該資產自己 series 的實現波動推出來 (原型讀的是一個
adr欄位,AssetRow沒有)。意圖相同,不新增欄位。 - 鍵盤只支援 Escape 關閉。band 是 SVG
rect, 目前不可 focus —— 要做完整鍵盤走訪需要另一層可 tab 的入口,尚未設計。