@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.
Files changed (73) hide show
  1. package/dist/components/control_area/index.d.ts +0 -0
  2. package/dist/components/cursor/cursor.d.ts +20 -0
  3. package/dist/components/cut-overlay/CutOverlay.d.ts +202 -0
  4. package/dist/components/edit_area/cross_row_drag.d.ts +50 -0
  5. package/dist/components/edit_area/drag_lines.d.ts +11 -0
  6. package/dist/components/edit_area/drag_preview.d.ts +14 -0
  7. package/dist/components/edit_area/drag_utils.d.ts +39 -0
  8. package/dist/components/edit_area/edit_action.d.ts +19 -0
  9. package/dist/components/edit_area/edit_area.d.ts +56 -0
  10. package/dist/components/edit_area/edit_row.d.ts +27 -0
  11. package/dist/components/edit_area/hooks/use_drag_line.d.ts +33 -0
  12. package/dist/components/edit_area/insertion_line.d.ts +12 -0
  13. package/dist/components/loop-zone/LoopZoneOverlay.d.ts +243 -0
  14. package/dist/components/row_rnd/hooks/useAutoScroll.d.ts +7 -0
  15. package/dist/components/row_rnd/interactable.d.ts +14 -0
  16. package/dist/components/row_rnd/row_rnd.d.ts +3 -0
  17. package/dist/components/row_rnd/row_rnd_interface.d.ts +47 -0
  18. package/dist/components/time_area/time_area.d.ts +17 -0
  19. package/dist/components/timeline.d.ts +3 -0
  20. package/dist/components/transport/TransportBar.d.ts +132 -0
  21. package/dist/components/transport/useTimelinePlayer.d.ts +164 -0
  22. package/dist/index.cjs.js +13 -0
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.es.js +10102 -0
  25. package/dist/index.umd.js +13 -0
  26. package/dist/interface/common_prop.d.ts +12 -0
  27. package/dist/interface/const.d.ts +28 -0
  28. package/dist/interface/timeline.d.ts +342 -0
  29. package/dist/react-timeline-editor.css +1 -0
  30. package/dist/utils/check_props.d.ts +2 -0
  31. package/dist/utils/deal_class_prefix.d.ts +1 -0
  32. package/dist/utils/deal_data.d.ts +58 -0
  33. package/dist/utils/logger.d.ts +132 -0
  34. package/package.json +70 -0
  35. package/src/components/control_area/index.tsx +1 -0
  36. package/src/components/cursor/cursor.css +26 -0
  37. package/src/components/cursor/cursor.tsx +105 -0
  38. package/src/components/cut-overlay/CutOverlay.css +68 -0
  39. package/src/components/cut-overlay/CutOverlay.tsx +491 -0
  40. package/src/components/edit_area/cross_row_drag.tsx +174 -0
  41. package/src/components/edit_area/drag_lines.css +13 -0
  42. package/src/components/edit_area/drag_lines.tsx +31 -0
  43. package/src/components/edit_area/drag_preview.tsx +50 -0
  44. package/src/components/edit_area/drag_utils.ts +77 -0
  45. package/src/components/edit_area/edit_action.css +56 -0
  46. package/src/components/edit_area/edit_action.tsx +362 -0
  47. package/src/components/edit_area/edit_area.css +24 -0
  48. package/src/components/edit_area/edit_area.tsx +606 -0
  49. package/src/components/edit_area/edit_row.css +78 -0
  50. package/src/components/edit_area/edit_row.tsx +128 -0
  51. package/src/components/edit_area/hooks/use_drag_line.ts +93 -0
  52. package/src/components/edit_area/insertion_line.tsx +39 -0
  53. package/src/components/loop-zone/LoopZoneOverlay.css +65 -0
  54. package/src/components/loop-zone/LoopZoneOverlay.tsx +461 -0
  55. package/src/components/row_rnd/hooks/useAutoScroll.ts +81 -0
  56. package/src/components/row_rnd/interactable.tsx +55 -0
  57. package/src/components/row_rnd/row_rnd.tsx +365 -0
  58. package/src/components/row_rnd/row_rnd_interface.ts +59 -0
  59. package/src/components/time_area/time_area.css +35 -0
  60. package/src/components/time_area/time_area.tsx +93 -0
  61. package/src/components/timeline.css +12 -0
  62. package/src/components/timeline.tsx +227 -0
  63. package/src/components/transport/TransportBar.css +171 -0
  64. package/src/components/transport/TransportBar.tsx +322 -0
  65. package/src/components/transport/useTimelinePlayer.ts +319 -0
  66. package/src/index.tsx +17 -0
  67. package/src/interface/common_prop.ts +13 -0
  68. package/src/interface/const.ts +32 -0
  69. package/src/interface/timeline.ts +329 -0
  70. package/src/utils/check_props.ts +77 -0
  71. package/src/utils/deal_class_prefix.ts +6 -0
  72. package/src/utils/deal_data.ts +159 -0
  73. package/src/utils/logger.ts +239 -0
