@keplar-404/react-timeline-editor 1.0.6
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/dist/components/control_area/index.d.ts +0 -0
- package/dist/components/cursor/cursor.d.ts +20 -0
- package/dist/components/cut-overlay/CutOverlay.d.ts +202 -0
- package/dist/components/edit_area/cross_row_drag.d.ts +50 -0
- package/dist/components/edit_area/drag_lines.d.ts +11 -0
- package/dist/components/edit_area/drag_preview.d.ts +14 -0
- package/dist/components/edit_area/drag_utils.d.ts +39 -0
- package/dist/components/edit_area/edit_action.d.ts +19 -0
- package/dist/components/edit_area/edit_area.d.ts +56 -0
- package/dist/components/edit_area/edit_row.d.ts +27 -0
- package/dist/components/edit_area/hooks/use_drag_line.d.ts +33 -0
- package/dist/components/edit_area/insertion_line.d.ts +12 -0
- package/dist/components/loop-zone/LoopZoneOverlay.d.ts +243 -0
- package/dist/components/row_rnd/hooks/useAutoScroll.d.ts +7 -0
- package/dist/components/row_rnd/interactable.d.ts +14 -0
- package/dist/components/row_rnd/row_rnd.d.ts +3 -0
- package/dist/components/row_rnd/row_rnd_interface.d.ts +47 -0
- package/dist/components/time_area/time_area.d.ts +17 -0
- package/dist/components/timeline.d.ts +3 -0
- package/dist/components/transport/TransportBar.d.ts +132 -0
- package/dist/components/transport/useTimelinePlayer.d.ts +164 -0
- package/dist/index.cjs.js +13 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.es.js +10102 -0
- package/dist/index.umd.js +13 -0
- package/dist/interface/common_prop.d.ts +12 -0
- package/dist/interface/const.d.ts +28 -0
- package/dist/interface/timeline.d.ts +342 -0
- package/dist/react-timeline-editor.css +1 -0
- package/dist/utils/check_props.d.ts +2 -0
- package/dist/utils/deal_class_prefix.d.ts +1 -0
- package/dist/utils/deal_data.d.ts +58 -0
- package/dist/utils/logger.d.ts +132 -0
- package/package.json +70 -0
- package/src/components/control_area/index.tsx +1 -0
- package/src/components/cursor/cursor.css +26 -0
- package/src/components/cursor/cursor.tsx +105 -0
- package/src/components/cut-overlay/CutOverlay.css +68 -0
- package/src/components/cut-overlay/CutOverlay.tsx +491 -0
- package/src/components/edit_area/cross_row_drag.tsx +174 -0
- package/src/components/edit_area/drag_lines.css +13 -0
- package/src/components/edit_area/drag_lines.tsx +31 -0
- package/src/components/edit_area/drag_preview.tsx +50 -0
- package/src/components/edit_area/drag_utils.ts +77 -0
- package/src/components/edit_area/edit_action.css +56 -0
- package/src/components/edit_area/edit_action.tsx +362 -0
- package/src/components/edit_area/edit_area.css +24 -0
- package/src/components/edit_area/edit_area.tsx +606 -0
- package/src/components/edit_area/edit_row.css +78 -0
- package/src/components/edit_area/edit_row.tsx +128 -0
- package/src/components/edit_area/hooks/use_drag_line.ts +93 -0
- package/src/components/edit_area/insertion_line.tsx +39 -0
- package/src/components/loop-zone/LoopZoneOverlay.css +65 -0
- package/src/components/loop-zone/LoopZoneOverlay.tsx +461 -0
- package/src/components/row_rnd/hooks/useAutoScroll.ts +81 -0
- package/src/components/row_rnd/interactable.tsx +55 -0
- package/src/components/row_rnd/row_rnd.tsx +365 -0
- package/src/components/row_rnd/row_rnd_interface.ts +59 -0
- package/src/components/time_area/time_area.css +35 -0
- package/src/components/time_area/time_area.tsx +93 -0
- package/src/components/timeline.css +12 -0
- package/src/components/timeline.tsx +227 -0
- package/src/components/transport/TransportBar.css +171 -0
- package/src/components/transport/TransportBar.tsx +322 -0
- package/src/components/transport/useTimelinePlayer.ts +319 -0
- package/src/index.tsx +17 -0
- package/src/interface/common_prop.ts +13 -0
- package/src/interface/const.ts +32 -0
- package/src/interface/timeline.ts +329 -0
- package/src/utils/check_props.ts +77 -0
- package/src/utils/deal_class_prefix.ts +6 -0
- package/src/utils/deal_data.ts +159 -0
- package/src/utils/logger.ts +239 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { Emitter, EventTypes, ITimelineEngine } from '@keplar-404/timeline-engine';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { OnScrollParams } from 'react-virtualized';
|
|
4
|
+
import { TimelineAction, TimelineRow } from '@keplar-404/timeline-engine';
|
|
5
|
+
import { TimelineEffect } from '@keplar-404/timeline-engine';
|
|
6
|
+
|
|
7
|
+
export interface EditData {
|
|
8
|
+
/**
|
|
9
|
+
* @description Timeline editing data
|
|
10
|
+
*/
|
|
11
|
+
editorData: TimelineRow[];
|
|
12
|
+
/**
|
|
13
|
+
* @description Timeline action effect map
|
|
14
|
+
*/
|
|
15
|
+
effects: Record<string, TimelineEffect>;
|
|
16
|
+
/**
|
|
17
|
+
* @description Scaling factor for individual scale marks (>0)
|
|
18
|
+
* @default 1
|
|
19
|
+
*/
|
|
20
|
+
scale?: number;
|
|
21
|
+
/**
|
|
22
|
+
* @description Minimum number of scale marks (>=1)
|
|
23
|
+
* @default 20
|
|
24
|
+
*/
|
|
25
|
+
minScaleCount?: number;
|
|
26
|
+
/**
|
|
27
|
+
* @description Maximum number of scale marks (>=minScaleCount)
|
|
28
|
+
* @default Infinity
|
|
29
|
+
*/
|
|
30
|
+
maxScaleCount?: number;
|
|
31
|
+
/**
|
|
32
|
+
* @description Number of subdivision units for a single scale mark (integer >0)
|
|
33
|
+
* @default 10
|
|
34
|
+
*/
|
|
35
|
+
scaleSplitCount?: number;
|
|
36
|
+
/**
|
|
37
|
+
* @description Display width of a single scale mark (>0, unit: px)
|
|
38
|
+
* @default 160
|
|
39
|
+
*/
|
|
40
|
+
scaleWidth?: number;
|
|
41
|
+
/**
|
|
42
|
+
* @description Distance from the start of the scale to the left edge (>=0, unit: px)
|
|
43
|
+
* @default 20
|
|
44
|
+
*/
|
|
45
|
+
startLeft?: number;
|
|
46
|
+
/**
|
|
47
|
+
* @description Default height for each editing row (>0, unit: px)
|
|
48
|
+
* @default 32
|
|
49
|
+
*/
|
|
50
|
+
rowHeight?: number;
|
|
51
|
+
/**
|
|
52
|
+
* @description Whether to enable grid movement snapping
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
gridSnap?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* @description Enable snapping to drag auxiliary lines
|
|
58
|
+
* @default false
|
|
59
|
+
*/
|
|
60
|
+
dragLine?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* @description Whether to hide the cursor
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
hideCursor?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* @description Prevent dragging in all action areas
|
|
68
|
+
* @default false
|
|
69
|
+
*/
|
|
70
|
+
disableDrag?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* @description Prevent dragging of all rows
|
|
73
|
+
* @default false
|
|
74
|
+
*/
|
|
75
|
+
enableRowDrag?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* @description Enable dragging action blocks between different rows
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
enableCrossRowDrag?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* @description Show a ghost/preview element following the cursor during cross-row block drag.
|
|
83
|
+
* Set to `false` to disable the ghost entirely, or use `getGhostPreview` for a custom component.
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
enableGhostPreview?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* @description Custom render function for the drag ghost/preview element shown while
|
|
89
|
+
* dragging a block across rows. When provided, replaces the default blue glowing box.
|
|
90
|
+
*
|
|
91
|
+
* The function receives the action being dragged and its source row, so you can
|
|
92
|
+
* render a fully custom preview that matches your block's actual appearance.
|
|
93
|
+
*
|
|
94
|
+
* @param params - The action being dragged and the row it originated from
|
|
95
|
+
* @returns A React node to render inside the ghost container
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* <Timeline
|
|
100
|
+
* enableCrossRowDrag
|
|
101
|
+
* getGhostPreview={({ action, row }) => (
|
|
102
|
+
* <div style={{ background: '#1a3a5c', border: '2px solid #3b82f6', height: '100%', borderRadius: 4, padding: '0 8px', display: 'flex', alignItems: 'center' }}>
|
|
103
|
+
* <span style={{ color: '#3b82f6', fontSize: 12 }}>{action.id}</span>
|
|
104
|
+
* </div>
|
|
105
|
+
* )}
|
|
106
|
+
* />
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
getGhostPreview?: (params: { action: TimelineAction; row: TimelineRow }) => ReactNode;
|
|
110
|
+
/**
|
|
111
|
+
* @description Timeline engine; uses the built-in engine if not provided
|
|
112
|
+
*/
|
|
113
|
+
engine?: ITimelineEngine;
|
|
114
|
+
/**
|
|
115
|
+
* @description Custom action area rendering
|
|
116
|
+
*/
|
|
117
|
+
getActionRender?: (action: TimelineAction, row: TimelineRow) => ReactNode;
|
|
118
|
+
/**
|
|
119
|
+
* @description Custom scale rendering
|
|
120
|
+
*/
|
|
121
|
+
getScaleRender?: (scale: number) => ReactNode;
|
|
122
|
+
/**
|
|
123
|
+
* @description Callback when movement starts
|
|
124
|
+
*/
|
|
125
|
+
onActionMoveStart?: (params: { action: TimelineAction; row: TimelineRow }) => void;
|
|
126
|
+
/**
|
|
127
|
+
* @description Movement callback (return false to prevent movement)
|
|
128
|
+
*/
|
|
129
|
+
onActionMoving?: (params: { action: TimelineAction; row: TimelineRow; start: number; end: number }) => void | boolean;
|
|
130
|
+
/**
|
|
131
|
+
* @description Movement end callback (return false to prevent onChange from triggering)
|
|
132
|
+
*/
|
|
133
|
+
onActionMoveEnd?: (params: { action: TimelineAction; row: TimelineRow; start: number; end: number }) => void;
|
|
134
|
+
/**
|
|
135
|
+
* @description Callback when resizing starts
|
|
136
|
+
*/
|
|
137
|
+
onActionResizeStart?: (params: { action: TimelineAction; row: TimelineRow; dir: 'right' | 'left' }) => void;
|
|
138
|
+
/**
|
|
139
|
+
* @description Resizing callback (return false to prevent change)
|
|
140
|
+
*/
|
|
141
|
+
onActionResizing?: (params: { action: TimelineAction; row: TimelineRow; start: number; end: number; dir: 'right' | 'left' }) => void | boolean;
|
|
142
|
+
/**
|
|
143
|
+
* @description Callback when resizing ends (return false to prevent onChange from triggering)
|
|
144
|
+
*/
|
|
145
|
+
onActionResizeEnd?: (params: { action: TimelineAction; row: TimelineRow; start: number; end: number; dir: 'right' | 'left' }) => void;
|
|
146
|
+
/**
|
|
147
|
+
* @description Callback when a row is clicked
|
|
148
|
+
*/
|
|
149
|
+
onClickRow?: (
|
|
150
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
151
|
+
param: {
|
|
152
|
+
row: TimelineRow;
|
|
153
|
+
time: number;
|
|
154
|
+
},
|
|
155
|
+
) => void;
|
|
156
|
+
/**
|
|
157
|
+
* @description Callback when an action is clicked
|
|
158
|
+
*/
|
|
159
|
+
onClickAction?: (
|
|
160
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
161
|
+
param: {
|
|
162
|
+
action: TimelineAction;
|
|
163
|
+
row: TimelineRow;
|
|
164
|
+
time: number;
|
|
165
|
+
},
|
|
166
|
+
) => void;
|
|
167
|
+
/**
|
|
168
|
+
* @description Callback when an action is clicked (not executed when drag is triggered)
|
|
169
|
+
*/
|
|
170
|
+
onClickActionOnly?: (
|
|
171
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
172
|
+
param: {
|
|
173
|
+
action: TimelineAction;
|
|
174
|
+
row: TimelineRow;
|
|
175
|
+
time: number;
|
|
176
|
+
},
|
|
177
|
+
) => void;
|
|
178
|
+
/**
|
|
179
|
+
* @description Callback when a row is double-clicked
|
|
180
|
+
*/
|
|
181
|
+
onDoubleClickRow?: (
|
|
182
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
183
|
+
param: {
|
|
184
|
+
row: TimelineRow;
|
|
185
|
+
time: number;
|
|
186
|
+
},
|
|
187
|
+
) => void;
|
|
188
|
+
/**
|
|
189
|
+
* @description Callback when an action is double-clicked
|
|
190
|
+
*/
|
|
191
|
+
onDoubleClickAction?: (
|
|
192
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
193
|
+
param: {
|
|
194
|
+
action: TimelineAction;
|
|
195
|
+
row: TimelineRow;
|
|
196
|
+
time: number;
|
|
197
|
+
},
|
|
198
|
+
) => void;
|
|
199
|
+
/**
|
|
200
|
+
* @description Callback when a row is right-clicked
|
|
201
|
+
*/
|
|
202
|
+
onContextMenuRow?: (
|
|
203
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
204
|
+
param: {
|
|
205
|
+
row: TimelineRow;
|
|
206
|
+
time: number;
|
|
207
|
+
},
|
|
208
|
+
) => void;
|
|
209
|
+
/**
|
|
210
|
+
* @description Callback when an action is right-clicked
|
|
211
|
+
*/
|
|
212
|
+
onContextMenuAction?: (
|
|
213
|
+
e: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
214
|
+
param: {
|
|
215
|
+
action: TimelineAction;
|
|
216
|
+
row: TimelineRow;
|
|
217
|
+
time: number;
|
|
218
|
+
},
|
|
219
|
+
) => void;
|
|
220
|
+
/**
|
|
221
|
+
* @description Get a list of action IDs for auxiliary lines, calculated at move/resize start; defaults to all except the current moving action
|
|
222
|
+
*/
|
|
223
|
+
getAssistDragLineActionIds?: (params: { action: TimelineAction; editorData: TimelineRow[]; row: TimelineRow }) => string[];
|
|
224
|
+
/**
|
|
225
|
+
* @description Cursor start drag event
|
|
226
|
+
*/
|
|
227
|
+
onCursorDragStart?: (time: number) => void;
|
|
228
|
+
/**
|
|
229
|
+
* @description Cursor end drag event
|
|
230
|
+
*/
|
|
231
|
+
onCursorDragEnd?: (time: number) => void;
|
|
232
|
+
/**
|
|
233
|
+
* @description Cursor drag event
|
|
234
|
+
*/
|
|
235
|
+
onCursorDrag?: (time: number) => void;
|
|
236
|
+
/**
|
|
237
|
+
* @description Click on time area event; return false to prevent setting time
|
|
238
|
+
*/
|
|
239
|
+
onClickTimeArea?: (time: number, e: React.MouseEvent<HTMLDivElement, MouseEvent>) => boolean | undefined;
|
|
240
|
+
/**
|
|
241
|
+
* @description Row drag start callback
|
|
242
|
+
* @param params row is the data of the row being dragged
|
|
243
|
+
*/
|
|
244
|
+
onRowDragStart?: (params: { row: TimelineRow }) => void;
|
|
245
|
+
/**
|
|
246
|
+
* @description Row drag end callback
|
|
247
|
+
* @param params row is the data of the row being dragged; editorData is the new data arrangement after the row is dragged
|
|
248
|
+
* @returns
|
|
249
|
+
*/
|
|
250
|
+
onRowDragEnd?: (params: { row: TimelineRow; editorData: TimelineRow[] }) => void;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface TimelineState {
|
|
254
|
+
/** DOM node */
|
|
255
|
+
target: HTMLElement | null;
|
|
256
|
+
/** Execution listener */
|
|
257
|
+
listener: Emitter<EventTypes>;
|
|
258
|
+
/** Whether it is playing */
|
|
259
|
+
isPlaying: boolean;
|
|
260
|
+
/** Whether it is paused */
|
|
261
|
+
isPaused: boolean;
|
|
262
|
+
/** Set current playback time */
|
|
263
|
+
setTime: (time: number) => void;
|
|
264
|
+
/** Get current playback time */
|
|
265
|
+
getTime: () => number;
|
|
266
|
+
/** Set playback rate */
|
|
267
|
+
setPlayRate: (rate: number) => void;
|
|
268
|
+
/** Get playback rate */
|
|
269
|
+
getPlayRate: () => number;
|
|
270
|
+
/** Re-render current time */
|
|
271
|
+
reRender: () => void;
|
|
272
|
+
/** Play */
|
|
273
|
+
play: (param: {
|
|
274
|
+
/** Default run from start to end, priority greater than autoEnd */
|
|
275
|
+
toTime?: number;
|
|
276
|
+
/** Whether it is automatically end after playback */
|
|
277
|
+
autoEnd?: boolean;
|
|
278
|
+
/** List of actionIds to run; runs all by default if not provided */
|
|
279
|
+
runActionIds?: string[];
|
|
280
|
+
}) => boolean;
|
|
281
|
+
/** Pause */
|
|
282
|
+
pause: () => void;
|
|
283
|
+
/** Set scroll left */
|
|
284
|
+
setScrollLeft: (val: number) => void;
|
|
285
|
+
/** Set scroll top */
|
|
286
|
+
setScrollTop: (val: number) => void;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Animation editor parameters
|
|
291
|
+
* @export
|
|
292
|
+
* @interface TimelineProp
|
|
293
|
+
*/
|
|
294
|
+
export interface TimelineEditor extends EditData {
|
|
295
|
+
/**
|
|
296
|
+
* @description Scroll distance of the editing area from the top (please use ref.setScrollTop instead)
|
|
297
|
+
* @deprecated
|
|
298
|
+
*/
|
|
299
|
+
scrollTop?: number;
|
|
300
|
+
/**
|
|
301
|
+
* @description Scroll callback for the editing area (used to control synchronization with scroll of editing rows)
|
|
302
|
+
*/
|
|
303
|
+
onScroll?: (params: OnScrollParams) => void;
|
|
304
|
+
/**
|
|
305
|
+
* @description Whether to enable automatic scrolling during dragging
|
|
306
|
+
* @default false
|
|
307
|
+
*/
|
|
308
|
+
autoScroll?: boolean;
|
|
309
|
+
/**
|
|
310
|
+
* @description Custom timeline style
|
|
311
|
+
*/
|
|
312
|
+
style?: React.CSSProperties;
|
|
313
|
+
/**
|
|
314
|
+
* @description Whether to re-render automatically (update tick when data changes or cursor time changes)
|
|
315
|
+
* @default true
|
|
316
|
+
*/
|
|
317
|
+
autoReRender?: boolean;
|
|
318
|
+
/**
|
|
319
|
+
* @description Data change callback, triggered after the end of an action operation changes data (returning false will prevent automatic engine synchronization, used to reduce performance overhead)
|
|
320
|
+
*/
|
|
321
|
+
onChange?: (editorData: TimelineRow[]) => void | boolean;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Define a utility type to make specified properties required
|
|
325
|
+
export type RequiredPick<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
|
326
|
+
|
|
327
|
+
export type RequiredEditData = RequiredPick<EditData, 'editorData' | 'effects' | 'scale' | 'scaleSplitCount' | 'scaleWidth' | 'startLeft' | 'minScaleCount' | 'maxScaleCount' | 'rowHeight'>;
|
|
328
|
+
|
|
329
|
+
export type RequiredTimelineEditor = RequiredPick<TimelineEditor, 'scrollTop'> & RequiredEditData;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DEFAULT_ROW_HEIGHT, DEFAULT_SCALE, DEFAULT_SCALE_SPLIT_COUNT, DEFAULT_SCALE_WIDTH, DEFAULT_START_LEFT, MIN_SCALE_COUNT } from '../interface/const';
|
|
2
|
+
import { RequiredTimelineEditor, TimelineEditor } from '../interface/timeline';
|
|
3
|
+
import ConsoleLogger from './logger';
|
|
4
|
+
const logger = new ConsoleLogger('timeline');
|
|
5
|
+
|
|
6
|
+
export function checkProps(props: TimelineEditor): RequiredTimelineEditor {
|
|
7
|
+
let {
|
|
8
|
+
editorData = [],
|
|
9
|
+
effects = {},
|
|
10
|
+
scrollTop = 0,
|
|
11
|
+
scale = DEFAULT_SCALE,
|
|
12
|
+
scaleSplitCount = DEFAULT_SCALE_SPLIT_COUNT,
|
|
13
|
+
scaleWidth = DEFAULT_SCALE_WIDTH,
|
|
14
|
+
startLeft = DEFAULT_START_LEFT,
|
|
15
|
+
minScaleCount = MIN_SCALE_COUNT,
|
|
16
|
+
maxScaleCount = Infinity,
|
|
17
|
+
rowHeight = DEFAULT_ROW_HEIGHT,
|
|
18
|
+
} = props;
|
|
19
|
+
|
|
20
|
+
if (scale <= 0) {
|
|
21
|
+
logger.error('Error: scale must be greater than 0!');
|
|
22
|
+
scale = DEFAULT_SCALE;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (scrollTop < 0) {
|
|
26
|
+
logger.warn('Warning: scrollTop cannot be less than 0!');
|
|
27
|
+
scrollTop = 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (scaleSplitCount <= 0) {
|
|
31
|
+
logger.warn('Warning: scaleSplitCount cannot be less than 1!');
|
|
32
|
+
scaleSplitCount = 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (scaleWidth <= 0) {
|
|
36
|
+
logger.warn('Warning: scaleWidth must be greater than 0!');
|
|
37
|
+
scaleWidth = DEFAULT_SCALE_WIDTH;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (startLeft < 0) {
|
|
41
|
+
logger.warn('Warning: startLeft cannot be less than 0!');
|
|
42
|
+
startLeft = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (minScaleCount < 1) {
|
|
46
|
+
logger.warn('Warning: minScaleCount must be greater than 1!');
|
|
47
|
+
minScaleCount = MIN_SCALE_COUNT;
|
|
48
|
+
}
|
|
49
|
+
minScaleCount = parseInt(minScaleCount + '');
|
|
50
|
+
|
|
51
|
+
if (maxScaleCount < minScaleCount) {
|
|
52
|
+
logger.warn('Warning: maxScaleCount cannot be less than minScaleCount!');
|
|
53
|
+
maxScaleCount = minScaleCount;
|
|
54
|
+
}
|
|
55
|
+
maxScaleCount = maxScaleCount === Infinity ? Infinity : parseInt(maxScaleCount + '');
|
|
56
|
+
|
|
57
|
+
if (rowHeight <= 0) {
|
|
58
|
+
logger.warn('Warning: rowHeight must be greater than 0!');
|
|
59
|
+
rowHeight = DEFAULT_ROW_HEIGHT;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const temp = { ...props };
|
|
63
|
+
delete temp['style'];
|
|
64
|
+
return {
|
|
65
|
+
...temp,
|
|
66
|
+
editorData,
|
|
67
|
+
effects,
|
|
68
|
+
scrollTop,
|
|
69
|
+
scale,
|
|
70
|
+
scaleSplitCount,
|
|
71
|
+
scaleWidth,
|
|
72
|
+
startLeft,
|
|
73
|
+
minScaleCount,
|
|
74
|
+
maxScaleCount,
|
|
75
|
+
rowHeight,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { TimelineAction, TimelineRow } from "@keplar-404/timeline-engine";
|
|
2
|
+
import { ADD_SCALE_COUNT } from "../interface/const";
|
|
3
|
+
|
|
4
|
+
/** Time to pixel */
|
|
5
|
+
export function parserTimeToPixel(
|
|
6
|
+
data: number,
|
|
7
|
+
param: {
|
|
8
|
+
startLeft: number;
|
|
9
|
+
scale: number;
|
|
10
|
+
scaleWidth: number;
|
|
11
|
+
}
|
|
12
|
+
) {
|
|
13
|
+
const { startLeft, scale, scaleWidth } = param;
|
|
14
|
+
return startLeft + (data / scale) * scaleWidth;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Pixel to time */
|
|
18
|
+
export function parserPixelToTime(
|
|
19
|
+
data: number,
|
|
20
|
+
param: {
|
|
21
|
+
startLeft: number;
|
|
22
|
+
scale: number;
|
|
23
|
+
scaleWidth: number;
|
|
24
|
+
}
|
|
25
|
+
) {
|
|
26
|
+
const { startLeft, scale, scaleWidth } = param;
|
|
27
|
+
return ((data - startLeft) / scaleWidth) * scale;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Position + Width to Start + End */
|
|
31
|
+
export function parserTransformToTime(
|
|
32
|
+
data: {
|
|
33
|
+
left: number;
|
|
34
|
+
width: number;
|
|
35
|
+
},
|
|
36
|
+
param: {
|
|
37
|
+
startLeft: number;
|
|
38
|
+
scale: number;
|
|
39
|
+
scaleWidth: number;
|
|
40
|
+
}
|
|
41
|
+
) {
|
|
42
|
+
const { left, width } = data;
|
|
43
|
+
const start = parserPixelToTime(left, param);
|
|
44
|
+
const end = parserPixelToTime(left + width, param);
|
|
45
|
+
return {
|
|
46
|
+
start,
|
|
47
|
+
end,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Start + End to Position + Width */
|
|
52
|
+
export function parserTimeToTransform(
|
|
53
|
+
data: {
|
|
54
|
+
start: number;
|
|
55
|
+
end: number;
|
|
56
|
+
},
|
|
57
|
+
param: {
|
|
58
|
+
startLeft: number;
|
|
59
|
+
scale: number;
|
|
60
|
+
scaleWidth: number;
|
|
61
|
+
}
|
|
62
|
+
) {
|
|
63
|
+
const { start, end } = data;
|
|
64
|
+
const left = parserTimeToPixel(start, param);
|
|
65
|
+
const width = parserTimeToPixel(end, param) - left;
|
|
66
|
+
return {
|
|
67
|
+
left,
|
|
68
|
+
width,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Get number of scale marks based on data */
|
|
73
|
+
export function getScaleCountByRows(data: TimelineRow[], param: { scale: number }) {
|
|
74
|
+
let max = 0;
|
|
75
|
+
data.forEach((row) => {
|
|
76
|
+
row.actions.forEach((action: TimelineAction) => {
|
|
77
|
+
max = Math.max(max, action.end);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
const count = Math.ceil(max / param.scale);
|
|
81
|
+
return count + ADD_SCALE_COUNT;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Get current number of scale marks based on time */
|
|
85
|
+
export function getScaleCountByPixel(
|
|
86
|
+
data: number,
|
|
87
|
+
param: {
|
|
88
|
+
startLeft: number;
|
|
89
|
+
scaleWidth: number;
|
|
90
|
+
scaleCount: number;
|
|
91
|
+
}
|
|
92
|
+
) {
|
|
93
|
+
const { startLeft, scaleWidth } = param;
|
|
94
|
+
const count = Math.ceil((data - startLeft) / scaleWidth);
|
|
95
|
+
return Math.max(count + ADD_SCALE_COUNT, param.scaleCount);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Get collection of positions for all action times */
|
|
99
|
+
export function parserActionsToPositions(
|
|
100
|
+
actions: TimelineAction[],
|
|
101
|
+
param: {
|
|
102
|
+
startLeft: number;
|
|
103
|
+
scale: number;
|
|
104
|
+
scaleWidth: number;
|
|
105
|
+
}
|
|
106
|
+
) {
|
|
107
|
+
const positions: number[] = [];
|
|
108
|
+
actions.forEach((item) => {
|
|
109
|
+
positions.push(parserTimeToPixel(item.start, param));
|
|
110
|
+
positions.push(parserTimeToPixel(item.end, param));
|
|
111
|
+
});
|
|
112
|
+
return positions;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Split an action in a row at a given time.
|
|
117
|
+
* Automatically updates start/end times and creates a new adjacent action.
|
|
118
|
+
*/
|
|
119
|
+
export function splitActionInRow(
|
|
120
|
+
data: TimelineRow[],
|
|
121
|
+
rowId: string,
|
|
122
|
+
actionId: string,
|
|
123
|
+
cutTime: number
|
|
124
|
+
): TimelineRow[] {
|
|
125
|
+
const rowIdx = data.findIndex((r) => r.id === rowId);
|
|
126
|
+
if (rowIdx === -1) return data;
|
|
127
|
+
|
|
128
|
+
const row = data[rowIdx];
|
|
129
|
+
const actIdx = row.actions.findIndex((a) => a.id === actionId);
|
|
130
|
+
if (actIdx === -1) return data;
|
|
131
|
+
|
|
132
|
+
const action = row.actions[actIdx];
|
|
133
|
+
|
|
134
|
+
// Validate the cut time is within the block boundaries
|
|
135
|
+
if (cutTime <= action.start || cutTime >= action.end) {
|
|
136
|
+
return data;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Clone immutably
|
|
140
|
+
const newData = [...data];
|
|
141
|
+
const targetRow = { ...row, actions: [...row.actions] };
|
|
142
|
+
newData[rowIdx] = targetRow;
|
|
143
|
+
|
|
144
|
+
const targetAction = { ...action };
|
|
145
|
+
const originalEnd = targetAction.end;
|
|
146
|
+
targetAction.end = cutTime;
|
|
147
|
+
targetRow.actions[actIdx] = targetAction;
|
|
148
|
+
|
|
149
|
+
const newAction = {
|
|
150
|
+
...targetAction,
|
|
151
|
+
id: `${targetAction.id}_split_${Date.now()}`,
|
|
152
|
+
start: cutTime,
|
|
153
|
+
end: originalEnd,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
targetRow.actions.splice(actIdx + 1, 0, newAction);
|
|
157
|
+
|
|
158
|
+
return newData;
|
|
159
|
+
}
|