@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,128 @@
|
|
|
1
|
+
import React, { FC, useRef, useCallback } from 'react';
|
|
2
|
+
import { TimelineAction, TimelineRow } from '@keplar-404/timeline-engine';
|
|
3
|
+
import { CommonProp } from '../../interface/common_prop';
|
|
4
|
+
import { prefix } from '../../utils/deal_class_prefix';
|
|
5
|
+
import { parserPixelToTime } from '../../utils/deal_data';
|
|
6
|
+
import { DragLineData } from './drag_lines';
|
|
7
|
+
import { EditAction } from './edit_action';
|
|
8
|
+
import './edit_row.css';
|
|
9
|
+
|
|
10
|
+
export type EditRowProps = CommonProp & {
|
|
11
|
+
areaRef: React.RefObject<HTMLDivElement>;
|
|
12
|
+
rowData?: TimelineRow;
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
dragLineData: DragLineData;
|
|
15
|
+
setEditorData: (params: TimelineRow[]) => void;
|
|
16
|
+
/** Horizontal scroll offset */
|
|
17
|
+
scrollLeft: number;
|
|
18
|
+
/** Horizontal scroll delta */
|
|
19
|
+
deltaScrollLeft: (scrollLeft: number) => void;
|
|
20
|
+
/** Row index in the editor data array */
|
|
21
|
+
rowIndex?: number;
|
|
22
|
+
/** Current row-reorder drag state */
|
|
23
|
+
dragState?: {
|
|
24
|
+
isDragging: boolean;
|
|
25
|
+
draggedIndex: number;
|
|
26
|
+
};
|
|
27
|
+
/** Enable cross-row block drag */
|
|
28
|
+
enableCrossRowDrag?: boolean;
|
|
29
|
+
/** Show ghost preview while block dragging across rows */
|
|
30
|
+
enableGhostPreview?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const EditRow: FC<EditRowProps> = (props) => {
|
|
34
|
+
const {
|
|
35
|
+
rowData,
|
|
36
|
+
style = {},
|
|
37
|
+
onClickRow,
|
|
38
|
+
onDoubleClickRow,
|
|
39
|
+
onContextMenuRow,
|
|
40
|
+
areaRef,
|
|
41
|
+
scrollLeft,
|
|
42
|
+
startLeft,
|
|
43
|
+
scale,
|
|
44
|
+
scaleWidth,
|
|
45
|
+
enableRowDrag,
|
|
46
|
+
onRowDragStart,
|
|
47
|
+
rowIndex = -1,
|
|
48
|
+
dragState,
|
|
49
|
+
enableCrossRowDrag,
|
|
50
|
+
enableGhostPreview,
|
|
51
|
+
} = props;
|
|
52
|
+
|
|
53
|
+
const classNames = ['edit-row'];
|
|
54
|
+
if (rowData?.selected) classNames.push('edit-row-selected');
|
|
55
|
+
if (dragState?.isDragging && dragState.draggedIndex === rowIndex) {
|
|
56
|
+
classNames.push('edit-row-dragging');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const dragHandleRef = useRef<HTMLDivElement>(null);
|
|
60
|
+
|
|
61
|
+
const handleTime = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
62
|
+
if (!areaRef.current) return 0;
|
|
63
|
+
const rect = areaRef.current.getBoundingClientRect();
|
|
64
|
+
const position = e.clientX - rect.x;
|
|
65
|
+
const left = position + scrollLeft;
|
|
66
|
+
return parserPixelToTime(left, { startLeft, scale, scaleWidth });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Row-reorder drag handle
|
|
70
|
+
const handleDragHandleMouseDown = useCallback(
|
|
71
|
+
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
72
|
+
if (!enableRowDrag || !rowData || rowIndex === -1) return;
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
onRowDragStart?.({ row: rowData });
|
|
76
|
+
},
|
|
77
|
+
[enableRowDrag, rowData, rowIndex, onRowDragStart],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
className={`${prefix(...classNames)} ${(rowData?.classNames || []).join(' ')}`}
|
|
83
|
+
style={style}
|
|
84
|
+
onClick={(e) => {
|
|
85
|
+
if (rowData && onClickRow) {
|
|
86
|
+
const time = handleTime(e);
|
|
87
|
+
onClickRow(e, { row: rowData, time });
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
onDoubleClick={(e) => {
|
|
91
|
+
if (rowData && onDoubleClickRow) {
|
|
92
|
+
const time = handleTime(e);
|
|
93
|
+
onDoubleClickRow(e, { row: rowData, time });
|
|
94
|
+
}
|
|
95
|
+
}}
|
|
96
|
+
onContextMenu={(e) => {
|
|
97
|
+
if (rowData && onContextMenuRow) {
|
|
98
|
+
const time = handleTime(e);
|
|
99
|
+
onContextMenuRow(e, { row: rowData, time });
|
|
100
|
+
}
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
{/* Row-reorder drag handle */}
|
|
104
|
+
{enableRowDrag && rowData && (
|
|
105
|
+
<div
|
|
106
|
+
ref={dragHandleRef}
|
|
107
|
+
className={prefix('edit-row-drag-handle')}
|
|
108
|
+
onMouseDown={handleDragHandleMouseDown}
|
|
109
|
+
title="Drag to reorder row"
|
|
110
|
+
>
|
|
111
|
+
⋮⋮
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
{(rowData?.actions || []).map((action: TimelineAction) => (
|
|
116
|
+
<EditAction
|
|
117
|
+
key={action.id}
|
|
118
|
+
{...props}
|
|
119
|
+
handleTime={handleTime}
|
|
120
|
+
row={rowData!}
|
|
121
|
+
action={action}
|
|
122
|
+
enableCrossRowDrag={enableCrossRowDrag}
|
|
123
|
+
enableGhostPreview={enableGhostPreview}
|
|
124
|
+
/>
|
|
125
|
+
))}
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { TimelineAction, TimelineRow } from "@keplar-404/timeline-engine";
|
|
3
|
+
import { parserActionsToPositions, parserTimeToTransform } from "../../../utils/deal_data";
|
|
4
|
+
import { DragLineData } from "../drag_lines";
|
|
5
|
+
|
|
6
|
+
export function useDragLine() {
|
|
7
|
+
const [dragLineData, setDragLineData] = useState<DragLineData>({ isMoving: false, movePositions: [], assistPositions: [] });
|
|
8
|
+
|
|
9
|
+
/** Get auxiliary lines */
|
|
10
|
+
const defaultGetAssistPosition = (data: {
|
|
11
|
+
editorData: TimelineRow[];
|
|
12
|
+
assistActionIds?: string[];
|
|
13
|
+
action: TimelineAction;
|
|
14
|
+
row: TimelineRow;
|
|
15
|
+
startLeft: number;
|
|
16
|
+
scale: number;
|
|
17
|
+
scaleWidth: number;
|
|
18
|
+
hideCursor: boolean;
|
|
19
|
+
cursorLeft: number;
|
|
20
|
+
}) => {
|
|
21
|
+
const { editorData, assistActionIds, action, row, scale, scaleWidth, startLeft, cursorLeft, hideCursor } = data;
|
|
22
|
+
const otherActions: TimelineAction[] = [];
|
|
23
|
+
if (assistActionIds) {
|
|
24
|
+
editorData.forEach((rowItem) => {
|
|
25
|
+
rowItem.actions.forEach((actionItem: TimelineAction) => {
|
|
26
|
+
if (assistActionIds.includes(actionItem.id)) otherActions.push(actionItem);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
editorData.forEach((rowItem) => {
|
|
31
|
+
if (rowItem.id !== row.id) {
|
|
32
|
+
otherActions.push(...rowItem.actions);
|
|
33
|
+
} else {
|
|
34
|
+
rowItem.actions.forEach((actionItem: TimelineAction) => {
|
|
35
|
+
if (actionItem.id !== action.id) otherActions.push(actionItem);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const positions = parserActionsToPositions(otherActions, {
|
|
42
|
+
startLeft,
|
|
43
|
+
scale,
|
|
44
|
+
scaleWidth,
|
|
45
|
+
});
|
|
46
|
+
if (!hideCursor) positions.push(cursorLeft);
|
|
47
|
+
|
|
48
|
+
return positions;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Get current movement markers */
|
|
52
|
+
const defaultGetMovePosition = (data: { start: number; end: number; dir?: "right" | "left"; startLeft: number; scale: number; scaleWidth: number }) => {
|
|
53
|
+
const { start, end, dir, scale, scaleWidth, startLeft } = data;
|
|
54
|
+
const { left, width } = parserTimeToTransform({ start, end }, { startLeft, scaleWidth, scale });
|
|
55
|
+
if (!dir) return [left, left + width];
|
|
56
|
+
return dir === "right" ? [left + width] : [left];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Initialize drag lines */
|
|
60
|
+
const initDragLine = (data: { movePositions?: number[]; assistPositions?: number[] }) => {
|
|
61
|
+
const { movePositions, assistPositions } = data;
|
|
62
|
+
|
|
63
|
+
setDragLineData({
|
|
64
|
+
isMoving: true,
|
|
65
|
+
movePositions: movePositions || [],
|
|
66
|
+
assistPositions: assistPositions || [],
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Update drag line */
|
|
71
|
+
const updateDragLine = (data: { movePositions?: number[]; assistPositions?: number[] }) => {
|
|
72
|
+
const { movePositions, assistPositions } = data;
|
|
73
|
+
setDragLineData((pre) => ({
|
|
74
|
+
...pre,
|
|
75
|
+
movePositions: movePositions || pre.movePositions,
|
|
76
|
+
assistPositions: assistPositions || pre.assistPositions,
|
|
77
|
+
}));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Dispose drag lines */
|
|
81
|
+
const disposeDragLine = () => {
|
|
82
|
+
setDragLineData({ isMoving: false, movePositions: [], assistPositions: [] });
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
initDragLine,
|
|
87
|
+
updateDragLine,
|
|
88
|
+
disposeDragLine,
|
|
89
|
+
dragLineData,
|
|
90
|
+
defaultGetAssistPosition,
|
|
91
|
+
defaultGetMovePosition,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
import { prefix } from '../../utils/deal_class_prefix';
|
|
3
|
+
import { TimelineRow } from '@keplar-404/timeline-engine';
|
|
4
|
+
import { calculateInsertionLineTop } from './drag_utils';
|
|
5
|
+
|
|
6
|
+
interface InsertionLineProps {
|
|
7
|
+
/** Distance from top */
|
|
8
|
+
top: number;
|
|
9
|
+
/** Whether the insertion line is visible */
|
|
10
|
+
visible: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Insertion line component - displays the drag insertion position
|
|
15
|
+
*/
|
|
16
|
+
export const InsertionLine: FC<InsertionLineProps> = ({ top, visible }) => {
|
|
17
|
+
if (!visible || top < 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Calculate insertion line position
|
|
22
|
+
// const top = calculateInsertionLineTop(editorData, insertionLineIndex, rowHeight);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
className={prefix('edit-area-insertion-line')}
|
|
27
|
+
style={{
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
left: 0,
|
|
30
|
+
right: 0,
|
|
31
|
+
height: '2px',
|
|
32
|
+
background: '#4a90e2',
|
|
33
|
+
zIndex: 1000,
|
|
34
|
+
pointerEvents: 'none',
|
|
35
|
+
top,
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
.loop-zone-overlay {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
right: 0;
|
|
6
|
+
height: 100%;
|
|
7
|
+
pointer-events: none;
|
|
8
|
+
z-index: 4;
|
|
9
|
+
overflow: visible;
|
|
10
|
+
}
|
|
11
|
+
.loop-zone-band {
|
|
12
|
+
position: absolute;
|
|
13
|
+
top: 0;
|
|
14
|
+
height: 100%;
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
border-left: 1px solid transparent;
|
|
17
|
+
border-right: 1px solid transparent;
|
|
18
|
+
border-top: 2px solid transparent;
|
|
19
|
+
}
|
|
20
|
+
.loop-zone-handle {
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 0;
|
|
23
|
+
height: 100%;
|
|
24
|
+
transform: translateX(-50%);
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
user-select: none;
|
|
27
|
+
}
|
|
28
|
+
.loop-zone-handle__line {
|
|
29
|
+
position: absolute;
|
|
30
|
+
top: 32px;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
left: 50%;
|
|
33
|
+
transform: translateX(-50%);
|
|
34
|
+
width: 1px;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
border-left: 1px dashed transparent;
|
|
37
|
+
background: currentColor;
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
}
|
|
40
|
+
.loop-zone-handle__grip {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 6px;
|
|
43
|
+
left: 50%;
|
|
44
|
+
transform: translateX(-50%);
|
|
45
|
+
pointer-events: auto;
|
|
46
|
+
cursor: ew-resize;
|
|
47
|
+
user-select: none;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
padding: 5px 7px;
|
|
52
|
+
border-radius: 5px;
|
|
53
|
+
transition: filter 0.12s, transform 0.1s;
|
|
54
|
+
}
|
|
55
|
+
.loop-zone-handle__grip:hover {
|
|
56
|
+
filter: brightness(1.2);
|
|
57
|
+
transform: translateX(-50%) scaleY(1.1);
|
|
58
|
+
}
|
|
59
|
+
.loop-zone-handle__grip:active {
|
|
60
|
+
transform: translateX(-50%) scale(0.94);
|
|
61
|
+
}
|
|
62
|
+
.loop-zone-handle__grip svg {
|
|
63
|
+
display: block;
|
|
64
|
+
flex-shrink: 0;
|
|
65
|
+
}
|