react-av-timeline 2.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 ArSrNa(root@arsrna.cn)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -14,6 +14,8 @@
14
14
 
15
15
  # Breaking Changes
16
16
 
17
+ 3.0.0 新增 `TraceWaterfall` 瀑布时间线;现有 `Timeline` / `Vertical` 的 API 与行为不变。
18
+
17
19
  2.0以后,数据结构发生改变,需要重新更新代码
18
20
 
19
21
  同时之后项目使用Vibe Coding,实现功能请参考`.trae/commands/code.md`
@@ -35,12 +37,12 @@ bun i react-av-timeline
35
37
  首先引入组件,由于css未注入,需要手动引入css
36
38
 
37
39
  ````jsx
38
- import { Timeline } from 'react-av-timeline';
40
+ import Timeline from 'react-av-timeline';
39
41
  import 'react-av-timeline/dist/index.css';
40
42
  ````
41
43
 
42
44
  ````jsx
43
- <Component items={[{
45
+ <Timeline items={[{
44
46
  time:1,
45
47
  content:'这是第一秒展示的内容',
46
48
  style:{
@@ -85,6 +87,39 @@ import 'react-av-timeline/dist/index.css'
85
87
  }]} currentTime={currentTime} scale={10} totalTime={100} />
86
88
  ````
87
89
 
90
+ ## 瀑布时间线(TraceWaterfall)
91
+
92
+ 用于音视频导播 / 剪辑:多轨、多片段、时间码、固定播放头、首行跟随。片段只有 `start` + `duration`,相同 `index` 即同一轨。
93
+
94
+ ````jsx
95
+ import { TraceWaterfall } from 'react-av-timeline';
96
+ import 'react-av-timeline/dist/index.css';
97
+
98
+ const clips = [
99
+ { id: 'lyric-0', name: '船 摇啊摇', index: 0, start: 10.473, duration: 1.766 },
100
+ { id: 'lyric-1', name: '摇到陌生的远方摇断了船桨', index: 0, start: 12.239, duration: 3.848 }
101
+ ]
102
+
103
+ <TraceWaterfall
104
+ clips={clips}
105
+ tracks={[{ index: 0, name: '歌词 1' }]}
106
+ currentTime={currentTime}
107
+ onCurrentTimeChange={setCurrentTime}
108
+ formatTime={{ type: 'clock', millisecond: true }}
109
+ fixedPlayhead
110
+ playheadPosition={0.5}
111
+ height={420}
112
+ />
113
+ ````
114
+
115
+ 要点:
116
+
117
+ - `viewStart` / `viewDuration` 定义可 seek 的视图范围;晚于 `viewEnd` 的片段仍会渲染,但会压暗,`currentTime` 不会越过 `viewEnd`。
118
+ - `fixedPlayhead` 会在视图前后补留白,游标从起点到终点都停在 `playheadPosition`。
119
+ - `followMode` 非 `off` 时强制 `sequence` 平铺;支持 `scroll` 上滚与 `hide` 直接消失。
120
+ - `layout` 默认 `lanes`;`zoom` 受控、`defaultZoom` 非受控。
121
+ - 文案通过 `labels` 覆盖;颜色通过 CSS 变量 `--wf-*` 覆盖。
122
+
88
123
  # 应用
89
124
 
90
125
  可以结合audio或video,通过监听timeupdate事件,实时更新currentTime,实现同步播放
@@ -147,6 +182,66 @@ interface itemsType {
147
182
  }
148
183
  ````
149
184
 
185
+ ## 瀑布时间线 (TraceWaterfall)
186
+
187
+ ````typescript
188
+ interface TimelineClip {
189
+ id: string
190
+ name?: string
191
+ index: number
192
+ start: number
193
+ duration: number
194
+ label?: ReactNode
195
+ color?: string
196
+ style?: CSSProperties
197
+ meta?: Record<string, unknown>
198
+ }
199
+
200
+ interface TraceWaterfallProps {
201
+ clips: TimelineClip[]
202
+ tracks?: { index: number, name?: string }[]
203
+ layout?: 'lanes' | 'sequence'
204
+ unit?: 'ms' | 's'
205
+ viewStart?: number
206
+ viewDuration?: number
207
+ currentTime?: number
208
+ onCurrentTimeChange?: (time: number) => void
209
+ formatTime?: TimeFormat | ((value: number) => string)
210
+ formatDuration?: (value: number) => string
211
+ fixedPlayhead?: boolean
212
+ playheadPosition?: number
213
+ followMode?: 'off' | 'scroll' | 'hide'
214
+ zoom?: number
215
+ defaultZoom?: number
216
+ onZoomChange?: (zoom: number) => void
217
+ labelWidth?: number
218
+ height?: CSSProperties['height']
219
+ minTickGap?: number
220
+ showTime?: boolean
221
+ showDetail?: boolean
222
+ showSearch?: boolean
223
+ showZoom?: boolean
224
+ emptyText?: string
225
+ labels?: Partial<TimelineLabels>
226
+ onClipClick?: (clip: TimelineClip) => void
227
+ className?: string
228
+ style?: CSSProperties
229
+ }
230
+ ````
231
+
232
+ ````typescript
233
+ type TimeFormat =
234
+ | { type: 'number' }
235
+ | { type: 'clock', millisecond?: boolean, offset?: number }
236
+ | { type: 'smpte', fps: number, offset?: number }
237
+ ````
238
+
239
+ - `formatTime` 用于绝对时间(应用 `offset`),`formatDuration` 用于时长(不应用 `offset`)。
240
+ - `currentTime` 受控;点击标尺触发 `onCurrentTimeChange`,点击片段只打开详情。
241
+ - `showSearch` / `showZoom` 可关闭内置搜索框与缩放按钮;关闭后可用受控 `zoom` 在外部控制缩放。
242
+ - `labels` 可覆盖 `search` / `noMatch` / `empty` / `current` / `total` / `track` / `start` / `end` / `duration` / `close` / `zoom` / `lanesColumn` / `sequenceColumn`。
243
+ - 纯函数 `getActiveAt`、`getTopActiveAt`、`getClipEnd`、`resolveRange`、`buildRows`、`buildTicks`、`formatTime`、`formatDuration` 也可从包中导入。
244
+
150
245
  ## 重叠规则 (Level)
151
246
  - **水平模式**:当多个 items 时间重叠时,系统会自动将它们纵向排列。你可以通过 `level` 属性手动强制指定某个 item 所在的行数(Level 0 为最上方)。
152
247
  - **垂直模式**:垂直时间轴**不受 `level` 影响**。它始终保持每行一个 item,并根据时间进度实现自动消失和后续元素的向上补位。
@@ -0,0 +1,2 @@
1
+ import type { TraceWaterfallProps } from './waterfall';
2
+ export default function TraceWaterfall({ clips, tracks, layout, unit, viewStart, viewDuration, currentTime, onCurrentTimeChange, formatTime: formatTimeProp, formatDuration: formatDurationProp, fixedPlayhead, playheadPosition, followMode, zoom, defaultZoom, onZoomChange, labelWidth, height, minTickGap, showTime, showDetail, showSearch, showZoom, emptyText, labels, onClipClick, className, style: rootStyle }: TraceWaterfallProps): import("react/jsx-runtime").JSX.Element;
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- ._timeline_x6ik7_1{position:relative;overflow:clip;width:100%}._timeline-items_x6ik7_6{position:relative;transform:translate(var(--translate));transition:all .13s;width:100%}._timeline-item_x6ik7_6{position:absolute;display:flex;align-items:center;justify-content:center;color:#000;background:var(--bg);flex-shrink:0;border-right:2px solid white;box-sizing:border-box;white-space:nowrap;height:40px;top:0}._timeline-indicator_x6ik7_26{z-index:1;position:absolute;top:0;left:50%;background-color:#000;transform:translate(-50%);width:3px;opacity:.4;height:100%}._timeline_kp8su_1{overflow:clip;position:relative;height:200px}._timeline-items_kp8su_6{position:relative;transform:translate(var(--translate));transition:all .13s;height:100%;width:100%}._timeline-item_kp8su_6{background:var(--bg);position:absolute;display:flex;justify-content:center;align-items:center;white-space:nowrap;height:35px;color:#fff;padding:0 10px;box-sizing:border-box}._timeline-indicator_kp8su_25{z-index:1;position:absolute;top:0;background-color:#000;width:3px;opacity:.4;height:100%}
1
+ ._timeline_x6ik7_1{position:relative;overflow:clip;width:100%}._timeline-items_x6ik7_6{position:relative;transform:translate(var(--translate));transition:all .13s;width:100%}._timeline-item_x6ik7_6{position:absolute;display:flex;align-items:center;justify-content:center;color:#000;background:var(--bg);flex-shrink:0;border-right:2px solid white;box-sizing:border-box;white-space:nowrap;height:40px;top:0}._timeline-indicator_x6ik7_26{z-index:1;position:absolute;top:0;left:50%;background-color:#000;transform:translate(-50%);width:3px;opacity:.4;height:100%}._timeline_kp8su_1{overflow:clip;position:relative;height:200px}._timeline-items_kp8su_6{position:relative;transform:translate(var(--translate));transition:all .13s;height:100%;width:100%}._timeline-item_kp8su_6{background:var(--bg);position:absolute;display:flex;justify-content:center;align-items:center;white-space:nowrap;height:35px;color:#fff;padding:0 10px;box-sizing:border-box}._timeline-indicator_kp8su_25{z-index:1;position:absolute;top:0;background-color:#000;width:3px;opacity:.4;height:100%}._waterfall_113bd_1,._popover_113bd_2{--wf-bg: #ffffff;--wf-sub: #f7f8fa;--wf-hover: #f1f3f8;--wf-border: #e6e8ee;--wf-text: #1f2430;--wf-muted: #7a8194;--wf-grid: rgba(31, 36, 48, .07);--wf-clip-1: #6366f1;--wf-clip-2: #0d9488;--wf-clip-3: #d97706;--wf-clip-4: #e11d48;--wf-clip-5: #0284c7;--wf-clip-6: #7c3aed;--wf-active: #6366f1;--wf-cursor: #ef4444;--wf-dim: rgba(31, 36, 48, .28);--wf-row-h: 32px;--wf-ruler-h: 30px}@media(prefers-color-scheme:dark){._waterfall_113bd_1,._popover_113bd_2{--wf-bg: #14161c;--wf-sub: #191c24;--wf-hover: #1f2330;--wf-border: #2a2f3a;--wf-text: #e7eaf3;--wf-muted: #8b93a7;--wf-grid: rgba(255, 255, 255, .07);--wf-active: #818cf8;--wf-dim: rgba(0, 0, 0, .42)}}._waterfall_113bd_1{position:relative;display:flex;flex-direction:column;width:100%;overflow:hidden;border:1px solid var(--wf-border);border-radius:12px;background:var(--wf-bg);color:var(--wf-text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,PingFang SC,sans-serif;font-size:12px;line-height:1.4;box-sizing:border-box}._waterfall_113bd_1 *,._waterfall_113bd_1 *:before,._waterfall_113bd_1 *:after{box-sizing:border-box}._waterfall_113bd_1 button{font:inherit;color:inherit}._timebar_113bd_62{display:flex;align-items:center;flex-wrap:wrap;gap:4px 18px;padding:7px 12px;border-bottom:1px solid var(--wf-border);background:var(--wf-sub);font-variant-numeric:tabular-nums}._timeItem_113bd_73{color:var(--wf-muted);white-space:nowrap}._timeItem_113bd_73 b{color:var(--wf-text);font-weight:600;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-variant-numeric:tabular-nums}._toolbar_113bd_84{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px;padding:8px;border-bottom:1px solid var(--wf-border)}._search_113bd_94{position:relative;display:flex;align-items:center;flex:1 1 160px;min-width:140px;max-width:280px;margin-left:auto}._search_113bd_94 input{width:100%;height:26px;padding:0 40px 0 8px;border:1px solid var(--wf-border);border-radius:6px;background:var(--wf-sub);color:var(--wf-text);font:inherit;outline:none}._search_113bd_94 input:focus{border-color:var(--wf-active)}._search_113bd_94 input::placeholder{color:var(--wf-muted)}._search_113bd_94 kbd{position:absolute;right:6px;padding:1px 4px;border:1px solid var(--wf-border);border-radius:4px;color:var(--wf-muted);font-family:inherit;font-size:10px;pointer-events:none}._zoom_113bd_132{display:flex;gap:2px;padding:2px;border:1px solid var(--wf-border);border-radius:6px;background:var(--wf-sub)}._zoomItem_113bd_141{min-width:34px;padding:2px 6px;border:0;border-radius:4px;background:transparent;color:var(--wf-muted);font-variant-numeric:tabular-nums;cursor:pointer}._zoomItem_113bd_141:hover{color:var(--wf-text)}._zoomActive_113bd_155{background:var(--wf-bg);color:var(--wf-text);box-shadow:0 1px 2px #0000001f}._scroller_113bd_161{position:relative;overflow:auto;overscroll-behavior:contain;max-height:100%}._canvas_113bd_168{position:relative;min-width:100%}._ruler_113bd_173{position:sticky;top:0;z-index:5;display:flex;height:var(--wf-ruler-h);background:var(--wf-bg);border-bottom:1px solid var(--wf-border)}._rulerLabel_113bd_183{position:sticky;left:0;z-index:2;display:flex;align-items:center;flex:0 0 auto;padding:0 8px;background:var(--wf-bg);border-right:1px solid var(--wf-border);color:var(--wf-muted);font-size:11px;white-space:nowrap}._rulerTrack_113bd_198{position:relative;flex:0 0 auto;height:100%}._rulerDim_113bd_204{position:absolute;top:0;bottom:0;z-index:0;background:var(--wf-dim);pointer-events:none}._tick_113bd_213{position:absolute;top:0;z-index:1;display:flex;align-items:center;height:100%;padding-left:5px;border-left:1px solid var(--wf-grid);color:var(--wf-muted);font-size:11px;font-variant-numeric:tabular-nums;white-space:nowrap}._grid_113bd_228{position:absolute;top:var(--wf-ruler-h);bottom:0;z-index:0;pointer-events:none;background-image:linear-gradient(to right,var(--wf-grid) 0 1px,transparent 1px);background-repeat:repeat}._rows_113bd_238{position:relative}._row_113bd_238{display:flex;height:var(--wf-row-h)}._row_113bd_238:hover ._label_113bd_246,._row_113bd_238:hover ._track_113bd_249{background:var(--wf-hover)}._label_113bd_246{position:sticky;left:0;z-index:4;display:flex;align-items:center;gap:5px;flex:0 0 auto;padding-right:8px;padding-left:8px;background:var(--wf-bg);border-right:1px solid var(--wf-border);overflow:hidden;cursor:pointer}._dot_113bd_269{flex:0 0 auto;width:7px;height:7px;border-radius:50%}._name_113bd_276{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._tag_113bd_284{flex:0 0 auto;max-width:84px;padding:0 5px;border-radius:4px;background:var(--wf-sub);color:var(--wf-muted);font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._duration_113bd_297{flex:0 0 auto;color:var(--wf-muted);font-size:11px;font-variant-numeric:tabular-nums;white-space:nowrap}._mark_113bd_305{padding:0;border-radius:2px;background:#f59e0b52;color:inherit}._track_113bd_249{position:relative;flex:0 0 auto;height:100%}._bar_113bd_318{position:absolute;top:50%;z-index:1;height:14px;min-width:2px;padding:0 5px;border:0;border-radius:4px;transform:translateY(-50%);color:#fff;font-size:10px;text-align:left;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}._bar_113bd_318:hover{filter:brightness(1.08)}._bar_113bd_318:focus-visible{outline:2px solid var(--wf-text);outline-offset:1px}._barActive_113bd_344{outline:2px solid var(--wf-active);outline-offset:1px}._barText_113bd_349{pointer-events:none}._idTag_113bd_353{padding:0 3px;border-radius:3px;background:#ffffff2e;font-size:10px}._dim_113bd_360{position:absolute;top:var(--wf-ruler-h);bottom:0;z-index:2;background:var(--wf-dim);pointer-events:none}._cursor_113bd_369{position:absolute;top:0;bottom:0;z-index:3;width:1px;background:var(--wf-cursor);pointer-events:none}._cursor_113bd_369:before{content:"";position:absolute;top:calc(var(--wf-ruler-h) - 5px);left:-3px;border-top:5px solid var(--wf-cursor);border-left:3.5px solid transparent;border-right:3.5px solid transparent}._empty_113bd_388{display:flex;align-items:center;justify-content:center;height:140px;color:var(--wf-muted)}._popover_113bd_2{position:fixed;z-index:9999;width:300px;max-width:calc(100vw - 24px);max-height:60vh;padding:10px 12px;border:1px solid var(--wf-border);border-radius:10px;background:var(--wf-bg);color:var(--wf-text);box-shadow:0 12px 32px #0f141e2e;overflow:auto;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,PingFang SC,sans-serif;font-size:12px;line-height:1.5;box-sizing:border-box}._popoverHead_113bd_415{display:flex;align-items:center;gap:6px;margin-bottom:8px}._popoverHead_113bd_415 strong{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}._close_113bd_430{flex:0 0 auto;width:20px;height:20px;padding:0;border:0;border-radius:4px;background:transparent;color:var(--wf-muted);font-size:16px;line-height:1;cursor:pointer}._close_113bd_430:hover{background:var(--wf-hover);color:var(--wf-text)}._kv_113bd_448{display:grid;grid-template-columns:64px minmax(0,1fr);gap:2px 8px;margin:0}._kv_113bd_448 dt{color:var(--wf-muted)}._kv_113bd_448 dd{margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._raw_113bd_464{margin:8px 0 0;padding:8px;border-radius:6px;background:var(--wf-sub);color:var(--wf-muted);font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:180px;overflow:auto}@media(max-width:720px){._search_113bd_94{flex:1 1 100%;max-width:none;margin-left:0}}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
- import Horizonal from "./Component";
1
+ import Horizontal from './Component';
2
2
  import Vertical from './Vertical';
3
+ import TraceWaterfall from './TraceWaterfall';
3
4
  import type { PropsType, itemsType } from './utils';
4
- export default Horizonal;
5
- export { Vertical, type PropsType, type itemsType };
5
+ import { buildRows, buildTicks, formatDuration, formatTime, getActiveAt, getClipEnd, getTopActiveAt, resolveRange } from './waterfall';
6
+ import type { TimelineClip, TimelineTrack, TimelineLabels, TimeFormat, TimelineRange, TimelineRow, TraceWaterfallProps } from './waterfall';
7
+ export default Horizontal;
8
+ export { Vertical, TraceWaterfall, buildRows, buildTicks, formatDuration, formatTime, getActiveAt, getClipEnd, getTopActiveAt, resolveRange, type PropsType, type itemsType, type TimelineClip, type TimelineTrack, type TimelineLabels, type TimeFormat, type TimelineRange, type TimelineRow, type TraceWaterfallProps };