@@ -0,0 +1,174 @@
1
+ import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
2
+ import { TimelineAction, TimelineRow } from '@keplar-404/timeline-engine';
3
+
4
+ // ─────────────────────────────────────────────
5
+ // Types
6
+ // ─────────────────────────────────────────────
7
+
8
+ export interface CrossRowDragState {
9
+ isDragging: boolean;
10
+ /** The action currently being dragged across rows */
11
+ action: TimelineAction | null;
12
+ /** The source row (where the action came from) */
13
+ sourceRow: TimelineRow | null;
14
+ /** Visual width of the ghost (px) */
15
+ ghostWidth: number;
16
+ /** Visual height of the ghost (px) */
17
+ ghostHeight: number;
18
+ /** Current cursor X (client) */
19
+ cursorX: number;
20
+ /** Current cursor Y (client) */
21
+ cursorY: number;
22
+ /** Offset from the left edge of the block where the user grabbed it */
23
+ grabOffsetX: number;
24
+ }
25
+
26
+ export interface CrossRowDragAPI {
27
+ state: CrossRowDragState;
28
+ startCrossRowDrag: (params: {
29
+ action: TimelineAction;
30
+ sourceRow: TimelineRow;
31
+ ghostWidth: number;
32
+ ghostHeight: number;
33
+ grabOffsetX: number;
34
+ initialX: number;
35
+ initialY: number;
36
+ }) => void;
37
+ endCrossRowDrag: () => void;
38
+ /** Called by the EditArea when mouse is released to commit the move */
39
+ onCommit: (
40
+ action: TimelineAction,
41
+ sourceRow: TimelineRow,
42
+ targetRow: TimelineRow,
43
+ newStart: number,
44
+ newEnd: number,
45
+ ) => void;
46
+ setOnCommit: (fn: CrossRowDragAPI['onCommit']) => void;
47
+ }
48
+
49
+ const initialState: CrossRowDragState = {
50
+ isDragging: false,
51
+ action: null,
52
+ sourceRow: null,
53
+ ghostWidth: 0,
54
+ ghostHeight: 0,
55
+ cursorX: 0,
56
+ cursorY: 0,
57
+ grabOffsetX: 0,
58
+ };
59
+
60
+ // ─────────────────────────────────────────────
61
+ // Context
62
+ // ─────────────────────────────────────────────
63
+
64
+ const CrossRowDragContext = createContext<CrossRowDragAPI | null>(null);
65
+
66
+ export const useCrossRowDrag = (): CrossRowDragAPI => {
67
+ const ctx = useContext(CrossRowDragContext);
68
+ if (!ctx) throw new Error('useCrossRowDrag must be used inside CrossRowDragProvider');
69
+ return ctx;
70
+ };
71
+
72
+ // ─────────────────────────────────────────────
73
+ // Provider
74
+ // ─────────────────────────────────────────────
75
+
76
+ export const CrossRowDragProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
77
+ const [state, setState] = useState<CrossRowDragState>(initialState);
78
+ const onCommitRef = useRef<CrossRowDragAPI['onCommit']>(() => {});
79
+
80
+ const startCrossRowDrag: CrossRowDragAPI['startCrossRowDrag'] = useCallback((params) => {
81
+ setState({
82
+ isDragging: true,
83
+ action: params.action,
84
+ sourceRow: params.sourceRow,
85
+ ghostWidth: params.ghostWidth,
86
+ ghostHeight: params.ghostHeight,
87
+ cursorX: params.initialX,
88
+ cursorY: params.initialY,
89
+ grabOffsetX: params.grabOffsetX,
90
+ });
91
+ }, []);
92
+
93
+ const endCrossRowDrag: CrossRowDragAPI['endCrossRowDrag'] = useCallback(() => {
94
+ setState(initialState);
95
+ }, []);
96
+
97
+ const setOnCommit: CrossRowDragAPI['setOnCommit'] = useCallback((fn) => {
98
+ onCommitRef.current = fn;
99
+ }, []);
100
+
101
+ const onCommit: CrossRowDragAPI['onCommit'] = useCallback(
102
+ (action, sourceRow, targetRow, newStart, newEnd) => {
103
+ onCommitRef.current(action, sourceRow, targetRow, newStart, newEnd);
104
+ },
105
+ [],
106
+ );
107
+
108
+ return (
109
+ <CrossRowDragContext.Provider
110
+ value={{ state, startCrossRowDrag, endCrossRowDrag, onCommit, setOnCommit }}
111
+ >
112
+ {children}
113
+ </CrossRowDragContext.Provider>
114
+ );
115
+ };
116
+
117
+ // ─────────────────────────────────────────────
118
+ // Ghost Element
119
+ // ─────────────────────────────────────────────
120
+
121
+ interface GhostProps {
122
+ state: CrossRowDragState;
123
+ enableGhostPreview: boolean;
124
+ /** Optional custom render function; receives the dragged action + source row */
125
+ getGhostPreview?: (params: { action: TimelineAction; row: TimelineRow }) => React.ReactNode;
126
+ }
127
+
128
+ export const CrossRowGhost: React.FC<GhostProps> = ({
129
+ state,
130
+ enableGhostPreview,
131
+ getGhostPreview,
132
+ }) => {
133
+ if (!state.isDragging || !enableGhostPreview) return null;
134
+ if (!state.action || !state.sourceRow) return null;
135
+
136
+ const left = state.cursorX - state.grabOffsetX;
137
+ const top = state.cursorY - state.ghostHeight / 2;
138
+
139
+ // Render custom preview if provided, otherwise use default glowing box
140
+ const customContent = getGhostPreview
141
+ ? getGhostPreview({ action: state.action, row: state.sourceRow })
142
+ : null;
143
+
144
+ return (
145
+ <div
146
+ style={{
147
+ position: 'fixed',
148
+ left,
149
+ top,
150
+ width: state.ghostWidth,
151
+ height: state.ghostHeight,
152
+ pointerEvents: 'none',
153
+ zIndex: 9999,
154
+ transition: 'none',
155
+ // Default appearance only when no custom content provided
156
+ ...(customContent == null && {
157
+ background: 'rgba(99, 179, 237, 0.55)',
158
+ border: '2px solid rgba(99, 179, 237, 0.9)',
159
+ borderRadius: 4,
160
+ boxShadow: '0 0 16px rgba(99,179,237,0.7), 0 0 4px rgba(99,179,237,0.9)',
161
+ backdropFilter: 'blur(2px)',
162
+ }),
163
+ // When custom: just a transparent, sized container
164
+ ...(customContent != null && {
165
+ overflow: 'hidden',
166
+ opacity: 0.85,
167
+ }),
168
+ }}
169
+ >
170
+ {customContent}
171
+ </div>
172
+ );
173
+ };
174
+
@@ -0,0 +1,13 @@
1
+ .timeline-editor-drag-line-container {
2
+ position: absolute;
3
+ height: 100%;
4
+ top: 0;
5
+ left: 0;
6
+ }
7
+ .timeline-editor-drag-line {
8
+ width: 0;
9
+ position: absolute;
10
+ top: 0;
11
+ height: 99%;
12
+ border-left: 1px dashed rgba(82, 151, 255, 0.6);
13
+ }
@@ -0,0 +1,31 @@
1
+ import React, { FC, useEffect, useState } from "react";
2
+ import { prefix } from "../../utils/deal_class_prefix";
3
+ import './drag_lines.css';
4
+
5
+ export interface DragLineData {
6
+ isMoving: boolean;
7
+ movePositions: number[];
8
+ assistPositions: number[];
9
+ }
10
+
11
+ export type DragLineProps = DragLineData & {scrollLeft: number};
12
+
13
+ /** Drag auxiliary lines */
14
+ export const DragLines: FC<DragLineProps> = ({
15
+ isMoving,
16
+ movePositions = [],
17
+ assistPositions = [],
18
+ scrollLeft,
19
+ }) => {
20
+ return(
21
+ <div className={prefix('drag-line-container')}>
22
+ {
23
+ isMoving && movePositions.filter(item => assistPositions.includes(item)).map(((linePos, index) => {
24
+ return (
25
+ <div key={index} className={prefix('drag-line')} style={{left: linePos - scrollLeft}} />
26
+ )
27
+ }))
28
+ }
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,50 @@
1
+ import React, { FC } from 'react';
2
+ import { prefix } from '../../utils/deal_class_prefix';
3
+
4
+ interface DragPreviewProps {
5
+ /** Top position of the preview element */
6
+ top: number;
7
+ /** Height of the preview element */
8
+ height: number;
9
+ /** Whether the preview element is visible */
10
+ visible: boolean;
11
+ }
12
+
13
+ /**
14
+ * Drag preview component - displays a preview of the row being dragged
15
+ */
16
+ export const DragPreview: FC<DragPreviewProps> = ({ top, height, visible }) => {
17
+ if (!visible) {
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <div
23
+ className={prefix('edit-area-drag-preview')}
24
+ style={{
25
+ position: 'absolute',
26
+ left: 0,
27
+ right: 0,
28
+ top,
29
+ height,
30
+ background: 'rgba(74, 144, 226, 0.3)',
31
+ border: '2px dashed #4a90e2',
32
+ borderRadius: '4px',
33
+ zIndex: 1001,
34
+ pointerEvents: 'none',
35
+ opacity: 0.8,
36
+ }}
37
+ >
38
+ <div
39
+ style={{
40
+ padding: '8px 16px',
41
+ color: '#4a90e2',
42
+ fontSize: '12px',
43
+ fontWeight: 'bold',
44
+ }}
45
+ >
46
+ Dragging...
47
+ </div>
48
+ </div>
49
+ );
50
+ };
@@ -0,0 +1,77 @@
1
+ import { TimelineRow } from '@keplar-404/timeline-engine';
2
+
3
+ /**
4
+ * Calculate the accumulated height of rows
5
+ * @param editorData Editor data
6
+ * @param rowIndex Target row index
7
+ * @param defaultRowHeight Default row height
8
+ * @returns Accumulated height
9
+ */
10
+ export const calculateRowAccumulatedHeight = (
11
+ editorData: TimelineRow[],
12
+ rowIndex: number,
13
+ defaultRowHeight: number
14
+ ): number => {
15
+ let accumulatedHeight = 0;
16
+ for (let i = 0; i < rowIndex; i++) {
17
+ accumulatedHeight += editorData[i]?.rowHeight || defaultRowHeight;
18
+ }
19
+ return accumulatedHeight;
20
+ };
21
+
22
+ /**
23
+ * Calculate total height of all rows
24
+ * @param editorData Editor data
25
+ * @param defaultRowHeight Default row height
26
+ * @returns Total height
27
+ */
28
+ export const calculateTotalHeight = (
29
+ editorData: TimelineRow[],
30
+ defaultRowHeight: number
31
+ ): number => {
32
+ return editorData.reduce((total, row) => total + (row?.rowHeight || defaultRowHeight), 0);
33
+ };
34
+
35
+ /**
36
+ * Get an array of actual heights for each row
37
+ * @param editorData Editor data
38
+ * @param defaultRowHeight Default row height
39
+ * @returns Height array
40
+ */
41
+ export const getRowHeights = (
42
+ editorData: TimelineRow[],
43
+ defaultRowHeight: number
44
+ ): number[] => {
45
+ return editorData.map(row => row?.rowHeight || defaultRowHeight);
46
+ };
47
+
48
+ /**
49
+ * Calculate the position of the insertion line
50
+ * @param editorData Editor data
51
+ * @param targetIndex Target index
52
+ * @param defaultRowHeight Default row height
53
+ * @returns Insertion line top position
54
+ */
55
+ export const calculateInsertionLineTop = (
56
+ editorData: TimelineRow[],
57
+ targetIndex: number,
58
+ defaultRowHeight: number
59
+ ): number => {
60
+ return calculateRowAccumulatedHeight(editorData, targetIndex, defaultRowHeight);
61
+ };
62
+
63
+ /**
64
+ * Validate whether the drag target index is valid
65
+ * @param targetIndex Target index
66
+ * @param draggedIndex Index of the row being dragged
67
+ * @param totalRows Total number of rows
68
+ * @returns Whether it is valid
69
+ */
70
+ export const isValidDragTarget = (
71
+ targetIndex: number,
72
+ draggedIndex: number,
73
+ totalRows: number
74
+ ): boolean => {
75
+ // Allow dragging to the last row (targetIndex = totalRows)
76
+ return targetIndex >= 0 && targetIndex <= totalRows && targetIndex !== draggedIndex;
77
+ };
@@ -0,0 +1,56 @@
1
+ .timeline-editor-action {
2
+ position: absolute;
3
+ left: 0;
4
+ top: 0;
5
+ background-color: #2f3134;
6
+ cursor: default;
7
+ transition: box-shadow 0.15s ease;
8
+ /** Glow effect when the block is in an active drag state (cross-row) */
9
+ }
10
+ .timeline-editor-action.action-cross-row-dragging {
11
+ box-shadow: 0 0 0 2px rgba(99, 179, 237, 0.8), 0 0 16px rgba(99, 179, 237, 0.6), 0 0 32px rgba(99, 179, 237, 0.3);
12
+ opacity: 0.75;
13
+ z-index: 10;
14
+ }
15
+ .timeline-editor-action.action-movable {
16
+ cursor: move;
17
+ }
18
+ .timeline-editor-action .timeline-editor-action-left-stretch,
19
+ .timeline-editor-action .timeline-editor-action-right-stretch {
20
+ position: absolute;
21
+ top: 0;
22
+ width: 10px;
23
+ border-radius: 4px;
24
+ height: 100%;
25
+ overflow: hidden;
26
+ }
27
+ .timeline-editor-action .timeline-editor-action-left-stretch::after,
28
+ .timeline-editor-action .timeline-editor-action-right-stretch::after {
29
+ position: absolute;
30
+ top: 0;
31
+ bottom: 0;
32
+ margin: auto;
33
+ border-radius: 4px;
34
+ border-top: 28px solid transparent;
35
+ border-bottom: 28px solid transparent;
36
+ }
37
+ .timeline-editor-action .timeline-editor-action-left-stretch {
38
+ left: 0;
39
+ cursor: w-resize;
40
+ }
41
+ .timeline-editor-action .timeline-editor-action-left-stretch::after {
42
+ left: 0;
43
+ content: '';
44
+ border-left: 7px solid rgba(255, 255, 255, 0.1);
45
+ border-right: 7px solid transparent;
46
+ }
47
+ .timeline-editor-action .timeline-editor-action-right-stretch {
48
+ right: 0;
49
+ cursor: e-resize;
50
+ }
51
+ .timeline-editor-action .timeline-editor-action-right-stretch::after {
52
+ right: 0;
53
+ content: '';
54
+ border-right: 7px solid rgba(255, 255, 255, 0.1);
55
+ border-left: 7px solid transparent;
56
+ }