Update
This commit is contained in:
@@ -7,7 +7,7 @@ import { generateTimelineFromTemplate } from '../../utils/timeline-template-proc
|
||||
import useVideoExport from './video-export';
|
||||
import VideoPreview from './video-preview';
|
||||
|
||||
const VideoEditor = ({ width, height }) => {
|
||||
const VideoEditor = ({ width, height, onOpenTextSidebar }) => {
|
||||
const [showConsoleLogs] = useState(true);
|
||||
|
||||
const [dimensions] = useState({
|
||||
@@ -586,6 +586,7 @@ const VideoEditor = ({ width, height }) => {
|
||||
copyFFmpegCommand={copyFFmpegCommand}
|
||||
exportVideo={exportVideo}
|
||||
onElementUpdate={handleElementUpdate}
|
||||
onOpenTextSidebar={onOpenTextSidebar}
|
||||
layerRef={layerRef}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useMitt } from '@/plugins/MittContext';
|
||||
import useVideoEditorStore from '@/stores/VideoEditorStore';
|
||||
import { Type } from 'lucide-react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Image, Layer, Line, Stage, Text, Transformer } from 'react-konva';
|
||||
import { Group, Image, Layer, Line, Stage, Text, Transformer } from 'react-konva';
|
||||
import { Html } from 'react-konva-utils';
|
||||
|
||||
// Import our custom hooks and utilities
|
||||
import { useElementSelection } from './video-preview/video-preview-element-selection';
|
||||
@@ -38,11 +42,13 @@ const VideoPreview = ({
|
||||
copyFFmpegCommand,
|
||||
exportVideo,
|
||||
onElementUpdate, // New prop for updating element properties
|
||||
onOpenTextSidebar, // New prop for opening text sidebar
|
||||
|
||||
// Refs
|
||||
layerRef,
|
||||
}) => {
|
||||
const emitter = useMitt();
|
||||
const { selectedTextElement } = useVideoEditorStore();
|
||||
|
||||
// Refs for elements and transformer
|
||||
const transformerRef = useRef(null);
|
||||
@@ -87,7 +93,7 @@ const VideoPreview = ({
|
||||
}, [selectedElementId, activeElements]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="relative">
|
||||
<Stage width={dimensions.width} height={dimensions.height} ref={stageRef} onClick={handleStageClick} onTap={handleStageClick}>
|
||||
<Layer ref={layerRef}>
|
||||
{activeElements.map((element) => {
|
||||
@@ -133,45 +139,72 @@ const VideoPreview = ({
|
||||
);
|
||||
} else if (element.type === 'text') {
|
||||
return (
|
||||
<Text
|
||||
key={element.id}
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
elementRefs.current[element.id] = node;
|
||||
}
|
||||
}}
|
||||
text={element.text}
|
||||
x={element.x}
|
||||
y={element.y}
|
||||
fontSize={element.fontSize}
|
||||
fontStyle={getTextFontStyle(element)}
|
||||
fontFamily={element.fontFamily || 'Arial'}
|
||||
fill={element.fill || '#ffffff'}
|
||||
stroke={element.strokeWidth > 0 ? element.stroke || '#000000' : undefined}
|
||||
strokeWidth={element.strokeWidth * 3 || 0}
|
||||
fillAfterStrokeEnabled={true}
|
||||
strokeScaleEnabled={false}
|
||||
rotation={element.rotation || 0}
|
||||
// Center the text horizontally
|
||||
align="center"
|
||||
verticalAlign="middle"
|
||||
// Let text have natural width and height for multiline support
|
||||
wrap="word"
|
||||
// Always scale 1 - size changes go through fontSize
|
||||
scaleX={1}
|
||||
scaleY={1}
|
||||
draggable
|
||||
dragBoundFunc={createDragBoundFunc(element.id)}
|
||||
onClick={() => handleElementSelect(element.id)}
|
||||
onTap={() => handleElementSelect(element.id)}
|
||||
onDragMove={(e) => handleDragMove(element.id, e)}
|
||||
onDragEnd={(e) => handleDragEnd(element.id, e)}
|
||||
onTransform={() => handleTransform(element.id)}
|
||||
// Apply fixedWidth and offsetX if they exist
|
||||
width={element.fixedWidth}
|
||||
offsetX={element.offsetX}
|
||||
// Visual feedback for selection
|
||||
/>
|
||||
<Group key={element.id}>
|
||||
<Text
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
elementRefs.current[element.id] = node;
|
||||
}
|
||||
}}
|
||||
text={element.text}
|
||||
x={element.x}
|
||||
y={element.y}
|
||||
fontSize={element.fontSize}
|
||||
fontStyle={getTextFontStyle(element)}
|
||||
fontFamily={element.fontFamily || 'Arial'}
|
||||
fill={element.fill || '#ffffff'}
|
||||
stroke={element.strokeWidth > 0 ? element.stroke || '#000000' : undefined}
|
||||
strokeWidth={element.strokeWidth * 3 || 0}
|
||||
fillAfterStrokeEnabled={true}
|
||||
strokeScaleEnabled={false}
|
||||
rotation={element.rotation || 0}
|
||||
// Center the text horizontally
|
||||
align="center"
|
||||
verticalAlign="middle"
|
||||
// Let text have natural width and height for multiline support
|
||||
wrap="word"
|
||||
// Always scale 1 - size changes go through fontSize
|
||||
scaleX={1}
|
||||
scaleY={1}
|
||||
draggable
|
||||
dragBoundFunc={createDragBoundFunc(element.id)}
|
||||
onClick={() => handleElementSelect(element.id)}
|
||||
onTap={() => handleElementSelect(element.id)}
|
||||
onDragMove={(e) => handleDragMove(element.id, e)}
|
||||
onDragEnd={(e) => handleDragEnd(element.id, e)}
|
||||
onTransform={() => handleTransform(element.id)}
|
||||
// Apply fixedWidth and offsetX if they exist
|
||||
width={element.fixedWidth}
|
||||
offsetX={element.offsetX}
|
||||
// Visual feedback for selection
|
||||
/>
|
||||
|
||||
{/* Edit button - only show when this text element is selected */}
|
||||
{isSelected && (
|
||||
<Html
|
||||
groupProps={{
|
||||
x: element.x - (element.offsetX || 0),
|
||||
y: element.y - 50,
|
||||
}}
|
||||
divProps={{
|
||||
style: {
|
||||
zIndex: 10,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
className="h-12 w-12 rounded-full border shadow-sm"
|
||||
onClick={() => {
|
||||
handleElementSelect(element.id);
|
||||
onOpenTextSidebar();
|
||||
}}
|
||||
>
|
||||
<Type className="h-8 w-8" />
|
||||
</Button>
|
||||
</Html>
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
} else if (element.type === 'image' && element.imageElement && element.isImageReady) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user