@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,365 @@
|
|
|
1
|
+
import { Interactable } from '@interactjs/core/Interactable';
|
|
2
|
+
import { DragEvent, ResizeEvent } from '@interactjs/types/index';
|
|
3
|
+
import React, { ReactElement, useEffect, useImperativeHandle, useRef } from 'react';
|
|
4
|
+
import { DEFAULT_ADSORPTION_DISTANCE, DEFAULT_MOVE_GRID, DEFAULT_START_LEFT } from '../../interface/const';
|
|
5
|
+
import { useAutoScroll } from './hooks/useAutoScroll';
|
|
6
|
+
import { InteractComp } from './interactable';
|
|
7
|
+
import { Direction, RowRndApi, RowRndProps } from './row_rnd_interface';
|
|
8
|
+
|
|
9
|
+
export const RowDnd = React.forwardRef<RowRndApi, RowRndProps>(
|
|
10
|
+
(
|
|
11
|
+
{
|
|
12
|
+
children,
|
|
13
|
+
edges,
|
|
14
|
+
left,
|
|
15
|
+
width,
|
|
16
|
+
|
|
17
|
+
start = DEFAULT_START_LEFT,
|
|
18
|
+
grid = DEFAULT_MOVE_GRID,
|
|
19
|
+
bounds = {
|
|
20
|
+
left: Number.MIN_SAFE_INTEGER,
|
|
21
|
+
right: Number.MAX_SAFE_INTEGER,
|
|
22
|
+
},
|
|
23
|
+
enableResizing = true,
|
|
24
|
+
enableDragging = true,
|
|
25
|
+
adsorptionDistance = DEFAULT_ADSORPTION_DISTANCE,
|
|
26
|
+
adsorptionPositions = [],
|
|
27
|
+
onResizeStart,
|
|
28
|
+
onResize,
|
|
29
|
+
onResizeEnd,
|
|
30
|
+
onDragStart,
|
|
31
|
+
onDragEnd,
|
|
32
|
+
onDrag,
|
|
33
|
+
parentRef,
|
|
34
|
+
deltaScrollLeft,
|
|
35
|
+
},
|
|
36
|
+
ref,
|
|
37
|
+
) => {
|
|
38
|
+
const interactable = useRef<Interactable | null>(null);
|
|
39
|
+
const deltaX = useRef(0);
|
|
40
|
+
const isAdsorption = useRef(false);
|
|
41
|
+
const { initAutoScroll, dealDragAutoScroll, dealResizeAutoScroll, stopAutoScroll } = useAutoScroll(parentRef);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
return () => {
|
|
45
|
+
interactable.current && interactable.current.unset();
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
//#region [rgba(100,120,156,0.08)] Assignment related APIs
|
|
50
|
+
useImperativeHandle(ref, () => ({
|
|
51
|
+
updateLeft: (left) => handleUpdateLeft(left || 0, false),
|
|
52
|
+
updateWidth: (width) => handleUpdateWidth(width, false),
|
|
53
|
+
getLeft: handleGetLeft,
|
|
54
|
+
getWidth: handleGetWidth,
|
|
55
|
+
}));
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const target = interactable.current?.target as HTMLElement;
|
|
58
|
+
handleUpdateWidth(typeof width === 'undefined' ? target.offsetWidth : width, false);
|
|
59
|
+
}, [width]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
handleUpdateLeft(left || 0, false);
|
|
62
|
+
}, [left]);
|
|
63
|
+
|
|
64
|
+
const handleUpdateLeft = (left: number, reset = true) => {
|
|
65
|
+
if (!interactable.current || !interactable.current.target) return;
|
|
66
|
+
reset && (deltaX.current = 0);
|
|
67
|
+
const target = interactable.current.target as HTMLElement;
|
|
68
|
+
target.style.left = `${left}px`;
|
|
69
|
+
Object.assign(target.dataset, { left });
|
|
70
|
+
};
|
|
71
|
+
const handleUpdateWidth = (width: number, reset = true) => {
|
|
72
|
+
if (!interactable.current || !interactable.current.target) return;
|
|
73
|
+
reset && (deltaX.current = 0);
|
|
74
|
+
const target = interactable.current.target as HTMLElement;
|
|
75
|
+
target.style.width = `${width}px`;
|
|
76
|
+
Object.assign(target.dataset, { width });
|
|
77
|
+
};
|
|
78
|
+
const handleGetLeft = () => {
|
|
79
|
+
const target = interactable.current?.target as HTMLElement;
|
|
80
|
+
return parseFloat(target?.dataset?.left || '0');
|
|
81
|
+
};
|
|
82
|
+
const handleGetWidth = () => {
|
|
83
|
+
const target = interactable.current?.target as HTMLElement;
|
|
84
|
+
return parseFloat(target?.dataset?.width || '0');
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
|
|
88
|
+
//#region [rgba(188,188,120,0.05)] Callback APIs
|
|
89
|
+
const handleMoveStart = (e: DragEvent) => {
|
|
90
|
+
deltaX.current = 0;
|
|
91
|
+
isAdsorption.current = false;
|
|
92
|
+
initAutoScroll();
|
|
93
|
+
onDragStart && onDragStart();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const move = (param: { preLeft: number; preWidth: number; scrollDelta?: number }) => {
|
|
97
|
+
const { preLeft, preWidth, scrollDelta } = param;
|
|
98
|
+
const distance = isAdsorption.current ? adsorptionDistance : grid;
|
|
99
|
+
if (Math.abs(deltaX.current) >= distance) {
|
|
100
|
+
const count = parseInt(deltaX.current / distance + '');
|
|
101
|
+
let curLeft = preLeft + count * distance;
|
|
102
|
+
|
|
103
|
+
// Control adsorption
|
|
104
|
+
let adsorption = curLeft;
|
|
105
|
+
let minDis = Number.MAX_SAFE_INTEGER;
|
|
106
|
+
adsorptionPositions.forEach((item) => {
|
|
107
|
+
const dis = Math.abs(item - curLeft);
|
|
108
|
+
if (dis < adsorptionDistance && dis < minDis) adsorption = item;
|
|
109
|
+
const dis2 = Math.abs(item - (curLeft + preWidth));
|
|
110
|
+
if (dis2 < adsorptionDistance && dis2 < minDis) adsorption = item - preWidth;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (adsorption !== curLeft) {
|
|
114
|
+
// Use adsorption data
|
|
115
|
+
isAdsorption.current = true;
|
|
116
|
+
curLeft = adsorption;
|
|
117
|
+
} else {
|
|
118
|
+
// Control grid
|
|
119
|
+
if ((curLeft - start) % grid !== 0) {
|
|
120
|
+
curLeft = start + grid * Math.round((curLeft - start) / grid);
|
|
121
|
+
}
|
|
122
|
+
isAdsorption.current = false;
|
|
123
|
+
}
|
|
124
|
+
deltaX.current = deltaX.current % distance;
|
|
125
|
+
|
|
126
|
+
// Control bounds
|
|
127
|
+
if (curLeft < bounds.left) curLeft = bounds.left;
|
|
128
|
+
else if (curLeft + preWidth > bounds.right) curLeft = bounds.right - preWidth;
|
|
129
|
+
|
|
130
|
+
if (onDrag) {
|
|
131
|
+
const ret = onDrag(
|
|
132
|
+
{
|
|
133
|
+
lastLeft: preLeft,
|
|
134
|
+
left: curLeft,
|
|
135
|
+
lastWidth: preWidth,
|
|
136
|
+
width: preWidth,
|
|
137
|
+
},
|
|
138
|
+
scrollDelta,
|
|
139
|
+
);
|
|
140
|
+
if (ret === false) return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
handleUpdateLeft(curLeft, false);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const handleMove = (e: DragEvent) => {
|
|
148
|
+
const target = e.target;
|
|
149
|
+
|
|
150
|
+
if (deltaScrollLeft && parentRef?.current) {
|
|
151
|
+
const result = dealDragAutoScroll(e, (delta) => {
|
|
152
|
+
deltaScrollLeft(delta);
|
|
153
|
+
|
|
154
|
+
let { left, width } = target.dataset;
|
|
155
|
+
const preLeft = parseFloat(left || '0');
|
|
156
|
+
const preWidth = parseFloat(width || '0');
|
|
157
|
+
deltaX.current += delta;
|
|
158
|
+
move({ preLeft, preWidth, scrollDelta: delta });
|
|
159
|
+
});
|
|
160
|
+
if (!result) return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let { left, width } = target.dataset;
|
|
164
|
+
const preLeft = parseFloat(left || '0');
|
|
165
|
+
const preWidth = parseFloat(width || '0');
|
|
166
|
+
|
|
167
|
+
deltaX.current += e.dx;
|
|
168
|
+
move({ preLeft, preWidth });
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleMoveStop = (e: DragEvent) => {
|
|
172
|
+
deltaX.current = 0;
|
|
173
|
+
isAdsorption.current = false;
|
|
174
|
+
stopAutoScroll();
|
|
175
|
+
|
|
176
|
+
const target = e.target;
|
|
177
|
+
let { left, width } = target.dataset;
|
|
178
|
+
onDragEnd && onDragEnd({ left: parseFloat(left || '0'), width: parseFloat(width || '0') });
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleResizeStart = (e: ResizeEvent) => {
|
|
182
|
+
deltaX.current = 0;
|
|
183
|
+
isAdsorption.current = false;
|
|
184
|
+
initAutoScroll();
|
|
185
|
+
|
|
186
|
+
let dir: Direction = e.edges?.right ? 'right' : 'left';
|
|
187
|
+
onResizeStart && onResizeStart(dir);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const resize = (param: { preLeft: number; preWidth: number; dir: 'left' | 'right' }) => {
|
|
191
|
+
const { dir, preWidth, preLeft } = param;
|
|
192
|
+
const distance = isAdsorption.current ? adsorptionDistance : grid;
|
|
193
|
+
|
|
194
|
+
if (dir === 'left') {
|
|
195
|
+
// Dragging left side
|
|
196
|
+
if (Math.abs(deltaX.current) >= distance) {
|
|
197
|
+
const count = parseInt(deltaX.current / distance + '');
|
|
198
|
+
let curLeft = preLeft + count * distance;
|
|
199
|
+
|
|
200
|
+
// Control adsorption
|
|
201
|
+
let adsorption = curLeft;
|
|
202
|
+
let minDis = Number.MAX_SAFE_INTEGER;
|
|
203
|
+
adsorptionPositions.forEach((item) => {
|
|
204
|
+
const dis = Math.abs(item - curLeft);
|
|
205
|
+
if (dis < adsorptionDistance && dis < minDis) adsorption = item;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (adsorption !== curLeft) {
|
|
209
|
+
// Use adsorption data
|
|
210
|
+
isAdsorption.current = true;
|
|
211
|
+
curLeft = adsorption;
|
|
212
|
+
} else {
|
|
213
|
+
// Control grid
|
|
214
|
+
if ((curLeft - start) % grid !== 0) {
|
|
215
|
+
curLeft = start + grid * Math.round((curLeft - start) / grid);
|
|
216
|
+
}
|
|
217
|
+
isAdsorption.current = false;
|
|
218
|
+
}
|
|
219
|
+
deltaX.current = deltaX.current % distance;
|
|
220
|
+
|
|
221
|
+
// Control bounds
|
|
222
|
+
const tempRight = preLeft + preWidth;
|
|
223
|
+
if (curLeft < bounds.left) curLeft = bounds.left;
|
|
224
|
+
const curWidth = tempRight - curLeft;
|
|
225
|
+
|
|
226
|
+
if (onResize) {
|
|
227
|
+
const ret = onResize('left', {
|
|
228
|
+
lastLeft: preLeft,
|
|
229
|
+
lastWidth: preWidth,
|
|
230
|
+
left: curLeft,
|
|
231
|
+
width: curWidth,
|
|
232
|
+
});
|
|
233
|
+
if (ret === false) return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
handleUpdateLeft(curLeft, false);
|
|
237
|
+
handleUpdateWidth(curWidth, false);
|
|
238
|
+
}
|
|
239
|
+
} else if (dir === 'right') {
|
|
240
|
+
// Dragging right side
|
|
241
|
+
if (Math.abs(deltaX.current) >= distance) {
|
|
242
|
+
const count = parseInt(deltaX.current / grid + '');
|
|
243
|
+
let curWidth = preWidth + count * grid;
|
|
244
|
+
|
|
245
|
+
// Control adsorption
|
|
246
|
+
let adsorption = preLeft + curWidth;
|
|
247
|
+
let minDis = Number.MAX_SAFE_INTEGER;
|
|
248
|
+
adsorptionPositions.forEach((item) => {
|
|
249
|
+
const dis = Math.abs(item - (preLeft + curWidth));
|
|
250
|
+
if (dis < adsorptionDistance && dis < minDis) adsorption = item;
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
if (adsorption !== preLeft + curWidth) {
|
|
254
|
+
// Use adsorption data
|
|
255
|
+
isAdsorption.current = true;
|
|
256
|
+
curWidth = adsorption - preLeft;
|
|
257
|
+
} else {
|
|
258
|
+
// Control grid
|
|
259
|
+
let tempRight = preLeft + curWidth;
|
|
260
|
+
if ((tempRight - start) % grid !== 0) {
|
|
261
|
+
tempRight = start + grid * Math.round((tempRight - start) / grid);
|
|
262
|
+
curWidth = tempRight - preLeft;
|
|
263
|
+
}
|
|
264
|
+
isAdsorption.current = false;
|
|
265
|
+
}
|
|
266
|
+
deltaX.current = deltaX.current % distance;
|
|
267
|
+
|
|
268
|
+
// Control bounds
|
|
269
|
+
if (preLeft + curWidth > bounds.right) curWidth = bounds.right - preLeft;
|
|
270
|
+
|
|
271
|
+
if (onResize) {
|
|
272
|
+
const ret = onResize('right', {
|
|
273
|
+
lastLeft: preLeft,
|
|
274
|
+
lastWidth: preWidth,
|
|
275
|
+
left: preLeft,
|
|
276
|
+
width: curWidth,
|
|
277
|
+
});
|
|
278
|
+
if (ret === false) return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
handleUpdateWidth(curWidth, false);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const handleResize = (e: ResizeEvent) => {
|
|
287
|
+
const target = e.target;
|
|
288
|
+
const dir = e.edges?.left ? 'left' : 'right';
|
|
289
|
+
|
|
290
|
+
if (deltaScrollLeft && parentRef?.current) {
|
|
291
|
+
const result = dealResizeAutoScroll(e, dir, (delta) => {
|
|
292
|
+
deltaScrollLeft(delta);
|
|
293
|
+
|
|
294
|
+
let { left, width } = target.dataset;
|
|
295
|
+
const preLeft = parseFloat(left || '0');
|
|
296
|
+
const preWidth = parseFloat(width || '0');
|
|
297
|
+
deltaX.current += delta;
|
|
298
|
+
resize({ preLeft, preWidth, dir });
|
|
299
|
+
});
|
|
300
|
+
if (!result) return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let { left, width } = target.dataset;
|
|
304
|
+
const preLeft = parseFloat(left || '0');
|
|
305
|
+
const preWidth = parseFloat(width || '0');
|
|
306
|
+
|
|
307
|
+
deltaX.current += dir === 'left' ? e.deltaRect?.left || 0 : e.deltaRect?.right || 0;
|
|
308
|
+
resize({ preLeft, preWidth, dir });
|
|
309
|
+
};
|
|
310
|
+
const handleResizeStop = (e: ResizeEvent) => {
|
|
311
|
+
deltaX.current = 0;
|
|
312
|
+
isAdsorption.current = false;
|
|
313
|
+
stopAutoScroll();
|
|
314
|
+
|
|
315
|
+
const target = e.target;
|
|
316
|
+
let { left, width } = target.dataset;
|
|
317
|
+
let dir: Direction = e.edges?.right ? 'right' : 'left';
|
|
318
|
+
onResizeEnd &&
|
|
319
|
+
onResizeEnd(dir, {
|
|
320
|
+
left: parseFloat(left || '0'),
|
|
321
|
+
width: parseFloat(width || '0'),
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
//#endregion
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<InteractComp
|
|
328
|
+
interactRef={interactable}
|
|
329
|
+
draggable={enableDragging}
|
|
330
|
+
resizable={enableResizing}
|
|
331
|
+
draggableOptions={{
|
|
332
|
+
lockAxis: 'x',
|
|
333
|
+
onmove: handleMove,
|
|
334
|
+
onstart: handleMoveStart,
|
|
335
|
+
onend: handleMoveStop,
|
|
336
|
+
cursorChecker: () => {
|
|
337
|
+
return '';
|
|
338
|
+
},
|
|
339
|
+
}}
|
|
340
|
+
resizableOptions={{
|
|
341
|
+
axis: 'x',
|
|
342
|
+
invert: 'none',
|
|
343
|
+
edges: {
|
|
344
|
+
left: true,
|
|
345
|
+
right: true,
|
|
346
|
+
top: false,
|
|
347
|
+
bottom: false,
|
|
348
|
+
...(edges || {}),
|
|
349
|
+
},
|
|
350
|
+
onmove: handleResize,
|
|
351
|
+
onstart: handleResizeStart,
|
|
352
|
+
onend: handleResizeStop,
|
|
353
|
+
}}
|
|
354
|
+
>
|
|
355
|
+
{React.cloneElement(children as ReactElement, {
|
|
356
|
+
style: {
|
|
357
|
+
...((children as ReactElement).props.style || {}),
|
|
358
|
+
left,
|
|
359
|
+
width,
|
|
360
|
+
},
|
|
361
|
+
})}
|
|
362
|
+
</InteractComp>
|
|
363
|
+
);
|
|
364
|
+
},
|
|
365
|
+
);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DragEvent, ResizeEvent } from "@interactjs/types/index";
|
|
2
|
+
|
|
3
|
+
type EventData = {
|
|
4
|
+
lastLeft: number;
|
|
5
|
+
left: number;
|
|
6
|
+
lastWidth: number;
|
|
7
|
+
width: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type RndDragStartCallback = () => void;
|
|
11
|
+
export type RndDragCallback = (
|
|
12
|
+
data: EventData,
|
|
13
|
+
scrollDelta?: number,
|
|
14
|
+
) => boolean | void;
|
|
15
|
+
export type RndDragEndCallback = (data: Pick<EventData, 'left' | 'width'>) => void;
|
|
16
|
+
|
|
17
|
+
export type Direction = "left" | "right";
|
|
18
|
+
export type RndResizeStartCallback = (dir: Direction) => void;
|
|
19
|
+
export type RndResizeCallback = (
|
|
20
|
+
dir: Direction,
|
|
21
|
+
data: EventData
|
|
22
|
+
) => boolean | void;
|
|
23
|
+
export type RndResizeEndCallback = (
|
|
24
|
+
dir: Direction,
|
|
25
|
+
data: Pick<EventData, 'left' | 'width'>
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
export interface RowRndApi {
|
|
29
|
+
updateWidth: (size: number) => void;
|
|
30
|
+
updateLeft: (left: number) => void;
|
|
31
|
+
getLeft: () => number;
|
|
32
|
+
getWidth: () => number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RowRndProps {
|
|
36
|
+
width?: number;
|
|
37
|
+
left?: number;
|
|
38
|
+
grid?: number;
|
|
39
|
+
start?: number;
|
|
40
|
+
bounds?: { left: number; right: number };
|
|
41
|
+
edges?: {left: boolean | string, right: boolean | string};
|
|
42
|
+
|
|
43
|
+
onResizeStart?: RndResizeStartCallback;
|
|
44
|
+
onResize?: RndResizeCallback;
|
|
45
|
+
onResizeEnd?: RndResizeEndCallback;
|
|
46
|
+
onDragStart?: RndDragStartCallback;
|
|
47
|
+
onDrag?: RndDragCallback;
|
|
48
|
+
onDragEnd?: RndDragEndCallback;
|
|
49
|
+
// Automatic scrolling is enabled when both parentRef and deltaScrollLeft are provided
|
|
50
|
+
parentRef: React.RefObject<HTMLDivElement>;
|
|
51
|
+
deltaScrollLeft?: (delta: number) => void;
|
|
52
|
+
|
|
53
|
+
children?: React.ReactNode;
|
|
54
|
+
|
|
55
|
+
enableResizing?: boolean;
|
|
56
|
+
enableDragging?: boolean;
|
|
57
|
+
adsorptionPositions?: number[];
|
|
58
|
+
adsorptionDistance?: number;
|
|
59
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.timeline-editor-time-area {
|
|
2
|
+
position: relative;
|
|
3
|
+
height: 32px;
|
|
4
|
+
flex: 0 0 auto;
|
|
5
|
+
}
|
|
6
|
+
.timeline-editor-time-area .ReactVirtualized__Grid {
|
|
7
|
+
outline: none;
|
|
8
|
+
}
|
|
9
|
+
.timeline-editor-time-area .ReactVirtualized__Grid::-webkit-scrollbar {
|
|
10
|
+
display: none;
|
|
11
|
+
}
|
|
12
|
+
.timeline-editor-time-area-interact {
|
|
13
|
+
position: absolute;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
left: 0;
|
|
16
|
+
top: 0;
|
|
17
|
+
}
|
|
18
|
+
.timeline-editor-time-unit {
|
|
19
|
+
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
|
20
|
+
position: relative;
|
|
21
|
+
box-sizing: content-box;
|
|
22
|
+
height: 4px !important;
|
|
23
|
+
bottom: 0 !important;
|
|
24
|
+
top: auto !important;
|
|
25
|
+
}
|
|
26
|
+
.timeline-editor-time-unit-big {
|
|
27
|
+
height: 8px !important;
|
|
28
|
+
}
|
|
29
|
+
.timeline-editor-time-unit-scale {
|
|
30
|
+
color: rgba(255, 255, 255, 0.6);
|
|
31
|
+
position: absolute;
|
|
32
|
+
right: 0;
|
|
33
|
+
top: 0;
|
|
34
|
+
transform: translate(50%, -100%);
|
|
35
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { FC, useEffect, useRef } from 'react';
|
|
2
|
+
import { AutoSizer, Grid, GridCellRenderer, OnScrollParams } from 'react-virtualized';
|
|
3
|
+
import { CommonProp } from '../../interface/common_prop';
|
|
4
|
+
import { prefix } from '../../utils/deal_class_prefix';
|
|
5
|
+
import { parserPixelToTime } from '../../utils/deal_data';
|
|
6
|
+
import './time_area.css';
|
|
7
|
+
|
|
8
|
+
/** Animation timeline component parameters */
|
|
9
|
+
export type TimeAreaProps = CommonProp & {
|
|
10
|
+
/** Scroll distance from left */
|
|
11
|
+
scrollLeft: number;
|
|
12
|
+
/** Scroll callback, used for synchronous scrolling */
|
|
13
|
+
onScroll: (params: OnScrollParams) => void;
|
|
14
|
+
/** Set cursor position */
|
|
15
|
+
setCursor: (param: { left?: number; time?: number }) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Animation timeline component */
|
|
19
|
+
export const TimeArea: FC<TimeAreaProps> = ({ setCursor, maxScaleCount, hideCursor, scale, scaleWidth, scaleCount, scaleSplitCount, startLeft, scrollLeft, onClickTimeArea, getScaleRender }) => {
|
|
20
|
+
const gridRef = useRef<Grid>(null);
|
|
21
|
+
/** Whether to show subdivision marks */
|
|
22
|
+
const showUnit = scaleSplitCount > 0;
|
|
23
|
+
|
|
24
|
+
/** Get rendering content for each cell */
|
|
25
|
+
const cellRenderer: GridCellRenderer = ({ columnIndex, key, style }) => {
|
|
26
|
+
const isShowScale = showUnit ? columnIndex % scaleSplitCount === 0 : true;
|
|
27
|
+
const classNames = ['time-unit'];
|
|
28
|
+
if (isShowScale) classNames.push('time-unit-big');
|
|
29
|
+
const item = (showUnit ? columnIndex / scaleSplitCount : columnIndex) * scale;
|
|
30
|
+
return (
|
|
31
|
+
<div key={key} style={style} className={prefix(...classNames)}>
|
|
32
|
+
{isShowScale && <div className={prefix('time-unit-scale')}>{getScaleRender ? getScaleRender(item) : item}</div>}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
gridRef.current?.recomputeGridSize();
|
|
39
|
+
}, [scaleWidth, startLeft]);
|
|
40
|
+
|
|
41
|
+
/** Get column width */
|
|
42
|
+
const getColumnWidth = (data: { index: number }) => {
|
|
43
|
+
switch (data.index) {
|
|
44
|
+
case 0:
|
|
45
|
+
return startLeft;
|
|
46
|
+
default:
|
|
47
|
+
return showUnit ? scaleWidth / scaleSplitCount : scaleWidth;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const estColumnWidth=getColumnWidth({index:1});
|
|
51
|
+
return (
|
|
52
|
+
<div className={prefix('time-area')}>
|
|
53
|
+
<AutoSizer>
|
|
54
|
+
{({ width, height }) => {
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
<Grid
|
|
58
|
+
ref={gridRef}
|
|
59
|
+
columnCount={showUnit ? scaleCount * scaleSplitCount + 1 : scaleCount}
|
|
60
|
+
columnWidth={getColumnWidth}
|
|
61
|
+
estimatedColumnSize={estColumnWidth}
|
|
62
|
+
rowCount={1}
|
|
63
|
+
rowHeight={height}
|
|
64
|
+
width={width}
|
|
65
|
+
height={height}
|
|
66
|
+
overscanRowCount={0}
|
|
67
|
+
overscanColumnCount={10}
|
|
68
|
+
cellRenderer={cellRenderer}
|
|
69
|
+
scrollLeft={scrollLeft}
|
|
70
|
+
></Grid>
|
|
71
|
+
<div
|
|
72
|
+
style={{ width, height }}
|
|
73
|
+
onClick={(e) => {
|
|
74
|
+
if (hideCursor) return;
|
|
75
|
+
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
|
76
|
+
const position = e.clientX - rect.x;
|
|
77
|
+
const left = Math.max(position + scrollLeft, startLeft);
|
|
78
|
+
if (left > maxScaleCount * scaleWidth + startLeft - scrollLeft) return;
|
|
79
|
+
|
|
80
|
+
const time = parserPixelToTime(left, { startLeft, scale, scaleWidth });
|
|
81
|
+
const result = onClickTimeArea && onClickTimeArea(time, e);
|
|
82
|
+
if (result === false) return; // Prevent setting time when false is returned
|
|
83
|
+
setCursor({ time });
|
|
84
|
+
}}
|
|
85
|
+
className={prefix('time-area-interact')}
|
|
86
|
+
></div>
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
}}
|
|
90
|
+
</AutoSizer>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
};
|