mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 16:38:14 +00:00
convert some code to signals from context
This commit is contained in:
1 parent
bcb3acbc7f
commit
f43d8bcdf5
23 files changed
+216
-186
No files matched your search
@@ -1,25 +1,28 @@
|
||||
import React, { useCallback, useContext } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import React, { useCallback } from "react";
|
||||
import { useMouseEventsHeight } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsHeight";
|
||||
import {
|
||||
currentTime,
|
||||
overTimeline,
|
||||
scale,
|
||||
timelineHeight,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
|
||||
interface LowerPanelPropsI {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
const { setOverTimeline, updateCurrentTime, scale, timelineHeight } =
|
||||
useContext(TrackContext);
|
||||
const { onPointerDown, height } = useMouseEventsHeight();
|
||||
|
||||
const displayHeight = height > -1 ? height : timelineHeight;
|
||||
const displayHeight = height > -1 ? height : timelineHeight.value;
|
||||
|
||||
const onTimelineClick = useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (event.button === 0) {
|
||||
updateCurrentTime((Math.round(event.clientX) - 92) / 4 / scale);
|
||||
currentTime.value = (Math.round(event.clientX) - 92) / 4 / scale.value;
|
||||
}
|
||||
},
|
||||
[updateCurrentTime, scale],
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -31,9 +34,9 @@ export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
].join(" ")}
|
||||
style={{ height: displayHeight }}
|
||||
onPointerOver={() => {
|
||||
setOverTimeline(true);
|
||||
overTimeline.value = true;
|
||||
}}
|
||||
onPointerLeave={() => setOverTimeline(false)}
|
||||
onPointerLeave={() => (overTimeline.value = false)}
|
||||
onPointerDown={onTimelineClick}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useContext, useEffect, useReducer } from "react";
|
||||
import { useEffect, useReducer } from "react";
|
||||
|
||||
import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
@@ -17,11 +17,12 @@ import { AppUIProvider } from "./contexts/AppUiContext";
|
||||
import { reducer, initialState, ACTION_TYPES } from "./reducer";
|
||||
import { VIEW_MODES } from "./reducer/types";
|
||||
import { ViewSideBySide } from "./comps/ViewSideBySide";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { timelineHeight } from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
export const PageEnigmaComponent = () => {
|
||||
const [appUiState, dispatchAppUiState] = useReducer(reducer, initialState);
|
||||
const { timelineHeight } = useContext(TrackContext);
|
||||
useSignals();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(
|
||||
@@ -33,6 +34,8 @@ export const PageEnigmaComponent = () => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const lowerHeight = timelineHeight.value;
|
||||
console.log("main", timelineHeight.value);
|
||||
return (
|
||||
<div>
|
||||
<TopBarHelmet>
|
||||
@@ -46,8 +49,7 @@ export const PageEnigmaComponent = () => {
|
||||
<div
|
||||
id="CanvasUiWrapper"
|
||||
className="flex"
|
||||
style={{ height: `calc(100% - ${timelineHeight}px)` }}
|
||||
// style={{ height: `calc(100% - 260px` }}
|
||||
style={{ height: `calc(100% - ${lowerHeight}px)` }}
|
||||
>
|
||||
<div className="relative w-full overflow-hidden bg-transparent">
|
||||
<div
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { BaseClip } from "~/pages/PageEnigma/models/track";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { canDrop, dragId, overTimeline, scale } from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
interface Props {
|
||||
clip: BaseClip;
|
||||
@@ -8,16 +10,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ClipElement = ({ clip, type }: Props) => {
|
||||
const {
|
||||
startDrag,
|
||||
dragId,
|
||||
endDrag,
|
||||
scale,
|
||||
canDrop,
|
||||
setCanDrop,
|
||||
overTimeline,
|
||||
dropId,
|
||||
} = useContext(TrackContext);
|
||||
useSignals();
|
||||
const { startDrag, endDrag } = useContext(TrackContext);
|
||||
const [initPosition, setInitPosition] = useState<{
|
||||
initX: number;
|
||||
initY: number;
|
||||
@@ -37,7 +31,7 @@ export const ClipElement = ({ clip, type }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const onPointerUp = () => {
|
||||
if (dragId) {
|
||||
if (dragId.value) {
|
||||
endDrag();
|
||||
}
|
||||
};
|
||||
@@ -60,7 +54,7 @@ export const ClipElement = ({ clip, type }: Props) => {
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
window.removeEventListener("pointermove", onMouseMove);
|
||||
};
|
||||
}, [dragId, startDrag, endDrag, initX, initY]);
|
||||
}, [startDrag, endDrag, initX, initY]);
|
||||
|
||||
const onPointerDown = useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||
@@ -77,10 +71,10 @@ export const ClipElement = ({ clip, type }: Props) => {
|
||||
initX: position.x,
|
||||
initY: position.y + position.height + 1,
|
||||
});
|
||||
setCanDrop(false);
|
||||
canDrop.value = false;
|
||||
}
|
||||
},
|
||||
[type, clip.id, startDrag, setCanDrop],
|
||||
[type, clip.id, startDrag],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -98,15 +92,15 @@ export const ClipElement = ({ clip, type }: Props) => {
|
||||
className={[
|
||||
"absolute p-2",
|
||||
"rounded-lg",
|
||||
!canDrop ? "bg-brand-primary" : "bg-brand-secondary-700",
|
||||
dragId ? "block" : "hidden",
|
||||
!canDrop.value ? "bg-brand-primary" : "bg-brand-secondary-700",
|
||||
dragId.value ? "block" : "hidden",
|
||||
].join(" ")}
|
||||
style={{
|
||||
top: overTimeline ? y + 24 : y,
|
||||
left: overTimeline ? x : x,
|
||||
top: overTimeline.value ? y + 24 : y,
|
||||
left: overTimeline.value ? x : x,
|
||||
zIndex: 5000,
|
||||
width: overTimeline ? clip.length * 4 * scale : 64,
|
||||
height: overTimeline ? 32 : 64,
|
||||
width: overTimeline.value ? clip.length * 4 * scale.value : 64,
|
||||
height: overTimeline.value ? 32 : 64,
|
||||
}}
|
||||
onPointerDown={onPointerDown}
|
||||
>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
import { fullWidth } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const Audio = () => {
|
||||
const { audio, updateAudio, toggleAudioMute, fullWidth } =
|
||||
useContext(TrackContext);
|
||||
const { audio, updateAudio, toggleAudioMute } = useContext(TrackContext);
|
||||
const { clips } = audio!;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="block rounded-lg bg-audio-groupBg pb-5 pl-2 pr-4"
|
||||
style={{ width: fullWidth + 90 }}
|
||||
style={{ width: fullWidth.value + 90 }}
|
||||
>
|
||||
<div className="prevent-select mb-5 pt-2 text-xs font-medium text-white">
|
||||
Global Audio
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
import { fullWidth } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const Camera = () => {
|
||||
const { camera, fullWidth, updateCamera } = useContext(TrackContext);
|
||||
const { camera, updateCamera } = useContext(TrackContext);
|
||||
const { clips } = camera!;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="block rounded-lg bg-camera-groupBg pb-5 pl-2 pr-4"
|
||||
style={{ width: fullWidth + 90 }}
|
||||
style={{ width: fullWidth.value + 90 }}
|
||||
>
|
||||
<div className="mb-5 pt-2 text-xs font-medium text-white">Camera</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useContext, useMemo } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
import { fullWidth } from "~/pages/PageEnigma/store";
|
||||
|
||||
function buildUpdaters(
|
||||
updateCharacters: (options: {
|
||||
@@ -38,7 +39,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const Character = ({ characterId }: Props) => {
|
||||
const { characters, updateCharacters, toggleLipSyncMute, fullWidth } =
|
||||
const { characters, updateCharacters, toggleLipSyncMute } =
|
||||
useContext(TrackContext);
|
||||
const character = characters.find((row) => (row.id = characterId));
|
||||
|
||||
@@ -56,7 +57,7 @@ export const Character = ({ characterId }: Props) => {
|
||||
return (
|
||||
<div
|
||||
className="block rounded-lg bg-character-groupBg pb-5 pl-2 pr-4"
|
||||
style={{ width: fullWidth + 90 }}
|
||||
style={{ width: fullWidth.value + 90 }}
|
||||
>
|
||||
<div className="prevent-select mb-5 pt-2 text-xs font-medium text-white">
|
||||
Character
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { useContext } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { Track } from "~/pages/PageEnigma/comps/Timeline/Track";
|
||||
import { fullWidth } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const Objects = () => {
|
||||
const { objects, updateObject, fullWidth } = useContext(TrackContext);
|
||||
const { objects, updateObject } = useContext(TrackContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="block rounded-lg bg-objects-groupBg pb-5 pl-2 pr-4"
|
||||
style={{ width: fullWidth + 90 }}
|
||||
style={{ width: fullWidth.value + 90 }}
|
||||
>
|
||||
<div className="prevent-select mb-5 pt-2 text-xs font-medium text-white">
|
||||
Objects
|
||||
|
||||
@@ -10,15 +10,18 @@ import { useMouseEventsAnimation } from "./utils/useMouseEventsAnimation";
|
||||
import { faSortDown } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { ConfirmationModal } from "~/components/ConfirmationModal";
|
||||
import {
|
||||
scale,
|
||||
currentTime,
|
||||
filmLength,
|
||||
timelineHeight,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
|
||||
export const Timeline = () => {
|
||||
const {
|
||||
characters,
|
||||
objects,
|
||||
scale,
|
||||
length,
|
||||
selectedClip,
|
||||
currentTime,
|
||||
deleteCharacterClip,
|
||||
deleteAudioClip,
|
||||
deleteCameraClip,
|
||||
@@ -27,10 +30,14 @@ export const Timeline = () => {
|
||||
const { onPointerDown, time } = useMouseEventsAnimation();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
const sectionWidth = 60 * 4 * scale;
|
||||
const sectionWidth = 60 * 4 * scale.value;
|
||||
const fullHeight =
|
||||
characters.length * 268 + objects.objects.length * 60 + 300 + 96;
|
||||
|
||||
useEffect(() => {
|
||||
timelineHeight.value = window.outerHeight * 0.25;
|
||||
}, []);
|
||||
|
||||
const onDeleteAsk = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (["Backspace", "Delete"].indexOf(event.key) > -1 && selectedClip) {
|
||||
@@ -40,7 +47,7 @@ export const Timeline = () => {
|
||||
[selectedClip],
|
||||
);
|
||||
|
||||
const displayTime = time === -1 ? currentTime : time;
|
||||
const displayTime = time === -1 ? currentTime.value : time;
|
||||
|
||||
const onDelete = useCallback(() => {
|
||||
deleteCharacterClip(selectedClip!);
|
||||
@@ -74,7 +81,7 @@ export const Timeline = () => {
|
||||
"text-xs text-white opacity-75",
|
||||
].join(" ")}
|
||||
>
|
||||
{Array(length)
|
||||
{Array(filmLength.value)
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
<Fragment key={index}>
|
||||
@@ -96,15 +103,18 @@ export const Timeline = () => {
|
||||
))}
|
||||
<div
|
||||
className="absolute"
|
||||
style={{ left: length * sectionWidth + 92 }}
|
||||
style={{ left: filmLength.value * sectionWidth + 92 }}
|
||||
>
|
||||
00:{length < 10 ? "0" + length.toString() : length.toString()}
|
||||
00:
|
||||
{filmLength.value < 10
|
||||
? "0" + filmLength.value.toString()
|
||||
: filmLength.value.toString()}
|
||||
</div>
|
||||
<div
|
||||
className="absolute block h-full bg-ui-divider"
|
||||
style={{
|
||||
width: 1,
|
||||
left: length * sectionWidth + 88,
|
||||
left: filmLength.value * sectionWidth + 88,
|
||||
height: fullHeight,
|
||||
}}
|
||||
/>
|
||||
@@ -125,7 +135,7 @@ export const Timeline = () => {
|
||||
</div>
|
||||
<div
|
||||
className="absolute text-brand-primary"
|
||||
style={{ top: 8, left: displayTime * 4 * scale + 88 }}
|
||||
style={{ top: 8, left: displayTime * 4 * scale.value + 88 }}
|
||||
onPointerDown={onPointerDown}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -2,8 +2,15 @@ import { TrackClip } from "~/pages/PageEnigma/comps/Timeline/TrackClip";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { BaseClip } from "~/pages/PageEnigma/models/track";
|
||||
import { PointerEvent, useContext } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { PointerEvent } from "react";
|
||||
import {
|
||||
canDrop,
|
||||
dragType,
|
||||
dropId,
|
||||
dropOffset,
|
||||
filmLength,
|
||||
scale,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
@@ -26,20 +33,18 @@ export const Track = ({
|
||||
style,
|
||||
type,
|
||||
}: Props) => {
|
||||
const { length, scale, setCanDrop, dragType, setDropId, setDropOffset } =
|
||||
useContext(TrackContext);
|
||||
const trackType = type ?? style;
|
||||
|
||||
function onPointerOver() {
|
||||
if (dragType !== trackType) {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
setCanDrop(true);
|
||||
setDropId(id);
|
||||
canDrop.value = true;
|
||||
dropId.value = id;
|
||||
}
|
||||
|
||||
function onPointerMove(event: PointerEvent<HTMLDivElement>) {
|
||||
if (dragType !== trackType) {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
const track = document.getElementById(`track-${trackType}-${id}`);
|
||||
@@ -47,13 +52,13 @@ export const Track = ({
|
||||
return;
|
||||
}
|
||||
const position = track.getBoundingClientRect();
|
||||
setDropOffset((event.clientX - position.x) / 4 / scale);
|
||||
dropOffset.value = (event.clientX - position.x) / 4 / scale.value;
|
||||
}
|
||||
function onPointerLeave() {
|
||||
if (dragType !== trackType) {
|
||||
if (dragType.value !== trackType) {
|
||||
return;
|
||||
}
|
||||
setCanDrop(false);
|
||||
canDrop.value = false;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -72,7 +77,9 @@ export const Track = ({
|
||||
index > 0 ? clips[index - 1].offset + clips[index - 1].length : 0
|
||||
}
|
||||
max={
|
||||
index < clips.length - 1 ? clips[index + 1].offset : length * 60
|
||||
index < clips.length - 1
|
||||
? clips[index + 1].offset
|
||||
: filmLength.value * 60
|
||||
}
|
||||
style={style}
|
||||
updateClip={updateClip}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMouseEventsClip } from "./utils/useMouseEventsClip";
|
||||
import { BaseClip } from "~/pages/PageEnigma/models/track";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { useContext, useState } from "react";
|
||||
import { scale } from "~/pages/PageEnigma/store";
|
||||
|
||||
interface Props {
|
||||
min: number;
|
||||
@@ -12,7 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
const { selectClip, selectedClip, scale } = useContext(TrackContext);
|
||||
const { selectClip, selectedClip } = useContext(TrackContext);
|
||||
const [state, setState] = useState({
|
||||
length: clip.length,
|
||||
offset: clip.offset,
|
||||
@@ -41,7 +42,7 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
"rounded",
|
||||
"prevent-select text-xs font-medium text-white",
|
||||
].join(" ")}
|
||||
style={{ top: -28, left: offset * 4 * scale + 2 }}
|
||||
style={{ top: -28, left: offset * 4 * scale.value + 2 }}
|
||||
>
|
||||
{clip.name}
|
||||
</div>
|
||||
@@ -54,7 +55,11 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
? "border border-b-2 border-l-2 border-r-0 border-t-2 border-white focus-visible:outline-0"
|
||||
: "",
|
||||
].join(" ")}
|
||||
style={{ width: 15, left: offset * 4 * scale, cursor: "w-resize" }}
|
||||
style={{
|
||||
width: 15,
|
||||
left: offset * 4 * scale.value,
|
||||
cursor: "w-resize",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "left")}
|
||||
onClick={() => selectClip(clip.id)}
|
||||
/>
|
||||
@@ -67,8 +72,8 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
: "",
|
||||
].join(" ")}
|
||||
style={{
|
||||
width: length * 4 * scale - 30,
|
||||
left: offset * 4 * scale + 15,
|
||||
width: length * 4 * scale.value - 30,
|
||||
left: offset * 4 * scale.value + 15,
|
||||
cursor: "move",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "drag")}
|
||||
@@ -85,7 +90,7 @@ export const TrackClip = ({ clip, min, max, style, updateClip }: Props) => {
|
||||
].join(" ")}
|
||||
style={{
|
||||
width: 15,
|
||||
left: offset * 4 * scale + length * 4 * scale - 15,
|
||||
left: offset * 4 * scale.value + length * 4 * scale.value - 15,
|
||||
cursor: "e-resize",
|
||||
}}
|
||||
onPointerDown={(event) => onPointerDown(event, "right")}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { currentTime, scale } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const useMouseEventsAnimation = () => {
|
||||
const { currentTime, updateCurrentTime, length, scale } =
|
||||
useContext(TrackContext);
|
||||
const [isActive, setIsActive] = useState("");
|
||||
const [clientX, setClientX] = useState(0);
|
||||
|
||||
const [time, setTime] = useState(-1);
|
||||
|
||||
useEffect(() => {
|
||||
const max = length * 60 * 4 * scale;
|
||||
const max = length * 60 * 4 * scale.value;
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (isActive) {
|
||||
updateCurrentTime(Math.round(time));
|
||||
currentTime.value = Math.round(time);
|
||||
setIsActive("");
|
||||
setTime(-1);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = (event: MouseEvent) => {
|
||||
const delta = (event.clientX - clientX) / 4 / scale + currentTime;
|
||||
const delta =
|
||||
(event.clientX - clientX) / 4 / scale.value + currentTime.value;
|
||||
if (isActive === "drag") {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
@@ -40,14 +39,14 @@ export const useMouseEventsAnimation = () => {
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
window.removeEventListener("pointermove", onMouseMove);
|
||||
};
|
||||
}, [clientX, isActive, length, updateCurrentTime, scale, time, currentTime]);
|
||||
}, [clientX, isActive, time]);
|
||||
|
||||
return {
|
||||
onPointerDown: useCallback((event: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (event.button === 0) {
|
||||
setClientX(event.clientX);
|
||||
setIsActive("drag");
|
||||
setTime(currentTime);
|
||||
setTime(currentTime.value);
|
||||
}
|
||||
}, []),
|
||||
time,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React, {
|
||||
Dispatch,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from "react";
|
||||
import React, { Dispatch, useCallback, useEffect, useRef } from "react";
|
||||
import { BaseClip } from "~/pages/PageEnigma/models/track";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { canDrop, scale } from "~/pages/PageEnigma/store";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
export const useMouseEventsClip = (
|
||||
clip: BaseClip,
|
||||
@@ -15,6 +10,7 @@ export const useMouseEventsClip = (
|
||||
updateClip: (args: { id: string; offset: number; length: number }) => void,
|
||||
setState: Dispatch<{ length: number; offset: number }>,
|
||||
) => {
|
||||
useSignals();
|
||||
const currLength = useRef(clip.length);
|
||||
const currOffset = useRef(clip.offset);
|
||||
const initLength = useRef(clip.length);
|
||||
@@ -22,8 +18,6 @@ export const useMouseEventsClip = (
|
||||
const isActive = useRef("");
|
||||
const clientX = useRef(0);
|
||||
|
||||
const { scale } = useContext(TrackContext);
|
||||
|
||||
const onPointerUp = useCallback(() => {
|
||||
if (isActive.current) {
|
||||
updateClip({
|
||||
@@ -32,12 +26,13 @@ export const useMouseEventsClip = (
|
||||
length: Math.round(currLength.current),
|
||||
});
|
||||
isActive.current = "";
|
||||
canDrop.value = false;
|
||||
}
|
||||
}, [updateClip, clip.id]);
|
||||
|
||||
const onMouseMove = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
const delta = (event.clientX - clientX.current) / 4 / scale;
|
||||
const delta = (event.clientX - clientX.current) / 4 / scale.value;
|
||||
const deltaOffset = delta + initOffset.current;
|
||||
if (isActive.current === "drag") {
|
||||
if (deltaOffset < min || deltaOffset + currLength.current > max) {
|
||||
@@ -63,7 +58,7 @@ export const useMouseEventsClip = (
|
||||
}
|
||||
setState({ length: currLength.current, offset: currOffset.current });
|
||||
},
|
||||
[max, min, scale, setState],
|
||||
[max, min, setState],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { timelineHeight } from "~/pages/PageEnigma/store";
|
||||
|
||||
export const useMouseEventsHeight = () => {
|
||||
const { timelineHeight, setTimelineHeight } = useContext(TrackContext);
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [clientY, setClientY] = useState(0);
|
||||
|
||||
@@ -11,7 +10,8 @@ export const useMouseEventsHeight = () => {
|
||||
useEffect(() => {
|
||||
const onPointerUp = () => {
|
||||
if (isActive) {
|
||||
setTimelineHeight(height);
|
||||
console.log("up", height);
|
||||
timelineHeight.value = Math.round(height);
|
||||
setIsActive(false);
|
||||
setHeight(-1);
|
||||
}
|
||||
@@ -22,10 +22,10 @@ export const useMouseEventsHeight = () => {
|
||||
const delta = event.clientY - clientY;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if (timelineHeight - delta < 30) {
|
||||
if (timelineHeight.value - delta < 30) {
|
||||
return;
|
||||
}
|
||||
setHeight(timelineHeight - delta);
|
||||
setHeight(timelineHeight.value - delta);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -37,20 +37,17 @@ export const useMouseEventsHeight = () => {
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
window.removeEventListener("pointermove", onMouseMove);
|
||||
};
|
||||
}, [clientY, isActive, height, timelineHeight, setTimelineHeight]);
|
||||
}, [clientY, isActive, height]);
|
||||
|
||||
return {
|
||||
onPointerDown: useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (event.button === 0) {
|
||||
event.stopPropagation();
|
||||
setClientY(event.clientY);
|
||||
setIsActive(true);
|
||||
setHeight(timelineHeight);
|
||||
}
|
||||
},
|
||||
[timelineHeight],
|
||||
),
|
||||
onPointerDown: useCallback((event: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (event.button === 0) {
|
||||
event.stopPropagation();
|
||||
setClientY(event.clientY);
|
||||
setIsActive(true);
|
||||
setHeight(timelineHeight.value);
|
||||
}
|
||||
}, []),
|
||||
height,
|
||||
};
|
||||
};
|
||||
@@ -79,35 +79,8 @@ export const TrackContext = createContext<{
|
||||
audioClips: AudioClip[];
|
||||
|
||||
// drag and drop
|
||||
dragType: "animations" | "lipSync" | null;
|
||||
dragId: string | null;
|
||||
startDrag: (type: "animations" | "lipSync", id: string) => void;
|
||||
endDrag: () => void;
|
||||
canDrop: boolean;
|
||||
setCanDrop: (can: boolean) => void;
|
||||
overTimeline: boolean;
|
||||
setOverTimeline: (over: boolean) => void;
|
||||
dropId: string;
|
||||
setDropId: (id: string) => void;
|
||||
dropOffset: number;
|
||||
setDropOffset: (offset: number) => void;
|
||||
|
||||
// scale of timeline displa
|
||||
scale: number;
|
||||
|
||||
// current time position
|
||||
currentTime: number;
|
||||
updateCurrentTime: (time: number) => void;
|
||||
|
||||
// total length of the film clip
|
||||
length: number;
|
||||
|
||||
// height of the timeline section
|
||||
timelineHeight: number;
|
||||
setTimelineHeight: (height: number) => void;
|
||||
|
||||
// computed width length * 60 * 4 * scale
|
||||
fullWidth: number;
|
||||
}>({
|
||||
characters: [],
|
||||
updateCharacters: () => {},
|
||||
@@ -140,24 +113,6 @@ export const TrackContext = createContext<{
|
||||
animationClips: [],
|
||||
audioClips: [],
|
||||
|
||||
dragType: null,
|
||||
dragId: null,
|
||||
startDrag: () => {},
|
||||
endDrag: () => {},
|
||||
dropId: "",
|
||||
setDropId: () => {},
|
||||
canDrop: false,
|
||||
setCanDrop: () => {},
|
||||
overTimeline: false,
|
||||
setOverTimeline: () => {},
|
||||
dropOffset: 0,
|
||||
setDropOffset: () => {},
|
||||
|
||||
scale: 1,
|
||||
currentTime: 0,
|
||||
updateCurrentTime: () => {},
|
||||
length: 12,
|
||||
fullWidth: 0,
|
||||
timelineHeight: 0,
|
||||
setTimelineHeight: () => {},
|
||||
});
|
||||
@@ -6,6 +6,13 @@ import useUpdateAudio from "~/pages/PageEnigma/contexts/TrackContext/utils/useUp
|
||||
import useUpdateObject from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject";
|
||||
import { AnimationClip, AudioClip } from "~/pages/PageEnigma/models/track";
|
||||
import useUpdateDragDrop from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateDragDrop";
|
||||
import {
|
||||
canDrop,
|
||||
dragId,
|
||||
dragType,
|
||||
dropId,
|
||||
dropOffset,
|
||||
} from "~/pages/PageEnigma/store";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
@@ -47,19 +54,18 @@ export const TrackProvider = ({ children }: Props) => {
|
||||
|
||||
// cross group functions
|
||||
const dropClip = useCallback(() => {
|
||||
const { canDrop, dragType, dropId, dragId, dropOffset } = dragDrop;
|
||||
if (canDrop) {
|
||||
if (dragType === "animations") {
|
||||
if (canDrop.value) {
|
||||
if (dragType.value === "animations") {
|
||||
characters.addCharacterAnimation({
|
||||
clipId: dragId!,
|
||||
characterId: dropId,
|
||||
clipId: dragId.value!,
|
||||
characterId: dropId.value,
|
||||
animationClips,
|
||||
offset: dropOffset,
|
||||
offset: dropOffset.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
endDrag();
|
||||
}, [dragDrop, animationClips, characters, endDrag]);
|
||||
}, [animationClips, characters, endDrag]);
|
||||
|
||||
const fullWidth = useMemo(() => {
|
||||
return length * 60 * 4 * scale;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { BaseClip, CharacterGroup } from "~/pages/PageEnigma/models/track";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
export default function useUpdateCharacters() {
|
||||
const [characters, setCharacters] = useState<CharacterGroup[]>([
|
||||
@@ -128,7 +129,10 @@ export default function useUpdateCharacters() {
|
||||
}
|
||||
return {
|
||||
...character,
|
||||
animationClips: [...character.animationClips, { ...clip, offset }],
|
||||
animationClips: [
|
||||
...character.animationClips,
|
||||
{ ...clip, id: uuid.v4(), offset },
|
||||
],
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,38 +1,23 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { canDrop, dragId, dragType } from "~/pages/PageEnigma/store";
|
||||
|
||||
export default function useUpdateDragDrop() {
|
||||
const [state, setState] = useState<{
|
||||
dragType: "animations" | "lipSync" | null;
|
||||
dragId: string | null;
|
||||
}>({ dragType: null, dragId: null });
|
||||
const startDrag = useCallback(
|
||||
(type: "animations" | "lipSync", id: string) => {
|
||||
setState({ dragId: id, dragType: type });
|
||||
dragId.value = id;
|
||||
dragType.value = type;
|
||||
},
|
||||
[],
|
||||
);
|
||||
const [canDrop, setCanDrop] = useState(false);
|
||||
const [overTimeline, setOverTimeline] = useState(false);
|
||||
|
||||
const endDrag = useCallback(() => {
|
||||
setState({ dragId: null, dragType: null });
|
||||
dragId.value = null;
|
||||
dragType.value = null;
|
||||
canDrop.value = false;
|
||||
}, []);
|
||||
|
||||
const [dropId, setDropId] = useState("");
|
||||
const [dropOffset, setDropOffset] = useState(0);
|
||||
|
||||
return {
|
||||
dragId: state.dragId,
|
||||
dragType: state.dragType,
|
||||
startDrag,
|
||||
endDrag,
|
||||
canDrop,
|
||||
setCanDrop,
|
||||
overTimeline,
|
||||
setOverTimeline,
|
||||
dropId,
|
||||
setDropId,
|
||||
dropOffset,
|
||||
setDropOffset,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
export const dragType = signal<"animations" | "lipSync" | null>(null);
|
||||
export const dragId = signal<string | null>(null);
|
||||
export const canDrop = signal(false);
|
||||
export const overTimeline = signal(false);
|
||||
export const dropId = signal("");
|
||||
export const dropOffset = signal(0);
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./dragAndDrop";
|
||||
export * from "./sizing";
|
||||
export * from "./timing";
|
||||
@@ -0,0 +1,8 @@
|
||||
import { signal, computed } from "@preact/signals-core";
|
||||
|
||||
export const scale = signal(1);
|
||||
export const filmLength = signal(12);
|
||||
export const timelineHeight = signal(0);
|
||||
export const fullWidth = computed(() => {
|
||||
return filmLength.value * 60 * 4 * scale.value;
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
|
||||
export const currentTime = signal(0);
|
||||
Generated
+41
@@ -17,6 +17,7 @@
|
||||
"@netlify/edge-functions": "^2.3.1",
|
||||
"@netlify/remix-edge-adapter": "^3.2.0",
|
||||
"@netlify/remix-runtime": "^2.2.0",
|
||||
"@preact/signals-react": "^2.0.1",
|
||||
"@remix-run/deno": "^2.8.1",
|
||||
"@remix-run/node": "^2.8.1",
|
||||
"@remix-run/react": "^2.8.1",
|
||||
@@ -37,6 +38,7 @@
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/three": "^0.162.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"eslint": "^8.38.0",
|
||||
@@ -1735,6 +1737,31 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@preact/signals-core": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.6.0.tgz",
|
||||
"integrity": "sha512-O/XGxwP85h1F7+ouqTMOIZ3+V1whfaV9ToIVcuyGriD4JkSD00cQo54BKdqjvBJxbenvp7ynfqRHEwI6e+NIhw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/@preact/signals-react": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@preact/signals-react/-/signals-react-2.0.1.tgz",
|
||||
"integrity": "sha512-QQoxAPiliH6oeZPF5OZHmyala5zyFya0ZCp/A0lR4y6R9wO4lT7GdtYg5UFlmKNWMcFN6PiAjNPMTiNFMmnCpw==",
|
||||
"dependencies": {
|
||||
"@preact/signals-core": "^1.6.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || 17.x || 18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/deno": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/deno/-/deno-2.8.1.tgz",
|
||||
@@ -2429,6 +2456,12 @@
|
||||
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/webxr": {
|
||||
"version": "0.5.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.14.tgz",
|
||||
@@ -12478,6 +12511,14 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@netlify/edge-functions": "^2.3.1",
|
||||
"@netlify/remix-edge-adapter": "^3.2.0",
|
||||
"@netlify/remix-runtime": "^2.2.0",
|
||||
"@preact/signals-react": "^2.0.1",
|
||||
"@remix-run/deno": "^2.8.1",
|
||||
"@remix-run/node": "^2.8.1",
|
||||
"@remix-run/react": "^2.8.1",
|
||||
@@ -43,6 +44,7 @@
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/three": "^0.162.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"eslint": "^8.38.0",
|
||||
|
||||
Reference in new issue
Block a user